Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17de41dc57 | ||
|
|
d2eaccaf93 | ||
|
|
37e13347b2 | ||
|
|
7dd40b4525 | ||
|
|
2315b995e0 | ||
|
|
83401f2a7a | ||
|
|
50bcb4e1df |
6
.env
6
.env
@@ -61,4 +61,10 @@ NTFY_DSN=
|
|||||||
|
|
||||||
###> sentry/sentry-symfony ###
|
###> sentry/sentry-symfony ###
|
||||||
SENTRY_DSN=
|
SENTRY_DSN=
|
||||||
|
SENTRY_JS_URL=
|
||||||
###< sentry/sentry-symfony ###
|
###< sentry/sentry-symfony ###
|
||||||
|
|
||||||
|
# TMDB 'with_original_language' option
|
||||||
|
# - only include media originally
|
||||||
|
# produced in this language
|
||||||
|
TMDB_ORIGINAL_LANGUAGE=en
|
||||||
|
|||||||
1
assets/icons/bi/camera-video.svg
Normal file
1
assets/icons/bi/camera-video.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="https://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="currentColor" fill-rule="evenodd" d="M0 5a2 2 0 0 1 2-2h7.5a2 2 0 0 1 1.983 1.738l3.11-1.382A1 1 0 0 1 16 4.269v7.462a1 1 0 0 1-1.406.913l-3.111-1.382A2 2 0 0 1 9.5 13H2a2 2 0 0 1-2-2zm11.5 5.175l3.5 1.556V4.269l-3.5 1.556zM2 4a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h7.5a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1z"/></svg>
|
||||||
|
After Width: | Height: | Size: 374 B |
1
assets/icons/bi/cloud-download.svg
Normal file
1
assets/icons/bi/cloud-download.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="https://www.w3.org/2000/svg" viewBox="0 0 16 16"><g fill="currentColor"><path d="M4.406 1.342A5.53 5.53 0 0 1 8 0c2.69 0 4.923 2 5.166 4.579C14.758 4.804 16 6.137 16 7.773C16 9.569 14.502 11 12.687 11H10a.5.5 0 0 1 0-1h2.688C13.979 10 15 8.988 15 7.773c0-1.216-1.02-2.228-2.313-2.228h-.5v-.5C12.188 2.825 10.328 1 8 1a4.53 4.53 0 0 0-2.941 1.1c-.757.652-1.153 1.438-1.153 2.055v.448l-.445.049C2.064 4.805 1 5.952 1 7.318C1 8.785 2.23 10 3.781 10H6a.5.5 0 0 1 0 1H3.781C1.708 11 0 9.366 0 7.318c0-1.763 1.266-3.223 2.942-3.593c.143-.863.698-1.723 1.464-2.383"/><path d="M7.646 15.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 14.293V5.5a.5.5 0 0 0-1 0v8.793l-2.146-2.147a.5.5 0 0 0-.708.708z"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 720 B |
1
assets/icons/bi/download.svg
Normal file
1
assets/icons/bi/download.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="https://www.w3.org/2000/svg" viewBox="0 0 16 16"><g fill="currentColor"><path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5"/><path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708z"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 377 B |
@@ -4,7 +4,6 @@ when@prod:
|
|||||||
register_error_handler: true # Disables the ErrorListener, ExceptionListener and FatalErrorListener integrations of the base PHP SDK
|
register_error_handler: true # Disables the ErrorListener, ExceptionListener and FatalErrorListener integrations of the base PHP SDK
|
||||||
|
|
||||||
options:
|
options:
|
||||||
release: '%app.version%'
|
|
||||||
traces_sample_rate: 1
|
traces_sample_rate: 1
|
||||||
profiles_sample_rate: 1
|
profiles_sample_rate: 1
|
||||||
attach_stacktrace: true
|
attach_stacktrace: true
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
twig:
|
twig:
|
||||||
globals:
|
globals:
|
||||||
version: '%app.version%'
|
version: '%app.version%'
|
||||||
|
sentry_javascript_url: '%sentry.javascript_url%'
|
||||||
file_name_pattern: '*.twig'
|
file_name_pattern: '*.twig'
|
||||||
date:
|
date:
|
||||||
format: 'm/d/Y'
|
format: 'm/d/Y'
|
||||||
|
|||||||
@@ -48,6 +48,10 @@ parameters:
|
|||||||
notification.transport: '%env(NOTIFICATION_TRANSPORT)%'
|
notification.transport: '%env(NOTIFICATION_TRANSPORT)%'
|
||||||
notification.ntfy.dsn: '%env(NTFY_DSN)%'
|
notification.ntfy.dsn: '%env(NTFY_DSN)%'
|
||||||
|
|
||||||
|
# Sentry
|
||||||
|
sentry.dsn: '%env(SENTRY_DSN)%'
|
||||||
|
sentry.javascript_url: '%env(SENTRY_JS_URL)%'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# default configuration for services in *this* file
|
# default configuration for services in *this* file
|
||||||
_defaults:
|
_defaults:
|
||||||
|
|||||||
@@ -53,6 +53,15 @@ final class ConfigResolver
|
|||||||
|
|
||||||
#[Autowire(param: 'notification.ntfy.dsn')]
|
#[Autowire(param: 'notification.ntfy.dsn')]
|
||||||
private ?string $notificationNtfyDsn = null,
|
private ?string $notificationNtfyDsn = null,
|
||||||
|
|
||||||
|
#[Autowire(param: 'sentry.dsn')]
|
||||||
|
private ?string $sentryDsn = null,
|
||||||
|
|
||||||
|
#[Autowire(param: 'sentry.environment')]
|
||||||
|
private ?string $sentryEnvironment = null,
|
||||||
|
|
||||||
|
#[Autowire(param: 'sentry.javascript_url')]
|
||||||
|
private ?string $sentryJavascriptUrl = null,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function validate(): bool
|
public function validate(): bool
|
||||||
@@ -120,4 +129,13 @@ final class ConfigResolver
|
|||||||
]
|
]
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getSentryConfig()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'dsn' => $this->sentryDsn,
|
||||||
|
'environment' => $this->sentryEnvironment,
|
||||||
|
'javascript_url' => $this->sentryJavascriptUrl,
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ final class IndexController extends AbstractController
|
|||||||
#[Route('/test')]
|
#[Route('/test')]
|
||||||
public function monitorTvShow(MonitorTvShowHandler $handler): Response
|
public function monitorTvShow(MonitorTvShowHandler $handler): Response
|
||||||
{
|
{
|
||||||
// $handler->handle(new MonitorTvShowCommand(82));
|
throw new \Exception('Test');
|
||||||
return $this->render('index/test.html.twig', []);
|
return $this->render('index/test.html.twig', []);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,10 +29,13 @@ class CalendarController extends AbstractController
|
|||||||
|
|
||||||
$monitors = $monitorRepository->whereAirDateNotNull();
|
$monitors = $monitorRepository->whereAirDateNotNull();
|
||||||
$calendar->event(Map::from($monitors)->map(function (Monitor $monitor) {
|
$calendar->event(Map::from($monitors)->map(function (Monitor $monitor) {
|
||||||
return new Event($monitor->getTitle())
|
$event = new Event($monitor->getTitle())
|
||||||
->startsAt($monitor->getAirDate())
|
->startsAt($monitor->getAirDate())
|
||||||
->attachment($monitor->getPoster())
|
|
||||||
->fullDay();
|
->fullDay();
|
||||||
|
if (null !== $monitor->getPoster()) {
|
||||||
|
$event->attachment($monitor->getPoster());
|
||||||
|
}
|
||||||
|
return $event;
|
||||||
})->toArray());
|
})->toArray());
|
||||||
|
|
||||||
return new Response($calendar->get(), 200, [
|
return new Response($calendar->get(), 200, [
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ use Tmdb\Event\Listener\Request\ApiTokenRequestListener;
|
|||||||
use Tmdb\Event\Listener\Request\ContentTypeJsonRequestListener;
|
use Tmdb\Event\Listener\Request\ContentTypeJsonRequestListener;
|
||||||
use Tmdb\Event\Listener\Request\UserAgentRequestListener;
|
use Tmdb\Event\Listener\Request\UserAgentRequestListener;
|
||||||
use Tmdb\Event\RequestEvent;
|
use Tmdb\Event\RequestEvent;
|
||||||
|
use Tmdb\Repository\DiscoverRepository;
|
||||||
use Tmdb\Repository\MovieRepository;
|
use Tmdb\Repository\MovieRepository;
|
||||||
use Tmdb\Repository\SearchRepository;
|
use Tmdb\Repository\SearchRepository;
|
||||||
use Tmdb\Repository\TvEpisodeRepository;
|
use Tmdb\Repository\TvEpisodeRepository;
|
||||||
@@ -30,6 +31,7 @@ use Tmdb\Token\Api\BearerToken;
|
|||||||
class TmdbClient
|
class TmdbClient
|
||||||
{
|
{
|
||||||
const POSTER_IMG_PATH = "https://image.tmdb.org/t/p/w500";
|
const POSTER_IMG_PATH = "https://image.tmdb.org/t/p/w500";
|
||||||
|
const APPEND_TO_RESPONSE = 'external_ids,credits,watch/providers';
|
||||||
|
|
||||||
protected Client $client;
|
protected Client $client;
|
||||||
protected MovieRepository $movieRepository;
|
protected MovieRepository $movieRepository;
|
||||||
@@ -38,6 +40,8 @@ class TmdbClient
|
|||||||
protected TvEpisodeRepository $tvEpisodeRepository;
|
protected TvEpisodeRepository $tvEpisodeRepository;
|
||||||
protected SearchRepository $searchRepository;
|
protected SearchRepository $searchRepository;
|
||||||
|
|
||||||
|
protected DiscoverRepository $discoverRepository;
|
||||||
|
|
||||||
protected array $mediaTypeMap = [
|
protected array $mediaTypeMap = [
|
||||||
MediaType::Movie->value => MediaType::Movie->value,
|
MediaType::Movie->value => MediaType::Movie->value,
|
||||||
MediaType::TvShow->value => MediaType::TvShow->value,
|
MediaType::TvShow->value => MediaType::TvShow->value,
|
||||||
@@ -48,11 +52,14 @@ class TmdbClient
|
|||||||
|
|
||||||
protected $repos = [];
|
protected $repos = [];
|
||||||
|
|
||||||
|
private ?string $originalLanguage = 'en';
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly SerializerInterface $serializer,
|
private readonly SerializerInterface $serializer,
|
||||||
private readonly CacheItemPoolInterface $cache,
|
private readonly CacheItemPoolInterface $cache,
|
||||||
private readonly EventDispatcherInterface $eventDispatcher,
|
private readonly EventDispatcherInterface $eventDispatcher,
|
||||||
#[Autowire(env: 'TMDB_API')] string $apiKey,
|
#[Autowire(env: 'TMDB_API')] string $apiKey,
|
||||||
|
#[Autowire(env: 'TMDB_ORIGINAL_LANGUAGE')] ?string $originalLanguage = null,
|
||||||
) {
|
) {
|
||||||
$this->client = new Client(
|
$this->client = new Client(
|
||||||
[
|
[
|
||||||
@@ -74,7 +81,7 @@ class TmdbClient
|
|||||||
'hydration' => [
|
'hydration' => [
|
||||||
'event_listener_handles_hydration' => false,
|
'event_listener_handles_hydration' => false,
|
||||||
'only_for_specified_models' => []
|
'only_for_specified_models' => []
|
||||||
]
|
],
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -107,11 +114,14 @@ class TmdbClient
|
|||||||
$this->tvSeasonRepository = new TvSeasonRepository($this->client);
|
$this->tvSeasonRepository = new TvSeasonRepository($this->client);
|
||||||
$this->tvEpisodeRepository = new TvEpisodeRepository($this->client);
|
$this->tvEpisodeRepository = new TvEpisodeRepository($this->client);
|
||||||
$this->searchRepository = new SearchRepository($this->client);
|
$this->searchRepository = new SearchRepository($this->client);
|
||||||
|
$this->discoverRepository = new DiscoverRepository($this->client);
|
||||||
|
|
||||||
$this->repos = [
|
$this->repos = [
|
||||||
MediaType::Movie->value => $this->movieRepository,
|
MediaType::Movie->value => $this->movieRepository,
|
||||||
MediaType::TvShow->value => $this->tvRepository,
|
MediaType::TvShow->value => $this->tvRepository,
|
||||||
MediaType::TvEpisode->value => $this->tvEpisodeRepository,
|
MediaType::TvEpisode->value => $this->tvEpisodeRepository,
|
||||||
];
|
];
|
||||||
|
$this->originalLanguage = $originalLanguage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function search(string $term): TmdbResult|Map
|
public function search(string $term): TmdbResult|Map
|
||||||
@@ -133,7 +143,7 @@ class TmdbClient
|
|||||||
{
|
{
|
||||||
$tmdbId = $this->findByImdbId($imdbId)['id'];
|
$tmdbId = $this->findByImdbId($imdbId)['id'];
|
||||||
return $this->parseResult(
|
return $this->parseResult(
|
||||||
$this->movieRepository->getApi()->getMovie($tmdbId, ['append_to_response' => 'external_ids,credits']),
|
$this->movieRepository->getApi()->getMovie($tmdbId, ['append_to_response' => static::APPEND_TO_RESPONSE]),
|
||||||
MediaType::Movie->value,
|
MediaType::Movie->value,
|
||||||
$imdbId
|
$imdbId
|
||||||
);
|
);
|
||||||
@@ -142,7 +152,7 @@ class TmdbClient
|
|||||||
public function tvshowDetails(string $imdbId): ?TmdbResult
|
public function tvshowDetails(string $imdbId): ?TmdbResult
|
||||||
{
|
{
|
||||||
$tmdbId = $this->findByImdbId($imdbId)['id'];
|
$tmdbId = $this->findByImdbId($imdbId)['id'];
|
||||||
$media = $this->tvRepository->getApi()->getTvShow($tmdbId, ['append_to_response' => 'external_ids,credits']);
|
$media = $this->tvRepository->getApi()->getTvShow($tmdbId, ['append_to_response' => static::APPEND_TO_RESPONSE]);
|
||||||
|
|
||||||
$media['seasons'] = Map::from($media['seasons'])->filter(function ($data) {
|
$media['seasons'] = Map::from($media['seasons'])->filter(function ($data) {
|
||||||
return $data['season_number'] !== 0 &&
|
return $data['season_number'] !== 0 &&
|
||||||
@@ -163,7 +173,7 @@ class TmdbClient
|
|||||||
|
|
||||||
public function tvSeasonDetails(string $tmdbId, int $season): array
|
public function tvSeasonDetails(string $tmdbId, int $season): array
|
||||||
{
|
{
|
||||||
$result = $this->tvSeasonRepository->getApi()->getSeason($tmdbId, $season, ['append_to_response' => 'external_ids,credits']);
|
$result = $this->tvSeasonRepository->getApi()->getSeason($tmdbId, $season, ['append_to_response' => static::APPEND_TO_RESPONSE]);
|
||||||
$result['episodes'] = Map::from($result['episodes'])->map(function ($data) {
|
$result['episodes'] = Map::from($result['episodes'])->map(function ($data) {
|
||||||
$data['still_path'] = self::POSTER_IMG_PATH . $data['still_path'];
|
$data['still_path'] = self::POSTER_IMG_PATH . $data['still_path'];
|
||||||
$data['poster'] = $data['still_path'];
|
$data['poster'] = $data['still_path'];
|
||||||
@@ -174,7 +184,7 @@ class TmdbClient
|
|||||||
|
|
||||||
public function tvEpisodeDetails(string $tmdbId, string $showImdbId, int $season, int $episode): TmdbResult|TmdbEpisodeDto|null
|
public function tvEpisodeDetails(string $tmdbId, string $showImdbId, int $season, int $episode): TmdbResult|TmdbEpisodeDto|null
|
||||||
{
|
{
|
||||||
$result = $this->tvEpisodeRepository->getApi()->getEpisode($tmdbId, $season, $episode, ['append_to_response' => 'external_ids,credits']);
|
$result = $this->tvEpisodeRepository->getApi()->getEpisode($tmdbId, $season, $episode, ['append_to_response' => static::APPEND_TO_RESPONSE]);
|
||||||
return $this->parseResult(
|
return $this->parseResult(
|
||||||
$result,
|
$result,
|
||||||
MediaType::TvEpisode->value,
|
MediaType::TvEpisode->value,
|
||||||
@@ -193,7 +203,10 @@ class TmdbClient
|
|||||||
|
|
||||||
public function popularMovies(int $resultCount = 6): Map
|
public function popularMovies(int $resultCount = 6): Map
|
||||||
{
|
{
|
||||||
$results = $this->movieRepository->getApi()->getPopular();
|
$results = $this->discoverRepository->getApi()->discoverMovies([
|
||||||
|
'with_original_language' => $this->originalLanguage,
|
||||||
|
'append_to_response' => 'external_ids,watch/providers',
|
||||||
|
]);
|
||||||
$results['results'] = Map::from($results['results'])->map(function ($result) {
|
$results['results'] = Map::from($results['results'])->map(function ($result) {
|
||||||
$result['media_type'] = MediaType::Movie->value;
|
$result['media_type'] = MediaType::Movie->value;
|
||||||
return $result;
|
return $result;
|
||||||
@@ -206,7 +219,10 @@ class TmdbClient
|
|||||||
|
|
||||||
public function popularTvShows(int $resultCount = 6): Map
|
public function popularTvShows(int $resultCount = 6): Map
|
||||||
{
|
{
|
||||||
$results = $this->tvRepository->getApi()->getPopular();
|
$results = $this->discoverRepository->getApi()->discoverTv([
|
||||||
|
'with_original_language' => $this->originalLanguage,
|
||||||
|
'append_to_response' => 'external_ids,watch/providers',
|
||||||
|
]);
|
||||||
$results['results'] = Map::from($results['results'])->map(function ($result) {
|
$results['results'] = Map::from($results['results'])->map(function ($result) {
|
||||||
$result['media_type'] = MediaType::TvShow->value;
|
$result['media_type'] = MediaType::TvShow->value;
|
||||||
return $result;
|
return $result;
|
||||||
|
|||||||
@@ -68,4 +68,10 @@ class Torrentio
|
|||||||
|
|
||||||
return $results;
|
return $results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getDestinationUrl(string $url)
|
||||||
|
{
|
||||||
|
$request = get_headers($url)[8];
|
||||||
|
return explode(' ', $request)[1];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use App\Torrentio\Action\Handler\GetMovieOptionsHandler;
|
|||||||
use App\Torrentio\Action\Handler\GetTvShowOptionsHandler;
|
use App\Torrentio\Action\Handler\GetTvShowOptionsHandler;
|
||||||
use App\Torrentio\Action\Input\GetMovieOptionsInput;
|
use App\Torrentio\Action\Input\GetMovieOptionsInput;
|
||||||
use App\Torrentio\Action\Input\GetTvShowOptionsInput;
|
use App\Torrentio\Action\Input\GetTvShowOptionsInput;
|
||||||
|
use App\Torrentio\Client\Torrentio;
|
||||||
use App\Torrentio\Exception\TorrentioRateLimitException;
|
use App\Torrentio\Exception\TorrentioRateLimitException;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use OneToMany\RichBundle\Contract\ResultInterface;
|
use OneToMany\RichBundle\Contract\ResultInterface;
|
||||||
@@ -21,6 +22,8 @@ use Symfony\UX\Turbo\TurboBundle;
|
|||||||
|
|
||||||
final class WebController extends AbstractController
|
final class WebController extends AbstractController
|
||||||
{
|
{
|
||||||
|
const REAL_DEBRID_STREAM_URL = 'https://real-debrid.com/streaming-%s';
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly GetMovieOptionsHandler $getMovieOptionsHandler,
|
private readonly GetMovieOptionsHandler $getMovieOptionsHandler,
|
||||||
private readonly GetTvShowOptionsHandler $getTvShowOptionsHandler,
|
private readonly GetTvShowOptionsHandler $getTvShowOptionsHandler,
|
||||||
@@ -99,4 +102,14 @@ final class WebController extends AbstractController
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Route('/torrentio/stream/{url}', name: 'app.torrentio.stream')]
|
||||||
|
public function streamVideo(string $url, Torrentio $torrentio): Response
|
||||||
|
{
|
||||||
|
$destinationUrl = $torrentio->getDestinationUrl(\base64_decode($url));
|
||||||
|
$urlPathParts = explode('/', parse_url($destinationUrl)['path']);
|
||||||
|
$videoId = $urlPathParts[2];
|
||||||
|
$url = sprintf(self::REAL_DEBRID_STREAM_URL, $videoId);
|
||||||
|
return $this->redirect($url);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,4 +112,18 @@ class UtilExtension
|
|||||||
|
|
||||||
return new EpisodeIdDto($season, $episode);
|
return new EpisodeIdDto($season, $episode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[AsTwigFunction('sentry_enabled')]
|
||||||
|
public function sentryEnabled(): bool
|
||||||
|
{
|
||||||
|
$sentryConfig = $this->config->getSentryConfig();
|
||||||
|
return $sentryConfig['javascript_url'] !== null &&
|
||||||
|
$sentryConfig['javascript_url'] !== '';
|
||||||
|
}
|
||||||
|
|
||||||
|
#[AsTwigFilter('base64_encode')]
|
||||||
|
public function base64_encode(string $data): string
|
||||||
|
{
|
||||||
|
return \base64_encode($data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
{% block javascripts %}
|
{% block javascripts %}
|
||||||
{% block importmap %}{{ importmap('app') }}{% endblock %}
|
{% block importmap %}{{ importmap('app') }}{% endblock %}
|
||||||
|
<script src="https://bugs.caldwell.digital/js-sdk-loader/8dddf7fb26fbec602ad212173a942450.min.js" crossorigin="anonymous"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-cyan-950 flex flex-col h-full">
|
<body class="bg-cyan-950 flex flex-col h-full">
|
||||||
|
|||||||
@@ -12,6 +12,10 @@
|
|||||||
|
|
||||||
{% block javascripts %}
|
{% block javascripts %}
|
||||||
{% block importmap %}{{ importmap('app') }}{% endblock %}
|
{% block importmap %}{{ importmap('app') }}{% endblock %}
|
||||||
|
|
||||||
|
{% if sentry_enabled() %}
|
||||||
|
<script src="{{ sentry_javascript_url }}" crossorigin="anonymous"></script>
|
||||||
|
{% endif %}
|
||||||
<script src='https://cdn.jsdelivr.net/npm/fullcalendar@6.1.19/index.global.min.js'></script>
|
<script src='https://cdn.jsdelivr.net/npm/fullcalendar@6.1.19/index.global.min.js'></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
@@ -86,9 +86,18 @@
|
|||||||
{{ result.languageFlags|raw }}
|
{{ result.languageFlags|raw }}
|
||||||
</td>
|
</td>
|
||||||
<td class="px-4 py-4 whitespace-nowrap text-sm text-end text-gray-800 dark:text-gray-50 flex flex-row gap-2 items-center justify-start mb:justify-end">
|
<td class="px-4 py-4 whitespace-nowrap text-sm text-end text-gray-800 dark:text-gray-50 flex flex-row gap-2 items-center justify-start mb:justify-end">
|
||||||
<button class="download-btn p-1.5 bg-green-600 rounded-md text-gray-50">
|
<a href="{{ url('app.torrentio.stream', {url: result.url|base64_encode}) }}" title="Stream in your browser." class="p-1.5 bg-blue-600 rounded-md text-gray-50">
|
||||||
Download
|
<twig:ux:icon name="bi:camera-video" width="20"/>
|
||||||
</button>
|
</a>
|
||||||
|
|
||||||
|
<button class="download-btn p-1.5 bg-green-600 rounded-md text-gray-50" title="Download to your mounted directory.">
|
||||||
|
<twig:ux:icon name="bi:cloud-download" width="20" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<a href="{{ result.url }}" title="Download to your local device." class="p-1.5 bg-orange-500 rounded-md text-gray-50">
|
||||||
|
<twig:ux:icon name="bi:download" width="20" />
|
||||||
|
</a>
|
||||||
|
|
||||||
<label for="select">
|
<label for="select">
|
||||||
<input id="select" type="checkbox" name="select" />
|
<input id="select" type="checkbox" name="select" />
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
7
templates/torrentio/stream.html.twig
Normal file
7
templates/torrentio/stream.html.twig
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{% extends 'base.html.twig' %}
|
||||||
|
|
||||||
|
{% block title %}Streaming — Torrentio{% endblock %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<iframe width="100%" height="100%" src="{{ stream_url }}"></iframe>
|
||||||
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user