Last active
October 9, 2021 04:20
-
-
Save andre-st/60c3db8f68c7d9aa191cd13b3621efbb to your computer and use it in GitHub Desktop.
Organize access to media files via tags
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# Purpose: Creates sub-directories with symlinks to files that are scattered over | |
# different directories but share a commonality, e.g, being 'a favourite' | |
# Context: Media collections | |
# Analogy: Database views, locate-command result saved as filesystem objects | |
# Author : github.com/andre-st | |
# Version: 2021-01-28 | |
# | |
# Usage: | |
# 1. Copy this file to /usr/loca/bin/ | |
# 2. create a ".views" directory in your collection | |
# 3. $ ln -s /usr/local/bin/mkview.sh your_collection/.views/update.sh | |
# 4. Tag filenames in your collection with: | |
# + or ++ or +++ to indicate favourites (sorts them to the top in each dir too) | |
# todo to indicate a planned edit | |
# | |
# Note: | |
# - browse XXXmb directories to find and delete big files if HDD space shrinks | |
# (favourites are excluded by default) | |
# - adjust views in this file as you need or create an "update.cfg" in a .views-directory | |
# with nothing more than custom 'mkview', 'mksample' and 'mkquality' | |
# calls similar to this executable. | |
# Update.cfg can also sets extra variables such as: FINDOPTEXTRA=-follow to follow symbolic links | |
# - dot-directories are ignored | |
# | |
# | |
# Example result: | |
# your_collection | |
# |-- .views | |
# | |-- 100mb | |
# | |-- 200mb | |
# | |-- 80mb | |
# | | |-- 1080p | |
# | | `-- 360p | |
# | |-- fav+ | |
# | | `-- sample | |
# | |-- fav++ | |
# | | `-- sample | |
# | |-- fav+++ | |
# | |-- recent | |
# | `-- todo | |
# | | |
# |-- your_A | |
# |-- your_B | |
# |-- ... | |
# `-- your_Z | |
# Creates a "dir/sample" directory with a random selection of files from a source directory "dir" | |
# given that "dir" has more than 60 symbolic links. | |
# -> string dir with symlinks | |
function mksample() | |
{ | |
dir="$1" | |
sampledir="sample" | |
samplesize=60 | |
largersamplesize=$(( samplesize*2 )) | |
pushd "$dir" > /dev/null | |
rm -rf "$sampledir" | |
numlinks=$( find . -type l -maxdepth 1 | wc -l ) | |
if (( $numlinks > $samplesize )) | |
then | |
mkdir "$sampledir" | |
find . -type l -maxdepth 1 | shuf -n $largersamplesize | shuf -n $samplesize | while read linkname | |
do | |
abspath=$( readlink -e "$linkname" ) | |
relpath=$( realpath --relative-to "$sampledir" "$abspath" ) | |
ln -sfn "$relpath" "$sampledir/$linkname" | |
done | |
fi | |
popd > /dev/null | |
} | |
# Creates a subdir based on video resolution from links in a source directory "dir". | |
# -> string dir with symlinks | |
function mkquality() | |
{ | |
dir="$1" | |
lq_height=360 | |
hq_height=1080 | |
lq_dir="${lq_height}p" | |
hq_dir="${hq_height}p" | |
pushd "$dir" > /dev/null | |
rm -rf "$lq_dir" "$hq_dir" | |
mkdir "$lq_dir" "$hq_dir" | |
find . -type l -maxdepth 1 | while read linkname | |
do | |
abspath=$( readlink -e "$linkname" ) | |
relpath=$( realpath --relative-to "$lq_dir" "$abspath" ) | |
height=$( ffprobe -v quiet -select_streams v:0 -show_entries stream=height -of csv=s=x:p=0 "$abspath" | head -n 1 ) | |
[ -z "$height" ] && continue | |
if (( "$height" <= $lq_height )) | |
then | |
ln -sfn "$relpath" "$lq_dir/$linkname" | |
elif (( "$height" >= $hq_height )) | |
then | |
ln -sfn "$relpath" "$hq_dir/$linkname" | |
fi | |
done | |
popd > /dev/null | |
} | |
# -> string view name | |
# -> string find-command expressions and test options (view filter) | |
# -> --add dont delete links in view before adding new ones | |
function mkview() | |
{ | |
viewdir=$1 | |
findopt="-type f $2 -and ! -name '*.txt' -and ! -path '*/.*' $SKIPFINDOPT" | |
findcmd="find .. $FINDOPTEXTRA \( $findopt \) -print $findpipe" | |
mkdir -p $viewdir | |
if [[ "$@" != *"--add"* ]] | |
then | |
find $viewdir -type l -delete | |
fi | |
IFS=$'\n' # Bash's field seperator != space (in filenames) | |
while read filename | |
do | |
linkname=$(basename "$filename") | |
while [ -e "$viewdir/$linkname" ] # if dir1/f.mp4 and dir2/f.mp4 rename one link: | |
do | |
target_of_exist_link=`readlink $viewdir/$linkname` | |
[[ "$target_of_exist_link" == "../$filename" ]] && break # identical targets | |
linkname="_$linkname" # _f.mp4, then __f.mp4, ___f.mp4 until unique | |
done | |
ln -s "../$filename" "$viewdir/$linkname" 2>/dev/null | |
done < <(eval "$findcmd") | |
} | |
echo "Updating .views" | |
pushd "$(dirname "${BASH_SOURCE[0]}")" > /dev/null | |
# Skip sub-directories with own .views (useless redundancy otherwise): | |
skipfindoptarr=(`find .. -type d -name '.views' -and ! -path '../.views' -printf '-and ! -path "%h/*" '`) | |
SKIPFINDOPT="${skipfindoptarr[@]}" | |
# Custom views and changes to global vars with effect on default views: | |
[[ -e update.cfg ]] && source update.cfg | |
# Default views: | |
mkview 'todo' '-iname "*todo*"' | |
mkview 'fav+' '-name "*+*" -and ! -name "*.jpg" -and ! -name "* + *" ' | |
mkview 'fav++' '-name "*++*" -and ! -name "*.jpg" -and ! -name "* + *" ' | |
mkview 'fav+++' '-name "*+++*" -and ! -name "*.jpg" -and ! -name "* + *" ' | |
mksample 'fav+' | |
mksample 'fav++' | |
mksample 'fav+++' | |
mkview '80mb' '-size +80000k -and ! -name "*+*"' | |
mkview '100mb' '-size +100000k -and ! -name "*+*"' | |
mkview '200mb' '-size +200000k -and ! -name "*+*"' | |
mkquality '80mb' | |
mkview 'recent' '-mtime -31' # 31 days | |
#mkview 'oldfmt' '-name "**.avi" -or -name "*.mpg" -or -name "*.mpeg" -or -name "*.wmv"' | |
#mkview 'cut' '-iname "*.cut.*"' | |
popd > /dev/null |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment