Last active 2 weeks ago

See what's coming and if the episode was grabbed

kody's Avatar kody revised this gist 2 weeks ago. Go to revision

2 files changed, 182 insertions

index.html(file created)

@@ -0,0 +1,125 @@
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.0">
6 + <meta name="robots" content="noindex">
7 + <title>Schedule</title>
8 +
9 + <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/water.css@2/out/water.min.css">
10 + <style>
11 + body {
12 + overflow-y: scroll;
13 + }
14 +
15 + h1 {
16 + margin: 1em 0;
17 + }
18 +
19 + .nopics {
20 + display: none;
21 + }
22 +
23 + h3 {
24 + margin: 2em 0 0.5em;
25 + }
26 +
27 + .episode {
28 + align-items: center;
29 + border-left: 4px solid transparent;
30 + display: flex;
31 + margin-bottom: 1em;
32 + padding-left: 5px;
33 + }
34 +
35 + .episode-ready {
36 + border-left: 4px solid green;
37 + }
38 +
39 + .episode-wait {
40 + border-left: 4px solid red;
41 + }
42 +
43 + img {
44 + height: 100px;
45 + width: 68px;
46 + }
47 +
48 + .details {
49 + padding: 0 1em;
50 + }
51 + </style>
52 + </head>
53 + <body>
54 + <h1>Schedule</h1>
55 + <small class="nopics">(Can't see pictures? Disable your adblock)</small>
56 + <div class="stuff">Loading the stuff...</div>
57 + <script>
58 + function buildPage (j) {
59 + const container = document.createElement('div')
60 + container.className = 'stuff'
61 +
62 + let lastDate = 0
63 +
64 + j.forEach((i) => {
65 + const d = new Date(i.timestamp)
66 + let waiting = d < (new Date())
67 +
68 + if (d.getDate() !== lastDate) {
69 + lastDate = d.getDate()
70 + const dayTitle = document.createElement('h3')
71 + dayTitle.innerHTML = d.toLocaleDateString()
72 + container.appendChild(dayTitle)
73 + }
74 +
75 + const e = document.createElement('div')
76 +
77 + const imgPoster = i.images.find((el) => el.coverType === 'poster')
78 + let img
79 + if (imgPoster) {
80 + img = document.createElement('img')
81 + img.src = imgPoster.remoteUrl
82 + img.loading = 'lazy'
83 + img.height = '100'
84 + img.width = '68'
85 + img.onerror = () => {
86 + document.querySelector('.nopics').style.display = 'block'
87 + }
88 + }
89 +
90 + const ed = document.createElement('div')
91 + ed.className = 'details'
92 + let edbody = `
93 + <b>${i.show}</b><br/>
94 + S${i.season.toString().padStart(2, '0')}E${i.episode.toString().padStart(2, '0')}
95 + - ${i.title}<br/><br/>
96 + `
97 +
98 + if (i.ready) {
99 + const qual = i.quality.split('-')
100 + edbody += `Available in ${qual[qual.length - 1]} <small>(${i.mediainfo.videoCodec}/${i.mediainfo.audioCodec})</small>`
101 + } else if (waiting) {
102 + edbody += `Aired at ${d.toLocaleTimeString({}, { hour: '2-digit', minute: '2-digit' })}, waiting for import`
103 + } else {
104 + edbody += `Airs at ${d.toLocaleTimeString({}, { hour: '2-digit', minute: '2-digit' })}`
105 + }
106 + ed.innerHTML = edbody
107 +
108 + if (imgPoster) e.appendChild(img)
109 + e.appendChild(ed)
110 +
111 + e.className = 'episode' + (i.ready ? ' episode-ready' : waiting ? ' episode-wait' : '')
112 + container.appendChild(e)
113 + })
114 +
115 + document.querySelector('.stuff').replaceWith(container)
116 + }
117 +
118 + document.addEventListener('DOMContentLoaded', () => {
119 + fetch('stuff.php')
120 + .then((r) => r.json())
121 + .then(buildPage)
122 + })
123 + </script>
124 + </body>
125 + </html>

stuff.php(file created)

@@ -0,0 +1,57 @@
1 + <?php
2 + $domain = ''; // EDIT ME (eg. 'sonarr.example.com')
3 + $apikey = ''; // EDIT ME (found in Settings > General)
4 +
5 + $start = date('Y-m-d', strtotime('-1 days'));
6 + $end = date('Y-m-d', strtotime('+7 days'));
7 +
8 + // ---
9 +
10 + $url = sprintf(
11 + 'https://%s/api/v3/calendar?start=%s&end=%s&includeSeries=true&includeEpisodeFile=true&includeEpisodeImages=true',
12 + $domain,
13 + $start,
14 + $end,
15 + );
16 +
17 + $curl = curl_init();
18 + curl_setopt_array($curl, array(
19 + CURLOPT_URL => $url,
20 + CURLOPT_RETURNTRANSFER => true,
21 + CURLOPT_SSL_VERIFYHOST => 0,
22 + CURLOPT_SSL_VERIFYPEER => 0,
23 + CURLOPT_ENCODING => '',
24 + CURLOPT_MAXREDIRS => 10,
25 + CURLOPT_TIMEOUT => 0,
26 + CURLOPT_FOLLOWLOCATION => true,
27 + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
28 + CURLOPT_HTTPHEADER => array(
29 + 'X-Api-Key: ' . $apikey,
30 + )
31 + ));
32 +
33 + $response = curl_exec($curl);
34 + curl_close($curl);
35 +
36 + $data = json_decode($response);
37 +
38 + $out = [];
39 +
40 + foreach($data as $d) {
41 + $out[] = array(
42 + 'show' => $d->series->title,
43 + 'images' => $d->series->images,
44 + 'season' => $d->seasonNumber,
45 + 'episode' => $d->episodeNumber,
46 + 'title' => $d->title,
47 + 'airdate' => $d->airDateUtc,
48 + 'timestamp' => strtotime($d->airDateUtc) * 1000,
49 + 'ready' => $d->hasFile,
50 + // 'sceneName' => @$d->episodeFile->sceneName,
51 + 'quality' => @$d->episodeFile->quality->quality->name,
52 + 'mediainfo' => @$d->episodeFile->mediaInfo,
53 + );
54 + }
55 +
56 + header('Content-Type: application/json');
57 + echo json_encode($out);
Newer Older