configs/server-dockers/jellyfin-news/index.html
server-dockers/jellyfin-news/index.htmlRaw
1<!doctype html>
2<html lang="en">
3<head>
4 <meta charset="utf-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1">
6 <title>Jellyfin updates</title>
7 <style>
8 body { max-width: 760px; margin: 2rem auto; padding: 0 1rem; font: 16px/1.5 system-ui, sans-serif; color: #202124; }
9 h1 { margin-bottom: .25rem; }
10 .controls { display: flex; flex-wrap: wrap; gap: .65rem; margin: 1.5rem 0; align-items: center; }
11 button, input, select { font: inherit; padding: .6rem .8rem; }
12 button { cursor: pointer; }
13 input { width: 5rem; }
14 #status { min-height: 1.5rem; color: #555; }
15 textarea { box-sizing: border-box; width: 100%; min-height: 22rem; padding: 1rem; font: 15px/1.5 ui-monospace, monospace; resize: vertical; }
16 [hidden] { display: none; }
17 .copy { margin-top: .65rem; }
18 </style>
19</head>
20<body>
21 <h1>Jellyfin updates</h1>
22 <p>Choose a time period, then generate a newly added or watched recap.</p>
23
24 <div class="controls">
25 <label for="period">Time period:</label>
26 <select id="period">
27 <option value="current-week">Current week</option>
28 <option value="week">Past week</option>
29 <option value="month">Past month</option>
30 </select>
31 </div>
32
33 <div id="week-controls" class="controls" hidden>
34 <label for="week-offset">Week offset:</label>
35 <input id="week-offset" type="number" min="-1200" max="-1" value="-1" required>
36 <span>−1 is last week, −2 is two weeks ago</span>
37 </div>
38
39 <div id="month-controls" class="controls" hidden>
40 <label for="month-offset">Month offset:</label>
41 <input id="month-offset" type="number" min="-1200" max="-1" value="-1" required>
42 <span>−1 is last month, −2 is two months ago</span>
43 </div>
44
45 <div class="controls">
46 <button id="added">Newly added in selected period</button>
47 <button id="watched">{{WATCHED_BUTTON}}</button>
48 </div>
49
50 <p id="status" role="status"></p>
51 <textarea id="output" readonly aria-label="Jellyfin update output" placeholder="Your recap will appear here"></textarea>
52 <button id="copy" class="copy">Copy to clipboard</button>
53
54 <script>
55 const period = document.querySelector('#period');
56 const output = document.querySelector('#output');
57 const status = document.querySelector('#status');
58 const controls = [...document.querySelectorAll('button, input, select')];
59
60 async function loadReport(url) {
61 controls.forEach(control => control.disabled = true);
62 status.textContent = 'Loading…';
63 try {
64 const response = await fetch(url, { headers: { Accept: 'text/plain' } });
65 const text = await response.text();
66 if (!response.ok) throw new Error(text.trim() || `Request failed (${response.status})`);
67 output.value = text;
68 status.textContent = 'Recap ready.';
69 } catch (error) {
70 status.textContent = error.message;
71 } finally {
72 controls.forEach(control => control.disabled = false);
73 }
74 }
75
76 // Period changes only update the selection; either report can run first.
77 function updatePeriod() {
78 document.querySelector('#week-controls').hidden = period.value !== 'week';
79 document.querySelector('#month-controls').hidden = period.value !== 'month';
80 output.value = '';
81 status.textContent = '';
82 }
83 period.addEventListener('change', updatePeriod);
84 document.querySelectorAll('input').forEach(input => input.addEventListener('input', updatePeriod));
85 updatePeriod();
86
87 function generateReport(watched) {
88 const selectedPeriod = period.value;
89 const params = new URLSearchParams();
90 if (selectedPeriod !== 'current-week') {
91 const offset = document.querySelector(`#${selectedPeriod}-offset`);
92 if (!offset.reportValidity()) return;
93 params.set('offset', offset.value);
94 }
95 if (watched) params.set('period', selectedPeriod);
96 const path = watched ? '/api/watched' : `/api/${selectedPeriod}`;
97 loadReport(`${path}${params.size ? `?${params}` : ''}`);
98 }
99 document.querySelector('#added').addEventListener('click', () => generateReport(false));
100 document.querySelector('#watched').addEventListener('click', () => generateReport(true));
101
102 document.querySelector('#copy').addEventListener('click', async () => {
103 if (!output.value) return;
104 try {
105 await navigator.clipboard.writeText(output.value);
106 } catch (_) {
107 output.select();
108 document.execCommand('copy');
109 output.setSelectionRange(0, 0);
110 }
111 status.textContent = 'Copied to clipboard.';
112 });
113 </script>
114</body>
115</html>