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
+80
View File
@@ -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"
+78
View File
@@ -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()
+49
View File
@@ -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
+66
View File
@@ -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)
+37
View File
@@ -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
+94
View File
@@ -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)
+37
View File
@@ -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
+88
View File
@@ -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)
+12
View File
@@ -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 \"\$@\"" -- "$@"
+27
View File
@@ -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
+125
View File
@@ -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()
+282
View File
@@ -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."
+88
View File
@@ -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}")
+130
View File
@@ -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()