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