first time run auto-config + update readme
This commit is contained in:
parent
5b93f81e05
commit
767ce3fa9c
11
DBA.py
11
DBA.py
@ -2,17 +2,16 @@ import sqlite3
|
|||||||
import logging
|
import logging
|
||||||
from configparser import ConfigParser
|
from configparser import ConfigParser
|
||||||
|
|
||||||
|
|
||||||
class DBAccess:
|
class DBAccess:
|
||||||
def __init__(self, db_name=None):
|
def __init__(self, db_name=None):
|
||||||
logging.info('Instantiating DBAccess')
|
logging.info("Instantiating DBAccess")
|
||||||
config = ConfigParser()
|
config = ConfigParser()
|
||||||
config.read('config.ini')
|
config.read("config.ini")
|
||||||
if db_name is None:
|
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)
|
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()
|
self._cursor: sqlite3.Cursor = self._conn.cursor()
|
||||||
logging.info(f'DBAccess | self._cursor = [\n{type(self._cursor)}\n{self._cursor}\n]')
|
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
return self
|
return self
|
||||||
@ -40,7 +39,7 @@ class DBAccess:
|
|||||||
self.cursor.execute(sql, params or ())
|
self.cursor.execute(sql, params or ())
|
||||||
|
|
||||||
def executemany(self, sql, seq_of_params):
|
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):
|
def fetchall(self):
|
||||||
return self.cursor.fetchall()
|
return self.cursor.fetchall()
|
||||||
|
|||||||
23
README.md
23
README.md
@ -2,6 +2,29 @@
|
|||||||
|
|
||||||
PyQt5 music player inspired by MusicBee & iTunes
|
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:
|
## Todo:
|
||||||
|
|
||||||
- [ ] Delete songs from library (del key || right-click delete)
|
- [ ] Delete songs from library (del key || right-click delete)
|
||||||
|
|||||||
@ -7,14 +7,23 @@ from PyQt5.QtGui import (
|
|||||||
QDragEnterEvent,
|
QDragEnterEvent,
|
||||||
QDropEvent,
|
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 PyQt5.QtCore import QModelIndex, Qt, pyqtSignal, QTimer
|
||||||
from utils import add_files_to_library
|
from utils import add_files_to_library
|
||||||
from utils import update_song_in_library
|
from utils import update_song_in_library
|
||||||
from utils import get_id3_tags
|
from utils import get_id3_tags
|
||||||
from utils import get_album_art
|
from utils import get_album_art
|
||||||
from utils import set_id3_tag
|
from utils import set_id3_tag
|
||||||
|
from utils import delete_and_create_library_database
|
||||||
from subprocess import Popen
|
from subprocess import Popen
|
||||||
|
from sqlite3 import OperationalError
|
||||||
import logging
|
import logging
|
||||||
import configparser
|
import configparser
|
||||||
import os
|
import os
|
||||||
@ -57,8 +66,8 @@ class MusicTable(QTableView):
|
|||||||
self.database_columns = str(self.config["table"]["columns"]).split(",")
|
self.database_columns = str(self.config["table"]["columns"]).split(",")
|
||||||
self.vertical_scroll_position = 0
|
self.vertical_scroll_position = 0
|
||||||
self.songChanged = None
|
self.songChanged = None
|
||||||
self.selected_song_filepath = ''
|
self.selected_song_filepath = ""
|
||||||
self.current_song_filepath = ''
|
self.current_song_filepath = ""
|
||||||
# self.tableView.resizeColumnsToContents()
|
# self.tableView.resizeColumnsToContents()
|
||||||
self.clicked.connect(self.set_selected_song_filepath)
|
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
|
# 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)
|
# self.model().removeRow(index)
|
||||||
for file in selected_filepaths:
|
for file in selected_filepaths:
|
||||||
with DBA.DBAccess() as db:
|
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):
|
def open_directory(self):
|
||||||
filepath = self.get_selected_song_filepath().split('/')
|
filepath = self.get_selected_song_filepath().split("/")
|
||||||
filepath.pop()
|
filepath.pop()
|
||||||
path = '/'.join(filepath)
|
path = "/".join(filepath)
|
||||||
Popen(["xdg-open", path])
|
Popen(["xdg-open", path])
|
||||||
|
|
||||||
def show_lyrics_menu(self):
|
def show_lyrics_menu(self):
|
||||||
@ -241,7 +250,7 @@ class MusicTable(QTableView):
|
|||||||
self.playPauseSignal.emit()
|
self.playPauseSignal.emit()
|
||||||
|
|
||||||
def fetch_library(self):
|
def fetch_library(self):
|
||||||
"""Initialize the tableview model"""
|
"""Initializes the tableview model"""
|
||||||
self.vertical_scroll_position = (
|
self.vertical_scroll_position = (
|
||||||
self.verticalScrollBar().value()
|
self.verticalScrollBar().value()
|
||||||
) # Get my scroll position before clearing
|
) # Get my scroll position before clearing
|
||||||
@ -249,18 +258,22 @@ class MusicTable(QTableView):
|
|||||||
self.model.clear()
|
self.model.clear()
|
||||||
self.model.setHorizontalHeaderLabels(self.table_headers)
|
self.model.setHorizontalHeaderLabels(self.table_headers)
|
||||||
# Fetch library data
|
# Fetch library data
|
||||||
with DBA.DBAccess() as db:
|
try:
|
||||||
data = db.query(
|
with DBA.DBAccess() as db:
|
||||||
"SELECT id, title, artist, album, genre, codec, album_date, filepath FROM library;",
|
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
|
# Populate the model
|
||||||
for row_data in data:
|
for row_data in data:
|
||||||
id, *rest_of_data = row_data
|
id, *rest_of_data = row_data
|
||||||
items = [QStandardItem(str(item)) for item in rest_of_data]
|
items = [QStandardItem(str(item)) for item in rest_of_data]
|
||||||
self.model.appendRow(items)
|
self.model.appendRow(items)
|
||||||
# store id using setData - useful for later faster db fetching
|
# store id using setData - useful for later faster db fetching
|
||||||
row = self.model.rowCount() - 1
|
# row = self.model.rowCount() - 1
|
||||||
for item in items:
|
for item in items:
|
||||||
item.setData(id, Qt.UserRole)
|
item.setData(id, Qt.UserRole)
|
||||||
# Update the viewport/model
|
# Update the viewport/model
|
||||||
|
|||||||
22
main.py
22
main.py
@ -1,10 +1,13 @@
|
|||||||
import os
|
import os
|
||||||
import configparser
|
import configparser
|
||||||
import sys
|
import sys
|
||||||
|
from subprocess import run
|
||||||
import qdarktheme
|
import qdarktheme
|
||||||
from pyqtgraph import mkBrush
|
from pyqtgraph import mkBrush
|
||||||
from mutagen.id3 import ID3, APIC, error
|
from mutagen.id3 import ID3, APIC, error
|
||||||
from mutagen.mp3 import MP3
|
from mutagen.mp3 import MP3
|
||||||
|
from configparser import ConfigParser
|
||||||
|
import DBA
|
||||||
from ui import Ui_MainWindow
|
from ui import Ui_MainWindow
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QMainWindow,
|
QMainWindow,
|
||||||
@ -337,6 +340,25 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
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
|
# Allow for dynamic imports of my custom classes and utilities
|
||||||
project_root = os.path.abspath(os.path.dirname(__file__))
|
project_root = os.path.abspath(os.path.dirname(__file__))
|
||||||
sys.path.append(project_root)
|
sys.path.append(project_root)
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[db]
|
[db]
|
||||||
# The library database file
|
# The library database file
|
||||||
library = db/library.db
|
database = db/library.db
|
||||||
|
|
||||||
[directories]
|
[directories]
|
||||||
# Useful paths to have stored
|
# Useful paths to have stored
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user