From: Paul Hänsch Date: Mon, 6 Apr 2020 14:42:15 +0000 (+0200) Subject: Merge commit '25d0e33ef0647e0019b18a934100a4f15a773a55' as 'cgilite' X-Git-Url: http://git.plutz.net/?p=serve0;a=commitdiff_plain;h=7db7dc21eaa620d93ab65b41ae5aa336cb501db5;hp=25d0e33ef0647e0019b18a934100a4f15a773a55 Merge commit '25d0e33ef0647e0019b18a934100a4f15a773a55' as 'cgilite' --- diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a01ee28 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.*.swp diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..e69de29 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..de179ab --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +.PHONY: _subtrees + +_subtrees: _cgilite + +cgilite: + git subtree add --squash -P $@ https://git.plutz.net/git/$@ master + +_cgilite: cgilite + git subtree pull --squash -P $< https://git.plutz.net/git/$< master + diff --git a/advsearch.sh b/advsearch.sh new file mode 100644 index 0000000..3a2f333 --- /dev/null +++ b/advsearch.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +f='' +order="$(POST order |grep -m1 -xE 'Name|Date|Length|Group' || printf Name)" + +for n in 1 2 3 4 5 6 7 8 9; do + [ "$(POST pol_$n)" = neg ] \ + && f="$f~" + cat="$(POST cat_$n)" + for m in $(seq 1 $(POST_COUNT tag_$n)); do + tag="$(POST tag_$n $m)" + [ ! "${tag##${cat}:*}" ] || [ ! "${tag##-${cat}:*}" ] || [ "$cat" = '*' -a "${tag##*:*}" ] \ + && f="${f}${tag}|" + done + f="${f%[|^]}^" +done +f="${f%^}" + +REDIRECT "$(URL "${ITEM}")?o=${order}&f=${f}" diff --git a/cgilite.sh b/cgilite/cgilite.sh similarity index 100% rename from cgilite.sh rename to cgilite/cgilite.sh diff --git a/file.sh b/cgilite/file.sh similarity index 100% rename from file.sh rename to cgilite/file.sh diff --git a/html-sh.sed b/cgilite/html-sh.sed similarity index 100% rename from html-sh.sed rename to cgilite/html-sh.sed diff --git a/logging.sh b/cgilite/logging.sh similarity index 100% rename from logging.sh rename to cgilite/logging.sh diff --git a/session.sh b/cgilite/session.sh similarity index 100% rename from session.sh rename to cgilite/session.sh diff --git a/storage.sh b/cgilite/storage.sh similarity index 100% rename from storage.sh rename to cgilite/storage.sh diff --git a/index.cgi b/index.cgi new file mode 100755 index 0000000..a33211d --- /dev/null +++ b/index.cgi @@ -0,0 +1,106 @@ +#!/bin/sh + +for n in "$@"; do case ${n%%=*} in + data) _DATA="${n#data=}";; + exec) _exec="${n#exec=}";; + noerr) exec 2>&-;; +esac; done + +[ -z "${_EXEC%/}" ] && _EXEC="$(realpath "${0%/*}")" || _EXEC="${_EXEC%/}" +[ -z "${_DATA%/}" ] && _DATA=. || _DATA="${_DATA%/}" + +file_pattern='^.*\.(mov|ts|mpg|mpeg|mp4|m4v|avi|mkv|flv|sfv|wmv|ogm|ogv|webm|iso|rmvb)$' + +. "$_EXEC/cgilite/cgilite.sh" + +FILTER="$(GET f)" +SEARCH="$(GET s)" +ORDER="$(GET o |grep -m1 -axE 'Date|Name|Length|Group' || printf Name)" +LISTSIZE="$(COOKIE pagesize |grep -m1 -axE '[1-9][0-9]*' || printf 50)" +ITEM="$(PATH "${PATH_INFO#/}")" +ACTION="$(GET a)" + +case $ACTION in + setprefs) + SET_COOKIE +$((86400 * 90)) pagesize="$(POST pagesize |grep -m1 -axE '[1-9][0-9]*' || printf 50)" + SET_COOKIE +$((86400 * 90)) mode="$(POST mode |grep -m1 -axE 'browse|index' || printf browse)" + SET_COOKIE +$((86400 * 90)) fakemp4="$(POST fakemp4 |grep -m1 -axE 'yes' || printf no)" + SET_COOKIE +$((86400 * 90)) downscale="$(POST downscale |grep -m1 -axE 'yes' || printf no)" + [ "$(POST index)" = "update" ] && touch -cd @0 "${_DATA}/.index/meta.time" + REDIRECT "$(POST ref)" + ;; + bookmark) + bm="$_DATA/.index/bookmarks" + . "$_EXEC/cgilite/storage.sh" + + s="$(POST search |STRING)"; f="$(POST filter |STRING)" + if LOCK "$bm"; then + grep -avF " search=$s filter=$f${CR}" "$bm" >"$bm.tmp" + [ ! "$(POST delete)" ] \ + && printf '%s search=%s filter=%s\r\n' \ + "$(POST name |STRING)" "$s" "$f" >>"$bm.tmp" + mv "$bm.tmp" "$bm" + RELEASE "$bm" + fi + REDIRECT "$(POST ref)" + ;; + multitag) + . "$_EXEC/multitag.sh" + REDIRECT "$(POST ref)" + ;; +esac + +if [ "$ITEM" = "/style.css" ]; then + . "$_EXEC/cgilite/file.sh" + [ -r "$_DATA/$ITEM" ] && FILE "$_DATA/$ITEM" \ + || FILE "$_EXEC/style.css" +elif [ "$ITEM" = "/stereoview.js" ]; then + . "$_EXEC/cgilite/file.sh" + FILE "$_EXEC/stereoview.js" +elif [ -f "$_DATA/$ITEM" ]; then + case $ACTION in + thumbnail) + . "$_EXEC/cgilite/file.sh" + . "$_EXEC/thumbnail.sh" + index="$_DATA/${ITEM%/*}/.index" + thumb="$index/${ITEM##*/}"; thumb="${thumb%.*}.jpg" + [ -d "$index" -a ! -f "$thumb" ] \ + && { printf %s "$ITEM" |grep -qE -e "${file_pattern}" ;} \ + && gen_thumb "$_DATA/$ITEM" "$thumb" + FILE "$thumb" + ;; + delete) + ;; + download) + . "$_EXEC/cgilite/file.sh" + fakemp4="$(COOKIE fakemp4)" + downscale="$(COOKIE downscale)" + downfile="$_DATA/${ITEM%/*}/.transcode/${ITEM%.*}.480p.webm" + if [ "$downscale" = yes -a -f "$downfile" ]; then + FILE "$downfile" "$([ "$fakemp4" = yes ] && printf 'video/mp4')" + else + FILE "$_DATA/$ITEM" "$([ "$fakemp4" = yes ] && printf 'video/mp4')" + fi + ;; + *) . "$_EXEC/view.sh" + esac +elif [ -d "$_DATA/$ITEM" ]; then + case $ACTION in + advsearch) + . "$_EXEC/advsearch.sh" + ;; + spawnindex) + if [ "$(POST recursive)" = yes ]; then + find "$_DATA/$ITEM" -depth -type d \! -name .index \ + -exec mkdir -p '{}'/.index \; + else + mkdir -p "$_DATA/$ITEM/.index" + fi + REDIRECT "$(POST ref)" + ;; + *) . "$_EXEC/list.sh" + ;; + esac +else + printf 'Status: 404 Not Found\r\nContent-Length 0:\r\n\r\n' +fi diff --git a/indexmeta.sh b/indexmeta.sh new file mode 100644 index 0000000..2da085f --- /dev/null +++ b/indexmeta.sh @@ -0,0 +1,107 @@ +#!/bin/sh + +[ -n "$include_indexmeta" ] && return 0 +include_indexmeta="$0" + +. "$_EXEC/cgilite/storage.sh" +file_pattern='^.*\.(mov|ts|mpg|mpeg|mp4|m4v|avi|mkv|flv|sfv|wmv|ogm|ogv|webm|iso|rmvb)$' + +meta_name() { + local fn + fn="$1"; fn="${fn##*/}"; fn="${fn%.*}" + STRING "$fn"; printf '\r' +} + +meta_line() { + local video l w h + video="$1" + + read l h w <<__EOF + $(printf '' \ + | mplayer -input nodefault-bindings -nosound -vo null -identify -frames 0 "$video" 2>&- \ + | sort | sed -rn ' + s:ID_LENGTH=(.*)(\..*)$:\1:p; + s:ID_VIDEO_HEIGHT=(.*):\1:p; + s:ID_VIDEO_WIDTH=(.*):\1:p;' \ + | tr '\n' ' ' + ) +__EOF + printf '%i\t%i\t%i\ttags=\tcomment=\t%s\n' \ + "${l-0}" "${w-0}" "${h-0}" "$(meta_name "$video")" +} + +meta_file(){ + local file meta name + file="$1" + meta="${file%/*}/.index/meta" + name="$(meta_name "$file")" + + if [ -d "${meta%/meta}" ] && LOCK "$meta"; then + grep -avF " ${name}" "$meta" >"$meta.tmp" + meta_line "$file" \ + | tee -a "$meta.tmp" + mv "$meta.tmp" "$meta" + RELEASE "$meta" + fi +} + +meta_purge(){ + local file meta name + file="$1" + meta="${file%/*}/.index/meta" + name="$(meta_name "$file")" + + if [ -d "${meta%/meta}" ] && LOCK "$meta"; then + grep -avF " ${name}" "$meta" >"${meta}.tmp" + grep -aF " ${name}" "$meta" >>"${meta}.trash" + mv "${meta}.tmp" "$meta" + RELEASE "$meta" + fi +} + +meta_info(){ + local file meta + file="$1"; meta="${file%/*}/.index/meta" + + if [ -d "${meta%/meta}" ]; then + grep -aF " $(meta_name "$file")" "$meta" \ + | grep -m1 -axE '[0-9]+ [0-9]+ [0-9]+ tags=[^ ]* comment=[^ ]* .+' \ + || meta_file "$file" + else + printf '0\t0\t0\ttags=\tcomment=\t%s\r' "$(meta_name "$file")" + fi +} + +meta_dir(){ + local dir meta v + dir="${1}" + meta="${dir}/.index/meta" + metat="${dir}/.index/meta.time" + + [ -f "$metat" ] || touch -d @0 "$metat" + + if [ -d "$dir/.index" -a \! -f "$meta" ] && LOCK "$meta"; then + touch "$meta" # preliminary touch to prevent concurrent generators + find "$dir" -type f -mindepth 1 -maxdepth 1 \ + | grep -aE "$file_pattern" \ + | while read -r v; do + meta_line "$v" + done >"$meta" + + RELEASE "$meta" + elif [ -d "$dir/.index" -a "$dir" -nt "$metat" ] && LOCK "$meta"; then + cp -p "$meta" "$meta.ref"; touch "$meta" + find "$dir" -type f -newer "$metat" \ + -mindepth 1 -maxdepth 1 \ + | grep -aE "$file_pattern" \ + | while read -r v; do + grep -qF " $(meta_name "$v")" "$meta" \ + || meta_line "$v" + done >>"$meta" + sort -u "$meta" >"$meta.ref" + mv "$meta.ref" "$meta" + touch "$metat" + + RELEASE "$meta" + fi +} diff --git a/list.sh b/list.sh new file mode 100644 index 0000000..58eb062 --- /dev/null +++ b/list.sh @@ -0,0 +1,237 @@ +#!/bin/sh + +. "$_EXEC/indexmeta.sh" +. "$_EXEC/widgets.sh" + +list_item() { + local meta type length width height tags comment name display link + meta="${1}"; type="${meta%% *}"; meta="${meta#* }" + + if [ "$type" = dir ]; then + name="${meta%% *}"; + display="$(HTML "$name")"; link="$(URL "$ITEM/$name")" + printf '[a .list .dir href="%s" %s]' "${link}?${w_refuri#*\?}" "$name" + return 0 + fi + + length="${meta%% *}"; meta="${meta#* }" + width="${meta%% *}"; meta="${meta#* }" + height="${meta%% *}"; meta="${meta#* }" + tags="${meta%% *}"; meta="${meta#* }" + comment="${meta%% *}"; meta="${meta#* }" + name="${meta%% *}"; meta="${meta#* }" + + if [ "$type" = metashort ]; then + name="$(list_fullname "$(UNSTRING "${name%${CR}}")")" + fi + if [ -f "$_DATA/$ITEM/$name" ]; then + link="$(URL "$ITEM/$name")" + name="$(HTML "$ITEM/$name")" + printf '[div .list .file + [a href="%s" [img src="%s?a=thumbnail"]][label %s] + [span .time %i:%02imin] [span .dim %ix%i] %s + [checkbox "select" "%s" id="select_%s"][label for="select_%s" +] + ]' \ + "$link" "$link" "${name##*/}" \ + "$((length / 60))" "$((length % 60))" \ + "$width" "$height" \ + "$(printf '%s\n' "${tags#tags=}" \ + | sed -r "$UNSTRING"' s;^;,;; s;,+;,;g; s;,$;;; + :X s;,-?([^,]+)(,|$); [span .tag\n \1]\2;; tX;' + )" "$name" "$link" "$link" + else + printf 'Canning record for nonexist file: %s\n' "$name" >&2 + meta_purge "$_DATA/$ITEM/$name" + fi +} + + +[ "$FILTER" ] && list_fex="$( + fex='p' + STRING "$FILTER^" \ + | sed -r 's;\^;\n;g; s;[]\/\(\)\\\^\$\?\.\+\*\;\[\{\}];\\&;g' \ + | while read -r f; do + [ ! "${f#~}" ] && continue + [ "${f#~}" = "$f" ] \ + && fex="/(\ttags=([^\t]*,)?)(${f})((,[^\t]*)?\t)/{${fex}}" \ + || fex="/(\ttags=([^\t]*,)?)(${f#~})((,[^\t]*)?\t)/d; ${fex}" + printf '%s\n' "${fex}" + done \ + | tail -n1 +)" + +list_fullname(){ + sn="$1" + [ ! "${sn%%*/*}" ] && base="${sn%/*}" || base=. + file="$(printf '%s' "$_DATA/$ITEM/$sn".*)" + file="${file##*/}" + [ -e "$_DATA/$ITEM/$base/${file}" ] \ + && printf '%s\n' "${base}/${file}" +} + +list_filter(){ + if [ "$FILTER" ]; then + sed -nr "$list_fex" + elif [ "${SEARCH#!}" != "${SEARCH}" ]; then + grep -aviEe "$(STRING "${SEARCH}" \ + | sed -r ':x s;((^|[^\\])(\\\\)*)\+;\1 ;g; tx; + s;((^|[^\\])(\\\\)*)\\\+;\1+;g; + s; ;\\+;g;')" + elif [ "${SEARCH}" ]; then + grep -aiEe "$(STRING "${SEARCH}" \ + | sed -r ':x s;((^|[^\\])(\\\\)*)\+;\1 ;g; tx; + s;((^|[^\\])(\\\\)*)\\\+;\1+;g; + s; ;\\+;g;')" + else + cat + fi +} + +list_order(){ + local fm fn fn al length ln h w t c name buffer l + + if [ $ORDER = Name ]; then + sort -k6 |sed 's;^;metashort\t;;' + elif [ $ORDER = Group ]; then + sed -E ' + :X + s;^([^\t]+\t[^\t]+\t[^\t]+\t[^\t]+\t[^\t]+\t[^ 0-9]*)(-[0-9a-zA-Z_-]{11}\r|-ph[0-9a-f]{13}\r|-[0-9]{8}\r|[0-9]+\+?)(.*)$;\1\r\3 \2;; + tX;' \ + | { sort -n -k7 |sort -s -k6,6; echo '0 0 0 tags= comment= _'; } \ + | while read -r length h w t c name; do + if [ "${ln%% *}" = "${name%% *}" ]; then + al=$((al + length)) + buffer="${buffer}${BR}$length $h $w $t $c $name" + else + printf '%s\n' "$buffer" |while read -r l; do + [ "$l" ] && printf '%s %s\n' "$al" "$l" + done + al="$length" + buffer="$length $h $w $t $c $name" + fi + ln="$name" + done \ + | sort -s -n -k1 |sed -r 's;^[0-9]+\t;metashort\t;;' \ + | sed -E ':X s;^([^\t]+\t[^\t]+\t[^\t]+\t[^\t]+\t[^\t]+\t[^\r]*)\r([^ ]*) ([^ ]+)( .*)?$;\1\3\2\4;; tX' + elif [ $ORDER = Length ]; then + sort -sn -k1 |sed 's;^;metashort\t;;' + elif [ $ORDER = Date ]; then + while read -r fm; do + sn="${fm##* }" + fn="$(list_fullname "$(UNSTRING "${sn%${CR}}")")" + printf '%i %s %s\n' \ + "$(stat -c %Y "$fn")" "${fm% *}" "$fn" + done \ + | sort -srn -k1 |sed -r 's;^[0-9]+\t;metalong\t;;' + fi +} + +list_filemeta(){ + local meta base cbase fm cachename + base="$1" + meta="$_DATA/$ITEM/$base/.index/meta" + meta_dir "$_DATA/$ITEM/$base" + + cachename="$(printf '%s\n' "$mode" "$FILTER" "$SEARCH" "$ORDER" |sha1sum)" + cachename="$_DATA/$ITEM/.index/${cachename% -}.cache" + + if [ "$cachename" -nt "$meta" ] 2>&-; then + cat "$cachename" + else + cbase="$(STRING "$base")" + grep -axE '[0-9]+ [0-9]+ [0-9]+ tags=[^ ]* comment=[^ ]* .+' "$meta" \ + | while read -r fm; do + printf '%s %s/%s\n' "${fm% *}" "$cbase" "${fm##* }" + done \ + | list_filter \ + | list_order \ + | { [ -d "${meta%meta}" ] && tee "$cachename" || cat; } + fi +} + +list_items() { + local mode meta + mode="$(COOKIE mode |grep -m1 -axE 'index|browse' || printf index )" + + if [ "$mode" = browse ]; then + [ "$ITEM" ] && printf 'dir\t..\n' + (cd "$_DATA/$ITEM"; + find ./ -type d \! -name .index -mindepth 1 -maxdepth 1 \ + ) | cut -d/ -f2- | sort |sed 's;^;dir\t;;' + list_filemeta . + elif [ "$mode" = index ]; then + (cd "$_DATA/$ITEM"; + find ./ -path '*/.index/meta' + ) | while read -r meta; do + list_filemeta "${meta%/.index/meta}" + done + fi +} + +list_paginate() { + local page i c n end qry + page="$(GET p |grep -axE '[0-9]+' || printf 1)"; c=1 + end=$((page + LISTSIZE)) + qry="${w_refuri#*\?}"; qry="${qry#p=*&}" + + printf '[div .itemlist ' + while read -r i; do + c=$((c + 1)) + [ $c -gt $page -a $c -le $end ] && list_item "$i" + done + printf ']' + + [ $(( c % LISTSIZE )) -gt 0 ] \ + && end=$((c / LISTSIZE + 1)) \ + || end=$((c / LISTSIZE)) + + printf '[div .pagination' + for n in $( seq 1 $end ); do + c=$(( (n - 1) * LISTSIZE + 1 )) + [ $c = $page ] \ + && printf '[a .page .current href="%s" %s]' "?p=${c}&${qry}" "$n" \ + || printf '[a .page href="%s" %s]' "?p=${c}&${qry}" "$n" + done + printf ']' +} + +printf 'Content-Type: text/html;charset=utf-8\r\n\r\n' + +{ printf ' +[!DOCTYPE HTML] +[html [head [title ' + w_bmname + printf ' by %s]' "$ORDER" + printf ' + [meta name="viewport" content="width=device-width"] + [link rel=stylesheet href="/style.css" ] +] [body + [div #navigation + [a #t_bookmarks href="#bookmarks" ★]' + w_search + printf ' + [a #t_avsearch href="#advsearch" Advanced] + [a #t_prefs href="#prefs" ⚙] + ]' + w_bookmarks + w_advsearch + w_prefs + printf ' + [form method=POST action="?a=multitag"' + list_items \ + | list_paginate + [ -d "$_DATA/$ITEM/.index" ] && { printf ' + [div #editing' + w_tagging + printf ' + ]'; } + printf ' + ]' + [ ! -d "$_DATA/$ITEM/.index" ] && { printf ' + [div #editing' + w_index + printf ' + ]'; } + printf ' +] ] +'; } | "$_EXEC/cgilite/html-sh.sed" diff --git a/multitag.sh b/multitag.sh new file mode 100644 index 0000000..353ead6 --- /dev/null +++ b/multitag.sh @@ -0,0 +1,55 @@ +#!/bin/sh + +. "$_EXEC/cgilite/storage.sh" +. "$_EXEC/indexmeta.sh" + +newtags='' +for tn in $(seq 1 $(POST_COUNT tag)); do + newtags="$(POST tag $tn)${BR}${newtags}" +done +newtags="$(POST newtag |tr -d '\r' |tr , '\n')${BR}${newtags}" + +while [ "${newtags#${BR}}" != "${newtags}" ]; do newtags="${newtags#${BR}}"; done +while [ "${newtags%${BR}}" != "${newtags}" ]; do newtags="${newtags%${BR}}"; done + +[ "$(POST op)" = del ] && deltags="$newtags" + +for select in $(seq 1 $(POST_COUNT select)); do + file="$_DATA/$ITEM/$(POST select $select |PATH)" + meta="${file%/*}/.index/meta" + + read -r length width height tags comment fn <<-EOF + $(meta_info "$file") + EOF + tags="$(UNSTRING "${tags#tags=}" |tr , '\n')" + + if [ ! "$deltags" ]; then + extags="$(printf '%s' "${newtags}" |grep -e "^-" |cut -d: -f1 )" + [ "$extags" ] && tags="$(printf %s\\n "$tags" |grep -vwFe "$extags")" + if printf %s "${newtags}" |grep -e "^-" |grep -qEe "^-[^:]+$"; then + tags="$(printf %s\\n "$tags" |grep -vEe '^-[^:]+$')" + fi + tags="$(printf '%s\n' "${tags},${newtags}" \ + | tr , '\n' |sort -u |tr '\n' , \ + | STRING)" + else + detag="${deltags}${BR}"; while [ "$detag" ]; do + tags="$(printf '%s\n' "$tags" |grep -vxFe "${detag%%${BR}*}")" + detag="${detag#*${BR}}" + done + tags="$(printf '%s\n' "$tags" |sort -u |tr '\n' , |STRING)" + fi + tags="${tags#,}"; tags="${tags%,}" + + if LOCK "$meta"; then + grep -avF " $fn" "$meta" >"${meta}.tmp" + + printf '%i %i %i tags=%s comment=%s %s\n' \ + "$length" "$width" "$height" "$tags" \ + "${comment#comment=}" "$fn" \ + >>"${meta}.tmp" + mv "${meta}.tmp" "$meta" + + RELEASE "$meta" + fi +done diff --git a/stereoview.js b/stereoview.js new file mode 100644 index 0000000..edba378 --- /dev/null +++ b/stereoview.js @@ -0,0 +1,147 @@ +/* Copyright 2018 Paul Hänsch + + This file is part of Serve0 + + Serve0 is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Serve0 is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with Serve0 If not, see . +*/ + +var render, video, controlTimeout = 0; +var pitch = 0, roll = 0, yaw = 0; +var w, h, hdeg, vdeg, scale, fov = 90; +var lv = document.createElement("canvas"); +var rv = document.createElement("canvas"); +var debug = document.createElement("p"); +var gp; + +function draw() { + sw = fov * hdeg |0; + sh = h / 2 |0; + dh = h / 2 * scale |0; + + if ( layout == "180" ) { + sx = (w / 2 - fov * hdeg) / 2 + yaw * hdeg |0; + sy = ( h - fov * vdeg) / 2 + pitch * vdeg |0; + if (sx + sw > w / 2) { sx = w / 2 - sw; } else if (sx < 0) { sx = 0; } + lc.drawImage(video, sx, sy, sw, sh, 0, 0, lv.width, dh); + rc.drawImage(video, w / 2 + sx, sy, sw, sh, 0, 0, rv.width, dh); + } else { + sx = (w - fov * hdeg) / 2 + yaw * hdeg |0; + sy = (h - fov * vdeg) / 4 + pitch * vdeg |0; + lc.drawImage(video, sx, sy, sw, sh, 0, 0, lv.width, dh); + rc.drawImage(video, sx, h/2 + sy, sw, sh, 0, 0, rv.width, dh); + if (sx < 0) { + lc.drawImage(video, sx + w, sy, sw, sh, 0, 0, lv.width, dh); + rc.drawImage(video, sx + w, h/2 + sy, sw, sh, 0, 0, rv.width, dh); + } else if ( sx + fov * hdeg > w) { + lc.drawImage(video, sx - w, sy, sw, sh, 0, 0, lv.width, dh); + rc.drawImage(video, sx - w, h/2 + sy, sw, sh, 0, 0, rv.width, dh); + } + } + + lv.style.transform = "rotate(" + roll + "deg)"; + rv.style.transform = "rotate(" + roll + "deg)"; + + requestAnimationFrame(draw); + + gp = navigator.getGamepads()[0]; + if ( gp && Date.now() > controlTimeout ) { + if ( gp.axes[0] > .3) { video.currentTime += 10; controlTimeout = Date.now() + 500; } + if ( gp.axes[0] < -.3) { video.currentTime -= 10; controlTimeout = Date.now() + 500; } + if ( gp.axes[1] < -.3) { video.currentTime += 60; controlTimeout = Date.now() + 500; } + if ( gp.axes[1] > .3) { video.currentTime -= 60; controlTimeout = Date.now() + 500; } + if ( gp.buttons[0].pressed ) { video.currentTime += 1/30; video.pause(); } + if ( gp.buttons[1].pressed ) { video.play(); } + if ( gp.buttons[2].pressed ) { fov -= 10; controlTimeout = Date.now() + 500; } + if ( gp.buttons[3].pressed ) { fov += 10; controlTimeout = Date.now() + 500; } + } + + // debug.textContent = "" + video.currentTime + " " + controlTimeout + " " + tx + " " + ty + " " + tz; +}; + +function stereoview(layout, video) { + this.layout = layout; this.video = video; + document.body.appendChild( lv ); + document.body.appendChild( rv ); + document.body.appendChild( debug ); + + lv.setAttribute( "style", "position: fixed; top: 0; left: 0 ; width: 50%; height: 100%; z-index: 100;"); + rv.setAttribute( "style", "position: fixed; top: 0; left: 50%; width: 50%; height: 100%; z-index: 100;"); + debug.setAttribute( "style", "position: fixed; top: 0; left: 0; z-index: 101; background: #000;"); + + lv.setAttribute( "width", "" + lv.offsetWidth); + rv.setAttribute( "width", "" + rv.offsetWidth); + lv.setAttribute("height", "" + lv.offsetHeight); + rv.setAttribute("height", "" + rv.offsetHeight); + + lc = lv.getContext("2d"); + rc = rv.getContext("2d"); + + mpuevent = new EventSource("http://localhost:314"); + var x = [], y = [], z = [], cnt = -1, inertia = 6; + + mpuevent.addEventListener("bearing", function(e) { + bearing = e.data.split(" "); + yaw = -parseFloat(bearing[0]); + }, false); + mpuevent.addEventListener("motion", function(e) { + motion = e.data.split(" "); + + cnt = (cnt + 1) % inertia; + x[cnt] = parseFloat(motion[0]); + y[cnt] = parseFloat(motion[1]); + z[cnt] = parseFloat(motion[2]); + + // tx = 0; x.forEach( function(n, i){ tx += n; } ); tx /= inertia; + ty = 0; y.forEach( function(n, i){ ty += n; } ); ty /= inertia; + tz = 0; z.forEach( function(n, i){ tz += n; } ); tz /= inertia; + + pitch = Math.asin((tz / 9.81 > 1)?1:(tz/9.81)) / Math.PI * 180 + 22.5; + roll = - Math.asin((ty / 9.81 > 1)?1:(ty/9.81)) / Math.PI * 180; + // yaw = (yaw + ty) % 360; + }, false ); + + window.addEventListener("devicemotion", (function() { + var x = [], y = [], z = [], cnt = -1, inertia = 6; + return function(event) { + cnt = (cnt + 1) % inertia; + + x[cnt] = event.accelerationIncludingGravity.x; + y[cnt] = event.accelerationIncludingGravity.y; + z[cnt] = event.accelerationIncludingGravity.z; + + tx = 0; x.forEach( function(n, i){ tx += n; } ); tx /= inertia; + ty = 0; y.forEach( function(n, i){ ty += n; } ); ty /= inertia; + tz = 0; z.forEach( function(n, i){ tz += n; } ); tz /= inertia; + + pitch = Math.asin((tz / 9.81 > 1)?1:(tz/9.81)) / Math.PI * 180 + 22.5; + roll = - Math.asin((ty / 9.81 > 1)?1:(ty/9.81)) / Math.PI * 180; + yaw = (yaw + ty) % 360; + }; + })()); + + window.addEventListener("click", function(event) { + (lv.parentElement)?lv.parentElement.removeChild(lv):{}; + (rv.parentElement)?rv.parentElement.removeChild(rv):{}; + video.style.display = "block"; + video.pause(); + }, true); + + w = video.videoWidth; h = video.videoHeight; + hdeg = w / 360; vdeg = h / 180; + scale = lv.width / (fov * hdeg |0); + // scale = lv.width / (w / 4); + video.play(); + video.style.display = "none"; + draw(); +}; diff --git a/style.css b/style.css new file mode 100644 index 0000000..2e461e1 --- /dev/null +++ b/style.css @@ -0,0 +1,424 @@ +* { + box-sizing: border-box; + margin: 0; padding: 0; + text-align: left; +} +button { padding: .125em .5em; } +a { color: inherit; text-decoration: none;} + +input { + border: 1px solid; + padding: .25em .5em; + line-height: 1em; + vertical-align: bottom; +} +input[type=number] { padding-right: 0; } +input[type=radio], input[type=checkbox] { vertical-align: baseline; } +select { + border: 1px solid; + padding: .125em .5em; +} +select[multiple] { padding: 0; } +select[multiple] option { padding: .25em .5em; } + +body { + color: white; + background-color: black; + min-height: 100%; +} + + +/* ### Main Page Elements ### */ + +#navigation{ + position: relative; + text-align: center; + border-bottom: 1px solid; + background-color: #333; +} + +#editing, +#multitag { + position: fixed; + bottom: 0; width: 100%; + padding: .25em 0; + border-top: 1px solid; + background-color: #333; +} + +#search { display: inline; } + +a[href="#prefs"], +a[href="#bookmarks"] { + position: absolute; + top: 0; + margin: 0 .25em; + font-size: 1.5em; +} +a[href="#prefs"] { right: 0; } +a[href="#bookmarks"] { left: 0; } + +a[href="#advsearch"] { margin-left: .5em; } +a[href="#advsearch"]:before { + content: '\25b8'; + margin: 0 .5em; +} + + +/* ### Expandable Drawers ### */ + +#prefs, #bookmarks, #multitag, #advsearch { + display: block; position: absolute; + max-height: 0; width: 100%; max-width: 100%; + margin-top: -1px; + padding: 0 1em; + border: 1px none; + overflow: hidden; + background-color: #333; + transition: max-height .3s linear; + z-index: 1; +} +#bookmarks { + left: 0; width: 30%; + min-width: 300px; + margin-right: auto; +} +#prefs { + right: 0; width: 20%; + min-width: 200px; + margin-left: auto; +} +#advsearch { + text-align: center; +} + +#advsearch:target, #multitag:target, +#prefs:target, #bookmarks:target { + max-height: 25em; overflow-y: scroll; +} +#advsearch:target { border-style: none none solid none; } +#prefs:target { border-style: none none solid solid; } +#bookmarks:target { border-style: none solid solid none; } +#multitag:target { border-style: solid none none none; } + +#advsearch a[href="#"], +#multitag a[href="#"], +#bookmarks a[href="#"], +#prefs a[href="#"] { + display: block; + line-height: 2em; + font-weight: bold; + background-color: inherit; +} +-#prefs a[href="#"] { width: 2.5em; left: auto; } +-#bookmarks a[href="#"] { width: 2.5em; right: auto; } +#advsearch a[href="#"] { border-bottom: 1px solid; } + + +/* ### Preferences Drawer ### */ + +#prefs label[for=prefs_ps] { + font-weight: bold; +} +#prefs #prefs_ps { + max-width: 4em; + margin-bottom: 1em; +} +#prefs button { + margin: 1em 0; +} +#prefs input { vertical-align: top; } +#prefs input[type=radio] + label, +#prefs input[type=checkbox] + label { + display: inline-block; + margin-bottom: .5em; + max-width: 85%; +} + + +/* ### Bookmarks Drawer ### */ + +#bookmarks input, +#bookmarks button { + margin-bottom: 1.25em; +} +#bookmarks label { + display: block; + font-weight: bold; + word-break: break-word; + overflow: hidden; +} +#bookmarks a.link { + display: inline-block; + font-size: .75em; + text-decoration: underline; + margin: 0 1em 1.25em 0; +} + + +/* ### Advance Search Drawer ###*/ + +#advsearch .help { + display: block; + margin: 1em .5em 1em .5em; + padding: .5em 1em; + background-color: #444; + line-height: 1.5em; +} + +#advsearch input.and {display: none;} +#advsearch input.and + label {display: none} +#advsearch input.and + label + .select { + display: inline-block; + position: relative; + width: 100%; min-width: 0; max-width: 0; + min-height: 12em; + overflow: hidden; + vertical-align: top; + border: none; + transition: max-width .3s linear; +} +#advsearch .submit { + display: inline-block; + width: 100%; + vertical-align: top; +} + +#advsearch input.and + label:nth-of-type(2), +#advsearch input.and:checked + label + .select + input + label { + display: inline-block; + vertical-align: top; + margin: 0 .5% 1em .5%; + width: 4%; min-width: 4em; + padding: .5em 0; + text-align: center; + font-weight: bold; + border: 1px solid; +} + +#advsearch input.and:checked + label + .select + input:checked + label, +#advsearch input.and:checked + label:nth-of-type(2), +#advsearch input.and:checked + label { display: none; } + +#advsearch input.and + label + .select:first-of-type, +#advsearch input.and:checked + label + .select { + min-width: 200px; max-width: 100%; + margin: 0 .5% 1em .5%; + border: 1px solid; +} + +@media (min-width: 460px){ +#advsearch .submit, +#advsearch input.and + label + .select:first-of-type, +#advsearch input.and:checked + label + .select { + width: 49%; +} } +@media (min-width: 660px){ +#advsearch .submit, +#advsearch input.and + label + .select:first-of-type, +#advsearch input.and:checked + label + .select { + width: 32%; +} } +@media (min-width: 860px){ +#advsearch .submit, +#advsearch input.and + label + .select:first-of-type, +#advsearch input.and:checked + label + .select { + width: 24%; +} } +@media (min-width: 1060px){ +#advsearch .submit, +#advsearch input.and + label + .select:first-of-type, +#advsearch input.and:checked + label + .select { + width: 19%; +} } + +#advsearch .submit { min-height: 0; } +#advsearch .submit * { width: 50%; } + +#advsearch .select input.pol { + margin: .5em .25em 0 .5em; +} +#advsearch .select input.pol + label { + font-weight: bold; +} +#advsearch .select label.head { + display: block; + font-weight: bold; + padding: .5em 0 0 .5em; + border-bottom: 1px solid; +} + +#advsearch .select select {display: none;} +#advsearch .select input.cat { display: none; } +#advsearch .select input.cat + label { + display: block; + margin-right: 50%; + padding: .25em .5em; + font-size: 1.125em; + border-bottom: 1px solid; +} +#advsearch .select input.cat:checked + label { + background-color: #444; +} +#advsearch .select input.cat:checked + label + select { + display: block; + position: absolute; + top: 3.5em; bottom: 0; + right: 0; left: 50%; + width: 50%; +} + + +/* ### Item Listing ### */ + +.list { + position: relative; + display: inline-block; + width: 100%; + padding: .375em; + vertical-align: top; + overflow: hidden; +} + +.list.dir { padding: .5em 1em; } +.list:before { + position: absolute; + top: .25em; left: .25em; + bottom: .25em; right: .25em; + content: ''; + z-index: -2; +} +.list.dir:before { background-color: #CCF; color: black; } +.list.file:before { background-color: #333; } +.list.file:first-of-type { clear: left; } + +.list.file a img{ + display: block; + width: 100%; + min-height: 4em; + border-bottom: 1px solid black; +} +.list.file a + label{ + display: inline-block; + width: 100%; max-height: 2.5em; + margin-right: -10em; + padding: .25em .5em; + word-break: break-word; + background-color: #222; + overflow: hidden; +} + +.list.file .time, .list.file .dim { + float: right; position: relative; + display: inline-block; + top: -1.5em; bottom: 1.5; + margin-right: .125em; + padding: .125em .25em; + background-color: rgba(0,0,0,.75); +} + +.list.file .tag, +.list.file input + label { + display: inline-block; + margin: .125em -.125em 0 0; + padding: .125em .5em; + color: black; + background-color: #DCC; +} + +.list.file input[type="checkbox"] { display: none; } +.list.file input[type="checkbox"] + label { + border: 1px solid; + background-color: #ECC; +} +.list.file input[type="checkbox"]:checked + label { + background-color: #8F8; +} + +.itemlist, +.pagination { + display: block; + text-align: center; + margin-top: 1em; +} +.pagination { margin-bottom: 3em;} +.page { + display: inline-block; + margin: 0 .125em; + padding: .25em .5em; + color: #DAA; + background-color: #000; + border: 1px solid; +} +.page.current { + font-weight: bold; + color: black; + background-color: #FDD; + border-color: #DAA; +} + +#index label:first-of-type { font-weight: bold; } +#index input, #index button { margin-left: 1em;} + + +/* ### Video View ### */ + +body#view { padding-bottom: 6em; } + +#view h1 { + max-width: 100%; + margin: .5em .75em; + font-size: 1.25em; + font-weight: bold; + word-break: break-word; + text-decoration: none; +} + +#view #mainvideo { + display: block; + width: 98%; + margin-left: auto; margin-right: auto; + max-height: 240px; +} +@media(min-height: 400px) { #view #mainvideo { max-height: 320px; } } +@media(min-height: 480px) { #view #mainvideo { max-height: 460px; } } +@media(min-height: 520px) { #view #mainvideo { max-height: 480px; } } +@media(min-height: 640px) { #view #mainvideo { max-height: 600px; } } +@media(min-height: 700px) { #view #mainvideo { max-height: 640px; } } +@media(min-height: 760px) { #view #mainvideo { max-height: 720px; } } +@media(min-height: 1000px){ #view #mainvideo { max-height: 960px; } } +@media(min-height: 1200px){ #view #mainvideo { max-height: 1080px;} } + +#view a[href$=download], +#view a[href^=javascript] { + margin-left: 1em; +} + +#view .time, #view .dim, +#view .tag { + margin-right: -.125em; + padding: .125em .25em; + color: black; +} +#view .time, +#view .dim { background-color: #CCD; } +#view .tag { background-color: #DCC; } + + + +/* ### Multi Tagging Drawer ### */ + +#multitag fieldset{ + display: inline-block; + width: 100%; + vertical-align: top; + padding: 0 .25em; + border: none; +} + +@media (min-width: 520px) { .list, #multitag fieldset { width: 50%; min-width: 250px; } } +@media (min-width: 760px) { .list, #multitag fieldset { width: 33%; } } +@media (min-width: 1020px){ .list, #multitag fieldset { width: 25%; } } + +#multitag fieldset * { width: 100%; } +#multitag fieldset button { width: 50%; } diff --git a/thumbnail.sh b/thumbnail.sh new file mode 100644 index 0000000..1430776 --- /dev/null +++ b/thumbnail.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +[ -n "$include_thumbnails" ] && return 0 +include_thumbnails="$0" + +gen_thumb(){ + file="$1"; thumb="$2"; + + if [ ! -e "$thumb" ] && [ -f "$file" ]; then + l="$( + printf '' \ + | mplayer -input nodefault-bindings -nosound -vo null -identify -frames 0 "$file" 2>&- \ + | sed -rn 's:ID_LENGTH=(.*)(\..*)$:\1:p;' \ + )" + + chunk="$((${l:-10} / 5))" + + tmp="$(mktemp -d)" + for cnt in 1 2 3 4; do + printf '' \ + | mplayer -input nodefault-bindings -nosound -benchmark \ + -noconfig all -really-quiet \ + -frames 1 -ss "$((cnt * chunk))" \ + -vf framestep=I \ + -vo jpeg:quality=100:outdir="${tmp}" \ + "$file" 2>&- + mv "${tmp}/00000001.jpg" "${tmp}/_${cnt}.jpg" + done + printf '' \ + | mplayer -input nodefault-bindings -nosound -benchmark \ + -noconfig all -really-quiet \ + -vf scale=159:-2,tile=2:2:4:0:2 \ + -vo jpeg:quality=96:outdir="${tmp}"\ + "mf://$tmp/_*.jpg" 2>&- + + mv "${tmp}/00000001.jpg" "$thumb" + rm -r "${tmp}" + + elif [ ! -e "$thumb" ]; then + touch "$thumb" + fi +} diff --git a/view.sh b/view.sh new file mode 100644 index 0000000..4ee5fb5 --- /dev/null +++ b/view.sh @@ -0,0 +1,52 @@ +#!/bin/sh + +. "$_EXEC/indexmeta.sh" +. "$_EXEC/widgets.sh" + +read length width height tags comment short <<-EOF + $(meta_info "$_DATA/$ITEM") + EOF + +printf 'Content-Type: text/html;charset=utf-8\r\n\r\n' + +{ printf ' +[!DOCTYPE HTML] +[html [head [title \n' + HTML "${ITEM##*/}" + printf '] + [meta name="viewport" content="width=device-width"] + [link rel=stylesheet href="/style.css" ] +] [body #view + [script type="text/javascript" src="/stereoview.js"\n] + [div #navigation + [a #t_bookmarks href="#bookmarks" ★]' + w_search + printf ' + [a #t_avsearch href="#advsearch" Advanced] + [a #t_prefs href="#prefs" ⚙] + ]' + w_bookmarks + w_advsearch + w_prefs + printf ' + [video #mainvideo controls="controls" [source src="?a=download" type="video/mp4"]] + [a "?a=download" Download] + [a "javascript:stereoview(180, document.getElementById("mainvideo"));" View 180° Stereoscopic] + [a "javascript:stereoview(360, document.getElementById("mainvideo"));" View 360° Stereoscopic] + [h1\n %s] + [span .time %i:%02imin] [span .dim %ix%i] %s + ' "$(HTML "${ITEM##*/}" |sed -r "$w_ascii"' s;[^0-9a-zA-Z&#];&[wbr];g')" \ + "$((length / 60))" "$((length % 60))" "$width" "$height" \ + "$(printf '%s\n' "${tags#tags=}" |sed -r "$UNSTRING"' + s;^;,;; s;,+;,;g; s;,$;;; :X s;,-?([^,]+)(,|$); [span .tag\n \1]\2;; tX;' + )" + printf ' + [div #editing + [form method=POST action="/?a=multitag" + [hidden "select" "%s"]' "$(HTML "${ITEM}")" + [ -d "$_DATA/${ITEM%/*}/.index/" ] && w_tagging + printf ' + ] + ] +] ] +'; } | "$_EXEC/cgilite/html-sh.sed" diff --git a/widgets.sh b/widgets.sh new file mode 100644 index 0000000..7c945d6 --- /dev/null +++ b/widgets.sh @@ -0,0 +1,283 @@ +#!/bin/sh + +[ -n "$include_widgets" ] && return 0 +include_widgets="$0" + +. "$_EXEC/cgilite/storage.sh" + +w_refuri="$(URL "$PATH_INFO")?$(HTML "$QUERY_STRING")" + +w_str_s="$(STRING "$SEARCH")" +w_str_f="$(STRING "$FILTER")" + +w_ascii=' + s-0-0-g; s-1-1-g; s-2-2-g; s-3-3-g; s-4-4-g; s-5-5-g; + s-6-6-g; s-7-7-g; s-8-8-g; s-9-9-g; + s-A-A-g; s-B-B-g; s-C-C-g; s-D-D-g; s-E-E-g; s-F-F-g; + s-G-G-g; s-H-H-g; s-I-I-g; s-J-J-g; s-K-K-g; s-L-L-g; + s-M-M-g; s-N-N-g; s-O-O-g; s-P-P-g; s-Q-Q-g; s-R-R-g; + s-S-S-g; s-T-T-g; s-U-U-g; s-V-V-g; s-W-W-g; s-X-X-g; + s-Y-Y-g; s-Z-Z-g; + s-a-a-g; s-b-b-g; s-c-c-g; s-d-d-g; s-e-e-g; s-f-f-g; + s-g-g-g; s-h-h-g; s-i-i-g; s-j-j-g; s-k-k-g; s-l-l-g; + s-m-m-g; s-n-n-g; s-o-o-g; s-p-p-g; s-q-q-g; s-r-r-g; + s-s-s-g; s-t-t-g; s-u-u-g; s-v-v-g; s-w-w-g; s-x-x-g; + s-y-y-g; s-z-z-g; + s---\--g; s-/-/-g; s-/-/-g; s-:-:-g; s-=-=-g; s-@-@-g; + s-_-_-g; s-~-~-g; s- - -g; s-^-^-g; s-|-|-g; +' + +w_tags="$_DATA/.index/tags.cache"; w_tagcategories="$_DATA/.index/tagcategories.cache" +if [ ! -f "$w_tags" -o ! -f "$w_tagcategories" ] \ + || [ "$(find "$_DATA/" -path '*/.index/meta' -newer "$w_tags")" ] + then + w_tags="$( { local cn=1 + find "$_DATA/" -path '*/.index/meta' -print0 \ + | xargs -r0 sed -r ' + s;^.*\t.*\t.*\ttags=(.*)\tcomment=.*\t.*\r$;\1;; + s;,;\n;g;'"$UNSTRING" \ + | { sort; printf '\n'; } \ + | while read -r tag; do + [ "$tag" = "$otag" ] \ + && cn=$((cn + 1)) \ + || { + printf "%i %s\n" "$cn" "$otag" + cn=1 + } + otag="$tag" + done + } |sort -rn |cut -f2- |HTML |sed "$w_ascii s- -\n-g; s;\n\n;\n;g;" |tee "$w_tags" )" + w_tagcategories="$(printf %s "$w_tags" \ + | sed -rn '/:/s;^-?([^:]+):.*$;\1;p' |sort -u \ + | tee "$w_tagcategories" )" +else + w_tags="$(cat "$w_tags")" + w_tagcategories="$(cat "$w_tagcategories")" +fi + + +[ "$ORDER" = Name ] && w_coname=checked +[ "$ORDER" = Date ] && w_codate=checked +[ "$ORDER" = Length ] && w_colength=checked +[ "$ORDER" = Group ] && w_cogroup=checked + +w_bmname= +w_bmname(){ + [ "$w_bmname" ] || w_bmname="$( + bm="$_DATA/.index/bookmarks" + name="$(grep -m1 -aF " search=${w_str_s} filter=${w_str_f}${CR}" "$bm" 2>&-)" + + if [ "$name" ]; then + printf '%s' "$name" |cut -f1 |UNSTRING |HTML + else + printf '%s\t%s' "$SEARCH" "$FILTER" \ + | sed -r '/^\t$/{ s;\t;All;; q;} + /.*\t$/{ s;\t$;;; q;} + /^\t.*/{ s;^\t;;; + :x; s;(^|[~^|])([^|^~:]+):;\1;; tx; + s;\^; and ;g; s;\|;,;g; s;~;not ;g; q;}' \ + | HTML + fi + )" + printf '%s' "$w_bmname" +} + +w_bookmarks(){ + local bm="$_DATA/.index/bookmarks" name='' search='' filter='' + [ ! -d "${bm%/*}" ] && return 0 + [ ! -f "$bm" ] && touch "$bm" + + grep -qaF " search=$w_str_s filter=${w_str_f}${CR}" "$bm" && name=Update || name=Add + + printf '[form #bookmarks action=?a=bookmark method=POST + [a href="#" x] + [hidden "ref" "%s"] + [hidden "search" "%s"][hidden "filter" "%s"] + [label Name for current page:] + [input name="name" value="%s" placeholder="Name" ] + [button type="submit" %s]' \ + "$w_refuri" \ + "$(HTML "$SEARCH")" "$(HTML "$FILTER")" \ + "$(w_bmname)" "${name}" + [ "$name" ] && printf ' [submit "delete" "delete" Delete]' + + sort "$bm" |while read -r name search filter; do + search="${search#search=}" filter="${filter#filter=}" filter="${filter%${CR}}" + [ "$search" = "${w_str_s}" -a "$filter" = "${w_str_f}" ] && continue + + name="$(UNSTRING "$name")"; + search="$(UNSTRING "${search}" |URL)"; + filter="$(UNSTRING "${filter}" |URL)"; + printf '[label .link %s] + [a .link target=blank href="/?o=Name&s=%s&f=%s" by Name] + [a .link target=blank href="/?o=Date&s=%s&f=%s" by Date] + [a .link target=blank href="/?o=Length&s=%s&f=%s" by Length] + [a .link target=blank href="/?o=Group&s=%s&f=%s" by Group] + [br]' \ + "$(HTML "$name" |sed 's;,\;;&[wbr];g;')" \ + "$search" "$filter" \ + "$search" "$filter" \ + "$search" "$filter" \ + "$search" "$filter" + done + printf ']' +} + +w_search(){ + printf ' + [form #search method=GET action=./? + [select name=o size=1 + [option disabled=disabled Order By] + [option value=Name %s Name] + [option value=Date %s Date] + [option value=Length %s Length] + [option value=Group %s Group] + ] + [input name=s placeholder=Search value="%s"] + ] + ' \ + "$w_coname" "$w_codate" "$w_colength" "$w_cogroup" \ + "$(HTML "$SEARCH")" +} + +w_prefs(){ + local tm tf td + + tm=''; [ "$(COOKIE mode)" = index ] && tm=' ' + tf=''; [ "$(COOKIE fakemp4)" = yes ] && tf=checked + td=''; [ "$(COOKIE downscale)" = yes ] && td=checked + + printf ' + [form #prefs method="POST" action="?a=setprefs" + [a href="#" x] + [hidden "ref" "%s"] + [label for=prefs_ps Pagesize] + [input #prefs_ps type=number name=pagesize value="%s"][br] + [radio "mode" "browse" %s #prefs_modebrowse] [label for=prefs_modebrowse Browse Folders][br] + [radio "mode" "index" %s #prefs_modeindex ] [label for=prefs_modeindex View Full Index][br] + [checkbox "fakemp4" "yes" %s #prefs_fmp4] [label for=prefs_fmp4 Fake .MP4 file type][br] + [checkbox "downscale" "yes" %s #prefs_downscale] [label for=prefs_downscale Prefer downscale to 480p][br] + [submit "index" "update" Force Index Update][br] + [submit "store" "store" Set Cookie] + ] + ' \ + "$w_refuri" "$LISTSIZE" \ + "${tm:-checked}" "${tm:+checked}" "$tf" "$td" +} + +w_index(){ + printf ' + [form #index method="POST" action="?a=spawnindex" + [hidden "ref" "%s"] + [label Set up for Index view: ] + [checkbox "recursive" "yes" #spawn_recursive] [label for=spawn_recursive Include subdirectories] + [submit "spawn" "spawn" Set up] + ] + ' "$w_refuri" + return 0 +} + +w_advsearch(){ + local n lbid tag category filter f t d + filter="$(HTML "${FILTER}^" |sed "$w_ascii")" + + printf '[form #advsearch action=?a=advsearch method=POST + [a href="#" Hide] + [p .help Select multiple tags from each category by holding down the [strong Ctrl] key on your keyboard.[br] + Refine the search further by setting additional search tags using the [strong "+and"] button.]' + + for n in 1 2 3 4 5 6 7 8 9 10; do + f="${filter%%^*}"; filter="${filter#*^}" + + t=''; [ "$f" -a ! "${f%%~*}" ] && t=" " + + lbid="cat_${n}_(none)" + printf '[input .and type=checkbox name=and id="and_%i" %s][label for="and_%i" +and + ][fieldset .select + [radio "pol_%i" "pos" .pol %s #pol_pos_%i"][label for=pol_pos_%i Any] + [radio "pol_%i" "neg" .pol %s #pol_neg_%i"][label for=pol_neg_%i None] + [label .head Category:]' \ + $n "${f:+checked}" $n \ + $n "${t:-checked}" $n $n \ + $n "${t:+checked}" $n $n + + f="|${f#~}|" + printf '*\n%s\n' "$w_tagcategories" \ + | while read -r category; do + lbid="cat_${n}_${category}" + + t='' + [ "$category" = '*' -a "${f%%|${category}:*}" ] && t=checked + [ "$category" != '*' -a ! "${f%%|${category}:*}" ] && t=checked + [ "$category" != '*' -a ! "${f%%|-${category}:*}" ] && t=checked + + printf '[radio "cat_%i" "%s" .cat %s id="%s"][label for="%s" %s] + [select name=tag_%s size=10 multiple' \ + $n "$category" "$t" "$lbid" "$lbid" "$category" $n + + printf '%s\n' "$w_tags" \ + | { [ "$category" = '*' ] && grep -avF ':' || grep -awF "${category}"; } \ + | { for n in 1 2 3 4 5 6 7 8 9 0; do + read -r line && printf '%s\n' "$line" || break; + done; # pass 10 lines through without modification + sort; # and sort remaining lines + } | while read -r tag; do + [ "$tag" ] || continue + t=''; [ ! "${f%%*|${tag}|*}" ] && t=checked + d="${tag#-}"; d="${d#*:}" + printf '[option %s value="%s"\n%s]' "$t" "$tag" "$d" + done + printf '\n]' + done + printf ']' + done + + printf '[fieldset .submit [select name=order + [option disabled=disabled Order By] + [option value=Name %s Name] + [option value=Date %s Date] + [option value=Length %s Length] + [option value=Group %s Group] + ][button type=submit Apply Filter]] + ]' \ + "$w_coname" "$w_codate" \ + "$w_colength" "$w_cogroup" +} + +w_delete(){ + printf '[a href="#multitag" Add Tags / Remove Tags] + [div #multitag [input type="hidden" name="ref" value="%s"] + [a href="#" Hide][br] + [fieldset [legend New:] + [submit "op" "filedelete" Delete Files] + ]]' "$w_refuri" +} + +w_tagging(){ + local tag category d + printf '[a href="#multitag" Add Tags / Remove Tags] + [div #multitag [input type="hidden" name="ref" value="%s"]' "$w_refuri" + printf '[a href="#" Hide][br]' + + printf 'Tags\n%s\n' "$w_tagcategories" \ + | while read -r category; do + [ "$category" ] || continue + printf '[fieldset [legend %s:][select name=tag size=4 multiple\n' "$category" + printf %s "$w_tags" \ + | { [ "$category" = 'Tags' ] && grep -avF ':' || grep -awF "${category}"; } \ + | { for n in 1 2 3 4 5 6 7 8 9 0; do + read -r line && printf '%s\n' "$line" || break + done; + sort; + } | while read -r tag; do + [ "$tag" ] || continue + d="${tag#-}"; d="${d#*:}" + printf '[option value="%s"\n%s]' "$tag" "$d" + done + printf ']]' + done + + printf '[fieldset [legend New:][textarea name=newtag\n] + [submit "op" "del" Remove Tags][submit "op" "add" Add Tags] + ]]' +}