From 77907601f8b2035a4efbe44eccecbaad7c425b57 Mon Sep 17 00:00:00 2001 From: Brock H Caldwell Date: Mon, 21 Apr 2025 11:30:18 -0500 Subject: [PATCH] feat: media result page --- composer.json | 2 + composer.lock | 93 ++++++- config/routes.yaml | 8 - src/Controller/SearchController.php | 21 +- .../Command/GetDownloadOptionsCommand.php | 14 + .../Handler/GetDownloadOptionsHandler.php | 22 ++ .../Action/Input/GetDownloadOptionsInput.php | 24 ++ .../Result/GetDownloadOptionsResult.php | 12 + .../Action/Command/GetMediaInfoCommand.php | 14 + .../Action/Handler/GetMediaInfoHandler.php | 23 ++ src/Search/Action/Input/GetMediaInfoInput.php | 25 ++ .../Action/Result/GetMediaInfoResult.php | 14 + src/Tmdb/Tmdb.php | 9 +- src/Tmdb/TmdbResult.php | 2 +- src/Torrentio/Result/ResultFactory.php | 107 +++++++ src/Torrentio/Result/TorrentioResult.php | 25 ++ .../Rule/DownloadOptionFilter/Resolution.php | 18 ++ src/Torrentio/Rule/RuleEngine.php | 34 +++ src/Torrentio/Torrentio.php | 117 ++++++++ src/Util/CountryCodes.php | 260 ++++++++++++++++++ templates/base.html.twig | 2 +- templates/components/SearchResult.html.twig | 2 +- templates/search/result.html.twig | 14 + templates/search/results.html.twig | 1 + 24 files changed, 844 insertions(+), 19 deletions(-) create mode 100644 src/Download/Action/Command/GetDownloadOptionsCommand.php create mode 100644 src/Download/Action/Handler/GetDownloadOptionsHandler.php create mode 100644 src/Download/Action/Input/GetDownloadOptionsInput.php create mode 100644 src/Download/Action/Result/GetDownloadOptionsResult.php create mode 100644 src/Search/Action/Command/GetMediaInfoCommand.php create mode 100644 src/Search/Action/Handler/GetMediaInfoHandler.php create mode 100644 src/Search/Action/Input/GetMediaInfoInput.php create mode 100644 src/Search/Action/Result/GetMediaInfoResult.php create mode 100644 src/Torrentio/Result/ResultFactory.php create mode 100644 src/Torrentio/Result/TorrentioResult.php create mode 100644 src/Torrentio/Rule/DownloadOptionFilter/Resolution.php create mode 100644 src/Torrentio/Rule/RuleEngine.php create mode 100644 src/Torrentio/Torrentio.php create mode 100644 src/Util/CountryCodes.php create mode 100644 templates/search/result.html.twig diff --git a/composer.json b/composer.json index 2a4071d..9cb1cc4 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,9 @@ "ext-ctype": "*", "ext-iconv": "*", "1tomany/rich-bundle": "^1.8", + "nihilarr/parse-torrent-name": "^0.0.1", "nyholm/psr7": "*", + "p3k/emoji-detector": "^1.2", "php-tmdb/api": "^4.1", "symfony/asset": "7.2.*", "symfony/console": "7.2.*", diff --git a/composer.lock b/composer.lock index 4b13968..c7d1538 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "52ea99b6f072a3dba10316c24060b886", + "content-hash": "e8c968ee6b83d42fa44746ec5e4d303d", "packages": [ { "name": "1tomany/data-uri", @@ -251,6 +251,50 @@ }, "time": "2025-04-07T20:06:18+00:00" }, + { + "name": "nihilarr/parse-torrent-name", + "version": "v0.0.1", + "source": { + "type": "git", + "url": "https://gitlab.com/nihilarr/parse-torrent-name.git", + "reference": "0d8ddb6c91b33845d2e26677304ea91f69c90319" + }, + "dist": { + "type": "zip", + "url": "https://gitlab.com/api/v4/projects/nihilarr%2Fparse-torrent-name/repository/archive.zip?sha=0d8ddb6c91b33845d2e26677304ea91f69c90319", + "reference": "0d8ddb6c91b33845d2e26677304ea91f69c90319", + "shasum": "" + }, + "require": { + "php": ">=5.3.24" + }, + "type": "library", + "autoload": { + "psr-4": { + "Nihilarr\\": "PTN/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Drew Smith", + "homepage": "https://www.nihilarr.com" + } + ], + "description": "Extract media information from torrent-like filename", + "homepage": "https://gitlab.com/nihilarr/parse-torrent-name", + "keywords": [ + "library", + "parse", + "parser", + "ptn", + "torrent" + ], + "time": "2018-04-23T01:48:14+00:00" + }, { "name": "nyholm/psr7", "version": "1.8.2", @@ -519,6 +563,53 @@ }, "time": "2021-05-22T15:57:08+00:00" }, + { + "name": "p3k/emoji-detector", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/aaronpk/emoji-detector-php.git", + "reference": "dce4638e215622181d272f08145a3f97b735b1c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/aaronpk/emoji-detector-php/zipball/dce4638e215622181d272f08145a3f97b735b1c7", + "reference": "dce4638e215622181d272f08145a3f97b735b1c7", + "shasum": "" + }, + "require": { + "ext-intl": "*", + "ext-mbstring": "*", + "php": ">=7.4" + }, + "require-dev": { + "phpunit/phpunit": "^8.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/Emoji.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Parecki", + "email": "aaron@parecki.com", + "homepage": "https://aaronparecki.com/" + } + ], + "description": "Detect and return all emoji found in a string", + "homepage": "https://github.com/aaronpk/emoji-detector-php", + "support": { + "issues": "https://github.com/aaronpk/emoji-detector-php/issues", + "source": "https://github.com/aaronpk/emoji-detector-php/tree/1.2.0" + }, + "time": "2024-02-19T18:29:05+00:00" + }, { "name": "php-http/discovery", "version": "1.20.0", diff --git a/config/routes.yaml b/config/routes.yaml index 57238a9..a785e77 100644 --- a/config/routes.yaml +++ b/config/routes.yaml @@ -6,11 +6,3 @@ controllersIndex: defaults: schemes: [ https ] -controllersSearch: - resource: - path: ../src/Search/Framework/Controller - namespace: App\Search\Framework\Controller - type: attribute - defaults: - schemes: [ https ] - diff --git a/src/Controller/SearchController.php b/src/Controller/SearchController.php index 96d3f54..3c0b596 100644 --- a/src/Controller/SearchController.php +++ b/src/Controller/SearchController.php @@ -2,7 +2,9 @@ namespace App\Controller; +use App\Search\Action\Handler\GetMediaInfoHandler; use App\Search\Action\Handler\SearchHandler; +use App\Search\Action\Input\GetMediaInfoInput; use App\Search\Action\Input\SearchInput; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; @@ -12,17 +14,30 @@ final class SearchController extends AbstractController { public function __construct( private SearchHandler $searchHandler, + private GetMediaInfoHandler $getMediaInfoHandler, ) {} #[Route('/search', name: 'app_search', methods: ['GET'])] - public function index( + public function search( SearchInput $searchInput, - ): Response - { + ): Response { $results = $this->searchHandler->handle($searchInput->toCommand()); return $this->render('search/results.html.twig', [ 'results' => $results, ]); } + + #[Route('/result/{mediaType}/{tmdbId}', name: 'app_search_result')] + public function result( + GetMediaInfoInput $getDownloadOptionsInput, + ): Response { + $result = $this->getMediaInfoHandler->handle( + $getDownloadOptionsInput->toCommand() + ); + + return $this->render('search/result.html.twig', [ + 'result' => $result, + ]); + } } diff --git a/src/Download/Action/Command/GetDownloadOptionsCommand.php b/src/Download/Action/Command/GetDownloadOptionsCommand.php new file mode 100644 index 0000000..2225d7c --- /dev/null +++ b/src/Download/Action/Command/GetDownloadOptionsCommand.php @@ -0,0 +1,14 @@ + */ + public function __construct( + public string $tmdbId, + public string $mediaType, + ) {} +} \ No newline at end of file diff --git a/src/Download/Action/Handler/GetDownloadOptionsHandler.php b/src/Download/Action/Handler/GetDownloadOptionsHandler.php new file mode 100644 index 0000000..32daa81 --- /dev/null +++ b/src/Download/Action/Handler/GetDownloadOptionsHandler.php @@ -0,0 +1,22 @@ +tmdb->mediaDetails($command->tmdbId, $command->mediaType); + } +} diff --git a/src/Download/Action/Input/GetDownloadOptionsInput.php b/src/Download/Action/Input/GetDownloadOptionsInput.php new file mode 100644 index 0000000..2f3b849 --- /dev/null +++ b/src/Download/Action/Input/GetDownloadOptionsInput.php @@ -0,0 +1,24 @@ +tmdbId, $this->mediaType); + } +} \ No newline at end of file diff --git a/src/Download/Action/Result/GetDownloadOptionsResult.php b/src/Download/Action/Result/GetDownloadOptionsResult.php new file mode 100644 index 0000000..dea1aa9 --- /dev/null +++ b/src/Download/Action/Result/GetDownloadOptionsResult.php @@ -0,0 +1,12 @@ + */ + public function __construct( + public string $tmdbId, + public string $mediaType, + ) {} +} \ No newline at end of file diff --git a/src/Search/Action/Handler/GetMediaInfoHandler.php b/src/Search/Action/Handler/GetMediaInfoHandler.php new file mode 100644 index 0000000..67f7198 --- /dev/null +++ b/src/Search/Action/Handler/GetMediaInfoHandler.php @@ -0,0 +1,23 @@ +tmdb->mediaDetails($command->tmdbId, $command->mediaType); + + return new GetMediaInfoResult($media); + } +} diff --git a/src/Search/Action/Input/GetMediaInfoInput.php b/src/Search/Action/Input/GetMediaInfoInput.php new file mode 100644 index 0000000..d138922 --- /dev/null +++ b/src/Search/Action/Input/GetMediaInfoInput.php @@ -0,0 +1,25 @@ +tmdbId, $this->mediaType); + } +} \ No newline at end of file diff --git a/src/Search/Action/Result/GetMediaInfoResult.php b/src/Search/Action/Result/GetMediaInfoResult.php new file mode 100644 index 0000000..8d11885 --- /dev/null +++ b/src/Search/Action/Result/GetMediaInfoResult.php @@ -0,0 +1,14 @@ + */ + public function __construct( + public TmdbResult $media, + ) {} +} diff --git a/src/Tmdb/Tmdb.php b/src/Tmdb/Tmdb.php index 7940f74..a3e948c 100644 --- a/src/Tmdb/Tmdb.php +++ b/src/Tmdb/Tmdb.php @@ -182,10 +182,10 @@ class Tmdb function parseTvShow(array $data, string $posterBasePath): TmdbResult { return new TmdbResult( imdbId: $data['external_ids']['imdb_id'], - tvdbId: $data['id'], + tmdbId: $data['id'], title: $data['name'], poster: $posterBasePath . $data['poster_path'], - excerpt: $data['overview'], + description: $data['overview'], year: (new \DateTime($data['first_air_date']))->format('Y'), mediaType: "tvshows", episodes: $data['episodes'], @@ -195,10 +195,10 @@ class Tmdb function parseMovie(array $data, string $posterBasePath): TmdbResult { return new TmdbResult( imdbId: $data['external_ids']['imdb_id'], - tvdbId: $data['id'], + tmdbId: $data['id'], title: $data['title'], poster: $posterBasePath . $data['poster_path'], - excerpt: $data['overview'], + description: $data['overview'], year: (new \DateTime($data['release_date']))->format('Y'), mediaType: "movies", ); @@ -215,6 +215,7 @@ class Tmdb $mediaType = $result instanceof Movie ? MediaType::Movie->value : MediaType::TvShow->value; $tmdbResult = new TmdbResult(); $tmdbResult->mediaType = $mediaType; + $tmdbResult->tmdbId = $result->getId(); $tmdbResult->imdbId = $this->getImdbId($result->getId(), $mediaType); $tmdbResult->title = $this->getTitle($result, $mediaType); $tmdbResult->poster = self::POSTER_IMG_PATH . $result->getPosterImage(); diff --git a/src/Tmdb/TmdbResult.php b/src/Tmdb/TmdbResult.php index 3cd484d..2875d8e 100644 --- a/src/Tmdb/TmdbResult.php +++ b/src/Tmdb/TmdbResult.php @@ -6,7 +6,7 @@ class TmdbResult { public function __construct( public ?string $imdbId = "", - public ?string $tvdbId = "", + public ?string $tmdbId = "", public ?string $title = "", public ?string $poster = "", public ?string $description = "", diff --git a/src/Torrentio/Result/ResultFactory.php b/src/Torrentio/Result/ResultFactory.php new file mode 100644 index 0000000..c9afd00 --- /dev/null +++ b/src/Torrentio/Result/ResultFactory.php @@ -0,0 +1,107 @@ +parse($title); + return new TorrentioResult( + self::trimTitle($title), + $url, + self::setSize($title), + self::setSeeders($title), + self::setProvider($title), + self::setEpisode($title), + $ptn->season ?? "-", + $bingeGroup, + $ptn->resolution ?? "-", + $ptn->codec ?? "-", + $ptn, + substr(base64_encode($url), strlen($url) - 10), + $ptn->episode ?? "-", + self::setLanguages($title), + self::setLanguageFlags($title), + false + ); + } + + public static function setSize(string $title): string + { + $sizeMatch = []; + preg_match('/(\d+\.?\d+ )(GB|MB)/', $title, $sizeMatch); + return $sizeMatch[0] ?? "-"; + } + + private static function setSeeders(string $title): string + { + $emoji = \Emoji\detect_emoji($title); + return intval( + grapheme_substr($title, $emoji[0]['grapheme_offset'] + 1, $emoji[1]['grapheme_offset'] - $emoji[0]['grapheme_offset']) + ); + } + + private static function setProvider(string $title): string + { + $emoji = \Emoji\detect_emoji($title); + $provider = trim( + grapheme_substr($title, $emoji[2]['grapheme_offset'] + 1, strlen($title) - $emoji[1]['grapheme_offset']) + ); + $providerParts = explode("\n", $provider); + return $providerParts[0]; + } + + private static function setLanguageFlags(string $title): string + { + $emoji = \Emoji\detect_emoji($title); + $provider = trim( + grapheme_substr($title, $emoji[2]['grapheme_offset'] + 1, strlen($title) - $emoji[1]['grapheme_offset']) + ); + + $providerParts = explode("\n", $provider); + + if (array_key_exists(1, $providerParts)) { + return $providerParts[1]; + } else { + return "🇺🇸"; + } + } + + public static function setLanguages(string $title): array + { + $emoji = \Emoji\detect_emoji($title); + $flags = array_filter($emoji, function ($emoji) { + return str_starts_with($emoji['short_name'], 'flag-'); + }); + + $languages = array_map(function ($flag) { + return CountryCodes::convertFromAbbr(strtoupper(substr($flag['short_name'], strlen('flag-')))); + }, + $flags); + if (count($languages) > 0) { + return array_values($languages); + } else { + return ["English"]; + } + } + + private static function setEpisode(string $title) + { + $value = []; + preg_match('/[sS]\d\d[eE]\d\d/', $title, $value); + return array_key_exists(0, $value) ? strtoupper($value[0]) : "n/a"; + } + + private static function trimTitle(string $title) + { + $emoji = \Emoji\detect_emoji($title); + return trim(grapheme_substr($title, 0, $emoji[0]['grapheme_offset'])); + } +} diff --git a/src/Torrentio/Result/TorrentioResult.php b/src/Torrentio/Result/TorrentioResult.php new file mode 100644 index 0000000..fbdef74 --- /dev/null +++ b/src/Torrentio/Result/TorrentioResult.php @@ -0,0 +1,25 @@ +resolution === $this->expectedValue; + } +} diff --git a/src/Torrentio/Rule/RuleEngine.php b/src/Torrentio/Rule/RuleEngine.php new file mode 100644 index 0000000..0efed4d --- /dev/null +++ b/src/Torrentio/Rule/RuleEngine.php @@ -0,0 +1,34 @@ +rules[] = $rule; + } + + public function validateAny($fact): bool + { + foreach ($this->rules as $rule) { + if ($rule($fact)) { + return true; + } + } + return false; + } + + public function validateAll($fact): bool + { + foreach ($this->rules as $rule) { + if (!$rule($fact)) { + return false; + } + } + + return true; + } +} \ No newline at end of file diff --git a/src/Torrentio/Torrentio.php b/src/Torrentio/Torrentio.php new file mode 100644 index 0000000..63164cb --- /dev/null +++ b/src/Torrentio/Torrentio.php @@ -0,0 +1,117 @@ +searchUrl = str_replace('{realDebridKey}', $this->realDebridKey, $this->baseUrl); + } + + public function search(string $imdbCode, string $type, array $filter = []): array + { + $cacheKey = "torrentio.{$imdbCode}"; + + $results = $this->cache->get($cacheKey, function (ItemInterface $item) use ($imdbCode) { + $item->expiresAt(new \DateTimeImmutable("today 11:59 pm")); + $response = file_get_contents(str_replace('{imdbCode}', $imdbCode, $this->searchUrl)); + return json_decode( + $response, + true + ); + }); + + return $this->parse($results, $filter); + } + + public function searchBySeriesSeason(MediaResult $series): MediaResult + { + $imdbCode = $series->imdbId; +// foreach ($series->episodes as $season => $episodes) { +// foreach ($episodes as $key => $episode) { +// $cacheKey = "torrentio.$series->imdbId.$season.{$episode['episode_number']}"; +// $downloadOptions = $this->cache->get($cacheKey, function (ItemInterface $item) use ($imdbCode, $season, $episode) { +// $item->expiresAt(new \DateTimeImmutable("today 11:59 pm")); +// $response = file_get_contents(str_replace('{imdbCode}', "$imdbCode:$season:{$episode['episode_number']}", $this->searchUrl)); +// return json_decode( +// $response, +// true +// ); +// }); +// $series->episodes[$season][$key]['download_options'] = $this->parse($downloadOptions, []); +// } +// } + return $series; + } + + public function fetchEpisodeResults(string $imdbId, int $season, int $episode): array + { + $cacheKey = "torrentio.$imdbId.$season.$episode"; + $results = $this->cache->get($cacheKey, function (ItemInterface $item) use ($imdbId, $season, $episode) { + $item->expiresAt(new \DateTimeImmutable("today 11:59 pm")); + $response = file_get_contents(str_replace('{imdbCode}', "$imdbId:$season:$episode", $this->searchUrl)); + return json_decode( + $response, + true + ); + }); + + return $this->parse($results, []); + } + + public function parse(array $data, array $filter): array + { + $ruleEngine = new RuleEngine(); + foreach ($filter as $rule => $value) { + if ('resolution' === $rule) { + $ruleEngine->addRule(new Resolution($value)); + } + } + + $results = []; + foreach ($data['streams'] as $stream) { + if (!str_starts_with($stream['url'], "https")) { + continue; + } + + if ( + array_key_exists('behaviorHints', $stream) && + array_key_exists('bingeGroup', $stream['behaviorHints']) + ) { + $bingeGroup = $stream['behaviorHints']['bingeGroup']; + } else { + $bingeGroup = '-'; + } + + $result = ResultFactory::map( + $stream['url'], + $stream['title'], + $bingeGroup + ); + + if ($ruleEngine->validateAll($result)) { + $results[] = $result; + } + } + + return $results; + } +} diff --git a/src/Util/CountryCodes.php b/src/Util/CountryCodes.php new file mode 100644 index 0000000..b8b8f63 --- /dev/null +++ b/src/Util/CountryCodes.php @@ -0,0 +1,260 @@ + 'Afghanistan', + 'AX' => 'Aland Islands', + 'AL' => 'Albania', + 'DZ' => 'Algeria', + 'AS' => 'American Samoa', + 'AD' => 'Andorra', + 'AO' => 'Angola', + 'AI' => 'Anguilla', + 'AQ' => 'Antarctica', + 'AG' => 'Antigua and Barbuda', + 'AR' => 'Argentina', + 'AM' => 'Armenia', + 'AW' => 'Aruba', + 'AU' => 'Australia', + 'AT' => 'Austria', + 'AZ' => 'Azerbaijan', + 'BS' => 'Bahamas the', + 'BH' => 'Bahrain', + 'BD' => 'Bangladesh', + 'BB' => 'Barbados', + 'BY' => 'Belarus', + 'BE' => 'Belgium', + 'BZ' => 'Belize', + 'BJ' => 'Benin', + 'BM' => 'Bermuda', + 'BT' => 'Bhutan', + 'BO' => 'Bolivia', + 'BA' => 'Bosnia and Herzegovina', + 'BW' => 'Botswana', + 'BV' => 'Bouvet Island (Bouvetoya)', + 'BR' => 'Brazil', + 'IO' => 'British Indian Ocean Territory (Chagos Archipelago)', + 'VG' => 'British Virgin Islands', + 'BN' => 'Brunei Darussalam', + 'BG' => 'Bulgaria', + 'BF' => 'Burkina Faso', + 'BI' => 'Burundi', + 'KH' => 'Cambodia', + 'CM' => 'Cameroon', + 'CA' => 'Canada', + 'CV' => 'Cape Verde', + 'KY' => 'Cayman Islands', + 'CF' => 'Central African Republic', + 'TD' => 'Chad', + 'CL' => 'Chile', + 'CN' => 'China', + 'CX' => 'Christmas Island', + 'CC' => 'Cocos (Keeling) Islands', + 'CO' => 'Colombia', + 'KM' => 'Comoros the', + 'CD' => 'Congo', + 'CG' => 'Congo the', + 'CK' => 'Cook Islands', + 'CR' => 'Costa Rica', + 'CI' => 'Cote d\'Ivoire', + 'HR' => 'Croatia', + 'CU' => 'Cuba', + 'CY' => 'Cyprus', + 'CZ' => 'Czech Republic', + 'DK' => 'Denmark', + 'DJ' => 'Djibouti', + 'DM' => 'Dominica', + 'DO' => 'Dominican Republic', + 'EC' => 'Ecuador', + 'EG' => 'Egypt', + 'SV' => 'El Salvador', + 'GQ' => 'Equatorial Guinea', + 'ER' => 'Eritrea', + 'EE' => 'Estonia', + 'ET' => 'Ethiopia', + 'FO' => 'Faroe Islands', + 'FK' => 'Falkland Islands (Malvinas)', + 'FJ' => 'Fiji the Fiji Islands', + 'FI' => 'Finland', + 'FR' => 'France, French Republic', + 'GF' => 'French Guiana', + 'PF' => 'French Polynesia', + 'TF' => 'French Southern Territories', + 'GA' => 'Gabon', + 'GM' => 'Gambia the', + 'GE' => 'Georgia', + 'DE' => 'Germany', + 'GH' => 'Ghana', + 'GI' => 'Gibraltar', + 'GR' => 'Greece', + 'GL' => 'Greenland', + 'GD' => 'Grenada', + 'GP' => 'Guadeloupe', + 'GU' => 'Guam', + 'GT' => 'Guatemala', + 'GG' => 'Guernsey', + 'GN' => 'Guinea', + 'GW' => 'Guinea-Bissau', + 'GY' => 'Guyana', + 'HT' => 'Haiti', + 'HM' => 'Heard Island and McDonald Islands', + 'VA' => 'Holy See (Vatican City State)', + 'HN' => 'Honduras', + 'HK' => 'Hong Kong', + 'HU' => 'Hungary', + 'IS' => 'Iceland', + 'IN' => 'India', + 'ID' => 'Indonesia', + 'IR' => 'Iran', + 'IQ' => 'Iraq', + 'IE' => 'Ireland', + 'IM' => 'Isle of Man', + 'IL' => 'Israel', + 'IT' => 'Italy', + 'JM' => 'Jamaica', + 'JP' => 'Japan', + 'JE' => 'Jersey', + 'JO' => 'Jordan', + 'KZ' => 'Kazakhstan', + 'KE' => 'Kenya', + 'KI' => 'Kiribati', + 'KP' => 'Korea', + 'KR' => 'Korea', + 'KW' => 'Kuwait', + 'KG' => 'Kyrgyz Republic', + 'LA' => 'Lao', + 'LV' => 'Latvia', + 'LB' => 'Lebanon', + 'LS' => 'Lesotho', + 'LR' => 'Liberia', + 'LY' => 'Libyan Arab Jamahiriya', + 'LI' => 'Liechtenstein', + 'LT' => 'Lithuania', + 'LU' => 'Luxembourg', + 'MO' => 'Macao', + 'MK' => 'Macedonia', + 'MG' => 'Madagascar', + 'MW' => 'Malawi', + 'MY' => 'Malaysia', + 'MV' => 'Maldives', + 'ML' => 'Mali', + 'MT' => 'Malta', + 'MH' => 'Marshall Islands', + 'MQ' => 'Martinique', + 'MR' => 'Mauritania', + 'MU' => 'Mauritius', + 'YT' => 'Mayotte', + 'MX' => 'Mexico', + 'FM' => 'Micronesia', + 'MD' => 'Moldova', + 'MC' => 'Monaco', + 'MN' => 'Mongolia', + 'ME' => 'Montenegro', + 'MS' => 'Montserrat', + 'MA' => 'Morocco', + 'MZ' => 'Mozambique', + 'MM' => 'Myanmar', + 'NA' => 'Namibia', + 'NR' => 'Nauru', + 'NP' => 'Nepal', + 'AN' => 'Netherlands Antilles', + 'NL' => 'Netherlands the', + 'NC' => 'New Caledonia', + 'NZ' => 'New Zealand', + 'NI' => 'Nicaragua', + 'NE' => 'Niger', + 'NG' => 'Nigeria', + 'NU' => 'Niue', + 'NF' => 'Norfolk Island', + 'MP' => 'Northern Mariana Islands', + 'NO' => 'Norway', + 'OM' => 'Oman', + 'PK' => 'Pakistan', + 'PW' => 'Palau', + 'PS' => 'Palestinian Territory', + 'PA' => 'Panama', + 'PG' => 'Papua New Guinea', + 'PY' => 'Paraguay', + 'PE' => 'Peru', + 'PH' => 'Philippines', + 'PN' => 'Pitcairn Islands', + 'PL' => 'Poland', + 'PT' => 'Portugal, Portuguese Republic', + 'PR' => 'Puerto Rico', + 'QA' => 'Qatar', + 'RE' => 'Reunion', + 'RO' => 'Romania', + 'RU' => 'Russian Federation', + 'RW' => 'Rwanda', + 'BL' => 'Saint Barthelemy', + 'SH' => 'Saint Helena', + 'KN' => 'Saint Kitts and Nevis', + 'LC' => 'Saint Lucia', + 'MF' => 'Saint Martin', + 'PM' => 'Saint Pierre and Miquelon', + 'VC' => 'Saint Vincent and the Grenadines', + 'WS' => 'Samoa', + 'SM' => 'San Marino', + 'ST' => 'Sao Tome and Principe', + 'SA' => 'Saudi Arabia', + 'SN' => 'Senegal', + 'RS' => 'Serbia', + 'SC' => 'Seychelles', + 'SL' => 'Sierra Leone', + 'SG' => 'Singapore', + 'SK' => 'Slovakia (Slovak Republic)', + 'SI' => 'Slovenia', + 'SB' => 'Solomon Islands', + 'SO' => 'Somalia, Somali Republic', + 'ZA' => 'South Africa', + 'GS' => 'South Georgia and the South Sandwich Islands', + 'ES' => 'Spain', + 'LK' => 'Sri Lanka', + 'SD' => 'Sudan', + 'SR' => 'Suriname', + 'SJ' => 'Svalbard & Jan Mayen Islands', + 'SZ' => 'Swaziland', + 'SE' => 'Sweden', + 'CH' => 'Switzerland, Swiss Confederation', + 'SY' => 'Syrian Arab Republic', + 'TW' => 'Taiwan', + 'TJ' => 'Tajikistan', + 'TZ' => 'Tanzania', + 'TH' => 'Thailand', + 'TL' => 'Timor-Leste', + 'TG' => 'Togo', + 'TK' => 'Tokelau', + 'TO' => 'Tonga', + 'TT' => 'Trinidad and Tobago', + 'TN' => 'Tunisia', + 'TR' => 'Turkey', + 'TM' => 'Turkmenistan', + 'TC' => 'Turks and Caicos Islands', + 'TV' => 'Tuvalu', + 'UG' => 'Uganda', + 'UA' => 'Ukraine', + 'AE' => 'United Arab Emirates', + 'GB' => 'United Kingdom', + 'US' => 'United States of America', + 'UM' => 'United States Minor Outlying Islands', + 'VI' => 'United States Virgin Islands', + 'UY' => 'Uruguay, Eastern Republic of', + 'UZ' => 'Uzbekistan', + 'VU' => 'Vanuatu', + 'VE' => 'Venezuela', + 'VN' => 'Vietnam', + 'WF' => 'Wallis and Futuna', + 'EH' => 'Western Sahara', + 'YE' => 'Yemen', + 'ZM' => 'Zambia', + 'ZW' => 'Zimbabwe' + ]; + + public static function convertFromAbbr(string $abbr): ?string + { + return self::$countries[$abbr] ?? null; + } +} diff --git a/templates/base.html.twig b/templates/base.html.twig index dca2b76..01642b7 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -15,7 +15,7 @@ -
+
{% block body %}{% endblock %}
diff --git a/templates/components/SearchResult.html.twig b/templates/components/SearchResult.html.twig index 5dc11d5..78625c6 100644 --- a/templates/components/SearchResult.html.twig +++ b/templates/components/SearchResult.html.twig @@ -10,7 +10,7 @@

choose diff --git a/templates/search/result.html.twig b/templates/search/result.html.twig new file mode 100644 index 0000000..82d16a4 --- /dev/null +++ b/templates/search/result.html.twig @@ -0,0 +1,14 @@ +{% extends 'base.html.twig' %} + +{% block title %}Search Results &mdash - Torsearch{% endblock %} + +{% block body %} +
+

Search Results

+
+ + + +
+
+{% endblock %} diff --git a/templates/search/results.html.twig b/templates/search/results.html.twig index 70c4a08..49e9e01 100644 --- a/templates/search/results.html.twig +++ b/templates/search/results.html.twig @@ -17,6 +17,7 @@ description="{{ result.description }}" poster="{{ result.poster }}" imdbId="{{ result.imdbId }}" + tmdbId="{{ result.tmdbId }}" />