Scorch/database.py
2020-06-03 15:50:49 -04:00

94 lines
2.5 KiB
Python

#!/usr/bin/env python3
"""
Database operations for Scorch.
"""
import os
import multiprocessing
from datetime import datetime, timezone
import asyncpg
import mutagen
import mutagen.mp3
import config
MUSIC_EXT = ['flac', 'mp3']
async def build_library(root_dir):
"""Walks the music directory and builds a library of tracks."""
print("Building library")
filepaths = []
for dir_name, sub_dirs, files in os.walk(root_dir):
for file in files:
if not os.path.splitext(file)[1][1:] in MUSIC_EXT:
continue
filepath = os.path.join(root_dir, dir_name, file)
last_modified = datetime.fromtimestamp(
os.path.getmtime(filepath), timezone.utc)
filepaths.append((filepath, last_modified))
db_pool = await asyncpg.create_pool(**config.db)
async with db_pool.acquire() as conn:
tracks_prev = await conn.fetch("SELECT * FROM track")
tracks_prev = {track['filepath']: track for track in tracks_prev}
global worker
def worker(tup):
"""Worker for multi-processing tracks."""
filepath, last_modified = tup
track_prev = tracks_prev.get(filepath)
if track_prev:
if track_prev['last_modified'] >= last_modified:
return tuple(track_prev)
data = read_track(filepath)
return data
with multiprocessing.Pool() as pool:
mapping = pool.imap(worker, filepaths)
tracks = []
prev_percent = 0
while True:
try:
tracks.append(mapping.next())
except StopIteration:
break
percent = round(len(tracks) / len(filepaths) * 100, 2)
if percent >= prev_percent + 2.5:
print(f"{percent}%")
prev_percent = percent
async with db_pool.acquire() as conn:
await conn.execute("DELETE FROM track")
await conn.executemany(
"INSERT INTO track VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)",
tracks
)
print("Done")
def read_track(filepath):
"""
Reads the specified file and extracts relevant information from it.
Returns a tuple.
"""
if filepath.endswith("mp3"):
m = mutagen.mp3.EasyMP3(filepath)
else:
m = mutagen.File(filepath)
artist = m.get('artist', [''])[0]
if m.get('albumartist'):
albumartist = m.get('albumartist', [''])[0]
else:
albumartist = m.get('artist', [''])[0]
date = m.get('date', [''])[0]
album = m.get('album', [''])[0]
discnumber = m.get('discnumber', [''])[0]
tracknumber = m.get('tracknumber', [''])[0]
title = m.get('title', [''])[0]
genre = m.get('genre', [''])[0]
length = m.info.length
last_modified = datetime.fromtimestamp(
os.path.getmtime(filepath), timezone.utc)
d = locals()
d.pop('m')
return tuple(d.values())