Introduction
Managing a large music library can be challenging, especially when files are scattered across folders and lack consistent metadata. I wanted a tool that could scan my music collection, extract metadata (artist, album, title, year, genre), and store it in a database for easy searching and categorization. This blog post documents my journey from idea to working solution using Python, Mutagen, and SQLite.
The Idea
I started with a simple goal: automate the process of cataloging my music files. I wanted to:
- Recursively scan a directory for music files (MP3, FLAC, M4A)
- Extract metadata from each file
- Store the metadata in a SQLite database
- Update the database if files change
A caveat is I am using plexmedia server to manage and play my music, so this script is primarily for my own organizational purposes, to learn, and to determine if I really want to pay for the plex pass for simply having a “smart” playlist generator. Why can’t I just do this myself?
Choosing the Tools
I gravitated to using python since there are so many tools available for processing files and that maybe someday I could access via a webapp. For metadata extraction, I found Mutagen, a powerful library that supports many audio formats. SQLite provided a lightweight, file-based database solution.
Writing the Code
1. Scanning Directories
I used Python’s os.walk to recursively scan the target directory and collect all files with allowed extensions:
for root, dirs, files in os.walk(music_dir):
for f in files:
if os.path.splitext(f)[1].lower() in ALLOWED_EXTENSIONS:
music_files.append(os.path.join(root, f))2. Extracting Metadata
Mutagen makes it easy to extract metadata. For MP3 files, I used mutagen.mp3.MP3, and for FLAC files, mutagen.flac.FLAC. I wrote a helper function to select the right class based on file extension:
def get_mutagen_class(file_path):
ext = os.path.splitext(file_path)[1].lower()
if ext == ".mp3":
return MP3(file_path)
elif ext == ".flac":
return flac.FLAC(file_path)
return NoneThen, I extracted fields like artist, album, title, date, and genre:
def extract_metadata(file_path):
audio = get_mutagen_class(file_path)
if audio is None:
return None, None, None, None, None
artist = audio.get('artist', [None])[0] or None
album = audio.get('album', [None])[0] or None
title = audio.get('title', [None])[0] or None
date = audio.get('date', [None])[0] or None
genre = audio.get('genre', [None])[0] or None
return artist, album, title, date, genre3. Storing Metadata in SQLite
I created a songs table in music.db to store the metadata. For each file, I checked if it already existed in the database and updated or inserted as needed:
cursor.execute("SELECT * FROM songs WHERE file_path = ?", (file,))
existing_entry = cursor.fetchone()
if existing_entry:
cursor.execute("UPDATE songs SET artist = ?, album = ?, title = ?, date = ?, genre = ? WHERE file_path = ?",
(metadata[0], metadata[1], metadata[2], metadata[3], metadata[4], file))
db_conn.commit()
else:
cursor.execute("INSERT INTO songs (artist, album, title, date, genre, file_path) VALUES (?, ?, ?, ?, ?, ?)",
(metadata[0], metadata[1], metadata[2], metadata[3], metadata[4], file))
db_conn.commit()4. Database Example
After running the script, my database was populated with entries like this:

4. Handling Edge Cases
I learned that not all files have complete metadata. For example, the year is often stored as the “date” tag, not “year”. I also added error handling to skip files that couldn’t be read.
Another edge case—which I haven’t addressed yet—was that the directories in the folders where I store all my music have system specific folders used for backup and recycle. So I will need to add code to skip those folders in the future.
Lessons Learned
- Mutagen is flexible but metadata tags can vary between formats and files.
- Always check for missing or unexpected tags.
- SQLite is perfect for small, local databases.
- Commenting code and writing helper functions makes future maintenance easier.
Conclusion and Future work
This project helped me organize my music library and taught me a lot about audio metadata and Python scripting. If you have a large collection of music files, consider automating your cataloging process—you’ll save time and discover hidden gems in your library!
For future improvements, I plan to:
- Add support for more audio formats (e.g., OGG, WAV)
- Assign genres and tags using my own ollama server
- Create a simple web interface to browse and search the database
- Create playlists based on metadata (e.g., all songs from a specific year or genre) where I can just say “Play me programming music” and it will play songs it considers good for programming based on the metadata where that metadata was assigned by my ollama server.