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 exit 1
fi fi
/home/honney/.bin/tracker.py add append
# Process files # Process files
for file in "$folder"/*; do for file in "$folder"/*; do
[[ -f "$file" ]] || continue # skip non-files [[ -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 "_" SYMBOL="_" # Default separator is "_"
fi fi
/home/honney/.bin/tracker.py add create_folder_counting
# Ensure the target directory exists # Ensure the target directory exists
mkdir -p "$TARGET_DIR" mkdir -p "$TARGET_DIR"
+2
View File
@@ -6,6 +6,8 @@ if [ "$#" -lt 2 ] || [ "$#" -gt 3 ]; then
exit 1 exit 1
fi fi
/home/honney/.bin/tracker.py add create_folder_date
# Set folder path # Set folder path
if [ "$#" -eq 3 ]; then if [ "$#" -eq 3 ]; then
TARGET_DIR="$1" 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: if len(sys.argv) < 3:
print("Usage: deappend.py <mode> <string> [directory]") print("Usage: deappend.py <mode> <string> [directory]")
sys.exit(1) sys.exit(1)
import subprocess
subprocess.run(["python", "/home/honney/.bin/tracker.py", "add", "deappend"])
# Get arguments # Get arguments
mode = sys.argv[1] # Mode: back, front, ext/extension mode = sys.argv[1] # Mode: back, front, ext/extension
+2
View File
@@ -9,6 +9,8 @@ if [ ! -f "$INPUT" ]; then
exit 1 exit 1
fi fi
/home/honney/.bin/tracker.py add deinterlace_cpu
# Step 1: Try runtime detection with idet # Step 1: Try runtime detection with idet
ffmpeg -hide_banner -vstats -nostats \ ffmpeg -hide_banner -vstats -nostats \
-i "$INPUT" \ -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" OTPUT="${INPUT%.*}_deinterlaced_vaapi.mp4"
TEMP_STATS=$(mktemp) 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" 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") SUMMARY_LINE=$(grep "Multi frame detection" "$TEMP_STATS")
+87 -13
View File
@@ -324,13 +324,64 @@ class subtitles:
return " ".join(parts) 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): def get_media_info(file):
cmd = [ cmd = [
"ffprobe", "ffprobe",
"-v", "error", "-v", "error",
"-show_entries", "-show_entries",
( (
"format=duration:" "format=duration:format_tags=title:"
"stream=index,codec_type,codec_name," "stream=index,codec_type,codec_name,"
"width,height,r_frame_rate,bit_rate,duration,nb_frames," "width,height,r_frame_rate,bit_rate,duration,nb_frames,"
"pix_fmt,field_order,time_base,display_aspect_ratio," "pix_fmt,field_order,time_base,display_aspect_ratio,"
@@ -348,13 +399,18 @@ def get_media_info(file):
info = json.loads(result.stdout) info = json.loads(result.stdout)
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
print(f"Error running ffprobe: {e.stderr}") print(f"Error running ffprobe: {e.stderr}")
return None, [], [], [] return None, [], [], [], None
# Container / file duration (string seconds, per ffprobe convention) # Duration
duration = None duration = None
if "format" in info: if "format" in info:
duration = info["format"].get("duration") duration = info["format"].get("duration")
# ✅ Extract container title
title = None
if "format" in info:
title = info["format"].get("tags", {}).get("title")
video_streams = [] video_streams = []
audio_streams = [] audio_streams = []
subtitle_streams = [] subtitle_streams = []
@@ -368,16 +424,27 @@ def get_media_info(file):
elif stream_type == "subtitle": elif stream_type == "subtitle":
subtitle_streams.append(stream) 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): def seconds_to_hms(seconds):
h = int(seconds // 3600) if type(seconds) is float:
m = int((seconds % 3600) // 60) h = int(seconds // 3600)
s = int(seconds % 60) m = int((seconds % 3600) // 60)
return f"{h:02}:{m:02}:{s:02}" s = int(seconds % 60)
return f"{h:02}:{m:02}:{s:02}"
else:
return "ERROR"
def get_stream_bitrate(file_size, duration): 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: class video_file:
def __init__(self, path, base_tab=""): def __init__(self, path, base_tab=""):
@@ -386,7 +453,7 @@ class video_file:
self.name = os.path.basename(path) # 25.mkv self.name = os.path.basename(path) # 25.mkv
self.size = os.path.getsize(path) 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_video_info(videos)
self.sort_audio_info(audios) self.sort_audio_info(audios)
@@ -395,6 +462,7 @@ class video_file:
self.bitrate = get_stream_bitrate(self.size, self.duration) self.bitrate = get_stream_bitrate(self.size, self.duration)
self.size = human_readable_size(self.size) # 198MB self.size = human_readable_size(self.size) # 198MB
self.duration = seconds_to_hms(self.duration) self.duration = seconds_to_hms(self.duration)
self.title = title
def sort_video_info(self, videos): def sort_video_info(self, videos):
self.videos = [] self.videos = []
@@ -422,6 +490,8 @@ class video_file:
else: else:
np(f"{os.path.dirname(self.path)}/", INFO_STYLE) np(f"{os.path.dirname(self.path)}/", INFO_STYLE)
np(f"\t{self.name} ({self.size}, {self.duration}, {self.bitrate} MB/s):", NORMAL_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: for video in self.videos:
np(f"\t\t{video}", NORMAL_STYLE) np(f"\t\t{video}", NORMAL_STYLE)
for audio in self.audios: for audio in self.audios:
@@ -484,14 +554,15 @@ def handle_folders(dirs, all_files):
file = file.path file = file.path
if check_video_ext(os.path.splitext(file)[1]): if check_video_ext(os.path.splitext(file)[1]):
dir_files.append(file) dir_files.append(file)
else: # else:
np(f"{file} is not a compatabile Video file", WARN_STYLE) # np(f"{file} is not a compatabile Video file", WARN_STYLE)
if(dir_files != []): if(dir_files != []):
dir_files.sort() dir_files.sort()
all_files.append(dir_files) all_files.append(dir_files)
if __name__ == "__main__": if __name__ == "__main__":
# print(sys.argv) # print(sys.argv)
subprocess.run(["python", "/home/honney/.bin/tracker.py", "add", "ff"])
file_dir_array = [] file_dir_array = []
if len(sys.argv) == 0: if len(sys.argv) == 0:
print("Something went horribly wrong!") print("Something went horribly wrong!")
@@ -504,7 +575,10 @@ if __name__ == "__main__":
dirs = [] dirs = []
for argv in sys.argv[1:]: for argv in sys.argv[1:]:
if os.path.isfile(argv): 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): elif os.path.isdir(argv):
dirs.append(os.path.abspath(argv)) dirs.append(os.path.abspath(argv))
else: else:
+2
View File
@@ -6,6 +6,8 @@ if [ -z "$1" ]; then
exit 1 exit 1
fi fi
/home/honney/.bin/tracker.py add ffbitrate
# Get the video file path from the argument # Get the video file path from the argument
VIDEO_FILE="$1" VIDEO_FILE="$1"
+2
View File
@@ -11,6 +11,8 @@ if [ "$#" -lt 2 ]; then
usage usage
fi fi
/home/honney/.bin/tracker.py add ffsplit
# Get the input file and validate its existence # Get the input file and validate its existence
input_file="$1" input_file="$1"
if [ ! -f "$input_file" ]; then if [ ! -f "$input_file" ]; then
+2
View File
@@ -20,6 +20,8 @@ if [[ ! -d "$DIR" ]]; then
exit 1 exit 1
fi fi
/home/honney/.bin/tracker.py add ff_testing
echo "Scanning directory: $DIR" echo "Scanning directory: $DIR"
for ext in "${EXTENSIONS[@]}"; do for ext in "${EXTENSIONS[@]}"; do
# Find files with the current extension # Find files with the current extension
+2
View File
@@ -47,3 +47,5 @@ for dir in */; do
done done
fi fi
done 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" rm -rf "$workdir" "$trimmed"
done done
/home/honney/.bin/tracker.py add generate-thumb
########## Using full video duration ########## ########## Using full video duration ##########
+1
View File
@@ -49,3 +49,4 @@ list_non_empty_directories() {
# Start listing from the current directory # Start listing from the current directory
list_non_empty_directories "." 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 def process_files(files, scheme):
if [ -z "$1" ]; then scheme_len = len(scheme)
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"
exit 1 for filename in files:
fi print(f"Processing file: {filename}")
print(f"Against scheme: {scheme}")
# Call the Python script with scheme and optional folder argument # Get the actual filename without the directory path
python3 /home/honney/.bin/rename_filtered.py "$@" 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: else:
targets = sys.argv[1:] targets = sys.argv[1:]
lang = "German" # Hardcoded to German lang = "German" # Hardcoded to German
subprocess.run(["python", "/home/honney/.bin/tracker.py", "add", "set_1_ger"])
lang = normalize_language(lang) lang = normalize_language(lang)
lang_code = normalize_lang_code(lang) lang_code = normalize_lang_code(lang)
+2 -1
View File
@@ -102,11 +102,12 @@ def main():
else: else:
targets = sys.argv[1:-1] targets = sys.argv[1:-1]
lang = sys.argv[-1] lang = sys.argv[-1]
subprocess.run(["python", "/home/honney/.bin/tracker.py", "add", "set_audio_lang"])
lang = normalize_language(lang) lang = normalize_language(lang)
for f in collect_files(targets): for f in collect_files(targets):
set_audio_lang(f, lang) set_audio_lang(f, lang)
if __name__ == "__main__": if __name__ == "__main__":
main() main()
+1 -1
View File
@@ -1,3 +1,3 @@
#!/bin/bash #!/bin/bash
/home/honney/.bin/tracker.py add show_path
echo $PATH | tr ":" "\n" | nl echo $PATH | tr ":" "\n" | nl
+1
View File
@@ -338,6 +338,7 @@ process_file() {
echo -e "\t$subtitle_output" echo -e "\t$subtitle_output"
} }
/home/honney/.bin/tracker.py add simple_ffprobe_script
# Main script logic # Main script logic
if [ -z "$1" ]; then if [ -z "$1" ]; then
+1
View File
@@ -115,6 +115,7 @@ if __name__ == "__main__":
if len(sys.argv) < 3: if len(sys.argv) < 3:
print("Usage: python split_mkv_by_chapter.py <input_file> <timestamp1> <timestamp2> ...") print("Usage: python split_mkv_by_chapter.py <input_file> <timestamp1> <timestamp2> ...")
sys.exit(1) sys.exit(1)
subprocess.run(["python", "/home/honney/.bin/tracker.py", "add", "split_mkv_by_chapter"])
input_file = sys.argv[1] input_file = sys.argv[1]
timestamps = sys.argv[2:] 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 exit 1
fi fi
/home/honney/.bin/tracker.py add sync_delete
TARGET_DIR="$1" TARGET_DIR="$1"
REFERENCE_DIR="$2" REFERENCE_DIR="$2"
+2
View File
@@ -1,5 +1,6 @@
import re import re
from datetime import timedelta from datetime import timedelta
import subprocess
def parse_timecode(timecode): def parse_timecode(timecode):
parts = list(map(int, timecode.split(':'))) parts = list(map(int, timecode.split(':')))
@@ -18,6 +19,7 @@ def add_timecodes(*timecodes):
return f"{hours:02}:{minutes:02}:{seconds:02}" return f"{hours:02}:{minutes:02}:{seconds:02}"
if __name__ == "__main__": if __name__ == "__main__":
subprocess.run(["python", "/home/honney/.bin/tracker.py", "add", "time_add"])
timecodes = input("Enter timecodes separated by spaces: ").split() timecodes = input("Enter timecodes separated by spaces: ").split()
total = add_timecodes(*timecodes) total = add_timecodes(*timecodes)
print("Total time:", total) 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()