Refresh and added usage tracking
This commit is contained in:
Executable
+80
@@ -0,0 +1,80 @@
|
||||
#!/bin/bash
|
||||
|
||||
LOG_DIR="/home/honney/.log"
|
||||
LOG_FILE="${LOG_DIR}/rename.log"
|
||||
|
||||
mkdir -p "$LOG_DIR"
|
||||
|
||||
reverse_mode=false
|
||||
|
||||
if [[ "$1" == "reverse" ]]; then
|
||||
reverse_mode=true
|
||||
elif [[ -z "$1" ]]; then
|
||||
echo "Usage: $0 {start_number} | reverse"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if $reverse_mode; then
|
||||
if [[ ! -f "$LOG_FILE" ]]; then
|
||||
echo "No log file found. Nothing to reverse."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
current_dir=$(realpath .)
|
||||
match_start=$(grep -n "=== $current_dir ===" "$LOG_FILE" | cut -d: -f1)
|
||||
|
||||
if [[ -z "$match_start" ]]; then
|
||||
echo "No entries for this directory in the log."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sed -n "$((match_start + 1)),/^===/p" "$LOG_FILE" | grep -v "^===" | while IFS="|" read -r original renamed; do
|
||||
if [[ -e "$renamed" ]]; then
|
||||
mv -v "$renamed" "$original"
|
||||
else
|
||||
echo "Warning: '$renamed' not found. Skipping."
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Reversal complete."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Start of normal renaming
|
||||
start_number=$1
|
||||
counter=0
|
||||
|
||||
extensions=("mkv" "mp4" "avi" "mov" "jpg" "png")
|
||||
|
||||
files=()
|
||||
for ext in "${extensions[@]}"; do
|
||||
while IFS= read -r -d '' file; do
|
||||
files+=("$file")
|
||||
done < <(find . -maxdepth 1 -type f -iname "*.${ext}" -print0)
|
||||
done
|
||||
|
||||
IFS=$'\n' sorted_files=($(printf '%s\n' "${files[@]}" | sort -rz | tr '\0' '\n'))
|
||||
|
||||
# Log the current session
|
||||
current_dir=$(realpath .)
|
||||
echo "=== $current_dir ===" >> "$LOG_FILE"
|
||||
|
||||
for file in "${sorted_files[@]}"; do
|
||||
new_number=$(printf "%02d" $((start_number + counter)))
|
||||
extension="${file##*.}"
|
||||
base_name="E${new_number}"
|
||||
new_name="${base_name}.${extension}"
|
||||
suffix=1
|
||||
|
||||
# Avoid overwriting
|
||||
while [ -e "$new_name" ]; do
|
||||
new_name="${base_name}_$suffix.${extension}"
|
||||
((suffix++))
|
||||
done
|
||||
|
||||
mv "$file" "$new_name"
|
||||
echo "${file}|${new_name}" >> "$LOG_FILE"
|
||||
((counter++))
|
||||
done
|
||||
|
||||
echo "Renaming complete! Changes logged to $LOG_FILE"
|
||||
Executable
+78
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/python3
|
||||
import sys
|
||||
import subprocess
|
||||
import os
|
||||
import glob
|
||||
|
||||
def embed_audio(video_file, audio_file, output_file):
|
||||
"""
|
||||
Embeds an audio track into a video file as the first audio track.
|
||||
|
||||
Parameters:
|
||||
video_file (str): Path to the video file.
|
||||
audio_file (str): Path to the audio file.
|
||||
output_file (str): Path to save the output file.
|
||||
"""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
[
|
||||
"ffmpeg", "-i", video_file, "-i", audio_file,
|
||||
"-c:v", "copy", "-c:a", "aac",
|
||||
"-map", "0:v:0", "-map", "1:a:0",
|
||||
"-map", "0:a?", # Add any existing audio tracks as secondary tracks
|
||||
"-shortest", output_file, "-y"
|
||||
],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
|
||||
)
|
||||
if result.returncode != 0:
|
||||
print(f"Error embedding audio for {video_file}: {result.stderr}")
|
||||
else:
|
||||
print(f"Audio successfully embedded: {output_file}")
|
||||
except Exception as e:
|
||||
print(f"An error occurred for {video_file}: {e}")
|
||||
|
||||
def main():
|
||||
# No arguments: auto-pairing mode
|
||||
if len(sys.argv) == 1:
|
||||
video_extensions = ["*.mp4", "*.mkv", "*.avi", "*.mov"]
|
||||
files_processed = 0
|
||||
|
||||
for ext in video_extensions:
|
||||
for video_file in glob.glob(ext):
|
||||
base_name = os.path.splitext(video_file)[0]
|
||||
audio_file = f"{base_name}.mp3"
|
||||
if os.path.isfile(audio_file):
|
||||
output_file = f"{base_name}_with_audio.mp4"
|
||||
print(f"Processing: {video_file} with {audio_file} -> {output_file}")
|
||||
embed_audio(video_file, audio_file, output_file)
|
||||
files_processed += 1
|
||||
else:
|
||||
print(f"No matching audio file found for: {video_file}")
|
||||
|
||||
if files_processed == 0:
|
||||
print("No video-audio pairs found in the current directory.")
|
||||
sys.exit(1)
|
||||
|
||||
# Explicit arguments mode
|
||||
elif len(sys.argv) == 4:
|
||||
video_file = sys.argv[1]
|
||||
audio_file = sys.argv[2]
|
||||
output_file = sys.argv[3]
|
||||
|
||||
if not os.path.isfile(video_file):
|
||||
print(f"Error: Video file '{video_file}' not found.")
|
||||
sys.exit(1)
|
||||
if not os.path.isfile(audio_file):
|
||||
print(f"Error: Audio file '{audio_file}' not found.")
|
||||
sys.exit(1)
|
||||
|
||||
embed_audio(video_file, audio_file, output_file)
|
||||
|
||||
else:
|
||||
print("Usage:")
|
||||
print(" Auto-pairing mode (no arguments): script will pair videos and matching audio in current directory")
|
||||
print(" Explicit mode: <video_file> <audio_file> <output_file>")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Executable
+49
@@ -0,0 +1,49 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Paths to Python scripts
|
||||
SUBTITLE_SCRIPT="$HOME/.bin/auto_embed_subtitles.py"
|
||||
|
||||
# Check both Python scripts exist
|
||||
if [ ! -f "$SUBTITLE_SCRIPT" ]; then
|
||||
echo "Error: Subtitle script '$SUBTITLE_SCRIPT' not found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# === Mode: Manual Audio Embed ===
|
||||
if [ "$#" -eq 3 ]; then
|
||||
echo "Embedding subtitle: $1 + $2 -> $3"
|
||||
python "$SUBTITLE_SCRIPT" "$1" "$2" "$3"
|
||||
exit 0
|
||||
|
||||
# === Mode: Auto Detection ===
|
||||
elif [ "$#" -eq 0 ]; then
|
||||
echo "No arguments provided. Attempting to embed audio and subtitles in the current directory."
|
||||
|
||||
files_processed=0
|
||||
|
||||
# Process audio embeddings
|
||||
for video_file in *.mp4 *.mkv *.avi *.mov; do
|
||||
[ -e "$video_file" ] || continue
|
||||
base_name="${video_file%.*}"
|
||||
audio_file="${base_name}.mp3"
|
||||
|
||||
if [ -f "$audio_file" ]; then
|
||||
output_file="${base_name}_with_audio.mp4"
|
||||
echo "Embedding subtitle: $video_file + $audio_file -> $output_file"
|
||||
python "$SUBTITLE_SCRIPT" "$video_file" "$audio_file" "$output_file"
|
||||
files_processed=1
|
||||
fi
|
||||
done
|
||||
|
||||
# Process subtitle embeddings
|
||||
echo "Scanning for subtitles to embed..."
|
||||
python "$SUBTITLE_SCRIPT" "."
|
||||
|
||||
exit 0
|
||||
|
||||
else
|
||||
echo "Usage:"
|
||||
echo " $0 <video_file> <subtitle_file> <output_file> # Manual subtitel embed"
|
||||
echo " $0 # Auto mode (pair audio + embed subtitles)"
|
||||
exit 1
|
||||
fi
|
||||
@@ -0,0 +1,66 @@
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import re
|
||||
|
||||
def find_matching_subtitles(video_path, search_dir):
|
||||
base_name = os.path.splitext(os.path.basename(video_path))[0]
|
||||
subtitle_files = []
|
||||
for file in os.listdir(search_dir):
|
||||
if re.match(rf"^{re.escape(base_name)}__.*\.srt$", file):
|
||||
subtitle_files.append(os.path.join(search_dir, file))
|
||||
return sorted(subtitle_files)
|
||||
|
||||
def embed_subtitles(video_path, subtitle_paths, output_path):
|
||||
try:
|
||||
cmd = ["ffmpeg", "-i", video_path]
|
||||
|
||||
for subtitle in subtitle_paths:
|
||||
cmd += ["-i", subtitle]
|
||||
|
||||
cmd += ["-c:v", "copy", "-c:a", "copy"]
|
||||
|
||||
if output_path.lower().endswith(".mp4"):
|
||||
cmd += ["-c:s", "mov_text"]
|
||||
else:
|
||||
cmd += ["-c:s", "copy"]
|
||||
|
||||
cmd += ["-map", "0"]
|
||||
for i in range(1, len(subtitle_paths) + 1):
|
||||
cmd += ["-map", str(i)]
|
||||
|
||||
cmd += ["-y", output_path]
|
||||
|
||||
result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||
if result.returncode != 0:
|
||||
print(f"Error embedding subtitles into {video_path}:\n{result.stderr}")
|
||||
else:
|
||||
print(f"Embedded subtitles into: {output_path}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Failed to embed subtitles: {e}")
|
||||
|
||||
def process_path(input_path):
|
||||
if os.path.isfile(input_path):
|
||||
directory = os.path.dirname(input_path)
|
||||
subtitles = find_matching_subtitles(input_path, directory)
|
||||
if subtitles:
|
||||
output_file = os.path.splitext(input_path)[0] + "_subbed" + os.path.splitext(input_path)[1]
|
||||
embed_subtitles(input_path, subtitles, output_file)
|
||||
else:
|
||||
print(f"No matching subtitles found for: {input_path}")
|
||||
elif os.path.isdir(input_path):
|
||||
for file_name in os.listdir(input_path):
|
||||
full_path = os.path.join(input_path, file_name)
|
||||
if os.path.isfile(full_path) and file_name.lower().endswith(('.mp4', '.mkv', '.avi', '.mov')):
|
||||
process_path(full_path)
|
||||
else:
|
||||
print(f"Invalid input path: {input_path}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 2:
|
||||
print("Usage: python auto_embed_subtitles.py <video_file_or_directory>")
|
||||
sys.exit(1)
|
||||
|
||||
input_path = sys.argv[1]
|
||||
process_path(input_path)
|
||||
Executable
+37
@@ -0,0 +1,37 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Get the path to the Python script (adjust if needed)
|
||||
PYTHON_SCRIPT="$HOME/.bin/extract_audio.py"
|
||||
|
||||
# Check if the Python script exists
|
||||
if [ ! -f "$PYTHON_SCRIPT" ]; then
|
||||
echo "Error: Python script '$PYTHON_SCRIPT' not found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if arguments are provided
|
||||
if [ "$#" -eq 0 ]; then
|
||||
echo "No arguments provided. Attempting to process all video files in the current directory."
|
||||
|
||||
# Initialize a flag to track if any files are processed
|
||||
files_processed=0
|
||||
|
||||
# Find all video files in the current directory
|
||||
for video_file in *.mp4 *.mkv *.avi *.mov; do
|
||||
# Check if the file exists (in case no matches were found)
|
||||
if [ -e "$video_file" ]; then
|
||||
echo "Processing: $video_file"
|
||||
python "$PYTHON_SCRIPT" "$video_file"
|
||||
files_processed=1
|
||||
fi
|
||||
done
|
||||
|
||||
# If no files were processed, print a message
|
||||
if [ "$files_processed" -eq 0 ]; then
|
||||
echo "No video files found in the current directory."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
# Run the Python script with the provided arguments
|
||||
python "$PYTHON_SCRIPT" "$@"
|
||||
fi
|
||||
@@ -0,0 +1,94 @@
|
||||
import os
|
||||
import subprocess
|
||||
import re
|
||||
import json
|
||||
|
||||
|
||||
def sanitize_filename(name):
|
||||
"""
|
||||
Removes invalid characters from the filename.
|
||||
"""
|
||||
return re.sub(r'[<>:"/\\|?*@]', '_', name)
|
||||
|
||||
|
||||
def extract_audio_tracks(video_path):
|
||||
"""
|
||||
Extracts all audio tracks from a video file and saves them with their respective titles.
|
||||
|
||||
Parameters:
|
||||
video_path (str): Path to the video file.
|
||||
"""
|
||||
try:
|
||||
# Get the base name and directory of the video file
|
||||
base_name = os.path.splitext(os.path.basename(video_path))[0]
|
||||
directory = os.path.dirname(video_path)
|
||||
|
||||
# Use ffmpeg to get information about the video file
|
||||
probe_cmd = ["ffprobe", "-v", "error", "-show_streams", "-of", "json", video_path]
|
||||
probe_result = subprocess.run(probe_cmd, capture_output=True, text=True, check=True)
|
||||
streams = json.loads(probe_result.stdout).get('streams', [])
|
||||
|
||||
# Process each audio stream
|
||||
audio_streams = [s for s in streams if s.get('codec_type') == 'audio']
|
||||
if not audio_streams:
|
||||
print(f"No audio tracks found in {video_path}.")
|
||||
return
|
||||
|
||||
for i, stream in enumerate(audio_streams):
|
||||
# Extract title or fallback to index
|
||||
title = stream.get('tags', {}).get('title', f"track_{i + 1}")
|
||||
language = stream.get('tags', {}).get('language', 'unknown')
|
||||
sanitized_title = sanitize_filename(title)
|
||||
sanitized_language = sanitize_filename(language)
|
||||
output_name = f"{base_name}__{sanitized_title}__{sanitized_language}.mp3"
|
||||
output_path = os.path.join(directory, output_name)
|
||||
|
||||
# Extract the specific audio stream
|
||||
print(f"Extracting audio: {sanitized_title} ({sanitized_language}) to {output_path}")
|
||||
result = subprocess.run([
|
||||
"ffmpeg", "-i", video_path,
|
||||
"-map", f"0:a:{i}", # Select the specific audio stream
|
||||
"-c:a", "mp3", # Set the codec to mp3
|
||||
output_path,
|
||||
"-y" # Overwrite if exists
|
||||
], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||
|
||||
if result.returncode != 0:
|
||||
print(f"ffmpeg error for stream {i}: {result.stderr}")
|
||||
|
||||
print(f"Extraction completed for {video_path}.")
|
||||
except Exception as e:
|
||||
print(f"Error processing {video_path}: {e}")
|
||||
|
||||
|
||||
def process_path(input_path):
|
||||
"""
|
||||
Processes a file or directory, extracting all audio tracks from video files.
|
||||
|
||||
Parameters:
|
||||
input_path (str): Path to the video file or directory.
|
||||
"""
|
||||
if os.path.isfile(input_path):
|
||||
# Process a single file
|
||||
extract_audio_tracks(input_path)
|
||||
elif os.path.isdir(input_path):
|
||||
# Process all video files in the directory
|
||||
for file_name in os.listdir(input_path):
|
||||
file_path = os.path.join(input_path, file_name)
|
||||
if os.path.isfile(file_path) and file_name.lower().endswith(('.mp4', '.mkv', '.avi', '.mov')):
|
||||
extract_audio_tracks(file_path)
|
||||
else:
|
||||
print(f"Invalid path: {input_path}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
|
||||
# Set up argument parser
|
||||
parser = argparse.ArgumentParser(description="Extract all audio tracks from video files.")
|
||||
parser.add_argument("input_path", help="Path to the video file or directory.")
|
||||
|
||||
# Parse arguments
|
||||
args = parser.parse_args()
|
||||
|
||||
# Process the input path
|
||||
process_path(args.input_path)
|
||||
Executable
+37
@@ -0,0 +1,37 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Get the path to the Python script (adjust if needed)
|
||||
PYTHON_SCRIPT="$HOME/.bin/extract_subtitles.py"
|
||||
|
||||
# Check if the Python script exists
|
||||
if [ ! -f "$PYTHON_SCRIPT" ]; then
|
||||
echo "Error: Python script '$PYTHON_SCRIPT' not found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if arguments are provided
|
||||
if [ "$#" -eq 0 ]; then
|
||||
echo "No arguments provided. Attempting to process all video files in the current directory."
|
||||
|
||||
# Initialize a flag to track if any files are processed
|
||||
files_processed=0
|
||||
|
||||
# Find all video files in the current directory
|
||||
for video_file in *.mp4 *.mkv *.avi *.mov; do
|
||||
# Check if the file exists (in case no matches were found)
|
||||
if [ -e "$video_file" ]; then
|
||||
echo "Processing: $video_file"
|
||||
python "$PYTHON_SCRIPT" "$video_file"
|
||||
files_processed=1
|
||||
fi
|
||||
done
|
||||
|
||||
# If no files were processed, print a message
|
||||
if [ "$files_processed" -eq 0 ]; then
|
||||
echo "No video files found in the current directory."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
# Run the Python script with the provided arguments
|
||||
python "$PYTHON_SCRIPT" "$@"
|
||||
fi
|
||||
@@ -0,0 +1,88 @@
|
||||
import os
|
||||
import subprocess
|
||||
import re
|
||||
import json
|
||||
|
||||
|
||||
def sanitize_filename(name):
|
||||
"""
|
||||
Removes invalid characters from the filename.
|
||||
"""
|
||||
return re.sub(r'[<>:"/\\|?*@]', '_', name)
|
||||
|
||||
|
||||
def extract_subtitle_tracks(video_path):
|
||||
"""
|
||||
Extracts all subtitle tracks from a video file and saves them with their respective titles.
|
||||
|
||||
Parameters:
|
||||
video_path (str): Path to the video file.
|
||||
"""
|
||||
try:
|
||||
base_name = os.path.splitext(os.path.basename(video_path))[0]
|
||||
directory = os.path.dirname(video_path)
|
||||
|
||||
# Get stream info using ffprobe
|
||||
probe_cmd = ["ffprobe", "-v", "error", "-show_streams", "-of", "json", video_path]
|
||||
probe_result = subprocess.run(probe_cmd, capture_output=True, text=True, check=True)
|
||||
streams = json.loads(probe_result.stdout).get('streams', [])
|
||||
|
||||
# Filter subtitle streams
|
||||
subtitle_streams = [s for s in streams if s.get('codec_type') == 'subtitle']
|
||||
if not subtitle_streams:
|
||||
print(f"No subtitle tracks found in {video_path}.")
|
||||
return
|
||||
|
||||
for i, stream in enumerate(subtitle_streams):
|
||||
title = stream.get('tags', {}).get('title', f"subtitle_{i + 1}")
|
||||
language = stream.get('tags', {}).get('language', 'unknown')
|
||||
sanitized_title = sanitize_filename(title)
|
||||
sanitized_language = sanitize_filename(language)
|
||||
output_name = f"{base_name}__{sanitized_title}__{sanitized_language}.srt"
|
||||
output_path = os.path.join(directory, output_name)
|
||||
|
||||
# Extract subtitle stream
|
||||
print(f"Extracting subtitle: {sanitized_title} ({sanitized_language}) to {output_path}")
|
||||
result = subprocess.run([
|
||||
"ffmpeg", "-i", video_path,
|
||||
"-map", f"0:s:{i}",
|
||||
"-c:s", "srt",
|
||||
output_path,
|
||||
"-y"
|
||||
], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||
|
||||
if result.returncode != 0:
|
||||
print(f"ffmpeg error for subtitle stream {i}: {result.stderr}")
|
||||
|
||||
print(f"Subtitle extraction completed for {video_path}.")
|
||||
except Exception as e:
|
||||
print(f"Error processing {video_path}: {e}")
|
||||
|
||||
|
||||
def process_path(input_path):
|
||||
"""
|
||||
Processes a file or directory, extracting all subtitle tracks from video files.
|
||||
|
||||
Parameters:
|
||||
input_path (str): Path to the video file or directory.
|
||||
"""
|
||||
if os.path.isfile(input_path):
|
||||
extract_subtitle_tracks(input_path)
|
||||
elif os.path.isdir(input_path):
|
||||
for file_name in os.listdir(input_path):
|
||||
file_path = os.path.join(input_path, file_name)
|
||||
if os.path.isfile(file_path) and file_name.lower().endswith(('.mp4', '.mkv', '.avi', '.mov')):
|
||||
extract_subtitle_tracks(file_path)
|
||||
else:
|
||||
print(f"Invalid path: {input_path}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description="Extract all subtitle tracks from video files.")
|
||||
parser.add_argument("input_path", help="Path to the video file or directory.")
|
||||
args = parser.parse_args()
|
||||
|
||||
process_path(args.input_path)
|
||||
|
||||
Executable
+12
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
APP_DIR="$HOME/.local/share/grayjay"
|
||||
|
||||
# Check if app is already installed in user directory
|
||||
if [ ! -d "$APP_DIR" ]; then
|
||||
echo "First run - installing Grayjay to $APP_DIR"
|
||||
mkdir -p "$APP_DIR"
|
||||
cp -r /usr/share/grayjay/* "$APP_DIR/"
|
||||
chmod u+w -R "$APP_DIR"
|
||||
fi
|
||||
|
||||
exec sh -c "cd '$APP_DIR' && exec ./Grayjay \"\$@\"" -- "$@"
|
||||
Executable
+27
@@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Use the first argument as the directory or default to the current directory
|
||||
directory="${1:-$(pwd)}"
|
||||
|
||||
# Change to the specified directory
|
||||
cd "$directory" || { echo "Directory not found: $directory"; exit 1; }
|
||||
|
||||
# Regular expression to match " 00 ", " 01 ", ..., " 99 "
|
||||
for file in *; do
|
||||
if [[ "$file" =~ ([[:space:]]([0-9]{2})[[:space:]]) ]]; then
|
||||
# Extract the number (e.g., "00", "01")
|
||||
number="${BASH_REMATCH[2]}"
|
||||
|
||||
# Get the file extension
|
||||
extension="${file##*.}"
|
||||
[[ "$file" == "$extension" ]] && extension="" || extension=".$extension"
|
||||
|
||||
# Construct the new filename
|
||||
new_filename="E$number$extension"
|
||||
|
||||
# Rename the file
|
||||
mv -v "$file" "$new_filename"
|
||||
else
|
||||
echo "Skipped: $file (no match)"
|
||||
fi
|
||||
done
|
||||
@@ -0,0 +1,125 @@
|
||||
#!/usr/bin/env python3
|
||||
import subprocess
|
||||
import sys
|
||||
import json
|
||||
import pathlib
|
||||
|
||||
VIDEO_EXTS = {".mkv", ".mp4"}
|
||||
|
||||
LANG_CODES = {
|
||||
"eng": "English", "en": "English", "english": "English",
|
||||
"spa": "Spanish", "es": "Spanish", "spanish": "Spanish",
|
||||
"fra": "French", "fre": "French", "fr": "French", "french": "French",
|
||||
"deu": "German", "ger": "German", "de": "German", "german": "German",
|
||||
}
|
||||
|
||||
CANONICAL = {
|
||||
"English": "eng",
|
||||
"Spanish": "spa",
|
||||
"French": "fra",
|
||||
"German": "deu",
|
||||
}
|
||||
|
||||
def normalize_language(value):
|
||||
key = value.lower()
|
||||
if key not in LANG_CODES:
|
||||
raise ValueError(f"Unknown language: {value}")
|
||||
return CANONICAL[LANG_CODES[key]]
|
||||
|
||||
def probe(file):
|
||||
cmd = [
|
||||
"ffprobe",
|
||||
"-v", "error",
|
||||
"-print_format", "json",
|
||||
"-show_streams",
|
||||
str(file),
|
||||
]
|
||||
return json.loads(subprocess.check_output(cmd))
|
||||
|
||||
def collect_files(paths):
|
||||
files = []
|
||||
for p in paths:
|
||||
p = pathlib.Path(p)
|
||||
if p.is_dir():
|
||||
files.extend(
|
||||
f for f in p.iterdir()
|
||||
if f.suffix.lower() in VIDEO_EXTS
|
||||
)
|
||||
elif p.suffix.lower() in VIDEO_EXTS:
|
||||
files.append(p)
|
||||
return files
|
||||
|
||||
def preserve_original(file):
|
||||
original = file.with_name(f"{file.stem}_original{file.suffix}")
|
||||
if original.exists():
|
||||
return
|
||||
file.rename(original)
|
||||
print(f"preserve: {file.name} -> {original.name}")
|
||||
|
||||
def set_sub_lang(file, lang):
|
||||
data = probe(file)
|
||||
subs = [s for s in data["streams"] if s["codec_type"] == "subtitle"]
|
||||
|
||||
if not subs:
|
||||
return
|
||||
|
||||
for i, s in enumerate(subs):
|
||||
cur = s.get("tags", {}).get("language", "unset")
|
||||
print(f"{i}: subtitle ({cur})")
|
||||
|
||||
sel = input(f"{file.name}: select subtitle index (blank = skip): ")
|
||||
if sel == "":
|
||||
return
|
||||
|
||||
idx = int(sel)
|
||||
current = subs[idx].get("tags", {}).get("language")
|
||||
|
||||
if current:
|
||||
ans = input(f"Overwrite existing '{current}'? [y/N] ")
|
||||
if ans.lower() != "y":
|
||||
return
|
||||
|
||||
out = file.with_name(f"{file.stem}_sub-{lang}{file.suffix}")
|
||||
print(f"{file.name} -> {out.name}")
|
||||
|
||||
cmd = [
|
||||
"ffmpeg", "-y",
|
||||
"-i", str(file),
|
||||
"-map", "0",
|
||||
"-c", "copy",
|
||||
f"-metadata:s:s:{idx}", f"language={lang}",
|
||||
str(out)
|
||||
]
|
||||
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True
|
||||
)
|
||||
|
||||
if result.returncode != 0:
|
||||
print(f"ERROR processing {file.name}")
|
||||
print(result.stderr.strip())
|
||||
return
|
||||
|
||||
preserve_original(file)
|
||||
|
||||
def main():
|
||||
if len(sys.argv) == 1:
|
||||
targets = ["."]
|
||||
lang = input("Subtitle language: ")
|
||||
elif len(sys.argv) == 2:
|
||||
targets = ["."]
|
||||
lang = sys.argv[1]
|
||||
else:
|
||||
targets = sys.argv[1:-1]
|
||||
lang = sys.argv[-1]
|
||||
|
||||
lang = normalize_language(lang)
|
||||
|
||||
for f in collect_files(targets):
|
||||
set_sub_lang(f, lang)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Executable
+282
@@ -0,0 +1,282 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Language code mapping
|
||||
declare -A LANG_CODES=(
|
||||
["eng"]="English" ["en"]="English"
|
||||
["spa"]="Spanish" ["es"]="Spanish"
|
||||
["fra"]="French" ["fr"]="French"
|
||||
["deu"]="German" ["ger"]="German" ["de"]="German"
|
||||
["jpn"]="Japanese" ["ja"]="Japanese"
|
||||
["ita"]="Italian" ["it"]="Italian"
|
||||
["por"]="Portuguese" ["pt"]="Portuguese"
|
||||
["rus"]="Russian" ["ru"]="Russian"
|
||||
["chi"]="Chinese" ["zh"]="Chinese"
|
||||
["kor"]="Korean" ["ko"]="Korean"
|
||||
["dut"]="Dutch" ["nl"]="Dutch"
|
||||
["swe"]="Swedish" ["sv"]="Swedish"
|
||||
["fin"]="Finnish" ["fi"]="Finnish"
|
||||
["pol"]="Polish" ["pl"]="Polish"
|
||||
["ara"]="Arabic" ["ar"]="Arabic"
|
||||
["hin"]="Hindi" ["hi"]="Hindi"
|
||||
["tur"]="Turkish" ["tr"]="Turkish"
|
||||
["und"]="Undefined" [" "]="Undefined"
|
||||
["ab"]="Abkhazian" ["abk"]="Abkhazian"
|
||||
["aa"]="Afar" ["aar"]="Afar"
|
||||
["af"]="Afrikaans" ["afr"]="Afrikaans"
|
||||
["ak"]="Akan" ["aka"]="Akan"
|
||||
["twi"]="Twi"
|
||||
["fat"]="Fanti"
|
||||
["sq"]="Albanian" ["sqi"]="Albanian" ["alb"]="Albanian"
|
||||
["am"]="Amharic"
|
||||
["amh"]="Amharic"
|
||||
["arb"]="Arabic"
|
||||
["an"]="Aragonese" ["arg"]="Aragonese"
|
||||
["hy"]="Armenian" ["hye"]="Armenian" ["arm"]="Armenian"
|
||||
["as"]="Assamese" ["asm"]="Assamese"
|
||||
["av"]="Avaric" ["ava"]="Avaric"
|
||||
["ae"]="Avestan" ["ave"]="Avestan"
|
||||
["ay"]="Aymara" ["aym"]="Aymara"
|
||||
["az"]="Azerbaijani" ["aze"]="Azerbaijani"
|
||||
["bm"]="Bambara" ["bam"]="Bambara"
|
||||
["ba"]="Bashkir" ["bak"]="Bashkir"
|
||||
["eu"]="Basque" ["eus"]="Basque" ["baq"]="Basque"
|
||||
["be"]="Belarusian" ["bel"]="Belarusian"
|
||||
["bn"]="Bengali" ["ben"]="Bengali"
|
||||
["bi"]="Bislama" ["bis"]="Bislama"
|
||||
["bs"]="Bosnian" ["bos"]="Bosnian"
|
||||
["br"]="Breton" ["bre"]="Breton"
|
||||
["bg"]="Bulgarian" ["bul"]="Bulgarian"
|
||||
["my"]="Burmese" ["mya"]="Burmese"
|
||||
["ca"]="Catalan" ["cat"]="Catalan"
|
||||
["ch"]="Chamorro" ["cha"]="Chamorro"
|
||||
["ce"]="Chechen" ["che"]="Chechen"
|
||||
["ny"]="Chichewa" ["nya"]="Chichewa" ["zho"]="Chinese"
|
||||
["cu"]="Church Slavonic" ["chu"]="Church Slavonic"
|
||||
["cv"]="Chuvash" ["chv"]="Chuvash"
|
||||
["kw"]="Cornish" ["cor"]="Cornish"
|
||||
["co"]="Corsican" ["cos"]="Corsican"
|
||||
["cr"]="Cree" ["cre"]="Cree"
|
||||
["hr"]="Croatian" ["hrv"]="Croatian"
|
||||
["cs"]="Czech" ["ces"]="Czech" ["cze"]="Czech"
|
||||
["da"]="Danish" ["dan"]="Danish"
|
||||
["dv"]="Divehi" ["div"]="Divehi"
|
||||
["dz"]="Dzongkha" ["dzo"]="Dzongkha"
|
||||
["eo"]="Esperanto" ["epo"]="Esperanto"
|
||||
["et"]="Estonian" ["est"]="Estonian"
|
||||
["ee"]="Ewe" ["ewe"]="Ewe"
|
||||
["fo"]="Faroese" ["fao"]="Faroese"
|
||||
["fj"]="Fijian" ["fij"]="Fijian"
|
||||
["fre"]="French"
|
||||
["fy"]="Western Frisian" ["fry"]="Western Frisian"
|
||||
["ff"]="Fulah" ["ful"]="Fulah"
|
||||
["gd"]="Gaelic, Scottish Gaelic"
|
||||
["gla"]="Gaelic"
|
||||
["gl"]="Galician" ["glg"]="Galician"
|
||||
["lg"]="Ganda" ["lug"]="Ganda"
|
||||
["ka"]="Georgian" ["kat"]="Georgian" ["geo"]="Georgian"
|
||||
["el"]="Greek" ["ell"]="Greek" ["gre"]="Greek"
|
||||
["kl"]="Kalaallisut" ["kal"]="Kalaallisut"
|
||||
["gn"]="Guarani" ["grn"]="Guarani"
|
||||
["gu"]="Gujarati" ["guj"]="Gujarati"
|
||||
["ht"]="Haitian Creole" ["hat"]="Haitian Creole"
|
||||
["ha"]="Hausa" ["hau"]="Hausa"
|
||||
["he"]="Hebrew" ["heb"]="Hebrew"
|
||||
["hz"]="Herero" ["her"]="Herero"
|
||||
["ho"]="Hiri Motu" ["hmo"]="Hiri Motu"
|
||||
["hu"]="Hungarian" ["hun"]="Hungarian"
|
||||
["is"]="Icelandic" ["isl"]="Icelandic" ["ice"]="Icelandic"
|
||||
["io"]="Ido" ["ido"]="Ido"
|
||||
["ig"]="Igbo" ["ibo"]="Igbo"
|
||||
["id"]="Indonesian" ["ind"]="Indonesian"
|
||||
["ia"]="Interlingua" ["ina"]="Interlingua"
|
||||
["ie"]="Interlingue" ["ile"]="Interlingue"
|
||||
["iu"]="Inuktitut" ["iku"]="Inuktitut"
|
||||
["ik"]="Inupiaq" ["ipk"]="Inupiaq"
|
||||
["ga"]="Irish" ["gle"]="Irish"
|
||||
["jv"]="Javanese" ["jav"]="Javanese"
|
||||
["kn"]="Kannada" ["kan"]="Kannada"
|
||||
["kr"]="Kanuri" ["kau"]="Kanuri"
|
||||
["ks"]="Kashmiri" ["kas"]="Kashmiri"
|
||||
["kk"]="Kazakh" ["kaz"]="Kazakh"
|
||||
["km"]="Central Khmer" ["khm"]="Central Khmer"
|
||||
["ki"]="Kikuyu" ["kik"]="Kikuyu"
|
||||
["rw"]="Kinyarwanda" ["kin"]="Kinyarwanda"
|
||||
["ky"]="Kyrgyz" ["kir"]="Kyrgyz"
|
||||
["kv"]="Komi" ["kom"]="Komi"
|
||||
["kg"]="Kongo" ["kon"]="Kongo"
|
||||
["kj"]="Kuanyama" ["kua"]="Kuanyama"
|
||||
["ku"]="Kurdish" ["kur"]="Kurdish"
|
||||
["lo"]="Lao" ["lao"]="Lao"
|
||||
["la"]="Latin" ["lat"]="Latin"
|
||||
["lv"]="Latvian" ["lav"]="Latvian"
|
||||
["li"]="Limburgan" ["lim"]="Limburgan"
|
||||
["ln"]="Lingala" ["lin"]="Lingala"
|
||||
["lt"]="Lithuanian" ["lit"]="Lithuanian"
|
||||
["lu"]="Luba-Katanga" ["lub"]="Luba-Katanga"
|
||||
["lb"]="Luxembourgish" ["ltz"]="Luxembourgish"
|
||||
["mk"]="Macedonian" ["mkd"]="Macedonian" ["mac"]="Macedonian"
|
||||
["mg"]="Malagasy" ["mlg"]="Malagasy"
|
||||
["ms"]="Malay" ["msa"]="Malay"
|
||||
["ml"]="Malayalam" ["mal"]="Malayalam"
|
||||
["mt"]="Maltese" ["mlt"]="Maltese"
|
||||
["gv"]="Manx" ["glv"]="Manx"
|
||||
["mi"]="Maori" ["mri"]="Maori" ["mao"]="Maori"
|
||||
["mr"]="Marathi" ["mar"]="Marathi"
|
||||
["mh"]="Marshallese" ["mah"]="Marshallese"
|
||||
["mn"]="Mongolian" ["mon"]="Mongolian"
|
||||
["na"]="Nauru" ["nau"]="Nauru"
|
||||
["nv"]="Navajo" ["nav"]="Navajo"
|
||||
["nd"]="North Ndebele" ["nde"]="North Ndebele"
|
||||
["nr"]="South Ndebele" ["nbl"]="South Ndebele"
|
||||
["ng"]="Ndonga" ["ndo"]="Ndonga"
|
||||
["ne"]="Nepali" ["nep"]="Nepali"
|
||||
["no"]="Norwegian" ["nor"]="Norwegian"
|
||||
["nb"]="Norwegian Bokmål" ["nob"]="Norwegian Bokmål"
|
||||
["nn"]="Norwegian Nynorsk" ["nno"]="Norwegian Nynorsk"
|
||||
["oc"]="Occitan" ["oci"]="Occitan"
|
||||
["oj"]="Ojibwa" ["oji"]="Ojibwa"
|
||||
["or"]="Oriya" ["ori"]="Oriya"
|
||||
["om"]="Oromo" ["orm"]="Oromo"
|
||||
["os"]="Ossetian" ["oss"]="Ossetian"
|
||||
["pi"]="Pali" ["pli"]="Pali"
|
||||
["ps"]="Pashto" ["pus"]="Pashto"
|
||||
["fa"]="Persian" ["fas"]="Persian" ["per"]="Persian"
|
||||
["pa"]="Punjabi" ["pan"]="Punjabi"
|
||||
["qu"]="Quechua" ["que"]="Quechua"
|
||||
["ro"]="Romanian" ["ron"]="Romanian" ["rum"]="Romanian"
|
||||
["rm"]="Romansh" ["roh"]="Romansh"
|
||||
["rn"]="Rundi" ["run"]="Rundi"
|
||||
["se"]="Northern Sami" ["sme"]="Northern Sami"
|
||||
["sm"]="Samoan" ["smo"]="Samoan"
|
||||
["sg"]="Sango" ["sag"]="Sango"
|
||||
["sa"]="Sanskrit" ["san"]="Sanskrit"
|
||||
["sc"]="Sardinian" ["srd"]="Sardinian"
|
||||
["sr"]="Serbian" ["srp"]="Serbian"
|
||||
["sn"]="Shona" ["sna"]="Shona"
|
||||
["sd"]="Sindhi" ["snd"]="Sindhi" ["si"]="Sinhala" ["sin"]="Sinhala"
|
||||
["sk"]="Slovak" ["slk"]="Slovak" ["slo"]="Slovak"
|
||||
["sl"]="Slovenian" ["slv"]="Slovenian"
|
||||
["so"]="Somali" ["som"]="Somali"
|
||||
["st"]="Southern Sotho" ["sot"]="Southern Sotho"
|
||||
["su"]="Sundanese" ["sun"]="Sundanese"
|
||||
["sw"]="Swahil" ["swa"]="Swahili"
|
||||
["ss"]="Swati" ["ssw"]="Swati"
|
||||
["tl"]="Tagalog" ["tgl"]="Tagalog"
|
||||
["ty"]="Tahitian" ["tah"]="Tahitian"
|
||||
["tg"]="Tajik" ["tgk"]="Tajik"
|
||||
["ta"]="Tamil" ["tam"]="Tamil"
|
||||
["tt"]="Tatar" ["tat"]="Tatar"
|
||||
["te"]="Telugu" ["tel"]="Telugu"
|
||||
["th"]="Thai" ["tha"]="Thai"
|
||||
["bo"]="Tibetan" ["bod"]="Tibetan" ["tib"]="Tibetan"
|
||||
["ti"]="Tigrinya" ["tir"]="Tigrinya"
|
||||
["to"]="Tongan" ["ton"]="Tongan" ["ts"]="Tsonga" ["tso"]="Tsonga"
|
||||
["tn"]="Tswana" ["tsn"]="Tswana"
|
||||
["tk"]="Turkmen" ["tuk"]="Turkmen"
|
||||
["ug"]="Uighur" ["uig"]="Uighur"
|
||||
["uk"]="Ukrainian" ["ukr"]="Ukrainian"
|
||||
["ur"]="Urdu" ["urd"]="Urdu"
|
||||
["uz"]="Uzbek" ["uzb"]="Uzbek"
|
||||
["ve"]="Venda" ["ven"]="Venda"
|
||||
["vi"]="Vietnamese" ["vie"]="Vietnamese"
|
||||
["vo"]="Volapük" ["vol"]="Volapük"
|
||||
["wa"]="Walloon" ["wln"]="Walloon"
|
||||
["cy"]="Welsh" ["cym"]="Welsh" ["wel"]="Welsh"
|
||||
["wo"]="Wolof" ["wol"]="Wolof"
|
||||
["xh"]="Xhosa" ["xho"]="Xhosa"
|
||||
["ii"]="Sichuan Yi" ["iii"]="Sichuan Yi"
|
||||
["yi"]="Yiddish" ["yid"]="Yiddish"
|
||||
["yo"]="Yoruba" ["yor"]="Yoruba"
|
||||
["za"]="Zhuang" ["zha"]="Zhuang"
|
||||
["zu"]="Zulu" ["zul"]="Zulu"
|
||||
)
|
||||
|
||||
# Function to translate language codes
|
||||
translate_language() {
|
||||
local code="$1"
|
||||
# Sanitize input: remove spaces and convert to lowercase
|
||||
code=$(echo "$code" | tr -d '[:space:]' | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
# Check for valid subscript and handle fallback
|
||||
if [[ -z "$code" || ! ${LANG_CODES[$code]+_} ]]; then
|
||||
echo "Undefined"
|
||||
else
|
||||
echo "${LANG_CODES[$code]}"
|
||||
fi
|
||||
}
|
||||
|
||||
extract_audio_languages() {
|
||||
audio_info=$(ffprobe -v quiet -select_streams a -show_entries stream=codec_name,channel_layout:stream_tags=language \
|
||||
-of default=noprint_wrappers=1:nokey=1 "$file" | paste -sd "," -)
|
||||
languages=""
|
||||
IFS=',' read -r -a audio_streams <<< "$audio_info"
|
||||
for (( i=0; i<${#audio_streams[@]}; i+=3 )); do
|
||||
lang_code="${audio_streams[i+2]}"
|
||||
lang=$(translate_language "${lang_code:-" "}")
|
||||
|
||||
# Collect languages
|
||||
languages+="$lang"
|
||||
|
||||
# Add comma separator if not the last item
|
||||
if (( i+3 < ${#audio_streams[@]} )); then
|
||||
languages+=", "
|
||||
fi
|
||||
done
|
||||
|
||||
# Format output with languages first, followed by detailed info in brackets
|
||||
echo "$languages"
|
||||
}
|
||||
|
||||
extract_subtitle_languages() {
|
||||
subtitle_info=$(ffprobe -v quiet -select_streams s -show_entries stream_tags=language \
|
||||
-of default=noprint_wrappers=1:nokey=1 "$file" | paste -sd "," -)
|
||||
languages=""
|
||||
# echo "Subtitle Info: $subtitle_info" # Debugging line
|
||||
IFS=',' read -r -a subtitle_streams <<< "$subtitle_info"
|
||||
for ((i=0; i<${#subtitle_streams[@]}; i++)); do
|
||||
lang_code="${subtitle_streams[i]}"
|
||||
lang_code=$(echo "$lang_code" | tr -d '[:space:]' | tr '[:upper:]' '[:lower:]') # Normalize case
|
||||
lang=$(translate_language "$lang_code")
|
||||
languages+="$lang"
|
||||
|
||||
# Add comma separator if not the last item
|
||||
if (( i < ${#subtitle_streams[@]} - 1 )); then
|
||||
languages+=", "
|
||||
fi
|
||||
done
|
||||
# Format output with languages
|
||||
echo "$languages"
|
||||
}
|
||||
|
||||
|
||||
# Function to detect if codec is h265
|
||||
check_h265_codec() {
|
||||
local file="$1"
|
||||
ffmpeg -i "$file" 2>&1 | grep -qi 'Video: hevc' && echo "h265" || echo "other"
|
||||
}
|
||||
|
||||
# Process all .mkv files in the directory
|
||||
for file in *.mkv; do
|
||||
if [[ -f "$file" ]]; then
|
||||
# echo "Processing: $file"
|
||||
|
||||
# Extract filename without extension
|
||||
filename=$(basename "$file" .mkv)
|
||||
|
||||
# Extract and translate audio and subtitle languages
|
||||
audio_languages=$(extract_audio_languages "$file" "Audio")
|
||||
subtitle_languages=$(extract_subtitle_languages "$file" "Subtitle")
|
||||
|
||||
# Debugging: Ensure languages are detected
|
||||
# echo "Audio Languages: $audio_languages"
|
||||
# echo "Subtitle Languages: $subtitle_languages"
|
||||
|
||||
# Detect codec
|
||||
codec=$(check_h265_codec "$file")
|
||||
|
||||
# Call Python script to update Excel
|
||||
python3 /home/honney/.bin/sub_dub.py "$filename" "$audio_languages" "$subtitle_languages" "$codec"
|
||||
fi
|
||||
done
|
||||
|
||||
# echo "Processing completed."
|
||||
@@ -0,0 +1,88 @@
|
||||
import openpyxl
|
||||
import sys
|
||||
|
||||
# Ensure correct number of arguments
|
||||
if len(sys.argv) != 5:
|
||||
print("Usage: update_excel.py <filename> <audio_languages> <subtitle_languages> <codec>")
|
||||
sys.exit(1)
|
||||
|
||||
# Read command-line arguments
|
||||
filename = sys.argv[1].strip()
|
||||
audio_languages = sys.argv[2].strip() if sys.argv[2].strip() else "NONE"
|
||||
subtitle_languages = sys.argv[3].strip() if sys.argv[3].strip() else "NONE"
|
||||
codec = sys.argv[4].strip().lower()
|
||||
|
||||
# Excel file path
|
||||
excel_file = "/home/honney/mount/Storage/Nextcloud/DVDs_Blueray.xlsx"
|
||||
|
||||
# Load the workbook and select the first sheet
|
||||
workbook = openpyxl.load_workbook(excel_file)
|
||||
sheet = workbook.active
|
||||
temp = 0
|
||||
|
||||
# Find the row containing the filename
|
||||
for row in sheet.iter_rows(min_row=2, max_row=sheet.max_row, min_col=1, max_col=1):
|
||||
file_cell = row[0]
|
||||
|
||||
if file_cell.value and isinstance(file_cell.value, str) and file_cell.value.strip() == filename:
|
||||
temp += 1
|
||||
row_index = file_cell.row # Get row number
|
||||
|
||||
# Ensure filename stays unchanged
|
||||
sheet.cell(row=row_index, column=1).value = filename # Column A (Filename)
|
||||
|
||||
# Update Audio and Subtitle languages (if empty, set to "NONE")
|
||||
if sheet.cell(row=row_index, column=7).value == audio_languages:
|
||||
temp += 1
|
||||
else:
|
||||
sheet.cell(row=row_index, column=7).value = audio_languages # Column G (Audio)
|
||||
|
||||
if sheet.cell(row=row_index, column=8).value == subtitle_languages:
|
||||
temp += 1
|
||||
else:
|
||||
sheet.cell(row=row_index, column=8).value = subtitle_languages # Column H (Subtitles)
|
||||
|
||||
# If codec is h265, set columns D, E, F to 1
|
||||
if codec == "h265":
|
||||
if sheet.cell(row=row_index, column=4).value == 1:
|
||||
temp += 1
|
||||
else:
|
||||
sheet.cell(row=row_index, column=4).value = 1
|
||||
|
||||
if sheet.cell(row=row_index, column=5).value == 1:
|
||||
temp += 1
|
||||
else:
|
||||
sheet.cell(row=row_index, column=5).value = 1
|
||||
|
||||
if sheet.cell(row=row_index, column=6).value == 1:
|
||||
temp += 1
|
||||
else:
|
||||
sheet.cell(row=row_index, column=6).value = 1
|
||||
if sheet.cell(row=row_index, column=3).value:
|
||||
dvd = sheet.cell(row=row_index, column=3).value
|
||||
else:
|
||||
sheet.cell(row=row_index, column=3).value = "DVD"
|
||||
dvd = "DVD"
|
||||
if sheet.cell(row=row_index, column=2).value == "NOT CHECKED":
|
||||
sheet.cell(row=row_index, column=2).value = ""
|
||||
# Debugging: Confirm columns updated
|
||||
else: print(f"❌ Codec Wrong {filename}")
|
||||
|
||||
# Debugging: Confirm updates
|
||||
# print(f"✅ Updated row {row_index} -> Audio: {audio_languages}, Subtitles: {subtitle_languages}, Codec: {codec}")
|
||||
|
||||
break # Stop loop once match is found
|
||||
if temp == 0:
|
||||
print(f"❌ No matching row found for {filename}")
|
||||
elif temp < 0 and temp != 6:
|
||||
print(f"🟨 Updated Info for {filename}")
|
||||
elif temp == 6:
|
||||
print(f"🔵 Allready has info for {filename}")
|
||||
else:
|
||||
print(f"✅ {row_index}: {filename} | | {dvd} | 1 | 1 | 1 | {audio_languages} | {subtitle_languages}")
|
||||
|
||||
|
||||
# Save the updated workbook
|
||||
workbook.save(excel_file)
|
||||
|
||||
# print(f"✅ Successfully updated Excel for {filename}")
|
||||
Executable
+130
@@ -0,0 +1,130 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
|
||||
def run(cmd):
|
||||
print(f"[*] Running: {' '.join(map(str, cmd))}")
|
||||
subprocess.run(cmd, check=True)
|
||||
|
||||
def get_tracks(video):
|
||||
result = subprocess.run(
|
||||
["mkvmerge", "-J", str(video)],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
)
|
||||
data = json.loads(result.stdout)
|
||||
return [t for t in data["tracks"] if t["type"] == "audio"]
|
||||
|
||||
def codec_to_ext(codec):
|
||||
codec = codec.lower()
|
||||
if "aac" in codec: return "aac"
|
||||
if "ac-3" in codec or "ac3" in codec: return "ac3"
|
||||
if "dts" in codec: return "dts"
|
||||
if "flac" in codec: return "flac"
|
||||
if "opus" in codec: return "opus"
|
||||
return "mka"
|
||||
|
||||
def clean(text):
|
||||
return "".join(c if c.isalnum() or c in "-_." else "_" for c in text)
|
||||
|
||||
def extract(vdir, outdir):
|
||||
outdir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
for video in vdir.iterdir():
|
||||
if not video.is_file():
|
||||
continue
|
||||
|
||||
name = video.stem
|
||||
print(f"\n=== Scanning {video.name} ===")
|
||||
|
||||
for track in get_tracks(video):
|
||||
tid = track["id"]
|
||||
props = track.get("properties", {})
|
||||
lang = props.get("language", "und")
|
||||
tname = props.get("track_name", "")
|
||||
codec = track.get("codec", "")
|
||||
|
||||
ext = codec_to_ext(codec)
|
||||
|
||||
suffix = lang
|
||||
if tname:
|
||||
suffix += "-" + clean(tname)
|
||||
|
||||
outfile = outdir / f"{name}-{suffix}.{ext}"
|
||||
|
||||
print(f"Extracting track {tid}: lang={lang}, name={tname}, codec={codec}")
|
||||
run(["mkvextract", "tracks", str(video), f"{tid}:{outfile}"])
|
||||
|
||||
def include(vdir, adir):
|
||||
for video in vdir.iterdir():
|
||||
if not video.is_file():
|
||||
continue
|
||||
|
||||
name = video.stem
|
||||
tmp = video.with_name(name + "-tmp.mkv")
|
||||
|
||||
print(f"\n=== Processing {video.name} ===")
|
||||
|
||||
audios = list(adir.glob(f"{name}-*"))
|
||||
if not audios:
|
||||
print("No matching audio found.")
|
||||
continue
|
||||
|
||||
unknown = []
|
||||
known = []
|
||||
|
||||
for a in audios:
|
||||
meta = a.stem[len(name)+1:] # remove "name-"
|
||||
parts = meta.split("-", 1)
|
||||
lang = parts[0]
|
||||
tname = parts[1] if len(parts) > 1 else ""
|
||||
|
||||
entry = (a, lang, tname)
|
||||
if lang in ("und", "unknown", ""):
|
||||
unknown.append(entry)
|
||||
else:
|
||||
known.append(entry)
|
||||
|
||||
cmd = ["mkvmerge", "-o", str(tmp), str(video)]
|
||||
|
||||
for file, lang, tname in unknown + known:
|
||||
print(f"Adding {file.name} lang={lang} name={tname}")
|
||||
cmd += ["--language", f"0:{lang}"]
|
||||
if tname:
|
||||
cmd += ["--track-name", f"0:{tname}"]
|
||||
cmd.append(str(file))
|
||||
|
||||
run(cmd)
|
||||
shutil.move(tmp, video)
|
||||
print(f"Finished {video.name}")
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage:")
|
||||
print(" script extract <video_dir> [audio_out_dir]")
|
||||
print(" script include <video_dir> [audio_dir]")
|
||||
sys.exit(1)
|
||||
|
||||
action = sys.argv[1]
|
||||
vdir = Path(sys.argv[2]).resolve()
|
||||
adir = Path(sys.argv[3]).resolve() if len(sys.argv) > 3 else vdir
|
||||
|
||||
if not vdir.is_dir():
|
||||
print("Video directory not found.")
|
||||
sys.exit(1)
|
||||
|
||||
if action == "extract":
|
||||
extract(vdir, adir)
|
||||
elif action == "include":
|
||||
include(vdir, adir)
|
||||
else:
|
||||
print("Unknown action.")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user