From e4acf11151a4f2819653885b26df28e12f0eb569 Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Sun, 2 Oct 2022 23:00:45 -0400 Subject: [PATCH 01/45] Removed ': mixed' for php7.4 compatibility --- src/jc21/Movies/Movie.php | 4 ++-- src/jc21/Util/Duration.php | 2 +- src/jc21/Util/Location.php | 2 +- src/jc21/Util/Media.php | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/jc21/Movies/Movie.php b/src/jc21/Movies/Movie.php index 8844e9b..d8c329d 100644 --- a/src/jc21/Movies/Movie.php +++ b/src/jc21/Movies/Movie.php @@ -94,7 +94,7 @@ public function __construct() * * @return mixed */ - public function __get(string $var): mixed + public function __get(string $var) { if (isset($this->data[$var])) { return $this->data[$var]; @@ -219,7 +219,7 @@ public static function fromLibrary(array $library): Movie * * @return mixed */ - public function jsonSerialize(): mixed + public function jsonSerialize() { return $this->data; } diff --git a/src/jc21/Util/Duration.php b/src/jc21/Util/Duration.php index a140d48..63ce195 100644 --- a/src/jc21/Util/Duration.php +++ b/src/jc21/Util/Duration.php @@ -61,7 +61,7 @@ public function __toString(): string * * @return mixed */ - public function jsonSerialize(): mixed + public function jsonSerialize() { return (string) $this->duration; } diff --git a/src/jc21/Util/Location.php b/src/jc21/Util/Location.php index 4ff59f6..1644be0 100644 --- a/src/jc21/Util/Location.php +++ b/src/jc21/Util/Location.php @@ -53,7 +53,7 @@ public static function fromLibrary(array $location): self * * @return mixed */ - public function jsonSerialize(): mixed + public function jsonSerialize() { return $this->data; } diff --git a/src/jc21/Util/Media.php b/src/jc21/Util/Media.php index 499dfcc..ff6145c 100644 --- a/src/jc21/Util/Media.php +++ b/src/jc21/Util/Media.php @@ -44,7 +44,7 @@ class Media implements JsonSerializable * * @return mixed */ - public function __get(string $var): mixed + public function __get(string $var) { if (isset($this->data[$var])) { return $this->data[$var]; @@ -99,7 +99,7 @@ public static function fromLibrary(array $library): Media * * @return mixed */ - public function jsonSerialize(): mixed + public function jsonSerialize() { return $this->data; } From 0b933d96aaf6589a1314f058b54c2952143a7a96 Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Fri, 7 Oct 2022 12:41:41 -0400 Subject: [PATCH 02/45] Fix bug in checking for result type --- src/jc21/PlexApi.php | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/jc21/PlexApi.php b/src/jc21/PlexApi.php index 141acbd..454a6d8 100644 --- a/src/jc21/PlexApi.php +++ b/src/jc21/PlexApi.php @@ -237,10 +237,10 @@ public function getOnDeck(bool $returnCollection = false) return $results; } - $tag = 'Video'; - if (!isset($results[$tag]) && isset($results['Directory'])) { - $tag = 'Directory'; - } + $tag = (isset($results['Video']) ? 'Video' : null); + $tag = (isset($results['Directory']) ? 'Directory' : $tag); + + if (is_null($tag)): return false; endif; return ($returnCollection ? $this->array2collection($results[$tag]) : $results); } @@ -272,10 +272,10 @@ public function getLibrarySectionContents($sectionKey, bool $returnCollection = return $results; } - $tag = 'Video'; - if (!isset($results[$tag]) && isset($results['Directory'])) { - $tag = 'Directory'; - } + $tag = (isset($results['Video']) ? 'Video' : null); + $tag = (isset($results['Directory']) ? 'Directory' : $tag); + + if (is_null($tag)): return false; endif; return ($returnCollection ? $this->array2collection($results[$tag]) : $results); } @@ -335,10 +335,10 @@ public function getRecentlyAdded(bool $returnCollection = false) return $results; } - $tag = 'Video'; - if (!isset($results[$tag]) && isset($results['Directory'])) { - $tag = 'Directory'; - } + $tag = (isset($results['Video']) ? 'Video' : null); + $tag = (isset($results['Directory']) ? 'Directory' : $tag); + + if (is_null($tag)): return false; endif; return ($returnCollection ? $this->array2collection($results[$tag]) : $results); } @@ -372,10 +372,10 @@ public function search($query, bool $returnCollection = false) return $results; } - $tag = 'Video'; - if (!isset($results[$tag]) && isset($results['Directory'])) { - $tag = 'Directory'; - } + $tag = (isset($results['Video']) ? 'Video' : null); + $tag = (isset($results['Directory']) ? 'Directory' : $tag); + + if (is_null($tag)): return false; endif; return ($returnCollection ? $this->array2collection($results[$tag]) : $results); } @@ -397,10 +397,10 @@ public function filter(int $sectionKey, array $filter, bool $returnCollection = return $results; } - $tag = 'Video'; - if (!isset($results[$tag]) && isset($results['Directory'])) { - $tag = 'Directory'; - } + $tag = (isset($results['Video']) ? 'Video' : null); + $tag = (isset($results['Directory']) ? 'Directory' : $tag); + + if (is_null($tag)): return false; endif; return ($returnCollection ? $this->array2collection($results[$tag]) : $results); } From 2549a6403b96da60c2690af9b6a45e4da66141a7 Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Sat, 8 Oct 2022 10:21:31 -0400 Subject: [PATCH 03/45] Added dev phpunit for unit testing --- composer.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 4687322..d3e7f94 100644 --- a/composer.json +++ b/composer.json @@ -21,11 +21,12 @@ }, "require-dev": { "victorjonsson/markdowndocs": "dev-master", - "symfony/dotenv": "6.2.x-dev" + "symfony/dotenv": "6.2.x-dev", + "phpunit/phpunit": "^9" }, "autoload": { "psr-0": { "jc21": "src" } } -} \ No newline at end of file +} From d57e4ac72d82c15b544253cb64891b2d380ca596 Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Sat, 8 Oct 2022 10:22:15 -0400 Subject: [PATCH 04/45] Added for unit testing --- phpunit.xml | 19 ++++ tests/PlexApiTest.php | 228 ++++++++++++++++++++++++++++++++++++++++++ tests/bootstrap.php | 2 + 3 files changed, 249 insertions(+) create mode 100644 phpunit.xml create mode 100644 tests/PlexApiTest.php create mode 100644 tests/bootstrap.php diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..5faf4b2 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,19 @@ + + + + + src/ + + + + + tests/ + + + diff --git a/tests/PlexApiTest.php b/tests/PlexApiTest.php new file mode 100644 index 0000000..17516c1 --- /dev/null +++ b/tests/PlexApiTest.php @@ -0,0 +1,228 @@ +loadEnv($envfname); + + $this->api = null; + if(!$this->envCheck()) { + die; + } + + $this->api = new PlexApi($this->host, $this->port, $this->ssl); + if ($this->token) { + $this->api->setToken($this->token); + } else { + $this->api->setAuth($this->user, $this->password); + + throw new \Exception("Put this token in your .env file {$envfname} as 'PLEX_TOKEN={$this->api->getToken()}'"); + } + } + + private function envCheck() + { + $this->host = (isset($_ENV['PLEX_HOST']) ? $_ENV['PLEX_HOST'] : false); + $this->token = (isset($_ENV['PLEX_TOKEN']) ? $_ENV['PLEX_TOKEN'] : false); + $this->user = (isset($_ENV['PLEX_USER']) ? $_ENV['PLEX_USER'] : false); + $this->password = (isset($_ENV['PLEX_PASSWORD']) ? $_ENV['PLEX_PASSWORD'] : false); + $this->port = (isset($_ENV['PLEX_PORT']) ? $_ENV['PLEX_PORT'] : false); + $ret = true; + + if ($this->host === false) { + print("PLEX_HOST not found in .env file".PHP_EOL); + } + + if ($this->token === false && ($this->user === false || $this->password === false)) { + print("PLEX_TOKEN not found in .env file".PHP_EOL); + $ret = false; + } + + if (!isset($_ENV['SECTION_KEY']) || !is_numeric($_ENV['SECTION_KEY'])) { + print("SECTION_KEY not found or not INT in .env, populate with ID of library you want to test".PHP_EOL); + $ret = false; + } + + if (!isset($_ENV['ITEM_ID']) || !is_numeric($_ENV['ITEM_ID'])) { + print("ITEM_ID not found or not INT in .env".PHP_EOL); + $ret = false; + } + + if (!isset($_ENV['SEARCH_QUERY'])) { + print("SEARCH_QUERY not found in .env".PHP_EOL); + $ret = false; + } + + if (!isset($_ENV['FILTER_QUERY'])) { + print("FILTER_QUERY not found in .env, MUST be a title filter".PHP_EOL); + $ret = false; + } + + return $ret; + } + + public function testConnection() + { + $this->assertTrue(is_a($this->api, "jc21\PlexApi")); + } + + public function testGetSessions() + { + $this->assertArrayHasKey('size', $this->api->getSessions()); + } + + public function testOnDeck() + { + $od = $this->api->getOnDeck(); + $this->assertArrayHasKey('size', $od); + } + + public function testOnDeckReturnCollection() + { + $od = $this->api->getOnDeck(true); + $this->assertInstanceOf(ItemCollection::class, $od); + } + + public function testOnDeckHasItems() + { + $od = $this->api->getOnDeck(true); + $this->assertGreaterThan(0, $od->count()); + } + + public function testGetSections() + { + $sec = $this->api->getLibrarySections(); + $this->assertArrayHasKey('size', $sec); + + $this->assertGreaterThan(0, $sec['size']); + } + + public function testGetLibrarySectionContents() + { + $res = $this->api->getLibrarySectionContents($_ENV['SECTION_KEY']); + $this->assertArrayHasKey('size', $res); + + $this->assertGreaterThan(0, $res['size']); + } + + public function testGetLibrarySectionContentsAsCollection() + { + $res = $this->api->getLibrarySectionContents($_ENV['SECTION_KEY'], true); + $this->assertInstanceOf(ItemCollection::class, $res); + + $this->assertGreaterThan(0, $res->count()); + } + + public function testGetRecentlyAdded() + { + $res = $this->api->getRecentlyAdded(); + $this->assertIsArray($res); + + $this->assertArrayHasKey('size', $res); + + $this->assertGreaterThan(0, $res['size']); + } + + public function testGetMetadata() + { + $res = $this->api->getMetadata($_ENV['ITEM_ID']); + $this->assertIsArray($res); + $this->assertArrayHasKey('size', $res); + $this->assertGreaterThan(0, $res['size']); + } + + public function testSearch() + { + $res = $this->api->search($_ENV['SEARCH_QUERY']); + $this->assertIsArray($res); + $this->assertArrayHasKey('size', $res); + $this->assertGreaterThan(0, $res['size']); + } + + public function testSearchReturnCollection() + { + $res = $this->api->search($_ENV['SEARCH_QUERY'], true); + $this->assertInstanceOf(ItemCollection::class, $res); + $this->assertGreaterThan(0, $res->count()); + } + + public function testFilter() + { + $res = $this->api->filter($_ENV['SECTION_KEY'], ['title' => $_ENV['FILTER_QUERY']]); + $this->assertIsArray($res); + $this->assertArrayHasKey('size', $res); + $this->assertGreaterThan(0, $res['size']); + } + + public function testFilterWithFilterObject() + { + $filter = new Filter('title', $_ENV['FILTER_QUERY']); + $res = $this->api->filter($_ENV['SECTION_KEY'], [$filter]); + $this->assertIsArray($res); + $this->assertArrayHasKey('size', $res); + $this->assertGreaterThan(0, $res['size']); + } + + public function testFilterReturnCollection() + { + $res = $this->api->filter($_ENV['SECTION_KEY'], ['title' => $_ENV['FILTER_QUERY']], true); + $this->assertInstanceOf(ItemCollection::class, $res); + $this->assertGreaterThan(0, $res->count()); + } + + public function testFilterWithFilterObjectReturnCollection() + { + $filter = new Filter('title', $_ENV['FILTER_QUERY']); + $res = $this->api->filter($_ENV['SECTION_KEY'], [$filter], true); + $this->assertInstanceOf(ItemCollection::class, $res); + $this->assertGreaterThan(0, $res->count()); + } + + public function testGetMatches() + { + $res = $this->api->getMatches($_ENV['ITEM_ID']); + $this->assertIsArray($res); + $this->assertArrayHasKey('size', $res); + $this->assertGreaterThan(0, $res['size']); + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..9dd56d6 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,2 @@ + Date: Sat, 8 Oct 2022 10:23:47 -0400 Subject: [PATCH 05/45] Add IMDB movie agent const Simplified result checking --- src/jc21/PlexApi.php | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/src/jc21/PlexApi.php b/src/jc21/PlexApi.php index 454a6d8..01c675f 100644 --- a/src/jc21/PlexApi.php +++ b/src/jc21/PlexApi.php @@ -36,6 +36,7 @@ class PlexApi // Plex agents public const PLEX_AGENT_NONE = 'com.plexapp.agents.none'; public const PLEX_MOVIE_AGENT = 'tv.plex.agents.movie'; + public const PLEX_IMDB_MOVIE_AGENT = 'com.plexapp.agents.imdb'; public const PLEX_TV_AGENT = 'tv.plex.agents.series'; public const PLEX_MUSIC_AGENT = 'tv.plex.agents.music'; @@ -237,12 +238,14 @@ public function getOnDeck(bool $returnCollection = false) return $results; } + if (is_bool($results) || !$returnCollection): return $results; endif; + $tag = (isset($results['Video']) ? 'Video' : null); $tag = (isset($results['Directory']) ? 'Directory' : $tag); if (is_null($tag)): return false; endif; - return ($returnCollection ? $this->array2collection($results[$tag]) : $results); + return $this->array2collection($results[$tag]); } @@ -268,16 +271,14 @@ public function getLibrarySectionContents($sectionKey, bool $returnCollection = { $results = $this->call('/library/sections/' . $sectionKey . '/all'); - if (is_bool($results)) { - return $results; - } + if (is_bool($results) || !$returnCollection): return $results; endif; $tag = (isset($results['Video']) ? 'Video' : null); $tag = (isset($results['Directory']) ? 'Directory' : $tag); if (is_null($tag)): return false; endif; - return ($returnCollection ? $this->array2collection($results[$tag]) : $results); + return $this->array2collection($results[$tag]); } @@ -331,16 +332,14 @@ public function getRecentlyAdded(bool $returnCollection = false) { $results = $this->call('/library/recentlyAdded'); - if (is_bool($results)) { - return $results; - } + if (is_bool($results) || !$returnCollection): return $results; endif; $tag = (isset($results['Video']) ? 'Video' : null); $tag = (isset($results['Directory']) ? 'Directory' : $tag); if (is_null($tag)): return false; endif; - return ($returnCollection ? $this->array2collection($results[$tag]) : $results); + return $this->array2collection($results[$tag]); } @@ -368,16 +367,14 @@ public function search($query, bool $returnCollection = false) { $results = $this->call('/search', ['query' => $query]); - if (is_bool($results)) { - return $results; - } + if (is_bool($results) || !$returnCollection): return $results; endif; $tag = (isset($results['Video']) ? 'Video' : null); $tag = (isset($results['Directory']) ? 'Directory' : $tag); if (is_null($tag)): return false; endif; - return ($returnCollection ? $this->array2collection($results[$tag]) : $results); + return $this->array2collection($results[$tag]); } /** @@ -393,16 +390,14 @@ public function filter(int $sectionKey, array $filter, bool $returnCollection = { $results = $this->call("/library/sections/{$sectionKey}/all", $filter); - if (is_bool($results)) { - return $results; - } + if (is_bool($results) || !$returnCollection): return $results; endif; $tag = (isset($results['Video']) ? 'Video' : null); $tag = (isset($results['Directory']) ? 'Directory' : $tag); if (is_null($tag)): return false; endif; - return ($returnCollection ? $this->array2collection($results[$tag]) : $results); + return $this->array2collection($results[$tag]); } /** From 267b0697a275de0ad4798867cd1438ddf027f089 Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Sat, 8 Oct 2022 10:46:32 -0400 Subject: [PATCH 06/45] Added checkResults method and refactored --- src/jc21/PlexApi.php | 69 ++++++++++++++++---------------------------- 1 file changed, 25 insertions(+), 44 deletions(-) diff --git a/src/jc21/PlexApi.php b/src/jc21/PlexApi.php index 01c675f..1cafb42 100644 --- a/src/jc21/PlexApi.php +++ b/src/jc21/PlexApi.php @@ -234,18 +234,7 @@ public function getOnDeck(bool $returnCollection = false) { $results = $this->call('/library/onDeck'); - if (is_bool($results)) { - return $results; - } - - if (is_bool($results) || !$returnCollection): return $results; endif; - - $tag = (isset($results['Video']) ? 'Video' : null); - $tag = (isset($results['Directory']) ? 'Directory' : $tag); - - if (is_null($tag)): return false; endif; - - return $this->array2collection($results[$tag]); + return $this->checkResults($results, $returnCollection); } @@ -271,14 +260,7 @@ public function getLibrarySectionContents($sectionKey, bool $returnCollection = { $results = $this->call('/library/sections/' . $sectionKey . '/all'); - if (is_bool($results) || !$returnCollection): return $results; endif; - - $tag = (isset($results['Video']) ? 'Video' : null); - $tag = (isset($results['Directory']) ? 'Directory' : $tag); - - if (is_null($tag)): return false; endif; - - return $this->array2collection($results[$tag]); + return $this->checkResults($results, $returnCollection); } @@ -332,14 +314,7 @@ public function getRecentlyAdded(bool $returnCollection = false) { $results = $this->call('/library/recentlyAdded'); - if (is_bool($results) || !$returnCollection): return $results; endif; - - $tag = (isset($results['Video']) ? 'Video' : null); - $tag = (isset($results['Directory']) ? 'Directory' : $tag); - - if (is_null($tag)): return false; endif; - - return $this->array2collection($results[$tag]); + return $this->checkResults($results, $returnCollection); } @@ -367,14 +342,7 @@ public function search($query, bool $returnCollection = false) { $results = $this->call('/search', ['query' => $query]); - if (is_bool($results) || !$returnCollection): return $results; endif; - - $tag = (isset($results['Video']) ? 'Video' : null); - $tag = (isset($results['Directory']) ? 'Directory' : $tag); - - if (is_null($tag)): return false; endif; - - return $this->array2collection($results[$tag]); + return $this->checkResults($results, $returnCollection); } /** @@ -390,14 +358,7 @@ public function filter(int $sectionKey, array $filter, bool $returnCollection = { $results = $this->call("/library/sections/{$sectionKey}/all", $filter); - if (is_bool($results) || !$returnCollection): return $results; endif; - - $tag = (isset($results['Video']) ? 'Video' : null); - $tag = (isset($results['Directory']) ? 'Directory' : $tag); - - if (is_null($tag)): return false; endif; - - return $this->array2collection($results[$tag]); + return $this->checkResults($results, $returnCollection); } /** @@ -718,4 +679,24 @@ protected static function normalizeSimpleXML($obj, &$result) $result = $data; } } + + /** + * Method to check the results for what is expected + * + * @param array $results + * @param bool $returnCollection + * + * @return ItemCollection|bool|array + */ + private function checkResults($results, bool $returnCollection) + { + if (is_bool($results) || !$returnCollection): return $results; endif; + + $tag = (isset($results['Video']) ? 'Video' : null); + $tag = (isset($results['Directory']) ? 'Directory' : $tag); + + if (is_null($tag)): return false; endif; + + return $this->array2collection($results[$tag]); + } } From 886402c0ff0aba2f5f55fd0cf314ca84c4094c97 Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Sat, 8 Oct 2022 10:47:25 -0400 Subject: [PATCH 07/45] Added SSL bool and docs --- tests/PlexApiTest.php | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/tests/PlexApiTest.php b/tests/PlexApiTest.php index 17516c1..ecdb834 100644 --- a/tests/PlexApiTest.php +++ b/tests/PlexApiTest.php @@ -2,13 +2,12 @@ namespace jc21\PlexApi\Tests; -use InvalidArgumentException; -use jc21\Collections\ItemCollection; use PHPUnit\Framework\TestCase; use Symfony\Component\Dotenv\Dotenv; use jc21\PlexApi; use jc21\Util\Filter; +use jc21\Collections\ItemCollection; /** * @coversDefaultClass PlexApi @@ -18,17 +17,51 @@ class TestPlexApi extends TestCase /** * Api to check + * + * @var PlexApi */ private ?PlexApi $api; + /** + * Plex.tv username + * + * @var string + */ private string $user; + /** + * Plex.tv password + * + * @var string + */ private string $password; + /** + * Local Plex server + * + * @var string + */ private string $host; + /** + * Port to connect to Plex through + * + * @var int + */ private int $port; + /** + * Connect to Plex through SSL + * + * @var bool + */ + private bool $ssl; + + /** + * Plex token for authentication + * + * @var string + */ private string $token; /** @@ -67,6 +100,7 @@ private function envCheck() $this->user = (isset($_ENV['PLEX_USER']) ? $_ENV['PLEX_USER'] : false); $this->password = (isset($_ENV['PLEX_PASSWORD']) ? $_ENV['PLEX_PASSWORD'] : false); $this->port = (isset($_ENV['PLEX_PORT']) ? $_ENV['PLEX_PORT'] : false); + $this->ssl = (isset($_ENV['PLEX_SSL']) ? (bool) $_ENV['PLEX_SSL'] : false); $ret = true; if ($this->host === false) { From 8316d6b1955fe21921d7a3707bc1e857c002da2e Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Thu, 13 Oct 2022 18:20:57 -0400 Subject: [PATCH 08/45] Add .env and .cache files to ignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 624851a..ce491e0 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ .idea vendor composer.lock -.env \ No newline at end of file +*.env +*.cache \ No newline at end of file From 95d4076cbff62ea5c5f43aca1aaadb2dc8b13c67 Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Thu, 13 Oct 2022 18:21:21 -0400 Subject: [PATCH 09/45] Add composer script for testing --- composer.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index d3e7f94..0553f25 100644 --- a/composer.json +++ b/composer.json @@ -28,5 +28,8 @@ "psr-0": { "jc21": "src" } + }, + "scripts": { + "test": "php vendor/bin/phpunit" } -} +} \ No newline at end of file From 7c137c6b04776beb1eb9bc18674c2e0005a82899 Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Thu, 13 Oct 2022 18:21:39 -0400 Subject: [PATCH 10/45] Cleanup --- phpunit.xml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/phpunit.xml b/phpunit.xml index 5faf4b2..162742c 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -2,10 +2,8 @@ + colors="true" verbose="true" cacheResult="true" + xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"> src/ From 1e36879e140b9dc0a2b6b04873cb9cfa42b2e9aa Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Thu, 13 Oct 2022 18:22:47 -0400 Subject: [PATCH 11/45] formatting --- src/jc21/PlexApi.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/jc21/PlexApi.php b/src/jc21/PlexApi.php index 1cafb42..0c26f13 100644 --- a/src/jc21/PlexApi.php +++ b/src/jc21/PlexApi.php @@ -5,7 +5,6 @@ use jc21\Collections\ItemCollection; use jc21\Movies\Movie; use jc21\TV\Show; -use jc21\Util\Item; /** * Plex API Class - Communicate with your Plex Media Server. @@ -682,20 +681,22 @@ protected static function normalizeSimpleXML($obj, &$result) /** * Method to check the results for what is expected - * + * * @param array $results * @param bool $returnCollection - * + * * @return ItemCollection|bool|array */ private function checkResults($results, bool $returnCollection) { - if (is_bool($results) || !$returnCollection): return $results; endif; + if (is_bool($results) || !$returnCollection): return $results; + endif; $tag = (isset($results['Video']) ? 'Video' : null); $tag = (isset($results['Directory']) ? 'Directory' : $tag); - if (is_null($tag)): return false; endif; + if (is_null($tag)): return false; + endif; return $this->array2collection($results[$tag]); } From 0958264446adb377c8b63d1d117b712cbf142407 Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Thu, 13 Oct 2022 18:23:11 -0400 Subject: [PATCH 12/45] formatting --- tests/PlexApiTest.php | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/PlexApiTest.php b/tests/PlexApiTest.php index ecdb834..61c81e0 100644 --- a/tests/PlexApiTest.php +++ b/tests/PlexApiTest.php @@ -17,49 +17,49 @@ class TestPlexApi extends TestCase /** * Api to check - * + * * @var PlexApi */ private ?PlexApi $api; /** * Plex.tv username - * + * * @var string */ private string $user; /** * Plex.tv password - * + * * @var string */ private string $password; /** * Local Plex server - * + * * @var string */ private string $host; /** * Port to connect to Plex through - * + * * @var int */ private int $port; /** * Connect to Plex through SSL - * + * * @var bool */ private bool $ssl; /** * Plex token for authentication - * + * * @var string */ private string $token; @@ -72,14 +72,14 @@ public function setUp(): void $dot = new Dotenv(); $envfname = __DIR__.'/.env'; - if(!file_exists($envfname)) { + if (!file_exists($envfname)) { throw new \InvalidArgumentException(sprintf('%s does not exist', $envfname)); } $dot->loadEnv($envfname); $this->api = null; - if(!$this->envCheck()) { + if (!$this->envCheck()) { die; } @@ -93,6 +93,11 @@ public function setUp(): void } } + /** + * Helper method to check available environment variables + * + * @return bool + */ private function envCheck() { $this->host = (isset($_ENV['PLEX_HOST']) ? $_ENV['PLEX_HOST'] : false); @@ -167,7 +172,6 @@ public function testGetSections() { $sec = $this->api->getLibrarySections(); $this->assertArrayHasKey('size', $sec); - $this->assertGreaterThan(0, $sec['size']); } @@ -175,7 +179,6 @@ public function testGetLibrarySectionContents() { $res = $this->api->getLibrarySectionContents($_ENV['SECTION_KEY']); $this->assertArrayHasKey('size', $res); - $this->assertGreaterThan(0, $res['size']); } @@ -183,7 +186,6 @@ public function testGetLibrarySectionContentsAsCollection() { $res = $this->api->getLibrarySectionContents($_ENV['SECTION_KEY'], true); $this->assertInstanceOf(ItemCollection::class, $res); - $this->assertGreaterThan(0, $res->count()); } @@ -191,9 +193,7 @@ public function testGetRecentlyAdded() { $res = $this->api->getRecentlyAdded(); $this->assertIsArray($res); - $this->assertArrayHasKey('size', $res); - $this->assertGreaterThan(0, $res['size']); } From 7fb8e1b07dcf16fe1db8c17b2dca8964918c6fda Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Thu, 13 Oct 2022 18:23:27 -0400 Subject: [PATCH 13/45] formatting --- docs/PlexApi.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/PlexApi.md b/docs/PlexApi.md index e015186..2ae3759 100644 --- a/docs/PlexApi.md +++ b/docs/PlexApi.md @@ -4,7 +4,7 @@ | Visibility | Function (parameters,...): return | |:-----------|:---------| -| public | __construct(string $host=`'127.0.0.1'`, mixed/int $port=32400, bool $ssl=false) : void
Instantiate the class with your Host/Port | +| public | __construct(string $host = `'127.0.0.1'`, mixed/int $port = 32400, bool $ssl = false) : void
Instantiate the class with your Host/Port | | public | getBaseInfo() : array\|bool
Get Plex Server basic info | | public | getLastCallStats() : array
Get last curl stats, for debugging purposes | | public | getLibrarySections() : array\|bool
Get Library Sections ie Movies, TV Shows etc | From c00326373f34ee5301e253cf14aaa6df1e7a1ffb Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Thu, 20 Oct 2022 20:40:40 -0400 Subject: [PATCH 14/45] Add section and item list --- composer.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 0553f25..e71c840 100644 --- a/composer.json +++ b/composer.json @@ -30,6 +30,8 @@ } }, "scripts": { - "test": "php vendor/bin/phpunit" + "test": "php vendor/bin/phpunit", + "sections": "php tests/SectionList.php", + "items": "php tests/ItemList.php" } } \ No newline at end of file From 3ec7e0fa53e3cadc2d64707e7ea43e5ddbe0ce73 Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Thu, 20 Oct 2022 20:44:10 -0400 Subject: [PATCH 15/45] Limit tests to files ending in "Test.php" --- phpunit.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpunit.xml b/phpunit.xml index 162742c..1877e9e 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -11,7 +11,7 @@
- tests/ + tests/
From 41917f71cf9fc091f43915947ef090a63f0ed1f2 Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Thu, 20 Oct 2022 21:13:54 -0400 Subject: [PATCH 16/45] Added array2object method and refactored array2collection Added param to return metadata as object --- src/jc21/PlexApi.php | 67 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 57 insertions(+), 10 deletions(-) diff --git a/src/jc21/PlexApi.php b/src/jc21/PlexApi.php index 0c26f13..bb3a1f9 100644 --- a/src/jc21/PlexApi.php +++ b/src/jc21/PlexApi.php @@ -4,14 +4,19 @@ use jc21\Collections\ItemCollection; use jc21\Movies\Movie; +use jc21\Music\Artist; +use jc21\Music\Album; +use jc21\Music\Track; use jc21\TV\Show; +use jc21\TV\Season; +use jc21\TV\Episode; /** * Plex API Class - Communicate with your Plex Media Server. * * @license BSD * @author Jamie Curnow - * @version 1.3 + * @version 2.1 * * @example * @@ -25,7 +30,7 @@ class PlexApi { - public const VERSION = '1.3'; + public const VERSION = '2.1'; const GET = 'GET'; const POST = 'POST'; @@ -321,11 +326,20 @@ public function getRecentlyAdded(bool $returnCollection = false) * Get Metadata for an Item * * @param int $item - * @return array|bool + * @param bool $returnObject + * @return array|bool|object */ - public function getMetadata($item) + public function getMetadata($item, bool $returnObject = false) { - return $this->call('/library/metadata/' . (int) $item); + $res = $this->call('/library/metadata/' . (int) $item); + if (!$returnObject): return $res; + endif; + + $tag = (isset($res['Video']) ? 'Video' : null); + $tag = (isset($res['Directory']) ? 'Directory' : $tag); + + $ret = $this->array2object($res[$tag]); + return $ret; } @@ -640,18 +654,51 @@ public static function array2collection($array) continue; } - if ($a['type'] == 'movie') { - $i = Movie::fromLibrary($a); - } elseif ($a['type'] == 'show') { - $i = Show::fromLibrary($a); - } else { + $i = self::array2object($a); + + if (is_null($i)) { continue; } + $ic->addData($i); } return $ic; } + /** + * Method to convert a returned array from the API to an specific object + * + * @param array $arr + * + * @return Show|Season|Episode|Artist|Album|Track|Movie + */ + public static function array2object(array $arr) + { + if (!isset($arr['type'])) { + return null; + } + + $ns = "jc21\\"; + if (in_array($arr['type'], ['show', 'season', 'episode'])) { + $ns .= "TV\\"; + } elseif (in_array($arr['type'], ['artist', 'album', 'track'])) { + $ns .= "Music\\"; + } elseif ($arr['type'] == 'movie') { + $ns .= "Movies\\"; + } + + if ($ns == "jc21\\") { + return null; + } + + $class = $ns.ucfirst($arr['type'])."::fromLibrary"; + if (!method_exists($ns.ucfirst($arr['type']), "fromLibrary")) { + return null; + } + $ret = $class($arr); + return $ret; + } + /** * normalizeSimpleXML * From bccd29040f63a809d195172308a6e724ae185fce Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Thu, 20 Oct 2022 21:14:41 -0400 Subject: [PATCH 17/45] Update getData method to allow for other returns --- src/jc21/Collections/ItemCollection.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/jc21/Collections/ItemCollection.php b/src/jc21/Collections/ItemCollection.php index f49a717..feedd32 100644 --- a/src/jc21/Collections/ItemCollection.php +++ b/src/jc21/Collections/ItemCollection.php @@ -44,7 +44,7 @@ public function count(): int * * @param int $position * - * @return null|Movie|Show + * @return null|Movie|Show|Season|Episode|Artist|Album|Track */ public function getData(int $position = 0) { @@ -57,6 +57,8 @@ public function getData(int $position = 0) /** * Method to add a Item to the collection + * + * @param Item $Item */ public function addData(Item $Item) { From 8845e9e1182b296da562958d55be4029699d4812 Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Thu, 20 Oct 2022 21:16:06 -0400 Subject: [PATCH 18/45] Implement Item interface Fix bug for director --- src/jc21/TV/Episode.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/jc21/TV/Episode.php b/src/jc21/TV/Episode.php index e5a0600..7c8e8b1 100644 --- a/src/jc21/TV/Episode.php +++ b/src/jc21/TV/Episode.php @@ -3,9 +3,10 @@ namespace jc21\TV; use DateTime; +use JsonSerializable; use jc21\Util\Media; use jc21\Util\Duration; -use JsonSerializable; +use jc21\Util\Item; /** * Class to store episode data @@ -42,7 +43,7 @@ * @property DateTime $updatedAt * @property Media $media */ -class Episode implements JsonSerializable +class Episode implements Item, JsonSerializable { /** * Class data @@ -124,7 +125,7 @@ public function __set(string $var, $val) * * @return Episode */ - public static function fromLibrary(array $lib) + public static function fromLibrary(array $lib): Episode { $me = new static(); $me->data = $lib; @@ -160,7 +161,7 @@ public static function fromLibrary(array $lib) } if (isset($lib['Director']) && is_array($lib['Director'])) { - if (count($lib['Director']) == 1) { + if (isset($lib['Director']['tag'])) { $me->data['director'][] = $lib['Director']['tag']; } else { foreach ($lib['Director'] as $d) { From 3620be0ca91e9757db9d51da428f6b302e983d2b Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Thu, 20 Oct 2022 21:17:17 -0400 Subject: [PATCH 19/45] Add more tests --- tests/PlexApiTest.php | 343 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 300 insertions(+), 43 deletions(-) diff --git a/tests/PlexApiTest.php b/tests/PlexApiTest.php index 61c81e0..c71dc89 100644 --- a/tests/PlexApiTest.php +++ b/tests/PlexApiTest.php @@ -8,6 +8,7 @@ use jc21\PlexApi; use jc21\Util\Filter; use jc21\Collections\ItemCollection; +use jc21\TV\Episode; /** * @coversDefaultClass PlexApi @@ -15,6 +16,27 @@ class TestPlexApi extends TestCase { + /** + * Constant message to output when test skipped + * + * @var string + */ + private const MOVIE_OFF_MSG = "Movie tests turned off"; + + /** + * Constant message to output when test skipped + * + * @var string + */ + private const TV_OFF_MSG = "TV tests turned off"; + + /** + * Constant message to output when test skipped + * + * @var string + */ + private const MUSIC_OFF_MSG = "Music tests turned off"; + /** * Api to check * @@ -64,11 +86,36 @@ class TestPlexApi extends TestCase */ private string $token; + /** + * Variable to decide if we are running movie tests + * + * @var bool + */ + private bool $runMovieTests; + + /** + * Variable to decide if we are running TV tests + * + * @var bool + */ + private bool $runTVTests; + + /** + * Variable to decide if we are running music tests + * + * @var bool + */ + private bool $runMusicTests; + /** * Setup function */ public function setUp(): void { + $this->runMovieTests = false; + $this->runTVTests = false; + $this->runMusicTests = false; + $dot = new Dotenv(); $envfname = __DIR__.'/.env'; @@ -89,8 +136,10 @@ public function setUp(): void } else { $this->api->setAuth($this->user, $this->password); - throw new \Exception("Put this token in your .env file {$envfname} as 'PLEX_TOKEN={$this->api->getToken()}'"); + die("Put this token in your .env file {$envfname} as 'PLEX_TOKEN={$this->api->getToken()}' and then you can remove PLEX_USER and PLEX_PASSWORD if you like"); } + + $GLOBALS['client'] = $this->api; } /** @@ -101,10 +150,10 @@ public function setUp(): void private function envCheck() { $this->host = (isset($_ENV['PLEX_HOST']) ? $_ENV['PLEX_HOST'] : false); - $this->token = (isset($_ENV['PLEX_TOKEN']) ? $_ENV['PLEX_TOKEN'] : false); + $this->token = (isset($_ENV['PLEX_TOKEN']) ? $_ENV['PLEX_TOKEN'] : ''); $this->user = (isset($_ENV['PLEX_USER']) ? $_ENV['PLEX_USER'] : false); $this->password = (isset($_ENV['PLEX_PASSWORD']) ? $_ENV['PLEX_PASSWORD'] : false); - $this->port = (isset($_ENV['PLEX_PORT']) ? $_ENV['PLEX_PORT'] : false); + $this->port = (isset($_ENV['PLEX_PORT']) ? $_ENV['PLEX_PORT'] : 32400); $this->ssl = (isset($_ENV['PLEX_SSL']) ? (bool) $_ENV['PLEX_SSL'] : false); $ret = true; @@ -112,29 +161,76 @@ private function envCheck() print("PLEX_HOST not found in .env file".PHP_EOL); } - if ($this->token === false && ($this->user === false || $this->password === false)) { + if (empty($this->token) && ($this->user === false || $this->password === false)) { print("PLEX_TOKEN not found in .env file".PHP_EOL); $ret = false; } - if (!isset($_ENV['SECTION_KEY']) || !is_numeric($_ENV['SECTION_KEY'])) { - print("SECTION_KEY not found or not INT in .env, populate with ID of library you want to test".PHP_EOL); - $ret = false; - } + if (isset($_ENV['MOVIE_TESTS']) && ((bool) $_ENV['MOVIE_TESTS'])) { + $this->runMovieTests = true; - if (!isset($_ENV['ITEM_ID']) || !is_numeric($_ENV['ITEM_ID'])) { - print("ITEM_ID not found or not INT in .env".PHP_EOL); - $ret = false; + if (!isset($_ENV['MOVIE_SECTION_KEY']) || !is_numeric($_ENV['MOVIE_SECTION_KEY'])) { + print("MOVIE_SECTION_KEY not found or not INT in .env, populate with ID of library you want to test".PHP_EOL); + $ret = false; + } + + if (!isset($_ENV['MOVIE_ITEM_ID']) || !is_numeric($_ENV['MOVIE_ITEM_ID'])) { + print("MOVIE_ITEM_ID not found or not INT in .env".PHP_EOL); + $ret = false; + } + + if (!isset($_ENV['MOVIE_SEARCH_QUERY'])) { + print("MOVIE_SEARCH_QUERY not found in .env".PHP_EOL); + $ret = false; + } + + if (!isset($_ENV['MOVIE_FILTER_QUERY'])) { + print("MOVIE_FILTER_QUERY not found in .env, MUST be a title filter".PHP_EOL); + $ret = false; + } } - if (!isset($_ENV['SEARCH_QUERY'])) { - print("SEARCH_QUERY not found in .env".PHP_EOL); - $ret = false; + if (isset($_ENV['TV_TESTS']) && ((bool) $_ENV['TV_TESTS'])) { + $this->runTVTests = true; + + if (!isset($_ENV['TV_SECTION_KEY']) || !is_numeric($_ENV['TV_SECTION_KEY'])) { + print("TV_SECTION_KEY not found or not INT in .env, populate with ID of library you want to test".PHP_EOL); + $ret = false; + } + + if (!isset($_ENV['TV_ITEM_ID']) || !is_numeric($_ENV['TV_ITEM_ID'])) { + print("TV_ITEM_ID not found or not INT in .env".PHP_EOL); + $ret = false; + } + + if (!isset($_ENV['TV_SEARCH_QUERY'])) { + print("TV_SEARCH_QUERY not found in .env".PHP_EOL); + $ret = false; + } + + if (!isset($_ENV['TV_FILTER_QUERY'])) { + print("TV_FILTER_QUERY not found in .env, MUST be a title filter".PHP_EOL); + $ret = false; + } } - if (!isset($_ENV['FILTER_QUERY'])) { - print("FILTER_QUERY not found in .env, MUST be a title filter".PHP_EOL); - $ret = false; + if (isset($_ENV['MUSIC_TESTS']) && ((bool) $_ENV['MUSIC_TESTS'])) { + $this->runMusicTests = true; + + if (!isset($_ENV['MUSIC_SECTION_KEY']) || !is_numeric($_ENV['MUSIC_SECTION_KEY'])) { + print("MUSIC_SECTION_KEY not found in .env".PHP_EOL); + $ret = false; + } + + if (!isset($_ENV['MUSIC_SEARCH_QUERY'])) { + print("MUSIC_SEARCH_QUERY not found in .env".PHP_EOL); + $ret = false; + } + + if (!isset($_ENV['MUSIC_FILTER_QUERY'])) { + print("MUSIC_FILTER_QUERY not found in .env MUST be a title filter".PHP_EOL); + $ret = false; + } } return $ret; @@ -168,6 +264,14 @@ public function testOnDeckHasItems() $this->assertGreaterThan(0, $od->count()); } + public function testGetRecentlyAdded() + { + $res = $this->api->getRecentlyAdded(); + $this->assertIsArray($res); + $this->assertArrayHasKey('size', $res); + $this->assertGreaterThan(0, $res['size']); + } + public function testGetSections() { $sec = $this->api->getLibrarySections(); @@ -175,88 +279,241 @@ public function testGetSections() $this->assertGreaterThan(0, $sec['size']); } - public function testGetLibrarySectionContents() + public function testGetMovieLibrarySectionContents() { - $res = $this->api->getLibrarySectionContents($_ENV['SECTION_KEY']); + if (!$this->runMovieTests) { + $this->markTestSkipped(self::MOVIE_OFF_MSG); + return; + } + + $res = $this->api->getLibrarySectionContents($_ENV['MOVIE_SECTION_KEY']); $this->assertArrayHasKey('size', $res); $this->assertGreaterThan(0, $res['size']); } - public function testGetLibrarySectionContentsAsCollection() + public function testGetMovieLibrarySectionContentsAsCollection() { - $res = $this->api->getLibrarySectionContents($_ENV['SECTION_KEY'], true); + if (!$this->runMovieTests) { + $this->markTestSkipped(self::MOVIE_OFF_MSG); + return; + } + + $res = $this->api->getLibrarySectionContents($_ENV['MOVIE_SECTION_KEY'], true); $this->assertInstanceOf(ItemCollection::class, $res); $this->assertGreaterThan(0, $res->count()); } - public function testGetRecentlyAdded() + public function testGetMetadata() { - $res = $this->api->getRecentlyAdded(); + if (!$this->runMovieTests) { + $this->markTestSkipped(self::MOVIE_OFF_MSG); + return; + } + + $res = $this->api->getMetadata($_ENV['MOVIE_ITEM_ID']); $this->assertIsArray($res); $this->assertArrayHasKey('size', $res); $this->assertGreaterThan(0, $res['size']); } - public function testGetMetadata() + public function testMovieSearch() + { + if (!$this->runMovieTests) { + $this->markTestSkipped(self::MOVIE_OFF_MSG); + return; + } + + $res = $this->api->search($_ENV['MOVIE_SEARCH_QUERY']); + $this->assertIsArray($res); + $this->assertArrayHasKey('Video', $res); + $this->assertGreaterThan(0, count($res['Video'])); + } + + public function testMovieSearchReturnCollection() { - $res = $this->api->getMetadata($_ENV['ITEM_ID']); + if (!$this->runMovieTests) { + $this->markTestSkipped(self::MOVIE_OFF_MSG); + return; + } + + $res = $this->api->search($_ENV['MOVIE_SEARCH_QUERY'], true); + $this->assertInstanceOf(ItemCollection::class, $res); + $this->assertGreaterThan(0, $res->count()); + } + + public function testMovieFilterAsFilterArray() + { + if (!$this->runMovieTests) { + $this->markTestSkipped(self::MOVIE_OFF_MSG); + return; + } + + $res = $this->api->filter($_ENV['MOVIE_SECTION_KEY'], ['title' => $_ENV['MOVIE_FILTER_QUERY']]); $this->assertIsArray($res); $this->assertArrayHasKey('size', $res); $this->assertGreaterThan(0, $res['size']); } - public function testSearch() + public function testMovieFilterWithFilterObject() { - $res = $this->api->search($_ENV['SEARCH_QUERY']); + if (!$this->runMovieTests) { + $this->markTestSkipped(self::MOVIE_OFF_MSG); + return; + } + + $filter = new Filter('title', $_ENV['MOVIE_FILTER_QUERY']); + $res = $this->api->filter($_ENV['MOVIE_SECTION_KEY'], [$filter]); $this->assertIsArray($res); $this->assertArrayHasKey('size', $res); $this->assertGreaterThan(0, $res['size']); } - public function testSearchReturnCollection() + public function testMovieFilterReturnCollection() + { + if (!$this->runMovieTests) { + $this->markTestSkipped(self::MOVIE_OFF_MSG); + return; + } + + $res = $this->api->filter($_ENV['MOVIE_SECTION_KEY'], ['title' => $_ENV['MOVIE_FILTER_QUERY']], true); + $this->assertInstanceOf(ItemCollection::class, $res); + $this->assertGreaterThan(0, $res->count()); + } + + public function testMovieFilterWithFilterObjectReturnCollection() { - $res = $this->api->search($_ENV['SEARCH_QUERY'], true); + if (!$this->runMovieTests) { + $this->markTestSkipped(self::MOVIE_OFF_MSG); + return; + } + + $filter = new Filter('title', $_ENV['MOVIE_FILTER_QUERY']); + $res = $this->api->filter($_ENV['MOVIE_SECTION_KEY'], [$filter], true); $this->assertInstanceOf(ItemCollection::class, $res); $this->assertGreaterThan(0, $res->count()); } - public function testFilter() + public function testMovieGetMatches() { - $res = $this->api->filter($_ENV['SECTION_KEY'], ['title' => $_ENV['FILTER_QUERY']]); + if (!$this->runMovieTests) { + $this->markTestSkipped(self::MOVIE_OFF_MSG); + return; + } + + $res = $this->api->getMatches($_ENV['MOVIE_ITEM_ID']); $this->assertIsArray($res); $this->assertArrayHasKey('size', $res); $this->assertGreaterThan(0, $res['size']); } - public function testFilterWithFilterObject() + public function testGetTVLibrarySectionContents() { - $filter = new Filter('title', $_ENV['FILTER_QUERY']); - $res = $this->api->filter($_ENV['SECTION_KEY'], [$filter]); + if (!$this->runTVTests) { + $this->markTestSkipped(self::TV_OFF_MSG); + return; + } + + $res = $this->api->getLibrarySectionContents($_ENV['TV_SECTION_KEY']); $this->assertIsArray($res); $this->assertArrayHasKey('size', $res); $this->assertGreaterThan(0, $res['size']); } - public function testFilterReturnCollection() + public function testGetTVLibrarySectionContentsReturnCollection() { - $res = $this->api->filter($_ENV['SECTION_KEY'], ['title' => $_ENV['FILTER_QUERY']], true); + if (!$this->runTVTests) { + $this->markTestSkipped(self::TV_OFF_MSG); + return; + } + + $res = $this->api->getLibrarySectionContents($_ENV['TV_SECTION_KEY'], true); $this->assertInstanceOf(ItemCollection::class, $res); $this->assertGreaterThan(0, $res->count()); } - public function testFilterWithFilterObjectReturnCollection() + public function testGetTVItemMetadata() { - $filter = new Filter('title', $_ENV['FILTER_QUERY']); - $res = $this->api->filter($_ENV['SECTION_KEY'], [$filter], true); + if (!$this->runTVTests) { + $this->markTestSkipped(self::TV_OFF_MSG); + return; + } + + $res = $this->api->getMetadata($_ENV['TV_ITEM_ID']); + $this->assertIsArray($res); + $this->assertArrayHasKey('size', $res); + $this->assertGreaterThan(0, $res['size']); + } + + public function testGetTVItemMetadataAsObject() + { + if (!$this->runTVTests) { + $this->markTestSkipped(self::TV_OFF_MSG); + return; + } + + $res = $this->api->getMetadata($_ENV['TV_ITEM_ID'], true); + $this->assertInstanceOf(Episode::class, $res); + } + + public function testTVSearch() + { + if (!$this->runTVTests) { + $this->markTestSkipped(self::TV_OFF_MSG); + return; + } + + $res = $this->api->search($_ENV['TV_SEARCH_QUERY']); + $this->assertIsArray($res); + $this->assertArrayHasKey('Video', $res); + $this->assertGreaterThan(0, count($res['Video'])); + } + + public function testTVFilterWithFilterObjectReturnCollection() + { + if (!$this->runTVTests) { + $this->markTestSkipped(self::TV_OFF_MSG); + return; + } + + $filter = new Filter('title', $_ENV['TV_FILTER_QUERY']); + $res = $this->api->filter($_ENV['TV_SECTION_KEY'], [$filter], true); $this->assertInstanceOf(ItemCollection::class, $res); $this->assertGreaterThan(0, $res->count()); } - public function testGetMatches() + public function testGetMusic() { - $res = $this->api->getMatches($_ENV['ITEM_ID']); + if (!$this->runMusicTests) { + $this->markTestSkipped(self::MUSIC_OFF_MSG); + return; + } + + $res = $this->api->getLibrarySectionContents($_ENV['MUSIC_SECTION_KEY'], true); + $this->assertGreaterThan(0, $res->count()); + } + + public function testMusicSearch() + { + if (!$this->runMusicTests) { + $this->markTestSkipped(self::MUSIC_OFF_MSG); + return; + } + + $res = $this->api->search($_ENV['MUSIC_SEARCH_QUERY']); $this->assertIsArray($res); - $this->assertArrayHasKey('size', $res); - $this->assertGreaterThan(0, $res['size']); + $this->assertArrayHasKey('Directory', $res); + $this->assertGreaterThan(0, count($res['Directory'])); + } + + public function testMusicFilterAsObject() + { + if (!$this->runMusicTests) { + $this->markTestSkipped(self::MUSIC_OFF_MSG); + return; + } + + $filter = new Filter('title', $_ENV['MUSIC_FILTER_QUERY']); + $res = $this->api->filter($_ENV['MUSIC_SECTION_KEY'], [$filter], true); + $this->assertGreaterThan(0, $res->count()); } } From 7eac623b8840f7bea829743cc3e37a7f2ef4cafb Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Thu, 20 Oct 2022 21:18:32 -0400 Subject: [PATCH 20/45] Add SectionList and ItemList.php for testing --- tests/ItemList.php | 34 ++++++++++++++++++++++++++++++++++ tests/SectionList.php | 18 ++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 tests/ItemList.php create mode 100644 tests/SectionList.php diff --git a/tests/ItemList.php b/tests/ItemList.php new file mode 100644 index 0000000..dc99786 --- /dev/null +++ b/tests/ItemList.php @@ -0,0 +1,34 @@ +loadEnv('tests/.env'); + +$client = new PlexApi($_ENV['PLEX_HOST']); +$client->setToken($_ENV['PLEX_TOKEN']); + +print PHP_EOL."Select one of the ID's below to put in your .env file for the *_ITEM_ID value".PHP_EOL; + +if (isset($_ENV['MOVIE_TESTS']) && (bool) $_ENV['MOVIE_TESTS']) { + print PHP_EOL."List of 10 Movies".PHP_EOL; + $movieCollection = $client->getLibrarySectionContents($_ENV['MOVIE_SECTION_KEY'], true); + for ($x = 0; $x < 10; $x++) { + $movie = $movieCollection->getData($x); + print "{$movie->ratingKey}: {$movie->title}".PHP_EOL; + } + print PHP_EOL; +} + +if (isset($_ENV['TV_TESTS']) && (bool) $_ENV['TV_TESTS']) { + print "List of 10 TV shows".PHP_EOL; + $tvCollection = $client->getLibrarySectionContents($_ENV['TV_SECTION_KEY'], true); + for ($x = 0; $x < 10; $x++) { + $tv = $tvCollection->getData($x); + print "{$tv->ratingKey}: {$tv->title}".PHP_EOL; + } + print PHP_EOL; +} diff --git a/tests/SectionList.php b/tests/SectionList.php new file mode 100644 index 0000000..c8f0211 --- /dev/null +++ b/tests/SectionList.php @@ -0,0 +1,18 @@ +loadEnv("tests/.env"); + +$client = new PlexApi($_ENV['PLEX_HOST']); +$client->setToken($_ENV['PLEX_TOKEN']); +$result = $client->getLibrarySections(); + +foreach ($result['Directory'] as $section) { + // Output + print "{$section['key']}: {$section['title']} (type: {$section['type']})".PHP_EOL; +} From 2e5b8a517974b527c4ed165568429ab54343624f Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Sat, 22 Oct 2022 20:20:26 -0400 Subject: [PATCH 21/45] Add support for music libraries --- docs/Album.md | 44 +++++++++ docs/Artist.md | 33 +++++++ docs/Track.md | 43 +++++++++ src/jc21/Music/Album.php | 186 ++++++++++++++++++++++++++++++++++++++ src/jc21/Music/Artist.php | 164 +++++++++++++++++++++++++++++++++ src/jc21/Music/Track.php | 121 +++++++++++++++++++++++++ 6 files changed, 591 insertions(+) create mode 100644 docs/Album.md create mode 100644 docs/Artist.md create mode 100644 docs/Track.md create mode 100644 src/jc21/Music/Album.php create mode 100644 src/jc21/Music/Artist.php create mode 100644 src/jc21/Music/Track.php diff --git a/docs/Album.md b/docs/Album.md new file mode 100644 index 0000000..3cfe020 --- /dev/null +++ b/docs/Album.md @@ -0,0 +1,44 @@ +# Album + +The object to represent a music album + +## Property List + +| Data type | Property | Description | +| :-------- | :---------------------- | :-------------------------------------------------- | +| int | ratingKey | | +| int | parentRatingKey | | +| string | key | The key to get the details of the album | +| string | parentKey | The link back to the artist | +| string | guid | | +| string | parentGuid | | +| string | studio | The studio that produced the album | +| string | type | The media type `album` | +| string | title | The title of the album | +| string | titleSort | The title used in sorting the album in the UI | +| string | parentTitle | The name of the parent artist | +| string | summary | | +| string | rating | User rating | +| int | index | | +| int | viewCount | | +| int | skipCount | | +| int | year | The year the album was released | +| DateTime | lastVeiwedAt | Date/time the album was last played | +| DateTime | originallyAvailableAt | The date/time the album was released | +| DateTime | addedAt | Date/time the album was added to the library | +| DateTime | updatedAt | Date/time the album database entry was last changed | +| string | thumb | URL to thumbnail | +| string | parentThumb | URL to artist thumbnail | +| int | loudnessAnalysisVersion | | +| array | directory | | +| array | genre | Genre's of music on the album | + +## Function List + +| Visibility | Function (parameters,...): return | +| :------------ | :--------------------------------------------------------------------------------------------------------------------------------------- | +| public | __construct(): void
| +| public | __get(string $var): mixed
Magic getter | +| public | __set(string \$var, mixed $val): void
Magic setter | +| public | addTrack(Track $a): void | +| public static | fromLibrary(array $library): Album
Create a Album from the Plex API call return | diff --git a/docs/Artist.md b/docs/Artist.md new file mode 100644 index 0000000..e4e1840 --- /dev/null +++ b/docs/Artist.md @@ -0,0 +1,33 @@ +# Artist + +The object to represent a music artist + +## Property List + +| Data type | Property | Description | +| :-------- | :----------- | :-------------------------------------------------- | +| int | ratingKey | | +| string | key | The key to get the details of the artist | +| string | guid | | +| string | type | The media type `artist` | +| string | title | The artist's name | +| string | summary | | +| int | index | | +| int | viewCount | Number of times the artist details have been viewed | +| int | skipCount | | +| DateTime | lastViewedAt | Date/time somebody viewed this artist | +| DateTime | addedAt | Date/time this artist was added to the database | +| DateTime | updatedAt | Date/time this artist's database entry was updated | +| string | thumb | URL to thumbnail image | +| string | art | | +| array | genre | Genre's of music the artist has performed in | +| array | country | Country's the albums were recorded in | + +## Function List +| Visibility | Function (parameters,...): return | +| :------------ | :----------------------------------------------------------------------------------------------------------------------------------------- | +| public | __construct(): void
| +| public | __get(string $var): mixed
Magic getter | +| public | __set(string \$var, mixed $val): void
Magic setter | +| public | addAlbum(Album $a): void | +| public static | fromLibrary(array $library): Artist
Create a Artist from the Plex API call return | diff --git a/docs/Track.md b/docs/Track.md new file mode 100644 index 0000000..aad5e86 --- /dev/null +++ b/docs/Track.md @@ -0,0 +1,43 @@ +# Track + +The object to represent a music track + +## Property List + +| Data type | Property | Description | +| :-------- | :------------------- | :------------------------------------------------ | +| int | ratingKey | | +| int | parentRatingKey | | +| int | grandparentRatingKey | | +| string | key | The key to get the track details | +| string | parentKey | The key to get the album details | +| string | grandparentKey | The key to get the artist details | +| string | guid | | +| string | parentGuid | | +| string | grandparentGuid | | +| string | type | The media type `track` | +| string | title | The track title | +| string | parentTitle | The album title | +| string | grandparentTitle | The artist name | +| string | parentStudio | The publishing album studio | +| string | summary | | +| int | index | | +| int | parentIndex | | +| int | ratingCount | | +| int | parentYear | The release year | +| string | thumb | | +| string | parentThumb | | +| string | grandparentThumb | | +| Duration | duration | | +| DateTime | addedAt | The date/time the track was added to the database | +| DateTime | updatedAt | The date/time of track was last updated | +| Media | media | The details of the media file itself | + +## Function List + +| Visibility | Function (parameters,...): return | +| :------------ | :--------------------------------------------------------------------------------------------------------------------------------------- | +| public | __construct(): void
| +| public | __get(string $var): mixed
Magic getter | +| public | __set(string \$var, mixed $val): void
Magic setter | +| public static | fromLibrary(array $library): Album
Create a Album from the Plex API call return | diff --git a/src/jc21/Music/Album.php b/src/jc21/Music/Album.php new file mode 100644 index 0000000..7f27a08 --- /dev/null +++ b/src/jc21/Music/Album.php @@ -0,0 +1,186 @@ +tracks = []; + } + + /** + * Magic getter + * + * @param string $var + * + * @return mixed + */ + public function __get(string $var) + { + if (isset($this->data[$var])) { + return $this->data[$var]; + } + return null; + } + + /** + * Magic setter + * + * @param string $var + * @param mixed $val + */ + public function __set(string $var, $val) + { + $this->data[$var] = $val; + } + + /** + * Add a track to the library + * + * @param Track $t + */ + public function addTrack(Track $t) + { + $this->tracks[] = $t; + } + + /** + * Method to create an object from Plex data + * + * @param array $lib + * + * @return Album + */ + public static function fromLibrary(array $lib): Album + { + if (!isset($GLOBALS['client'])) { + throw new Exception('PlexApi client `$client` not available'); + } + global $client; + + $me = new static(); + $me->data = $lib; + + if (isset($lib['lastViewedAt'])) { + $lastViewedAt = new DateTime(); + $lastViewedAt->setTimestamp($lib['lastViewedAt']); + $me->lastViewedAt = $lastViewedAt; + } + + if (isset($lib['addedAt'])) { + $addedAt = new DateTime(); + $addedAt->setTimestamp($lib['addedAt']); + $me->addedAt = $addedAt; + } + + if (isset($lib['updatedAt'])) { + $updatedAt = new DateTime(); + $updatedAt->setTimestamp($lib['updatedAt']); + $me->updatedAt = $updatedAt; + } + + if (isset($lib['originallyAvailableAt'])) { + $me->originallyAvailableAt = new DateTime($lib['originallyAvailableAt']); + } + + if (isset($lib['Genre']) && is_array($lib['Genre'])) { + if (count($lib['Genre']) == 1) { + $me->data['genre'][] = $lib['Genre']['tag']; + } else { + foreach ($lib['Genre'] as $g) { + $me->data['genre'][] = $g['tag']; + } + } + unset($me->data['Genre']); + } + + if (isset($lib['Director']) && is_array($lib['Director'])) { + if (count($lib['Director']) == 1) { + $me->data['director'][] = $lib['Director']['tag']; + } else { + foreach ($lib['Director'] as $d) { + $me->data['director'][] = $d['tag']; + } + } + unset($me->data['Director']); + } + + $res = $client->call($me->key); + if (isset($res['Track']) && is_array($res['Track']) && count($res['Track'])) { + if (isset($res['Track'][0])) { + foreach ($res['Track'] as $t) { + $track = Track::fromLibrary($t); + $me->addTrack($track); + } + } else { + $track = Track::fromLibrary($res['Track']); + $me->addTrack($track); + } + } + + return $me; + } + + /** + * Method to serialize the object + * + * @return array + */ + public function jsonSerialize() + { + return $this->data; + } +} diff --git a/src/jc21/Music/Artist.php b/src/jc21/Music/Artist.php new file mode 100644 index 0000000..7bde8f0 --- /dev/null +++ b/src/jc21/Music/Artist.php @@ -0,0 +1,164 @@ +albums = []; + } + + /** + * Magic getter method + * + * @param string $var + * + * @return mixed + */ + public function __get(string $var) + { + if (isset($this->data[$var])) { + return $this->data[$var]; + } + + return null; + } + + /** + * Magic setter method + * + * @param string $var + * @param mixed $val + */ + public function __set(string $var, $val) + { + $this->data[$var] = $val; + } + + /** + * Method to add album + * + * @param Album $a + */ + public function addAlbum(Album $a) + { + $this->albums[] = $a; + } + + /** + * Method to create an object from a library + * + * @param array $lib + * + * @return Artist + */ + public static function fromLibrary(array $lib): Artist + { + if (!isset($GLOBALS['client'])) { + throw new Exception('PlexApi client `$client` not available'); + } + global $client; + + $me = new static(); + $me->data = $lib; + + if (isset($lib['lastViewedAt'])) { + $lastViewedAt = new DateTime(); + $lastViewedAt->setTimestamp($lib['lastViewedAt']); + $me->lastViewedAt = $lastViewedAt; + } + + if (isset($lib['addedAt'])) { + $addedAt = new DateTime(); + $addedAt->setTimestamp($lib['addedAt']); + $me->addedAt = $addedAt; + } + + if (isset($lib['updatedAt'])) { + $updatedAt = new DateTime(); + $updatedAt->setTimestamp($lib['updatedAt']); + $me->updatedAt = $updatedAt; + } + + if (isset($lib['Genre']) && is_array($lib['Genre'])) { + if (count($lib['Genre']) == 1) { + $me->data['genre'][] = $lib['Genre']['tag']; + } else { + foreach ($lib['Genre'] as $g) { + $me->data['genre'][] = $g['tag']; + } + } + unset($me->data['Genre']); + } + + unset($me->data['Country']); + + $res = $client->call($me->key); + if (isset($res['Directory']) && is_array($res['Directory']) && count($res['Directory'])) { + if (isset($res['Directory'][0])) { + foreach ($res['Directory'] as $a) { + $a = Album::fromLibrary($a); + $me->addAlbum($a); + } + } else { + $a = Album::fromLibrary($res['Directory']); + $me->addAlbum($a); + } + } + + return $me; + } + + /** + * Serialize the object + * + * @return array + */ + public function jsonSerialize() + { + return $this->data; + } +} diff --git a/src/jc21/Music/Track.php b/src/jc21/Music/Track.php new file mode 100644 index 0000000..6e118df --- /dev/null +++ b/src/jc21/Music/Track.php @@ -0,0 +1,121 @@ +data[$var])) { + return $this->data[$var]; + } + return null; + } + + /** + * Magic setter + * + * @param string $var + * @param mixed $val + */ + public function __set(string $var, $val) + { + $this->data[$var] = $val; + } + + /** + * Method to create an object from the library + * + * @param array $lib + * + * @return Track + */ + public static function fromLibrary(array $lib): Track + { + $me = new static(); + $me->data = $lib; + + if (isset($lib['addedAt'])) { + $addedAt = new DateTime(); + $addedAt->setTimestamp($lib['addedAt']); + $me->addedAt = $addedAt; + } + + if (isset($lib['updatedAt'])) { + $updatedAt = new DateTime(); + $updatedAt->setTimestamp($lib['updatedAt']); + $me->updatedAt = $updatedAt; + } + + if (isset($lib['duration'])) { + $me->duration = new Duration($lib['duration']); + } + + if (isset($lib['Media'])) { + $me->media = Media::fromLibrary($lib['Media']); + unset($lib['Media']); + } + + return $me; + } + + /** + * Method to serialize the object + * + * @return array + */ + public function jsonSerialize() + { + return $this->data; + } +} From 1e5c8afb6c17f7877f1ae81bf55050e93aba1537 Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Sat, 22 Oct 2022 20:27:06 -0400 Subject: [PATCH 22/45] Update filter docs --- docs/Filter.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Filter.md b/docs/Filter.md index 8bc3940..bc4be43 100644 --- a/docs/Filter.md +++ b/docs/Filter.md @@ -1,11 +1,11 @@ # Filter -This class is specifically intended to filter a library section, but could be used for other purposes. Objects are `immutable` as there are no publicly available properties or setter methods. +This class is specifically intended to filter a library section, but could be used for other purposes. Objects are `immutable` as there are no publicly available properties or setter methods. **Filters matches case-insensitive results.** ## Function List | Visibility | Function (parameters,...): return | -|:-----------|:---------| +| :--------- | :-------------------------------- | | public | __construct(string $field, string $value, string $operator = `'='`): void | | public | __toString(): string
Convert the object to a string | From e4273efece5668625da756aeb0c25924a7a02ffe Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Sat, 22 Oct 2022 20:27:39 -0400 Subject: [PATCH 23/45] Add links to Testing and Music docs --- docs/Documentation.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/Documentation.md b/docs/Documentation.md index 12481d5..49af066 100644 --- a/docs/Documentation.md +++ b/docs/Documentation.md @@ -15,6 +15,9 @@ [Show](Show.md)
[Season](Season.md)
[Episode](Episode.md)
+[Artist](Artist.md)
+[Album](Album.md)
+[Track](Track.md)
### Utility Classes @@ -26,6 +29,10 @@ Item - only present for inheritance and `ItemCollection`
[Size](Size.md)
[Section](Section.md) +### Dev Testing + +[Tests](Tests.md) +
## PlexApi From 68d3cef07b7e5e63fb210e607dd7872101a2eec8 Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Sat, 22 Oct 2022 20:28:16 -0400 Subject: [PATCH 24/45] Add check for .env file is readable --- tests/PlexApiTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/PlexApiTest.php b/tests/PlexApiTest.php index c71dc89..9439779 100644 --- a/tests/PlexApiTest.php +++ b/tests/PlexApiTest.php @@ -119,8 +119,8 @@ public function setUp(): void $dot = new Dotenv(); $envfname = __DIR__.'/.env'; - if (!file_exists($envfname)) { - throw new \InvalidArgumentException(sprintf('%s does not exist', $envfname)); + if (!file_exists($envfname) || !is_readable($envfname)) { + throw new \InvalidArgumentException(sprintf('%s does not exist or is not readable', $envfname)); } $dot->loadEnv($envfname); From 37e15138c1097cd9cea4556ebe61a5c6c8baa77e Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Sat, 22 Oct 2022 20:31:59 -0400 Subject: [PATCH 25/45] Add doc for array2object method --- docs/PlexApi.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/PlexApi.md b/docs/PlexApi.md index 2ae3759..f5cd28b 100644 --- a/docs/PlexApi.md +++ b/docs/PlexApi.md @@ -2,8 +2,8 @@ ## Function List -| Visibility | Function (parameters,...): return | -|:-----------|:---------| +| Visibility | Function (parameters,...): return | +| :--------------- | :---------------- | | public | __construct(string $host = `'127.0.0.1'`, mixed/int $port = 32400, bool $ssl = false) : void
Instantiate the class with your Host/Port | | public | getBaseInfo() : array\|bool
Get Plex Server basic info | | public | getLastCallStats() : array
Get last curl stats, for debugging purposes | @@ -31,4 +31,5 @@ | private | buildHttpQuery(array $query): string
Build http query string from array of `Filter` objects | | protected static | normalizeSimpleXML(mixed $obj, mixed $result) : void
normalizeSimpleXML | | protected static | xml2array(mixed $xml) : mixed
xml2array | -| public static | array2Collection(array $array): ItemCollection | +| public static | array2collection(array $array): ItemCollection | +| public static | array2object(array $array): Movie/Show/Season/Episode/Artist/Album/Track | From 09dacae58a4621dce17ea744f2807cf016c470b7 Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Sat, 22 Oct 2022 20:32:13 -0400 Subject: [PATCH 26/45] Add testing doc --- docs/Tests.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/Tests.md diff --git a/docs/Tests.md b/docs/Tests.md new file mode 100644 index 0000000..2a71515 --- /dev/null +++ b/docs/Tests.md @@ -0,0 +1,33 @@ +# Tests + +## Intro + +PHPUnit is added for development purposes to be able to perform unit tests and make sure that all functions operate as expected. To accomplish this an `.env` file needs to be created in the `/tests` folder with the following information: + +| **Env** | **Value** | **Description** | +| ---------------------- | -------------- | -------------------------------------------------------------------------- | +| **PLEX_HOST** | *ip\|hostname* | IP or host name of the Plex server | +| **PLEX_PORT** | *port* | Port to connect to Plex server (optional, defaults 32400) | +| **PLEX_USER** | *email* | Username to login to Plex.tv (only needed once) | +| **PLEX_PASSWORD** | *password* | Password to login to Plex.tv (only needed once) | +| **PLEX_SSL** | *0\|1* | Boolean to connect to Plex server over SSL | +| **MOVIE_TESTS** | *0\|1* | Boolean to conduct tests on movie library (optional) | +| **MOVIE_SECTION_KEY** | *int* | Integer of movie library (run `composer sections` to see list | +| **MOVIE_ITEM_ID** | *int* | Integer key of a specific movie to pull metadata for | +| **MOVIE_SEARCH_QUERY** | *string* | String query to search for in Movie library | +| **MOVIE_FILTER_QUERY** | *string* | String query to filter for in Movie library (must be a 'title') | +| **TV_TESTS** | *0\|1* | Boolean to conduct tests of TV library (optional) | +| **TV_SECTION_KEY** | *int* | Integer of TV library (run `composer sections` to see list) | +| **TV_ITEM_ID** | *int* | Integer key of a specific TV show, season, or episode to pull metadata for | +| **TV_SEARCH_QUERY** | *string* | String query to search for in TV library | +| **TV_FILTER_QUERY** | *string* | String query to filter for in TV library (must be 'title') | +| **MUSIC_TESTS** | *0\|1* | Boolean to conduct tests of Music library (optional) | +| **MUSIC_SECTION_KEY** | *int* | Integer of Music library (run `composer sections` to see list | +| **MUSIC_SEARCH_QUERY** | *string* | String query to search for in Music library | +| **MUSIC_FILTER_QUERY** | *string* | String query to filter for in Music library | + +The `PLEX_*` values are required. The username and password values can be deleted after the token is retrieved + +`*_TESTS` are optional, if they are not present, those tests will not be run. If they are present, then the similar ENV values are required. + +Once you have the PLEX_* values present you can run `composer sections` to retrieve the section keys for your libraries. Put in the section keys for the tests you want to run. After you've made the changes, run `composer test` to run php tests From 33db97207979a5a905e040c5b4d24c6e638bd8e1 Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Wed, 26 Oct 2022 09:53:16 -0400 Subject: [PATCH 27/45] Add method to get account data and add test --- src/jc21/PlexApi.php | 9 +++++++++ tests/PlexApiTest.php | 16 ++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/jc21/PlexApi.php b/src/jc21/PlexApi.php index bb3a1f9..3d8477f 100644 --- a/src/jc21/PlexApi.php +++ b/src/jc21/PlexApi.php @@ -226,6 +226,15 @@ public function getTranscodeSessions() return $this->call('/transcode/sessions'); } + /** + * Method to get plex account data + * + * @return array|bool + */ + public function getAccount() + { + return $this->call('/myplex/account'); + } /** * Get On Deck Info diff --git a/tests/PlexApiTest.php b/tests/PlexApiTest.php index 9439779..e995d7f 100644 --- a/tests/PlexApiTest.php +++ b/tests/PlexApiTest.php @@ -241,6 +241,22 @@ public function testConnection() $this->assertTrue(is_a($this->api, "jc21\PlexApi")); } + public function testGetBaseInfo() + { + $res = $this->api->getBaseInfo(); + $this->assertIsArray($res); + $this->assertArrayHasKey('size', $res); + $this->assertGreaterThan(0, $res['size']); + } + + public function testGetAccount() + { + $res = $this->api->getAccount(); + $this->assertIsArray($res); + $this->assertArrayHasKey('signInState', $res); + $this->assertEquals('ok', $res['signInState']); + } + public function testGetSessions() { $this->assertArrayHasKey('size', $this->api->getSessions()); From 14bf3d108ed421b59e44674d2c0d758ed93b57ee Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Tue, 1 Nov 2022 12:04:27 -0400 Subject: [PATCH 28/45] Add test to check for Collection Size --- tests/PlexApiTest.php | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/PlexApiTest.php b/tests/PlexApiTest.php index e995d7f..69b124b 100644 --- a/tests/PlexApiTest.php +++ b/tests/PlexApiTest.php @@ -8,6 +8,7 @@ use jc21\PlexApi; use jc21\Util\Filter; use jc21\Collections\ItemCollection; +use jc21\Section; use jc21\TV\Episode; /** @@ -295,6 +296,14 @@ public function testGetSections() $this->assertGreaterThan(0, $sec['size']); } + public function testGetSectionsAsObject() + { + $secs = $this->api->getLibrarySections(true); + $this->assertIsArray($secs); + $this->assertIsObject($secs[0]); + $this->assertInstanceOf(Section::class, $secs[0]); + } + public function testGetMovieLibrarySectionContents() { if (!$this->runMovieTests) { @@ -319,6 +328,19 @@ public function testGetMovieLibrarySectionContentsAsCollection() $this->assertGreaterThan(0, $res->count()); } + public function testGetMovieCollectionSize() + { + if (!$this->runMovieTests) { + $this->markTestSkipped(self::MOVIE_OFF_MSG); + return; + } + + $filter = new Filter('title', $_ENV['MOVIE_FILTER_QUERY']); + $res = $this->api->filter($_ENV['MOVIE_SECTION_KEY'], [$filter], true); + + $this->assertEquals($_ENV['MOVIE_FILTER_QUERY_SIZE'], $res->size()->bytes()); + } + public function testGetMetadata() { if (!$this->runMovieTests) { From d57802f8cd6dd155af3e608d633c847c06686c8b Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Tue, 1 Nov 2022 12:04:45 -0400 Subject: [PATCH 29/45] Add option to return size in TB --- src/jc21/Util/Size.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/jc21/Util/Size.php b/src/jc21/Util/Size.php index e333332..d39eb80 100644 --- a/src/jc21/Util/Size.php +++ b/src/jc21/Util/Size.php @@ -24,6 +24,16 @@ public function __construct(int $size) $this->size = $size; } + /** + * Returns the size in TB + * + * @return string + */ + public function TB() + { + return number_format($this->size / 1024 / 1024 / 1024 / 1024, 3); + } + /** * Return the size in GB * From 22167e9f6001db84b8ba4a8ff8bd01e9635cecaa Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Tue, 1 Nov 2022 12:04:59 -0400 Subject: [PATCH 30/45] Doc add property doc --- src/jc21/Util/Location.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/jc21/Util/Location.php b/src/jc21/Util/Location.php index 1644be0..74e2cb5 100644 --- a/src/jc21/Util/Location.php +++ b/src/jc21/Util/Location.php @@ -6,6 +6,9 @@ /** * Object to store Location info + * + * @property int $id + * @property string $path */ class Location implements JsonSerializable { From 552056f680052a88f07c99f46a003144ca1fb0c0 Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Tue, 1 Nov 2022 12:05:12 -0400 Subject: [PATCH 31/45] Add resolution as filter field --- src/jc21/Util/Filter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jc21/Util/Filter.php b/src/jc21/Util/Filter.php index 91f0860..79c97f3 100644 --- a/src/jc21/Util/Filter.php +++ b/src/jc21/Util/Filter.php @@ -43,7 +43,7 @@ public function __construct(string $field, string $value, string $operator = '=' throw new Exception("Invalid filter operator"); } - if (!in_array($field, ['title', 'rating', 'contentRating', 'year', 'studio'])) { + if (!in_array($field, ['title', 'rating', 'contentRating', 'year', 'studio', 'resolution'])) { throw new Exception("Invalid filter field"); } From 710e05fdd99b0e77e3d85a0b3b5c5dd125d91cb3 Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Tue, 1 Nov 2022 12:06:29 -0400 Subject: [PATCH 32/45] Converted episode property to ItemCollection --- src/jc21/TV/Season.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/jc21/TV/Season.php b/src/jc21/TV/Season.php index e11b2c6..b1f241e 100644 --- a/src/jc21/TV/Season.php +++ b/src/jc21/TV/Season.php @@ -3,9 +3,9 @@ namespace jc21\TV; use DateTime; -use jc21\PlexApi; use JsonSerializable; -use TypeError; +use jc21\Collections\ItemCollection; +use jc21\PlexApi; /** * To represent a season of a TV Show @@ -45,9 +45,9 @@ class Season implements JsonSerializable /** * Array to store episodes of a show in a season * - * @var array:Episode + * @var ItemCollection */ - private array $episodes; + private ItemCollection $episodes; /** * Constructor @@ -109,9 +109,9 @@ public function __set(string $var, $val) /** * Method to return the episodes * - * @return array + * @return ItemCollection */ - public function getEpisodes(): array + public function getChildren(): ItemCollection { return $this->episodes; } From 5b019c5a739dffe78c157926c3c650d03f9d695b Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Tue, 1 Nov 2022 12:06:59 -0400 Subject: [PATCH 33/45] Fixed error if only one Writer or Role --- src/jc21/TV/Episode.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jc21/TV/Episode.php b/src/jc21/TV/Episode.php index 7c8e8b1..555f0eb 100644 --- a/src/jc21/TV/Episode.php +++ b/src/jc21/TV/Episode.php @@ -172,7 +172,7 @@ public static function fromLibrary(array $lib): Episode } if (isset($lib['Writer']) && is_array($lib['Writer'])) { - if (count($lib['Writer']) == 1) { + if (isset($lib['Writer']['tag'])) { $me->data['writer'][] = $lib['Writer']['tag']; } else { foreach ($lib['Writer'] as $w) { @@ -183,7 +183,7 @@ public static function fromLibrary(array $lib): Episode } if (isset($lib['Role']) && is_array($lib['Role'])) { - if (count($lib['Role']) == 1) { + if (isset($lib['Role']['tag'])) { $me->data['role'][] = $lib['Role']['tag']; } else { foreach ($lib['Role'] as $r) { From 9ced51636e433b8cbca224cafd9950387c32a5ed Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Tue, 1 Nov 2022 12:28:25 -0400 Subject: [PATCH 34/45] Converted album array to ItemCollection --- src/jc21/Music/Artist.php | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/jc21/Music/Artist.php b/src/jc21/Music/Artist.php index 7bde8f0..8887328 100644 --- a/src/jc21/Music/Artist.php +++ b/src/jc21/Music/Artist.php @@ -6,6 +6,7 @@ use Exception; use JsonSerializable; use jc21\Util\Item; +use jc21\Collections\ItemCollection; /** * Artist @@ -39,16 +40,16 @@ class Artist implements Item, JsonSerializable /** * Array to store albums * - * @var array:Album + * @var ItemCollection */ - private array $albums; + private ItemCollection $albums; /** * Constructor */ public function __construct() { - $this->albums = []; + $this->albums = new ItemCollection(); } /** @@ -78,6 +79,16 @@ public function __set(string $var, $val) $this->data[$var] = $val; } + /** + * Method to retrieve the albums this artist has released + * + * @return ItemCollection + */ + public function getChildren(): ItemCollection + { + return $this->albums; + } + /** * Method to add album * @@ -85,7 +96,7 @@ public function __set(string $var, $val) */ public function addAlbum(Album $a) { - $this->albums[] = $a; + $this->albums->addData($a); } /** From 74f23bd6c8ce8c58e4b14bb38be360c3b005e575 Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Tue, 1 Nov 2022 12:29:42 -0400 Subject: [PATCH 35/45] Converted track array to ItemCollection --- src/jc21/Music/Album.php | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/jc21/Music/Album.php b/src/jc21/Music/Album.php index 7f27a08..90309db 100644 --- a/src/jc21/Music/Album.php +++ b/src/jc21/Music/Album.php @@ -5,6 +5,7 @@ use DateTime; use Exception; use JsonSerializable; +use jc21\Collections\ItemCollection; use jc21\Util\Item; /** @@ -49,16 +50,16 @@ class Album implements Item, JsonSerializable /** * Array to store track data * - * @var array + * @var ItemCollection */ - private array $tracks; + private ItemCollection $tracks; /** * Constructor */ public function __construct() { - $this->tracks = []; + $this->tracks = new ItemCollection(); } /** @@ -87,6 +88,16 @@ public function __set(string $var, $val) $this->data[$var] = $val; } + /** + * Method to retrieve tracks on this album + * + * @return ItemCollection + */ + public function getChildren(): ItemCollection + { + return $this->tracks; + } + /** * Add a track to the library * @@ -94,7 +105,7 @@ public function __set(string $var, $val) */ public function addTrack(Track $t) { - $this->tracks[] = $t; + $this->tracks->addData($t); } /** From 0173f9318076dd3a319ea1eaf676a5096c474c9d Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Tue, 1 Nov 2022 12:31:45 -0400 Subject: [PATCH 36/45] Add method to get size of all media in collection --- src/jc21/Collections/ItemCollection.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/jc21/Collections/ItemCollection.php b/src/jc21/Collections/ItemCollection.php index feedd32..d194516 100644 --- a/src/jc21/Collections/ItemCollection.php +++ b/src/jc21/Collections/ItemCollection.php @@ -8,6 +8,7 @@ use jc21\Movies\Movie; use jc21\TV\Show; use jc21\Iterators\ItemIterator; +use jc21\Util\Size; /** * Collection to store Items @@ -39,6 +40,26 @@ public function count(): int return count($this->collection); } + /** + * Get the size of the media in the collection + * + * @return Size + */ + public function size(): Size + { + $int = 0; + foreach ($this->collection as $item) { + if (method_exists($item, 'getChildren')) { + $item->getChildren()->size(); + } + /** @var Movie|Episode|Track $item */ + $int += (int) $item->media->size->bytes(); + } + $size = new Size($int); + + return $size; + } + /** * Method to get data at a position * From 26779282696eed55e0491c6404c9836ee5c73972 Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Tue, 1 Nov 2022 12:32:13 -0400 Subject: [PATCH 37/45] Allow return Library Sections as object --- src/jc21/PlexApi.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/jc21/PlexApi.php b/src/jc21/PlexApi.php index 3d8477f..a95859f 100644 --- a/src/jc21/PlexApi.php +++ b/src/jc21/PlexApi.php @@ -254,11 +254,23 @@ public function getOnDeck(bool $returnCollection = false) /** * Get Library Sections ie Movies, TV Shows etc * + * @param bool $returnObjects + * * @return array|bool */ - public function getLibrarySections() + public function getLibrarySections(bool $returnObjects = false) { - return $this->call('/library/sections'); + $res = $this->call('/library/sections'); + if (!$returnObjects): return $res; + endif; + + $ret = []; + foreach ($res['Directory'] as $s) { + $sec = Section::fromLibrary($s); + $ret[] = $sec; + } + + return $ret; } /** From cb9b06d66214c6a7e6a82c854f6ea914233b519e Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Tue, 1 Nov 2022 12:38:04 -0400 Subject: [PATCH 38/45] Update docs with getChildren method --- docs/Album.md | 1 + docs/Artist.md | 3 ++- docs/Season.md | 4 ++-- docs/Show.md | 4 ++-- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/Album.md b/docs/Album.md index 3cfe020..64c868c 100644 --- a/docs/Album.md +++ b/docs/Album.md @@ -40,5 +40,6 @@ The object to represent a music album | public | __construct(): void
| | public | __get(string $var): mixed
Magic getter | | public | __set(string \$var, mixed $val): void
Magic setter | +| public | getChildren(): ItemCollection:Track
Method to retrieve collection of tracks on this album | | public | addTrack(Track $a): void | | public static | fromLibrary(array $library): Album
Create a Album from the Plex API call return | diff --git a/docs/Artist.md b/docs/Artist.md index e4e1840..9259572 100644 --- a/docs/Artist.md +++ b/docs/Artist.md @@ -29,5 +29,6 @@ The object to represent a music artist | public | __construct(): void
| | public | __get(string $var): mixed
Magic getter | | public | __set(string \$var, mixed $val): void
Magic setter | -| public | addAlbum(Album $a): void | +| public | getChildren(): ItemCollection:Album
Method to retrieve all albums written by this artist | +| public | addAlbum(Album $a): void | | public static | fromLibrary(array $library): Artist
Create a Artist from the Plex API call return | diff --git a/docs/Season.md b/docs/Season.md index 9863fa4..72cbf80 100644 --- a/docs/Season.md +++ b/docs/Season.md @@ -26,7 +26,7 @@ This represents a single season within a show | int | viewedLeafCount | The number of times an episode in this season was viewed | | `DateTime` | addedAt | The date and time this season was added to the library | | `DateTime` | updatedAt | The date and time this season was last updated in the library | -| array:[Episode](Episode.md) | episodes | An array to store all the episodes in this season | +| `ItemCollection`:`Episode`(Episode.md) | episodes | An array to store all the episodes in this season | ## Function List @@ -35,6 +35,6 @@ This represents a single season within a show | public | __construct(): void
| | public | __get(string $var): mixed
Magic getter | | public | __set(string $var, mixed $val): void
Magic setter | -| public | getEpisodes(): array:Episodes
Method to get all episodes within this season | +| public | getChildren(): ItemCollection:Episodes
Method to get all episodes within this season | | public | addEpisode(Episode $episode): void
Method to add an episode to the season | | public static | fromLibrary(array $lib): Season
Method to create a season | diff --git a/docs/Show.md b/docs/Show.md index bb12a00..e7d359a 100644 --- a/docs/Show.md +++ b/docs/Show.md @@ -34,7 +34,7 @@ A TV Show | string | audienceRatingImage | | | array:string | genre | The genres the show is in | | array:string | role | The actor/actresses in the show | -| array:[Season](Season.md) | seasons | The array containing all the seasons | +| `ItemCollection`:`Season`(Season.md) | seasons | The array containing all the seasons | ## Function List @@ -43,6 +43,6 @@ A TV Show | public | __construct(): void
| | public | __get(string $var): mixed
Magic getter | | public | __set(string $var, mixed $val): void
Magic setter | -| public | getSeasons(): array:Season
Method to get the seasons | +| public | getChildren(): array:Season
Method to get the seasons | | public | addSeason(Season $season): bool
Method to add a season to the show | | public static | fromLibrary(array $lib): Show
Method to create a show from the Plex API call | From 8ffaa0b5ea96dc04fb1922646fda25b57045e796 Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Tue, 1 Nov 2022 12:38:38 -0400 Subject: [PATCH 39/45] Reformat media class list for better readability --- docs/Documentation.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/docs/Documentation.md b/docs/Documentation.md index 49af066..abb77ec 100644 --- a/docs/Documentation.md +++ b/docs/Documentation.md @@ -2,7 +2,7 @@ ### Main Class -[PlexApi](PlexApi.md)
+[PlexApi](PlexApi.md) ### Collection Classes @@ -11,13 +11,16 @@ ### Media Classes -[Movie](Movie.md)
-[Show](Show.md)
-[Season](Season.md)
-[Episode](Episode.md)
-[Artist](Artist.md)
-[Album](Album.md)
-[Track](Track.md)
+- Movie + - [Movie](Movie.md)
+- TV + - [Show](Show.md)
+ - [Season](Season.md)
+ - [Episode](Episode.md)
+- Music + - [Artist](Artist.md)
+ - [Album](Album.md)
+ - [Track](Track.md)
### Utility Classes From 75d077d3cf3b076c5323c0cc8e372dea9a39ef30 Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Tue, 1 Nov 2022 12:39:34 -0400 Subject: [PATCH 40/45] Fixed formatting for array2object method added doc for getAccount --- docs/PlexApi.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/PlexApi.md b/docs/PlexApi.md index f5cd28b..994311a 100644 --- a/docs/PlexApi.md +++ b/docs/PlexApi.md @@ -6,6 +6,7 @@ | :--------------- | :---------------- | | public | __construct(string $host = `'127.0.0.1'`, mixed/int $port = 32400, bool $ssl = false) : void
Instantiate the class with your Host/Port | | public | getBaseInfo() : array\|bool
Get Plex Server basic info | +| public | getAccount() : array\|bool
Get account info | | public | getLastCallStats() : array
Get last curl stats, for debugging purposes | | public | getLibrarySections() : array\|bool
Get Library Sections ie Movies, TV Shows etc | | public | getLibrarySectionContents(int $sectionKey, bool $returnCollection = `false`) : array\|ItemCollection\|bool
Get Library Section contents | @@ -32,4 +33,4 @@ | protected static | normalizeSimpleXML(mixed $obj, mixed $result) : void
normalizeSimpleXML | | protected static | xml2array(mixed $xml) : mixed
xml2array | | public static | array2collection(array $array): ItemCollection | -| public static | array2object(array $array): Movie/Show/Season/Episode/Artist/Album/Track | +| public static | array2object(array $array): Movie\|Show\|Season\|Episode\|Artist\|Album\|Track | From c720be6619118e0853955288addc7c7b1348fb1c Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Thu, 22 Dec 2022 23:27:05 -0500 Subject: [PATCH 41/45] Fixed error if multiple media files for the same movie --- src/jc21/Movies/Movie.php | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/jc21/Movies/Movie.php b/src/jc21/Movies/Movie.php index d8c329d..919ef75 100644 --- a/src/jc21/Movies/Movie.php +++ b/src/jc21/Movies/Movie.php @@ -78,7 +78,7 @@ public function __construct() 'updatedAt' => null, 'audienceRatingImage' => null, 'primaryExtraKey' => null, - 'media' => new Media(), + 'media' => null, 'genre' => [], 'director' => [], 'writer' => [], @@ -126,7 +126,9 @@ public static function fromLibrary(array $library): Movie $me = new static(); $me->data = $library; - $me->data['duration'] = new Duration($library['duration']); + if (isset($library['duration'])) { + $me->duration = new Duration($library['duration']); + } if (isset($library['lastViewedAt'])) { $lastViewedAt = new DateTime(); @@ -137,7 +139,7 @@ public static function fromLibrary(array $library): Movie $addedAt = new DateTime(); $addedAt->setTimestamp($library['addedAt']); $me->addedAt = $addedAt; - + $updatedAt = new DateTime(); $updatedAt->setTimestamp($library['updatedAt']); $me->updatedAt = $updatedAt; @@ -191,8 +193,17 @@ public static function fromLibrary(array $library): Movie } if (isset($library['Media'])) { - $media = Media::fromLibrary($library['Media']); - $me->media = $media; + if (isset($library['Media'][0])) { + $arr = []; + foreach ($library['Media'] as $m) { + $media = Media::fromLibrary($m); + $arr[] = $media; + } + $me->media = $arr; + } else { + $media = Media::fromLibrary($library['Media']); + $me->media = $media; + } unset($me->data['Media']); } From f1cb2553c7b70c1d4ff4a9f3f9e64cc73ad65590 Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Thu, 22 Dec 2022 23:57:07 -0500 Subject: [PATCH 42/45] Add method and docs to retrieve artwork data --- docs/PlexApi.md | 1 + src/jc21/PlexApi.php | 27 +++++++++++++++++++++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/docs/PlexApi.md b/docs/PlexApi.md index 994311a..bcdab53 100644 --- a/docs/PlexApi.md +++ b/docs/PlexApi.md @@ -11,6 +11,7 @@ | public | getLibrarySections() : array\|bool
Get Library Sections ie Movies, TV Shows etc | | public | getLibrarySectionContents(int $sectionKey, bool $returnCollection = `false`) : array\|ItemCollection\|bool
Get Library Section contents | | public | getMetadata(int $item) : array\|bool
Get Metadata for an Item | +| public | getArtwork(Movie $item, string $tag) : string
Get binary data for artwork, can store as `jpg` at return | | public | getOnDeck(bool $returnCollection = `false`) : array\|ItemCollection\|bool
Get On Deck Info | | public | getRecentlyAdded(bool $returnCollection = `false`) : array\|ItemCollection\|bool
Get Recently Added | | public | getServers() : array\|bool
Get Servers | diff --git a/src/jc21/PlexApi.php b/src/jc21/PlexApi.php index a95859f..69c151e 100644 --- a/src/jc21/PlexApi.php +++ b/src/jc21/PlexApi.php @@ -10,6 +10,7 @@ use jc21\TV\Show; use jc21\TV\Season; use jc21\TV\Episode; +use jc21\Util\Item; /** * Plex API Class - Communicate with your Plex Media Server. @@ -355,7 +356,7 @@ public function getMetadata($item, bool $returnObject = false) $res = $this->call('/library/metadata/' . (int) $item); if (!$returnObject): return $res; endif; - + $tag = (isset($res['Video']) ? 'Video' : null); $tag = (isset($res['Directory']) ? 'Directory' : $tag); @@ -364,6 +365,18 @@ public function getMetadata($item, bool $returnObject = false) } + /** + * Method for getting the artwork for a item + * + * @param Item $i + * @param string $tag + */ + public function getArtwork(Item $i, string $tag) + { + return $this->call($i->{$tag}, ['art' => true]); + } + + /** * Search for Items * @@ -596,6 +609,12 @@ public function call($path, $params = [], $method = self::GET, $isLoginCall = fa // Stats $this->lastCallStats = curl_getinfo($resource); + // Return if we are getting binary artwork data + if (isset($params['art']) && $params['art'] && $response !== false) { + curl_close($resource); + return $response; + } + // Errors and redirect failures if (!$response) { $response = false; @@ -636,7 +655,7 @@ private function buildHttpQuery(array $query): string } return substr($ret, 0, -1); } - + return http_build_query($query); } @@ -669,14 +688,14 @@ public static function array2collection($array) if (!isset($array[0])) { $array[0] = $array; } - + foreach ($array as $a) { if (!is_array($a) || !isset($a['type'])) { continue; } $i = self::array2object($a); - + if (is_null($i)) { continue; } From bcfcc8df0fe038a5f4d2129b127f29418fe7701c Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Fri, 23 Dec 2022 00:08:00 -0500 Subject: [PATCH 43/45] Complete conversion to ItemCollection for episodes --- src/jc21/TV/Season.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jc21/TV/Season.php b/src/jc21/TV/Season.php index b1f241e..3a27900 100644 --- a/src/jc21/TV/Season.php +++ b/src/jc21/TV/Season.php @@ -77,7 +77,7 @@ public function __construct() 'addedAt' => null, 'updatedAt' => null, ]; - $this->episodes = []; + $this->episodes = new ItemCollection(); } /** @@ -123,7 +123,7 @@ public function getChildren(): ItemCollection */ public function addEpisode(Episode $e) { - $this->episodes[$e->index] = $e; + $this->episodes->addData($e); } /** From da11003ad684c8e33177c0142dba1d3eab42e6cd Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Fri, 23 Dec 2022 00:37:15 -0500 Subject: [PATCH 44/45] Set minimum stability to stable and remove unncessary library --- composer.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/composer.json b/composer.json index e71c840..a3cf40d 100644 --- a/composer.json +++ b/composer.json @@ -14,13 +14,12 @@ "email": "jc@jc21.com" } ], - "minimum-stability": "dev", + "minimum-stability": "stable", "require": { "php": ">=7.4", "ext-curl": "*" }, "require-dev": { - "victorjonsson/markdowndocs": "dev-master", "symfony/dotenv": "6.2.x-dev", "phpunit/phpunit": "^9" }, From a4407c8a36100e1643e80af8a5877955be127707 Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Fri, 23 Dec 2022 00:47:18 -0500 Subject: [PATCH 45/45] Fix failed login returns as suggested in issue #8 --- src/jc21/PlexApi.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/jc21/PlexApi.php b/src/jc21/PlexApi.php index 69c151e..fa1b9f3 100644 --- a/src/jc21/PlexApi.php +++ b/src/jc21/PlexApi.php @@ -549,6 +549,9 @@ public function call($path, $params = [], $method = self::GET, $isLoginCall = fa { if (!$this->token && !$isLoginCall) { $this->call('https://plex.tv/users/sign_in.xml', [], self::POST, true); + if (!$this->token) { + return false; + } } if ($isLoginCall) {