diff --git a/append b/append index 53216b6..4dc1f02 100755 --- a/append +++ b/append @@ -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 diff --git a/count_files b/count_files new file mode 100755 index 0000000..3f8c058 --- /dev/null +++ b/count_files @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 +""" +Usage: + python3 count_files () () + python3 count_files () () --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 () [--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() diff --git a/create_folder_counting b/create_folder_counting index 1cfa1af..2ffbc47 100755 --- a/create_folder_counting +++ b/create_folder_counting @@ -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" diff --git a/create_folder_date b/create_folder_date index f11671d..230e275 100755 --- a/create_folder_date +++ b/create_folder_date @@ -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" diff --git a/data/tracker.csv b/data/tracker.csv new file mode 100644 index 0000000..78288ba --- /dev/null +++ b/data/tracker.csv @@ -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 diff --git a/deappend b/deappend index 912ccad..b661210 100755 --- a/deappend +++ b/deappend @@ -63,6 +63,8 @@ def main(): if len(sys.argv) < 3: print("Usage: deappend.py [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 diff --git a/deinterlacing_cpu.sh b/deinterlacing_cpu.sh index 792c696..fb8a846 100755 --- a/deinterlacing_cpu.sh +++ b/deinterlacing_cpu.sh @@ -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" \ diff --git a/deinterlacing_gpu.sh b/deinterlacing_gpu.sh index c6508c3..9d15ebc 100755 --- a/deinterlacing_gpu.sh +++ b/deinterlacing_gpu.sh @@ -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") diff --git a/ff b/ff index a68c5f1..93b232f 100755 --- a/ff +++ b/ff @@ -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: diff --git a/ffbitrate b/ffbitrate index f3ca62a..5808dbb 100755 --- a/ffbitrate +++ b/ffbitrate @@ -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" diff --git a/ffsplit b/ffsplit index f8bd2c4..8521ce7 100755 --- a/ffsplit +++ b/ffsplit @@ -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 diff --git a/fftesting b/fftesting index 6efd42b..3bbbab6 100755 --- a/fftesting +++ b/fftesting @@ -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 diff --git a/filter_extra b/filter_extra index 1786627..7267e05 100755 --- a/filter_extra +++ b/filter_extra @@ -47,3 +47,5 @@ for dir in */; do done fi done + +/home/honney/.bin/tracker.py add filter_extra diff --git a/generate-thumb b/generate-thumb index efa6263..81286b8 100755 --- a/generate-thumb +++ b/generate-thumb @@ -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 ########## diff --git a/list_folder_empty b/list_folder_empty index 54f4ccf..f28b953 100755 --- a/list_folder_empty +++ b/list_folder_empty @@ -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 \ No newline at end of file diff --git a/nohup.out b/nohup.out deleted file mode 100755 index fa7034a..0000000 --- a/nohup.out +++ /dev/null @@ -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 diff --git a/.broken_episode_numbers b/not_sure/.broken_episode_numbers similarity index 100% rename from .broken_episode_numbers rename to not_sure/.broken_episode_numbers diff --git a/embed_audio b/not_sure/embed_audio similarity index 100% rename from embed_audio rename to not_sure/embed_audio diff --git a/embed_subtitles b/not_sure/embed_subtitles similarity index 100% rename from embed_subtitles rename to not_sure/embed_subtitles diff --git a/embed_subtitles.py b/not_sure/embed_subtitles.py similarity index 100% rename from embed_subtitles.py rename to not_sure/embed_subtitles.py diff --git a/extract_audio b/not_sure/extract_audio similarity index 100% rename from extract_audio rename to not_sure/extract_audio diff --git a/extract_audio.py b/not_sure/extract_audio.py similarity index 100% rename from extract_audio.py rename to not_sure/extract_audio.py diff --git a/extract_subtitles b/not_sure/extract_subtitles similarity index 100% rename from extract_subtitles rename to not_sure/extract_subtitles diff --git a/extract_subtitles.py b/not_sure/extract_subtitles.py similarity index 100% rename from extract_subtitles.py rename to not_sure/extract_subtitles.py diff --git a/grayjay b/not_sure/grayjay similarity index 100% rename from grayjay rename to not_sure/grayjay diff --git a/rename_episodes b/not_sure/rename_episodes similarity index 100% rename from rename_episodes rename to not_sure/rename_episodes diff --git a/set_subtitle_lang[untested] b/not_sure/set_subtitle_lang[untested] similarity index 100% rename from set_subtitle_lang[untested] rename to not_sure/set_subtitle_lang[untested] diff --git a/sub_dub b/not_sure/sub_dub similarity index 100% rename from sub_dub rename to not_sure/sub_dub diff --git a/sub_dub.py b/not_sure/sub_dub.py similarity index 100% rename from sub_dub.py rename to not_sure/sub_dub.py diff --git a/video-audio b/not_sure/video-audio similarity index 100% rename from video-audio rename to not_sure/video-audio diff --git a/recount_files b/recount_files new file mode 100755 index 0000000..499b331 --- /dev/null +++ b/recount_files @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +""" +Usage: + python3 recount_files.py () +""" + +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 ()") + 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() diff --git a/remove_fname_last_n b/remove_fname_last_n new file mode 100755 index 0000000..ce00ad7 --- /dev/null +++ b/remove_fname_last_n @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +""" +Usage: + remove_fname_last_n () +""" + +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 ()") + 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() \ No newline at end of file diff --git a/rename_filtered b/rename_filtered index 767e085..855e4b3 100755 --- a/rename_filtered +++ b/rename_filtered @@ -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 [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 [folder]") + sys.exit(1) + else: + print("Usage: rename_filtered [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() diff --git a/rename_filtered.py b/rename_filtered.py deleted file mode 100644 index b00eba9..0000000 --- a/rename_filtered.py +++ /dev/null @@ -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 [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() diff --git a/rename_to_first4.py b/rename_to_first4.py deleted file mode 100755 index f33ef71..0000000 --- a/rename_to_first4.py +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env python3 -""" -rename_to_first4.py - -Usage: - python3 rename_to_first4.py - -Renames each file in 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 ") - 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() diff --git a/rename_to_first_n b/rename_to_first_n new file mode 100755 index 0000000..d7e2911 --- /dev/null +++ b/rename_to_first_n @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +""" +rename_to_first_n.py + +Usage: + python3 rename_to_first_n.py + +Renames each file in 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 ") + 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() diff --git a/set_1_ger b/set_1_ger index d3622e9..c392fab 100755 --- a/set_1_ger +++ b/set_1_ger @@ -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) diff --git a/set_audio_lang b/set_audio_lang index 88d103c..9858902 100755 --- a/set_audio_lang +++ b/set_audio_lang @@ -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() diff --git a/show_path b/show_path index d2988ec..e2193db 100755 --- a/show_path +++ b/show_path @@ -1,3 +1,3 @@ #!/bin/bash - +/home/honney/.bin/tracker.py add show_path echo $PATH | tr ":" "\n" | nl diff --git a/simple_ffprobe_script b/simple_ffprobe_script index 85e5240..bd6f764 100755 --- a/simple_ffprobe_script +++ b/simple_ffprobe_script @@ -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 diff --git a/split_mkv_by_chapter.py b/split_mkv_by_chapter.py index 03a950d..bbfd7bf 100644 --- a/split_mkv_by_chapter.py +++ b/split_mkv_by_chapter.py @@ -115,6 +115,7 @@ if __name__ == "__main__": if len(sys.argv) < 3: print("Usage: python split_mkv_by_chapter.py ...") 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:] diff --git a/substract_episode_num.py b/substract_episode_num.py new file mode 100644 index 0000000..7204155 --- /dev/null +++ b/substract_episode_num.py @@ -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] ") + 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() \ No newline at end of file diff --git a/sync_delete b/sync_delete index 3aef7fc..4f2a712 100755 --- a/sync_delete +++ b/sync_delete @@ -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" diff --git a/time_add.py b/time_add.py index 017de4c..0a6c9db 100644 --- a/time_add.py +++ b/time_add.py @@ -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) diff --git a/tracker.py b/tracker.py new file mode 100755 index 0000000..a059a55 --- /dev/null +++ b/tracker.py @@ -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 ") + print(" tracker.py sub ()") + 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()