Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11f162c91a | ||
|
|
7da0c922ad |
@@ -10,6 +10,7 @@ While created for my own use, they are shared here in case others find them usef
|
|||||||
- **check**: Validates script dependencies and configuration
|
- **check**: Validates script dependencies and configuration
|
||||||
- **e-ink**: Display handler for e-ink displays with image cycling support
|
- **e-ink**: Display handler for e-ink displays with image cycling support
|
||||||
- **indeel**: File organization tool that watches a directory and sorts incoming files based on content hashes
|
- **indeel**: File organization tool that watches a directory and sorts incoming files based on content hashes
|
||||||
|
- **lnw**: `ln` wrapper that always creates a symbolic link, auto-detects which argument is the existing target, and converts paths to absolute
|
||||||
- **mover**: Monitors a source directory and moves files with specified extensions to a destination directory
|
- **mover**: Monitors a source directory and moves files with specified extensions to a destination directory
|
||||||
- **pushover**: Sends push notifications via Pushover API
|
- **pushover**: Sends push notifications via Pushover API
|
||||||
- **stats**: HTTP server that provides system statistics (CPU, memory, disk usage, etc.) in JSON format
|
- **stats**: HTTP server that provides system statistics (CPU, memory, disk usage, etc.) in JSON format
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
import tempfile
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Optional, Dict, Set
|
||||||
|
|
||||||
|
def check_dependencies() -> bool:
|
||||||
|
"""Check if required commands are available."""
|
||||||
|
for cmd in ['file', 'avifenc']:
|
||||||
|
if not shutil.which(cmd):
|
||||||
|
print(f"Error: {cmd} command not found")
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def convert_file(filepath: Path, output_path: Path, quality: int = 60) -> bool:
|
||||||
|
"""Convert file to AVIF using avifenc"""
|
||||||
|
try:
|
||||||
|
subprocess.run(['avifenc', '-q', str(quality), str(filepath), str(output_path)], check=True)
|
||||||
|
return True
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
print(f"Error converting {filepath} to AVIF: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def delete_file(filepath: Path) -> bool:
|
||||||
|
"""Delete file"""
|
||||||
|
try:
|
||||||
|
filepath.unlink()
|
||||||
|
return True
|
||||||
|
except OSError as e:
|
||||||
|
print(f"Error deleting {filepath}: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def process_directory(directory: Path):
|
||||||
|
"""Process a single directory."""
|
||||||
|
print(f"Processing directory: {directory}")
|
||||||
|
|
||||||
|
# Get all image files in directory
|
||||||
|
image_files = sorted(
|
||||||
|
path for path in directory.iterdir()
|
||||||
|
if path.is_file() and path.suffix.lower() in {'.jpg', '.jpeg', '.png'}
|
||||||
|
)
|
||||||
|
|
||||||
|
for filepath in image_files:
|
||||||
|
print(f"Processing: {filepath.name}")
|
||||||
|
|
||||||
|
# Convert to avif
|
||||||
|
if convert_file(filepath, filepath.with_suffix('.avif')):
|
||||||
|
print(f"Converted {filepath.name} to AVIF")
|
||||||
|
if delete_file(filepath):
|
||||||
|
print(f"Deleted {filepath.name}")
|
||||||
|
else:
|
||||||
|
print(f"Failed to delete {filepath.name}")
|
||||||
|
else:
|
||||||
|
print(f"Failed to convert {filepath.name} to AVIF")
|
||||||
|
|
||||||
|
print(f"Done! Processed {len(image_files)} files in {directory}")
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if not check_dependencies():
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Get directories to process
|
||||||
|
if len(sys.argv) == 1:
|
||||||
|
directories = [Path('.')]
|
||||||
|
else:
|
||||||
|
recursive = sys.argv[1] in {'-r', '--recursive'}
|
||||||
|
start_idx = 2 if recursive else 1
|
||||||
|
|
||||||
|
if recursive:
|
||||||
|
directories = []
|
||||||
|
for dir_arg in sys.argv[start_idx:]:
|
||||||
|
path = Path(dir_arg)
|
||||||
|
if path.is_dir():
|
||||||
|
directories.extend(p for p in path.rglob('.') if p.is_dir())
|
||||||
|
else:
|
||||||
|
directories = [Path(dir_arg) for dir_arg in sys.argv[start_idx:] if Path(dir_arg).is_dir()]
|
||||||
|
|
||||||
|
# Process each directory
|
||||||
|
for directory in directories:
|
||||||
|
try:
|
||||||
|
process_directory(directory)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error processing directory {directory}: {e}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
import tempfile
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Optional, Dict, Set
|
||||||
|
|
||||||
|
def check_dependencies() -> bool:
|
||||||
|
"""Check if required commands are available."""
|
||||||
|
for cmd in ['file', 'dctfilename']:
|
||||||
|
if not shutil.which(cmd):
|
||||||
|
print(f"Error: {cmd} command not found")
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def is_processed_file(filename: str) -> bool:
|
||||||
|
"""Check if filename matches our format (NNNN_HASH.ext)."""
|
||||||
|
pattern = r'^[0-9]{4}_[0-9a-f]{16}\.(jpg|jpeg|png)$'
|
||||||
|
return bool(re.match(pattern, filename.lower()))
|
||||||
|
|
||||||
|
def extract_hash(filename: str) -> Optional[str]:
|
||||||
|
"""Extract hash from processed filename."""
|
||||||
|
match = re.match(r'^[0-9]{4}_([0-9a-f]{16})\.[^.]+$', filename.lower())
|
||||||
|
return match.group(1) if match else None
|
||||||
|
|
||||||
|
def get_file_hash(filepath: Path) -> str:
|
||||||
|
"""Get hash for a file using dctfilename."""
|
||||||
|
try:
|
||||||
|
# Create a temporary file for processing
|
||||||
|
with tempfile.NamedTemporaryFile(suffix=filepath.suffix) as tmp:
|
||||||
|
temp_path = Path(tmp.name)
|
||||||
|
try:
|
||||||
|
# Try hardlink first
|
||||||
|
os.link(str(filepath), tmp.name)
|
||||||
|
except OSError:
|
||||||
|
# Fall back to copy if hardlink fails
|
||||||
|
shutil.copy2(str(filepath), tmp.name)
|
||||||
|
|
||||||
|
result = subprocess.run(['dctfilename', tmp.name],
|
||||||
|
capture_output=True, text=True, check=True)
|
||||||
|
return result.stdout.strip()
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
print(f"Error getting hash for {filepath}: {e}")
|
||||||
|
return ""
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Unexpected error processing {filepath}: {e}")
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def process_directory(directory: Path):
|
||||||
|
"""Process a single directory."""
|
||||||
|
print(f"Processing directory: {directory}")
|
||||||
|
|
||||||
|
# Track files and their information
|
||||||
|
seen_hashes: Dict[str, Path] = {} # hash -> filepath
|
||||||
|
hash_sizes: Dict[str, int] = {} # hash -> filesize
|
||||||
|
|
||||||
|
# Get all image files in directory
|
||||||
|
image_files = sorted(
|
||||||
|
path for path in directory.iterdir()
|
||||||
|
if path.is_file() and path.suffix.lower() in {'.jpg', '.jpeg', '.png'}
|
||||||
|
)
|
||||||
|
|
||||||
|
# First pass: gather information and handle duplicates
|
||||||
|
kept_files = [] # Files to keep and rename
|
||||||
|
for filepath in image_files:
|
||||||
|
print(f"Processing: {filepath.name}")
|
||||||
|
|
||||||
|
# Get hash (either from filename or calculate)
|
||||||
|
if is_processed_file(filepath.name):
|
||||||
|
file_hash = extract_hash(filepath.name)
|
||||||
|
print(f"Using existing hash from filename: {file_hash}")
|
||||||
|
else:
|
||||||
|
file_hash = get_file_hash(filepath)
|
||||||
|
print(f"Calculated new hash: {file_hash}")
|
||||||
|
|
||||||
|
if not file_hash:
|
||||||
|
print(f"Skipping {filepath.name} due to hash error")
|
||||||
|
continue
|
||||||
|
|
||||||
|
filesize = filepath.stat().st_size
|
||||||
|
|
||||||
|
if file_hash in seen_hashes:
|
||||||
|
# Found duplicate
|
||||||
|
if filesize > hash_sizes[file_hash]:
|
||||||
|
print(f"Found larger duplicate: {filepath.name} replaces {seen_hashes[file_hash].name}")
|
||||||
|
seen_hashes[file_hash].unlink() # Remove smaller file
|
||||||
|
seen_hashes[file_hash] = filepath
|
||||||
|
hash_sizes[file_hash] = filesize
|
||||||
|
kept_files.append((filepath, file_hash))
|
||||||
|
else:
|
||||||
|
print(f"Removing smaller duplicate: {filepath.name}")
|
||||||
|
filepath.unlink()
|
||||||
|
else:
|
||||||
|
seen_hashes[file_hash] = filepath
|
||||||
|
hash_sizes[file_hash] = filesize
|
||||||
|
kept_files.append((filepath, file_hash))
|
||||||
|
|
||||||
|
# Second pass: rename files
|
||||||
|
for index, (filepath, file_hash) in enumerate(sorted(kept_files, key=lambda x: x[0].name), 1):
|
||||||
|
new_name = filepath.parent / f"{index:04d}_{file_hash}{filepath.suffix.lower()}"
|
||||||
|
|
||||||
|
if filepath != new_name:
|
||||||
|
print(f"Renaming: {filepath.name} -> {new_name.name}")
|
||||||
|
filepath.rename(new_name)
|
||||||
|
|
||||||
|
print(f"Done! Processed {len(kept_files)} files in {directory}")
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if not check_dependencies():
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Get directories to process
|
||||||
|
if len(sys.argv) == 1:
|
||||||
|
directories = [Path('.')]
|
||||||
|
else:
|
||||||
|
recursive = sys.argv[1] in {'-r', '--recursive'}
|
||||||
|
start_idx = 2 if recursive else 1
|
||||||
|
|
||||||
|
if recursive:
|
||||||
|
directories = []
|
||||||
|
for dir_arg in sys.argv[start_idx:]:
|
||||||
|
path = Path(dir_arg)
|
||||||
|
if path.is_dir():
|
||||||
|
directories.extend(p for p in path.rglob('.') if p.is_dir())
|
||||||
|
else:
|
||||||
|
directories = [Path(dir_arg) for dir_arg in sys.argv[start_idx:] if Path(dir_arg).is_dir()]
|
||||||
|
|
||||||
|
# Process each directory
|
||||||
|
for directory in directories:
|
||||||
|
try:
|
||||||
|
process_directory(directory)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error processing directory {directory}: {e}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# ln? pfft
|
||||||
|
# Ja het was waarschijnlijk minder moeite geweest om het te onthouden
|
||||||
|
# dan om dit scriptje te schrijven.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "Ook altijd geen idee hoe ln ook alweer werkt? Geen zorgen."
|
||||||
|
echo "Doe: $0 <file> <link>" >&2
|
||||||
|
echo " of: $0 <link> <file>" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ $# -ne 2 ]; then
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
|
||||||
|
a="$1"
|
||||||
|
b="$2"
|
||||||
|
|
||||||
|
a_exists=0
|
||||||
|
b_exists=0
|
||||||
|
if [ -e "$a" ]; then a_exists=1; fi
|
||||||
|
if [ -e "$b" ]; then b_exists=1; fi
|
||||||
|
|
||||||
|
if [ "$a_exists" -eq 1 ] && [ "$b_exists" -eq 1 ]; then
|
||||||
|
# is één van de twee al een link? Dan is het goed.
|
||||||
|
if { [ -L "$a" ] && [ "$(readlink "$a")" = "$(realpath "$b")" ]; } || \
|
||||||
|
{ [ -L "$b" ] && [ "$(readlink "$b")" = "$(realpath "$a")" ]; }; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "[FOUT] deze bestaan al: $a, $b" >&2
|
||||||
|
exit 1
|
||||||
|
# Geen van beide bestaat
|
||||||
|
elif [ "$a_exists" -eq 0 ] && [ "$b_exists" -eq 0 ]; then
|
||||||
|
echo "[FOUT] allebei niet gevonden: $a, $b" >&2
|
||||||
|
exit 1
|
||||||
|
elif [ "$a_exists" -eq 1 ]; then
|
||||||
|
target="$a"
|
||||||
|
link="$b"
|
||||||
|
else
|
||||||
|
target="$b"
|
||||||
|
link="$a"
|
||||||
|
fi
|
||||||
|
|
||||||
|
target_abs="$(realpath "$target")"
|
||||||
|
link_abs="$(realpath -m "$link")"
|
||||||
|
|
||||||
|
ln -s "$target_abs" "$link_abs"
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import shutil
|
||||||
|
import sqlite3
|
||||||
|
import tempfile
|
||||||
|
import os
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description='Command line arguments')
|
||||||
|
parser.add_argument('-d', '--db', required=True, help='database location')
|
||||||
|
parser.add_argument('-t', '--tag', type=int, required=False, help='tag id')
|
||||||
|
parser.add_argument('-n', '--number', type=int, default=4, help='number')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Copy db to temp location
|
||||||
|
temp_dir = tempfile.gettempdir()
|
||||||
|
temp_db = os.path.join(temp_dir, 'pmvg.db')
|
||||||
|
if not os.path.exists(temp_db):
|
||||||
|
shutil.copy2(args.db, temp_db)
|
||||||
|
|
||||||
|
# Open temp db with sqlite
|
||||||
|
conn = sqlite3.connect(temp_db)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# Print names of all tables
|
||||||
|
cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
|
||||||
|
tables = cursor.fetchall()
|
||||||
|
print("Tables:")
|
||||||
|
for table in tables:
|
||||||
|
print(table[0])
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Print schema of scenes_tags table
|
||||||
|
cursor.execute("SELECT sql FROM sqlite_master WHERE type='table' AND name='scenes_tags'")
|
||||||
|
schema = cursor.fetchone()
|
||||||
|
print("Schema for scenes_tags:")
|
||||||
|
print(schema[0])
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Print data of tags table if tag id not provided
|
||||||
|
if not args.tag:
|
||||||
|
cursor.execute("SELECT * FROM tags")
|
||||||
|
tags = cursor.fetchall()
|
||||||
|
print("Table: tags")
|
||||||
|
for tag in tags:
|
||||||
|
print(tag)
|
||||||
|
print("Select a tag with -t")
|
||||||
|
exit()
|
||||||
|
|
||||||
|
# Get random scenes based on tag id
|
||||||
|
cursor.execute(f"SELECT * FROM scenes_tags WHERE tag_id = {args.tag} ORDER BY RANDOM() LIMIT {args.number}")
|
||||||
|
scenes = cursor.fetchall()
|
||||||
|
print(f"\nRandom {args.number} scenes with tag_id {args.tag}:")
|
||||||
|
for scene in scenes:
|
||||||
|
scene_id = scene[0]
|
||||||
|
url = f"http://localhost:9999/scene/{scene_id}/stream"
|
||||||
|
os.system(f"nohup mpv --volume=0 {url} </dev/null >/dev/null 2>&1 &")
|
||||||
|
print(scene)
|
||||||
|
os._exit(0)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
Executable
+32
@@ -0,0 +1,32 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
SHUTDOWN_DELAY=60
|
||||||
|
|
||||||
|
while getopts "d:" opt; do
|
||||||
|
case $opt in
|
||||||
|
d) SHUTDOWN_DELAY="$OPTARG";;
|
||||||
|
\?) echo "Invalid option: -$OPTARG" >&2; exit 1;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Shift past the options to get positional argument
|
||||||
|
shift $((OPTIND-1))
|
||||||
|
|
||||||
|
if [ $# -ne 1 ]; then
|
||||||
|
echo "Usage: $0 [-d delay_seconds] process_name"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
PROCESS_NAME="$1" # Name of the process to monitor
|
||||||
|
|
||||||
|
# Check if process is running
|
||||||
|
while true; do
|
||||||
|
if ! pgrep -x "$PROCESS_NAME" > /dev/null; then
|
||||||
|
echo "Process $PROCESS_NAME is no longer running"
|
||||||
|
echo "System will shutdown in $SHUTDOWN_DELAY seconds"
|
||||||
|
sleep $SHUTDOWN_DELAY
|
||||||
|
shutdown -h now
|
||||||
|
fi
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
+2
-2
@@ -141,8 +141,8 @@ def get_deletion_candidates(torrents, limit=10):
|
|||||||
|
|
||||||
for torrent, score in sorted_torrents[:limit]:
|
for torrent, score in sorted_torrents[:limit]:
|
||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
age_days = (now - torrent['date_added']).days
|
age_days = (now - torrent.get('date_added', now)).days
|
||||||
inactive_days = (now - torrent['latest_activity']).days
|
inactive_days = (now - torrent.get('latest_activity', now)).days
|
||||||
|
|
||||||
size_str = torrent['size']
|
size_str = torrent['size']
|
||||||
size_num = float(size_str.split()[0])
|
size_num = float(size_str.split()[0])
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ def get_video_filter(crop_arg: str | None) -> str:
|
|||||||
|
|
||||||
Rescales video height to 720p or original height if smaller, maintaining aspect ratio.
|
Rescales video height to 720p or original height if smaller, maintaining aspect ratio.
|
||||||
Supports standard, percentage-based, and edge-based cropping formats."""
|
Supports standard, percentage-based, and edge-based cropping formats."""
|
||||||
vfilter = "scale=-2:'min(720,ih)'"
|
vfilter = "scale=-2:'min(2160,ih)'"
|
||||||
if crop_arg:
|
if crop_arg:
|
||||||
if crop_arg.endswith('%'):
|
if crop_arg.endswith('%'):
|
||||||
# Percentage-based centered cropping (single percentage value)
|
# Percentage-based centered cropping (single percentage value)
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import psutil
|
|||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
|
|
||||||
video_extensions: list[str] = ["mp4", "mkv", "avi", "wmv", "mov", "m4v", "ts", "flv", "mpg", "webm"]
|
video_extensions: list[str] = ["mp4", "mkv", "avi", "wmv", "mov", "m4v", "ts", "flv", "mpg", "webm"]
|
||||||
|
embedded_sub_extensions: list[str] = ["mkv"]
|
||||||
|
|
||||||
total_files: int = 0
|
total_files: int = 0
|
||||||
|
|
||||||
@@ -111,6 +112,38 @@ def ffmpeg_command(input: pathlib.Path, output: pathlib.Path, nice: int = 20) ->
|
|||||||
str(output)
|
str(output)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def extract_sub_command(input: pathlib.Path, output: pathlib.Path) -> list[str]:
|
||||||
|
return [
|
||||||
|
"ffmpeg",
|
||||||
|
"-progress", "pipe:1", # Send progress info to stdout
|
||||||
|
"-i", str(input),
|
||||||
|
"-map", "0:s:0",
|
||||||
|
str(output)
|
||||||
|
]
|
||||||
|
|
||||||
|
def has_subs(input: pathlib.Path) -> bool:
|
||||||
|
cmd = [
|
||||||
|
"ffprobe",
|
||||||
|
"-loglevel", "error",
|
||||||
|
"-select_streams", "s",
|
||||||
|
"-show_entries", "stream=index:stream_tags=language",
|
||||||
|
"-of", "csv=p=0",
|
||||||
|
str(input)
|
||||||
|
]
|
||||||
|
try:
|
||||||
|
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
|
||||||
|
print(input)
|
||||||
|
print(result.stdout)
|
||||||
|
print("---")
|
||||||
|
if result.stdout:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
except (subprocess.CalledProcessError, ValueError) as e:
|
||||||
|
logging.error(f"Error getting subs for {input}: {e}")
|
||||||
|
return False
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
def clean_directory(directory_path: pathlib.Path) -> None:
|
def clean_directory(directory_path: pathlib.Path) -> None:
|
||||||
"""Yeet all files and directories from a directory."""
|
"""Yeet all files and directories from a directory."""
|
||||||
for item in directory_path.iterdir():
|
for item in directory_path.iterdir():
|
||||||
@@ -122,6 +155,9 @@ def clean_directory(directory_path: pathlib.Path) -> None:
|
|||||||
def is_video(path: pathlib.Path) -> bool:
|
def is_video(path: pathlib.Path) -> bool:
|
||||||
return path.suffix.lower()[1:] in video_extensions
|
return path.suffix.lower()[1:] in video_extensions
|
||||||
|
|
||||||
|
def is_video_with_embedded_subs(path: pathlib.Path) -> bool:
|
||||||
|
return path.suffix.lower()[1:] in embedded_sub_extensions
|
||||||
|
|
||||||
def get_video_duration(input_file: pathlib.Path) -> float:
|
def get_video_duration(input_file: pathlib.Path) -> float:
|
||||||
cmd = [
|
cmd = [
|
||||||
"ffprobe",
|
"ffprobe",
|
||||||
@@ -313,6 +349,26 @@ def process_directory(
|
|||||||
logging.info(f"Processed {count} files, {successful} successful")
|
logging.info(f"Processed {count} files, {successful} successful")
|
||||||
return count
|
return count
|
||||||
|
|
||||||
|
def process_subtitles(
|
||||||
|
*,
|
||||||
|
source_dir: pathlib.Path,
|
||||||
|
destination_dir: pathlib.Path,
|
||||||
|
temp_dir: pathlib.Path):
|
||||||
|
for dirpath, _dirnames, filenames in os.walk(source_dir):
|
||||||
|
for filename in filenames:
|
||||||
|
filepath = pathlib.Path(dirpath) / filename
|
||||||
|
# File might have been deleted since starting script
|
||||||
|
if not filepath.exists() or not is_video_with_embedded_subs(filepath):
|
||||||
|
logging.debug(f"Skipping {filepath}: not a video file or doesn't exist")
|
||||||
|
continue
|
||||||
|
if has_subs(filepath):
|
||||||
|
target_path = destination_dir / filepath.relative_to(source_dir)
|
||||||
|
target_path = target_path.with_suffix(".cmp.en.srt")
|
||||||
|
if target_path.exists():
|
||||||
|
logging.debug(f"Skipping {filepath}: target already exists")
|
||||||
|
continue
|
||||||
|
cmd = extract_sub_command(filepath, target_path)
|
||||||
|
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
@@ -332,6 +388,7 @@ def main():
|
|||||||
help='Remove source files after successful compression')
|
help='Remove source files after successful compression')
|
||||||
parser.add_argument('-v', '--verbose', action='store_true',
|
parser.add_argument('-v', '--verbose', action='store_true',
|
||||||
help='Enable verbose logging output')
|
help='Enable verbose logging output')
|
||||||
|
parser.add_argument('--sub', action='store_true', help='Process files for subtitle extraction first')
|
||||||
parser.add_argument('-n', '--nice', type=lambda x: int(x) if 0 <= int(x) <= 39 else exec('raise ValueError("Nice value must be between 0 and 39")'),
|
parser.add_argument('-n', '--nice', type=lambda x: int(x) if 0 <= int(x) <= 39 else exec('raise ValueError("Nice value must be between 0 and 39")'),
|
||||||
default=20,
|
default=20,
|
||||||
help='Nice value for process priority (default: 20). Valid values are 0 (highest priority) to 39 (lowest priority). Lower values use more system resources.')
|
help='Nice value for process priority (default: 20). Valid values are 0 (highest priority) to 39 (lowest priority). Lower values use more system resources.')
|
||||||
@@ -352,22 +409,27 @@ def main():
|
|||||||
global ignore_busy
|
global ignore_busy
|
||||||
ignore_busy = bool(args.ignore_busy)
|
ignore_busy = bool(args.ignore_busy)
|
||||||
|
|
||||||
logging.info(f"Starting video compression from {source_dir} to {destination_dir}")
|
if args.sub:
|
||||||
|
process_subtitles(source_dir=source_dir,
|
||||||
global total_files
|
|
||||||
total_files = process_directory(
|
|
||||||
source_dir=source_dir,
|
|
||||||
destination_dir=destination_dir,
|
destination_dir=destination_dir,
|
||||||
temp_dir=temp_dir,
|
temp_dir=temp_dir)
|
||||||
count_only=True
|
else:
|
||||||
)
|
logging.info(f"Starting video compression from {source_dir} to {destination_dir}")
|
||||||
|
|
||||||
process_directory(
|
global total_files
|
||||||
source_dir=source_dir,
|
total_files = process_directory(
|
||||||
destination_dir=destination_dir,
|
source_dir=source_dir,
|
||||||
temp_dir=temp_dir,
|
destination_dir=destination_dir,
|
||||||
remove_source=args.remove_source,
|
temp_dir=temp_dir,
|
||||||
nice=args.nice
|
count_only=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
process_directory(
|
||||||
|
source_dir=source_dir,
|
||||||
|
destination_dir=destination_dir,
|
||||||
|
temp_dir=temp_dir,
|
||||||
|
remove_source=args.remove_source,
|
||||||
|
nice=args.nice
|
||||||
|
)
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
MAX=75576
|
||||||
|
BASE=$1
|
||||||
|
COUNT=$2
|
||||||
|
|
||||||
|
if [ -z "$1" ] || [ -z "$2" ]; then
|
||||||
|
echo "Usage: $0 <base_url> <count>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/mpv-urls"
|
||||||
|
mkdir -p "$STATE_DIR"
|
||||||
|
OUTPUT_FILE="$STATE_DIR/urls-$(date +%Y%m%d-%H%M%S).txt"
|
||||||
|
|
||||||
|
for ((i=1; i<=COUNT; i++)); do
|
||||||
|
URL="$BASE/$(( RANDOM % MAX + 1 ))/stream"
|
||||||
|
echo "$URL" >> "$OUTPUT_FILE"
|
||||||
|
mpv "$URL" &
|
||||||
|
done
|
||||||
|
|
||||||
|
wait
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Fan PWM Control
|
||||||
|
After=multi-user.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
RemainAfterExit=yes
|
||||||
|
ExecStart=/bin/bash -c 'echo 0 > /sys/class/pwm/pwmchip0/export && \
|
||||||
|
echo 40000 > /sys/class/pwm/pwmchip0/pwm0/period && \
|
||||||
|
echo 28000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle && \
|
||||||
|
echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable'
|
||||||
|
ExecStop=/bin/bash -c 'echo 0 > /sys/class/pwm/pwmchip0/pwm0/enable && \
|
||||||
|
echo 0 > /sys/class/pwm/pwmchip0/unexport'
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
Reference in New Issue
Block a user