diff --git a/app/config.py b/app/config.py
index fc494f9..029519f 100644
--- a/app/config.py
+++ b/app/config.py
@@ -8,6 +8,8 @@ class Config(object):
DBFS_SILENCE = -50
DISPLAY_SQL = False
ERRORS_TO = ['kae@midnighthax.com']
+ FADE_STEPS = 20
+ FADE_TIME = 5000
LOG_LEVEL_STDERR = logging.DEBUG
LOG_LEVEL_SYSLOG = logging.DEBUG
LOG_NAME = "musicmuster"
diff --git a/app/log.py b/app/log.py
old mode 100755
new mode 100644
index a5527f0..271afb1
--- a/app/log.py
+++ b/app/log.py
@@ -35,9 +35,10 @@ syslog.addFilter(filter)
stderr.addFilter(filter)
# create formatter and add it to the handlers
-formatter = logging.Formatter('[%(name)s] %(leveltag)s: %(message)s')
-stderr.setFormatter(formatter)
-syslog.setFormatter(formatter)
+stderr_fmt = logging.Formatter('%(leveltag)s: %(message)s')
+syslog_fmt = logging.Formatter('[%(name)s] %(leveltag)s: %(message)s')
+stderr.setFormatter(stderr_fmt)
+syslog.setFormatter(syslog_fmt)
# add the handlers to the log
log.addHandler(stderr)
diff --git a/app/model.py b/app/model.py
index d5bc719..89607d4 100644
--- a/app/model.py
+++ b/app/model.py
@@ -17,11 +17,8 @@ from sqlalchemy.orm.exc import NoResultFound
from sqlalchemy.orm import relationship, sessionmaker
from config import Config
-
from log import DEBUG, ERROR, INFO
-INFO("Starting")
-
# Create session at the global level as per
# https://docs.sqlalchemy.org/en/13/orm/session_basics.html
@@ -138,8 +135,10 @@ class Playlists(Base):
def get_only_playlist(cls):
return session.query(Playlists).filter(Playlists.id == 1).one()
- def add_track(self, track):
- self.tracks.append(track)
+ def add_track(self, track, position):
+ glue = PlaylistTracks(sort=position)
+ glue.track_id = track.id
+ self.tracks.append(glue)
def get_tracks(self):
return [a.tracks for a in self.tracks]
@@ -181,10 +180,9 @@ class Tracks(Base):
@staticmethod
def get_path(id):
try:
- path = session.query(Tracks.path).filter(Tracks.id == id).one()[0]
- return os.path.join(Config.ROOT, path)
+ return session.query(Tracks.path).filter(Tracks.id == id).one()[0]
except NoResultFound:
- print(f"Can't find track id {id}")
+ ERROR(f"Can't find track id {id}")
return None
@staticmethod
@@ -210,6 +208,9 @@ class Tracks(Base):
return session.query(Tracks).filter(
Tracks.id == id).one()
+ def update_lastplayed(self):
+ self.lastplayed = datetime.now()
+
class Playdates(Base):
__tablename__ = 'playdates'
@@ -226,4 +227,5 @@ class Playdates(Base):
pd.lastplayed = datetime.now()
pd.track_id = track.id
session.add(pd)
+ track.update_lastplayed()
session.commit()
diff --git a/app/musicmuster.py b/app/musicmuster.py
index 0666653..6b8a3e2 100755
--- a/app/musicmuster.py
+++ b/app/musicmuster.py
@@ -33,6 +33,7 @@ class Music:
"player": None,
"meta": None
}
+ self.fading = False
def get_current_artist(self):
try:
@@ -101,16 +102,27 @@ class Music:
return ""
def fade_current(self):
+ if not self.playing():
+ return
thread = threading.Thread(target=self._fade_current)
thread.start()
def _fade_current(self):
+ fade_time = Config.FADE_TIME / 1000
+ sleep_time = fade_time / Config.FADE_STEPS
+ step_percent = int((100 / Config.FADE_STEPS) * -1)
player = self.current_track['player']
position = player.get_position()
- for i in range(100, 0, -10):
+ for i in range(100, 0, step_percent):
player.audio_set_volume(i)
- sleep(0.2)
- player.pause()
+ sleep(sleep_time)
+ # If the user clicks "fade" and then, before the track has
+ # finished fading, click "play next", the "play next" will also
+ # call fade_current. When that second call to fade_current gets
+ # to the player.pause() line below, it will unpause it. So, we
+ # only pause if we're acutally playing.
+ if player.is_playing():
+ player.pause()
player.audio_set_volume(100)
player.set_position(position)
@@ -118,9 +130,11 @@ class Music:
if self.previous_track['player']:
self.previous_track['player'].release()
if self.current_track['player']:
- self.current_track['player'].stop()
+ self.fade_current()
self.previous_track = self.current_track
self.current_track = self.next_track
+ # Next in case player was left in odd (ie, silenced) state
+ self.current_track['player'].audio_set_volume(100)
self.current_track['player'].play()
Playdates.add_playdate(self.current_track['meta'])
@@ -210,7 +224,11 @@ class Window(QMainWindow, Ui_MainWindow):
self.actionPlay_next.triggered.connect(self.play_next)
self.actionPlay_selected.triggered.connect(self.play_next)
self.actionSearch_database.triggered.connect(self.selectFromDatabase)
+ self.btnSearchDatabase.clicked.connect(self.selectFromDatabase)
+ self.btnSkipNext.clicked.connect(self.play_next)
+ self.btnStop.clicked.connect(self.fade)
self.playlist.itemSelectionChanged.connect(self.set_next_track)
+
self.timer.timeout.connect(self.tick)
def selectFromDatabase(self):
@@ -400,7 +418,8 @@ class DbDialog(QDialog):
# Store in current playlist in database
db_playlist = Playlists.get_only_playlist()
- db_playlist.add_track(track)
+ # TODO: hack position to be at end of list
+ db_playlist.add_track(track, 99999)
# Add to on-screen playlist
self.parent().add_to_playlist(track)
diff --git a/app/songdb.py b/app/songdb.py
old mode 100755
new mode 100644
index aaa871f..0f04a9b
--- a/app/songdb.py
+++ b/app/songdb.py
@@ -9,12 +9,12 @@ from model import Tracks, session
from pydub import AudioSegment
from tinytag import TinyTag
-INFO("Starting")
-
def main():
"Main loop"
+ INFO("Starting")
+
# Parse command line
p = argparse.ArgumentParser()
p.add_argument('-u', '--update',
@@ -125,9 +125,9 @@ def update_db():
session.commit()
elif ext not in [".jpg"]:
- print(f"Unrecognised file type: {path}")
+ INFO(f"Unrecognised file type: {path}")
- print(f"{count} files processed")
+ INFO(f"{count} files processed")
if __name__ == '__main__':
diff --git a/app/ui/main_window.ui b/app/ui/main_window.ui
index b3f1e82..bf70a0e 100644
--- a/app/ui/main_window.ui
+++ b/app/ui/main_window.ui
@@ -16,153 +16,257 @@
-
-
-
-
- 0
- 0
-
-
-
-
- 230
- 16777215
-
-
-
-
- Sans
- 20
-
-
-
- background-color: #f8d7da;
+
+
-
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 230
+ 16777215
+
+
+
+
+ Sans
+ 20
+
+
+
+ background-color: #f8d7da;
border: 1px solid rgb(85, 87, 83);
-
-
- Last track:
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
-
- -
-
-
-
- Sans
- 20
-
-
-
- background-color: #f8d7da;
+
+
+ Last track:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 230
+ 16777215
+
+
+
+
+ Sans
+ 20
+
+
+
+ background-color: #d4edda;
border: 1px solid rgb(85, 87, 83);
-
-
-
-
-
+
+
+ Current track:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 230
+ 16777215
+
+
+
+
+ Sans
+ 20
+
+
+
+ background-color: #fff3cd;
+border: 1px solid rgb(85, 87, 83);
+
+
+ Next track:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+ -
+
+
-
+
+
+
+ Sans
+ 20
+
+
+
+ background-color: #f8d7da;
+border: 1px solid rgb(85, 87, 83);
+
+
+
+
+
+
+ -
+
+
+
+ Sans
+ 20
+
+
+
+ background-color: #d4edda;
+border: 1px solid rgb(85, 87, 83);
+
+
+
+
+
+
+ -
+
+
+
+ Sans
+ 20
+
+
+
+ background-color: #fff3cd;
+border: 1px solid rgb(85, 87, 83);
+
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+
+ 35
+
+
+
+ 00:00:00
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
-
+
+
+
+ 30
+ 30
+
+
+
+
+
+
+
+ previous.pngprevious.png
+
+
+
+ 41
+ 41
+
+
+
+
+ -
+
+
+
+ 30
+ 30
+
+
+
+
+
+
+
+ stop.pngstop.png
+
+
+
+ 41
+ 41
+
+
+
+
+ -
+
+
+
+ 30
+ 30
+
+
+
+
+
+
+
+ next.pngnext.png
+
+
+
+ 41
+ 41
+
+
+
+
+
+
+
+
+
-
-
-
-
- 0
- 0
-
-
-
-
- 230
- 16777215
-
-
-
-
- Sans
- 20
-
-
-
- background-color: #d4edda;
-border: 1px solid rgb(85, 87, 83);
-
-
- Current track:
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
-
- -
-
-
-
- Sans
- 20
-
-
-
- background-color: #d4edda;
-border: 1px solid rgb(85, 87, 83);
-
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 230
- 16777215
-
-
-
-
- Sans
- 20
-
-
-
- background-color: #fff3cd;
-border: 1px solid rgb(85, 87, 83);
-
-
- Next track:
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
-
- -
-
-
-
- Sans
- 20
-
-
-
- background-color: #fff3cd;
-border: 1px solid rgb(85, 87, 83);
-
-
-
-
-
-
- -
QAbstractItemView::NoEditTriggers
@@ -230,11 +334,8 @@ border: 1px solid rgb(85, 87, 83);
- -
+
-
-
- 5
-
-
@@ -332,8 +433,8 @@ border: 1px solid rgb(85, 87, 83);
- 20
- 20
+ 13
+ 60
@@ -388,8 +489,8 @@ border: 1px solid rgb(85, 87, 83);
- 20
- 20
+ 13
+ 60
@@ -444,8 +545,8 @@ border: 1px solid rgb(85, 87, 83);
- 20
- 20
+ 13
+ 60
@@ -500,8 +601,8 @@ border: 1px solid rgb(85, 87, 83);
- 20
- 20
+ 13
+ 60
@@ -562,16 +663,49 @@ border: 1px solid rgb(85, 87, 83);
+ -
+
+
-
+
+
-
+
+
+ Add &file...
+
+
+
+ -
+
+
+ Search &database
+
+
+
+
+
+ -
+
+
-
+
+
+ Set &next track
+
+
+
+ -
+
+
+ Track &info
+
+
+
+
+
+
+
- playlist
- current_track
- previous_track
- next_track
- current_track_2
- next_track_2
- previous_track_2
-
-
- toolBar
-
-
- TopToolBarArea
-
-
- false
-
-
-
-
-
-
-
-
-
-
-
-
- toolBar_2
-
-
- TopToolBarArea
-
-
- false
-
-
diff --git a/app/ui/next.png b/app/ui/next.png
new file mode 100644
index 0000000..25d76a3
Binary files /dev/null and b/app/ui/next.png differ
diff --git a/app/ui/previous.png b/app/ui/previous.png
new file mode 100644
index 0000000..8045b09
Binary files /dev/null and b/app/ui/previous.png differ
diff --git a/app/ui/stop.png b/app/ui/stop.png
new file mode 100644
index 0000000..3935df8
Binary files /dev/null and b/app/ui/stop.png differ