summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJon duSaint2023-10-02 11:27:17 -0700
committerJon duSaint2023-10-02 11:27:17 -0700
commitb56ee743bcd708ca560ebc205b1c3308b5eab6d6 (patch)
treec064d386ce0fded457bc5fab81062a47991ab755
parentc6ec5bc553ff58c845c13f2bb2860d9e05a75d6f (diff)

reolink: Revamp web page

The generated web page now supports multiple cameras and is usable on idevices.

-rwxr-xr-xreolink/reolink375
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()">&lt;&lt;</span>
- <span class="button" onclick="previous_image()">&lt</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()">&gt;</span>
- <span class="button" onclick="ffwd_image()">&gt;&gt;</span>
- </div>
- </div>
-
</body>
</html>
SLIDESHOW