From 6d1f384b223773a61f71581a2fbb343257d94efa Mon Sep 17 00:00:00 2001 From: luxagraf Date: Sat, 19 Oct 2024 07:21:54 -0500 Subject: updated photo scripts to use photos instead of pictures --- annex-audio-add-exif.sh | 12 ++ download-images-fujix100v.py | 2 +- download-images-fujix70.py | 2 +- download-images.py | 2 +- jrnl | 262 +++++++++++++++++++++++++++++++++++++++++++ srchx | 124 ++++++++++++++++++++ srchx.pl | 124 -------------------- 7 files changed, 401 insertions(+), 127 deletions(-) create mode 100755 annex-audio-add-exif.sh create mode 100755 jrnl create mode 100755 srchx delete mode 100755 srchx.pl diff --git a/annex-audio-add-exif.sh b/annex-audio-add-exif.sh new file mode 100755 index 0000000..b792261 --- /dev/null +++ b/annex-audio-add-exif.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +shopt -s extglob +for f in !(*.xmp) ; do + echo $f + git annex metadata $f --set exif="$(exiftool -S \ + -filename \ + -filetypeextensions \ + -model \ + -datetimeoriginal \ + -duration $f)" +done + diff --git a/download-images-fujix100v.py b/download-images-fujix100v.py index 8c9df16..b204942 100755 --- a/download-images-fujix100v.py +++ b/download-images-fujix100v.py @@ -23,7 +23,7 @@ for (dirname, dirs, files) in os.walk(path): sorter.append([int(filename.split('DSCF')[1].split(".JPG")[0]), filename]) for f in sorted(sorter): - dest = '/home/lxf/pictures/inbox/' + dest = '/home/lxf/photos/inbox/' if not os.path.exists(dest): os.makedirs(dest) print("copying:", dirn+'/'+f[1], "--->", dest+f[1]) diff --git a/download-images-fujix70.py b/download-images-fujix70.py index c934abb..591268b 100755 --- a/download-images-fujix70.py +++ b/download-images-fujix70.py @@ -23,7 +23,7 @@ for (dirname, dirs, files) in os.walk(path): sorter.append([int(filename.split('DSCF')[1].split(".JPG")[0]), filename]) for f in sorted(sorter): - dest = '/home/lxf/pictures/inbox/' + dest = '/home/lxf/photos/inbox/' if not os.path.exists(dest): os.makedirs(dest) print("copying:", dirn+'/'+f[1], "--->", dest+f[1]) diff --git a/download-images.py b/download-images.py index 5fa6a98..7d6fa38 100755 --- a/download-images.py +++ b/download-images.py @@ -19,7 +19,7 @@ for (dirname, dirs, files) in os.walk(path): if int(filename.split('DSC')[1].split(".ARW")[0]) > int(lastfile.split('DSC')[1].split(".ARW")[0]): sorter.append([int(filename.split('DSC')[1].split(".ARW")[0]), filename]) for f in sorted(sorter): - dest = os.path.join(home_directory, 'pictures', 'inbox') + dest = os.path.join(home_directory, 'photos', 'inbox') if not os.path.exists(dest): os.makedirs(dest) print("copying:", dirn+'/'+f[1], "--->", dest+'/'+f[1]) diff --git a/jrnl b/jrnl new file mode 100755 index 0000000..ff8e9a9 --- /dev/null +++ b/jrnl @@ -0,0 +1,262 @@ +#!/bin/bash +# (c) 2019-2020 Vitaly Parnas +# See LICENSE for licensing information. + +#TAG_COLOR="00;36" +TIMEFORMAT="%Y-%m-%d %H:%M" +RECORD_START_PAT='[0-9]{4}-[0-9]{2}-[0-9]{2}\s[0-9]{2}:[0-9]{2}' + +# The 'tac' utility doesn't support perl-compatible regex +TAC_RECORD_START_PAT='[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]\s[0-9][0-9]:[0-9][0-9]' + +declare -A JOURNALS=( + ['default']="$HOME/journals/journal.txt" +) + +[ -f "$JRNL2_CFG" ] && CFG_FILE="$JRNL2_CFG" || CFG_FILE="$HOME/.jrnl2.rc" +[ -f "$CFG_FILE" ] && . "$CFG_FILE" + +[ -z $DEFAULT_JRNL ] && DEFAULT_JRNL='default' +JOURNAL="${JOURNALS[$DEFAULT_JRNL]}" +[ -z "$JOURNAL" ] && echo "Journal '$DEFAULT_JRNL' not found" && exit 1 +[ ! -f "$JOURNAL" ] && echo "Cannot open $JOURNAL" && exit 1 + +# Pass disjunctive arguments in regex (ie 'pat1|pat2' or '@(tag1|tag2)' +# Pass conjunctive arguments as separate search -s operations, which are passed as separate arguments here + # awk '/foo/ && /bar/' + # grep -P '(?=.*?word1)(?=.*?word2)(?=.*?word3)^.*$' +function jrnl_entry_search +{ + short_srch="$1"; shift + reverse="$1"; shift + max_entries="$1"; shift + tags_only="$1"; shift + case_sens="$1"; shift + + $short_srch && instr='P' || instr='p' + $case_sens && mod='' || mod='I' + # case-insensitive match + search_cmd='sed -nr "/^'$RECORD_START_PAT'/{x;/./$instr;d; \${x;/./$instr;d}}; \${x;G;/./$instr}; {H}"' + while [ -n "$1" ]; do + pattern="$1" + search_cmd=$(sed -r 's/(\$instr)/{\/'"$pattern"'\/\$mod\1}/g' <<< "$search_cmd") + shift + done + if $tags_only; then + cmd_suffix+=" | list_tags" + else + $reverse && + cmd_suffix+=" | tac -s '^$TAC_RECORD_START_PAT' --before --regex" + [ -n "$TAG_COLOR" ] && + cmd_suffix+=" | GREP_COLORS='mt=$TAG_COLOR' grep --color -e '' -e '@\w\+'" + fi + if [ $max_entries -gt 0 ]; then + # Output entries in --short (1-line) mode to establish the start entry for the search space + prev_instr=$instr + instr='P' + start_line=$(eval "$search_cmd $JOURNAL | tail -$max_entries | head -1") + instr=$prev_instr + [ -z "$start_line" ] && return + # Use line number as start search space to avoid unescaped char issues. If completely duplicate headers exist, will choose the last as start space. + line_num=$(grep -nF "$start_line" $JOURNAL | awk -F':' '{print $1}' | tail -1) + sed -n "$line_num,\$p" "$JOURNAL" | eval "$search_cmd $cmd_suffix" + else + eval "$search_cmd $JOURNAL $cmd_suffix" + fi +} + +function jrnl_entry_edit +{ + max_entries="$1"; shift + orig=$(jrnl_entry_search false false $max_entries false $case_sens "$@") + [ -z "$orig" ] && echo "Nothing found by that search" && return + tmpfile=$(mktemp --tmpdir jrnl-edit.XXXXXX) + trap 'rm "$tmpfile"' 0 1 15 + echo "$orig" > "$tmpfile" + sed -i -r 's/^'"$RECORD_START_PAT"'.*/== MODIFY\/DELETE RECORD BELOW. DO NOT DELETE THIS LINE. \0 ==\n\n\0/' $tmpfile + $EDITOR $tmpfile + echo "$orig" | diff -q -BZ -I '==.*==' - $tmpfile \ + >/dev/null && echo "No changes" && return + num_edited=0 + num_deleted=0 + num_intact=0 + headers=$(sed -nr "s/^==.*($RECORD_START_PAT.*) ==$/\1/p" $tmpfile) + while read -r header; do + # Escape a bunch of characters conflicting with sed + header=$(sed -r 's/([?*+-/(){}|^$\\]|\[|\])/\\\1/g' <<< "$header") + old_record=$(sed -rn "/^$header/,/^$RECORD_START_PAT/{//!p;/^$header/p}" <<< "$orig") + new_record=$(sed -rn "/^==.*$header/,/^==/{//!{/^$RECORD_START_PAT/,\$p}}" "$tmpfile") + #echo -e "old:\n$old_record" + #echo -e "new:\n$new_record" + if [ "$old_record" = "$new_record" ]; then + (( num_intact++ )) + continue + fi + if [ -n "$new_record" ]; then + tmpjrnl=$(mktemp --tmpdir jrnl-temp.XXXXXX) + ( # Combine the section above entry + entry + section below + sed -rn '/^'"$header"'/,$!p' $JOURNAL + echo -e "$new_record""\n" + sed -r "1,/^$header/d" $JOURNAL | + sed -rn "/^$RECORD_START_PAT"'/,$p' + ) > "$tmpjrnl" + mv "$tmpjrnl" "$JOURNAL" + (( num_edited++ )) + else # delete old record + sed -i -nr "/^$header/,/$RECORD_START_PAT/{//!d;/^$header/d};p" $JOURNAL + (( num_deleted++ )) + fi + done <<< "$headers" + [[ $num_edited -gt 0 ]] && echo "Edited $num_edited entries" + [[ $num_deleted -gt 0 ]] && echo "Deleted $num_deleted entries" + [[ $num_intact -gt 0 ]] && echo "Intact $num_intact entries" +} + +# Adds entry in preference of +# 1. stdin +# 2. argument +# 3. Edited in $EDITOR +function jrnl_entry_add +{ + # First, try stdin + entry=$( + while read -rt .01 stdin; do + echo "$stdin" + done + ) + if [[ -z "$entry" ]]; then + if [[ -n $1 ]]; then + entry="${@:1}" + else + tmpfile=$(mktemp --tmpdir jrnl-new.XXXXXX) + trap 'rm "$tmpfile"' 0 1 15 + $EDITOR $tmpfile + entry=$(cat $tmpfile) + #rm $tmpfile + fi + fi + if [[ -n "$entry" ]]; then + filter='1s/\.\s\+/.\n/' # Separate the first sentence from rest by a NL. + sed -rn "1{/^$RECORD_START_PAT/q0};q1" <<< "$entry" && + prefix="" || prefix="$(date "+$TIMEFORMAT") " + echo -e "$prefix""$(sed "$filter" <<< "$entry")\n" >> "$JOURNAL" + echo "1 new entry written" + else + echo "Journal unmodified" + fi +} + +# List all tags within provided journal file or otherwise stdin +function list_tags { + [[ -n $1 ]] && input="$1" || input="-" + grep -E -o '(^|\s)@[_[:alnum:]\-]+' "$input" | sed 's/^\s*//' | sort -fib | uniq -ci | sort -nr +} + +# +## The stdout isn't immediately visible with each selected + tag as the entire output is piped to echo +#alias jrnl_select_tags="jrnl --tags | awk '{print \$1}' | dmenu | xargs echo" + +function usage { + cat <, + -ls|--list: list all available journals + -n|--max-entries : limit query to the last n entries, + -s|--search : search for records containing . Can combine for a conjunctive search. + -S|--search-all : search all journals for records containing . Cannot combine with --edit. + -c|--case: case-sensitive search (insensitive by default) + -e|--edit: edit/delete the searched entries + -t|--tags: list of existing tags + -r|--short: view entry headings only + -R|--reverse: view entries in reverse +EOF +} + +function assert_args() { + cmd="$1"; shift + [[ $# -lt 1 || $1 =~ ^\- ]] && echo "$cmd needs an argument" && exit 128 +} + +short_srch=false +max_entries=0 +tags_only=false +case_sens=false +edit=false +search_all=false +reverse=false +search_args=() +options_passed=false + +while [[ -n $1 ]]; do + case $1 in + -j|--journal) + shift + assert_args "journal" "$@" + if [ -n "${JOURNALS[$1]}" ]; then + JOURNAL="${JOURNALS[$1]}" + else + echo "Journal $1 undefined." + exit 1 + fi + ;; + -t|--tags) + options_passed=true + tags_only=true;; + -c|--case) + options_passed=true + case_sens=true;; + -ls|--ls|--list) + for j in "${!JOURNALS[@]}"; do echo "$j"; done + exit 0;; + -r|--short) + options_passed=true + short_srch=true;; + -R|--reverse) + options_passed=true + reverse=true;; + -s|--search) + options_passed=true + shift + assert_args "search" "$@" + search_args+=("$1") + ;; + -S|--search-all) + options_passed=true + shift + assert_args "search-all" "$@" + search_args+=("$1") + search_all=true + ;; + -e|--edit) + options_passed=true + edit=true;; + -n|--max-entries) + options_passed=true + shift + assert_args "max_entries" "$@" + max_entries=$1 + ;; + -h|--help|-*) + usage + exit 0;; + *) break;; + esac + shift +done + +if ! $options_passed; then + jrnl_entry_add "$@" +elif $search_all; then + for jrnl_label in "${!JOURNALS[@]}"; do + JOURNAL=${JOURNALS[$jrnl_label]} + echo "=== $jrnl_label ===" + jrnl_entry_search $short_srch $reverse $max_entries $tags_only $case_sens "${search_args[@]}" + done +elif $edit; then + jrnl_entry_edit $max_entries "${search_args[@]}" +elif $tags_only && [ -z "$search_args" ] && [ $max_entries == 0 ]; then + list_tags "$JOURNAL" +else + jrnl_entry_search $short_srch $reverse $max_entries $tags_only $case_sens "${search_args[@]}" +fi diff --git a/srchx b/srchx new file mode 100755 index 0000000..ccc9c87 --- /dev/null +++ b/srchx @@ -0,0 +1,124 @@ +#!/usr/bin/perl +# +# (c) 2019-2020 Vitaly Parnas +# See LICENSE for licensing information. + +use strict; +use warnings; +use 5.010; +use File::Basename; +use Data::Dumper; +use Term::ANSIColor qw(:constants); + +my $DELIM = ';'; +my $CONF_FILE = basename($0) . '.conf'; +my $COLOR_FILE = YELLOW; +#my $COLOR_FILE = BRIGHT_MAGENTA; +my $COLOR_PATTERN = BRIGHT_CYAN; +my $DEBUG = 0; + +sub usage () +{ + say " +Usage: " . basename($0) . " [options] + Options: + [-h|--help] - print this message. + [-d|--debug] - debug mode, default: no. + [-n|--no-act] - display search command, do not execute. + [-c|--count] - display counts of search results of each file only. + [-r|--recursive] - recurse into directories. + [-s|--suppress-err] - ignore missing files. +"; + exit(1); +} + +sub read_ini +{ + my $ini = shift; + my $config; + my $section; + open(INI, '<', $ini) or die "Could not open file '$ini' $!\n"; + while () { + chomp; + next if /^(#|;|\s*$)/; # comments, blanks + next if s/^\[(.*)\]/$section = $1/sie; # new config section + /^([^#;]+)\s*=\s*([^#]+)\s*/ and $config->{$section}->{$1} = $2; + } + close INI; + return $config; +} + +sub parse_free_args +{ + my ($conf, $free_args, $searches, $paths) = @_; + foreach (@$free_args) { + my $target; + my $val = $conf->{"content"}->{$_}; + if ($val) { + $target = $paths; + } else { + $val = ($conf->{"terms"}->{$_} or $_); + $target = $searches; + } + push (@$target, split(/$DELIM/, $val)); + } + say "Searches: @$searches" if $DEBUG; + say "Paths @$paths" if $DEBUG; +} + +sub exec_pretty_search +{ + my ($cmd, $search_str) = @_; + $_ = qx($cmd); + s/^[^\s:]+:/sprintf($COLOR_FILE . "%s" . RESET, $&)/egm; + s/$search_str/sprintf($COLOR_PATTERN . "%s" . RESET, $&)/eig; + s#$ENV{HOME}#~#g; # strip verbose $HOME path from results + say; +} + +# MAIN +{ + my $conf; + my (@free_args, @searches, @paths, @grep_options); + my ($no_act, $count_matches, $recurse, $min_count, $suppress_err) = (0,0,0,1,0); + my ($search_str, $cmd); + + while ($_ = shift) { + if (/^(-h|--help)/) { usage(); } + elsif (/^(-d|--debug)/) { $DEBUG = 1; } + elsif (/^(-n|--no-act)/) { $no_act = 1; } + elsif (/^(-c|--count)/) { $count_matches = 1; } + elsif (/^(-r|--recursive)/) { $recurse = 1;} + elsif (/^(-s|--suppress-err)/) { $suppress_err = 1;} + else { push (@free_args, $_); } + } + usage() unless (@free_args); + push (@grep_options, $recurse ? "-r" : "--exclude-dir=/*"); + push (@grep_options, "--no-messages") if $suppress_err; + foreach (".$CONF_FILE", "$ENV{HOME}/.$CONF_FILE", + "$ENV{HOME}/.config/$CONF_FILE") { + if (-e) { + say "Using conf $_"; + $conf = read_ini($_); + last; + } + } + $conf or die "Could not find or open $CONF_FILE\n"; + #print Dumper($conf) if $DEBUG; + parse_free_args($conf, \@free_args, \@searches, \@paths); + while (my ($filter_name, $val) = each %{$conf->{"filters"}}) { + ($filter_name eq "min-count") and $min_count = $val and next; + foreach (split(/$DELIM/, $val)) { + push (@grep_options, "--$filter_name '$_'"); + } + } + $count_matches and unshift (@grep_options, "-cH"); + $search_str = join('|', @searches); + $cmd = "grep -Ei @grep_options -e '$search_str' @paths"; + if ($count_matches) { + $cmd .= " | awk -F':' '{if (\$2 >= $min_count) " . + "printf(\"%4d : %s\\n\", \$2, \$1)}' | sort -n"; + } + $no_act ? say($cmd) : exec_pretty_search($cmd, $search_str); + exit(0); +} diff --git a/srchx.pl b/srchx.pl deleted file mode 100755 index ccc9c87..0000000 --- a/srchx.pl +++ /dev/null @@ -1,124 +0,0 @@ -#!/usr/bin/perl -# -# (c) 2019-2020 Vitaly Parnas -# See LICENSE for licensing information. - -use strict; -use warnings; -use 5.010; -use File::Basename; -use Data::Dumper; -use Term::ANSIColor qw(:constants); - -my $DELIM = ';'; -my $CONF_FILE = basename($0) . '.conf'; -my $COLOR_FILE = YELLOW; -#my $COLOR_FILE = BRIGHT_MAGENTA; -my $COLOR_PATTERN = BRIGHT_CYAN; -my $DEBUG = 0; - -sub usage () -{ - say " -Usage: " . basename($0) . " [options] - Options: - [-h|--help] - print this message. - [-d|--debug] - debug mode, default: no. - [-n|--no-act] - display search command, do not execute. - [-c|--count] - display counts of search results of each file only. - [-r|--recursive] - recurse into directories. - [-s|--suppress-err] - ignore missing files. -"; - exit(1); -} - -sub read_ini -{ - my $ini = shift; - my $config; - my $section; - open(INI, '<', $ini) or die "Could not open file '$ini' $!\n"; - while () { - chomp; - next if /^(#|;|\s*$)/; # comments, blanks - next if s/^\[(.*)\]/$section = $1/sie; # new config section - /^([^#;]+)\s*=\s*([^#]+)\s*/ and $config->{$section}->{$1} = $2; - } - close INI; - return $config; -} - -sub parse_free_args -{ - my ($conf, $free_args, $searches, $paths) = @_; - foreach (@$free_args) { - my $target; - my $val = $conf->{"content"}->{$_}; - if ($val) { - $target = $paths; - } else { - $val = ($conf->{"terms"}->{$_} or $_); - $target = $searches; - } - push (@$target, split(/$DELIM/, $val)); - } - say "Searches: @$searches" if $DEBUG; - say "Paths @$paths" if $DEBUG; -} - -sub exec_pretty_search -{ - my ($cmd, $search_str) = @_; - $_ = qx($cmd); - s/^[^\s:]+:/sprintf($COLOR_FILE . "%s" . RESET, $&)/egm; - s/$search_str/sprintf($COLOR_PATTERN . "%s" . RESET, $&)/eig; - s#$ENV{HOME}#~#g; # strip verbose $HOME path from results - say; -} - -# MAIN -{ - my $conf; - my (@free_args, @searches, @paths, @grep_options); - my ($no_act, $count_matches, $recurse, $min_count, $suppress_err) = (0,0,0,1,0); - my ($search_str, $cmd); - - while ($_ = shift) { - if (/^(-h|--help)/) { usage(); } - elsif (/^(-d|--debug)/) { $DEBUG = 1; } - elsif (/^(-n|--no-act)/) { $no_act = 1; } - elsif (/^(-c|--count)/) { $count_matches = 1; } - elsif (/^(-r|--recursive)/) { $recurse = 1;} - elsif (/^(-s|--suppress-err)/) { $suppress_err = 1;} - else { push (@free_args, $_); } - } - usage() unless (@free_args); - push (@grep_options, $recurse ? "-r" : "--exclude-dir=/*"); - push (@grep_options, "--no-messages") if $suppress_err; - foreach (".$CONF_FILE", "$ENV{HOME}/.$CONF_FILE", - "$ENV{HOME}/.config/$CONF_FILE") { - if (-e) { - say "Using conf $_"; - $conf = read_ini($_); - last; - } - } - $conf or die "Could not find or open $CONF_FILE\n"; - #print Dumper($conf) if $DEBUG; - parse_free_args($conf, \@free_args, \@searches, \@paths); - while (my ($filter_name, $val) = each %{$conf->{"filters"}}) { - ($filter_name eq "min-count") and $min_count = $val and next; - foreach (split(/$DELIM/, $val)) { - push (@grep_options, "--$filter_name '$_'"); - } - } - $count_matches and unshift (@grep_options, "-cH"); - $search_str = join('|', @searches); - $cmd = "grep -Ei @grep_options -e '$search_str' @paths"; - if ($count_matches) { - $cmd .= " | awk -F':' '{if (\$2 >= $min_count) " . - "printf(\"%4d : %s\\n\", \$2, \$1)}' | sort -n"; - } - $no_act ? say($cmd) : exec_pretty_search($cmd, $search_str); - exit(0); -} -- cgit v1.2.3-70-g09d2