From cd630834a985be4b39a673d022e180de3ff20517 Mon Sep 17 00:00:00 2001 From: Michaƫl Ball Date: Sat, 13 Dec 2014 16:41:35 +0000 Subject: Initial commit --- .gitignore | 12 ++ common/__init__.py | 0 common/utils.py | 57 +++++++ db/__init__.py | 0 db/db_manager.py | 54 ++++++ library.db | Bin 0 -> 17408 bytes library.py | 88 ++++++++++ mach2.ini | 4 + mach2.py | 95 +++++++++++ models/__init__.py | 0 models/album.py | 114 +++++++++++++ models/artist.py | 115 +++++++++++++ models/track.py | 487 +++++++++++++++++++++++++++++++++++++++++++++++++++++ watcher.py | 66 ++++++++ 14 files changed, 1092 insertions(+) create mode 100644 .gitignore create mode 100644 common/__init__.py create mode 100644 common/utils.py create mode 100644 db/__init__.py create mode 100644 db/db_manager.py create mode 100644 library.db create mode 100755 library.py create mode 100644 mach2.ini create mode 100644 mach2.py create mode 100644 models/__init__.py create mode 100644 models/album.py create mode 100644 models/artist.py create mode 100644 models/track.py create mode 100644 watcher.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8374d58 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +# Development stuff +.python-version +tags +GRTAGS +GTAGS +GPATH +*.swp +*.pyc +library.db-journal + +public +tmp diff --git a/common/__init__.py b/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/common/utils.py b/common/utils.py new file mode 100644 index 0000000..484179a --- /dev/null +++ b/common/utils.py @@ -0,0 +1,57 @@ +def make_where_clause(params): + """Create a where clause for each key-value pair in a dict, joined + by AND. + + Parameters + ---------- + params : dict + A dict of keys and values + """ + + where_items = [] + where_clause = None + + try: + for key in params.keys(): + where_items.append("%s=:%s" % (key, key)) + + where_statement = None + if len(where_items) > 1: + where_statement = " AND ".join(where_items) + else: + where_statement = where_items[0] + + where_clause = " ".join(("WHERE", where_statement)) + except AttributeError: + pass + + return where_clause + + +def update_clause_from_dict(data): + """Create an update clause from a dictionary + + Parameters + __________ + data: dict + A dict of the new value and the column name as key + """ + + update_items = [] + set_statement = None + update_clause = None + + try: + for key in data.keys(): + update_items.append("%s = :%s", (key, key)) + + if len(update_items) > 1: + update_clause = ", ".join(update_items) + else: + update_clause = update_items[0] + + set_statement = " ".join(("SET", update_clause)) + except AttributeError: + pass + + return set_statement diff --git a/db/__init__.py b/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/db/db_manager.py b/db/db_manager.py new file mode 100644 index 0000000..44d6b96 --- /dev/null +++ b/db/db_manager.py @@ -0,0 +1,54 @@ +import configparser +import sqlite3 + + +class DbManager: + class __DbManager: + def __init__(self): + config = configparser.ConfigParser() + config.read("mach2.ini") + self.conn = sqlite3.connect(config["DEFAULT"]["database"]) + + def __str__(self): + return repr(self) + + def execute(self, *args): + return self.conn.execute(*args) + + def cursor(self): + return self.conn.cursor() + + def commit(self): + return self.conn.commit() + + def close(self): + return self.conn.close() + + def rollback(self): + return self.conn.rollback() + + def executemany(self, *args): + return self.conn.executemany(*args) + + def executescript(self, *args): + return self.conn.executescript(*args) + + def create_function(self, *args): + return self.conn.create_function(*args) + + def create_aggregate(self, *args): + return self.conn.create_aggregate(*args) + + def create_collation(self, *args): + return self.conn.create_collation(*args) + + def interrupt(self): + return self.conn.interrupt() + + instance = None + + def __new__(cls): + if not DbManager.instance: + DbManager.instance = DbManager.__DbManager() + + return DbManager.instance diff --git a/library.db b/library.db new file mode 100644 index 0000000..46011ca Binary files /dev/null and b/library.db differ diff --git a/library.py b/library.py new file mode 100755 index 0000000..762b18b --- /dev/null +++ b/library.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python +import configparser +import mutagen +import os + +from models.track import Track + + +def run(path=None): + print("Scanning files") + + if path is not None: + if os.path.isdir(path): + store_dir(path) + else: + store_file(path) + else: + store_dir("/media/Music") + + +def store_file(path): + m = mutagen.File(path, easy=True) + if m: + if not Track.store(path, m): + print("Problem saving %s" % (path,)) + + +def store_dir(path): + file_store = [] + + for root, dirs, files in os.walk(path): + for name in files: + file_path = "".join([root, "/", name]) + file_store.append(file_path) + + file_store.sort() + j = 0 + media_files = 0 + print("Storing files") + for file_path in file_store: + j += 1 + m = mutagen.File(file_path, easy=True) + if m: + if not Track.store(file_path, m): + print("Problem saving %s" % (file_path,)) + + media_files += 1 + print( + "%d%% complete, (%d files)" % (((j / len(file_store)) * 100), + j) + ) + print("Stored %d tracks" % (media_files,)) + + +def delete_file(path): + track = Track.find_by_path(path) + + if track: + track_album = track.album + track_artists = track.artists + + track.delete() + + if track_album and len(track_album.tracks) == 0: + track_album.delete() + + for artist in track_artists: + if len(artist.tracks) == 0: + artist.delete() + + +def update_file(path): + m = mutagen.File(path, easy=True) + if m: + track = Track.find_by_path(path) + track.update(m) + + +def update_track_filename(oldpath, newpath): + track = Track.find_by_path(oldpath) + track.filename = newpath + track.save() + +if __name__ == "__main__": + config = configparser.ConfigParser() + config.read("mach2.ini") + + run(config["DEFAULT"]["media_dir"]) diff --git a/mach2.ini b/mach2.ini new file mode 100644 index 0000000..ca71e7e --- /dev/null +++ b/mach2.ini @@ -0,0 +1,4 @@ +[DEFAULT] +database = library.db +media_dir = /path/to/music/dir +debug = True diff --git a/mach2.py b/mach2.py new file mode 100644 index 0000000..479c0ab --- /dev/null +++ b/mach2.py @@ -0,0 +1,95 @@ +import configparser +import json + +from flask import Flask + +from models.album import Album +from models.artist import Artist +from models.track import Track + + +app = Flask(__name__) +app.config.from_object(__name__) + + +@app.route("/") +def hello(): + return "Hello world!" + + +@app.route("/search/album/") +def album_search(albumname): + albums = [] + for album in Album.search(name=albumname): + albums.append(album.__dict__) + + return json.dumps(albums) + + +@app.route("/search/artist/") +def artist_search(artistname): + artists = [] + for artist in Artist.search(name=artistname): + artists.append(artist.__dict__) + + return json.dumps(artists) + + +@app.route("/search/track/") +def track_search(trackname): + tracks = [] + for track in Track.search(name=trackname): + tracks.append(track.__dict__) + + return json.dumps(tracks) + + +@app.route("/artist//tracks") +def artist_tracks(artist_id): + tracks = [] + artist = Artist(artist_id) + + for track in artist.tracks: + tracks.append(track.__dict__) + + return json.dumps(tracks) + + +@app.route("/artist//albums") +def artist_albums(artist_id): + albums = [] + artist = Artist(artist_id) + + for album in artist.albums: + albums.append(album.__dict__) + + return json.dumps(albums) + + +@app.route("/album//tracks") +def album_tracks(album_id): + tracks = [] + album = Album(album_id) + + for track in album.tracks: + tracks.append(track.__dict__) + + return json.dumps(tracks) + + +@app.route("/album//artists") +def album_artists(album_id): + artists = [] + album = Album(album_id) + + for artist in album.artists: + artists.append(artist.__dict__) + + return json.dumps(artists) + + +if __name__ == "__main__": + config = configparser.ConfigParser() + config.read("mach2.ini") + + app.run(debug=config["DEFAULT"]["debug"]) diff --git a/models/__init__.py b/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/models/album.py b/models/album.py new file mode 100644 index 0000000..727156f --- /dev/null +++ b/models/album.py @@ -0,0 +1,114 @@ +from common import utils +from db.db_manager import DbManager + + +class Album(): + def __init__(self, id=None, **kwargs): + if id is not None: + db = DbManager() + for row in db.execute("""SELECT * FROM album WHERE id = ?""", + (id,)): + setattr(self, "id", id) + setattr(self, "name", row[1]) + setattr(self, "date", row[2]) + else: + for (key, value) in kwargs.items(): + setattr(self, key, value) + + def delete(self): + db = DbManager() + + for track in self.tracks: + track.delete() + + delete_sql = "DELETE FROM album WHERE id = ?" + db.execute(delete_sql, (self.id,)) + + delete_track_rel_sql = "DELETE FROM album_track WHERE album_id = ?" + db.execute(delete_track_rel_sql, (self.id,)) + + delete_artist_rel_sql = "DELETE FROM album_artist WHERE album_id = ?" + db.execute(delete_artist_rel_sql, (self.id,)) + + db.commit() + + return True + + @property + def artists(self): + from models.artist import Artist + + if not hasattr(self, "_artists"): + setattr(self, "_artists", []) + + db = DbManager() + + for row in db.execute("""SELECT artist.* FROM artist INNER JOIN + album_artist ON artist.id = + album_artist.artist_id WHERE album_id = ? + ORDER BY name ASC""", (self.id,)): + artist = Artist(id=row[0], name=row[1], sortname=row[2], + musicbrainz_artistid=row[3]) + self._artists.append(artist) + + return self._artists + + @property + def tracks(self): + from models.track import Track + + if not hasattr(self, "_tracks"): + setattr(self, "_tracks", []) + + db = DbManager() + + for row in db.execute("""SELECT track.* FROM track + INNER JOIN album_track ON track.id = + album_track.track_id WHERE album_id = ? + ORDER BY tracknumber ASC""", (self.id,)): + + track = Track(id=row[0], tracknumber=row[1], name=row[2], + grouping=row[3], filename=row[4]) + self._tracks.append(track) + + return self._tracks + + def save(self): + dirty_attributes = {} + + # check if the internal dict has been modified + for (attr, value) in self.__dict__.items(): + if self.__data[attr] != getattr(self, attr): + dirty_attributes[attr] = value + + if len(dirty_attributes) > 0: + db = DbManager() + + set_clause = utils.update_clause_from_dict(dirty_attributes) + + dirty_attributes[id] = self.id + + sql = " ".join(("UPDATE album"), set_clause, "WHERE id = :id") + db.execute(sql, dirty_attributes) + db.commit() + + def search(**search_params): + albums = [] + + db = DbManager() + + where_clause = utils.make_where_clause(search_params) + + result = None + if where_clause: + statement = " ".join(("SELECT * FROM album", where_clause)) + result = db.execute(statement, search_params) + else: + result = db.execute("SELECT * FROM album") + + for row in result: + albums.append( + Album(id=row[0], name=row[1], date=row[2]) + ) + + return albums diff --git a/models/artist.py b/models/artist.py new file mode 100644 index 0000000..eaae27e --- /dev/null +++ b/models/artist.py @@ -0,0 +1,115 @@ +from common import utils +from db.db_manager import DbManager + + +class Artist: + def __init__(self, id=None, **kwargs): + if id is not None: + db = DbManager() + for row in db.execute("""SELECT * FROM artist WHERE id = ?""", + (id,)): + setattr(self, "id", id) + setattr(self, "name", row[1]) + setattr(self, "sortname", row[2]) + setattr(self, "musicbrainz_artistid", row[3]) + else: + for (key, value) in kwargs.items(): + setattr(self, key, value) + + def delete(self): + db = DbManager() + + for album in self.albums: + album.delete() + + delete_sql = "DELETE FROM artist WHERE id = ?" + db.execute(delete_sql, (self.id,)) + + delete_track_rel_sql = "DELETE FROM artist_track WHERE artist_id = ?" + db.execute(delete_track_rel_sql, (self.id,)) + + delete_album_rel_sql = "DELETE FROM album_artist WHERE artist_id = ?" + db.execute(delete_album_rel_sql, (self.id,)) + + db.commit() + + return True + + @property + def tracks(self): + from models.track import Track + + if not hasattr(self, "_tracks"): + setattr(self, "_tracks", []) + + db = DbManager() + + for row in db.execute("""SELECT track.* FROM track + INNER JOIN artist_track ON track.id = + artist_track.track_id WHERE artist_id = ? + ORDER BY name ASC""", (self.id,)): + + track = Track(id=row[0], tracknumber=row[1], name=row[2], + grouping=row[3], filename=row[4]) + self._tracks.append(track) + + return self._tracks + + @property + def albums(self): + from models.album import Album + + if not hasattr(self, "_albums"): + setattr(self, "_albums", []) + + db = DbManager() + + for row in db.execute("""SELECT album.* FROM album + INNER JOIN album_artist ON album.id = + album_artist.album_id WHERE artist_id = ? + ORDER BY date ASC""", (self.id,)): + album = Album(id=row[0], name=row[1], date=row[2]) + self._albums.append(album) + + return self._albums + + def save(self): + dirty_attributes = {} + + # check if the internal dict has been modified + for (attr, value) in self.__dict__.items(): + if self.__data[attr] != getattr(self, attr): + dirty_attributes[attr] = value + + if len(dirty_attributes) > 0: + db = DbManager() + + set_clause = utils.update_clause_from_dict(dirty_attributes) + + dirty_attributes[id] = self.id + + sql = " ".join(("UPDATE artist"), set_clause, "WHERE id = :id") + db.execute(sql, dirty_attributes) + db.commit() + + def search(**search_params): + artists = [] + + db = DbManager() + + where_clause = utils.make_where_clause(search_params) + + result = [] + if where_clause: + statement = " ".join(("SELECT * FROM artist", where_clause)) + result = db.execute(statement, search_params) + else: + result = db.execute("SELECT * FROM artist") + + for row in result: + artists.append( + Artist(id=row[0], name=row[1], sortname=row[2], + musicbrainz_artistid=row[3]) + ) + + return artists diff --git a/models/track.py b/models/track.py new file mode 100644 index 0000000..264db81 --- /dev/null +++ b/models/track.py @@ -0,0 +1,487 @@ +import sqlite3 + +from common import utils +from db.db_manager import DbManager +from models.artist import Artist +from models.album import Album + + +class Track: + + def __init__(self, id=None, **kwargs): + + setattr(self, "__data", {}) + + if id is not None: + db = DbManager() + for row in db.execute("""SELECT * FROM track WHERE id = ?""", + (id,)): + setattr(self, "id", row[0]) + setattr(self, "tracknumber", row[1]) + setattr(self, "name", row[2]) + setattr(self, "grouping", row[3]) + setattr(self, "filename", row[4]) + else: + for (key, value) in kwargs.items(): + setattr(self, key, value) + self.__data[key] = value + + def delete(self): + db = DbManager() + + delete_sql = "DELETE FROM track WHERE id = ?" + db.execute(delete_sql, (self.id,)) + db.commit() + + return True + + @property + def album(self): + if not hasattr(self, "_album"): + setattr(self, "_album", None) + + db = DbManager() + + for row in db.execute("""SELECT album.* FROM album INNER JOIN + album_track ON album.id = + album_track.album_id WHERE track_id = ? + LIMIT 1""", (self.id,)): + setattr(self, "_album", Album(row[0])) + + return self._album + + @property + def artists(self): + if not hasattr(self, "_artists"): + db = DbManager() + + setattr(self, "_artists", []) + for row in db.execute("""SELECT artist.* FROM artist INNER JOIN + artist_track ON artist.id = + artist_track.artist_id WHERE + artist.id = ?""", (self.id,)): + self._artists.append(Artist(row[0])) + + return self._artists + + def update(self, metadata): + db = DbManager() + c = db.cursor() + + artist_names = metadata["artist"] + musicbrainz_artist_ids = [] + artistsorts = [] + try: + musicbrainz_artist_ids = metadata["musicbrainz_artistid"] + except KeyError: + pass + try: + artistsorts = metadata["artistsort"] + except KeyError: + pass + + i = 0 + artists = [] + + for artist_name in artist_names: + musicbrainz_artistid = None + artistsort = None + + try: + musicbrainz_artistid = musicbrainz_artist_ids[i] + except IndexError: + pass + try: + artistsort = artistsorts[i] + except IndexError: + pass + + rows = None + + if musicbrainz_artistid: + rows = c.execute("""SELECT * FROM artist WHERE + musicbrainz_artist_id = ?""", + (musicbrainz_artistid,)) + else: + rows = c.execute("""SELECT * FROM artist WHERE + name = ?""", (artist_name,)) + + row = rows.fetchone() + + if row: + artist = Artist(id=row[0], name=row[1], + sortname=row[2], + musicbrainz_artist_id=row[3]) + + if artist.name != artist_name: + c.execute("""UPDATE artist SET name = ? WHERE id = ?""", + (artist_name, artist.id)) + artist.name = artist_name + + if artist.sortname != artistsort: + c.execute("""UPDATE artist SET sortname = ? WHERE id = + ? """, (artistsort, id)) + artist.sortname = artistsort + + else: + c.execute("""INSERT INTO artist + (name, sortname, musicbrainz_artist_id) VALUES( + ?,?,?)""", (artist_name, artistsort, + musicbrainz_artistid)) + + artist = Artist( + id=c.lastrowid, name=artist_name, + sortname=artistsort, + musicbrainz_artist_id=musicbrainz_artistid + ) + + i += 1 + + artists.append(artist) + + album_name = None + album_date = None + mb_albumid = None + + album = None + try: + album_name = metadata["album"][0] + except KeyError: + pass + try: + album_date = metadata["date"][0] + except KeyError: + pass + + try: + mb_albumid = metadata["musicbrainz_albumid"][0] + except KeyError: + pass + + if mb_albumid: + rows = c.execute( + """SELECT * FROM album WHERE musicbrainz_albumid = ?""", + (mb_albumid,) + ) + + row = rows.fetchone() + + if row: + album = Album(id=row[0], name=row[1], date=row[2], + mb_albumid=row[3]) + else: + c.execute("""INSERT INTO album (name, `date`, + musicbrainz_albumid) VALUES (?,?,?)""", + (album_name, album_date, mb_albumid)) + + album = Album(id=c.lastrowid, name=album_name, + date=album_date, musicbrainz_albumid=mb_albumid) + + elif album_name: + rows = c.execute( + """SELECT album.* FROM album INNER JOIN album_artist ON + album_artist.album_id = album.id WHERE album.name = ? + AND artist_id = ?""", (album_name, artist.id) + ) + row = rows.fetchone() + + if row: + album = Album(id=row[0], name=row[1], date=row[2]) + else: + c.execute("""INSERT INTO album (name, `date`) VALUES + (?,?)""", (album_name, album_date)) + + album = Album(id=c.lastrowid, name=album_name, + date=album_date) + + if album: + if album.name != album_name: + c.execute("""UPDATE album SET name = ? WHERE id = ?""", + (album_name, album.id)) + album.name = album_name + + if album.date != album_date: + c.execute("""UPDATE album SET date = ? WHERE id = ?""", + (album_date, album.id)) + album.date = album_date + + for artist in artists: + if album: + try: + c.execute( + """INSERT INTO album_artist (artist_id, + album_id) VALUES(?,?)""", + (artist.id, album.id) + ) + except sqlite3.IntegrityError: + pass + + track_number = None + track_name = None + track_grouping = None + try: + track_number = metadata["tracknumber"][0] + except KeyError: + pass + try: + track_name = metadata["title"][0] + except KeyError: + pass + try: + track_grouping = metadata["grouping"][0] + except KeyError: + pass + + c.execute("""UPDATE track SET tracknumber = ?, name = ?, + grouping = ? WHERE id = ?""", + (track_number, track_name, track_grouping, + self.id)) + + if album: + try: + c.execute("""INSERT INTO album_track (album_id, + track_id) VALUES(?,?)""", + (album.id, self.id)) + except sqlite3.IntegrityError: + pass + + for artist in artists: + try: + c.execute("""INSERT INTO artist_track + (artist_id, track_id) VALUES(?,?)""", + (artist.id, self.id)) + except sqlite3.IntegrityError: + pass + + db.commit() + c.close() + return True + + def save(self): + dirty_attributes = {} + + # check if the internal dict has been modified + for (attr, value) in self.__dict__.items(): + if self.__data[attr] != getattr(self, attr): + dirty_attributes[attr] = value + + if len(dirty_attributes) > 0: + db = DbManager() + + set_clause = utils.update_clause_from_dict(dirty_attributes) + + dirty_attributes[id] = self.id + + sql = " ".join(("UPDATE track"), set_clause, "WHERE id = :id") + db.execute(sql, dirty_attributes) + db.commit() + + def search(**search_params): + db = DbManager() + tracks = [] + + where_clause = utils.make_where_clause(search_params) + + result = None + if where_clause: + statement = " ".join(("SELECT * FROM track", where_clause)) + result = db.execute(statement, search_params) + else: + result = db.execute("SELECT * FROM track") + + for row in result: + tracks.append( + Track(id=row[0], tracknumber=row[1], name=row[3], + grouping=row[3], filename=row[4]) + ) + + return tracks + + def find_by_path(path): + db = DbManager() + track = None + + for row in db.execute("SELECT * FROM track WHERE filename = ? LIMIT 1", + (path,)): + track = Track(row[0]) + + return track + + def store(filename, metadata): + db = DbManager() + c = db.cursor() + + artist_names = metadata["artist"] + musicbrainz_artist_ids = [] + artistsorts = [] + try: + musicbrainz_artist_ids = metadata["musicbrainz_artistid"] + except KeyError: + pass + try: + artistsorts = metadata["artistsort"] + except KeyError: + pass + + i = 0 + artists = [] + + for artist_name in artist_names: + musicbrainz_artistid = None + artistsort = None + try: + musicbrainz_artistid = musicbrainz_artist_ids[i] + except IndexError: + pass + try: + artistsort = artistsorts[i] + except IndexError: + pass + + rows = None + if musicbrainz_artistid: + rows = c.execute("""SELECT * FROM artist WHERE + musicbrainz_artist_id = ?""", + (musicbrainz_artistid,)) + else: + rows = c.execute("""SELECT * FROM artist WHERE + name = ?""", (artist_name,)) + row = rows.fetchone() + if row: + artist = Artist(id=row[0], name=row[1], + sortname=row[2], + musicbrainz_artist_id=row[3]) + else: + c.execute("""INSERT INTO artist + (name, sortname, musicbrainz_artist_id) VALUES( + ?,?,?)""", (artist_name, artistsort, + musicbrainz_artistid)) + + artist = Artist( + id=c.lastrowid, name=artist_name, + sortname=artistsort, + musicbrainz_artist_id=musicbrainz_artistid + ) + + i += 1 + + artists.append(artist) + + album_name = None + album_date = None + mb_albumid = None + + album = None + try: + album_name = metadata["album"][0] + except KeyError: + pass + try: + album_date = metadata["date"][0] + except KeyError: + pass + + try: + mb_albumid = metadata["musicbrainz_albumid"][0] + except KeyError: + pass + + if mb_albumid: + rows = c.execute( + """SELECT * FROM album WHERE musicbrainz_albumid = ?""", + (mb_albumid,) + ) + + row = rows.fetchone() + + if row: + album = Album(id=row[0], name=row[1], date=row[2], + mb_albumid=row[3]) + else: + c.execute("""INSERT INTO album (name, `date`, + musicbrainz_albumid) VALUES (?,?,?)""", + (album_name, album_date, mb_albumid)) + + album = Album(id=c.lastrowid, name=album_name, + date=album_date, musicbrainz_albumid=mb_albumid) + + elif album_name: + rows = c.execute( + """SELECT album.* FROM album INNER JOIN album_artist ON + album_artist.album_id = album.id WHERE album.name = ? + AND artist_id = ?""", (album_name, artist.id) + ) + row = rows.fetchone() + + if row: + album = Album(id=row[0], name=row[1], date=row[2]) + else: + c.execute("""INSERT INTO album (name, `date`) VALUES + (?,?)""", (album_name, album_date)) + + album = Album(id=c.lastrowid, name=album_name, + date=album_date) + + for artist in artists: + if album: + try: + c.execute( + """INSERT INTO album_artist (artist_id, + album_id) VALUES(?,?)""", + (artist.id, album.id) + ) + except sqlite3.IntegrityError: + pass + + track_number = None + track_name = None + track_grouping = None + try: + track_number = metadata["tracknumber"][0] + except KeyError: + pass + try: + track_name = metadata["title"][0] + except KeyError: + pass + try: + track_grouping = metadata["grouping"][0] + except KeyError: + pass + + track = None + rows = c.execute("""SELECT * FROM track WHERE filename = ?""", + (filename,)) + row = rows.fetchone() + if row: + track = Track(id=row[0], tracknumber=row[1], name=row[2], + grouping=row[3], filename=row[4]) + else: + c.execute("""INSERT INTO track (tracknumber, name, + grouping, filename) VALUES(?,?,?,?)""", + (track_number, track_name, track_grouping, + filename)) + + track = Track(id=c.lastrowid, tracknumber=track_number, + name=track_name, grouping=track_grouping, + filename=filename) + + if album: + try: + c.execute("""INSERT INTO album_track (album_id, + track_id) VALUES(?,?)""", + (album.id, track.id)) + except sqlite3.IntegrityError: + pass + + for artist in artists: + try: + c.execute("""INSERT INTO artist_track + (artist_id, track_id) VALUES(?,?)""", + (artist.id, track.id)) + except sqlite3.IntegrityError: + pass + + db.commit() + c.close() + return True diff --git a/watcher.py b/watcher.py new file mode 100644 index 0000000..7dbd434 --- /dev/null +++ b/watcher.py @@ -0,0 +1,66 @@ +import atexit +import configparser + +import pyinotify + +import library + + +class EventHandler(pyinotify.ProcessEvent): + def process_IN_CREATE(self, event): + print("Creating:", event.pathname) + library.run(event.pathname) + + def process_IN_DELETE(self, event): + print("Removing:", event.pathname) + library.delete_file(event.pathname) + + def process_IN_MOVED_TO(self, event): + print("Moved to:", event.pathname) + library.update_track_filename(event.src_pathname, event.pathname) + + def process_IN_MODIFY(self, event): + print("Modified:", event.pathname) + library.update_file(event.pathname) + + +class LibraryWatcher: + + def __init__(self, path): + print("Setting up library watcher") + + if not hasattr(self, "path"): + setattr(self, "path", path) + + if not hasattr(self, "wm"): + setattr(self, "wm", pyinotify.WatchManager()) + + mask = pyinotify.IN_DELETE | pyinotify.IN_CREATE | \ + pyinotify.IN_MOVED_TO | pyinotify.IN_MOVED_FROM | \ + pyinotify.IN_MODIFY + + if not hasattr(self, "notifier"): + setattr(self, + "notifier", + pyinotify.ThreadedNotifier(self.wm, EventHandler())) + + self.notifier.coalesce_events() + self.notifier.start() + + if not hasattr(self, "wdd"): + setattr(self, "wdd", self.wm.add_watch(path, mask, rec=True, + auto_add=True)) + + print("Set up watch on ", path) + + atexit.register(self.stop) + + def stop(self): + if self.wdd[self.path] > 0: + self.wm.rm_watch(self.wdd[self.path], rec=True) + +if __name__ == "__main__": + config = configparser.ConfigParser() + config.read("mach2.ini") + + watch = LibraryWatcher(config["DEFAULT"]["media_dir"]) -- cgit v1.2.3