arnaucube
arnaucube/configsPublic Self-hosted repository
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>