Do you wish to register an account?
API client for Kitsu.io, with optional Anime collection, and optional Anilist syncing.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

971 lines
21KB

  1. <?php declare(strict_types=1);
  2. /**
  3. * Hummingbird Anime List Client
  4. *
  5. * An API client for Kitsu to manage anime and manga watch lists
  6. *
  7. * PHP version 7.1
  8. *
  9. * @package HummingbirdAnimeClient
  10. * @author Timothy J. Warren <tim@timshomepage.net>
  11. * @copyright 2015 - 2018 Timothy J. Warren
  12. * @license http://www.opensource.org/licenses/mit-license.html MIT License
  13. * @version 4.1
  14. * @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
  15. */
  16. namespace Aviat\AnimeClient\API\Kitsu;
  17. use function Amp\Promise\wait;
  18. use Amp\Artax\Request;
  19. use Aviat\AnimeClient\API\{
  20. CacheTrait,
  21. JsonAPI,
  22. Kitsu as K,
  23. ParallelAPIRequest
  24. };
  25. use Aviat\AnimeClient\API\Enum\{
  26. AnimeWatchingStatus\Kitsu as KitsuWatchingStatus,
  27. MangaReadingStatus\Kitsu as KitsuReadingStatus
  28. };
  29. use Aviat\AnimeClient\API\Mapping\{AnimeWatchingStatus, MangaReadingStatus};
  30. use Aviat\AnimeClient\API\Kitsu\Transformer\{
  31. AnimeTransformer,
  32. AnimeListTransformer,
  33. MangaTransformer,
  34. MangaListTransformer
  35. };
  36. use Aviat\AnimeClient\Types\{
  37. Anime,
  38. FormItem,
  39. MangaPage
  40. };
  41. use Aviat\Ion\{Di\ContainerAware, Json};
  42. /**
  43. * Kitsu API Model
  44. */
  45. final class Model {
  46. use CacheTrait;
  47. use ContainerAware;
  48. use KitsuTrait;
  49. private const LIST_PAGE_SIZE = 100;
  50. /**
  51. * Class to map anime list items
  52. * to a common format used by
  53. * templates
  54. *
  55. * @var AnimeListTransformer
  56. */
  57. private $animeListTransformer;
  58. /**
  59. * @var AnimeTransformer
  60. */
  61. private $animeTransformer;
  62. /**
  63. * @var ListItem
  64. */
  65. private $listItem;
  66. /**
  67. * @var MangaTransformer
  68. */
  69. private $mangaTransformer;
  70. /**
  71. * @var MangaListTransformer
  72. */
  73. private $mangaListTransformer;
  74. /**
  75. * Constructor
  76. *
  77. * @param ListItem $listItem
  78. */
  79. public function __construct(ListItem $listItem)
  80. {
  81. $this->animeTransformer = new AnimeTransformer();
  82. $this->animeListTransformer = new AnimeListTransformer();
  83. $this->listItem = $listItem;
  84. $this->mangaTransformer = new MangaTransformer();
  85. $this->mangaListTransformer = new MangaListTransformer();
  86. }
  87. /**
  88. * Get the access token from the Kitsu API
  89. *
  90. * @param string $username
  91. * @param string $password
  92. * @return bool|array
  93. */
  94. public function authenticate(string $username, string $password)
  95. {
  96. // K::AUTH_URL
  97. $response = $this->getResponse('POST', K::AUTH_URL, [
  98. 'headers' => [
  99. 'accept' => NULL,
  100. 'Content-type' => 'application/x-www-form-urlencoded',
  101. 'client_id' => NULL,
  102. 'client_secret' => NULL
  103. ],
  104. 'form_params' => [
  105. 'grant_type' => 'password',
  106. 'username' => $username,
  107. 'password' => $password
  108. ]
  109. ]);
  110. $data = Json::decode(wait($response->getBody()));
  111. if (array_key_exists('error', $data))
  112. {
  113. dump($data['error']);
  114. dump($response);
  115. die();
  116. }
  117. if (array_key_exists('access_token', $data))
  118. {
  119. return $data;
  120. }
  121. return FALSE;
  122. }
  123. /**
  124. * Extend the current session with a refresh token
  125. *
  126. * @param string $token
  127. * @return bool|array
  128. */
  129. public function reAuthenticate(string $token)
  130. {
  131. $response = $this->getResponse('POST', K::AUTH_URL, [
  132. 'headers' => [
  133. 'Accept-encoding' => '*'
  134. ],
  135. 'form_params' => [
  136. 'grant_type' => 'refresh_token',
  137. 'refresh_token' => $token
  138. ]
  139. ]);
  140. $data = Json::decode(wait($response->getBody()));
  141. if (array_key_exists('access_token', $data))
  142. {
  143. return $data;
  144. }
  145. return FALSE;
  146. }
  147. /**
  148. * Get the userid for a username from Kitsu
  149. *
  150. * @param string $username
  151. * @return string
  152. */
  153. public function getUserIdByUsername(string $username = NULL): string
  154. {
  155. if ($username === NULL)
  156. {
  157. $username = $this->getUsername();
  158. }
  159. $cacheItem = $this->cache->getItem(K::AUTH_USER_ID_KEY);
  160. if ( ! $cacheItem->isHit())
  161. {
  162. $data = $this->getRequest('users', [
  163. 'query' => [
  164. 'filter' => [
  165. 'name' => $username
  166. ]
  167. ]
  168. ]);
  169. $cacheItem->set($data['data'][0]['id']);
  170. $cacheItem->save();
  171. }
  172. return $cacheItem->get();
  173. }
  174. /**
  175. * Get information about a character
  176. *
  177. * @param string $slug
  178. * @return array
  179. */
  180. public function getCharacter(string $slug): array
  181. {
  182. $data = $this->getRequest('characters', [
  183. 'query' => [
  184. 'filter' => [
  185. 'slug' => $slug,
  186. ],
  187. 'fields' => [
  188. 'anime' => 'canonicalTitle,titles,slug,posterImage',
  189. 'manga' => 'canonicalTitle,titles,slug,posterImage'
  190. ],
  191. 'include' => 'castings.person,castings.media'
  192. ]
  193. ]);
  194. return $data;
  195. }
  196. /**
  197. * Get information about a person
  198. *
  199. * @param string $id
  200. * @return array
  201. */
  202. public function getPerson(string $id): array
  203. {
  204. return $this->getRequest("people/{$id}", [
  205. 'query' => [
  206. 'include' => 'castings,castings.media,staff,staff.media,voices'
  207. ],
  208. ]);
  209. }
  210. /**
  211. * Get profile information for the configured user
  212. *
  213. * @param string $username
  214. * @return array
  215. */
  216. public function getUserData(string $username): array
  217. {
  218. // $userId = $this->getUserIdByUsername($username);
  219. $data = $this->getRequest("users", [
  220. 'query' => [
  221. 'filter' => [
  222. 'name' => $username,
  223. ],
  224. 'fields' => [
  225. // 'anime' => 'slug,name,canonicalTitle',
  226. 'characters' => 'slug,name,image'
  227. ],
  228. 'include' => 'waifu,pinnedPost,blocks,linkedAccounts,profileLinks,profileLinks.profileLinkSite,userRoles,favorites.item'
  229. ]
  230. ]);
  231. return $data;
  232. }
  233. /**
  234. * Search for an anime or manga
  235. *
  236. * @param string $type - 'anime' or 'manga'
  237. * @param string $query - name of the item to search for
  238. * @return array
  239. */
  240. public function search(string $type, string $query): array
  241. {
  242. $options = [
  243. 'query' => [
  244. 'filter' => [
  245. 'text' => $query,
  246. ],
  247. 'page' => [
  248. 'offset' => 0,
  249. 'limit' => 20
  250. ],
  251. 'include' => 'mappings'
  252. ]
  253. ];
  254. $raw = $this->getRequest($type, $options);
  255. $raw['included'] = JsonAPI::organizeIncluded($raw['included']);
  256. foreach ($raw['data'] as &$item)
  257. {
  258. $item['attributes']['titles'] = K::filterTitles($item['attributes']);
  259. array_shift($item['attributes']['titles']);
  260. // Map the mal_id if it exists for syncing with other APIs
  261. foreach($item['relationships']['mappings']['data'] as $rel)
  262. {
  263. $mapping = $raw['included']['mappings'][$rel['id']];
  264. if ($mapping['attributes']['externalSite'] === "myanimelist/{$type}")
  265. {
  266. $item['mal_id'] = $mapping['attributes']['externalId'];
  267. }
  268. }
  269. }
  270. return $raw;
  271. }
  272. /**
  273. * Find a media item on Kitsu by its associated MAL id
  274. *
  275. * @param string $malId
  276. * @param string $type "anime" or "manga"
  277. * @return string|NULL
  278. */
  279. public function getKitsuIdFromMALId(string $malId, string $type="anime")
  280. {
  281. $options = [
  282. 'query' => [
  283. 'filter' => [
  284. 'external_site' => "myanimelist/{$type}",
  285. 'external_id' => $malId
  286. ],
  287. 'fields' => [
  288. 'media' => 'id,slug'
  289. ],
  290. 'include' => 'item'
  291. ]
  292. ];
  293. $raw = $this->getRequest('mappings', $options);
  294. if ( ! array_key_exists('included', $raw))
  295. {
  296. return NULL;
  297. }
  298. return $raw['included'][0]['id'];
  299. }
  300. // -------------------------------------------------------------------------
  301. // ! Anime-specific methods
  302. // -------------------------------------------------------------------------
  303. /**
  304. * Get information about a particular anime
  305. *
  306. * @param string $slug
  307. * @return Anime
  308. */
  309. public function getAnime(string $slug): Anime
  310. {
  311. $baseData = $this->getRawMediaData('anime', $slug);
  312. if (empty($baseData))
  313. {
  314. return new Anime();
  315. }
  316. $transformed = $this->animeTransformer->transform($baseData);
  317. $transformed['included'] = JsonAPI::organizeIncluded($baseData['included']);
  318. return $transformed;
  319. }
  320. /**
  321. * Get information about a particular anime
  322. *
  323. * @param string $animeId
  324. * @return Anime
  325. */
  326. public function getAnimeById(string $animeId): Anime
  327. {
  328. $baseData = $this->getRawMediaDataById('anime', $animeId);
  329. return $this->animeTransformer->transform($baseData);
  330. }
  331. /**
  332. * Get the anime list for the configured user
  333. *
  334. * @param string $status - The watching status to filter the list with
  335. * @return array
  336. */
  337. public function getAnimeList(string $status): array
  338. {
  339. $cacheItem = $this->cache->getItem("kitsu-anime-list-{$status}");
  340. if ( ! $cacheItem->isHit())
  341. {
  342. $data = $this->getRawAnimeList($status) ?? [];
  343. // Bail out on no data
  344. if (empty($data))
  345. {
  346. return [];
  347. }
  348. $included = JsonAPI::organizeIncludes($data['included']);
  349. $included = JsonAPI::inlineIncludedRelationships($included, 'anime');
  350. foreach($data['data'] as $i => &$item)
  351. {
  352. $item['included'] = $included;
  353. }
  354. $transformed = $this->animeListTransformer->transformCollection($data['data']);
  355. $cacheItem->set($transformed);
  356. $cacheItem->save();
  357. }
  358. return $cacheItem->get();
  359. }
  360. /**
  361. * Get the number of anime list items
  362. *
  363. * @param string $status - Optional status to filter by
  364. * @return int
  365. */
  366. public function getAnimeListCount(string $status = '') : int
  367. {
  368. $options = [
  369. 'query' => [
  370. 'filter' => [
  371. 'user_id' => $this->getUserIdByUsername(),
  372. 'media_type' => 'Anime'
  373. ],
  374. 'page' => [
  375. 'limit' => 1
  376. ],
  377. 'sort' => '-updated_at'
  378. ]
  379. ];
  380. if ( ! empty($status))
  381. {
  382. $options['query']['filter']['status'] = $status;
  383. }
  384. $response = $this->getRequest('library-entries', $options);
  385. return $response['meta']['count'];
  386. }
  387. /**
  388. * Get the full anime list
  389. *
  390. * @param array $options
  391. * @return array
  392. */
  393. public function getFullRawAnimeList(array $options = [
  394. 'include' => 'anime.mappings'
  395. ]): array
  396. {
  397. $status = $options['filter']['status'] ?? '';
  398. $count = $this->getAnimeListCount($status);
  399. $size = static::LIST_PAGE_SIZE;
  400. $pages = ceil($count / $size);
  401. $requester = new ParallelAPIRequest();
  402. // Set up requests
  403. for ($i = 0; $i < $pages; $i++)
  404. {
  405. $offset = $i * $size;
  406. $requester->addRequest($this->getPagedAnimeList($size, $offset, $options));
  407. }
  408. $responses = $requester->makeRequests();
  409. $output = [];
  410. foreach($responses as $response)
  411. {
  412. $data = Json::decode($response);
  413. $output[] = $data;
  414. }
  415. return array_merge_recursive(...$output);
  416. }
  417. /**
  418. * Get all the anime entries, that are organized for output to html
  419. *
  420. * @return array
  421. */
  422. public function getFullOrganizedAnimeList(): array
  423. {
  424. $output = [];
  425. $statuses = KitsuWatchingStatus::getConstList();
  426. foreach ($statuses as $key => $status)
  427. {
  428. $mappedStatus = AnimeWatchingStatus::KITSU_TO_TITLE[$status];
  429. $output[$mappedStatus] = $this->getAnimeList($status) ?? [];
  430. }
  431. return $output;
  432. }
  433. /**
  434. * Get the mal id for the anime represented by the kitsu id
  435. * to enable updating MyAnimeList
  436. *
  437. * @param string $kitsuAnimeId The id of the anime on Kitsu
  438. * @return string|null Returns the mal id if it exists, otherwise null
  439. */
  440. public function getMalIdForAnime(string $kitsuAnimeId)
  441. {
  442. $options = [
  443. 'query' => [
  444. 'include' => 'mappings'
  445. ]
  446. ];
  447. $data = $this->getRequest("anime/{$kitsuAnimeId}", $options);
  448. if ( ! array_key_exists('included', $data))
  449. {
  450. return NULL;
  451. }
  452. $mappings = array_column($data['included'], 'attributes');
  453. foreach($mappings as $map)
  454. {
  455. if ($map['externalSite'] === 'myanimelist/anime')
  456. {
  457. return $map['externalId'];
  458. }
  459. }
  460. return NULL;
  461. }
  462. /**
  463. * Get the full anime list in paginated form
  464. *
  465. * @param int $limit
  466. * @param int $offset
  467. * @param array $options
  468. * @return Request
  469. */
  470. public function getPagedAnimeList(int $limit, int $offset = 0, array $options = [
  471. 'include' => 'anime.mappings'
  472. ]): Request
  473. {
  474. $defaultOptions = [
  475. 'filter' => [
  476. 'user_id' => $this->getUserIdByUsername($this->getUsername()),
  477. 'media_type' => 'Anime'
  478. ],
  479. 'page' => [
  480. 'offset' => $offset,
  481. 'limit' => $limit
  482. ],
  483. 'sort' => '-updated_at'
  484. ];
  485. $options = array_merge($defaultOptions, $options);
  486. return $this->setUpRequest('GET', 'library-entries', ['query' => $options]);
  487. }
  488. /**
  489. * Get the raw (unorganized) anime list for the configured user
  490. *
  491. * @param string $status - The watching status to filter the list with
  492. * @return array
  493. */
  494. public function getRawAnimeList(string $status): array
  495. {
  496. $options = [
  497. 'filter' => [
  498. 'user_id' => $this->getUserIdByUsername($this->getUsername()),
  499. 'media_type' => 'Anime',
  500. 'status' => $status,
  501. ],
  502. 'include' => 'media,media.categories,media.mappings,anime.streamingLinks',
  503. 'sort' => '-updated_at'
  504. ];
  505. return $this->getFullRawAnimeList($options);
  506. }
  507. // -------------------------------------------------------------------------
  508. // ! Manga-specific methods
  509. // -------------------------------------------------------------------------
  510. /**
  511. * Get information about a particular manga
  512. *
  513. * @param string $slug
  514. * @return MangaPage
  515. */
  516. public function getManga(string $slug): MangaPage
  517. {
  518. $baseData = $this->getRawMediaData('manga', $slug);
  519. if (empty($baseData))
  520. {
  521. return new MangaPage([]);
  522. }
  523. $transformed = $this->mangaTransformer->transform($baseData);
  524. $transformed['included'] = JsonAPI::organizeIncluded($baseData['included']);
  525. return $transformed;
  526. }
  527. /**
  528. * Get information about a particular manga
  529. *
  530. * @param string $mangaId
  531. * @return array
  532. */
  533. public function getMangaById(string $mangaId): MangaPage
  534. {
  535. $baseData = $this->getRawMediaDataById('manga', $mangaId);
  536. return $this->mangaTransformer->transform($baseData);
  537. }
  538. /**
  539. * Get the manga list for the configured user
  540. *
  541. * @param string $status - The reading status by which to filter the list
  542. * @param int $limit - The number of list items to fetch per page
  543. * @param int $offset - The page offset
  544. * @return array
  545. */
  546. public function getMangaList(string $status, int $limit = 200, int $offset = 0): array
  547. {
  548. $options = [
  549. 'query' => [
  550. 'filter' => [
  551. 'user_id' => $this->getUserIdByUsername($this->getUsername()),
  552. 'media_type' => 'Manga',
  553. 'status' => $status,
  554. ],
  555. 'include' => 'media,media.categories,media.mappings',
  556. 'page' => [
  557. 'offset' => $offset,
  558. 'limit' => $limit
  559. ],
  560. 'sort' => '-updated_at'
  561. ]
  562. ];
  563. $cacheItem = $this->cache->getItem("kitsu-manga-list-{$status}");
  564. if ( ! $cacheItem->isHit())
  565. {
  566. $data = $this->getRequest('library-entries', $options) ?? [];
  567. // Bail out on no data
  568. if (empty($data) || ( ! array_key_exists('included', $data)))
  569. {
  570. return [];
  571. }
  572. $included = JsonAPI::organizeIncludes($data['included']);
  573. $included = JsonAPI::inlineIncludedRelationships($included, 'manga');
  574. foreach($data['data'] as $i => &$item)
  575. {
  576. $item['included'] = $included;
  577. }
  578. $transformed = $this->mangaListTransformer->transformCollection($data['data']);
  579. $cacheItem->set($transformed);
  580. $cacheItem->save();
  581. }
  582. return $cacheItem->get();
  583. }
  584. /**
  585. * Get the number of manga list items
  586. *
  587. * @param string $status - Optional status to filter by
  588. * @return int
  589. */
  590. public function getMangaListCount(string $status = '') : int
  591. {
  592. $options = [
  593. 'query' => [
  594. 'filter' => [
  595. 'user_id' => $this->getUserIdByUsername(),
  596. 'media_type' => 'Manga'
  597. ],
  598. 'page' => [
  599. 'limit' => 1
  600. ],
  601. 'sort' => '-updated_at'
  602. ]
  603. ];
  604. if ( ! empty($status))
  605. {
  606. $options['query']['filter']['status'] = $status;
  607. }
  608. $response = $this->getRequest('library-entries', $options);
  609. return $response['meta']['count'];
  610. }
  611. /**
  612. * Get the full manga list
  613. *
  614. * @param array $options
  615. * @return array
  616. */
  617. public function getFullRawMangaList(array $options = [
  618. 'include' => 'manga.mappings'
  619. ]): array
  620. {
  621. $status = $options['filter']['status'] ?? '';
  622. $count = $this->getMangaListCount($status);
  623. $size = static::LIST_PAGE_SIZE;
  624. $pages = ceil($count / $size);
  625. $requester = new ParallelAPIRequest();
  626. // Set up requests
  627. for ($i = 0; $i < $pages; $i++)
  628. {
  629. $offset = $i * $size;
  630. $requester->addRequest($this->getPagedMangaList($size, $offset, $options));
  631. }
  632. $responses = $requester->makeRequests();
  633. $output = [];
  634. foreach($responses as $response)
  635. {
  636. $data = Json::decode($response);
  637. $output[] = $data;
  638. }
  639. return array_merge_recursive(...$output);
  640. }
  641. /**
  642. * Get all Manga lists
  643. *
  644. * @return array
  645. */
  646. public function getFullOrganizedMangaList(): array
  647. {
  648. $statuses = KitsuReadingStatus::getConstList();
  649. $output = [];
  650. foreach ($statuses as $status)
  651. {
  652. $mappedStatus = MangaReadingStatus::KITSU_TO_TITLE[$status];
  653. $output[$mappedStatus] = $this->getMangaList($status);
  654. }
  655. return $output;
  656. }
  657. /**
  658. * Get the full manga list in paginated form
  659. *
  660. * @param int $limit
  661. * @param int $offset
  662. * @param array $options
  663. * @return Request
  664. */
  665. public function getPagedMangaList(int $limit, int $offset = 0, array $options = [
  666. 'include' => 'manga.mappings'
  667. ]): Request
  668. {
  669. $defaultOptions = [
  670. 'filter' => [
  671. 'user_id' => $this->getUserIdByUsername($this->getUsername()),
  672. 'media_type' => 'Manga'
  673. ],
  674. 'page' => [
  675. 'offset' => $offset,
  676. 'limit' => $limit
  677. ],
  678. 'sort' => '-updated_at'
  679. ];
  680. $options = array_merge($defaultOptions, $options);
  681. return $this->setUpRequest('GET', 'library-entries', ['query' => $options]);
  682. }
  683. /**
  684. * Get the mal id for the manga represented by the kitsu id
  685. * to enable updating MyAnimeList
  686. *
  687. * @param string $kitsuMangaId The id of the manga on Kitsu
  688. * @return string|null Returns the mal id if it exists, otherwise null
  689. */
  690. public function getMalIdForManga(string $kitsuMangaId)
  691. {
  692. $options = [
  693. 'query' => [
  694. 'include' => 'mappings'
  695. ]
  696. ];
  697. $data = $this->getRequest("manga/{$kitsuMangaId}", $options);
  698. $mappings = array_column($data['included'], 'attributes');
  699. foreach($mappings as $map)
  700. {
  701. if ($map['externalSite'] === 'myanimelist/manga')
  702. {
  703. return $map['externalId'];
  704. }
  705. }
  706. return NULL;
  707. }
  708. // -------------------------------------------------------------------------
  709. // ! Generic API calls
  710. // -------------------------------------------------------------------------
  711. /**
  712. * Create a list item
  713. *
  714. * @param array $data
  715. * @return Request
  716. */
  717. public function createListItem(array $data): Request
  718. {
  719. $data['user_id'] = $this->getUserIdByUsername($this->getUsername());
  720. return $this->listItem->create($data);
  721. }
  722. /**
  723. * Get the data for a specific list item, generally for editing
  724. *
  725. * @param string $listId - The unique identifier of that list item
  726. * @return mixed
  727. */
  728. public function getListItem(string $listId)
  729. {
  730. $baseData = $this->listItem->get($listId);
  731. $included = JsonAPI::organizeIncludes($baseData['included']);
  732. switch (TRUE)
  733. {
  734. case array_key_exists('anime', $included): // in_array('anime', array_keys($included)):
  735. $included = JsonAPI::inlineIncludedRelationships($included, 'anime');
  736. $baseData['data']['included'] = $included;
  737. return $this->animeListTransformer->transform($baseData['data']);
  738. case array_key_exists('manga', $included): // in_array('manga', array_keys($included)):
  739. $included = JsonAPI::inlineIncludedRelationships($included, 'manga');
  740. $baseData['data']['included'] = $included;
  741. $baseData['data']['manga'] = $baseData['included'][0];
  742. return $this->mangaListTransformer->transform($baseData['data']);
  743. default:
  744. return $baseData['data'];
  745. }
  746. }
  747. /**
  748. * Increase the progress count for a list item
  749. *
  750. * @param FormItem $data
  751. * @return Request
  752. */
  753. public function incrementListItem(FormItem $data): Request
  754. {
  755. return $this->listItem->increment($data['id'], $data['data']);
  756. }
  757. /**
  758. * Modify a list item
  759. *
  760. * @param FormItem $data
  761. * @return Request
  762. */
  763. public function updateListItem(FormItem $data): Request
  764. {
  765. return $this->listItem->update($data['id'], $data['data']);
  766. }
  767. /**
  768. * Remove a list item
  769. *
  770. * @param string $id - The id of the list item to remove
  771. * @return Request
  772. */
  773. public function deleteListItem(string $id): Request
  774. {
  775. return $this->listItem->delete($id);
  776. }
  777. /**
  778. * Get the kitsu username from config
  779. *
  780. * @return string
  781. */
  782. private function getUsername(): string
  783. {
  784. return $this->getContainer()
  785. ->get('config')
  786. ->get(['kitsu_username']);
  787. }
  788. /**
  789. * Get the raw data for the anime id
  790. *
  791. * @param string $type
  792. * @param string $id
  793. * @return array
  794. */
  795. private function getRawMediaDataById(string $type, string $id): array
  796. {
  797. $options = [
  798. 'query' => [
  799. 'include' => ($type === 'anime')
  800. ? 'categories,mappings,streamingLinks'
  801. : 'categories,mappings',
  802. ]
  803. ];
  804. $data = $this->getRequest("{$type}/{$id}", $options);
  805. if (empty($data['data']))
  806. {
  807. return [];
  808. }
  809. $baseData = $data['data']['attributes'];
  810. $baseData['id'] = $id;
  811. $baseData['included'] = $data['included'];
  812. return $baseData;
  813. }
  814. /**
  815. * Get media item by slug
  816. *
  817. * @param string $type
  818. * @param string $slug
  819. * @return array
  820. */
  821. private function getRawMediaData(string $type, string $slug): array
  822. {
  823. $options = [
  824. 'query' => [
  825. 'filter' => [
  826. 'slug' => $slug
  827. ],
  828. 'fields' => [
  829. 'characters' => 'slug,name,image'
  830. ],
  831. 'include' => ($type === 'anime')
  832. ? 'staff,staff.person,categories,mappings,streamingLinks,animeCharacters.character'
  833. : 'staff,staff.person,categories,mappings,mangaCharacters.character,castings.character',
  834. ]
  835. ];
  836. $data = $this->getRequest($type, $options);
  837. if (empty($data['data']))
  838. {
  839. return [];
  840. }
  841. $baseData = $data['data'][0]['attributes'];
  842. $baseData['id'] = $data['data'][0]['id'];
  843. $baseData['included'] = $data['included'];
  844. return $baseData;
  845. }
  846. }