From 510ae24dcaa2535cd373e0f04f40a29a1a2ff49c Mon Sep 17 00:00:00 2001 From: Timothy J Warren Date: Wed, 19 Apr 2017 16:15:39 -0400 Subject: [PATCH] kitsu <-> mal comparison for anime, see #18 --- src/Command/SyncKitsuWithMal.php | 296 ++++++++++++++++++++++++------- 1 file changed, 230 insertions(+), 66 deletions(-) diff --git a/src/Command/SyncKitsuWithMal.php b/src/Command/SyncKitsuWithMal.php index 6af414ca..eebf529d 100644 --- a/src/Command/SyncKitsuWithMal.php +++ b/src/Command/SyncKitsuWithMal.php @@ -77,21 +77,34 @@ class SyncKitsuWithMal extends BaseCommand { $data = $this->diffAnimeLists(); - $this->echoBox("Number of anime items that need to be added to MAL: " . count($data['addToMAL'])); - if ( ! empty($data['addToMAL'])) { - $this->echoBox("Adding missing anime list items to MAL"); + $count = count($data['addToMAL']); + $this->echoBox("Adding {$count} missing anime list items to MAL"); $this->createMALListItems($data['addToMAL'], 'anime'); } - $this->echoBox('Number of anime items that need to be added to Kitsu: ' . count($data['addToKitsu'])); - if ( ! empty($data['addToKitsu'])) { - $this->echoBox("Adding missing anime list items to Kitsu"); + $count = count($data['addToKitsu']); + $this->echoBox("Adding {$count} missing anime list items to Kitsu"); $this->createKitsuListItems($data['addToKitsu'], 'anime'); } + + if ( ! empty($data['updateMAL'])) + { + $count = count($data['updateMAL']); + $this->echoBox("Updating {$count} outdated MAL anime list items"); + $this->updateMALListItems($data['updateMAL'], 'anime'); + } + + if ( ! empty($data['updateKitsu'])) + { + print_r($data['updateKitsu']); + $count = count($data['updateKitsu']); + $this->echoBox("Updating {$count} outdated Kitsu anime list items"); + // $this->updateKitsuListItems($data['updateKitsu'], 'anime'); + } } public function syncManga() @@ -104,21 +117,33 @@ class SyncKitsuWithMal extends BaseCommand { $data = $this->diffMangaLists(); - $this->echoBox("Number of manga items that need to be added to MAL: " . count($data['addToMAL'])); - if ( ! empty($data['addToMAL'])) { - $this->echoBox("Adding missing manga list items to MAL"); + $count = count($data['addToMAL']); + $this->echoBox("Adding {$count} missing manga list items to MAL"); $this->createMALListItems($data['addToMAL'], 'manga'); } - $this->echoBox('Number of manga items that need to be added to Kitsu: ' . count($data['addToKitsu'])); - if ( ! empty($data['addToKitsu'])) { - $this->echoBox("Adding missing manga list items to Kitsu"); + $count = count($data['addToKitsu']); + $this->echoBox("Adding {$count} missing manga list items to Kitsu"); $this->createKitsuListItems($data['addToKitsu'], 'manga'); } + + if ( ! empty($data['updateMAL'])) + { + $count = count($data['updateMAL']); + $this->echoBox("Updating {$count} outdated MAL manga list items"); + $this->updateMALListItems($data['updateMAL'], 'manga'); + } + + if ( ! empty($data['updateKitsu'])) + { + $count = count($data['updateKitsu']); + $this->echoBox("Updating {$count} outdated Kitsu manga list items"); + $this->updateKitsuListItems($data['updateKitsu'], 'manga'); + } } public function filterMappings(array $includes, string $type = 'anime'): array @@ -186,56 +211,19 @@ class SyncKitsuWithMal extends BaseCommand { return $output; } - public function filterKitsuAnimeList() + public function filterKitsuList(string $type = 'anime'): array { - $data = $this->kitsuModel->getFullAnimeList(); + $method = "getFull{$type}List"; + $data = $this->kitsuModel->$method(); $includes = JsonAPI::organizeIncludes($data['included']); - $includes['mappings'] = $this->filterMappings($includes['mappings']); + $includes['mappings'] = $this->filterMappings($includes['mappings'], $type); $output = []; foreach($data['data'] as $listItem) { - $animeId = $listItem['relationships']['anime']['data']['id']; - $potentialMappings = $includes['anime'][$animeId]['relationships']['mappings']; - $malId = NULL; - - foreach ($potentialMappings as $mappingId) - { - if (array_key_exists($mappingId, $includes['mappings'])) - { - $malId = $includes['mappings'][$mappingId]['externalId']; - } - } - - // Skip to the next item if there isn't a MAL ID - if (is_null($malId)) - { - continue; - } - - $output[$listItem['id']] = [ - 'id' => $listItem['id'], - 'malId' => $malId, - 'data' => $listItem['attributes'], - ]; - } - - return $output; - } - - public function filterKitsuMangaList() - { - $data = $this->kitsuModel->getFullMangaList(); - $includes = JsonAPI::organizeIncludes($data['included']); - $includes['mappings'] = $this->filterMappings($includes['mappings'], 'manga'); - - $output = []; - - foreach($data['data'] as $listItem) - { - $mangaId = $listItem['relationships']['manga']['data']['id']; - $potentialMappings = $includes['manga'][$mangaId]['relationships']['mappings']; + $id = $listItem['relationships'][$type]['data']['id']; + $potentialMappings = $includes[$type][$id]['relationships']['mappings']; $malId = NULL; foreach ($potentialMappings as $mappingId) @@ -264,7 +252,7 @@ class SyncKitsuWithMal extends BaseCommand { public function diffMangaLists() { - $kitsuList = $this->filterKitsuMangaList(); + $kitsuList = $this->filterKitsuList('manga'); $malList = $this->formatMALMangaList(); $itemsToAddToMAL = []; @@ -308,7 +296,7 @@ class SyncKitsuWithMal extends BaseCommand { { // Get libraryEntries with media.mappings from Kitsu // Organize mappings, and ignore entries without mappings - $kitsuList = $this->filterKitsuAnimeList(); + $kitsuList = $this->filterKitsuList('anime'); // Get MAL list data $malList = $this->formatMALAnimeList(); @@ -337,6 +325,23 @@ class SyncKitsuWithMal extends BaseCommand { { // Eventually, compare the list entries, and determine which // needs to be updated + $item = $this->compareAnimeListItems($kitsuItem, $malList[$kitsuItem['malId']]); + + if (is_null($item)) + { + continue; + } + + if (in_array('kitsu', $item['updateType'])) + { + $kitsuUpdateItems[] = $item['data']; + } + + if (in_array('mal', $item['updateType'])) + { + $malUpdateItems[] = $item['data']; + } + continue; } @@ -348,15 +353,6 @@ class SyncKitsuWithMal extends BaseCommand { } - // Compare each list entry - // If a list item exists only on MAL, create it on Kitsu with the existing data from MAL - // If a list item exists only on Kitsu, create it on MAL with the existing data from Kitsu - // If an item already exists on both APIS: - // Compare last updated dates, and use the later one - // Otherwise, use rewatch count, then episode progress as critera for selecting the more up - // to date entry - // Based on the 'newer' entry, update the other api list item - return [ 'addToMAL' => $itemsToAddToMAL, 'updateMAL' => $malUpdateItems, @@ -365,6 +361,174 @@ class SyncKitsuWithMal extends BaseCommand { ]; } + public function compareAnimeListItems(array $kitsuItem, array $malItem) + { + $compareKeys = ['status', 'progress', 'rating', 'reconsuming']; + $diff = []; + $dateDiff = (new \DateTime($kitsuItem['data']['updatedAt'])) <=> (new \DateTime($malItem['data']['updatedAt'])); + + foreach($compareKeys as $key) + { + $diff[$key] = $kitsuItem['data'][$key] <=> $malItem['data'][$key]; + } + + // No difference? Bail out early + $diffValues = array_values($diff); + $diffValues = array_unique($diffValues); + if (count($diffValues) === 1 && $diffValues[0] === 0) + { + return; + } + + $update = [ + 'id' => $kitsuItem['id'], + 'mal_id' => $kitsuItem['malId'], + 'data' => [] + ]; + $return = [ + 'updateType' => [] + ]; + + $sameStatus = $diff['status'] === 0; + $sameProgress = $diff['progress'] === 0; + $sameRating = $diff['rating'] === 0; + + + // If status is the same, and progress count is different, use greater progress + if ($sameStatus && ( ! $sameProgress)) + { + if ($diff['progress'] === 1) + { + $update['data']['progress'] = $kitsuItem['data']['progress']; + $return['updateType'][] = 'mal'; + } + else if($diff['progress'] === -1) + { + $update['data']['progress'] = $malItem['data']['progress']; + $return['updateType'][] = 'kitsu'; + } + } + + // If status and progress are different, it's a bit more complicated... + // But, at least for now, assume newer record is correct + if ( ! ($sameStatus || $sameProgress)) + { + if ($dateDiff === 1) + { + $update['data']['status'] = $kitsuItem['data']['status']; + + if ((int)$kitsuItem['data']['progress'] !== 0) + { + $update['data']['progress'] = $kitsuItem['data']['progress']; + } + + $return['updateType'][] = 'mal'; + } + else if($dateDiff === -1) + { + $update['data']['status'] = $malItem['data']['status']; + + if ((int)$malItem['data']['progress'] !== 0) + { + $update['data']['progress'] = $kitsuItem['data']['progress']; + } + + $return['updateType'][] = 'kitsu'; + } + } + + // If rating is different, use the rating from the item most recently updated + if ( ! $sameRating) + { + if ($dateDiff === 1) + { + $update['data']['rating'] = $kitsuItem['data']['rating']; + $return['updateType'][] = 'mal'; + } + else if ($dateDiff === -1) + { + $update['data']['rating'] = $malItem['data']['rating']; + $return['updateType'][] = 'kitsu'; + } + } + + // If status is different, use the status of the more recently updated item + if ( ! $sameStatus) + { + if ($dateDiff === 1) + { + $update['data']['status'] = $kitsuItem['data']['status']; + $return['updateType'][] = 'mal'; + } + else if ($dateDiff === -1) + { + $update['data']['status'] = $malItem['data']['status']; + $return['updateType'][] = 'kitsu'; + } + } + + $return['meta'] = [ + 'kitsu' => $kitsuItem['data'], + 'mal' => $malItem['data'], + 'dateDiff' => $dateDiff, + 'diff' => $diff, + ]; + $return['data'] = $update; + $return['updateType'] = array_unique($return['updateType']); + return $return; + } + + public function updateKitsuListItems($itemsToUpdate, $type = 'anime') + { + $requester = new ParallelAPIRequest(); + foreach($itemsToUpdate as $item) + { + $requester->addRequest($this->kitsuModel->updateListItem($item)); + } + + $responses = $requester->makeRequests(); + + foreach($responses as $key => $response) + { + $id = $itemsToUpdate[$key]['id']; + if ($response->getStatus() === 200) + { + $this->echoBox("Successfully updated Kitsu {$type} list item with id: {$id}"); + } + else + { + echo $response->getBody(); + $this->echoBox("Failed to update Kitsu {$type} list item with id: {$id}"); + } + } + } + + public function updateMALListItems($itemsToUpdate, $type = 'anime') + { + $transformer = new ALT(); + $requester = new ParallelAPIRequest(); + + foreach($itemsToUpdate as $item) + { + $requester->addRequest($this->malModel->updateListItem($item, $type)); + } + + $responses = $requester->makeRequests(); + + foreach($responses as $key => $response) + { + $id = $itemsToUpdate[$key]['mal_id']; + if ($response->getBody() === 'Updated') + { + $this->echoBox("Successfully updated MAL {$type} list item with id: {$id}"); + } + else + { + $this->echoBox("Failed to update MAL {$type} list item with id: {$id}"); + } + } + } + public function createKitsuListItems($itemsToAdd, $type = 'anime') { $requester = new ParallelAPIRequest();