Compare commits
6 Commits
04788ef923
...
c5f094443a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5f094443a | ||
|
|
70d986f4ac | ||
|
|
d9ccaf7caa | ||
|
|
d767c879c6 | ||
|
|
0caf48919c | ||
|
|
15ec91e446 |
@ -1,3 +1,6 @@
|
|||||||
|
import os
|
||||||
|
import psutil
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from PyQt5.QtWidgets import QMessageBox
|
from PyQt5.QtWidgets import QMessageBox
|
||||||
|
|
||||||
@ -37,6 +40,48 @@ def get_relative_date(past_date, reference_date=None):
|
|||||||
return f"{weeks} {weeks_str}, {days} {days_str} ago"
|
return f"{weeks} {weeks_str}, {days} {days_str} ago"
|
||||||
|
|
||||||
|
|
||||||
|
def open_in_audacity(path):
|
||||||
|
"""
|
||||||
|
Open passed file in Audacity
|
||||||
|
|
||||||
|
Return True if apparently opened successfully, else False
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Return if audacity not running
|
||||||
|
if "audacity" not in [i.name() for i in psutil.process_iter()]:
|
||||||
|
return False
|
||||||
|
|
||||||
|
to_pipe = '/tmp/audacity_script_pipe.to.' + str(os.getuid())
|
||||||
|
from_pipe = '/tmp/audacity_script_pipe.from.' + str(os.getuid())
|
||||||
|
EOL = '\n'
|
||||||
|
|
||||||
|
def send_command(command):
|
||||||
|
"""Send a single command."""
|
||||||
|
to_audacity.write(command + EOL)
|
||||||
|
to_audacity.flush()
|
||||||
|
|
||||||
|
def get_response():
|
||||||
|
"""Return the command response."""
|
||||||
|
result = ''
|
||||||
|
line = ''
|
||||||
|
while True:
|
||||||
|
result += line
|
||||||
|
line = from_audacity.readline()
|
||||||
|
if line == '\n' and len(result) > 0:
|
||||||
|
break
|
||||||
|
return result
|
||||||
|
|
||||||
|
def do_command(command):
|
||||||
|
"""Send one command, and return the response."""
|
||||||
|
send_command(command)
|
||||||
|
response = get_response()
|
||||||
|
return response
|
||||||
|
|
||||||
|
with open(to_pipe, 'w') as to_audacity, open(
|
||||||
|
from_pipe, 'rt') as from_audacity:
|
||||||
|
do_command(f'Import2: Filename="{path}"')
|
||||||
|
|
||||||
|
|
||||||
def show_warning(title, msg):
|
def show_warning(title, msg):
|
||||||
"Display a warning to user"
|
"Display a warning to user"
|
||||||
|
|
||||||
|
|||||||
20
app/model.py
20
app/model.py
@ -574,6 +574,14 @@ class Tracks(Base):
|
|||||||
|
|
||||||
return q.all()
|
return q.all()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def search_artists(session, text):
|
||||||
|
return (
|
||||||
|
session.query(Tracks)
|
||||||
|
.filter(Tracks.artist.ilike(f"%{text}%"))
|
||||||
|
.order_by(Tracks.title)
|
||||||
|
).all()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def search_titles(session, text):
|
def search_titles(session, text):
|
||||||
return (
|
return (
|
||||||
@ -590,5 +598,17 @@ class Tracks(Base):
|
|||||||
def update_lastplayed(self):
|
def update_lastplayed(self):
|
||||||
self.lastplayed = datetime.now()
|
self.lastplayed = datetime.now()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def update_artist(session, track_id, artist):
|
||||||
|
track = session.query(Tracks).filter(Tracks.id == track_id).one()
|
||||||
|
track.artist = artist
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def update_title(session, track_id, title):
|
||||||
|
track = session.query(Tracks).filter(Tracks.id == track_id).one()
|
||||||
|
track.title = title
|
||||||
|
session.commit()
|
||||||
|
|
||||||
def update_path(self, newpath):
|
def update_path(self, newpath):
|
||||||
self.path = newpath
|
self.path = newpath
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import webbrowser
|
import webbrowser
|
||||||
import os
|
import os
|
||||||
|
import psutil
|
||||||
import sys
|
import sys
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
|
|
||||||
@ -80,6 +81,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
|
|
||||||
self.load_last_playlists()
|
self.load_last_playlists()
|
||||||
self.enable_play_next_controls()
|
self.enable_play_next_controls()
|
||||||
|
self.check_audacity()
|
||||||
self.timer.start(Config.TIMER_MS)
|
self.timer.start(Config.TIMER_MS)
|
||||||
|
|
||||||
def add_file(self):
|
def add_file(self):
|
||||||
@ -112,6 +114,14 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
height = record.f_int or 981
|
height = record.f_int or 981
|
||||||
self.setGeometry(x, y, width, height)
|
self.setGeometry(x, y, width, height)
|
||||||
|
|
||||||
|
def check_audacity(self):
|
||||||
|
"Warn user if Audacity not running"
|
||||||
|
|
||||||
|
if "audacity" in [i.name() for i in psutil.process_iter()]:
|
||||||
|
return
|
||||||
|
|
||||||
|
helpers.show_warning("Audacity check", "Audacity is not running")
|
||||||
|
|
||||||
def clear_selection(self):
|
def clear_selection(self):
|
||||||
if self.visible_playlist_tab():
|
if self.visible_playlist_tab():
|
||||||
self.visible_playlist_tab().clearSelection()
|
self.visible_playlist_tab().clearSelection()
|
||||||
@ -744,12 +754,13 @@ class DbDialog(QDialog):
|
|||||||
self.session = session
|
self.session = session
|
||||||
self.ui = Ui_Dialog()
|
self.ui = Ui_Dialog()
|
||||||
self.ui.setupUi(self)
|
self.ui.setupUi(self)
|
||||||
self.ui.searchString.textEdited.connect(self.chars_typed)
|
|
||||||
self.ui.matchList.itemDoubleClicked.connect(self.double_click)
|
|
||||||
self.ui.btnAdd.clicked.connect(self.add_selected)
|
self.ui.btnAdd.clicked.connect(self.add_selected)
|
||||||
self.ui.btnAddClose.clicked.connect(self.add_selected_and_close)
|
self.ui.btnAddClose.clicked.connect(self.add_selected_and_close)
|
||||||
self.ui.btnClose.clicked.connect(self.close)
|
self.ui.btnClose.clicked.connect(self.close)
|
||||||
|
self.ui.matchList.itemDoubleClicked.connect(self.double_click)
|
||||||
self.ui.matchList.itemSelectionChanged.connect(self.selection_changed)
|
self.ui.matchList.itemSelectionChanged.connect(self.selection_changed)
|
||||||
|
self.ui.radioTitle.toggled.connect(self.radio_toggle)
|
||||||
|
self.ui.searchString.textEdited.connect(self.chars_typed)
|
||||||
|
|
||||||
record = Settings.get_int(self.session, "dbdialog_width")
|
record = Settings.get_int(self.session, "dbdialog_width")
|
||||||
width = record.f_int or 800
|
width = record.f_int or 800
|
||||||
@ -778,9 +789,21 @@ class DbDialog(QDialog):
|
|||||||
self.add_selected()
|
self.add_selected()
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
|
def radio_toggle(self):
|
||||||
|
"""
|
||||||
|
Handle switching between searching for artists and searching for
|
||||||
|
titles
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Logic is handled already in chars_typed(), so just call that.
|
||||||
|
self.chars_typed(self.ui.searchString.text())
|
||||||
|
|
||||||
def chars_typed(self, s):
|
def chars_typed(self, s):
|
||||||
if len(s) > 0:
|
if len(s) > 0:
|
||||||
|
if self.ui.radioTitle.isChecked():
|
||||||
matches = Tracks.search_titles(self.session, s)
|
matches = Tracks.search_titles(self.session, s)
|
||||||
|
else:
|
||||||
|
matches = Tracks.search_artists(self.session, s)
|
||||||
self.ui.matchList.clear()
|
self.ui.matchList.clear()
|
||||||
if matches:
|
if matches:
|
||||||
for track in matches:
|
for track in matches:
|
||||||
|
|||||||
163
app/playlists.py
163
app/playlists.py
@ -17,14 +17,19 @@ import os
|
|||||||
|
|
||||||
from config import Config
|
from config import Config
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from helpers import get_relative_date, show_warning
|
from helpers import get_relative_date, open_in_audacity, show_warning
|
||||||
from log import DEBUG, ERROR
|
from log import DEBUG, ERROR
|
||||||
from model import (
|
from model import (
|
||||||
Notes, Playdates, Playlists, PlaylistTracks, Session, Settings, Tracks
|
Notes, Playdates, Playlists, PlaylistTracks, Session, Settings, Tracks
|
||||||
)
|
)
|
||||||
|
from songdb import create_track_from_file, update_meta
|
||||||
|
|
||||||
|
|
||||||
class PlaylistTab(QTableWidget):
|
class PlaylistTab(QTableWidget):
|
||||||
|
|
||||||
|
cellEditingStarted = QtCore.pyqtSignal(int, int)
|
||||||
|
cellEditingEnded = QtCore.pyqtSignal()
|
||||||
|
|
||||||
# Column names
|
# Column names
|
||||||
COL_INDEX = 0
|
COL_INDEX = 0
|
||||||
COL_MSS = 1
|
COL_MSS = 1
|
||||||
@ -92,6 +97,12 @@ class PlaylistTab(QTableWidget):
|
|||||||
|
|
||||||
self.itemSelectionChanged.connect(self._select_event)
|
self.itemSelectionChanged.connect(self._select_event)
|
||||||
|
|
||||||
|
self.editing_cell = False
|
||||||
|
self.cellChanged.connect(self._cell_changed)
|
||||||
|
self.doubleClicked.connect(self._edit_cell)
|
||||||
|
self.cellEditingStarted.connect(self._cell_edit_started)
|
||||||
|
self.cellEditingEnded.connect(self._cell_edit_ended)
|
||||||
|
|
||||||
self.current_track_start_time = None
|
self.current_track_start_time = None
|
||||||
self.played_tracks = []
|
self.played_tracks = []
|
||||||
|
|
||||||
@ -141,6 +152,16 @@ class PlaylistTab(QTableWidget):
|
|||||||
self._save_playlist(session)
|
self._save_playlist(session)
|
||||||
self._repaint()
|
self._repaint()
|
||||||
|
|
||||||
|
def edit(self, index, trigger, event):
|
||||||
|
result = super(PlaylistTab, self).edit(index, trigger, event)
|
||||||
|
if result:
|
||||||
|
self.cellEditingStarted.emit(index.row(), index.column())
|
||||||
|
return result
|
||||||
|
|
||||||
|
def closeEditor(self, editor, hint):
|
||||||
|
super(PlaylistTab, self).closeEditor(editor, hint)
|
||||||
|
self.cellEditingEnded.emit()
|
||||||
|
|
||||||
def eventFilter(self, source, event):
|
def eventFilter(self, source, event):
|
||||||
"Used to process context (right-click) menu"
|
"Used to process context (right-click) menu"
|
||||||
|
|
||||||
@ -158,9 +179,14 @@ class PlaylistTab(QTableWidget):
|
|||||||
act_copypath = self.menu.addAction("Copy track path")
|
act_copypath = self.menu.addAction("Copy track path")
|
||||||
act_copypath.triggered.connect(
|
act_copypath.triggered.connect(
|
||||||
lambda: self._copy_path(row))
|
lambda: self._copy_path(row))
|
||||||
|
act_rescan = self.menu.addAction("Rescan track")
|
||||||
|
act_rescan.triggered.connect(lambda: self._rescan(row))
|
||||||
|
act_audacity = self.menu.addAction(
|
||||||
|
"Open track in Audacity")
|
||||||
|
act_audacity.triggered.connect(lambda: self._audacity(row))
|
||||||
self.menu.addSeparator()
|
self.menu.addSeparator()
|
||||||
act_delete = self.menu.addAction('Delete')
|
act_delete = self.menu.addAction('Delete')
|
||||||
act_delete.triggered.connect(lambda: self._delete_row(row))
|
act_delete.triggered.connect(self._delete_rows)
|
||||||
act_info = self.menu.addAction('Info')
|
act_info = self.menu.addAction('Info')
|
||||||
act_info.triggered.connect(lambda: self._info_row(row))
|
act_info.triggered.connect(lambda: self._info_row(row))
|
||||||
|
|
||||||
@ -528,6 +554,20 @@ class PlaylistTab(QTableWidget):
|
|||||||
|
|
||||||
# ########## Internally called functions ##########
|
# ########## Internally called functions ##########
|
||||||
|
|
||||||
|
def _audacity(self, row):
|
||||||
|
"Open track in Audacity. Audacity must be already running"
|
||||||
|
|
||||||
|
DEBUG(f"_audacity({row})")
|
||||||
|
|
||||||
|
if row in self._meta_get_notes():
|
||||||
|
return None
|
||||||
|
|
||||||
|
track_id = self._get_row_id(row)
|
||||||
|
if track_id:
|
||||||
|
with Session() as session:
|
||||||
|
track = Tracks.get_track(session, track_id)
|
||||||
|
open_in_audacity(track.path)
|
||||||
|
|
||||||
def _calculate_next_start_time(self, session, row, start):
|
def _calculate_next_start_time(self, session, row, start):
|
||||||
"Return this row's end time given its start time"
|
"Return this row's end time given its start time"
|
||||||
|
|
||||||
@ -568,11 +608,50 @@ class PlaylistTab(QTableWidget):
|
|||||||
cb.clear(mode=cb.Clipboard)
|
cb.clear(mode=cb.Clipboard)
|
||||||
cb.setText(path, mode=cb.Clipboard)
|
cb.setText(path, mode=cb.Clipboard)
|
||||||
|
|
||||||
def _delete_row(self, row):
|
def _cell_changed(self, row, column):
|
||||||
"Delete row"
|
"Called when cell content has changed"
|
||||||
|
|
||||||
DEBUG(f"playlist._delete_row({row})")
|
if not self.editing_cell:
|
||||||
|
return
|
||||||
|
|
||||||
|
new = self.item(row, column).text()
|
||||||
|
|
||||||
|
DEBUG(f"_cell_changed({row=}, {column=}, {new=}")
|
||||||
|
|
||||||
|
row_id = self._get_row_id(row)
|
||||||
|
with Session() as session:
|
||||||
|
if row in self._meta_get_notes():
|
||||||
|
Notes.update_note(session, row_id, row, new)
|
||||||
|
else:
|
||||||
|
track = Tracks.get_track(session, row_id)
|
||||||
|
if column == self.COL_ARTIST:
|
||||||
|
update_meta(session, track, artist=new)
|
||||||
|
elif column == self.COL_TITLE:
|
||||||
|
update_meta(session, track, title=new)
|
||||||
|
else:
|
||||||
|
ERROR("_cell_changed(): unrecognised column")
|
||||||
|
|
||||||
|
def _cell_edit_started(self, row, column):
|
||||||
|
DEBUG(f"_cell_edit_started({row=}, {column=})")
|
||||||
|
self.editing_cell = True
|
||||||
|
self.master_process.disable_play_next_controls()
|
||||||
|
|
||||||
|
def _cell_edit_ended(self):
|
||||||
|
DEBUG("_cell_edit_ended()")
|
||||||
|
self.editing_cell = False
|
||||||
|
self.master_process.enable_play_next_controls()
|
||||||
|
|
||||||
|
def _delete_rows(self):
|
||||||
|
"Delete mutliple rows"
|
||||||
|
|
||||||
|
DEBUG("playlist._delete_rows()")
|
||||||
|
|
||||||
|
rows = sorted(set(item.row() for item in self.selectedItems()))
|
||||||
|
rows_to_delete = []
|
||||||
|
notes = self._meta_get_notes()
|
||||||
|
|
||||||
|
with Session() as session:
|
||||||
|
for row in rows:
|
||||||
if row == self._meta_get_current():
|
if row == self._meta_get_current():
|
||||||
show_warning("Silly", "Can't delete playing track")
|
show_warning("Silly", "Can't delete playing track")
|
||||||
return
|
return
|
||||||
@ -580,7 +659,6 @@ class PlaylistTab(QTableWidget):
|
|||||||
show_warning("Safety", "Can't delete next track")
|
show_warning("Safety", "Can't delete next track")
|
||||||
return
|
return
|
||||||
|
|
||||||
with Session() as session:
|
|
||||||
title = self.item(row, self.COL_TITLE).text()
|
title = self.item(row, self.COL_TITLE).text()
|
||||||
|
|
||||||
msg = QMessageBox(self)
|
msg = QMessageBox(self)
|
||||||
@ -589,13 +667,19 @@ class PlaylistTab(QTableWidget):
|
|||||||
msg.setStandardButtons(QMessageBox.Yes | QMessageBox.Cancel)
|
msg.setStandardButtons(QMessageBox.Yes | QMessageBox.Cancel)
|
||||||
msg.setDefaultButton(QMessageBox.Cancel)
|
msg.setDefaultButton(QMessageBox.Cancel)
|
||||||
msg.setWindowTitle("Delete row")
|
msg.setWindowTitle("Delete row")
|
||||||
|
# Store list of notes
|
||||||
if msg.exec() == QMessageBox.Yes:
|
if msg.exec() == QMessageBox.Yes:
|
||||||
id = self._get_row_id(row)
|
rows_to_delete.append(row)
|
||||||
if row in self._meta_get_notes():
|
|
||||||
|
# delete in reverse row order so row numbers don't
|
||||||
|
# change
|
||||||
|
for del_row in sorted(rows_to_delete, reverse=True):
|
||||||
|
id = self._get_row_id(del_row)
|
||||||
|
if del_row in notes:
|
||||||
Notes.delete_note(session, id)
|
Notes.delete_note(session, id)
|
||||||
else:
|
else:
|
||||||
PlaylistTracks.remove_track(session, self.id, row)
|
PlaylistTracks.remove_track(session, self.id, del_row)
|
||||||
self.removeRow(row)
|
self.removeRow(del_row)
|
||||||
|
|
||||||
self._save_playlist(session)
|
self._save_playlist(session)
|
||||||
self._repaint()
|
self._repaint()
|
||||||
@ -652,8 +736,12 @@ class PlaylistTab(QTableWidget):
|
|||||||
txt = (
|
txt = (
|
||||||
f"Title: {track.title}\n"
|
f"Title: {track.title}\n"
|
||||||
f"Artist: {track.artist}\n"
|
f"Artist: {track.artist}\n"
|
||||||
|
f"Track ID: {track.id}\n"
|
||||||
|
f"Track duration: {helpers.ms_to_mmss(track.duration)}\n"
|
||||||
|
f"Track fade at: {helpers.ms_to_mmss(track.fade_at)}\n"
|
||||||
|
f"Track silence at: {helpers.ms_to_mmss(track.silence_at)}"
|
||||||
|
"\n\n"
|
||||||
f"Path: {track.path}\n"
|
f"Path: {track.path}\n"
|
||||||
f"Track ID: {track.id}"
|
|
||||||
)
|
)
|
||||||
info = QMessageBox(self)
|
info = QMessageBox(self)
|
||||||
info.setIcon(QMessageBox.Information)
|
info.setIcon(QMessageBox.Information)
|
||||||
@ -675,6 +763,16 @@ class PlaylistTab(QTableWidget):
|
|||||||
and pos.y() >= rect.center().y() # noqa W503
|
and pos.y() >= rect.center().y() # noqa W503
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _edit_cell(self, mi):
|
||||||
|
"Called when table is double-clicked"
|
||||||
|
|
||||||
|
row = mi.row()
|
||||||
|
column = mi.column()
|
||||||
|
item = self.item(row, column)
|
||||||
|
|
||||||
|
if column in [self.COL_TITLE, self.COL_ARTIST]:
|
||||||
|
self.editItem(item)
|
||||||
|
|
||||||
def _find_next_track_row(self):
|
def _find_next_track_row(self):
|
||||||
"""
|
"""
|
||||||
Find next track to play.
|
Find next track to play.
|
||||||
@ -954,6 +1052,24 @@ class PlaylistTab(QTableWidget):
|
|||||||
# Don't dim unplayed tracks
|
# Don't dim unplayed tracks
|
||||||
self._set_row_bold(row)
|
self._set_row_bold(row)
|
||||||
|
|
||||||
|
def _rescan(self, row):
|
||||||
|
"""
|
||||||
|
If passed row is track row, rescan it.
|
||||||
|
Otherwise return None.
|
||||||
|
"""
|
||||||
|
|
||||||
|
DEBUG(f"_rescan({row})")
|
||||||
|
|
||||||
|
if row in self._meta_get_notes():
|
||||||
|
return None
|
||||||
|
|
||||||
|
track_id = self._get_row_id(row)
|
||||||
|
if track_id:
|
||||||
|
with Session() as session:
|
||||||
|
track = Tracks.get_track(session, track_id)
|
||||||
|
create_track_from_file(session, track.path)
|
||||||
|
self._update_row(row, track)
|
||||||
|
|
||||||
def _save_playlist(self, session):
|
def _save_playlist(self, session):
|
||||||
"""
|
"""
|
||||||
Save playlist to database.
|
Save playlist to database.
|
||||||
@ -1099,3 +1215,28 @@ class PlaylistTab(QTableWidget):
|
|||||||
|
|
||||||
with Session() as session:
|
with Session() as session:
|
||||||
return os.access(Tracks.get_path(session, track_id), os.R_OK)
|
return os.access(Tracks.get_path(session, track_id), os.R_OK)
|
||||||
|
|
||||||
|
def _update_row(self, row, track):
|
||||||
|
"""
|
||||||
|
Update the passed row with info from the passed track.
|
||||||
|
"""
|
||||||
|
|
||||||
|
DEBUG(f"_update_row({row=}, {track=}")
|
||||||
|
|
||||||
|
item_startgap = self.item(row, self.COL_MSS)
|
||||||
|
item_startgap.setText(str(track.start_gap))
|
||||||
|
if track.start_gap >= 500:
|
||||||
|
item_startgap.setBackground(QColor(Config.COLOUR_LONG_START))
|
||||||
|
else:
|
||||||
|
item_startgap.setBackground(QColor("white"))
|
||||||
|
|
||||||
|
item_title = self.item(row, self.COL_TITLE)
|
||||||
|
item_title.setText(track.title)
|
||||||
|
|
||||||
|
item_artist = self.item(row, self.COL_ARTIST)
|
||||||
|
item_artist.setText(track.artist)
|
||||||
|
|
||||||
|
item_duration = self.item(row, self.COL_DURATION)
|
||||||
|
item_duration.setText(helpers.ms_to_mmss(track.duration))
|
||||||
|
|
||||||
|
self._repaint()
|
||||||
|
|||||||
@ -349,5 +349,36 @@ def update_db(session):
|
|||||||
Tracks.remove_path(session, path)
|
Tracks.remove_path(session, path)
|
||||||
|
|
||||||
|
|
||||||
|
def update_meta(session, track, artist=None, title=None):
|
||||||
|
"""
|
||||||
|
Updates both the tag info in the file and the database entry with passed
|
||||||
|
artist and tag details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
DEBUG(f"songdb.update_meta({session=}, {track=}, {artist=}, {title=})")
|
||||||
|
|
||||||
|
if not artist and not title:
|
||||||
|
return
|
||||||
|
|
||||||
|
ftype = os.path.splitext(track.path)[1][1:]
|
||||||
|
if ftype == 'flac':
|
||||||
|
tag_handler = FLAC
|
||||||
|
elif ftype == 'mp3':
|
||||||
|
tag_handler = MP3
|
||||||
|
else:
|
||||||
|
INFO(f"File type {ftype} not implemented")
|
||||||
|
return
|
||||||
|
|
||||||
|
f = tag_handler(track.path)
|
||||||
|
with Session() as session:
|
||||||
|
if artist:
|
||||||
|
f["artist"] = artist
|
||||||
|
Tracks.update_artist(session, track.id, artist)
|
||||||
|
if title:
|
||||||
|
f["title"] = title
|
||||||
|
Tracks.update_title(session, track.id, title)
|
||||||
|
f.save()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__' and '__file__' in globals():
|
if __name__ == '__main__' and '__file__' in globals():
|
||||||
main()
|
main()
|
||||||
|
|||||||
@ -6,15 +6,15 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>383</width>
|
<width>584</width>
|
||||||
<height>270</height>
|
<height>377</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Dialog</string>
|
<string>Dialog</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
<item>
|
<item row="0" column="0">
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QLabel" name="label">
|
<widget class="QLabel" name="label">
|
||||||
@ -28,26 +28,36 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item row="1" column="0">
|
||||||
<widget class="QListWidget" name="matchList"/>
|
<widget class="QListWidget" name="matchList"/>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="dbPath">
|
<widget class="QRadioButton" name="radioTitle">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string/>
|
<string>&Title</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<widget class="QRadioButton" name="radioArtist">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Artist</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<spacer name="horizontalSpacer">
|
<spacer name="horizontalSpacer_2">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" stdset="0">
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
<width>88</width>
|
<width>40</width>
|
||||||
<height>20</height>
|
<height>20</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
@ -79,6 +89,13 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="dbPath">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Form implementation generated from reading ui file 'app/ui/dlg_SearchDatabase.ui'
|
# Form implementation generated from reading ui file 'ui/dlg_SearchDatabase.ui'
|
||||||
#
|
#
|
||||||
# Created by: PyQt5 UI code generator 5.15.4
|
# Created by: PyQt5 UI code generator 5.15.4
|
||||||
#
|
#
|
||||||
@ -14,9 +14,9 @@ from PyQt5 import QtCore, QtGui, QtWidgets
|
|||||||
class Ui_Dialog(object):
|
class Ui_Dialog(object):
|
||||||
def setupUi(self, Dialog):
|
def setupUi(self, Dialog):
|
||||||
Dialog.setObjectName("Dialog")
|
Dialog.setObjectName("Dialog")
|
||||||
Dialog.resize(383, 270)
|
Dialog.resize(584, 377)
|
||||||
self.verticalLayout = QtWidgets.QVBoxLayout(Dialog)
|
self.gridLayout_2 = QtWidgets.QGridLayout(Dialog)
|
||||||
self.verticalLayout.setObjectName("verticalLayout")
|
self.gridLayout_2.setObjectName("gridLayout_2")
|
||||||
self.gridLayout = QtWidgets.QGridLayout()
|
self.gridLayout = QtWidgets.QGridLayout()
|
||||||
self.gridLayout.setObjectName("gridLayout")
|
self.gridLayout.setObjectName("gridLayout")
|
||||||
self.label = QtWidgets.QLabel(Dialog)
|
self.label = QtWidgets.QLabel(Dialog)
|
||||||
@ -25,17 +25,20 @@ class Ui_Dialog(object):
|
|||||||
self.searchString = QtWidgets.QLineEdit(Dialog)
|
self.searchString = QtWidgets.QLineEdit(Dialog)
|
||||||
self.searchString.setObjectName("searchString")
|
self.searchString.setObjectName("searchString")
|
||||||
self.gridLayout.addWidget(self.searchString, 0, 1, 1, 1)
|
self.gridLayout.addWidget(self.searchString, 0, 1, 1, 1)
|
||||||
self.verticalLayout.addLayout(self.gridLayout)
|
self.gridLayout_2.addLayout(self.gridLayout, 0, 0, 1, 1)
|
||||||
self.matchList = QtWidgets.QListWidget(Dialog)
|
self.matchList = QtWidgets.QListWidget(Dialog)
|
||||||
self.matchList.setObjectName("matchList")
|
self.matchList.setObjectName("matchList")
|
||||||
self.verticalLayout.addWidget(self.matchList)
|
self.gridLayout_2.addWidget(self.matchList, 1, 0, 1, 1)
|
||||||
self.dbPath = QtWidgets.QLabel(Dialog)
|
|
||||||
self.dbPath.setText("")
|
|
||||||
self.dbPath.setObjectName("dbPath")
|
|
||||||
self.verticalLayout.addWidget(self.dbPath)
|
|
||||||
self.horizontalLayout = QtWidgets.QHBoxLayout()
|
self.horizontalLayout = QtWidgets.QHBoxLayout()
|
||||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||||
spacerItem = QtWidgets.QSpacerItem(88, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
self.radioTitle = QtWidgets.QRadioButton(Dialog)
|
||||||
|
self.radioTitle.setChecked(True)
|
||||||
|
self.radioTitle.setObjectName("radioTitle")
|
||||||
|
self.horizontalLayout.addWidget(self.radioTitle)
|
||||||
|
self.radioArtist = QtWidgets.QRadioButton(Dialog)
|
||||||
|
self.radioArtist.setObjectName("radioArtist")
|
||||||
|
self.horizontalLayout.addWidget(self.radioArtist)
|
||||||
|
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||||
self.horizontalLayout.addItem(spacerItem)
|
self.horizontalLayout.addItem(spacerItem)
|
||||||
self.btnAdd = QtWidgets.QPushButton(Dialog)
|
self.btnAdd = QtWidgets.QPushButton(Dialog)
|
||||||
self.btnAdd.setDefault(True)
|
self.btnAdd.setDefault(True)
|
||||||
@ -47,7 +50,11 @@ class Ui_Dialog(object):
|
|||||||
self.btnClose = QtWidgets.QPushButton(Dialog)
|
self.btnClose = QtWidgets.QPushButton(Dialog)
|
||||||
self.btnClose.setObjectName("btnClose")
|
self.btnClose.setObjectName("btnClose")
|
||||||
self.horizontalLayout.addWidget(self.btnClose)
|
self.horizontalLayout.addWidget(self.btnClose)
|
||||||
self.verticalLayout.addLayout(self.horizontalLayout)
|
self.gridLayout_2.addLayout(self.horizontalLayout, 2, 0, 1, 1)
|
||||||
|
self.dbPath = QtWidgets.QLabel(Dialog)
|
||||||
|
self.dbPath.setText("")
|
||||||
|
self.dbPath.setObjectName("dbPath")
|
||||||
|
self.gridLayout_2.addWidget(self.dbPath, 3, 0, 1, 1)
|
||||||
|
|
||||||
self.retranslateUi(Dialog)
|
self.retranslateUi(Dialog)
|
||||||
QtCore.QMetaObject.connectSlotsByName(Dialog)
|
QtCore.QMetaObject.connectSlotsByName(Dialog)
|
||||||
@ -56,6 +63,8 @@ class Ui_Dialog(object):
|
|||||||
_translate = QtCore.QCoreApplication.translate
|
_translate = QtCore.QCoreApplication.translate
|
||||||
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
|
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
|
||||||
self.label.setText(_translate("Dialog", "Title:"))
|
self.label.setText(_translate("Dialog", "Title:"))
|
||||||
|
self.radioTitle.setText(_translate("Dialog", "&Title"))
|
||||||
|
self.radioArtist.setText(_translate("Dialog", "&Artist"))
|
||||||
self.btnAdd.setText(_translate("Dialog", "&Add"))
|
self.btnAdd.setText(_translate("Dialog", "&Add"))
|
||||||
self.btnAddClose.setText(_translate("Dialog", "A&dd and close"))
|
self.btnAddClose.setText(_translate("Dialog", "A&dd and close"))
|
||||||
self.btnClose.setText(_translate("Dialog", "&Close"))
|
self.btnClose.setText(_translate("Dialog", "&Close"))
|
||||||
|
|||||||
0
audacity_control.py
Normal file → Executable file
0
audacity_control.py
Normal file → Executable file
@ -17,6 +17,7 @@ parso==0.8.2
|
|||||||
pexpect==4.8.0
|
pexpect==4.8.0
|
||||||
pickleshare==0.7.5
|
pickleshare==0.7.5
|
||||||
prompt-toolkit==3.0.18
|
prompt-toolkit==3.0.18
|
||||||
|
psutil==5.8.0
|
||||||
ptyprocess==0.7.0
|
ptyprocess==0.7.0
|
||||||
pycodestyle==2.7.0
|
pycodestyle==2.7.0
|
||||||
pydub==0.25.1
|
pydub==0.25.1
|
||||||
@ -25,6 +26,8 @@ Pygments==2.8.1
|
|||||||
PyQt5==5.15.4
|
PyQt5==5.15.4
|
||||||
PyQt5-Qt5==5.15.2
|
PyQt5-Qt5==5.15.2
|
||||||
PyQt5-sip==12.8.1
|
PyQt5-sip==12.8.1
|
||||||
|
PyQtWebEngine==5.15.4
|
||||||
|
PyQtWebEngine-Qt5==5.15.2
|
||||||
python-dateutil==2.8.1
|
python-dateutil==2.8.1
|
||||||
python-editor==1.0.4
|
python-editor==1.0.4
|
||||||
python-slugify==5.0.0
|
python-slugify==5.0.0
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user