2 # Copyright: Julien Moutinho <julm+camera@sourcephile.fr>
3 # License: AGPL-3.0-or-later
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
11 # locations."/julm/perso/camera/" = {
12 # alias = "${root}/julm/perso/camera/";
13 # basicAuthFile = "/run/credentials/nginx.service/autogeree.net.www.julm.perso.camera.htpasswd";
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";
20 # shellcheck disable=SC2086
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 "$@"; }
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; }
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; }
41 mkdir -p {tmp
,wip
}/fav
/"$month"
46 <!-- Generator: https://git.sourcephile.fr/julm/camera.git -->
47 <!-- GenerationDate: $(date -R -d@"$genDate") -->
51 <meta charset="utf-8">
52 <title>camera/$user/$year/$month</title>
53 <link rel="stylesheet" href="../index.css?$genDate">
60 <li><a href='../../..'>camera</a>/ <a href='../..'>$user</a>/ <a href='..'>$year</a>/ $month</li>
66 echo >&2 "$dir: processing favorites in chronological order"
67 # Time is encoded in filenames like so: MM/{IMG,VID}_YYYYMMDD_hhmmss.*
68 # hence sorting after the underscore (2nd field, 5th char).
69 (cd src
; find "$month" -name '*.fav.*' -not -name "*.txt") | sort -n -t/ -k2.5
|
71 echo >&2 "$dir: processing $year/src/$src"
75 creationDate
=$(printf %s "${src##*/???_}" | sed -e 's/\(....\)\
(..\
)\
(..\
)_\
(..\
)\
(..\
)\
(..\
).
*/\
1-\
2-\
3 \
4:\
5:\
6/')
76 echo "<li id='$id'><div class='item
'>"
77 test -e fav/"$base".uuid ||
78 uuidgen --random >fav/"$base".uuid
79 uuid=$(cat fav/"$base".uuid)
80 mkdir -p fav/"$month"/by-uuid/"$uuid"
84 printf "<source type='image
/avif
' srcset='"
85 for w in {1200,600,300}; do
86 test fav/"$base"."$w"x.avif -nt src/"$src" || {
88 -sampling-factor 4:2:0 -colorspace sRGB \
89 -auto-orient -thumbnail "$w"x -unsharp 0x.5 \
90 wip/fav/"$base"."$w"x.avif
91 mv_ {wip/,}fav/"$base"."$w"x.avif
93 printf %s "$name.
${w}x.avif
${w}w
, "
97 test fav/"$base"."$w"x.avif -nt src/"$src" || {
98 magick_ -define jpeg:size=$((w * 2))x src/"$src" \
99 -sampling-factor 4:2:0 -interlace JPEG -colorspace sRGB \
100 -auto-orient -thumbnail "$w"x -unsharp 0x.5 \
101 wip/fav/"$base"."$w"x.jpg
102 mv_ {wip/,}fav/"$base"."$w"x.avif
104 echo "<source type='image
/jpeg
' srcset='$name.
${w}x.jpg
${w}w
' />"
105 echo "<img src='$name.
${w}x.jpg
' alt='$creationDate' />"
107 dst="$name".1200x.avif
108 ln -fs -t fav/"$month"/by-uuid/"$uuid" ../../"$dst"
112 orient=$(ffprobe -v 0 -select_streams v:0 -show_entries stream_side_data=rotation -of default=nw=1:nk=1 src/"$src")
113 if [ -z "$orient" ]; then scale="-2:360"; else scale="360:-2"; fi
115 test fav/"$base".avif -nt src/"$src" || {
117 ffmpeg_ -i src/"$src" \
119 -filter:v format=yuv420p10le,scale="$scale" \
120 -frames:v 1 -crf 35 \
122 mv_ {wip/,}fav/"$base".avif
125 test tmp/fav/"$base".opus -nt src/"$src" || {
126 ffmpeg_ -i src/"$src" \
127 -map 0:a -c:a libopus -b:a 64k -application voip \
129 mv_ {wip,tmp}/fav/"$base".opus
132 test fav/"$base".360p.av1.webm -nt src/"$src" || {
135 # See https://gitlab.com/AOMediaCodec/SVT-AV1/-/blob/master/Docs/CommonQuestions.md
136 # See https://gist.github.com/BlueSwordM/86dfcb6ab38a93a524472a0cbe4c4100
137 # -preset Range from 0 to 13, with higher numbers providing a higher encoding speed;
138 # -crf Range is 0-63, with the default being 50.
139 # Lower values correspond to higher quality and greater file size
141 # yuv420p10le can represent more shades of grey and colors
142 # and is less prone to certain artifacts, such as color
143 # banding and loss of detail in low luma areas
144 # cost in terms of resulting file size (~5%),
145 # can also be more compute-intensive than 8-bit in some decoders
146 # tune=0 subjective mode often results in an image with greater
147 # sharpness and is intended to produce a result that appears to humans
148 # to be of high quality (as opposed to doing well on basic objective measures,
150 # scd=1 biases the encoders' scene detection to insert
more intra refreshes
if needed
,
151 # and reduce temporal dependencies around scene-changes.
152 # scm=0 screen content mode decision making. 0 is best for live-action.
153 ffmpeg_
-i src
/"$src" -i tmp
/fav
/"$base".opus \
155 -filter:v format
=yuv420p
,scale
="$scale" \
156 -c:v libsvtav1
-preset 10 -crf 35 \
157 -svtav1-params keyint
=30s
:tune
=0:enable-overlays=1:scd
=1:scm
=0:fast
-decode=1:tile
-columns=2 \
159 wip
/fav
/"$base".360p.av1.webm
160 mv_
{wip
/,}fav
/"$base".360p.av1.webm
162 dst
="$name".360p.av1.webm
163 ln -fs -t fav
/"$month"/by
-uuid/"$uuid" ..
/..
/"$dst"
164 #test fav/"$base".360p.vp9.webm -nt src/"$src" || {
167 # # See https://developers.google.com/media/vp9/settings/vod#recommended_settings
168 # # See https://gist.github.com/mrintrepide/3033c35ee9557e66cff7806f48dbd339
170 # -filter:v format=yuv420p10le,scale="$scale" \
171 # -c:v libvpx-vp9 -quality good -crf 20 \
172 # -minrate 138k -b:v 276k -maxrate 400k \
173 # -cpu-used 4 -static-thresh 0 -tile-columns 0 -tile-rows 0 -frame-parallel 0 \
174 # -row-mt 1 -aq-mode 0 -auto-alt-ref 6 -lag-in-frames 25 -enable-tpl 1
175 # test -e tmp/fav/"$base".360p.vp9.webm-0.log || {
176 # # FIXME: how to disable h264 debug log?
177 # ffmpeg 2>/dev/null -i src/"$src" \
178 # -map 0:v "$@" -pass 1 -passlogfile wip/fav/"$base".360p.vp9.webm \
181 # mv_ -t tmp/fav/"${base%/*}" wip/fav/"$base".360p.vp9.webm-*.log
183 # ffmpeg -i src/"$src" -i tmp/fav/"$base".opus \
184 # -map 0:v "$@" -pass 2 -passlogfile tmp/fav/"$base".360p.vp9.webm \
185 # -map 1:a -c:a copy \
186 # wip/fav/"$base".360p.vp9.webm
187 # mkdir -p fav/"$month"/by-uuid/"$uuid"
188 # mv_ wip/fav/"$base".360p.vp9.webm fav/"$base".360p.vp9.webm
189 # rm -fv tmp/fav/"$base".360p.vp9.webm-*.log
191 #test fav/"$base".mp4 -nt src/"$src" || {
192 # ffmpeg -i src/"$src" \
193 # -filter:v format=yuv420p \
194 # -c:v libx264 -preset medium -crf 22 \
195 # -tune zerolatency \
196 # -maxrate 1M -bufsize 2M \
197 # -movflags use_metadata_tags +faststart \
198 # -c:a libopus -base:a 64k -application voip \
199 # -filter:v "scale=iw/2:ih/2" \
200 # wip/fav/"$base".mp4
201 # mv_ wip/fav/"$base".mp4 fav/"$base".mp4
203 # class='orient$orient'
204 echo "<video controls preload=none poster='$name.avif'>"
205 # See https://jakearchibald.com/2022/html-codecs-parameter-for-av1/
206 # and ffmpeg -i fav/$month/foo.av1.webm -c:v copy -bsf:v trace_headers -f null /dev/null |&
207 # grep -e seq_profile -e seq_level_idx -e seq_tier -e high_bitdepth -e twelve_bit
208 P
=0; LL
=01; T
=M
; DD
=08
209 echo "<source type='video/webm; codecs=av01.$P.$LL$T.$DD' src='$name.360p.av1.webm' />"
210 echo "<p><a href='$name.360p.av1.webm'>$name.360p.av1.webm</a></p>"
213 touch -a src
/"$base".txt
214 printf %s
"<span class='comment'>"
215 sed -e '$q;s/$/<br \/>\n/' src
/"$base".txt
218 echo "<span class='infos'>"
219 echo " <span class='creationDate'>$creationDate</span>"
221 echo "<span class='links'>"
222 echo " <a class='by-uuid' href='by-uuid/$uuid/$dst' title='Lien public vers cette capture' target='_blank'>@</a>"
223 echo " <a class='anchor' href='#$id' title='Ancre vers cette capture'>#</a>"
235 } >tmp
/fav
/"$month"/index.html
236 mv_
-f {tmp
/,}fav
/"$month"/index.html
238 echo >&2 "$dir: removing any deleted favorites"
239 (cd fav
; find "$month" -mindepth 1 -type f
-not -name index.html
) |
240 while read -r fav
; do
244 for src
in src
/"$base".fav.
*; do test "${src%.txt}" != "$src" || hasSrc
=set; done
245 test "${hasSrc:+set}" || {
246 echo >&2 "$dir: removing $year/fav/$fav"
248 rm -f fav
/"$month"/by
-uuid/*/"${name%.*}".
*
251 rmdir -p 2>/dev
/null fav
/"$month"/by
-uuid/*/ || true
252 rmdir -p 2>/dev
/null wip
/fav
/* || true