#!/usr/bin/env bash set -ueo pipefail [ "${DEBUG:-0}" = "1" ] && set -x # usage: # - init - create an empty database # - import - import $FILE or import $DIR, both can be multiple values # - autoimport ($REGEX|video) $FOLDER - will import all files in $FOLDER with a $REGEX filter, # or one of the preset filters: # - video # - add (tag|filename) $VALUE - adds a new tag or filename of $VALUE # - list (tags|files) - lists all the values in a table # - bytag $TAG - list all files by tag label # - tag $FILENAME $TAG - tag $FILENAME with $TAG, $TAG can be repeated # - listtags $FILENAME - list all tags for $FILENAME # TODO: when tagging, check if tags exist # TODO: when tagging, allow more than one tag # FIXME: adding tags doesn't work SCRIPT_DIR=$(dirname $(readlink "$0")) CURRENT_DIR=$(dirname "$PWD") SCRIPT_NAME=$(basename "$0") DB_FILE="${CURRENT_DIR}/tags.db" [ "$1" = "--db" ] && DB_FILE="$2" && shift 2 DB_SCHEMA="${SCRIPT_DIR}/database.sql" declare -A FILTERS FILTERS[video]="avi|flv|mkv|mov|mp4|mpg|ogv|webm|wmv" fail() { echo "$1" >&2; exit 1 } log() { echo "$1" } init() { # DB_NAME [ -f "$1" ] && fail "Database file \"$1\" already exists. Aborting." cat "$DB_SCHEMA" | sqlite3 "$1" log "Empty database file \"$1\" created." } enumerate_positional_parameters() { log "Positional parameters in $FUNCNAME:" CNT=0 for VALUE in "$@"; do let CNT++ || true echo "${CNT}: $VALUE" done } listtags() { # $FILENAME $LIMIT=ALL [ -z "$1" ] && fail "No filename supplied." FILENAME="$1" shift LIMIT="${1:-0}" ADDITIONAL_QUERY="" [ $LIMIT -gt 0 ] && ADDITIONAL_QUERY="LIMIT $LIMIT" sqlite_query \ "SELECT filename, label from files \ INNER JOIN tags_ties ON tags_ties.id = files.id \ INNER JOIN tags ON tags.id = tags_ties.id \ WHERE filename = \"$FILENAME\"\ $ADDITIONAL_QUERY" } add() { # $TYPE(tag|path|hash) $VALUE1..$VALUEN [ "$1" = "tag" ] && shift && add_tag "$@" && exit 0 [ "$1" = "path" ] && shift && add_path "$@" && exit 0 [ "$1" = "hash" ] && fail "Adding hashes is not implemented yet." && TABLE="hashes" && COLUMN="md5,filename" exit 0 } sqlite_query() { # $QUERY sqlite3 -table "$DB_FILE" "$1" } sqlite_lastrows() { # $TABLE $LIMIT sqlite_query "SELECT * FROM \"$1\" ORDER BY id DESC LIMIT ${2}" } sqlite_insert() { # $TABLE $COLUMN $VALUES # $VALUES can be comma-delimited [ -z "$1" ] && fail "No table specified." TABLE="$1" [ -z "$2" ] && fail "No column(s) supplied." COLUMN="$2" [ -z "$3" ] && fail "No column values supplied." shift 2 VALUES="(\"$1\")" if [[ ! -z "${2:-}" ]]; then while true; do shift [ -z "${1:-}" ] && break VALUES+=",(\"$1\")" done fi QUERY="INSERT INTO $TABLE ($COLUMN) VALUES ${VALUES} RETURNING *;" sqlite_query "$QUERY" } add_tag() { # $TAG1...$TAGN TABLE="tags" COLUMN="label" sqlite_insert "$TABLE" "$COLUMN" "$@" } add_path() { # $PATH1..$PATHN TABLE="files" COLUMN="filename" sqlite_insert "$TABLE" "$COLUMN" "$@" } add_path_auto() { # $REGEX $FOLDER find "${2}" -type f -regextype posix-extended -iregex "$1" -exec "$0" import "{}" + } main() { if [[ "$1" = "init" ]]; then init "${2:-$DB_FILE}" exit 0 fi if [[ "$1" = "import" ]]; then shift add "path" "$@" fi if [[ "$1" = "autoimport" ]]; then shift if [[ "${1:-}" =~ "${!FILTERS[@]}" ]]; then add_path_auto ".*(${FILTERS[$1]})" "$2" else add_path_auto "$1" "$2" fi fi if [[ "$1" = "add" ]]; then shift add "$@" fi if [[ "$1" = "list" ]]; then [ -z "$2" ] && fail "No table supplied." TABLE_NAME="$2" sqlite3 -table "$DB_FILE" "SELECT * FROM \"${TABLE_NAME}\"" exit 0 fi if [[ "$1" = "bytag" ]]; then [ -z "$2" ] && fail "No tag supplied." TAG_NAME="$2" sqlite3 -table "$DB_FILE" "SELECT filename FROM files WHERE id = (SELECT id FROM tags_ties WHERE id = (SELECT id FROM tags WHERE label = \"${TAG_NAME}\"))" exit 0 fi if [[ "$1" = "tag" ]]; then shift [ -z "$1" ] && fail "No filename supplied." FILENAME="$1" shift [ -z "$1" ] && fail "No tag supplied." COUNTER=0 while true; do LABEL="$1" shift sqlite3 -table "$DB_FILE" "INSERT INTO tags_ties (id, id) VALUES ((SELECT id FROM files WHERE filename = \"$FILENAME\"),(SELECT id FROM tags WHERE label = \"$LABEL\"))" COUNTER=$((COUNTER++)) [ -z "${1:-}" ] && break done listtags "$FILENAME" "$COUNTER" exit 0 fi if [[ "$1" = "listtags" ]]; then [ -z "${2:-}" ] && fail "Usage: $SCRIPT_NAME listtags filename" listtags "$2" exit 0 fi } main "$@"