]> Git — Sourcephile - julm/camera.git/blob - index.sh
css: remove dead rule
[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 -; mv -i >&2 "$@"; }
29
30 for dir in "$@"; do
31 IFS=/ read -r user year x month x <<<"$dir"
32 test -n "$year" || exec "$0" "$user"/2*/src/*/
33 test -n "$month" || exec "$0" "$user"/"$year"/src/*/
34 pushd "$user/$year" >/dev/null
35 test -d src || { popd; continue; }
36 mkdir -p fav
37 # cp, not ln, because in push.sh --copy-links cannot be used for by-uuid/
38 cp -f --remove-destination -t fav ../../index.css
39 test -d src/"$month" || { popd; continue; }
40 {
41 genDate=$(date +%s)
42 cat <<EOF
43 <!doctype html>
44 <!-- Generator: https://git.sourcephile.fr/julm/camera.git -->
45 <!-- GenerationDate: $(date -R -d@"$genDate") -->
46 <html>
47
48 <head lang="fr">
49 <meta charset="utf-8">
50 <title>camera/$user/$year/$month</title>
51 <link rel="stylesheet" href="../index.css?$genDate">
52 </head>
53
54 <body>
55
56 <nav class="path">
57 <a href='../../..'>camera</a>/ <a href='../..'>$user</a>/ <a href='..'>$year</a>/
58 EOF
59 for m in {01..12}; do
60 if [ "$m" -eq 01 ]; then printf '{ '; fi
61 if [ "$m" -eq "$month" ]; then
62 printf %s "$month"
63 else
64 printf %s "<a href='../$m'>$m</a>"
65 fi
66 if [ "$m" -eq 12 ]; then printf ' }'; else printf ', '; fi
67 done
68 cat <<EOF
69 </nav>
70
71 <div class="camera">
72 <ul>
73 EOF
74 echo >&2 "$dir: processing favorites in chronological order"
75 # Time is encoded in filenames like so: MM/{IMG,VID}_YYYYMMDD_hhmmss.*
76 # hence sorting after the underscore (2nd field, 5th char).
77 (cd src; find -L "$month" -name '*.fav.*' -not -name "*.txt") | sort -t/ -k2.5 |
78 while read -r src; do
79 echo >&2 "$dir: processing $year/src/$src"
80 base=${src%.*}
81 name=${base#*/}
82 id=${name%%.*}
83 creationDate=$(printf %s "${src##*/???_}" | sed -e 's/\(....\)\(..\)\(..\)_\(..\)\(..\)\(..\).*/\1-\2-\3 \4:\5:\6/')
84 test -e fav/"$base".uuid ||
85 uuidgen --random >fav/"$base".uuid
86 uuid=$(cat fav/"$base".uuid)
87 mkdir -p fav/"${base%/*}"
88 mkdir -p {tmp,wip}/fav/"${base%/*}"
89 mkdir -p fav/"$month"/by-uuid/"$uuid"
90 wow=
91 test "${src##*.wow.}" = "${src}" || wow=wow
92 case "$src" in
93 *.jpg)
94 echo "<li id='$id' class='$wow'><div class='item'>"
95 echo "<picture>"
96 printf "<source type='image/avif' srcset='"
97 for w in {1200,600,300}; do
98 test fav/"$base"."$w"x.avif -nt src/"$src" || {
99 magick_ src/"$src" \
100 -sampling-factor 4:2:0 -colorspace sRGB \
101 -auto-orient -thumbnail "$w"x -unsharp 0x.5 \
102 wip/fav/"$base"."$w"x.avif
103 mv_ {wip/,}fav/"$base"."$w"x.avif
104 }
105 printf %s "$name.${w}x.avif ${w}w, "
106 done
107 echo "' />"
108 w=600
109 test fav/"$base"."$w"x.avif -nt src/"$src" || {
110 magick_ -define jpeg:size=$((w * 2))x src/"$src" \
111 -sampling-factor 4:2:0 -interlace JPEG -colorspace sRGB \
112 -auto-orient -thumbnail "$w"x -unsharp 0x.5 \
113 wip/fav/"$base"."$w"x.jpg
114 mv_ {wip/,}fav/"$base"."$w"x.avif
115 }
116 echo "<source type='image/jpeg' srcset='$name.${w}x.jpg ${w}w' />"
117 echo "<img src='$name.${w}x.jpg' alt='$creationDate' />"
118 echo "</picture>"
119 dst="$name".1200x.avif
120 ln -fs -t fav/"$month"/by-uuid/"$uuid" ../../"$dst"
121 ;;
122 *.mp4)
123 # Slow, but needed to make wide videos span two columns of the CSS grid
124 orient=$(ffprobe -v 0 -select_streams v:0 -show_entries stream_side_data=rotation -of default=nw=1:nk=1 src/"$src")
125 if [ -z "$orient" ]; then scale="-2:360"; else scale="360:-2"; fi
126 echo "<li id='$id' class='$wow vid-orient-$orient'><div class='item'>"
127 test fav/"$base".avif -nt src/"$src" || {
128 ffmpeg_ -i src/"$src" \
129 -map 0:v \
130 -filter:v format=yuv420p10le,scale="$scale" \
131 -frames:v 1 -crf 35 \
132 wip/fav/"$base".avif
133 mv_ {wip/,}fav/"$base".avif
134 }
135 need_opus () {
136 test tmp/fav/"$base".opus -nt src/"$src" || {
137 ffmpeg_ -i src/"$src" \
138 -map 0:a -c:a libopus -b:a 64k -application voip \
139 wip/fav/"$base".opus
140 mv_ {wip,tmp}/fav/"$base".opus
141 }
142 }
143 test fav/"$base".360p.av1.webm -nt src/"$src" || {
144 need_opus
145 # See https://gitlab.com/AOMediaCodec/SVT-AV1/-/blob/master/Docs/CommonQuestions.md
146 # See https://gist.github.com/BlueSwordM/86dfcb6ab38a93a524472a0cbe4c4100
147 # -preset Range from 0 to 13, with higher numbers providing a higher encoding speed;
148 # -crf Range is 0-63, with the default being 50.
149 # Lower values correspond to higher quality and greater file size
150 # yuv420p
151 # yuv420p10le can represent more shades of grey and colors
152 # and is less prone to certain artifacts, such as color
153 # banding and loss of detail in low luma areas
154 # cost in terms of resulting file size (~5%),
155 # can also be more compute-intensive than 8-bit in some decoders
156 # tune=0 subjective mode often results in an image with greater
157 # sharpness and is intended to produce a result that appears to humans
158 # to be of high quality (as opposed to doing well on basic objective measures,
159 # such as PSNR)
160 # scd=1 biases the encoders' scene detection to insert more intra refreshes if needed,
161 # and reduce temporal dependencies around scene-changes.
162 # scm=0 screen content mode decision making. 0 is best for live-action.
163 ffmpeg_ -i src/"$src" -i tmp/fav/"$base".opus \
164 -map 0:v \
165 -filter:v format=yuv420p,scale="$scale" \
166 -c:v libsvtav1 -preset 10 -crf 35 \
167 -svtav1-params keyint=30s:tune=0:enable-overlays=1:scd=1:scm=0:fast-decode=1:tile-columns=2 \
168 -map 1:a -c:a copy \
169 wip/fav/"$base".360p.av1.webm
170 mv_ {wip/,}fav/"$base".360p.av1.webm
171 }
172 dst="$name".360p.av1.webm
173 ln -fs -t fav/"$month"/by-uuid/"$uuid" ../../"$dst"
174 #test fav/"$base".360p.vp9.webm -nt src/"$src" || {
175 # need_opus
176 # # See https://developers.google.com/media/vp9/settings/vod#recommended_settings
177 # # See https://gist.github.com/mrintrepide/3033c35ee9557e66cff7806f48dbd339
178 # set -- \
179 # -filter:v format=yuv420p10le,scale="$scale" \
180 # -c:v libvpx-vp9 -quality good -crf 20 \
181 # -minrate 138k -b:v 276k -maxrate 400k \
182 # -cpu-used 4 -static-thresh 0 -tile-columns 0 -tile-rows 0 -frame-parallel 0 \
183 # -row-mt 1 -aq-mode 0 -auto-alt-ref 6 -lag-in-frames 25 -enable-tpl 1
184 # test -e tmp/fav/"$base".360p.vp9.webm-0.log || {
185 # # FIXME: how to disable h264 debug log?
186 # ffmpeg 2>/dev/null -i src/"$src" \
187 # -map 0:v "$@" -pass 1 -passlogfile wip/fav/"$base".360p.vp9.webm \
188 # -f null \
189 # /dev/null
190 # mv_ -t tmp/fav/"${base%/*}" wip/fav/"$base".360p.vp9.webm-*.log
191 # }
192 # ffmpeg -i src/"$src" -i tmp/fav/"$base".opus \
193 # -map 0:v "$@" -pass 2 -passlogfile tmp/fav/"$base".360p.vp9.webm \
194 # -map 1:a -c:a copy \
195 # wip/fav/"$base".360p.vp9.webm
196 # mkdir -p fav/"$month"/by-uuid/"$uuid"
197 # mv_ wip/fav/"$base".360p.vp9.webm fav/"$base".360p.vp9.webm
198 # rm -fv tmp/fav/"$base".360p.vp9.webm-*.log
199 #}
200 #test fav/"$base".mp4 -nt src/"$src" || {
201 # ffmpeg -i src/"$src" \
202 # -filter:v format=yuv420p \
203 # -c:v libx264 -preset medium -crf 22 \
204 # -tune zerolatency \
205 # -maxrate 1M -bufsize 2M \
206 # -movflags use_metadata_tags +faststart \
207 # -c:a libopus -base:a 64k -application voip \
208 # -filter:v "scale=iw/2:ih/2" \
209 # wip/fav/"$base".mp4
210 # mv_ wip/fav/"$base".mp4 fav/"$base".mp4
211 #}
212 # class='orient$orient'
213 echo "<video controls preload=none poster='$name.avif'>"
214 # See https://jakearchibald.com/2022/html-codecs-parameter-for-av1/
215 # and ffmpeg -i fav/$month/foo.av1.webm -c:v copy -bsf:v trace_headers -f null /dev/null |&
216 # grep -e seq_profile -e seq_level_idx -e seq_tier -e high_bitdepth -e twelve_bit
217 P=0; LL=01; T=M; DD=08
218 echo "<source type='video/webm; codecs=av01.$P.$LL$T.$DD' src='$name.360p.av1.webm' />"
219 echo "<p><a href='$name.360p.av1.webm'>$name.360p.av1.webm</a></p>"
220 echo "</video>"
221 esac
222 touch -a src/"$base".txt
223 printf %s "<span class='comment'>"
224 sed -e '$q;s/$/<br \/>\n/' src/"$base".txt
225 echo "</span>"
226 echo "</div>" # class='item'
227 echo "<span class='infos'>"
228 echo " <span class='creationDate'>$creationDate</span>"
229 echo "</span>"
230 echo "<span class='links'>"
231 echo " <a class='by-uuid' href='by-uuid/$uuid/${dst##*/}' title='Lien public vers cette capture' target='_blank'>@</a>"
232 echo " <a class='anchor' href='#$id' title='Ancre vers cette capture'>#</a>"
233 echo "</span>"
234 echo "</li>"
235 echo
236 done
237 cat <<EOF
238 </ul>
239 </div>
240
241 </body>
242 </html>
243 EOF
244 } >tmp/fav/"$month"/index.html
245 mv_ -f {tmp/,}fav/"$month"/index.html
246
247 echo >&2 "$dir: removing any deleted favorites"
248 (cd fav; find -L "$month" -mindepth 1 -not -path '*/by-uuid/*' -type f -not -name index.html) |
249 while read -r fav; do
250 base=$fav
251 base=${base%.300x.avif}
252 base=${base%.600x.avif}
253 base=${base%.1200x.avif}
254 base=${base%.avif}
255 base=${base%.360p.av1.webm}
256 base=${base%.uuid}
257 name=${base#*/}
258 unset hasSrc
259 for src in src/"$base".???; do hasSrc=set; done
260 test "${hasSrc:+set}" || {
261 echo >&2 "$dir: removing $year/fav/$fav"
262 rm -f fav/"$fav"
263 rm -fv fav/"$month"/by-uuid/*/"$name".{300x.avif,600x.avif,1200x.avif,avif,360p.av1.webm,uuid}
264 }
265 done
266 rmdir -p 2>/dev/null fav/"$month"/by-uuid/*/ || true
267 rmdir -p 2>/dev/null wip/fav/* || true
268 popd >/dev/null
269 done