diff --git a/songdb.py b/songdb.py new file mode 100755 index 0000000..e28f52e --- /dev/null +++ b/songdb.py @@ -0,0 +1,129 @@ +#!/usr/bin/python3 + +import argparse +import hashlib +import logging +import os +import sqlalchemy + +import pytiger.logging.config + +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy import Column, Float, DateTime, Integer, String +from sqlalchemy.orm.exc import NoResultFound +from sqlalchemy.orm import sessionmaker +from tinytag import TinyTag + +# "Constants" +ROOT = "/home/kae/music" +DISPLAY_SQL = False +MYSQL_CONNECT = "mysql+mysqldb://songdb:songdb@localhost/songdb" + +# Instantiate logging +pytiger.logging.config.basic_config(stderr=False, level=logging.INFO) +log = logging.getLogger(__name__) +log.info("Starting") + +# Declare mapping +Base = declarative_base() + + +# Database classes +class Tracks(Base): + __tablename__ = 'tracks' + + id = Column(Integer, primary_key=True, autoincrement=True) + title = Column(String(256), index=True) + artist = Column(String(256), index=True) + length = Column(Integer, index=True) + path = Column(String(2048), index=False, nullable=False) + mtime = Column(Float, index=True) + lastplayed = Column(DateTime, index=True, default=None) + + def __repr__(self): + return ( + f"" + ) + + @classmethod + def get_or_create(cls, session, path): + try: + track = session.query(cls).filter(cls.path == path).one() + except NoResultFound: + track = Tracks() + track.path = path + session.add(track) + return track + + +def main(): + "Main loop" + + # Set up database connection + log.info("Connect to database") + engine = sqlalchemy.create_engine(f"{MYSQL_CONNECT}?charset=utf8", + encoding='utf-8', + echo=DISPLAY_SQL) + Base.metadata.create_all(engine) + + # Create a Session + Session = sessionmaker(bind=engine) + session = Session() + + # Parse command line + p = argparse.ArgumentParser() + p.add_argument('-u', '--update', + action="store_true", dest="update", + default=True, help="Update database") + args = p.parse_args() + + # Run as required + if args.update: + log.info("Updating database") + update_db(session) + + log.info("Finished") + + +def update_db(session): + """ + Repopulate database + + TODO: remove missing files + """ + + count = 0 + for root, dirs, files in os.walk(ROOT): + for f in files: + count += 1 + path = os.path.join(root, f) + ext = os.path.splitext(f)[1] + if ext in [".flac", ".mp3"]: + track = Tracks.get_or_create(session, + os.path.relpath(path, ROOT)) + tag = TinyTag.get(path) + track.title = tag.title + track.artist = tag.artist + track.length = tag.duration * 1000 + track.mtime = os.path.getmtime(path) + elif ext not in [".jpg"]: + print(f"Unrecognised file type: {path}") + + session.commit() + print(f"{count} files processed") + + +def md5(path): + "https://stackoverflow.com/questions/3431825/" + "generating-an-md5-checksum-of-a-file" + + hash_md5 = hashlib.md5() + with open(path, "rb") as f: + for chunk in iter(lambda: f.read(4096), b""): + hash_md5.update(chunk) + return hash_md5.hexdigest() + + +if __name__ == '__main__': + main()