Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b587302b30 | |||
| e5bab8e6fd | |||
| 502b85dda4 | |||
| 9c430290e9 | |||
| 583591bf4f |
2
.env
2
.env
@@ -57,4 +57,4 @@ OIDC_BYPASS_FORM_LOGIN=false
|
||||
###< symfony/ntfy-notifier ###
|
||||
|
||||
NOTIFICATION_TRANSPORT=
|
||||
NTFY_DNS=
|
||||
NTFY_DSN=
|
||||
|
||||
1
assets/icons/lets-icons/calendar-add-light.svg
Normal file
1
assets/icons/lets-icons/calendar-add-light.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="https://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="none" stroke="currentColor"><path d="M19.5 9.5v-.8c0-1.12 0-1.68-.218-2.108a2 2 0 0 0-.874-.874C17.98 5.5 17.42 5.5 16.3 5.5H7.7c-1.12 0-1.68 0-2.108.218a2 2 0 0 0-.874.874C4.5 7.02 4.5 7.58 4.5 8.7v.8m15 0v6.8c0 1.12 0 1.68-.218 2.108a2 2 0 0 1-.874.874c-.428.218-.988.218-2.108.218H7.7c-1.12 0-1.68 0-2.108-.218a2 2 0 0 1-.874-.874C4.5 17.98 4.5 17.42 4.5 16.3V9.5m15 0h-15"/><path stroke-linecap="round" d="M8.5 3.5v4m7-4v4M12 17v-5m2.5 2.5h-5"/></g></svg>
|
||||
|
After Width: | Height: | Size: 529 B |
1
assets/icons/solar/calendar-linear.svg
Normal file
1
assets/icons/solar/calendar-linear.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="https://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="none"><path stroke="currentColor" stroke-width="1.5" d="M2 12c0-3.771 0-5.657 1.172-6.828S6.229 4 10 4h4c3.771 0 5.657 0 6.828 1.172S22 8.229 22 12v2c0 3.771 0 5.657-1.172 6.828S17.771 22 14 22h-4c-3.771 0-5.657 0-6.828-1.172S2 17.771 2 14z"/><path stroke="currentColor" stroke-linecap="round" stroke-width="1.5" d="M7 4V2.5M17 4V2.5M2.5 9h19"/><path fill="currentColor" d="M18 17a1 1 0 1 1-2 0a1 1 0 0 1 2 0m0-4a1 1 0 1 1-2 0a1 1 0 0 1 2 0m-5 4a1 1 0 1 1-2 0a1 1 0 0 1 2 0m0-4a1 1 0 1 1-2 0a1 1 0 0 1 2 0m-5 4a1 1 0 1 1-2 0a1 1 0 0 1 2 0m0-4a1 1 0 1 1-2 0a1 1 0 0 1 2 0"/></g></svg>
|
||||
|
After Width: | Height: | Size: 653 B |
@@ -29,6 +29,7 @@
|
||||
|
||||
:root {
|
||||
--fc-border-color: #a65b27;
|
||||
--fc-page-bg-color: #a65b27;
|
||||
}
|
||||
|
||||
/* Prevent scrolling while dialog is open */
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"php-tmdb/api": "^4.1",
|
||||
"predis/predis": "^2.4",
|
||||
"runtime/frankenphp-symfony": "^0.2.0",
|
||||
"spatie/icalendar-generator": "^3.0",
|
||||
"spomky-labs/pwa-bundle": "^1.2",
|
||||
"stof/doctrine-extensions-bundle": "^1.14",
|
||||
"symfony/asset": "7.3.*",
|
||||
|
||||
61
composer.lock
generated
61
composer.lock
generated
@@ -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": "6f57ba35ae317ec6370836bda0012db8",
|
||||
"content-hash": "9ffd10f98137e8975de2c04ac2412ed5",
|
||||
"packages": [
|
||||
{
|
||||
"name": "1tomany/rich-bundle",
|
||||
@@ -4757,6 +4757,65 @@
|
||||
],
|
||||
"time": "2023-12-12T12:06:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "spatie/icalendar-generator",
|
||||
"version": "3.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/spatie/icalendar-generator.git",
|
||||
"reference": "32797f6e5afa3142d073f38d5f22ab377f4d8f90"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/spatie/icalendar-generator/zipball/32797f6e5afa3142d073f38d5f22ab377f4d8f90",
|
||||
"reference": "32797f6e5afa3142d073f38d5f22ab377f4d8f90",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-mbstring": "*",
|
||||
"php": "^8.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-json": "*",
|
||||
"larapack/dd": "^1.1",
|
||||
"nesbot/carbon": "^3.5",
|
||||
"pestphp/pest": "^2.34",
|
||||
"phpstan/phpstan": "^2.0",
|
||||
"spatie/pest-plugin-snapshots": "^2.1"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Spatie\\IcalendarGenerator\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ruben Van Assche",
|
||||
"email": "ruben@spatie.be",
|
||||
"homepage": "https://spatie.be",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Build calendars in the iCalendar format",
|
||||
"homepage": "https://github.com/spatie/icalendar-generator",
|
||||
"keywords": [
|
||||
"calendar",
|
||||
"iCalendar",
|
||||
"ical",
|
||||
"ics",
|
||||
"spatie"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/spatie/icalendar-generator/issues",
|
||||
"source": "https://github.com/spatie/icalendar-generator/tree/3.0.0"
|
||||
},
|
||||
"time": "2025-04-17T14:50:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "spomky-labs/pki-framework",
|
||||
"version": "1.3.0",
|
||||
|
||||
@@ -71,13 +71,33 @@ class ApiController extends AbstractController
|
||||
#[Route('/api/monitor/upcoming-episodes', name: 'api.monitor.upcoming-episodes', methods: ['GET'])]
|
||||
public function upcomingEpisodes(MonitorRepository $repository): Response
|
||||
{
|
||||
$colors = [
|
||||
'blue' => '#007bff',
|
||||
'indigo' => '#6610f2',
|
||||
'purple' => '#6f42c1',
|
||||
'pink' => '#e83e8c',
|
||||
'red' => '#dc3545',
|
||||
'orange' => '#fd7e14',
|
||||
'yellow' => '#ffc107',
|
||||
'green' => '#28a745',
|
||||
'teal' => '#20c997',
|
||||
'cyan' => '#17a2b8',
|
||||
];
|
||||
|
||||
$eventColors = [];
|
||||
$monitors = $repository->whereAirDateNotNull();
|
||||
$monitors = Map::from($monitors)->map(function ($monitor) {
|
||||
$monitors = Map::from($monitors)->map(function ($monitor) use (&$eventColors, $colors) {
|
||||
if (!array_key_exists($monitor->getImdbId(), $eventColors)) {
|
||||
$eventColors[$monitor->getImdbId()] = $colors[array_rand($colors)];
|
||||
}
|
||||
return [
|
||||
'id' => $monitor->getId(),
|
||||
'title' => $monitor->getTitle() . ' (S' . str_pad($monitor->getSeason(), 2, '0', STR_PAD_LEFT) . 'E' . str_pad($monitor->getEpisode(), 2, '0', STR_PAD_LEFT) . ')',
|
||||
'start' => $monitor->getAirDate()->format('Y-m-d H:i:s'),
|
||||
'allDay' => true
|
||||
'groupId' => $monitor->getImdbId(),
|
||||
'allDay' => true,
|
||||
'backgroundColor' => $eventColors[$monitor->getImdbId()],
|
||||
'borderColor' => $eventColors[$monitor->getImdbId()],
|
||||
];
|
||||
});
|
||||
|
||||
|
||||
33
src/Monitor/Framework/Controller/CalendarController.php
Normal file
33
src/Monitor/Framework/Controller/CalendarController.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Monitor\Framework\Controller;
|
||||
|
||||
use Aimeos\Map;
|
||||
use App\Monitor\Framework\Repository\MonitorRepository;
|
||||
use Spatie\IcalendarGenerator\Components\Calendar;
|
||||
use Spatie\IcalendarGenerator\Components\Event;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
|
||||
class CalendarController extends AbstractController
|
||||
{
|
||||
#[Route('/monitors/ical.ics', name: 'app.monitors.ical')]
|
||||
public function icalAction(MonitorRepository $monitorRepository)
|
||||
{
|
||||
$calendar = new Calendar();
|
||||
$monitors = $monitorRepository->whereAirDateNotNull();
|
||||
$events = Map::from($monitors)->map(function ($monitor) {
|
||||
return new Event($monitor->getTitle())
|
||||
->startsAt($monitor->getAirDate())
|
||||
->withoutTimezone()
|
||||
->fullDay()
|
||||
;
|
||||
});
|
||||
$calendar->event($events->toArray());
|
||||
return new Response($calendar->get(), 200, [
|
||||
'Content-Type' => 'text/calendar',
|
||||
'Content-Disposition' => 'attachment; filename="upcoming-episodes.ics"',
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -6,11 +6,16 @@
|
||||
<div class="md:flex md:items-center md:gap-12">
|
||||
<nav aria-label="Global" class="md:block">
|
||||
<ul class="flex items-center gap-6 text-sm">
|
||||
<li>
|
||||
<a href="{{ path('app.monitor.upcoming-episodes') }}" data-turbo="false" title="View upcoming episodes of the shows you're subscribed to.">
|
||||
<twig:ux:icon name="solar:calendar-linear" width="25px" class="text-orange-500" />
|
||||
</a>
|
||||
</li>
|
||||
<li class="hidden">
|
||||
<twig:ux:icon name="fluent:alert-12-regular" width="30px" class="text-gray-950 bg-orange-500 rounded-full p-2"/>
|
||||
</li>
|
||||
<li class="hidden md:block">
|
||||
<a href="{{ path('app_logout') }}">
|
||||
<a href="{{ path('app_logout') }}" title="Log out of Torsearch.">
|
||||
<twig:ux:icon name="material-symbols:logout" width="25px" class="text-orange-500" />
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@@ -16,20 +16,33 @@
|
||||
|
||||
<div class="p-4">
|
||||
<twig:Card title="Upcoming episodes of shows your monitoring">
|
||||
<a href="{{ path('app.monitors.ical') }}" title="Subscribe to the 'Upcoming Episodes' calendar via iCal. Click to export the events to a .ics file or copy the link and use it to subscribe in a calendar app that supports iCal/ics calendars." class="mb-2 self-end dark:text-white decoration-underline">
|
||||
<twig:ux:icon name="lets-icons:calendar-add-light" width="24" class="text-orange-500" />
|
||||
</a>
|
||||
<div id="calendar" class="text-white">
|
||||
</div>
|
||||
</twig:Card>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function getView() {
|
||||
if (window.innerWidth < 768) {
|
||||
return 'listWeek';
|
||||
} else {
|
||||
return 'dayGridMonth';
|
||||
}
|
||||
}
|
||||
document.addEventListener('DOMContentLoaded', async function() {
|
||||
let data = await fetch('/api/monitor/upcoming-episodes');
|
||||
data = (await data.json())['data'];
|
||||
|
||||
const calendarEl = document.getElementById('calendar');
|
||||
const calendar = new FullCalendar.Calendar(calendarEl, {
|
||||
initialView: 'dayGridMonth',
|
||||
initialView: getView(),
|
||||
events: data['episodes'],
|
||||
windowResize: function(arg) {
|
||||
this.changeView(getView());
|
||||
}
|
||||
});
|
||||
calendar.render();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user