Compare commits
3 Commits
987db155a1
...
6e754c1b3a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e754c1b3a | ||
|
|
a80dc3f165 | ||
|
|
73879c6a99 |
107
app/music.py
107
app/music.py
@ -8,6 +8,8 @@ from time import sleep
|
|||||||
|
|
||||||
from log import DEBUG, ERROR
|
from log import DEBUG, ERROR
|
||||||
|
|
||||||
|
lock = threading.Lock()
|
||||||
|
|
||||||
|
|
||||||
class Music:
|
class Music:
|
||||||
"""
|
"""
|
||||||
@ -16,7 +18,7 @@ class Music:
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.current_track_start_time = None
|
self.current_track_start_time = None
|
||||||
self.fading = False
|
self.fading = 0
|
||||||
self.VLC = vlc.Instance()
|
self.VLC = vlc.Instance()
|
||||||
self.player = None
|
self.player = None
|
||||||
self.track_path = None
|
self.track_path = None
|
||||||
@ -32,13 +34,13 @@ class Music:
|
|||||||
|
|
||||||
DEBUG("music.fade()", True)
|
DEBUG("music.fade()", True)
|
||||||
|
|
||||||
if not self.playing():
|
if not self.player:
|
||||||
return None
|
|
||||||
|
|
||||||
# Only allow one track to fade at a time
|
|
||||||
if self.fading:
|
|
||||||
return
|
return
|
||||||
self.fading = True
|
|
||||||
|
if not self.player.get_position() > 0 and self.player.is_playing():
|
||||||
|
return
|
||||||
|
|
||||||
|
self.fading += 1
|
||||||
|
|
||||||
thread = threading.Thread(target=self._fade)
|
thread = threading.Thread(target=self._fade)
|
||||||
thread.start()
|
thread.start()
|
||||||
@ -51,10 +53,14 @@ class Music:
|
|||||||
# Take a copy of current player to allow another track to be
|
# Take a copy of current player to allow another track to be
|
||||||
# started without interfering here
|
# started without interfering here
|
||||||
|
|
||||||
p = self.player
|
|
||||||
|
|
||||||
DEBUG(f"music._fade(), {self.player=}", True)
|
DEBUG(f"music._fade(), {self.player=}", True)
|
||||||
|
|
||||||
|
with lock:
|
||||||
|
p = self.player
|
||||||
|
self.player = None
|
||||||
|
|
||||||
|
DEBUG(f"music._fade() post-lock, {self.player=}", True)
|
||||||
|
|
||||||
fade_time = Config.FADE_TIME / 1000
|
fade_time = Config.FADE_TIME / 1000
|
||||||
steps = Config.FADE_STEPS
|
steps = Config.FADE_STEPS
|
||||||
sleep_time = fade_time / steps
|
sleep_time = fade_time / steps
|
||||||
@ -69,27 +75,38 @@ class Music:
|
|||||||
measures_to_reduce_by = 0
|
measures_to_reduce_by = 0
|
||||||
for i in range(1, steps + 1):
|
for i in range(1, steps + 1):
|
||||||
measures_to_reduce_by += i
|
measures_to_reduce_by += i
|
||||||
volume_factor = 1 - (measures_to_reduce_by / total_measures_count)
|
volume_factor = 1 - (
|
||||||
|
measures_to_reduce_by / total_measures_count)
|
||||||
p.audio_set_volume(int(self.max_volume * volume_factor))
|
p.audio_set_volume(int(self.max_volume * volume_factor))
|
||||||
sleep(sleep_time)
|
sleep(sleep_time)
|
||||||
|
|
||||||
self.stop(p)
|
with lock:
|
||||||
self.fading = False
|
DEBUG(f"music._facde(), stopping {p=}", True)
|
||||||
|
|
||||||
|
p.stop()
|
||||||
|
DEBUG(f"Releasing player {p=}", True)
|
||||||
|
p.release()
|
||||||
|
# Ensure we don't reference player after release
|
||||||
|
p = None
|
||||||
|
|
||||||
|
self.fading -= 1
|
||||||
|
|
||||||
def get_playtime(self):
|
def get_playtime(self):
|
||||||
"Return elapsed play time"
|
"Return elapsed play time"
|
||||||
|
|
||||||
if not self.player:
|
with lock:
|
||||||
return None
|
if not self.player:
|
||||||
|
return None
|
||||||
|
|
||||||
return self.player.get_time()
|
return self.player.get_time()
|
||||||
|
|
||||||
def get_position(self):
|
def get_position(self):
|
||||||
"Return current position"
|
"Return current position"
|
||||||
|
|
||||||
DEBUG("music.get_position", True)
|
with lock:
|
||||||
|
DEBUG("music.get_position", True)
|
||||||
|
|
||||||
return self.player.get_position()
|
return self.player.get_position()
|
||||||
|
|
||||||
def play(self, path):
|
def play(self, path):
|
||||||
"""
|
"""
|
||||||
@ -118,46 +135,44 @@ class Music:
|
|||||||
get_position seems more reliable.
|
get_position seems more reliable.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if self.player:
|
with lock:
|
||||||
if self.player.get_position() > 0 and self.player.is_playing():
|
if self.player:
|
||||||
return True
|
if self.player.get_position() > 0 and self.player.is_playing():
|
||||||
|
return True
|
||||||
|
|
||||||
# We take a copy of the player when fading, so we could be
|
# We take a copy of the player when fading, so we could be
|
||||||
# playing in a fade nowFalse
|
# playing in a fade nowFalse
|
||||||
return self.fading
|
return self.fading > 0
|
||||||
|
|
||||||
def set_position(self, ms):
|
def set_position(self, ms):
|
||||||
"Set current play time in milliseconds from start"
|
"Set current play time in milliseconds from start"
|
||||||
|
|
||||||
return self.player.set_time(ms)
|
with lock:
|
||||||
|
return self.player.set_time(ms)
|
||||||
|
|
||||||
def set_volume(self, volume):
|
def set_volume(self, volume):
|
||||||
"Set maximum volume used for player"
|
"Set maximum volume used for player"
|
||||||
|
|
||||||
if not self.player:
|
with lock:
|
||||||
return
|
|
||||||
|
|
||||||
self.max_volume = volume
|
|
||||||
self.player.audio_set_volume(volume)
|
|
||||||
|
|
||||||
def stop(self, player=None):
|
|
||||||
"Immediately stop playing"
|
|
||||||
|
|
||||||
DEBUG(f"music.stop(), {player=}", True)
|
|
||||||
|
|
||||||
if not player:
|
|
||||||
if not self.player:
|
if not self.player:
|
||||||
return
|
return
|
||||||
player = self.player
|
|
||||||
|
|
||||||
DEBUG(f"music.stop({player=})")
|
self.max_volume = volume
|
||||||
|
self.player.audio_set_volume(volume)
|
||||||
|
|
||||||
position = player.get_position()
|
def stop(self):
|
||||||
player.stop()
|
"Immediately stop playing"
|
||||||
p = player
|
|
||||||
# Ensure we don't reference player after release
|
|
||||||
player = None
|
|
||||||
DEBUG(f"Releasing player {p=}", True)
|
|
||||||
p.release()
|
|
||||||
|
|
||||||
return position
|
with lock:
|
||||||
|
DEBUG(f"music.stop(), {self.player=}", True)
|
||||||
|
|
||||||
|
if not self.player:
|
||||||
|
return
|
||||||
|
|
||||||
|
position = self.player.get_position()
|
||||||
|
self.player.stop()
|
||||||
|
DEBUG(f"Releasing player {self.player=}", True)
|
||||||
|
self.player.release()
|
||||||
|
# Ensure we don't reference player after release
|
||||||
|
self.player = None
|
||||||
|
return position
|
||||||
|
|||||||
@ -138,8 +138,11 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
self.actionPlay_next.triggered.connect(self.play_next)
|
self.actionPlay_next.triggered.connect(self.play_next)
|
||||||
self.actionSearch_database.triggered.connect(self.search_database)
|
self.actionSearch_database.triggered.connect(self.search_database)
|
||||||
self.actionSelect_next_track.triggered.connect(self.select_next_track)
|
self.actionSelect_next_track.triggered.connect(self.select_next_track)
|
||||||
|
self.actionSelect_played_tracks.triggered.connect(self.select_played)
|
||||||
self.actionSelect_previous_track.triggered.connect(
|
self.actionSelect_previous_track.triggered.connect(
|
||||||
self.select_previous_track)
|
self.select_previous_track)
|
||||||
|
self.actionSelect_unplayed_tracks.triggered.connect(
|
||||||
|
self.select_unplayed)
|
||||||
self.actionSetNext.triggered.connect(self.set_next_track)
|
self.actionSetNext.triggered.connect(self.set_next_track)
|
||||||
self.actionSkip_next.triggered.connect(self.play_next)
|
self.actionSkip_next.triggered.connect(self.play_next)
|
||||||
self.actionSkipToEnd.triggered.connect(self.test_skip_to_end)
|
self.actionSkipToEnd.triggered.connect(self.test_skip_to_end)
|
||||||
@ -429,6 +432,11 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
|
|
||||||
self.visible_playlist_tab().select_next_track()
|
self.visible_playlist_tab().select_next_track()
|
||||||
|
|
||||||
|
def select_played(self):
|
||||||
|
"Select all played tracks in playlist"
|
||||||
|
|
||||||
|
self.visible_playlist_tab().select_played_tracks()
|
||||||
|
|
||||||
def select_previous_track(self):
|
def select_previous_track(self):
|
||||||
"Select previous or first track in playlist"
|
"Select previous or first track in playlist"
|
||||||
|
|
||||||
@ -449,6 +457,11 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
self.next_track = Tracks.get_track(session, next_track_id)
|
self.next_track = Tracks.get_track(session, next_track_id)
|
||||||
self.update_headers()
|
self.update_headers()
|
||||||
|
|
||||||
|
def select_unplayed(self):
|
||||||
|
"Select all unplayed tracks in playlist"
|
||||||
|
|
||||||
|
self.visible_playlist_tab().select_unplayed_tracks()
|
||||||
|
|
||||||
def show_warning(self, title, msg):
|
def show_warning(self, title, msg):
|
||||||
"Display a warning to user"
|
"Display a warning to user"
|
||||||
|
|
||||||
@ -491,16 +504,19 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
def stop_playing(self, fade=True):
|
def stop_playing(self, fade=True):
|
||||||
"Stop playing current track"
|
"Stop playing current track"
|
||||||
|
|
||||||
DEBUG("musicmuster.stop_playing()")
|
DEBUG("musicmuster.stop_playing()", True)
|
||||||
|
|
||||||
if not self.music.playing():
|
if not self.music.playing():
|
||||||
|
DEBUG("musicmuster.stop_playing(): not playing", True)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.previous_track_position = self.music.get_position()
|
self.previous_track_position = self.music.get_position()
|
||||||
if fade:
|
if fade:
|
||||||
|
DEBUG("musicmuster.stop_playing(): fading music", True)
|
||||||
self.music.fade()
|
self.music.fade()
|
||||||
else:
|
else:
|
||||||
self.music.stop()
|
self.music.stop()
|
||||||
|
DEBUG("musicmuster.stop_playing(): stopping music", True)
|
||||||
self.current_track_playlist_tab.clear_current()
|
self.current_track_playlist_tab.clear_current()
|
||||||
|
|
||||||
# Shuffle tracks along
|
# Shuffle tracks along
|
||||||
@ -550,7 +566,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
if not self.even_tick:
|
if not self.even_tick:
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.music.playing():
|
if self.music.player and self.music.playing():
|
||||||
self.playing = True
|
self.playing = True
|
||||||
playtime = self.music.get_playtime()
|
playtime = self.music.get_playtime()
|
||||||
time_to_fade = (self.current_track.fade_at - playtime)
|
time_to_fade = (self.current_track.fade_at - playtime)
|
||||||
|
|||||||
@ -425,6 +425,21 @@ class PlaylistTab(QTableWidget):
|
|||||||
|
|
||||||
self.selectRow(row)
|
self.selectRow(row)
|
||||||
|
|
||||||
|
def select_played_tracks(self):
|
||||||
|
"Select all played tracks in playlist"
|
||||||
|
|
||||||
|
# Need to allow multiple rows to be selected
|
||||||
|
self.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
|
||||||
|
|
||||||
|
self.clearSelection()
|
||||||
|
|
||||||
|
for row in range(self.rowCount()):
|
||||||
|
if self._get_row_id(row) in self.played_tracks:
|
||||||
|
self.selectRow(row)
|
||||||
|
|
||||||
|
# Reset extended selection
|
||||||
|
self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
||||||
|
|
||||||
def select_previous_track(self):
|
def select_previous_track(self):
|
||||||
"""
|
"""
|
||||||
Select previous or last track. Don't select notes. Wrap at first row.
|
Select previous or last track. Don't select notes. Wrap at first row.
|
||||||
@ -458,6 +473,25 @@ class PlaylistTab(QTableWidget):
|
|||||||
|
|
||||||
self.selectRow(row)
|
self.selectRow(row)
|
||||||
|
|
||||||
|
def select_unplayed_tracks(self):
|
||||||
|
"Select all unplayed tracks in playlist"
|
||||||
|
|
||||||
|
# Need to allow multiple rows to be selected
|
||||||
|
self.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
|
||||||
|
notes_rows = self._meta_get_notes()
|
||||||
|
|
||||||
|
self.clearSelection()
|
||||||
|
|
||||||
|
for row in range(self.rowCount()):
|
||||||
|
if row in notes_rows:
|
||||||
|
continue
|
||||||
|
if self._get_row_id(row) in self.played_tracks:
|
||||||
|
continue
|
||||||
|
self.selectRow(row)
|
||||||
|
|
||||||
|
# Reset extended selection
|
||||||
|
self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
||||||
|
|
||||||
def set_selected_as_next(self):
|
def set_selected_as_next(self):
|
||||||
"""
|
"""
|
||||||
Sets the selected track as the next track.
|
Sets the selected track as the next track.
|
||||||
|
|||||||
@ -757,6 +757,8 @@ border: 1px solid rgb(85, 87, 83);</string>
|
|||||||
<addaction name="actionAdd_file"/>
|
<addaction name="actionAdd_file"/>
|
||||||
<addaction name="action_Clear_selection"/>
|
<addaction name="action_Clear_selection"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
|
<addaction name="actionSelect_unplayed_tracks"/>
|
||||||
|
<addaction name="actionSelect_played_tracks"/>
|
||||||
<addaction name="actionMoveSelected"/>
|
<addaction name="actionMoveSelected"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="actionExport_playlist"/>
|
<addaction name="actionExport_playlist"/>
|
||||||
@ -977,6 +979,16 @@ border: 1px solid rgb(85, 87, 83);</string>
|
|||||||
<string>K</string>
|
<string>K</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionSelect_played_tracks">
|
||||||
|
<property name="text">
|
||||||
|
<string>Select played tracks</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionSelect_unplayed_tracks">
|
||||||
|
<property name="text">
|
||||||
|
<string>Select unplayed tracks</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="icons.qrc"/>
|
<include location="icons.qrc"/>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user