first commit

This commit is contained in:
Soph :3 2025-12-20 12:02:36 +02:00
commit 010598a8be
15 changed files with 1128 additions and 0 deletions

View file

@ -0,0 +1,35 @@
#!/bin/bash
BASE=/tmp/deezer-downloader/albums/
cd "$BASE" || exit 1
shopt -s nullglob
for dir in */ ; do
DATES=$(~/bin/exiftool/exiftool -Date "$dir"/*.flac "$dir"/*.mp3 2>/dev/null \
| awk -F': ' '{if($2!="") print $2}' | sort -u)
COUNT=$(echo "$DATES" | wc -l)
if [ "$COUNT" -le 1 ]; then
continue
fi
NEWDATE=$(echo "$DATES" | sort -nr | head -n1)
echo "🔧 $dir → Fixing inconsistent dates to newest: $NEWDATE"
echo "$DATES" | sed 's/^/ - /'
for f in "$dir"/*.flac; do
[ -f "$f" ] || continue
metaflac --remove-tag=DATE "$f"
metaflac --set-tag=DATE="$NEWDATE" "$f"
done
for f in "$dir"/*.mp3; do
[ -f "$f" ] || continue
id3v2 -T "$NEWDATE" "$f" >/dev/null 2>&1
done
echo
done

63
misc/fix_singles.py Normal file
View file

@ -0,0 +1,63 @@
import os
import shutil
from mutagen import File
# Path where your music files are
MUSIC_DIR = "/home/fucksophie/OrpheusDL/downloads"
# First, collect all .lrc files
lrc_files = {}
for filename in os.listdir(MUSIC_DIR):
if filename.lower().endswith(".lrc"):
key = os.path.splitext(filename)[0] # filename without extension
lrc_files[key] = os.path.join(MUSIC_DIR, filename)
# Loop through all items in the directory
for filename in os.listdir(MUSIC_DIR):
filepath = os.path.join(MUSIC_DIR, filename)
# Only process files, ignore directories
if not os.path.isfile(filepath):
continue
# Only process .mp3 and .flac files
if not (filename.lower().endswith(".mp3") or filename.lower().endswith(".flac")):
continue
# Load file with mutagen
audio = File(filepath, easy=True)
if not audio:
print(f"Skipping {filename}, can't read metadata")
continue
# Try to get the album artist tag; fallback to artist if missing
artist = None
if "albumartist" in audio:
artist = audio["albumartist"][0]
elif "artist" in audio:
artist = audio["artist"][0]
else:
print(f"No artist tag found for {filename}, skipping")
continue
# Clean up artist name for folder creation
safe_artist = artist.replace("/", "_").replace("\\", "_")
folder_name = f"{safe_artist} (singles)"
target_folder = os.path.join(MUSIC_DIR, folder_name)
# Create folder if it doesn't exist
os.makedirs(target_folder, exist_ok=True)
# Move file into folder
target_path = os.path.join(target_folder, filename)
print(f"Moving '{filename}' to '{target_folder}'")
shutil.move(filepath, target_path)
# Also move corresponding .lrc file if it exists
base_name = os.path.splitext(filename)[0]
if base_name in lrc_files:
lrc_target = os.path.join(target_folder, os.path.basename(lrc_files[base_name]))
print(f"Moving lyrics '{lrc_files[base_name]}' to '{target_folder}'")
shutil.move(lrc_files[base_name], lrc_target)
print("Done!")

View file

@ -0,0 +1,27 @@
import os
import re
ROOT = "/home/fucksophie/media/Music/axxturel loosies"
SUPPORTED_EXTS = {".mp3", ".m4a"} # add other formats as needed
def sanitize_filename(filename: str) -> str:
# Keep ASCII printable characters; replace anything else with "_"
return re.sub(r"[^\x20-\x7E]", "_", filename)
def safe_print(s: str):
# Encode with backslashreplace to avoid crashing on surrogates
print(s.encode("utf-8", errors="backslashreplace").decode("utf-8"))
for root, _, files in os.walk(ROOT):
for f in files:
ext = os.path.splitext(f)[1].lower()
if ext not in SUPPORTED_EXTS:
continue
old_path = os.path.join(root, f)
new_name = sanitize_filename(f)
new_path = os.path.join(root, new_name)
if old_path != new_path:
safe_print(f"[DRY RUN] Would rename: {old_path}{new_path}")
# To apply changes, uncomment the next line:
os.rename(old_path, new_path)

75
misc/fix_utf8_tags.py Normal file
View file

@ -0,0 +1,75 @@
import os
from typing import Optional
from mutagen.id3 import ID3, ID3NoHeaderError, TALB, TIT2
MUSIC_ROOT = "/home/fucksophie/media/Music"
SUPPORTED_EXTS = {".mp3"}
DRY_RUN = False
def deduce_album_from_dir(dirname: str) -> Optional[str]:
# "[Facy] - Night school HOSTED BY BRAILLED"
if " - " in dirname:
return dirname.split(" - ", 1)[1].strip()
return None
def is_singles_dir(dirname: str) -> bool:
return dirname.lower().endswith("(singles)")
def title_from_filename(filename: str) -> str:
return os.path.splitext(filename)[0].strip()
for root, _, files in os.walk(MUSIC_ROOT):
dirname = os.path.basename(root)
album_name = deduce_album_from_dir(dirname)
singles = is_singles_dir(dirname)
for file in files:
if os.path.splitext(file)[1].lower() not in SUPPORTED_EXTS:
continue
path = os.path.join(root, file)
try:
try:
tags = ID3(path)
except ID3NoHeaderError:
tags = ID3()
# -------- Albums --------
if album_name:
existing_album = tags.get("TALB")
if not (existing_album and existing_album.text and existing_album.text[0].strip()):
print(f"[DRY RUN][ALBUM] {path}")
print(f" → set album = '{album_name}'")
if not DRY_RUN:
tags.add(TALB(encoding=3, text=album_name))
tags.save(path)
# -------- Singles --------
if singles:
title = title_from_filename(file)
existing_title = tags.get("TIT2")
existing_album = tags.get("TALB")
needs_title = not (existing_title and existing_title.text and existing_title.text[0].strip())
needs_album = not (existing_album and existing_album.text and existing_album.text[0].strip())
if needs_title or needs_album:
print(f"[DRY RUN][SINGLE] {path}")
if needs_title:
print(f" → set title = '{title}'")
if needs_album:
print(" → set album = 'singles'")
if not DRY_RUN:
if needs_title:
tags.add(TIT2(encoding=3, text=title))
if needs_album:
tags.add(TALB(encoding=3, text="singles"))
tags.save(path)
except Exception as e:
print(f"[ERROR] {path}: {e}")

131
misc/lrcput+txt.py Normal file
View file

@ -0,0 +1,131 @@
import os
import shutil
import argparse
from mutagen.flac import FLAC
from mutagen.mp4 import MP4
import eyed3
from tqdm import tqdm
def has_embedded_lyrics(audio):
if isinstance(audio, FLAC):
return 'LYRICS' in audio
elif isinstance(audio, MP4):
return '\xa9lyr' in audio.tags
elif isinstance(audio, eyed3.core.AudioFile):
return audio.tag.lyrics is not None
return False
def embed_lrc(directory, skip_existing, reduce_lrc, recursive):
embedded_lyrics_files = 0
failed_files = []
audio_files = []
for root, dirs, files in os.walk(directory):
for file in files:
if file.endswith('.flac') or file.endswith('.mp3') or file.endswith('.m4a'):
audio_files.append(os.path.join(root, file))
with tqdm(total=len(audio_files), desc='Embedding LRC files', unit='file') as pbar:
for audio_path in audio_files:
file = os.path.basename(audio_path)
base = os.path.splitext(file)[0]
lrc_path = os.path.join(os.path.dirname(audio_path), base + '.lrc')
txt_path = os.path.join(os.path.dirname(audio_path), base + '.txt')
lyrics_path = None
if os.path.exists(lrc_path):
lyrics_path = lrc_path
elif os.path.exists(txt_path):
lyrics_path = txt_path
if not lyrics_path:
pbar.set_postfix({"status": "no lrc/txt"})
pbar.update(1)
continue
if skip_existing:
audio = None
if file.endswith('.flac'):
audio = FLAC(audio_path)
elif file.endswith('.mp3'):
audio = eyed3.load(audio_path)
elif file.endswith('.m4a'):
audio = MP4(audio_path)
if has_embedded_lyrics(audio):
pbar.set_postfix({"status": "skipped"})
pbar.update(1)
continue
try:
lyrics = open(lyrics_path, 'r', encoding='utf-8').read()
if file.endswith('.flac'):
audio = FLAC(audio_path)
audio['LYRICS'] = lyrics
audio.save()
elif file.endswith('.mp3'):
audio = eyed3.load(audio_path)
tag = audio.tag
tag.lyrics.set(lyrics)
tag.save(version=eyed3.id3.ID3_V2_3)
elif file.endswith('.m4a'):
audio = MP4(audio_path)
audio.tags['\xa9lyr'] = lyrics
audio.save()
embedded_lyrics_files += 1
pbar.set_postfix({"status": f"embedded: {file}"})
pbar.update(1)
pbar.refresh()
if reduce_lrc:
os.remove(lyrics_path)
pbar.set_postfix({"status": f"embedded, reduced: {file}"})
pbar.refresh()
except Exception as e:
print(f"Error embedding lyrics for {file}: {str(e)}")
pbar.set_postfix({"status": f"error: {file}"})
pbar.update(1)
pbar.refresh()
failed_files.append(file)
if os.path.exists(lyrics_path):
shutil.move(lyrics_path, lyrics_path + ".failed")
continue
return len(audio_files), embedded_lyrics_files, failed_files
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Embed LRC files into audio files (FLAC, MP3, and M4A) and optionally reduce LRC files.')
parser.add_argument('-d', '--directory', required=True, help='Directory containing audio and LRC files')
parser.add_argument('-s', '--skip', action='store_true', help='Skip files that already have embedded lyrics')
parser.add_argument('-r', '--reduce', action='store_true', help='Reduce (delete) LRC files after embedding')
parser.add_argument('-R', '--recursive', action='store_true', help='Recursively process subdirectories')
args = parser.parse_args()
banner = """
Scripted by TheRedSpy15, .txt support by Sophie"""
print(banner)
directory_path = args.directory
skip_existing = args.skip
reduce_lrc = args.reduce
recursive = args.recursive
total, embedded, failed = embed_lrc(directory_path, skip_existing, reduce_lrc, recursive)
percentage = (embedded / total) * 100 if total > 0 else 0
print(f"Total audio files: {total}")
print(f"Embedded lyrics in {embedded} audio files.")
print(f"Percentage of audio files with embedded lyrics: {percentage:.2f}%")
if failed:
print("\nFailed to embed LRC for the following files:")
for file in failed:
print(file)