]> Git — Sourcephile - julm/camera.git/blob - index.sh
index: fix interrupted files
[julm/camera.git] / index.sh
1 #!/usr/bin/env bash
2 # Copyright: Julien Moutinho <julm+camera@sourcephile.fr>
3 # License: AGPL-3.0-or-later
4 # Usage:
5 # $ mv julm/2023/src/02/IMG_20230228_111645{,.fav}.jpg
6 # $ echo >julm/2023/src/02/IMG_20230228_111645.fav.txt "Some HTML <b>comment</b> about the picture"
7 # $ ./index.sh julm/2023/src/02
8 # $ sensible-browser julm/2023/fav/02/index.html
9 #
10 # NginxConfig:
11 # locations."/julm/perso/camera/" = {
12 # alias = "${root}/julm/perso/camera/";
13 # basicAuthFile = "/run/credentials/nginx.service/autogeree.net.www.julm.perso.camera.htpasswd";
14 # };
15 # # Disable basicAuthFile for by-uuid
16 # locations."~ ^/julm/perso/camera/([0-9]+/[0-9][0-9]/by-uuid/[0-9a-f-]+/.+)$" = {
17 # alias = "${root}/julm/perso/camera/$1";
18 # };
19
20 # shellcheck disable=SC2086
21
22 cd "${0%/*}"
23 set -eu
24 shopt -s nullglob
25
26 ffmpeg_ () { local -; set -x; nice -19 ffmpeg </dev/null -hide_banner -loglevel warning -stats -y "$@"; }
27 magick_ () { local -; set -x; nice -19 magick "$@"; }
28 mv_ () { local -; set -x; mv -iv >&2 "$@"; }
29
30 for dir in "$@"; do
31 IFS=/ read -r user year x month x <<<"$dir"
32 test -n "$month" || exec "$0" "$user"/"$year"/src/*/
33 pushd "$user/$year" >/dev/null
34 test -d src || { popd; continue; }
35 mkdir -p fav
36 # cp, not ln, because in push.sh --copy-links cannot be used for by-uuid/
37 cp -f --remove-destination -t fav ../../index.css
38 test -d src/"$month" || { popd; continue; }
39 mkdir -p fav/"$month"
40 mkdir -p {tmp,wip}/fav/"$month"
41 {
42 genDate=$(date +%s)
43 cat <<EOF
44 <!doctype html>
45 <!-- Generator: https://git.sourcephile.fr/julm/camera.git -->
46 <!-- GenerationDate: $(date -R -d@"$genDate") -->
47 <html>
48
49 <head lang="fr">
50 <meta charset="utf-8">
51 <title>camera/$user/$year/$month</title>
52 <link rel="stylesheet" href="../index.css?$genDate">
53 </head>
54
55 <body>
56
57 <nav class="path">
58 <ul>
59 <li><a href='../../..'>camera</a>/ <a href='../..'>$user</a>/ <a href='..'>$year</a>/ $month</li>
60 </ul></nav>
61
62 <div class="camera">
63 <ul>
64 EOF
65 echo >&2 "$dir: processing favorites in chronological order"
66 # Time is encoded in filenames like so: MM/{IMG,VID}_YYYYMMDD_hhmmss.*
67 # hence sorting after the underscore (2nd field, 5th char).
68 (cd src; find "$month" -name '*.fav.*' -not -name "*.txt") | sort -n -t/ -k2.5 |
69 while read -r src; do
70 echo >&2 "$dir: processing $year/src/$src"
71 base=${src%.*}
72 name=${base##*/}
73 id=${name%%.*}
74 creationDate=$(printf %s "${src##*/???_}" | sed -e 's/\(....\)\(..\)\(..\)_\(..\)\(..\)\(..\).*/\1-\2-\3 \4:\5:\6/')
75 echo "<li id='$id'><div class='item'>"
76 test -e fav/"$base".uuid ||
77 uuidgen --random >fav/"$base".uuid
78 uuid=$(cat fav/"$base".uuid)
79 mkdir -p fav/"$month"/by-uuid/"$uuid"
80 case "$src" in
81 *.jpg)
82 echo "<picture>"
83 printf "<source type='image/avif' srcset='"
84 for w in {1200,600,300}; do
85 test fav/"$base"."$w"x.avif -nt src/"$src" || {
86 magick_ src/"$src" \
87 -sampling-factor 4:2:0 -colorspace sRGB \
88 -auto-orient -thumbnail "$w"x -unsharp 0x.5 \
89 wip/fav/"$base"."$w"x.avif
90 mv_ {wip/,}fav/"$base"."$w"x.avif
91 }
92 printf %s "$name.${w}x.avif ${w}w, "
93 done
94 echo "' />"
95 w=600
96 test fav/"$base"."$w"x.avif -nt src/"$src" || {
97 magick_ -define jpeg:size=$((w * 2))x src/"$src" \
98 -sampling-factor 4:2:0 -interlace JPEG -colorspace sRGB \
99 -auto-orient -thumbnail "$w"x -unsharp 0x.5 \
100 wip/fav/"$base"."$w"x.jpg
101 mv_ {wip/,}fav/"$base"."$w"x.avif
102 }
103 echo "<source type='image/jpeg' srcset='$name.${w}x.jpg ${w}w' />"
104 echo "<img src='$name.${w}x.jpg' alt='$creationDate' />"
105 echo "</picture>"
106 dst="$name".1200x.avif
107 ln -fs -t fav/"$month"/by-uuid/"$uuid" ../../"$dst"
108 ;;
109 *.mp4)
110 test fav/"$base".avif -nt src/"$src" || {
111 ffmpeg_ -i src/"$src" \
112 -map 0:v \
113 -filter:v format=yuv420p,scale='trunc(oh*a/2)*2:360' \
114 -frames:v 1 -crf 40 \
115 wip/fav/"$base".avif
116 mv_ {wip/,}fav/"$base".avif
117 }
118 need_opus () {
119 test tmp/fav/"$base".opus -nt src/"$src" || {
120 ffmpeg_ -i src/"$src" \
121 -map 0:a -c:a libopus -b:a 64k -application voip \
122 wip/fav/"$base".opus
123 mv_ {wip,tmp}/fav/"$base".opus
124 }
125 }
126 test fav/"$base".360p.av1.webm -nt src/"$src" || {
127 need_opus
128 ffmpeg_ -i src/"$src" -i tmp/fav/"$base".opus \
129 -map 0:v \
130 -filter:v format=yuv420p,scale='trunc(oh*a/2)*2:360' \
131 -c:v libsvtav1 -crf 40 -g 120 \
132 -map 1:a -c:a copy \
133 wip/fav/"$base".360p.av1.webm
134 mv_ {wip/,}fav/"$base".360p.av1.webm
135 }
136 dst="$name".360p.av1.webm
137 ln -fs -t fav/"$month"/by-uuid/"$uuid" ../../"$dst"
138 #test fav/"$base".360p.vp9.webm -nt src/"$src" || {
139 # need_opus
140 # # See https://developers.google.com/media/vp9/settings/vod#recommended_settings
141 # # See https://gist.github.com/mrintrepide/3033c35ee9557e66cff7806f48dbd339
142 # set -- \
143 # -filter:v format=yuv420p,scale='trunc(oh*a/2)*2:360' \
144 # -c:v libvpx-vp9 -quality good -crf 20 \
145 # -minrate 138k -b:v 276k -maxrate 400k \
146 # -cpu-used 4 -static-thresh 0 -tile-columns 0 -tile-rows 0 -frame-parallel 0 \
147 # -row-mt 1 -aq-mode 0 -auto-alt-ref 6 -lag-in-frames 25 -enable-tpl 1
148 # test -e tmp/fav/"$base".360p.vp9.webm-0.log || {
149 # # FIXME: how to disable h264 debug log?
150 # ffmpeg 2>/dev/null -i src/"$src" \
151 # -map 0:v "$@" -pass 1 -passlogfile wip/fav/"$base".360p.vp9.webm \
152 # -f null \
153 # /dev/null
154 # mv_ -t tmp/fav/"${base%/*}" wip/fav/"$base".360p.vp9.webm-*.log
155 # }
156 # ffmpeg -i src/"$src" -i tmp/fav/"$base".opus \
157 # -map 0:v "$@" -pass 2 -passlogfile tmp/fav/"$base".360p.vp9.webm \
158 # -map 1:a -c:a copy \
159 # wip/fav/"$base".360p.vp9.webm
160 # mkdir -p fav/"$month"/by-uuid/"$uuid"
161 # mv_ wip/fav/"$base".360p.vp9.webm fav/"$base".360p.vp9.webm
162 # rm -fv tmp/fav/"$base".360p.vp9.webm-*.log
163 #}
164 #test fav/"$base".mp4 -nt src/"$src" || {
165 # ffmpeg -i src/"$src" \
166 # -filter:v format=yuv420p \
167 # -c:v libx264 -preset medium -crf 22 \
168 # -tune zerolatency \
169 # -maxrate 1M -bufsize 2M \
170 # -movflags use_metadata_tags +faststart \
171 # -c:a libopus -base:a 64k -application voip \
172 # -filter:v "scale=iw/2:ih/2" \
173 # wip/fav/"$base".mp4
174 # mv_ wip/fav/"$base".mp4 fav/"$base".mp4
175 #}
176 #orient=$(ffprobe -v 0 -select_streams v:0 -show_entries stream_side_data=rotation -of default=nw=1:nk=1 src/"$src")
177 # class='orient$orient'
178 echo "<video controls preload=none poster='$name.avif'>"
179 # See https://jakearchibald.com/2022/html-codecs-parameter-for-av1/
180 # and ffmpeg -i fav/$month/foo.av1.webm -c:v copy -bsf:v trace_headers -f null /dev/null |&
181 # grep -e seq_profile -e seq_level_idx -e seq_tier -e high_bitdepth -e twelve_bit
182 P=0; LL=01; T=M; DD=08
183 echo "<source type='video/webm; codecs=av01.$P.$LL$T.$DD' src='$name.360p.av1.webm' />"
184 echo "<p><a href='$name.360p.av1.webm'>$name.360p.av1.webm</a></p>"
185 echo "</video>"
186 esac
187 touch -a src/"$base".txt
188 printf %s "<span class='comment'>"
189 sed -e '$q;s/$/<br \/>\n/' src/"$base".txt
190 echo "</span>"
191 echo "</div>"
192 echo "<span class='infos'>"
193 echo " <span class='creationDate'>$creationDate</span>"
194 echo "</span>"
195 echo "<span class='links'>"
196 echo " <a class='by-uuid' href='by-uuid/$uuid/$dst' title='Lien public vers cette capture' target='_blank'>@</a>"
197 echo " <a class='anchor' href='#$id' title='Ancre vers cette capture'>#</a>"
198 echo "</span>"
199 echo "</li>"
200 echo
201 done
202 cat <<EOF
203 </ul>
204 </div>
205
206 </body>
207 </html>
208 EOF
209 } >tmp/fav/"$month"/index.html
210 mv_ -f {tmp/,}fav/"$month"/index.html
211
212 echo >&2 "$dir: removing any deleted favorites"
213 (cd fav; find "$month" -mindepth 1 -type f -not -name index.html) |
214 while read -r fav; do
215 base=${fav%.fav.*}
216 name=${base##*/}
217 unset hasSrc
218 for src in src/"$base".fav.*; do test "${src%.txt}" != "$src" || hasSrc=set; done
219 test "${hasSrc:+set}" || {
220 echo >&2 "$dir: removing $year/fav/$fav"
221 rm -f fav/"$fav"
222 rm -f fav/"$month"/by-uuid/*/"${name%.*}".*
223 }
224 done
225 rmdir -p 2>/dev/null fav/"$month"/by-uuid/*/ || true
226 rmdir -p 2>/dev/null wip/fav/* || true
227 popd >/dev/null
228 done