diff options
| -rwxr-xr-x | keyboard/bl | 2 | ||||
| -rwxr-xr-x | keyboard/dockfanctl | 10 | ||||
| -rwxr-xr-x | keyboard/monctl | 38 | ||||
| -rwxr-xr-x | keyboard/shutdownprompt | 6 | ||||
| -rwxr-xr-x | keyboard/sinkswitch | 15 | ||||
| -rwxr-xr-x | misc/diskhealth | 46 | ||||
| -rwxr-xr-x | misc/mkmount | 18 | ||||
| -rwxr-xr-x | misc/mountimg | 42 | ||||
| -rwxr-xr-x | misc/verifoo | 110 | ||||
| -rwxr-xr-x | video/d8concat | 164 | ||||
| -rwxr-xr-x | video/d8integrity | 9 | ||||
| -rw-r--r-- | video/dvgrabscript | 1 | ||||
| -rwxr-xr-x | video/mp4cp | 12 | 
13 files changed, 469 insertions, 4 deletions
diff --git a/keyboard/bl b/keyboard/bl index 84694e9..333d1e8 100755 --- a/keyboard/bl +++ b/keyboard/bl @@ -7,7 +7,7 @@ bl_min=0  bl_max=100  # DDC/CI stuff -ddcutil="ddcutil --sleep-less --noverify --sleep-multiplier $sleep_multiplier" # Make sure user is in group i2c +ddcutil="ddcutil --enable-sleep-less --noverify --sleep-multiplier $sleep_multiplier" # Make sure user is in group i2c  #ddcutil="ddcutil --sleep-less --noverify --enable-capabilities-cache --sleep-multiplier $sleep_multiplier" # Make sure user is in group i2c  max() { [ $1 -gt $2 ] && echo $1 || echo $2; } diff --git a/keyboard/dockfanctl b/keyboard/dockfanctl new file mode 100755 index 0000000..f422b3e --- /dev/null +++ b/keyboard/dockfanctl @@ -0,0 +1,10 @@ +#!/bin/sh +hub=$(doas uhubctl | grep -B3 'Port 3: .* highspeed enable connect .* Lenovo USB2.0 Hub' | grep -o -m1 '[0-9][^ ]*' | head -n1) +! [ "$hub" ] && exit 1 + +echo $hub + +case $1 in +	off) doas uhubctl -a off -p 4 -l $hub ;; +	on)  doas uhubctl -a on  -p 4 -l $hub ;; +esac diff --git a/keyboard/monctl b/keyboard/monctl new file mode 100755 index 0000000..116974f --- /dev/null +++ b/keyboard/monctl @@ -0,0 +1,38 @@ +#!/bin/sh + +display=$2 +input_vcp=0x60 +input_local=0x11 +input_other=0x0f +power_vcp=0xD6 +power_on=0x01 +power_off=0x05 +sleep_multiplier=.1  # Set this as low as you can get away with + +# DDC/CI stuff +ddcutil="ddcutil --noverify --sleep-multiplier $sleep_multiplier --display $display"  # Make sure user is in group i2c + +getstate() { $ddcutil getvcp $1 | grep -o '0x[0-9a-f]*' | tail -n1 ; } + +in_other() { $ddcutil setvcp $input_vcp $input_other ; } +in_local() { $ddcutil setvcp $input_vcp $input_local ; } +pw_on()    { $ddcutil setvcp $power_vcp $power_on    ; } +pw_off()   { $ddcutil setvcp $power_vcp $power_off   ; } + +in_toggle() { [ `getstate $input_vcp` = $input_local ] && in_other || in_local ; }  # Default to input_local +pw_toggle() { [ `getstate $power_vcp` = $pw_on       ] && pw_off   || pw_on    ; }  # Default to power_on + +case $1 in +	inputother)  in_other  ;; +	inputlocal)  in_local  ;; +	inputtoggle) in_toggle ;; +	poweron)     pw_on     ;; +	poweroff)    pw_off    ;; +	powertoggle) pw_toggle ;; +	getinput)    getstate $input_vcp ;; +	getpower)    getstate $power_vcp ;; +	*) +		echo "'$1' is not a recognized option" +		echo "$(basename "$0") [inputother|inputlocal|inputtoggle|poweron|poweroff|powertoggle] display_num" +	;; +esac diff --git a/keyboard/shutdownprompt b/keyboard/shutdownprompt index ce187a8..71cac3d 100755 --- a/keyboard/shutdownprompt +++ b/keyboard/shutdownprompt @@ -13,7 +13,7 @@ for process_name in $warn_process_names; do  done  case $option in -	Suspend)  launch zzz 	;; -	Shutdown) launch off	;; -	Restart)  launch res	;; +	Suspend)  launch zzz ;; +	Shutdown) launch off ;; +	Restart)  launch res ;;  esac diff --git a/keyboard/sinkswitch b/keyboard/sinkswitch new file mode 100755 index 0000000..3046498 --- /dev/null +++ b/keyboard/sinkswitch @@ -0,0 +1,15 @@ +#!/bin/sh +[ $(hostname) != 'T495' ] && echo "Not T495. Exiting" && exit 1 + +status="$(wpctl status)" + +headphones_sink=$(echo "$status" | grep -m1 'ThinkPad USB-C Dock Gen2 USB Audio Analog Stereo \[vol:'                        | grep -m1 -o '[0-9]*') +speakers_sink=$(  echo "$status" | grep -m1 'Raven/Raven2/Fenghuang HDMI/DP Audio Controller Digital Stereo (HDMI 2) \[vol:' | grep -m1 -o '[0-9]*') + +selected_sink=$(echo "$status" | grep -m1 '\*\s*[0-9]*' | grep -o '[0-9]*') + +case $selected_sink in +	$headphones_sink) wpctl set-default $speakers_sink   && echo "Switched to speakers"   ;; +	$speakers_sink)   wpctl set-default $headphones_sink && echo "Switched to headphones" ;; +	*) echo "Cannot find correct sink. Exiting" && exit 1 ;; +esac diff --git a/misc/diskhealth b/misc/diskhealth new file mode 100755 index 0000000..5cce07b --- /dev/null +++ b/misc/diskhealth @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 + +import subprocess +import json +from pprint import pprint + +smart_result = subprocess.run(["sudo", "smartctl", "-Aij", "/dev/sda"], capture_output=True, text=True) + +smart_result = json.loads(smart_result.stdout) + +sector_size = smart_result["logical_block_size"] + +from typing import NamedTuple +class RawValue(NamedTuple): +    value: int +    string: str + +class Flags(NamedTuple): +    value: int +    string: str +    prefailure: bool +    updated_online: bool +    performance: bool +    error_rate: bool +    event_count: bool +    auto_keep: bool + +class SmartAttribute(NamedTuple): +    id: int +    name: str +    value: int +    worst: int +    thresh: int +    when_failed: str +    flags: dict +    raw: dict + +attributes = {} + +for attr in smart_result["ata_smart_attributes"]["table"]: +    attr_l = SmartAttribute(**attr) +    attributes[attr_l.id] = attr_l + +if 241 in attributes: +    lbas_written = attributes[241].raw["value"] +    print((lbas_written * sector_size) / 10**12) diff --git a/misc/mkmount b/misc/mkmount new file mode 100755 index 0000000..b23d85b --- /dev/null +++ b/misc/mkmount @@ -0,0 +1,18 @@ +#!/bin/sh + +set -e + +mpdir="/run/media/$USER" +mkdir -p "$mpdir" + +mp="$1" + +if [ -z "$mp" ]; then +	mp="$(mktemp -d "$mpdir/XXXXXX")" +else +	mp="$mpdir/$mp" +	[ -f "$mp" ] && echo "Error: $mp already exists" && exit 1 +	mkdir -p "$mp" +fi + +echo "$mp" diff --git a/misc/mountimg b/misc/mountimg new file mode 100755 index 0000000..efe886c --- /dev/null +++ b/misc/mountimg @@ -0,0 +1,42 @@ +#!/bin/sh + +set -e + +die() { echo "$(basename "$0"): $1" && exit 1 ; } + +# file check +! [ "$1" ]         && die "No file was specified" +! [ -f "$1" ]      && die "$1 is not a file" +# mountpoint check +! [ "$2" ]         && die "No mountpoint was specified" +! [ -d "$2" ]      && die "Mountpoint $2 is not a directory" +[ "`ls -A "$2"`" ] && die "Mountpoint $2 is not empty" +# check disk image validity +! fdisk -lu "$1" 2>&1 | grep -m1 "\s*Start\s*End" >/dev/null && die "File $1 is not a disk image" + +# get sectorsize +#sectorsize=$(fdisk -lu "$1" | grep -m1 '^Units:' | cut -d' ' -f8)  # Sector size is 8th word +sectorsize=512 + +# get partition num, if there is more than one part, interactively if not available +# TODO check if there is no partitions +partition="$3" +if [ -z "$partition" ]; then +	fdisk -o Size,Type,Name -lu "$1" | awk '!f{print $0};f>0{print "  " f-1 ") " $0; f++};f==-1{print "Part " $0;f=1};/^$/{f=-1}' + +	printf "\nChoose partition number to mount: " +	read partition +fi + +# check if part num valid +[ -z "${partition##*[!0-9]*}" ] && die "Illegal number: '$partition'" + +# get offset from fdisk +fdisk_ignore_lines=9  # ignore first 9 lines of output +offset=$(fdisk -o Start -l "$1" | sed $(( fdisk_ignore_lines + partition ))'q;d' ) + +# check if that produced a valid offset +[ -z "$offset" ] && die "Partition $partition does not exist" + +# try to mount +mount -o loop,offset=$(( offset * sectorsize )) "$1" "$2" && echo "Successfully mounted" || die "Error mounting" diff --git a/misc/verifoo b/misc/verifoo new file mode 100755 index 0000000..34089cb --- /dev/null +++ b/misc/verifoo @@ -0,0 +1,110 @@ +#!/bin/sh + +help() { +cat << HELPDOC +usage: $(basename "$0") [options] [file]... + +OPTIONS: +  -h, --help                show this menu +  -c, --check               read and verify checksums from file +  -r, --recursive           produce a checksum for all files in a directory recursively +  --recursive-sum           produce a checksum for a directory. sensitive to filenames +  --recursive-sum-nopath    produce a checksum for a directory. not sensitive to filenames/placement +  -s, --silent              produce no output to stderr +  -v, --verbose             output a progress bar of hash progress if pv is avaliable (wip) + +MODES: +  -S, --sha                 use sha1sum for checksums +  -M, --md5                 use md5sum for checksums +  -X, --xxh                 use xxhsum for checksums (default if avaliable with sha1sum as fallback) +HELPDOC +} + +# set defaults +cat="pv -F'%r_[%b]_[%t]_[%e]_%p'" +! command -v pv >/dev/null && cat=cat + +hash=xxhsum +! command -v xxhsum >/dev/null && hash=sha1sum + +hashfiles="" +verbose=1 +recursive="" +outputfile="" + +while true; do +	case "$1" in +		-c|--check) [ -n "$2" ] && hashfiles="$hashfiles $2" && shift ;; +		-s|--silent)  verbose=0 ;; +		-v|--verbose) verbose=2 ;; +		--recursive|-r)         recursive=3 ;; +		--recursive-sum)        recursive=2 ;; +		--recursive-sum-nopath) recursive=1 ;; +		-S|--sha) hash=sha1sum ;; +		-M|--md5) hash=md5sum  ;; +		-X|--xxh) hash=xxhsum  ;; +		-h|--help) help; exit 0 ;; +		-*)        help; exit 1 ;; +		*) break ;; +	esac +	shift +done + +case $verbose in +	0) cat=cat ;; +	1) cat=cat ;; +esac + +# run hash +while [ -n "$1" ]; do +	if [ -d "$1" ]; then # is directory +		# check if using -r flag +		case "$recursive" in +			1) +				# get hash of all files, sort hashes, hash hashes +				# TODO make pv work in this mode + +				#catd="$cat" +				#if [ $verbose -ge 2 ] && [ "$catd" != "cat" ]; then +				#	total_files=$(du -a "$1" | wc -l) +				#	catd="pv -l -s$total_files -F'[%b/$total_files]_[%t]_[%e]_%p'" +				#fi +				#sum=`find "$1" -type f -print0 | xargs -r0 $hash | $catd | cut -d' ' -f1 | sort | $hash | cut -d' ' -f1` 2>/dev/null +				sum=`find "$1" -type f -print0 | xargs -r0 $hash | cut -d' ' -f1 | sort | $hash | cut -d' ' -f1` 2>/dev/null +				echo "r/$sum  $1" +			;; +			2) +				# hash sorted filenames +				# sort all filenames w/ full path and hash +				# hash those sums together +				# `tar -c -f - "$1" | $hash` is a good idea but it doesn't work since changes to the fs affect tar +				filehash=`find "$1" -type f -print0 | sort -z | xargs -r0 $cat | $hash | cut -d' ' -f1` +				pathhash=`find "$1" -printf "%P\0" | sort -z | $hash | cut -d' ' -f1` +				sum=`printf "$filehash$pathhash" | $hash | cut -d' ' -f1` +				echo "p/$sum  $1" +			;; +			3) +				# sort files and hash each one in sequence +				# TODO make pv work in this mode +				find "$1" -type f -print0 | sort -z | xargs -r0 $hash +			;; +			*) +				[ $verbose -ge 1 ] && echo "-r not specified for directory $1. Exiting" +				exit 1 +			;; +		esac +	else # is file +		if ! [ -f "$1" ]; then +			[ $verbose -gt 1 ] && echo "File $1 does not exist. Exiting" +			exit 1 +		fi +		files="$1" + +		while [ -f "$2" ]; do +			files="$files\0$2" +			shift +		done +		printf "$files" | xargs -r0 $hash +	fi +	shift +done diff --git a/video/d8concat b/video/d8concat new file mode 100755 index 0000000..bf8b608 --- /dev/null +++ b/video/d8concat @@ -0,0 +1,164 @@ +#!/bin/sh + +[ -d "$1" ] || exit 1  # Exit with error if not pointing to a directory + +# Misc +duration() { ffprobe -i "$1" -show_entries format=duration -v quiet -of csv="p=0" | tr -d . ; } +timebase="1/1000000"  # duration function gives microseconds + +safename() { echo "$1" | tr -d '<>:"/\|?*' ; } + +# Temp dirs +workdir="$(mktemp -d "${TMPDIR:-/tmp}/combinetapesworkdir.XXXXXX")" + +cleanup() { rm -rf "$workdir" ; } +trap cleanup EXIT INT HUP QUIT TERM ALRM USR1 + +# Loop vars +current_date=0 +duration_total=0 +chapter_start=0 +chapter_time=0 +last_chapter_concurrent=0 + +# TODO this relys on the for loop looping in order + +# Either can be ran on single dv containing directory, or many dv containing directories +for dir in "$1"/ "$1"/*/; do +	dir="${dir%/}"  # Remove trailing slash +	title="" + +	if [ "$dir" != "$1" ]; then +		title="$(basename "$dir")" +	fi +	if echo "$dir" | grep '/\*' >/dev/null; then +		break +	fi + +	for tape in "$dir"/*.dv; do +		date="$(echo "$(basename "$tape")" | grep -o '[0-9]*\.[0-9]*\.[0-9]*' | tr '.' '-')" +		time="$(echo "$(basename "$tape")" | grep -o '[0-9]*-[0-9]*-[0-9]*'   | tr '-' ':')" + +		if [ "$title$date" != "$current_date" ]; then +			# Reset vars +			current_date="$title$date" +			duration_total=0 +			chapter_start=0 +			chapter_time=0 +			last_chapter_concurrent=0 + +			# Create dirs +			date_dir="$workdir/$current_date" +			mkdir -p "$date_dir" + +			files="$date_dir/files" +			chapters="$date_dir/chapters" + +			# Init chapters file +			printf ";FFMETADATA1\n\n" > "$chapters" + +			# title file +			if [ "$title" ]; then +				echo "$title" > "$date_dir/title" +			fi +		fi + +		# Concat files +		echo "file '$(realpath "$tape" | sed "s/'/'\\\\''/g")'" >> "$files" + +		# Chapter marker +		tape_duration=$(duration "$tape" | grep -o "[1-9][0-9]*") +		duration_total=$(( $duration_total + $tape_duration )) + +		# Only insert chapter marker if recordings are not concurrent. dvgrab +		# splits files into 1gb chunks, or 291557933 microseconds of raw dv footage +		# (with digital 8) +		if [ $last_chapter_concurrent = 0 ]; then +			chapter_time=$time +		fi +		if [ $tape_duration = 291557933 ]; then +			last_chapter_concurrent=1 +			continue +		else +			last_chapter_concurrent=0 +		fi + +		echo "[CHAPTER]"                             >> "$chapters" +		echo "TIMEBASE=$timebase"                    >> "$chapters" +		echo "START=$chapter_start"                  >> "$chapters" +		echo "END=$duration_total"                   >> "$chapters" +		echo "title=Section starts at $chapter_time" >> "$chapters" +		echo                                         >> "$chapters" + +		chapter_start=$duration_total +	done +done + + +# ffmpeg combine +out_dir=export +default_title=Untitled +mkdir -p "$out_dir" + +for date_dir in "$workdir"/*; do +	date="$(echo "$date_dir" | grep -o "[0-9][0-9][0-9][0-9]-[0-9]*-[0-9]*")" +	origin_tape="$(basename "$1")" + +	# Files +	title="$date_dir/title" +	files="$date_dir/files" +	chapters="$date_dir/chapters" + +	# Nicely title +	if [ -e "$title" ]; then +		# title file +		output_title="$(cat "$title")" +	#elif echo "$origin_tape" | grep "^[^0-9].*[0-9]*-[0-9]*-[0-9]*" >/dev/null; then +	#	# Beginning descriptor +	#	output_title="$(echo "$origin_tape" | sed 's/ [0-9#].*//')" +	#elif echo "$origin_tape" | grep "$date [A-Z]" >/dev/null; then +	#	# Date range or list of dates with titles (Title must be capitalized, 'to' must be made lowercase +	#	output_title="$(echo "$origin_tape" | sed "s/.*$date //; s/ to [0-9].*//; s/;.*//")" +	else +		# default +		output_title=$default_title +	fi + +	output_title="$(echo "$output_title" | sed 's/\s*#[0-9]*$//')"  # Remove sequence numbers + +	# File name +	if [ "$output_title" = "$default_title" ]; then +		output_name="$date" +	else +		output_name="$date $output_title" +	fi + +	if [ -e "$out_dir/$output_name.mp4" ]; then +		output=$(ls "$out_dir" | grep "$output_name *[0-9]*.mp4" | while read -r file; do +			file_tape="$(mediainfo "$out_dir/$file" | sed -n 's/Comment.* - Origin tape: //p')" +			if [ "$origin_tape" = "$file_tape" ]; then +				echo 0 +				break +			fi +		done) +		if [ "$output" = 0 ]; then +			continue +		fi +		output_name="$output_name $(ls "$out_dir" | grep "$output_name *[0-9]*.mp4" | wc -l)" +	fi + +	# Convert +		#-c copy \ +		#-metadata "title=$output_title" \  # Dont set title since it will be stuck if file is renamed +	ffmpeg -hide_banner -nostdin \ +		-n \ +		-f concat -safe 0 -i "$files" \ +		-i "$chapters" -map_metadata 1 \ +		-c:v libx264 -crf 22 -preset medium -tune grain \ +		-vf yadif \ +		-c:a aac -b:a 320k \ +		-metadata "date=$date" \ +		-metadata "comment=tapes.tjkeller.xyz - Origin tape: $origin_tape" \ +		"$out_dir/$output_name.mp4" +		#"$out_dir/$output_name.mkv" +done diff --git a/video/d8integrity b/video/d8integrity new file mode 100755 index 0000000..717032b --- /dev/null +++ b/video/d8integrity @@ -0,0 +1,9 @@ +#!/bin/sh + +find . -type f -name "*.dv" | while read file; do +	ffmpeg -v error -i "$file" 2>&1 | grep "Concealing bitstream errors" >/dev/null && echo "$file" +done + +find . -type f -name "*.dv" | while read file; do +	ffmpeg -v error -i "$file" -f null - 2>&1 | grep "Concealing bitstream errors" >/dev/null && echo "$file" +done diff --git a/video/dvgrabscript b/video/dvgrabscript new file mode 100644 index 0000000..d82ba55 --- /dev/null +++ b/video/dvgrabscript @@ -0,0 +1 @@ +sudo dvgrab -autosplit -rewind -timestamp -format raw -csize 100000000 diff --git a/video/mp4cp b/video/mp4cp new file mode 100755 index 0000000..31f8cc9 --- /dev/null +++ b/video/mp4cp @@ -0,0 +1,12 @@ +#!/bin/sh +set -e +echo "Usage: $(basename $0) [SRCDIR] [DESTDIR]" + +srcdir="$1" +destdir="$2" +# TODO mkdir destdir + +for vid in "$srcdir"/*; do +	# TODO test if is vid +	ffmpeg -n -i "$vid" -c copy -o "$destdir/$(basename "$vid" | sed 's/\.[^.]*$//').mp4" +done  | 
