diff options
| author | Jon duSaint | 2023-10-02 11:27:17 -0700 |
|---|---|---|
| committer | Jon duSaint | 2023-10-02 11:27:17 -0700 |
| commit | b56ee743bcd708ca560ebc205b1c3308b5eab6d6 (patch) | |
| tree | c064d386ce0fded457bc5fab81062a47991ab755 | |
| parent | c6ec5bc553ff58c845c13f2bb2860d9e05a75d6f (diff) | |
reolink: Revamp web page
The generated web page now supports multiple cameras and is usable on idevices.
| -rwxr-xr-x | reolink/reolink | 375 |
1 files changed, 196 insertions, 179 deletions
diff --git a/reolink/reolink b/reolink/reolink index b932af9..e38eb55 100755 --- a/reolink/reolink +++ b/reolink/reolink @@ -425,7 +425,14 @@ sub timelapse { } sub respool_and_generate_slideshow { - my $camera = 'camera1'; ### XXX: + my @cameras; + foreach my $a (0..$#_) { + if (defined $cameras{$_[$a]}) { + push @cameras, $_[$a]; + delete $_[$a]; + } + } + @cameras = (sort { $cameras{$a}->{display} cmp $cameras{$b}->{display} } keys (%cameras)) unless @cameras; # Retain only past 24 hours of stills my $t = time - $server_params{spooltime} * 60 * 60; @@ -438,7 +445,6 @@ sub respool_and_generate_slideshow { my $generated = localtime; - ### XXX: Figure out how to get iDevice page size correct if ($fh) { print $fh <<"SLIDESHOW"; <!DOCTYPE html> @@ -446,30 +452,110 @@ sub respool_and_generate_slideshow { <head> <title>Slideshow [$generated]</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> + + <script type="text/javascript"> + var cameras = { +SLIDESHOW + + print $fh ' ' . join (",\n ", map { qq("$_" : { "name" : "$cameras{$_}->{display}", "spool" : "$cameras{$_}->{spool}" }) } @cameras) . "\n"; + + print $fh <<"SLIDESHOW"; + }; + + var images = { +SLIDESHOW + + foreach my $camera (@cameras) { + my $spool = spool ($camera); + + print $fh qq( "$camera" : [\n); + + foreach my $image (<$spool/*.jpg>) { + if ($image =~ m/(\d{4})(\d{2})(\d{2})-(\d{2})(\d{2})(\d{2})\.jpg/) { + my $file_time = mktime ($6, $5, $4, $3, $2 - 1, $1 - 1900); + if ($file_time < $t) { + debug ("delete $image ($file_time $t)"); + unlink $image; + } else { + my $im = basename ($image); + + print $fh <<"IMAGE"; + { image: "$im", title: "$1-$2-$3 $4:$5:$6" }, +IMAGE + } + } + } + + print $fh qq( ],\n); + } + + + print $fh <<"SLIDESHOW"; + }; + var videos = { +SLIDESHOW + + foreach my $camera (@cameras) { + my $spool = spool ($camera); + + print $fh qq( "$camera" : [\n); + + my @video_list; + my @videos; + foreach my $ext (@video_extensions) { + push @video_list, <$spool/*.$ext>; + } + + foreach my $video (sort @video_list) { + my $ext = join ('|', @video_extensions); + if ($video =~ m,/(\d{4})(\d{2})(\d{2})\.($ext)$,) { + push @videos, [$1, $2, $3, $4]; + } + } + + # 0=Y 1=M 2=D 3=ext + print $fh $_ foreach map { qq( { "video" : "$_->[0]$_->[1]$_->[2].$_->[3]", "title" : "$_->[0]-$_->[1]-$_->[2]", "keyframe" : "$_->[0]$_->[1]$_->[2]_kf.jpg" },\n) } @videos; + + print $fh qq( ],\n); + } + + print $fh <<"SLIDESHOW"; + }; + </script> + <style type="text/css"> - /* Image size (actual size is 2560x1920) */ - :root { - --width: 1440px; - --height: 1080px; - } body { background-color: #008080; color: black; } div.tabbed-wrapper { - position: fixed; - top: 50%; - margin-top: calc(-1 * var(--height) / 2); - left: 50%; - margin-left: calc(-1 * var(--width) / 2); + margin: 1%; } div.tabs { display: flex; } + div.camera-name { + display: flex; + flex-grow: 1; + align-items: center; + justify-content: center; + font-size: 4em; + font-weight: bold; + } + span.camera-name { + display: flex; + flex-grow: 1; + font-size: 2em; + font-weight: bold; + text-align: center; + border: 1px solid black; + border-radius: 4px; + margin-bottom: 2px; + } span.tab { background-color: #004040; flex-grow: 1; - font-size: 2em; + font-size: 3em; text-align: center; border: 1px solid black; border-radius: 10px; @@ -482,6 +568,7 @@ sub respool_and_generate_slideshow { border: 1px solid black; border-radius: 10px; padding: 10px; + max-width: 100%; } select.video-menu { background-color: #004040; @@ -494,217 +581,147 @@ sub respool_and_generate_slideshow { border: 1px solid black; border-radius: 10px; padding: 10px; + max-width: 100%; + } + img { + max-width: 100%; + } + video { + max-width: 100%; + display: flex; + margin: auto; } div.caption { font-size: 2em; display: flex; + font-weight: bold; } div.caption > span, select { margin: auto; } - div.controls { + input { display: flex; - justify-content: space-between; - align-items: center; - font-weight: bold; - margin: 8px; - cursor: pointer; - } - span.button { - font-size: 4em; - border: 2px solid black; - border-radius: 10%; - flex-grow: 1; - margin: 4px; - } - span.tick { - flex-grow: 1; - font-weight: bold; - } - span.bigtick { - font-size: 2em; - flex-grow: 1; - font-weight: bold; + width: 100%; } </style> <script type="text/javascript"> - var images = [ -SLIDESHOW - } - - foreach my $image (<spool($camera)/*.jpg>) { - if ($image =~ m/(\d{4})(\d{2})(\d{2})-(\d{2})(\d{2})(\d{2})\.jpg/) { - my $file_time = mktime ($6, $5, $4, $3, $2 - 1, $1 - 1900); - if ($file_time < $t) { - debug ("delete $image ($file_time $t)"); - unlink $image; - } elsif ($fh) { - my $im = basename ($image); - print $fh <<"IMAGE"; - { image: "$im", title: "$1-$2-$3 $4:$5:$6" }, -IMAGE - } - } - } - - if ($fh) { - print $fh <<"SLIDESHOW"; - ]; - - var current = 0; - var originalTick = null; - - function set_image() { - if (current >= 0 && current < images.length) { - img = document.getElementById ("photo-display"); - img.src = images[current].image; - cap = document.getElementById ("photo-caption"); - cap.innerHTML = images[current].title; - - percent = parseInt(current / (images.length - 1) * 100); - tick = document.getElementById ("tick" + percent); - tick.style.color = "blue"; - tick.innerHTML = '⋄'; - if (originalTick != null && originalTick != tick) { - originalTick.style.color = "black"; - originalTick.innerHTML = '|'; - } - originalTick = tick; - } - } - - function rewind_image() { - current = 0; - set_image(); - } - - function previous_image() { - if (current > 0) { - current--; - } - set_image(); - } - - function next_image() { - if (current + 1 < images.length) { - current++; - } - set_image(); + function id (i, camera) { + return i+'-'+camera; } - function ffwd_image() { - current = images.length - 1; - set_image(); - } + function set_image (camera) { + slider = document.getElementById (id ("image-slider", camera)); + current = slider.value; - function seek_image(p) { - current = parseInt(p / 100 * (images.length - 1)) - set_image(); + img = document.getElementById (id ("photo-display", camera)); + img.src = cameras[camera].spool + "/" + images[camera][current].image; + a = document.getElementById (id ("photo-link", camera)); + a.href = img.src; + cap = document.getElementById (id ("photo-caption", camera)); + cap.innerHTML = images[camera][current].title; } - function slideshow_tab() { - s = document.getElementById ("slideshow-tab"); - v = document.getElementById ("video-tab"); - ts = document.getElementById ("tab-slideshow"); - tv = document.getElementById ("tab-video"); + function slideshow_tab (camera) { + s = document.getElementById (id ("slideshow-tab", camera)); + v = document.getElementById (id ("video-tab", camera)); + ts = document.getElementById (id ("tab-slideshow", camera)); + tv = document.getElementById (id ("tab-video", camera)); s.style.display = ""; v.style.display = "none"; ts.style.fontWeight = "bold"; tv.style.fontWeight = "normal"; } - function video_select() { - op = document.getElementById ("video-menu"); - stem = op.options[op.selectedIndex].value; - vb = document.getElementById ("videobox"); - vb.src = stem + ".$video_extension"; - vb.poster = stem + "_kf.jpg"; + function video_select (camera) { + op = document.getElementById (id ("video-menu", camera)); + if (op.options.length == 0) { return; } + idx = op.options[op.selectedIndex].value; + vb = document.getElementById (id ("videobox", camera)); + vb.src = cameras[camera].spool + '/' + videos[camera][idx].video; + vb.poster = cameras[camera].spool + '/' + videos[camera][idx].keyframe; } - function video_tab() { - s = document.getElementById ("slideshow-tab"); - v = document.getElementById ("video-tab"); - ts = document.getElementById ("tab-slideshow"); - tv = document.getElementById ("tab-video"); + function video_tab (camera) { + s = document.getElementById (id ("slideshow-tab", camera)); + v = document.getElementById (id ("video-tab", camera)); + ts = document.getElementById (id ("tab-slideshow", camera)); + tv = document.getElementById (id ("tab-video", camera)); s.style.display = "none"; v.style.display = ""; ts.style.fontWeight = "normal"; tv.style.fontWeight = "bold"; } - </script> - </head> - <body onload="slideshow_tab(); ffwd_image();"> - <div class="tabbed-wrapper"> - <div class="tabs"> - <span class="tab tab-left" id="tab-slideshow" onclick="slideshow_tab();">Slideshow</span> - <span class="tab tab-right" id="tab-video" onclick="video_tab();">Video</span> - </div> - - <div class="video" id="video-tab"> - <div class="caption"> - <select class="video-menu" id="video-menu" onchange="video_select();"> -SLIDESHOW - - my @video_list; - my @videos; - foreach my $ext (@video_extensions) { - push @video_list, <spool($camera)/*.$ext>; - } - foreach my $video (sort @video_list) { - my $ext = join ('|', @video_extensions); - if ($video =~ m,/(\d{4})(\d{2})(\d{2})\.($ext)$,) { - push @videos, [$1, $2, $3, $4]; - } - } + function init_camera (camera) { + slideshow_tab (camera); + slider = document.getElementById (id ("image-slider", camera)); + slider.min = 0; + slider.step = 1; + slider.max = images[camera].length - 1; + slider.value = slider.max; + slider.addEventListener ("input", () => set_image (camera)); + set_image (camera); + + select = document.getElementById (id ("video-menu", camera)); + for (let idx = 0; idx < videos[camera].length; idx++) { + opt = document.createElement ("option"); + opt.value = idx; + opt.innerHTML = videos[camera][idx].title; + select.appendChild (opt); + select.selectedIndex = idx; + } + video_select (camera); + } - print $fh $_ foreach map { qq( <option value="$_->[0]$_->[1]$_->[2]">$_->[0]-$_->[1]-$_->[2]</option>\n) } reverse @videos; + function onload() { + for (const camera in cameras) { + init_camera (camera); + } + } + </script> + </head> + <body onload="onload();"> - print $fh <<"SLIDESHOW"; - </select> - </div> SLIDESHOW - if (@videos) { - my ($video, $keyframe) = ("$videos[-1]->[0]$videos[-1]->[1]$videos[-1]->[2].$videos[-1]->[3]", - "$videos[-1]->[0]$videos[-1]->[1]$videos[-1]->[2]_kf.jpg"); - + foreach my $camera (@cameras) { print $fh <<"SLIDESHOW"; - <video id="videobox" src="$video" poster="$keyframe" controls> - This could be a video instead. - </video> -SLIDESHOW - } - - print $fh <<"SLIDESHOW"; - </div> + <!-- $cameras{$camera}->{display} --> + <div id="$camera"> + <div class="camera-name">$cameras{$camera}->{display}</div> + <div class="tabbed-wrapper"> + <div class="tabs"> + <span class="tab tab-left" id="tab-slideshow-$camera" onclick="slideshow_tab('$camera');">Slideshow</span> + <span class="tab tab-right" id="tab-video-$camera" onclick="video_tab('$camera');">Video</span> + </div> - <div class="slideshow" id="slideshow-tab"> - <div class="caption"> - <span id="photo-caption"></span> + <div class="video" id="video-tab-$camera"> + <div class="caption"> + <select class="video-menu" id="video-menu-$camera" onchange="video_select('$camera');"> + </select> + </div> + <video id="videobox-$camera" controls> + This could be a video instead. + </video> </div> - <div class="inner"> - <div class="slide"><img id="photo-display" src="" width="1440" height="1080"/></div> + + <div class="slideshow" id="slideshow-tab-$camera"> + <div class="caption"> + <span id="photo-caption-$camera"></span> + </div> + <div class="inner"> + <div class="slide"><a id="photo-link-$camera" href=""><img id="photo-display-$camera" src=""/></a></div> + </div> + <input class="slider" id="image-slider-$camera" type="range" min="0" max="146" step="1" value="0"/> </div> - <div class="controls"> - <span class="button" onclick="rewind_image()"><<</span> - <span class="button" onclick="previous_image()"><</span> -SLIDESHOW + </div> + </div> - foreach (0..100) { - my $class = ($_ % 10) ? 'tick' : 'bigtick'; - print $fh <<"SLIDESHOW"; - <span class="$class" id="tick$_" onclick="seek_image($_);">|</span> SLIDESHOW } print $fh <<"SLIDESHOW"; - <span class="button" onclick="next_image()">></span> - <span class="button" onclick="ffwd_image()">>></span> - </div> - </div> - </body> </html> SLIDESHOW |
