diff --git a/scripts/README b/scripts/README index 5ee413e8..186a1736 100644 --- a/scripts/README +++ b/scripts/README @@ -1,5 +1,18 @@ +sync-flash.sh + Syncs a camera flash card to an archive folder. + + Dependencies: + - process-archive.sh + - compress-archive.sh (optional) + - bash + + For more info see: + sync-flash.sh --help + + process-archive.sh - Processes and prepares the archive folder for viewing. + Processes and prepares the archive folder for viewing via + ImageGrid.Viewer. Dependencies: - exiftool @@ -9,17 +22,19 @@ process-archive.sh XXX add self dependency check... - -process-archive.bat -- windows version of process-archive.sh (partial) + For more info see: + process-archive.sh --help -sync-flash.sh - Syncs a camera flash card to an archive folder. +compress-archive.sh + compresses raw files. + + By default this will NTFS compress sony ARW files, but other + compression methods and raw formats are supported... + + For more info see: + compress-archive.sh --help - Dependencies: - - process-archive.sh - - bash - update-exif.sh Updates processed preview metadata from appropriate .PSD files. @@ -27,6 +42,12 @@ update-exif.sh Dependencies: - exiv2 (to be deprecated) + For more info see: + update-exif.sh --help + + + +--- flatten.sh Flatten flickr/instagram favorite folder structure created by @@ -34,11 +55,13 @@ flatten.sh .// -> ./ALL/ - + + vips-tn.sh cleannwcache.bat extract-metadata.sh - +process-archive.bat -- windows version of process-archive.sh (partial) diff --git a/scripts/compress-archive.sh b/scripts/compress-archive.sh index 8ac40a49..98e8c7f2 100755 --- a/scripts/compress-archive.sh +++ b/scripts/compress-archive.sh @@ -29,11 +29,11 @@ printhelp(){ echo "Arguments:" echo " -h --help - print this help and exit." echo - echo " -bz -bzip2 - use bzip2 to compress (default)." - echo " -gz -gzip - use gzip to compress." - echo " -c -compact - use ntfs compression." + echo " -bz -bzip2 - use bzip2 to compress`[[ $ARCH == $ARCH_BZIP2 ]] && echo " (default)" || echo ""`." + echo " -gz -gzip - use gzip to compress`[[ $ARCH == $ARCH_GZIP ]] && echo " (default)" || echo ""`." + echo " -c -compact - use ntfs compression`[[ $ARCH == $ARCH_NTFS ]] && echo " (default)" || echo ""`." echo - echo " -ext EXT - set file extension to compress (default: ARW)" + echo " -ext EXT - set file extension to compress (default: ${EXT})" echo " NOTE: only one -ext is supported now". echo } diff --git a/scripts/sync-flash.sh b/scripts/sync-flash.sh index 03eed2fe..c37d0ad1 100755 --- a/scripts/sync-flash.sh +++ b/scripts/sync-flash.sh @@ -5,7 +5,8 @@ COUNT=1 TITLE="" RSYNC=rsync -RSYNCFLAGS="-arptgoA --info=progress2,flist --human-readable" +#RSYNCFLAGS="-arptgoA --info=progress2,flist --human-readable" +RSYNCFLAGS="-arpt --info=progress2,flist --human-readable" CP=cp CPFLAGS=-Rpfv @@ -14,6 +15,10 @@ CPFLAGS=-Rpfv COPY=$RSYNC COPYFLAGS=$RSYNCFLAGS +COMPRESSOR=./compress-archive.sh +COMPRESS=1 + + # base mount dir... # systems with /mnt if [ -d /mnt ] ; then @@ -40,10 +45,20 @@ while true ; do echo " single shoot." echo " -l|-last last flash card in set, run" echo " process-archive.sh after copying." - echo " -b|-base the base dir to look for drives in" + echo " -b|-base BASE the base dir to look for drives in" echo " default: $BASE" echo " --rsync use rsync (default)" echo " --cp use cp" + if ! [ -z $COMPRESSOR ] ; then + echo " --compress toggle archive compression" + echo " default: `[[ $COMPRESS ]] && echo "on" || echo "off"`" + fi + # notes... + echo + if ! [ -z $COMPRESSOR ] ; then + echo "NOTE: the index is fully usable during the compression stage" + fi + echo "NOTE: cp under Cygwin may messup permissions, use rsync." echo exit ;; @@ -61,17 +76,24 @@ while true ; do shift ;; -b|-base|--base) - BASE=1 - shift + BASE=$2 + shift 2 ;; -cp|--cp) COPY=cp COPYFLAGS=-Rpfv + shift break ;; -rsync|--rsync) COPY=$RSYNC COPYFLAGS=$RSYNCFLAGS + shift + break + ;; + -compress|--compress) + COMPRESS=`[[ $COMPRESS ]] && echo "" || echo 1` + shift break ;; *) @@ -99,7 +121,12 @@ while true ; do echo "Enter) copy drive ${DRIVE}" fi echo "2) build." - echo "3) quit." + if ! [ -z $COMPRESSOR ] ; then + echo "3) compresion is `[[ $COMPRESS ]] && echo "on" || echo "off"`" + echo "4) quit." + else + echo "3) quit." + fi read -p ": " RES case $RES in @@ -126,8 +153,15 @@ while true ; do LAST=1 break ;; - 3) + if ! [ -z $COMPRESSOR ] ; then + COMPRESS=`[[ ! $COMPRESS ]] && echo 1 || echo ""` + else + exit + fi + continue + ;; + 4) exit ;; @@ -138,6 +172,17 @@ while true ; do esac fi + # sanity check... + if ! [ -e "${BASE}/${DRIVE}" ] ; then + echo + echo "ERR: ${BASE}/${DRIVE}: does not exist, nothing to copy." + echo + if [[ $INTERACTIVE || ! $DRIVE ]] ; then + continue + fi + exit + fi + # XXX do a real three digit count... # single flash card... SCOUNT=`printf "%03d" $COUNT` @@ -166,7 +211,7 @@ while true ; do mkdir -vp "$DIR" - echo "Copying files from ${BASE}/${DRIVE}..." + echo "Copying files from ${BASE}/${DRIVE} (`du -hs "${BASE}/${DRIVE}" | cut -f 1`)..." $COPY $COPYFLAGS ${BASE}/${DRIVE}/* "$DIR" echo "Copying files: done." @@ -187,4 +232,13 @@ if [[ ! $MULTI || $LAST ]] ; then echo "Building archive: done." fi +if [[ $COMPRESS ]] ; then + echo "Compressing archive..." + ${COMPRESSOR} "$BASE_DIR" + echo "Compressing archive: done." +fi + +echo "`basename "$0"`: done." + +# vim:set nowrap : diff --git a/scripts/update-exif.sh b/scripts/update-exif.sh index 9a34e68a..8f15d8d6 100755 --- a/scripts/update-exif.sh +++ b/scripts/update-exif.sh @@ -1,7 +1,32 @@ #!/bin/bash + + + + DIR=`pwd` +printhelp(){ + echo "Usage: `basename $0` [ARGUMENTS] [PATH]" + echo + echo "Arguments:" + echo " -h --help - print this help and exit." + echo +} + +while true ; do + case $1 in + -h|--help) + printhelp + exit + ;; + *) + break + ;; + esac +done + + exifup(){ PREVIEW_DIR=$1 diff --git a/ui (gen4)/archive/media/img/my/work/compress-archive.sh b/ui (gen4)/archive/media/img/my/work/compress-archive.sh new file mode 100644 index 00000000..98e8c7f2 --- /dev/null +++ b/ui (gen4)/archive/media/img/my/work/compress-archive.sh @@ -0,0 +1,92 @@ +#!/bin/bash + +# TODO make this runnable from anywhere... +# - prepend paths with './' only if local/relative + +BASE_PATH=. + + +ARCH_BZIP2='bzip2 -v {}' +ARCH_GZIP='gzip -v {}' +# XXX should we cygpath -w all the inputs??? +ARCH_NTFS='compact /c /exe:lzx {}' + + +# default... +ARCH=$ARCH_NTFS + + +EXT=ARW + +# HACK: this is here to avoid using windows find... +PATH=/bin:$PATH + + + +printhelp(){ + echo "Usage: `basename $0` [ARGUMENTS] [PATH]" + echo + echo "Arguments:" + echo " -h --help - print this help and exit." + echo + echo " -bz -bzip2 - use bzip2 to compress`[[ $ARCH == $ARCH_BZIP2 ]] && echo " (default)" || echo ""`." + echo " -gz -gzip - use gzip to compress`[[ $ARCH == $ARCH_GZIP ]] && echo " (default)" || echo ""`." + echo " -c -compact - use ntfs compression`[[ $ARCH == $ARCH_NTFS ]] && echo " (default)" || echo ""`." + echo + echo " -ext EXT - set file extension to compress (default: ${EXT})" + echo " NOTE: only one -ext is supported now". + echo +} + +# process args... +while true ; do + case $1 in + -h|--help) + printhelp + exit + ;; + + # archivers... + -bz|--bzip2) + ARCH=$ARCH_BZIP2 + shift + ;; + -gz|--gzip) + ARCH=$ARCH_GZIP + shift + ;; + -c|--compact) + ARCH=$ARCH_NTFS + shift + ;; + + # extension to compress... + --ext) + EXT=$2 + shift + shift + ;; + + *) + break + ;; + esac +done + +# get path... +if [ "$1" ] ; then + BASE_PATH=$1 +fi + + + +# do the work... +find "$BASE_PATH" -name \*.${EXT} -exec ${ARCH} \; + + + +echo done. + + + +# vim:set nowrap nospell : diff --git a/ui (gen4)/archive/media/img/my/work/sync-flash.sh b/ui (gen4)/archive/media/img/my/work/sync-flash.sh index 03eed2fe..c37d0ad1 100755 --- a/ui (gen4)/archive/media/img/my/work/sync-flash.sh +++ b/ui (gen4)/archive/media/img/my/work/sync-flash.sh @@ -5,7 +5,8 @@ COUNT=1 TITLE="" RSYNC=rsync -RSYNCFLAGS="-arptgoA --info=progress2,flist --human-readable" +#RSYNCFLAGS="-arptgoA --info=progress2,flist --human-readable" +RSYNCFLAGS="-arpt --info=progress2,flist --human-readable" CP=cp CPFLAGS=-Rpfv @@ -14,6 +15,10 @@ CPFLAGS=-Rpfv COPY=$RSYNC COPYFLAGS=$RSYNCFLAGS +COMPRESSOR=./compress-archive.sh +COMPRESS=1 + + # base mount dir... # systems with /mnt if [ -d /mnt ] ; then @@ -40,10 +45,20 @@ while true ; do echo " single shoot." echo " -l|-last last flash card in set, run" echo " process-archive.sh after copying." - echo " -b|-base the base dir to look for drives in" + echo " -b|-base BASE the base dir to look for drives in" echo " default: $BASE" echo " --rsync use rsync (default)" echo " --cp use cp" + if ! [ -z $COMPRESSOR ] ; then + echo " --compress toggle archive compression" + echo " default: `[[ $COMPRESS ]] && echo "on" || echo "off"`" + fi + # notes... + echo + if ! [ -z $COMPRESSOR ] ; then + echo "NOTE: the index is fully usable during the compression stage" + fi + echo "NOTE: cp under Cygwin may messup permissions, use rsync." echo exit ;; @@ -61,17 +76,24 @@ while true ; do shift ;; -b|-base|--base) - BASE=1 - shift + BASE=$2 + shift 2 ;; -cp|--cp) COPY=cp COPYFLAGS=-Rpfv + shift break ;; -rsync|--rsync) COPY=$RSYNC COPYFLAGS=$RSYNCFLAGS + shift + break + ;; + -compress|--compress) + COMPRESS=`[[ $COMPRESS ]] && echo "" || echo 1` + shift break ;; *) @@ -99,7 +121,12 @@ while true ; do echo "Enter) copy drive ${DRIVE}" fi echo "2) build." - echo "3) quit." + if ! [ -z $COMPRESSOR ] ; then + echo "3) compresion is `[[ $COMPRESS ]] && echo "on" || echo "off"`" + echo "4) quit." + else + echo "3) quit." + fi read -p ": " RES case $RES in @@ -126,8 +153,15 @@ while true ; do LAST=1 break ;; - 3) + if ! [ -z $COMPRESSOR ] ; then + COMPRESS=`[[ ! $COMPRESS ]] && echo 1 || echo ""` + else + exit + fi + continue + ;; + 4) exit ;; @@ -138,6 +172,17 @@ while true ; do esac fi + # sanity check... + if ! [ -e "${BASE}/${DRIVE}" ] ; then + echo + echo "ERR: ${BASE}/${DRIVE}: does not exist, nothing to copy." + echo + if [[ $INTERACTIVE || ! $DRIVE ]] ; then + continue + fi + exit + fi + # XXX do a real three digit count... # single flash card... SCOUNT=`printf "%03d" $COUNT` @@ -166,7 +211,7 @@ while true ; do mkdir -vp "$DIR" - echo "Copying files from ${BASE}/${DRIVE}..." + echo "Copying files from ${BASE}/${DRIVE} (`du -hs "${BASE}/${DRIVE}" | cut -f 1`)..." $COPY $COPYFLAGS ${BASE}/${DRIVE}/* "$DIR" echo "Copying files: done." @@ -187,4 +232,13 @@ if [[ ! $MULTI || $LAST ]] ; then echo "Building archive: done." fi +if [[ $COMPRESS ]] ; then + echo "Compressing archive..." + ${COMPRESSOR} "$BASE_DIR" + echo "Compressing archive: done." +fi + +echo "`basename "$0"`: done." + +# vim:set nowrap : diff --git a/ui (gen4)/archive/media/img/my/work/update-exif.sh b/ui (gen4)/archive/media/img/my/work/update-exif.sh index 9a34e68a..8f15d8d6 100755 --- a/ui (gen4)/archive/media/img/my/work/update-exif.sh +++ b/ui (gen4)/archive/media/img/my/work/update-exif.sh @@ -1,7 +1,32 @@ #!/bin/bash + + + + DIR=`pwd` +printhelp(){ + echo "Usage: `basename $0` [ARGUMENTS] [PATH]" + echo + echo "Arguments:" + echo " -h --help - print this help and exit." + echo +} + +while true ; do + case $1 in + -h|--help) + printhelp + exit + ;; + *) + break + ;; + esac +done + + exifup(){ PREVIEW_DIR=$1 diff --git a/ui (gen4)/lib/widget/browse2.js b/ui (gen4)/lib/widget/browse2.js index ffb8a263..21261dca 100755 --- a/ui (gen4)/lib/widget/browse2.js +++ b/ui (gen4)/lib/widget/browse2.js @@ -524,17 +524,52 @@ var BaseItemPrototype = { : undefined }, - // XXX BUG: this does not work for nested header elements... + // NOTE: these should not clash with user-supplied handlers ('update' does not)... + // + // XXX BUG: calling this on a nested/inlined browser will mess things up... + // ...the issue seems to be with root options not being available + // for partial render in a nested context... + // ...one way to fix this would be to make the options inheritance + // protocol more strict: + // - if no options given use defaults (i.e. this.options) + // - if options given use as-is + // - defaults are taken from this.options if not present + // // to reproduce: - // dialog - // .get({children: true}) - // .update() - // ...for some reason the affected element is removed from dom... + // dialog.disable('2') + // or: + // dialog.disable('B/C/D/a') + // + // ...in both cases the whole nested browser disappears... + // + // but this works OK: + // dialog.disable('nested/2') + // + // This might also be a side effect of the .dom / .elem set of + // issues... + // + // This issue seems to go away after expanding/collapsing the + // nested item several times, the buttons are gone but the + // subtrees stop vanishing on update -- could this be related + // to .dom/.elem again??? update: function(options){ this.parent && this.parent.render(this, options) - return this - }, + return this }, + /* XXX this is disabled to avoid user confusion when a user item + // event handler would not call/replicate the top-level (this) + // functionality and this break code relying on it... + // ...for this to work well need to either: + // - separate item options form the item object, + // - force the user to follow strict protocols, + // - use a proxy when accessing items (reverse of #1) + // XXX this also applies to .update(..) above... + focus: function(){ + arguments.length == 0 + && this.parent + && this.parent.focus(this) + return this }, + //*/ __init__(...state){ @@ -785,6 +820,9 @@ object.Constructor('BaseRenderer', { // placeholders... root: null, + isRendered: function(){ + throw new Error('.isRendered(..): Not implemented.') }, + // component renderers... elem: function(item, index, path, options){ throw new Error('.elem(..): Not implemented.') }, @@ -810,6 +848,9 @@ module.TextRenderer = object.Constructor('TextRenderer', { __proto__: BaseRenderer.prototype, + // always render... + isRendered: function(){ return false }, + elem: function(item, index, path, options){ return path .slice(0, -1) @@ -840,6 +881,9 @@ module.PathRenderer = object.Constructor('PathRenderer', { __proto__: TextRenderer.prototype, + // always render... + isRendered: function(){ return false }, + elem: function(item, index, path, options){ return path.join('/') }, inline: function(item, lst, index, path, options){ @@ -2654,10 +2698,14 @@ var BaseBrowserPrototype = { return r && typeof(e) != typeof({}) }, true))){ // reverse index... index = this - .reduce(function(res, e, i, p){ - res.set(e, [i, p]) - return res - }, new Map(), {iterateAll: true}) + .reduce( + function(res, e, i, p){ + res.set(e, [i, p]) + return res }, + new Map(), + Object.assign( + Object.flatCopy(options || {}), + {iterateAll: true})) var res var Stop = new Error('Stop iteration') try { @@ -2676,7 +2724,10 @@ var BaseBrowserPrototype = { throw Stop }) : pattern ] // search... - : that.search(pattern, ...args.slice(1)) }) + : !(pattern instanceof BaseItem) ? + that.search(pattern, ...args.slice(1)) + // not found... + : [] }) .flat() .unique() } catch(e){ @@ -2946,6 +2997,13 @@ var BaseBrowserPrototype = { __renderer__: TextRenderer, + isRendered: function(renderer){ + var render = renderer || this.__renderer__ + render = render.root == null ? + new render(this, this.options) + : render + return render.isRendered() }, + // // Render browser... // .render([options]) @@ -3594,19 +3652,39 @@ var BaseBrowserPrototype = { // XXX should we force calling update if options are given??? // ...and should full get passed if at least one call in sequence // got a full=true??? - __update_full: undefined, + // XXX supported mdoes: + // 'full' | true - make - render - post-render + // 'normal' - render - post-render + // 'partial' - post-render + __update_mode: undefined, __update_args: undefined, __update_timeout: undefined, __update_max_timeout: undefined, update: makeEventMethod('update', - function(evt, full, options){ - options = - (full && full !== true && full !== false) ? - full - : options - full = full === options ? - false - : full + function(evt, mode, options){ + var modes = [ + 'full', + 'normal', + 'partial', + ] + + options = mode instanceof Object ? + mode + : options + mode = mode === options ? + 'normal' + : mode === true ? + 'full' + : mode + // sanity check... + if(!modes.includes(mode)){ + throw new Error(`.update(..): unsupported mode: ${mode}`) } + var m = this.__update_mode + // if the queued mode is deeper than the requested, ignore the requested... + if(m != null && modes.indexOf(mode) > modes.indexOf(m)){ + return this } + + // queue update... // NOTE: we can't simply use _update(..) closure for this as // it can be called out of two contexts (timeout and // max_timeout), one (timeout) is renewed on each call @@ -3616,14 +3694,14 @@ var BaseBrowserPrototype = { // its setTimeout(..)... // storing the arguments in .__update_args would remove // this inconsistency... + this.__update_mode = mode var args = this.__update_args = [ - [evt, full, + [evt, mode, ...(options ? [options] : [])], options] - this.__update_full = (full && args) - || this.__update_full + var timeout = (options || {}).updateTimeout || this.options.updateTimeout var max_timeout = (options || {}).updateMaxDelay @@ -3637,20 +3715,22 @@ var BaseBrowserPrototype = { delete this.__update_timeout }.bind(this) var _update = function(){ _clear_timers() - var full = !!this.__update_full - var [args, opts] = this.__update_full - || this.__update_args + var mode = this.__update_mode + var [args, opts] = this.__update_args - delete this.__update_full + delete this.__update_mode delete this.__update_args - full + // make... + modes.indexOf(mode) <= modes.indexOf('full') && this.make(opts) - var context = {} - this - // XXX this needs access to render context.... - .preRender(opts, (opts || {}).renderer, context) - .render(opts, (opts || {}).renderer, context) + // render... + ;(!this.isRendered((opts || {}).renderer) + || modes.indexOf(mode) <= modes.indexOf('normal')) + && this + .preRender(opts, (opts || {}).renderer) + .render(opts, (opts || {}).renderer) + // update... this.trigger(...args) }.bind(this) var _update_n_delay = function(){ // call... @@ -3771,6 +3851,7 @@ object.Constructor('BaseBrowser', //--------------------------------------------------------------------- +// HTML-specific UI... var KEYBOARD_CONFIG = module.KEYBOARD_CONFIG = { @@ -4034,17 +4115,17 @@ var updateElemClass = function(action, cls, handler){ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// Renderer... +// HTML Renderer... -// XXX needs testing... -// - partial rendering... -// - local re-rendering... // XXX HACK: see .nest(..) var HTMLRenderer = module.HTMLRenderer = object.Constructor('HTMLRenderer', { __proto__: BaseRenderer.prototype, + isRendered: function(){ + return !!this.root.dom }, + // secondary renderers... // // base dialog... @@ -4654,7 +4735,6 @@ var HTMLBrowserPrototype = { __item__: HTMLItem, __renderer__: HTMLRenderer, - options: { __proto__: BaseBrowser.prototype.options, @@ -5045,29 +5125,28 @@ var HTMLBrowserPrototype = { __copy: function(text){ navigator.clipboard.writeText(text || this.path) }, - // XXX need a better name... - _updateDOMItems: function(){ + + // Events extensions... + // + // XXX should tweaking DOM be done here or in the renderer??? + __update__: function(){ var c = 0 this.forEach(function(e){ // shortcut number hint... if(c < 10 && !e.disabled && !e.hidden){ var a = e.attrs = e.attrs || {} - c = a['shortcut-number'] = (++c) % 10 e.elem - && e.elem.setAttribute('shortcut-number', c) - + && e.elem.setAttribute('shortcut-number', + a['shortcut-number'] = (c+1) % 10) // cleanup... } else { delete (e.attrs || {})['shortcut-number'] e.elem && e.elem.removeAttribute('shortcut-number') } + c++ }) }, - - // Events extensions... - // // NOTE: this will also kill any user-set keys for disabled/hidden items... - // // XXX also handle global button keys... __preRender__: function(evt, options, renderer, context){ var that = this @@ -5079,18 +5158,6 @@ var HTMLBrowserPrototype = { var i = 0 this.map(function(e){ - // shortcut number hint... - // NOTE: these are just hints, the actual keys are handled - // in .keybindings... - // XXX move this to the renderer... - if(i < 10 && !e.disabled && !e.hidden){ - var attrs = e.attrs = e.attrs || {} - attrs['shortcut-number'] = (++i) % 10 - // cleanup... - } else { - delete (e.attrs || {})['shortcut-number'] - } - // handle item keys... if(!e.disabled && !e.hidden){ ;((e.value instanceof Array ? @@ -5140,7 +5207,7 @@ var HTMLBrowserPrototype = { block: 'nearest', }) }) - // set focus... + // XXX do we need this??? .focus() }, __blur__: function(evt, elem){ var that = this @@ -5157,14 +5224,22 @@ var HTMLBrowserPrototype = { // element up to reveal the expanded subtree... // ...would also be logical to "show" the expanded tree but // keeping the focused elem in view... - __expand__: function(evt, elem){ elem.update() }, - __collapse__: function(evt, elem){ elem.update() }, + __expand__: function(evt, elem){ + elem.update() + this.update('partial') }, + __collapse__: function(evt, elem){ + elem.update() + this.update('partial') }, __select__: updateElemClass('add', 'selected'), __deselect__: updateElemClass('remove', 'selected'), __disable__: updateElemClass('add', 'disabled', - function(evt, elem){ elem.update() }), + function(evt, elem){ + elem.update() + this.update('partial') }), __enable__: updateElemClass('remove', 'disabled', - function(evt, elem){ elem.update() }), + function(evt, elem){ + elem.update() + this.update('partial') }), __hide__: updateElemClass('add', 'hidden'), __show__: updateElemClass('remove', 'hidden'),