Last active
December 1, 2022 21:24
-
-
Save pcewing/2b5ec2f2a75773f889e845c6b6bd4ec9 to your computer and use it in GitHub Desktop.
Rename MP3 Files
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import os | |
import mutagen.id3 | |
import unicodedata | |
import re | |
import errno | |
from shutil import copyfile | |
OUTPUT_FILENAMES = set() | |
DUPLICATES = set() | |
def slugify(filename): | |
""" | |
Heavily inspired by: | |
https://stackoverflow.com/questions/295135/turn-a-string-into-a-valid-filename/46801075 | |
""" | |
filename = str(filename) | |
# Convert to ASCII | |
filename = ( | |
unicodedata.normalize("NFKD", filename) | |
.encode("ascii", "ignore") | |
.decode("ascii") | |
) | |
# Replace ampersands with 'and' | |
filename = re.sub(r"&", "and", filename) | |
# Remove characters that aren't alphanumerics, underscores, or hyphens | |
filename = re.sub(r"[^\w\s-]", "", filename) | |
# Convert spaces or repeated dashes to single dashes | |
filename = re.sub(r"[-\s]+", "_", filename) | |
# Also strip leading and trailing whitespace, dashes, and underscores | |
return filename.strip("-_") | |
def is_mp3(filename): | |
return filename.lower().endswith(".mp3") | |
def mkdir(path: str) -> None: | |
"""Make a directory recursively | |
Functionality should be equivalent to `mkdir -p`. | |
""" | |
try: | |
os.makedirs(path) | |
except OSError as e: | |
if e.errno != errno.EEXIST or not os.path.isdir(path): | |
raise | |
def process_file(directory, filename): | |
fullpath = os.path.join(directory, filename) | |
if not is_mp3(filename): | |
print("Not an MP3, skipping: {}".format(fullpath)) | |
return | |
print("Processing file: {}".format(fullpath)) | |
metadata = mutagen.id3.Open(fullpath) | |
artists = metadata["TPE1"] | |
title = metadata["TIT2"] | |
new_filename = "{} {}".format(title, artists) | |
new_filename = slugify(new_filename) | |
new_filename = "{}.mp3".format(new_filename) | |
print("New filename: {}".format(new_filename)) | |
new_directory = "{}.renamed".format(directory) | |
mkdir(new_directory) | |
new_fullpath = os.path.join(new_directory, new_filename) | |
# Detect duplicates so we can warn at the end | |
global OUTPUT_FILENAMES | |
global DUPLICATES | |
if new_fullpath in OUTPUT_FILENAMES: | |
DUPLICATES.add(new_fullpath) | |
else: | |
OUTPUT_FILENAMES.add(new_fullpath) | |
copyfile(fullpath, new_fullpath) | |
def process_files(directory): | |
for root, _, files in os.walk(directory): | |
for f in files: | |
process_file(root, f) | |
def main(): | |
# Download files from beatport and unzip them into a directory structure such as: | |
# Temp/ | |
# |_ Beatport/ | |
# |_ Song 1.mp3 | |
# |_ Song 2.mp3 | |
# | |
# The Temp directory should not contain any other mp3 files, either | |
# directly or in a child directory. Run the script, which will copy the | |
# folder structure of Beatport into Beatport.renamed, renaming all mp3 | |
# files based on metadata in the ID3 tags. | |
process_files(".") | |
global DUPLICATES | |
for dupe in DUPLICATES: | |
print( | |
'WARNING: Duplicate output filename "{}", possible loss of data. This can occur if two files have the same ID3 metadata for title and artist.'.format( | |
dupe | |
) | |
) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment