Compare commits
3
Commits
477b1bf985
...
6d0daddea5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d0daddea5 | ||
|
|
06c7268308 | ||
|
|
184b99add1 |
@@ -3,6 +3,7 @@ import sys
|
|||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import json
|
import json
|
||||||
|
import argparse
|
||||||
from check_video import check_video_ext, normalize_language, normalize_lang_code
|
from check_video import check_video_ext, normalize_language, normalize_lang_code
|
||||||
|
|
||||||
color = True
|
color = True
|
||||||
@@ -124,7 +125,7 @@ def short_codec_name(codec):
|
|||||||
return fixed_width(new_codec.upper(), 5)
|
return fixed_width(new_codec.upper(), 5)
|
||||||
|
|
||||||
class video_lines:
|
class video_lines:
|
||||||
def __init__(self, stream):
|
def __init__(self, stream, hdr_type):
|
||||||
if stream.get("index"):
|
if stream.get("index"):
|
||||||
self.id = stream.get("index")
|
self.id = stream.get("index")
|
||||||
else:
|
else:
|
||||||
@@ -185,6 +186,11 @@ class video_lines:
|
|||||||
else:
|
else:
|
||||||
self.field_order = ""
|
self.field_order = ""
|
||||||
|
|
||||||
|
if hdr_type:
|
||||||
|
self.hdr_type = hdr_type
|
||||||
|
else:
|
||||||
|
self.hdr_type = ""
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
string = fixed_width("Video", 6)
|
string = fixed_width("Video", 6)
|
||||||
if self.id != None:
|
if self.id != None:
|
||||||
@@ -197,7 +203,10 @@ class video_lines:
|
|||||||
if self.resolution != "x":
|
if self.resolution != "x":
|
||||||
string += f" ({self.resolution}"
|
string += f" ({self.resolution}"
|
||||||
if self.framerate != "x":
|
if self.framerate != "x":
|
||||||
string += f"@{self.framerate})"
|
string += f"@{self.framerate}"
|
||||||
|
string += f" {self.hdr_type})"
|
||||||
|
elif self.hdr_type != "Unknown HDR":
|
||||||
|
string += f" ({self.hdr_type})"
|
||||||
if self.aspect_ratio:
|
if self.aspect_ratio:
|
||||||
string += f" [{self.aspect_ratio}]"
|
string += f" [{self.aspect_ratio}]"
|
||||||
if self.pix_fmt and self.color_space:
|
if self.pix_fmt and self.color_space:
|
||||||
@@ -399,7 +408,7 @@ 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, [], [], [], None
|
return None, [], [], [], None, "UNKNOWN"
|
||||||
|
|
||||||
# Duration
|
# Duration
|
||||||
duration = None
|
duration = None
|
||||||
@@ -424,12 +433,41 @@ def get_media_info(file):
|
|||||||
elif stream_type == "subtitle":
|
elif stream_type == "subtitle":
|
||||||
subtitle_streams.append(stream)
|
subtitle_streams.append(stream)
|
||||||
|
|
||||||
|
# Detect HDR type from the first video stream
|
||||||
|
hdr_types = []
|
||||||
|
|
||||||
|
if video_streams:
|
||||||
|
for video in video_streams:
|
||||||
|
color_transfer = video.get("color_transfer")
|
||||||
|
color_primaries = video.get("color_primaries")
|
||||||
|
|
||||||
|
if color_transfer == "smpte2084":
|
||||||
|
hdr_types.append("HDR10")
|
||||||
|
|
||||||
|
elif color_transfer == "arib-std-b67":
|
||||||
|
hdr_types.append("HLG")
|
||||||
|
|
||||||
|
elif (
|
||||||
|
color_transfer == "bt709"
|
||||||
|
and color_primaries == "bt709"
|
||||||
|
):
|
||||||
|
hdr_types.append("SDR")
|
||||||
|
else:
|
||||||
|
hdr_types.append("Unknown HDR")
|
||||||
|
|
||||||
if duration is not None:
|
if duration is not None:
|
||||||
duration = float(duration)
|
duration = float(duration)
|
||||||
else:
|
else:
|
||||||
duration = float('nan')
|
duration = float('nan')
|
||||||
|
|
||||||
return duration, video_streams, audio_streams, subtitle_streams, title
|
return (
|
||||||
|
duration,
|
||||||
|
video_streams,
|
||||||
|
audio_streams,
|
||||||
|
subtitle_streams,
|
||||||
|
title,
|
||||||
|
hdr_types
|
||||||
|
)
|
||||||
|
|
||||||
def seconds_to_hms(seconds):
|
def seconds_to_hms(seconds):
|
||||||
if type(seconds) is float:
|
if type(seconds) is float:
|
||||||
@@ -453,9 +491,9 @@ 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, title = get_media_info(path)
|
self.duration, videos, audios, subs, title, hdr_types = get_media_info(path)
|
||||||
|
|
||||||
self.sort_video_info(videos)
|
self.sort_video_info(videos, hdr_types)
|
||||||
self.sort_audio_info(audios)
|
self.sort_audio_info(audios)
|
||||||
self.sort_subs_info(subs)
|
self.sort_subs_info(subs)
|
||||||
|
|
||||||
@@ -464,11 +502,11 @@ class video_file:
|
|||||||
self.duration = seconds_to_hms(self.duration)
|
self.duration = seconds_to_hms(self.duration)
|
||||||
self.title = title
|
self.title = title
|
||||||
|
|
||||||
def sort_video_info(self, videos):
|
def sort_video_info(self, videos, hdr_types):
|
||||||
self.videos = []
|
self.videos = []
|
||||||
if videos:
|
if videos:
|
||||||
for vl in videos:
|
for vl, hdr_t in zip(videos, hdr_types):
|
||||||
video_line = video_lines(vl)
|
video_line = video_lines(vl, hdr_t)
|
||||||
self.videos.append(video_line)
|
self.videos.append(video_line)
|
||||||
|
|
||||||
def sort_audio_info(self, audios):
|
def sort_audio_info(self, audios):
|
||||||
@@ -485,6 +523,7 @@ class video_file:
|
|||||||
self.subtitles.append(subtitle)
|
self.subtitles.append(subtitle)
|
||||||
|
|
||||||
def print(self):
|
def print(self):
|
||||||
|
if not mini:
|
||||||
if self.base_tab == "\t":
|
if self.base_tab == "\t":
|
||||||
np(f"{self.base_tab}{self.name} ({self.size}, {self.duration}, {self.bitrate} MB/s):", NORMAL_STYLE)
|
np(f"{self.base_tab}{self.name} ({self.size}, {self.duration}, {self.bitrate} MB/s):", NORMAL_STYLE)
|
||||||
else:
|
else:
|
||||||
@@ -499,6 +538,16 @@ class video_file:
|
|||||||
for subtitle in self.subtitles:
|
for subtitle in self.subtitles:
|
||||||
np(f"\t\t{subtitle}", NORMAL_STYLE)
|
np(f"\t\t{subtitle}", NORMAL_STYLE)
|
||||||
print()
|
print()
|
||||||
|
else:
|
||||||
|
if self.base_tab == "\t":
|
||||||
|
np(f"{self.base_tab}{self.name} ({self.size}, {self.duration}, {self.bitrate} MB/s):", NORMAL_STYLE)
|
||||||
|
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)
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_folder_info(files):
|
def get_folder_info(files):
|
||||||
@@ -561,24 +610,30 @@ def handle_folders(dirs, all_files):
|
|||||||
all_files.append(dir_files)
|
all_files.append(dir_files)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# print(sys.argv)
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("-m", "--mini", action="store_true", help="Enable mini mode")
|
||||||
|
parser.add_argument("paths", nargs="*", help="Files or directories to process")
|
||||||
|
args = parser.parse_args()
|
||||||
|
mini = args.mini
|
||||||
|
# print(args)
|
||||||
subprocess.run(["python", "/home/honney/.bin/tracker.py", "add", "ff"])
|
subprocess.run(["python", "/home/honney/.bin/tracker.py", "add", "ff"])
|
||||||
file_dir_array = []
|
file_dir_array = []
|
||||||
if len(sys.argv) == 0:
|
# No paths were provided: use the current directory
|
||||||
print("Something went horribly wrong!")
|
if not args.paths:
|
||||||
if len(sys.argv) == 1:
|
|
||||||
# current_dir = os.path.dirname(os.path.realpath(__file__))
|
|
||||||
current_dir = os.getcwd()
|
current_dir = os.getcwd()
|
||||||
handle_folders([os.path.abspath(current_dir)], file_dir_array)
|
handle_folders([os.path.abspath(current_dir)], file_dir_array)
|
||||||
else:
|
else:
|
||||||
files = []
|
files = []
|
||||||
dirs = []
|
dirs = []
|
||||||
for argv in sys.argv[1:]:
|
for argv in args.paths:
|
||||||
if os.path.isfile(argv):
|
if os.path.isfile(argv):
|
||||||
if check_video_ext(os.path.splitext(argv)[1]):
|
if check_video_ext(os.path.splitext(argv)[1]):
|
||||||
files.append(os.path.abspath(argv))
|
files.append(os.path.abspath(argv))
|
||||||
# else:
|
# else:
|
||||||
# np(f"{os.path.abspath(argv)} is not a compatabile Video file", WARN_STYLE)
|
# np(
|
||||||
|
# f"{os.path.abspath(argv)} is not a compatible 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:
|
||||||
@@ -586,7 +641,6 @@ if __name__ == "__main__":
|
|||||||
sys.exit()
|
sys.exit()
|
||||||
handle_folders(dirs, file_dir_array)
|
handle_folders(dirs, file_dir_array)
|
||||||
handle_files(files, file_dir_array)
|
handle_files(files, file_dir_array)
|
||||||
|
|
||||||
for element in file_dir_array:
|
for element in file_dir_array:
|
||||||
if type(element) == list:
|
if type(element) == list:
|
||||||
get_folder_info(element)
|
get_folder_info(element)
|
||||||
|
|||||||
+61
-15
@@ -18,32 +18,54 @@ def main():
|
|||||||
else:
|
else:
|
||||||
print("Usage: recount_files.py <number> (<directory>)")
|
print("Usage: recount_files.py <number> (<directory>)")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
subprocess.run(["python", "/home/honney/.bin/tracker.py", "add", "recount_files"])
|
subprocess.run(["python", "/home/honney/.bin/tracker.py", "add", "recount_files"])
|
||||||
|
|
||||||
number = int(sys.argv[1])
|
number = int(sys.argv[1])
|
||||||
|
|
||||||
pattern = re.compile(r'^\d+')
|
# Pattern matches numbers (int or float) at the start (Group 1)
|
||||||
|
# and captures the rest of the filename (Group 2)
|
||||||
|
pattern = re.compile(r'^(\d+(?:\.\d+)?)(.*)')
|
||||||
|
|
||||||
files = sorted(
|
files_data = []
|
||||||
[f for f in dir.iterdir() if f.is_file() and pattern.match(f.name)],
|
for f in dir.iterdir():
|
||||||
key=lambda f: float(f.stem)
|
if f.is_file():
|
||||||
)
|
match = pattern.match(f.stem)
|
||||||
|
if match:
|
||||||
|
# Store a tuple: (Path object, numeric_string, rest_of_filename)
|
||||||
|
files_data.append((f, match.group(1), match.group(2)))
|
||||||
|
|
||||||
min_num = int(files[0].stem)
|
if not files_data:
|
||||||
max_len = max(len(files[-1].stem), 2)
|
print("No matching files found.")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
# Sort files based on the numeric part cast to a float
|
||||||
|
files_data = sorted(files_data, key=lambda x: float(x[1]))
|
||||||
|
|
||||||
|
min_num_float = float(files_data[0][1])
|
||||||
|
|
||||||
|
# Calculate padding based on the longest numeric string
|
||||||
|
max_len = max(len(files_data[-1][1]), 2)
|
||||||
|
|
||||||
|
failed = []
|
||||||
|
|
||||||
|
for f, num_str, rest in files_data:
|
||||||
|
# Determine if the current prefix is a float or an int
|
||||||
|
if '.' not in num_str:
|
||||||
|
delta = int(num_str) - int(min_num_float)
|
||||||
|
new_num_str = f"{delta+number:0{max_len}d}"
|
||||||
|
else:
|
||||||
|
delta = float(num_str) - min_num_float
|
||||||
|
new_num_str = f"{delta+number:0{max_len+2}.1f}"
|
||||||
|
|
||||||
|
# Recombine: New Number + Rest of Name + Extension
|
||||||
|
new_file_name = Path(f"{f.parent}/{new_num_str}{rest}{f.suffix}")
|
||||||
|
|
||||||
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():
|
if not new_file_name.is_file():
|
||||||
print(
|
print(
|
||||||
colored("Renamed: ", "green") +
|
colored("Renamed: ", "green") +
|
||||||
colored(str(f.parent), "cyan") +
|
colored(str(f.parent)+"/", "cyan") +
|
||||||
colored("[", "white") +
|
colored("[", "white") +
|
||||||
colored(f.name, "red") +
|
colored(f.name, "red") +
|
||||||
colored(" -> ", "white") +
|
colored(" -> ", "white") +
|
||||||
@@ -52,8 +74,32 @@ def main():
|
|||||||
)
|
)
|
||||||
f.rename(new_file_name)
|
f.rename(new_file_name)
|
||||||
else:
|
else:
|
||||||
|
failed.append([f, new_file_name])
|
||||||
|
print(colored(f"{new_file_name.name} already exists. Retrying later", "red"))
|
||||||
|
|
||||||
|
failed2 = []
|
||||||
|
if failed:
|
||||||
|
failed.reverse()
|
||||||
|
for f, new_file_name in failed:
|
||||||
|
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:
|
||||||
|
failed2.append([f, new_file_name])
|
||||||
print(colored(f"{new_file_name.name} already exists. Aborting", "red"))
|
print(colored(f"{new_file_name.name} already exists. Aborting", "red"))
|
||||||
|
|
||||||
|
if failed2:
|
||||||
|
print(colored("Failed to rename the following files:", "red"))
|
||||||
|
for f, new_file_name in failed2:
|
||||||
|
print(colored(f"{new_file_name.name} already exists. Aborting", "red"))
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
Executable
+19
@@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [ "$#" -lt 1 ]; then
|
||||||
|
echo "Usage: run_in_all_folders command [args...]"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for dir in */; do
|
||||||
|
[ -d "$dir" ] || continue
|
||||||
|
|
||||||
|
echo "==> Running in $dir"
|
||||||
|
|
||||||
|
(
|
||||||
|
cd "$dir" || exit
|
||||||
|
"$@"
|
||||||
|
)
|
||||||
|
done
|
||||||
Reference in New Issue
Block a user