diff --git a/DBA.py b/DBA.py index 5e5b29e..1f32305 100644 --- a/DBA.py +++ b/DBA.py @@ -2,17 +2,16 @@ import sqlite3 import logging from configparser import ConfigParser + class DBAccess: def __init__(self, db_name=None): - logging.info('Instantiating DBAccess') + logging.info("Instantiating DBAccess") config = ConfigParser() - config.read('config.ini') + config.read("config.ini") if db_name is None: - db_name = config.get('db', 'database') + db_name = config.get("db", "database") self._conn: sqlite3.Connection = sqlite3.connect(db_name) - logging.info(f'DBAccess | self._conn = [\n{type(self._conn)}\n{self._conn}\n]') self._cursor: sqlite3.Cursor = self._conn.cursor() - logging.info(f'DBAccess | self._cursor = [\n{type(self._cursor)}\n{self._cursor}\n]') def __enter__(self): return self @@ -38,9 +37,9 @@ class DBAccess: def execute(self, sql, params): self.cursor.execute(sql, params or ()) - + def executemany(self, sql, seq_of_params): - self.cursor.executemany(sql, seq_of_params) #sqlite has execute many i guess? + self.cursor.executemany(sql, seq_of_params) # sqlite has execute many i guess? def fetchall(self): return self.cursor.fetchall() diff --git a/README.md b/README.md index 7cc7821..8feb894 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,29 @@ PyQt5 music player inspired by MusicBee & iTunes +## Installation: +clone the repo +``` +git clone https://github.com/billypom/musicpom +``` +install system packages +``` +sudo apt install ffmpeg +sudo apt install python3-pyqt5 +``` +create environment +``` +cd musicpom +virtualenv venv +cd .. +cd musicpom +pip install -r requirements.txt +``` +run the program +``` +python3 main.py +``` + ## Todo: - [ ] Delete songs from library (del key || right-click delete) diff --git a/components/MusicTable.py b/components/MusicTable.py index bcc4465..92c3cd6 100644 --- a/components/MusicTable.py +++ b/components/MusicTable.py @@ -7,14 +7,23 @@ from PyQt5.QtGui import ( QDragEnterEvent, QDropEvent, ) -from PyQt5.QtWidgets import QAction, QMenu, QTableView, QShortcut, QMessageBox, QAbstractItemView +from PyQt5.QtWidgets import ( + QAction, + QMenu, + QTableView, + QShortcut, + QMessageBox, + QAbstractItemView, +) from PyQt5.QtCore import QModelIndex, Qt, pyqtSignal, QTimer from utils import add_files_to_library from utils import update_song_in_library from utils import get_id3_tags from utils import get_album_art from utils import set_id3_tag +from utils import delete_and_create_library_database from subprocess import Popen +from sqlite3 import OperationalError import logging import configparser import os @@ -57,8 +66,8 @@ class MusicTable(QTableView): self.database_columns = str(self.config["table"]["columns"]).split(",") self.vertical_scroll_position = 0 self.songChanged = None - self.selected_song_filepath = '' - self.current_song_filepath = '' + self.selected_song_filepath = "" + self.current_song_filepath = "" # self.tableView.resizeColumnsToContents() self.clicked.connect(self.set_selected_song_filepath) # doubleClicked is a built in event for QTableView - we listen for this event and run set_current_song_filepath @@ -104,12 +113,12 @@ class MusicTable(QTableView): # self.model().removeRow(index) for file in selected_filepaths: with DBA.DBAccess() as db: - db.execute('DELETE FROM library WHERE filepath = ?', (file,)) + db.execute("DELETE FROM library WHERE filepath = ?", (file,)) def open_directory(self): - filepath = self.get_selected_song_filepath().split('/') + filepath = self.get_selected_song_filepath().split("/") filepath.pop() - path = '/'.join(filepath) + path = "/".join(filepath) Popen(["xdg-open", path]) def show_lyrics_menu(self): @@ -241,7 +250,7 @@ class MusicTable(QTableView): self.playPauseSignal.emit() def fetch_library(self): - """Initialize the tableview model""" + """Initializes the tableview model""" self.vertical_scroll_position = ( self.verticalScrollBar().value() ) # Get my scroll position before clearing @@ -249,18 +258,22 @@ class MusicTable(QTableView): self.model.clear() self.model.setHorizontalHeaderLabels(self.table_headers) # Fetch library data - with DBA.DBAccess() as db: - data = db.query( - "SELECT id, title, artist, album, genre, codec, album_date, filepath FROM library;", - (), - ) + try: + with DBA.DBAccess() as db: + data = db.query( + "SELECT id, title, artist, album, genre, codec, album_date, filepath FROM library;", + (), + ) + except Exception as e: + logging.warning(f"MusicTable.py | fetch_library | Unhandled exception: {e}") + return # Populate the model for row_data in data: id, *rest_of_data = row_data items = [QStandardItem(str(item)) for item in rest_of_data] self.model.appendRow(items) # store id using setData - useful for later faster db fetching - row = self.model.rowCount() - 1 + # row = self.model.rowCount() - 1 for item in items: item.setData(id, Qt.UserRole) # Update the viewport/model diff --git a/main.py b/main.py index a52fbf1..51fdb07 100644 --- a/main.py +++ b/main.py @@ -1,10 +1,13 @@ import os import configparser import sys +from subprocess import run import qdarktheme from pyqtgraph import mkBrush from mutagen.id3 import ID3, APIC, error from mutagen.mp3 import MP3 +from configparser import ConfigParser +import DBA from ui import Ui_MainWindow from PyQt5.QtWidgets import ( QMainWindow, @@ -337,6 +340,25 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow): if __name__ == "__main__": + # First run initialization + if not os.path.exists("config.ini"): + # Create config file from sample + run(["cp", "sample_config.ini", "config.ini"]) + config = ConfigParser() + config.read("config.ini") + db_name = config.get("db", "database") + db_path = db_name.split("/") + db_path.pop() + path_as_string = "/".join(db_path) + if not os.path.exists(path_as_string): + os.makedirs(path_as_string) + # Create database on first run + with DBA.DBAccess() as db: + with open("utils/delete_and_create_library.sql", "r") as file: + lines = file.read() + for statement in lines.split(";"): + print(f"executing [{statement}]") + db.execute(statement, ()) # Allow for dynamic imports of my custom classes and utilities project_root = os.path.abspath(os.path.dirname(__file__)) sys.path.append(project_root) diff --git a/requirements.txt b/requirements.txt index 51fea21..6757bf2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,4 @@ pyqt5 pydub pyqtdarktheme pyqtgraph -scipy \ No newline at end of file +scipy diff --git a/sample_config.ini b/sample_config.ini index 5df090a..5949539 100644 --- a/sample_config.ini +++ b/sample_config.ini @@ -1,6 +1,6 @@ [db] # The library database file -library = db/library.db +database = db/library.db [directories] # Useful paths to have stored