Refresh and added usage tracking

This commit is contained in:
Hannes
2026-04-24 00:47:51 +02:00
parent 8519d9f62e
commit 477b1bf985
45 changed files with 726 additions and 166 deletions
+2
View File
@@ -16,6 +16,8 @@ if [[ ! -d "$folder" ]]; then
exit 1
fi
/home/honney/.bin/tracker.py add append
# Process files
for file in "$folder"/*; do
[[ -f "$file" ]] || continue # skip non-files
Executable
+112
View File
@@ -0,0 +1,112 @@
#!/usr/bin/env python3
"""
Usage:
python3 count_files (<number>) (<directory>)
python3 count_files (<number>) (<directory>) --undo
"""
import os
from pathlib import Path
import sys
import re
from termcolor import colored
VIDEO_EXTENSIONS = {
".mkv", ".mp4", ".avi", ".mov", ".wmv", ".flv", ".webm", ".m4v"
}
def main():
args = sys.argv[1:]
undo = "--undo" in args
if undo:
args.remove("--undo")
if len(args) == 0:
number = 0
directory = Path(os.getcwd())
elif len(args) == 1:
try:
number = int(args[0])
directory = Path(os.getcwd())
except:
number = 0
directory = Path(args[0])
elif len(args) == 2:
try:
number = int(args[0])
directory = Path(args[1])
except:
number = int(args[1])
directory = Path(args[0])
else:
print("Usage: recount_videos.py (<directory>) [--undo]")
sys.exit(1)
if not directory.exists():
print(colored("Directory does not exist", "red"))
sys.exit(1)
import subprocess
subprocess.run(["python", "/home/honney/.bin/tracker.py", "add", "count_files"])
files = sorted(
[f for f in directory.iterdir()
if f.is_file() and f.suffix.lower() in VIDEO_EXTENSIONS],
key=lambda f: f.name
)
if not files:
print(colored("No video files found", "yellow"))
return
if undo:
# detect prefix like "00", "001", etc.
pattern = re.compile(r"^\d+")
for f in files:
if not pattern.match(f.name):
continue
new_name = Path(directory / pattern.sub("", f.name, count=1))
if new_name.exists():
print(colored(f"Conflict exists: {new_name.name}", "red"))
continue
print(
colored("Restored: ", "green") +
colored(f.name, "red") +
colored(" -> ", "white") +
colored(new_name.name, "green")
)
f.rename(new_name)
else:
max_len = max(len(str(len(files))), 2)
for i, f in enumerate(files):
if number:
new_name = Path(f"{f.parent}/{i+number:0{max_len}d}{f.name}")
else:
new_name = Path(f"{f.parent}/{i+1:0{max_len}d}{f.name}")
if new_name.exists():
print(colored(f"Exists, skipping: {new_name.name}", "red"))
continue
print(
colored("Renamed: ", "green") +
colored(f.name, "red") +
colored(" -> ", "white") +
colored(new_name.name, "green")
)
f.rename(new_name)
if __name__ == "__main__":
main()
+2
View File
@@ -29,6 +29,8 @@ else
SYMBOL="_" # Default separator is "_"
fi
/home/honney/.bin/tracker.py add create_folder_counting
# Ensure the target directory exists
mkdir -p "$TARGET_DIR"
+2
View File
@@ -6,6 +6,8 @@ if [ "$#" -lt 2 ] || [ "$#" -gt 3 ]; then
exit 1
fi
/home/honney/.bin/tracker.py add create_folder_date
# Set folder path
if [ "$#" -eq 3 ]; then
TARGET_DIR="$1"
+8
View File
@@ -0,0 +1,8 @@
ff,2
simple_ffprobe_script,1
show_path,2
append,15
deappend,17
rename_to_first_n,3
count_files,4
remove_fname_last_n,2
1 ff 2
2 simple_ffprobe_script 1
3 show_path 2
4 append 15
5 deappend 17
6 rename_to_first_n 3
7 count_files 4
8 remove_fname_last_n 2
+2
View File
@@ -63,6 +63,8 @@ def main():
if len(sys.argv) < 3:
print("Usage: deappend.py <mode> <string> [directory]")
sys.exit(1)
import subprocess
subprocess.run(["python", "/home/honney/.bin/tracker.py", "add", "deappend"])
# Get arguments
mode = sys.argv[1] # Mode: back, front, ext/extension
+2
View File
@@ -9,6 +9,8 @@ if [ ! -f "$INPUT" ]; then
exit 1
fi
/home/honney/.bin/tracker.py add deinterlace_cpu
# Step 1: Try runtime detection with idet
ffmpeg -hide_banner -vstats -nostats \
-i "$INPUT" \
+2
View File
@@ -4,6 +4,8 @@ INPUT="$HOME/mount/Ripping/Lutz/noch zu rendern/Brücke nach Terabithia/A1_t00.m
OTPUT="${INPUT%.*}_deinterlaced_vaapi.mp4"
TEMP_STATS=$(mktemp)
/home/honney/.bin/tracker.py add deinterlace_gpu
ffmpeg -filter:v idet -frames:v 5000 -an -f rawvideo -y /dev/null -i "$INPUT" 2> "$TEMP_STATS"
SUMMARY_LINE=$(grep "Multi frame detection" "$TEMP_STATS")
+87 -13
View File
@@ -324,13 +324,64 @@ class subtitles:
return " ".join(parts)
# def get_media_info(file):
# cmd = [
# "ffprobe",
# "-v", "error",
# "-show_entries",
# (
# "format=duration:"
# "stream=index,codec_type,codec_name,"
# "width,height,r_frame_rate,bit_rate,duration,nb_frames,"
# "pix_fmt,field_order,time_base,display_aspect_ratio,"
# "color_space,color_transfer,color_primaries,bits_per_raw_sample,"
# "sample_rate,channels,bits_per_sample,"
# "stream_disposition=forced,default:"
# "stream_tags=language,title"
# ),
# "-of", "json",
# file
# ]
# try:
# result = subprocess.run(cmd, capture_output=True, text=True, check=True)
# info = json.loads(result.stdout)
# except subprocess.CalledProcessError as e:
# print(f"Error running ffprobe: {e.stderr}")
# return None, [], [], []
# # Container / file duration (string seconds, per ffprobe convention)
# duration = None
# if "format" in info:
# duration = info["format"].get("duration")
# video_streams = []
# audio_streams = []
# subtitle_streams = []
# for stream in info.get("streams", []):
# stream_type = stream.get("codec_type")
# if stream_type == "video":
# video_streams.append(stream)
# elif stream_type == "audio":
# audio_streams.append(stream)
# elif stream_type == "subtitle":
# subtitle_streams.append(stream)
# if duration is not None:
# duration = float(duration)
# else:
# duration = float('nan')
# return duration, video_streams, audio_streams, subtitle_streams
def get_media_info(file):
cmd = [
"ffprobe",
"-v", "error",
"-show_entries",
(
"format=duration:"
"format=duration:format_tags=title:"
"stream=index,codec_type,codec_name,"
"width,height,r_frame_rate,bit_rate,duration,nb_frames,"
"pix_fmt,field_order,time_base,display_aspect_ratio,"
@@ -348,13 +399,18 @@ def get_media_info(file):
info = json.loads(result.stdout)
except subprocess.CalledProcessError as e:
print(f"Error running ffprobe: {e.stderr}")
return None, [], [], []
return None, [], [], [], None
# Container / file duration (string seconds, per ffprobe convention)
# Duration
duration = None
if "format" in info:
duration = info["format"].get("duration")
# ✅ Extract container title
title = None
if "format" in info:
title = info["format"].get("tags", {}).get("title")
video_streams = []
audio_streams = []
subtitle_streams = []
@@ -368,16 +424,27 @@ def get_media_info(file):
elif stream_type == "subtitle":
subtitle_streams.append(stream)
return float(duration), video_streams, audio_streams, subtitle_streams
if duration is not None:
duration = float(duration)
else:
duration = float('nan')
return duration, video_streams, audio_streams, subtitle_streams, title
def seconds_to_hms(seconds):
h = int(seconds // 3600)
m = int((seconds % 3600) // 60)
s = int(seconds % 60)
return f"{h:02}:{m:02}:{s:02}"
if type(seconds) is float:
h = int(seconds // 3600)
m = int((seconds % 3600) // 60)
s = int(seconds % 60)
return f"{h:02}:{m:02}:{s:02}"
else:
return "ERROR"
def get_stream_bitrate(file_size, duration):
return int((file_size * 8)/duration/1000000) if duration > 0 else 0
if type(duration) is float:
if duration != float('nan'):
return round(float((file_size * 8)/duration/1000000), 2) if duration > 0 else 0
return float('nan')
class video_file:
def __init__(self, path, base_tab=""):
@@ -386,7 +453,7 @@ class video_file:
self.name = os.path.basename(path) # 25.mkv
self.size = os.path.getsize(path)
self.duration, videos, audios, subs = get_media_info(path)
self.duration, videos, audios, subs, title = get_media_info(path)
self.sort_video_info(videos)
self.sort_audio_info(audios)
@@ -395,6 +462,7 @@ class video_file:
self.bitrate = get_stream_bitrate(self.size, self.duration)
self.size = human_readable_size(self.size) # 198MB
self.duration = seconds_to_hms(self.duration)
self.title = title
def sort_video_info(self, videos):
self.videos = []
@@ -422,6 +490,8 @@ class video_file:
else:
np(f"{os.path.dirname(self.path)}/", INFO_STYLE)
np(f"\t{self.name} ({self.size}, {self.duration}, {self.bitrate} MB/s):", NORMAL_STYLE)
if self.title:
np(f"\t\tTitle: \"{self.title}\"", NORMAL_STYLE)
for video in self.videos:
np(f"\t\t{video}", NORMAL_STYLE)
for audio in self.audios:
@@ -484,14 +554,15 @@ def handle_folders(dirs, all_files):
file = file.path
if check_video_ext(os.path.splitext(file)[1]):
dir_files.append(file)
else:
np(f"{file} is not a compatabile Video file", WARN_STYLE)
# else:
# np(f"{file} is not a compatabile Video file", WARN_STYLE)
if(dir_files != []):
dir_files.sort()
all_files.append(dir_files)
if __name__ == "__main__":
# print(sys.argv)
subprocess.run(["python", "/home/honney/.bin/tracker.py", "add", "ff"])
file_dir_array = []
if len(sys.argv) == 0:
print("Something went horribly wrong!")
@@ -504,7 +575,10 @@ if __name__ == "__main__":
dirs = []
for argv in sys.argv[1:]:
if os.path.isfile(argv):
files.append(os.path.abspath(argv))
if check_video_ext(os.path.splitext(argv)[1]):
files.append(os.path.abspath(argv))
# else:
# np(f"{os.path.abspath(argv)} is not a compatabile Video file", WARN_STYLE)
elif os.path.isdir(argv):
dirs.append(os.path.abspath(argv))
else:
+2
View File
@@ -6,6 +6,8 @@ if [ -z "$1" ]; then
exit 1
fi
/home/honney/.bin/tracker.py add ffbitrate
# Get the video file path from the argument
VIDEO_FILE="$1"
+2
View File
@@ -11,6 +11,8 @@ if [ "$#" -lt 2 ]; then
usage
fi
/home/honney/.bin/tracker.py add ffsplit
# Get the input file and validate its existence
input_file="$1"
if [ ! -f "$input_file" ]; then
+2
View File
@@ -20,6 +20,8 @@ if [[ ! -d "$DIR" ]]; then
exit 1
fi
/home/honney/.bin/tracker.py add ff_testing
echo "Scanning directory: $DIR"
for ext in "${EXTENSIONS[@]}"; do
# Find files with the current extension
+2
View File
@@ -47,3 +47,5 @@ for dir in */; do
done
fi
done
/home/honney/.bin/tracker.py add filter_extra
+1
View File
@@ -60,6 +60,7 @@ while IFS= read -r -d '' video; do
rm -rf "$workdir" "$trimmed"
done
/home/honney/.bin/tracker.py add generate-thumb
########## Using full video duration ##########
+1
View File
@@ -49,3 +49,4 @@ list_non_empty_directories() {
# Start listing from the current directory
list_non_empty_directories "."
/home/honney/.bin/tracker.py add list_folder_empty
-16
View File
@@ -1,16 +0,0 @@
node:events:502
throw er; // Unhandled 'error' event
^
Error: EBADF: bad file descriptor, read
Emitted 'error' event on ReadStream instance at:
at emitErrorNT (node:internal/streams/destroy:169:8)
at errorOrDestroy (node:internal/streams/destroy:238:7)
at node:internal/fs/streams:272:9
at FSReqCallback.wrapper [as oncomplete] (node:fs:683:5) {
errno: -9,
code: 'EBADF',
syscall: 'read'
}
Node.js v20.19.0
View File
View File
View File
Executable
+59
View File
@@ -0,0 +1,59 @@
#!/usr/bin/env python3
"""
Usage:
python3 recount_files.py <number> (<directory>)
"""
import os
from pathlib import Path
import sys
import re
from termcolor import colored
def main():
if len(sys.argv) == 2:
dir = Path(os.getcwd())
elif (len(sys.argv) == 3):
dir = Path(sys.argv[2])
else:
print("Usage: recount_files.py <number> (<directory>)")
sys.exit(1)
import subprocess
subprocess.run(["python", "/home/honney/.bin/tracker.py", "add", "recount_files"])
number = int(sys.argv[1])
pattern = re.compile(r'^\d+')
files = sorted(
[f for f in dir.iterdir() if f.is_file() and pattern.match(f.name)],
key=lambda f: float(f.stem)
)
min_num = int(files[0].stem)
max_len = max(len(files[-1].stem), 2)
for f in files:
try:
delta = int(f.stem)-min_num
new_file_name = Path(f"{f.parent}/{delta+number:0{max_len}d}{f.suffix}")
except:
delta = float(f.stem)-min_num
new_file_name = Path(f"{f.parent}/{delta+number:0{max_len+2}.1f}{f.suffix}")
if not new_file_name.is_file():
print(
colored("Renamed: ", "green") +
colored(str(f.parent), "cyan") +
colored("[", "white") +
colored(f.name, "red") +
colored(" -> ", "white") +
colored(new_file_name.name, "green") +
colored("]", "white")
)
f.rename(new_file_name)
else:
print(colored(f"{new_file_name.name} already exists. Aborting", "red"))
if __name__ == "__main__":
main()
+78
View File
@@ -0,0 +1,78 @@
#!/usr/bin/env python3
"""
Usage:
remove_fname_last_n <number> (<directory>)
"""
import sys
from pathlib import Path
import os
from termcolor import colored
VIDEO_EXTENSIONS = {
".mkv", ".mp4", ".avi", ".mov", ".wmv", ".flv", ".webm", ".m4v"
}
def main():
args = sys.argv[1:]
undo = "--undo" in args
if undo:
args.remove("--undo")
if len(args) == 0:
number = 0
directory = Path(os.getcwd())
elif len(args) == 1:
try:
number = int(args[0])
directory = Path(os.getcwd())
except:
number = 0
directory = Path(args[0])
elif len(args) == 2:
try:
number = int(args[0])
directory = Path(args[1])
except:
number = int(args[1])
directory = Path(args[0])
else:
print("Usage: remove_fname_last_n <number> (<directory>)")
sys.exit(1)
if not directory.exists():
print(colored("Directory does not exist", "red"))
sys.exit(1)
import subprocess
subprocess.run(["python", "/home/honney/.bin/tracker.py", "add", "remove_fname_last_n"])
files = sorted(
[f for f in directory.iterdir()
if f.is_file() and f.suffix.lower() in VIDEO_EXTENSIONS],
key=lambda f: f.name
)
if not files:
print(colored("No video files found", "yellow"))
return
for f in files:
new_name = f.parent/Path(f.stem[:-number]+f.suffix)
if new_name.exists():
print(colored(f"File {new_name.name} already exists", "red"))
else:
print(
colored("Renamed: ", "green") +
colored(f.name, "red") +
colored(" -> ", "white") +
colored(new_name.name, "green")
)
f.rename(new_name)
if __name__ == "__main__":
main()
+84 -24
View File
@@ -1,27 +1,87 @@
#!/bin/bash
#!/usr/bin/env python3
import os
import sys
import glob
# Check if scheme is provided
if [ -z "$1" ]; then
echo "Usage: rename_filtered <scheme> [folder]"
echo
echo "Example:"
echo " Files in folder:"
echo " myfile_E123.txt"
echo " myfile_E456.txt"
echo " myfile_E789.txt"
echo " myfile_E2468.txt"
echo
echo " Run:"
echo " rename_filtered \"????????E!!!\" ./"
echo
echo " Result:"
echo " myfile_E123.txt → 123.txt"
echo " myfile_E456.txt → 456.txt"
echo " myfile_E789.txt → 789.txt"
echo " myfile_E2468.txt → myfile_2468.txt"
def process_files(files, scheme):
scheme_len = len(scheme)
exit 1
fi
for filename in files:
print(f"Processing file: {filename}")
print(f"Against scheme: {scheme}")
# Call the Python script with scheme and optional folder argument
python3 /home/honney/.bin/rename_filtered.py "$@"
# Get the actual filename without the directory path
base = os.path.basename(filename)
name, ext = os.path.splitext(base)
if scheme_len == len(name):
new_name = ""
for i in range(scheme_len):
current_letter = scheme[i]
if current_letter == "?":
pass
elif current_letter == "!":
new_name += name[i]
elif current_letter == name[i]:
new_name += current_letter
else:
print(f"Mismatch at position {i}, skipping {filename}")
new_name = None
break
if new_name:
rename_file(filename, new_name, ext)
else:
print("No new name Generated. It is not supposed to do that")
return
def rename_file(file, new_name, ext):
dirname = os.path.dirname(file) # correct: preserve folder
old_base = os.path.basename(file)
if old_base != new_name:
new_filepath = os.path.join(dirname, new_name + ext)
os.rename(file, new_filepath)
print(f"Renamed '{old_base}' → '{new_name + ext}'")
else:
print(f"No renaming needed for '{old_base}'.")
def main():
if len(sys.argv) < 2:
print("Usage: scheme.py <scheme> [folder]")
sys.exit(1)
else:
print("Usage: rename_filtered <scheme> [folder]")
print()
print("Example:")
print(" Files in folder:")
print(" myfile_E123.txt")
print(" myfile_E456.txt")
print(" myfile_E789.txt")
print(" myfile_E2468.txt")
print()
print(" Run:")
print(" rename_filtered \"????????E!!!\" ./")
print()
print(" Result:")
print(" myfile_E123.txt → 123.txt")
print(" myfile_E456.txt → 456.txt")
print(" myfile_E789.txt → 789.txt")
print(" myfile_E2468.txt → myfile_2468.txt")
sys.exit(1)
import subprocess
subprocess.run(["python", "/home/honney/.bin/tracker.py", "add", "rename_filtered"])
scheme = sys.argv[1]
folder = sys.argv[2] if len(sys.argv) > 2 else "."
# Get all files in the folder (assuming files are in the current directory by default)
files = glob.glob(os.path.join(folder, "*"))
# Run the renaming logic
process_files(files, scheme)
if __name__ == "__main__":
main()
-64
View File
@@ -1,64 +0,0 @@
import os
import sys
import glob
def process_files(files, scheme):
scheme_len = len(scheme)
for filename in files:
print(f"Processing file: {filename}")
print(f"Against scheme: {scheme}")
# Get the actual filename without the directory path
base = os.path.basename(filename)
name, ext = os.path.splitext(base)
if scheme_len == len(name):
new_name = ""
for i in range(scheme_len):
current_letter = scheme[i]
if current_letter == "?":
pass
elif current_letter == "!":
new_name += name[i]
elif current_letter == name[i]:
new_name += current_letter
else:
print(f"Mismatch at position {i}, skipping {filename}")
new_name = None
break
if new_name:
rename_file(filename, new_name, ext)
else:
print("No new name Generated. It is not supposed to do that")
return
def rename_file(file, new_name, ext):
dirname = os.path.dirname(file) # correct: preserve folder
old_base = os.path.basename(file)
if old_base != new_name:
new_filepath = os.path.join(dirname, new_name + ext)
os.rename(file, new_filepath)
print(f"Renamed '{old_base}''{new_name + ext}'")
else:
print(f"No renaming needed for '{old_base}'.")
def main():
if len(sys.argv) < 2:
print("Usage: scheme.py <scheme> [folder]")
sys.exit(1)
scheme = sys.argv[1]
folder = sys.argv[2] if len(sys.argv) > 2 else "."
# Get all files in the folder (assuming files are in the current directory by default)
files = glob.glob(os.path.join(folder, "*"))
# Run the renaming logic
process_files(files, scheme)
if __name__ == "__main__":
main()
-47
View File
@@ -1,47 +0,0 @@
#!/usr/bin/env python3
"""
rename_to_first4.py
Usage:
python3 rename_to_first4.py <directory>
Renames each file in <directory> so its name becomes the first
four characters of the original filename (before the extension).
Keeps the file extension and avoids overwriting existing files.
"""
import os
import sys
from pathlib import Path
def main():
if len(sys.argv) != 2:
print("Usage: rename_to_first4.py <directory>")
sys.exit(1)
directory = Path(sys.argv[1])
if not directory.is_dir():
print(f"Error: '{directory}' is not a directory.")
sys.exit(1)
for f in directory.iterdir():
if not f.is_file():
continue
stem = f.stem[:4] # first 4 letters of filename (no extension)
new_name = f"{stem}{f.suffix}"
new_path = directory / new_name
# avoid overwriting existing files
counter = 1
while new_path.exists():
new_name = f"{stem}_{counter}{f.suffix}"
new_path = directory / new_name
counter += 1
print(f"Renaming: {f.name} -> {new_name}")
f.rename(new_path)
print("✅ Done renaming all files.")
if __name__ == "__main__":
main()
+57
View File
@@ -0,0 +1,57 @@
#!/usr/bin/env python3
"""
rename_to_first_n.py
Usage:
python3 rename_to_first_n.py <number> <directory>
Renames each file in <directory> so its name becomes the first
four characters of the original filename (before the extension).
Keeps the file extension and avoids overwriting existing files.
"""
import os
import sys
from pathlib import Path
def main():
if len(sys.argv) < 2:
print("Usage: rename_to_first4.py <number> <directory>")
sys.exit(1)
import subprocess
subprocess.run(["python", "/home/honney/.bin/tracker.py", "add", "rename_to_first_n"])
try:
length = int(sys.argv[1])
except:
raise ValueError(f"Error: '{sys.argv[1]}' is not a number.")
try:
directory = Path(sys.argv[2]) if len(sys.argv) > 2 else Path(os.getcwd())
except:
raise ValueError(f"Error: '{sys.argv[2] if len(sys.argv) > 2 else os.getcwd()}' is not a directory.")
if not directory.is_dir():
raise ValueError(f"Error: '{directory}' is not a directory.")
for f in directory.iterdir():
if not f.is_file():
continue
original_stem = f.stem
if len(original_stem) < length:
print(f"Skipping {f.name} because it is less than {length} characters.")
continue
stem = original_stem[:length] # first n letters of filename (no extension)
new_name = f"{stem}{f.suffix}"
new_path = directory / new_name
# avoid overwriting existing files
if new_path.exists():
print(f"File {new_name} already exists. Skipping.")
continue
print(f"Renaming: {f.name} -> {new_name}")
f.rename(new_path)
print("✅ Done renaming all files.")
if __name__ == "__main__":
main()
+1
View File
@@ -36,6 +36,7 @@ def main():
else:
targets = sys.argv[1:]
lang = "German" # Hardcoded to German
subprocess.run(["python", "/home/honney/.bin/tracker.py", "add", "set_1_ger"])
lang = normalize_language(lang)
lang_code = normalize_lang_code(lang)
+2 -1
View File
@@ -102,11 +102,12 @@ def main():
else:
targets = sys.argv[1:-1]
lang = sys.argv[-1]
subprocess.run(["python", "/home/honney/.bin/tracker.py", "add", "set_audio_lang"])
lang = normalize_language(lang)
for f in collect_files(targets):
set_audio_lang(f, lang)
if __name__ == "__main__":
main()
+1 -1
View File
@@ -1,3 +1,3 @@
#!/bin/bash
/home/honney/.bin/tracker.py add show_path
echo $PATH | tr ":" "\n" | nl
+1
View File
@@ -338,6 +338,7 @@ process_file() {
echo -e "\t$subtitle_output"
}
/home/honney/.bin/tracker.py add simple_ffprobe_script
# Main script logic
if [ -z "$1" ]; then
+1
View File
@@ -115,6 +115,7 @@ if __name__ == "__main__":
if len(sys.argv) < 3:
print("Usage: python split_mkv_by_chapter.py <input_file> <timestamp1> <timestamp2> ...")
sys.exit(1)
subprocess.run(["python", "/home/honney/.bin/tracker.py", "add", "split_mkv_by_chapter"])
input_file = sys.argv[1]
timestamps = sys.argv[2:]
+94
View File
@@ -0,0 +1,94 @@
#!/usr/bin/env python3
import sys
import re
from pathlib import Path
EP_REGEX = re.compile(r'(E)(\d{4})(.*)')
def main():
args = sys.argv[1:]
dry_run = False
if args and args[0] in ("--dry-run", "-n"):
dry_run = True
args = args[1:]
if len(args) != 3:
print("Usage: script.py [--dry-run|-n] <directory> <threshold> <subtract>")
sys.exit(1)
directory = Path(args[0])
threshold = int(args[1])
subtract = int(args[2])
if not directory.is_dir():
print(f"Error: '{directory}' is not a directory.")
sys.exit(1)
# Step 1: build array of original filenames and parsed numbers
old_array = []
for f in sorted(directory.iterdir()):
if not f.is_file():
continue
m = EP_REGEX.match(f.name)
if m:
prefix, num_str, rest = m.groups()
num = int(num_str)
old_array.append([prefix, num, rest])
# Step 2: compute new filenames
new_array = []
for prefix, num, rest in old_array:
if num >= threshold:
new_num = num - subtract
else:
new_num = num
if new_num < 0 or new_num > 9999:
raise ValueError(f"Invalid resulting number for file: {prefix}{num:04d}{rest}")
new_array.append([prefix, new_num, rest])
if subtract < 0:
new_array.reverse()
old_array.reverse()
# Step 3: sequential rename simulation / real rename
for i in range(len(old_array)):
old_name = f"{old_array[i][0]}{old_array[i][1]:04d}{old_array[i][2]}"
new_name = f"{new_array[i][0]}{new_array[i][1]:04d}{new_array[i][2]}"
for j in range(i):
if old_array[j][0] == new_array[i][0] and old_array[j][1] == new_array[i][1] and old_array[j][2] == new_array[i][2]:
raise RuntimeError(
f"Rename conflict in simulation:\n Source: {old_name}\n Target: {new_name}\n Conflicts with: {old_array[j][0]}{old_array[j][1]:04d}{old_array[j][2]}"
)
if old_array[j][1] > new_array[i][1]:
break
print(f"\"{old_name}\" -> \"{new_name}\"", end=" ")
if old_name == new_name:
print("Skipped")
continue
else:
print()
# Update simulated old_array to reflect that this file has been "renamed"
old_array[i][1] = new_array[i][1]
# Actually rename unless dry-run
if not dry_run:
target_path = directory / new_name
if target_path.exists():
raise RuntimeError(f"Rename would overwrite existing file on disk:\n Source: {old_name}\n Target: {new_name}")
(directory / old_name).rename(target_path)
if dry_run:
print("Dry-run complete. No files were renamed.")
else:
print("Done.")
if __name__ == "__main__":
import subprocess
subprocess.run(["python", "/home/honney/.bin/tracker.py", "add", "substract_episode_num"])
main()
+2
View File
@@ -16,6 +16,8 @@ if [ "$#" -ne 2 ]; then
exit 1
fi
/home/honney/.bin/tracker.py add sync_delete
TARGET_DIR="$1"
REFERENCE_DIR="$2"
+2
View File
@@ -1,5 +1,6 @@
import re
from datetime import timedelta
import subprocess
def parse_timecode(timecode):
parts = list(map(int, timecode.split(':')))
@@ -18,6 +19,7 @@ def add_timecodes(*timecodes):
return f"{hours:02}:{minutes:02}:{seconds:02}"
if __name__ == "__main__":
subprocess.run(["python", "/home/honney/.bin/tracker.py", "add", "time_add"])
timecodes = input("Enter timecodes separated by spaces: ").split()
total = add_timecodes(*timecodes)
print("Total time:", total)
Executable
+115
View File
@@ -0,0 +1,115 @@
#!/usr/bin/env python3
#
# Usage:
#
# Python: subprocess.run(["python", "/home/honney/.bin/tracker.py", "add", "name"])
# Bash: /home/honney/.bin/tracker.py add my_script
#
import sys
import csv
import os
DB_FILE = "/home/honney/.bin/data/tracker.csv"
def load_data():
data = {}
if not os.path.exists(DB_FILE):
return data
with open(DB_FILE, newline="") as f:
reader = csv.reader(f)
for row in reader:
if len(row) != 2:
continue
name, count = row
data[name] = int(count)
return data
def save_data(data):
with open(DB_FILE, "w", newline="") as f:
writer = csv.writer(f)
for name, count in data.items():
writer.writerow([name, count])
def add_entry(name):
data = load_data()
data[name] = data.get(name, 0) + 1
save_data(data)
# print(f"{name}: {data[name]}")
def sub_entry(name, amount=1):
data = load_data()
if name not in data:
print(f"{name} not found.")
return
data[name] -= amount
if data[name] <= 0:
del data[name] # remove entry completely
# alternatively: data[name] = 0
save_data(data)
def show_ranking():
data = load_data()
if not data:
print("No data yet.")
return
sorted_data = sorted(data.items(), key=lambda x: x[1], reverse=True)
print("Ranking:")
for i, (name, count) in enumerate(sorted_data, start=1):
print(f"{i}. {name} - {count}")
def main():
if len(sys.argv) < 2:
print("Usage:")
print(" tracker.py add <name>")
print(" tracker.py sub <name> (<amount>)")
print(" tracker.py ranking")
sys.exit(1)
mode = sys.argv[1]
if mode == "add":
if len(sys.argv) < 3:
print("Error: Missing name")
sys.exit(1)
name = sys.argv[2]
add_entry(name)
elif mode == "sub":
if len(sys.argv) < 3:
print("Error: Missing name")
sys.exit(1)
name = sys.argv[2]
amount = 1
if len(sys.argv) >= 4:
try:
amount = int(sys.argv[3])
except ValueError:
print("Error: amount must be a number")
sys.exit(1)
sub_entry(name, amount)
elif mode == "ranking":
show_ranking()
else:
print(f"Unknown mode: {mode}")
sys.exit(1)
if __name__ == "__main__":
main()