Skip to content

Instantly share code, notes, and snippets.

@kristianfreeman
Created October 7, 2024 15:05
Show Gist options
  • Save kristianfreeman/79ced22ee94ee8eca240b223037b6cc0 to your computer and use it in GitHub Desktop.
Save kristianfreeman/79ced22ee94ee8eca240b223037b6cc0 to your computer and use it in GitHub Desktop.
Fix rockbox cover art
from mutagen.flac import FLAC, Picture
from mutagen.id3 import APIC, ID3, PictureType, ID3NoHeaderError
import os
import subprocess
import sys
import time
import hashlib
num_files = 0
checksum_file = 'checksums.txt'
# Function to calculate the MD5 checksum of a file
def calculate_md5(file_path):
hash_md5 = hashlib.md5()
try:
with open(file_path, "rb") as f:
for chunk in iter(lambda: f.read(4096), b""):
hash_md5.update(chunk)
return hash_md5.hexdigest()
except Exception as e:
print(f"Error calculating MD5 for {file_path}: {e}")
return None
# Load the checksums from a file
def load_checksums(checksum_file):
checksums = {}
if os.path.exists(checksum_file):
with open(checksum_file, 'r') as f:
for line in f:
try:
md5, file_path = line.strip().split(' ')
checksums[file_path] = md5
except ValueError:
continue
return checksums
# Save the checksums to a file
def save_checksums(checksum_file, checksums):
try:
with open(checksum_file, 'w') as f:
for file_path, md5 in checksums.items():
f.write(f"{md5} {file_path}\n")
except Exception as e:
print(f"Error saving checksums: {e}")
# Initialize checksums
checksums = load_checksums(checksum_file)
updated_checksums = {**checksums} # Initialize with existing checksums
def run_fast_scandir(dir, ext):
subfolders, files = [], []
for f in os.scandir(dir):
if f.is_dir():
subfolders.append(f.path)
if f.is_file():
file_ext = os.path.splitext(f.name)[1].lower()
file_path = f.path
md5 = calculate_md5(file_path)
if md5 is None:
continue
if md5 == checksums.get(file_path):
print(f"Skipping {file_path} (already processed)")
continue
if file_ext in ext:
print(f"Processing {file_ext.upper()} file: {file_path}")
if file_ext == '.mp3':
fix_mp3(file_path)
elif file_ext == '.flac':
fix_flac(file_path)
elif file_ext == '.jpg':
fix_jpg(file_path)
files.append(file_path)
for dir in list(subfolders):
sf, f = run_fast_scandir(dir, ext)
subfolders.extend(sf)
files.extend(f)
return subfolders, files
def fix_mp3(file):
try:
tag = ID3(file)
except ID3NoHeaderError:
return
if len(tag.getall("APIC")) > 0:
with open("temp.jpg", "wb") as f:
f.write(tag.getall("APIC")[0].data)
subprocess.run(['magick', '-interlace', 'None', 'temp.jpg', 'temp.jpg'])
tag.delall("APIC")
with open("temp.jpg", 'rb') as f:
tag.add(APIC(3, 'image/jpeg', 3, u'cover', data=f.read()))
tag.save()
global num_files
num_files += 1
# Save checksum after processing
md5 = calculate_md5(file)
if md5:
updated_checksums[file] = md5
save_checksums(checksum_file, updated_checksums)
def fix_flac(file):
tag = FLAC(file)
pics = tag.pictures
for p in pics:
if p.type == 3:
with open("temp.jpg", "wb") as f:
f.write(p.data)
subprocess.run(['magick', '-interlace', 'None', 'temp.jpg', 'temp.jpg'])
tag.clear_pictures()
image = Picture()
image.type = PictureType.COVER_FRONT
image.mime = 'image/jpeg'
with open('temp.jpg', 'rb') as f:
image.data = f.read()
tag.add_picture(image)
tag.save(deleteid3=True)
global num_files
num_files += 1
# Save checksum after processing
md5 = calculate_md5(file)
if md5:
updated_checksums[file] = md5
save_checksums(checksum_file, updated_checksums)
def fix_jpg(file):
subprocess.run(['magick', '-interlace', 'None', file, file])
global num_files
num_files += 1
# Save checksum after processing
md5 = calculate_md5(file)
if md5:
updated_checksums[file] = md5
save_checksums(checksum_file, updated_checksums)
def timer(start, end):
hours, rem = divmod(end - start, 3600)
minutes, seconds = divmod(rem, 60)
return "{:0>2}:{:0>2}:{:05.2f}".format(int(hours), int(minutes), seconds)
if len(sys.argv) > 1:
start = time.time()
subfolders, files = run_fast_scandir(sys.argv[1].replace("\\", "/"), {".flac", ".mp3", ".jpg"})
end = time.time()
print("\n" + str(num_files) + " files processed in " + timer(start, end))
else:
print("You must provide a path between quotes. For example: python fix_albumart.py \"R:\\My Music\"")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment