chore: moves common code to Base namespace

This commit is contained in:
2025-07-06 22:53:13 -05:00
parent a0050e425b
commit 1fc5a8e500
28 changed files with 30 additions and 57 deletions

View File

@@ -0,0 +1,9 @@
<?php
namespace App\Base\Enum;
enum MediaType: string
{
case Movie = 'movies';
case TvShow = 'tvshows';
}

View File

@@ -0,0 +1,57 @@
<?php
namespace App\Base\Framework\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
#[AsCommand(
name: 'config:set',
description: 'Add a short description for your command',
)]
class ConfigSetCommand extends Command
{
public function __construct()
{
parent::__construct();
}
protected function configure(): void
{
$this
->addArgument('key', InputArgument::REQUIRED, 'Config key')
->addArgument('value', InputArgument::REQUIRED, 'Config value')
;
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$key = $input->getArgument('key');
$handlers = [
'auth.method' => 'setAuthMethod',
];
$handler = $handlers[$key];
$this->$handler($input, $io);
$io->success('Success: "' . $input->getArgument('key') . '" set to "' . $input->getArgument('value') . '"');
return Command::SUCCESS;
}
private function setAuthMethod(InputInterface $input, SymfonyStyle $io)
{
$config = [
'local' => 'config/dist/local.security.yaml',
'ldap' => 'config/dist/ldap.security.yaml',
];
$authMethod = $input->getArgument('value');
$io->text('> Setting auth method to: ' . $authMethod);
copy($config[$authMethod], 'config/packages/security.yaml');
}
}

View File

@@ -0,0 +1,204 @@
<?php
namespace App\Base\Framework\Command;
use App\User\Framework\Entity\UserPreference;
use App\User\Framework\Repository\PreferenceOptionRepository;
use App\User\Framework\Repository\PreferencesRepository;
use App\User\Framework\Repository\UserRepository;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
#[AsCommand(
name: 'db:seed',
description: 'Seed the database with required data.',
)]
class SeedDatabaseCommand extends Command
{
private PreferencesRepository $preferenceRepository;
private PreferenceOptionRepository $preferenceOptionRepository;
private UserRepository $userRepository;
public function __construct(
PreferencesRepository $preferenceRepository,
PreferenceOptionRepository $preferenceOptionRepository,
UserRepository $userRepository,
) {
parent::__construct();
$this->preferenceRepository = $preferenceRepository;
$this->preferenceOptionRepository = $preferenceOptionRepository;
$this->userRepository = $userRepository;
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$this->seedPreferences($io);
$this->seedPreferenceOptions($io);
$this->updateUserPreferences($io);
return Command::SUCCESS;
}
private function seedPreferences(SymfonyStyle $io)
{
$io->info('[SeedDatabaseCommand] > Seeding preferences...');
$preferences = $this->getPreferences();
foreach ($preferences as $preference) {
if ($this->preferenceRepository->find($preference['id'])) {
continue;
}
$this->preferenceRepository->getEntityManager()->persist((new \App\User\Framework\Entity\Preference())
->setId($preference['id'])
->setName($preference['name'])
->setDescription($preference['description'])
->setEnabled($preference['enabled'])
->setType($preference['type'])
);
}
$this->preferenceRepository->getEntityManager()->flush();
}
private function updateUserPreferences(SymfonyStyle $io)
{
$io->info('[SeedDatabaseCommand] > Updating user preferences...');
$users = $this->userRepository->findAll();
$preferences = $this->preferenceRepository->findAll();
foreach ($users as $user) {
foreach ($preferences as $preference) {
if (false === $user->hasUserPreference($preference->getId())) {
$user->addUserPreference(
new UserPreference()
->setPreference($preference)
->setUser($user)
->setPreferenceValue(null)
);
}
}
}
$this->userRepository->getEntityManager()->flush();
}
private function getPreferences(): array
{
return [
[
'id' => 'codec',
'name' => 'Codec',
'description' => null,
'enabled' => true,
'type' => 'media',
],
[
'id' => 'resolution',
'name' => 'Resolution',
'description' => null,
'enabled' => true,
'type' => 'media',
],
[
'id' => 'language',
'name' => 'Language',
'description' => null,
'enabled' => true,
'type' => 'media',
],
[
'id' => 'provider',
'name' => 'Provider',
'description' => null,
'enabled' => true,
'type' => 'media',
],
[
'id' => 'quality',
'name' => 'Quality',
'description' => null,
'enabled' => true,
'type' => 'media',
],
[
'id' => 'movie_folder',
'name' => 'Create new folder for Movies',
'description' => 'When downloading a movie, store it in a new folder in your base \'movies\' folder. (e.g.: .../movies/Inception/Inception.2160p.h265.mkv)',
'enabled' => true,
'type' => 'download'
],
];
}
private function seedPreferenceOptions(SymfonyStyle $io)
{
$io->info('[SeedDatabaseCommand] > Seeding preference options...');
$options = $this->getPreferenceOptions();
foreach ($options as $option) {
if ($this->preferenceOptionRepository->findBy([
'preference' => $option['preference_id'],
'name' => $option['name'],
'value' => $option['value'],
'enabled' => $option['enabled'],
])) {
continue;
}
$this->preferenceOptionRepository->getEntityManager()->persist(
(new \App\User\Framework\Entity\PreferenceOption())
->setPreference($this->preferenceRepository->find($option['preference_id']))
->setName($option['name'])
->setValue($option['value'])
->setEnabled($option['enabled'])
);
}
$this->preferenceOptionRepository->getEntityManager()->flush();
}
private function getPreferenceOptions(): array
{
return [
[
'preference_id' => 'resolution',
'name' => '720p',
'value' => '720p',
'enabled' => true
],
[
'preference_id' => 'resolution',
'name' => '1080p',
'value' => '1080p',
'enabled' => true
],
[
'preference_id' => 'resolution',
'name' => '2160p',
'value' => '2160p',
'enabled' => true
],
[
'preference_id' => 'codec',
'name' => '-',
'value' => '-',
'enabled' => true
],
[
'preference_id' => 'codec',
'name' => 'h264',
'value' => 'h264',
'enabled' => true
],
[
'preference_id' => 'codec',
'name' => 'h265/HEVC',
'value' => 'h265',
'enabled' => true
]
];
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace App\Base\Framework\Command;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
#[AsCommand(
name: 'startup:status',
description: 'Add a short description for your command',
)]
class StartupStatusCommand extends Command
{
private EntityManagerInterface $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
parent::__construct();
$this->entityManager = $entityManager;
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$ready = true;
$messengerTableExists = $this->doesMessengerTableExist();
if (false === $messengerTableExists) {
$ready = false;
}
return (true === $ready) ? Command::SUCCESS : Command::FAILURE;
}
private function doesMessengerTableExist()
{
return $this->entityManager->getConnection()
->createSchemaManager()
->tablesExist('messenger_messages');
}
}

View File

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Base\Framework\Controller;
use App\Base\Util\Broadcaster;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
final class AlertController extends AbstractController
{
public function __construct(
private readonly Broadcaster $broadcaster,
) {}
#[Route('/alert', name: 'app_alert')]
public function index(): Response
{
$this->broadcaster->alert(
'Added to queue',
'This is a testy test!'
);
return $this->json([
'Success' => 'Published'
]);
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Base\Framework\Controller;
use App\Monitor\Action\Handler\MonitorTvShowHandler;
use App\Tmdb\Tmdb;
use App\User\Framework\Entity\User;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
final class IndexController extends AbstractController
{
public function __construct(
private readonly Tmdb $tmdb,
private readonly MonitorTvShowHandler $monitorTvShowHandler,
) {}
#[Route('/', name: 'app_index')]
public function index(Request $request): Response
{
/** @var User $user */
$user = $this->getUser();
return $this->render('index/index.html.twig', [
'active_downloads' => $this->getUser()->getActiveDownloads(),
'recent_downloads' => $this->getUser()->getDownloads(),
'popular_movies' => $this->tmdb->popularMovies(1, 6),
'popular_tvshows' => $this->tmdb->popularTvShows(1, 6),
]);
}
}

View File

@@ -0,0 +1,83 @@
<?php
namespace App\Base\Framework\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 App\Tmdb\TmdbResult;
use App\Torrentio\Action\Command\GetMovieOptionsCommand;
use App\Torrentio\Action\Command\GetTvShowOptionsCommand;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Routing\Attribute\Route;
final class SearchController extends AbstractController
{
public function __construct(
private SearchHandler $searchHandler,
private GetMediaInfoHandler $getMediaInfoHandler,
private MessageBusInterface $bus,
) {}
#[Route('/search', name: 'app_search', methods: ['GET'])]
public function search(
SearchInput $searchInput,
): Response {
$results = $this->searchHandler->handle($searchInput->toCommand());
return $this->render('search/results.html.twig', [
'results' => $results,
]);
}
#[Route('/result/{mediaType}/{imdbId}/{season}', name: 'app_search_result')]
public function result(
GetMediaInfoInput $input,
?int $season = null,
): Response {
$result = $this->getMediaInfoHandler->handle($input->toCommand());
return $this->render('search/result.html.twig', [
'results' => $result,
'filter' => [
'resolution' => '',
'codec' => '',
'provider' => '',
'language' => '',
'season' => 1,
'episode' => ''
]
]);
}
private function warmDownloadOptionCache(TmdbResult $result)
{
if ($result->mediaType === 'tvshows') {
// dispatches a job to get the download options
// for each episode and load them in cache
foreach ($result->episodes as $season => $episodes) {
// Only do the first 2 seasons, so we reduce
// getting rate-limited by Torrentio
if ($season > 2) {
return;
}
foreach ($episodes as $episode) {
$this->bus->dispatch(new GetTvShowOptionsCommand(
tmdbId: $result->tmdbId,
imdbId: $result->imdbId,
season: $season,
episode: $episode['episode_number'],
));
}
}
} elseif ($result->mediaType === 'movies') {
$this->bus->dispatch(new GetMovieOptionsCommand(
$result->tmdbId,
$result->imdbId,
));
}
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace App\Base\Util;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Mercure\HubInterface;
use Symfony\Component\Mercure\Update;
use Twig\Environment;
readonly class Broadcaster
{
public function __construct(
#[Autowire(service: 'twig')]
private Environment $renderer,
private HubInterface $hub,
private RequestStack $requestStack,
) {}
public function alert(string $title, string $message, string $type = "success"): void
{
$userAlertTopic = $this->requestStack->getCurrentRequest()->getSession()->get('mercure_alert_topic');
$update = new Update(
$userAlertTopic,
$this->renderer->render('broadcast/Alert.stream.html.twig', [
'alert_id' => uniqid(),
'title' => $title,
'message' => $message,
'type' => $type,
])
);
$this->hub->publish($update);
}
}

View File

@@ -0,0 +1,260 @@
<?php
namespace App\Base\Util;
class CountryCodes
{
static $countries = [
'AF' => '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;
}
}

View File

@@ -0,0 +1,140 @@
<?php
namespace App\Base\Util;
class CountryLanguages
{
public static $languages = [
'English (US)',
'English',
'French',
'German',
'Italian',
'Spanish',
'Portuguese',
'Mandarin',
'Japanese',
'Korean',
'Russian',
'Hindi',
'Arabic',
'Dutch',
'Swedish',
'Norwegian',
'Danish',
'Finnish',
'Polish',
'Turkish',
'Persian',
'Thai',
'Vietnamese',
'Indonesian',
'Urdu',
'Bengali',
'Ukrainian',
'Greek',
'Hebrew',
'Malay',
'Filipino'
];
public static function fromCountryCode(string $countryCode): ?string
{
$countryLanguages = [
'US' => 'English (US)',
'GB' => 'English',
'CA' => 'English', // Note: French is also official in parts of Canada
'FR' => 'French',
'DE' => 'German',
'IT' => 'Italian',
'ES' => 'Spanish',
'MX' => 'Spanish',
'BR' => 'Portuguese',
'PT' => 'Portuguese',
'CN' => 'Mandarin',
'JP' => 'Japanese',
'KR' => 'Korean',
'RU' => 'Russian',
'IN' => 'Hindi', // Note: India has multiple official languages
'SA' => 'Arabic',
'EG' => 'Arabic',
'ZA' => 'English', // South Africa has 11 official languages
'NG' => 'English',
'AU' => 'English',
'AR' => 'Spanish',
'CH' => 'German', // Also French, Italian, Romansh
'BE' => 'Dutch', // Also French and German
'NL' => 'Dutch',
'SE' => 'Swedish',
'NO' => 'Norwegian',
'DK' => 'Danish',
'FI' => 'Finnish',
'PL' => 'Polish',
'TR' => 'Turkish',
'IR' => 'Persian',
'TH' => 'Thai',
'VN' => 'Vietnamese',
'ID' => 'Indonesian',
'PK' => 'Urdu',
'BD' => 'Bengali',
'UA' => 'Ukrainian',
'GR' => 'Greek',
'IL' => 'Hebrew',
'MY' => 'Malay',
'PH' => 'Filipino',
'KE' => 'English', // Also Swahili
];
return $countryLanguages[$countryCode] ?? null;
}
public static function fromCountryName(string $countryName): ?string
{
$countryLanguages = [
'United States' => 'English (US)',
'United Kingdom' => 'English',
'Canada' => 'English', // French is also official
'France' => 'French',
'Germany' => 'German',
'Italy' => 'Italian',
'Spain' => 'Spanish',
'Mexico' => 'Spanish',
'Brazil' => 'Portuguese',
'Portugal' => 'Portuguese',
'China' => 'Mandarin',
'Japan' => 'Japanese',
'South Korea' => 'Korean',
'Russia' => 'Russian',
'India' => 'Hindi', // Also English and many regional languages
'Saudi Arabia' => 'Arabic',
'Egypt' => 'Arabic',
'South Africa' => 'English', // One of 11 official languages
'Nigeria' => 'English',
'Australia' => 'English',
'Argentina' => 'Spanish',
'Switzerland' => 'German', // Also French, Italian, Romansh
'Belgium' => 'Dutch', // Also French and German
'Netherlands' => 'Dutch',
'Sweden' => 'Swedish',
'Norway' => 'Norwegian',
'Denmark' => 'Danish',
'Finland' => 'Finnish',
'Poland' => 'Polish',
'Turkey' => 'Turkish',
'Iran' => 'Persian',
'Thailand' => 'Thai',
'Vietnam' => 'Vietnamese',
'Indonesia' => 'Indonesian',
'Pakistan' => 'Urdu',
'Bangladesh' => 'Bengali',
'Ukraine' => 'Ukrainian',
'Greece' => 'Greek',
'Israel' => 'Hebrew',
'Malaysia' => 'Malay',
'Philippines' => 'Filipino',
'Kenya' => 'English', // Also Swahili
];
return $countryLanguages[$countryName] ?? null;
}
}

View File

@@ -0,0 +1,72 @@
<?php
namespace App\Base\Util;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\Tools\Pagination\Paginator as OrmPaginator;
class Paginator
{
/**
* @var integer
*/
private $total;
/**
* @var integer
*/
private $lastPage;
private $items;
public $currentPage = 1;
public $limit = 5;
/**
* @param QueryBuilder|Query $query
* @param int $page
* @param int $limit
* @return Paginator
*/
public function paginate($query, int $page = 1, int $limit = 5): Paginator
{
$paginator = new OrmPaginator($query);
$paginator
->getQuery()
->setFirstResult($limit * ($page - 1))
->setMaxResults($limit);
$this->total = $paginator->count();
$this->lastPage = (int) ceil($paginator->count() / $paginator->getQuery()->getMaxResults());
$this->items = $paginator;
$this->currentPage = $page;
$this->limit = $limit;
return $this;
}
public function getTotal(): int
{
return $this->total;
}
public function getLastPage(): int
{
return $this->lastPage;
}
public function getItems()
{
return $this->items;
}
public function getShowing()
{
$showingStart = ($this->currentPage - 1) * $this->limit;
$showingEnd = $showingStart + $this->limit;
return sprintf("Showing %d - %d of %d results.", $showingStart, $showingEnd, $this->total);
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace App\Base\Util;
class ProviderList
{
public static $providers = [
'1337x',
'Comando',
'EZTV',
'ilCorSaRoNeRo',
'MagnetDL',
'MejorTorrent',
'RARBG',
'Rutor',
'Rutracker',
'ThePirateBay',
'Torrent9',
'TorrentGalaxy',
];
public static function getProviders()
{
return self::$providers;
}
}

View File

@@ -0,0 +1,115 @@
<?php
namespace App\Base\Util;
class QualityList
{
public static $qualities = [
"dvd-rip" => [
"dvdrip",
"dvdmux",
"dvdr",
"dvd-full",
"full-rip",
"iso rip",
"lossless rip",
"untouched rip",
"dvd-5",
"dvd-9",
],
"hdtv, pdtv or dsrip" => [
"dsr",
"dsrip",
"satrip",
"dthrip",
"dvbrip",
"hdtv",
"pdtv",
"dtvrip",
"tvrip",
"hdtvrip",
],
"vodrip" => [
"vodrip",
"vodr",
],
"hc hd-rip" => [
"hc",
"hd-rip",
],
"webcap" => [
"web-cap",
"webcap",
"web cap",
],
"hdrip" => [
"hdrip",
"web-dlrip",
],
"webrip" => [
"webrip",
"web rip",
"web-rip",
"webrip (p2p)",
"web rip (p2p)",
"web-rip (p2p)",
],
"web-dl" => [
"webdl",
"web dl",
"web-dl",
"web (scene)",
"webrip",
],
"blu-ray/bd/brrip" => [
"blu-ray",
"bluray",
"bluray",
"bdrip",
"brip",
"brrip",
"bdr[13]",
"bd25",
"bd50",
"bd66",
"bd100",
"bd5",
"bd9",
"bdmv",
"bdiso",
"complete.bluray",
],
"4k" => [
"cbr",
"vbr",
],
];
public static function getQualities(): array
{
return self::$qualities;
}
public static function getBaseQualities(): array
{
return array_keys(self::$qualities);
}
public static function getBaseQualityFromSubQuality(string $key): ?string
{
return array_search($key, self::$qualities) ?? null;
}
public static function getAsReverseMap(): array
{
$results = [];
foreach (self::$qualities as $baseQualtiy => $subQualities) {
foreach ($subQualities as $subQuality) {
$results[$subQuality] = $baseQualtiy;
}
}
return $results;
}
}