Merge branch 'next'
This commit is contained in:
commit
b914e4b548
17
changelog.md
17
changelog.md
@ -4,6 +4,23 @@
|
|||||||
|
|
||||||
### New additions
|
### New additions
|
||||||
|
|
||||||
|
### bugfixes
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
|
||||||
|
* the audio player module has received some improvements:
|
||||||
|
- When there is something being played, the label of the "play" button, located in all audio buffers, will change automatically to "pause". When pressed, it will pause the song instead of starting the playback again since the beginning.
|
||||||
|
- The last change will work also in the dialog for displaying audio information. Now it's possible to play and pause an audio from such dialog.
|
||||||
|
- When playing a voice message, if a song is playing already socializer will decrease the volume so you can hear the voice message well enough. Some seconds after the message has finished, the song's volume will be restored.
|
||||||
|
* In audio buffers, you will play the focused audio when pressing enter in the item, instead of opening the audio information dialog.
|
||||||
|
* Removed some old keystrokes in socializer buffers due to better alternatives. The reason for this change is that currently you don't need to be focusing an item in a buffer for being able to use the alternative keystrokes, which makes those a better implementation.
|
||||||
|
- Control+Enter and control+Shift+Enter: superseeded by Control+P for playing (and pausing) the currently focused audio.
|
||||||
|
- F5 and F6: superseeded by Alt+down and up arrows for modifying volume.
|
||||||
|
|
||||||
|
## changes in version 0.20 (25.04.2019)
|
||||||
|
|
||||||
|
### New additions
|
||||||
|
|
||||||
* For users with multiple soundcards, there is a new tab in the preferences dialogue of Socializer, called sound. From there, you can define which soundcard will be used for input and output. [#25,](https://code.manuelcortez.net/manuelcortez/socializer/issues/25)
|
* For users with multiple soundcards, there is a new tab in the preferences dialogue of Socializer, called sound. From there, you can define which soundcard will be used for input and output. [#25,](https://code.manuelcortez.net/manuelcortez/socializer/issues/25)
|
||||||
* the audio player can seek positions in the currently playing track. You can use the menu items (located in the main menu) or use the new available keystrokes dedicated to the actions. Seeking will be made in 5 second intervals.
|
* the audio player can seek positions in the currently playing track. You can use the menu items (located in the main menu) or use the new available keystrokes dedicated to the actions. Seeking will be made in 5 second intervals.
|
||||||
* Alt+Shift+Left arrow: Seek 5 seconds backwards.
|
* Alt+Shift+Left arrow: Seek 5 seconds backwards.
|
||||||
|
@ -304,11 +304,7 @@ class baseBuffer(object):
|
|||||||
|
|
||||||
def get_event(self, ev):
|
def get_event(self, ev):
|
||||||
""" Parses keyboard input in the ListCtrl and executes the event associated with user keypresses."""
|
""" Parses keyboard input in the ListCtrl and executes the event associated with user keypresses."""
|
||||||
if ev.GetKeyCode() == wx.WXK_RETURN and ev.ControlDown() and ev.ShiftDown(): event = "pause_audio"
|
if ev.GetKeyCode() == wx.WXK_RETURN: event = "open_post"
|
||||||
elif ev.GetKeyCode() == wx.WXK_RETURN and ev.ControlDown(): event = "play_audio"
|
|
||||||
elif ev.GetKeyCode() == wx.WXK_RETURN: event = "open_post"
|
|
||||||
elif ev.GetKeyCode() == wx.WXK_F5: event = "volume_down"
|
|
||||||
elif ev.GetKeyCode() == wx.WXK_F6: event = "volume_up"
|
|
||||||
else:
|
else:
|
||||||
event = None
|
event = None
|
||||||
ev.Skip()
|
ev.Skip()
|
||||||
@ -629,12 +625,33 @@ class audioBuffer(feedBuffer):
|
|||||||
if self.name == "me_audio":
|
if self.name == "me_audio":
|
||||||
self.tab.post.Enable(True)
|
self.tab.post.Enable(True)
|
||||||
|
|
||||||
|
def get_event(self, ev):
|
||||||
|
if ev.GetKeyCode() == wx.WXK_RETURN: event = "play_audio_from_keystroke"
|
||||||
|
else:
|
||||||
|
event = None
|
||||||
|
ev.Skip()
|
||||||
|
if event != None:
|
||||||
|
try:
|
||||||
|
getattr(self, event)()
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
def connect_events(self):
|
def connect_events(self):
|
||||||
widgetUtils.connect_event(self.tab.play, widgetUtils.BUTTON_PRESSED, self.play_audio)
|
widgetUtils.connect_event(self.tab.play, widgetUtils.BUTTON_PRESSED, self.play_audio)
|
||||||
widgetUtils.connect_event(self.tab.play_all, widgetUtils.BUTTON_PRESSED, self.play_all)
|
widgetUtils.connect_event(self.tab.play_all, widgetUtils.BUTTON_PRESSED, self.play_all)
|
||||||
|
pub.subscribe(self.change_label, "playback-changed")
|
||||||
super(audioBuffer, self).connect_events()
|
super(audioBuffer, self).connect_events()
|
||||||
|
|
||||||
def play_audio(self, *args, **kwargs):
|
def play_audio(self, *args, **kwargs):
|
||||||
|
if player.player.stream != None:
|
||||||
|
return player.player.pause()
|
||||||
|
selected = self.tab.list.get_selected()
|
||||||
|
if selected == -1:
|
||||||
|
selected = 0
|
||||||
|
pub.sendMessage("play", object=self.session.db[self.name]["items"][selected])
|
||||||
|
return True
|
||||||
|
|
||||||
|
def play_audio_from_keystroke(self, *args, **kwargs):
|
||||||
selected = self.tab.list.get_selected()
|
selected = self.tab.list.get_selected()
|
||||||
if selected == -1:
|
if selected == -1:
|
||||||
selected = 0
|
selected = 0
|
||||||
@ -778,6 +795,16 @@ class audioBuffer(feedBuffer):
|
|||||||
url = "https://vk.com/audio{user_id}_{post_id}".format(user_id=post["owner_id"], post_id=post["id"])
|
url = "https://vk.com/audio{user_id}_{post_id}".format(user_id=post["owner_id"], post_id=post["id"])
|
||||||
webbrowser.open_new_tab(url)
|
webbrowser.open_new_tab(url)
|
||||||
|
|
||||||
|
def change_label(self, stopped):
|
||||||
|
if hasattr(self.tab, "play"):
|
||||||
|
if stopped == False:
|
||||||
|
self.tab.play.SetLabel(_("P&ause"))
|
||||||
|
else:
|
||||||
|
self.tab.play.SetLabel(_("P&lay"))
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
pub.unsubscribe(self.change_label, "playback-changed")
|
||||||
|
|
||||||
class audioAlbum(audioBuffer):
|
class audioAlbum(audioBuffer):
|
||||||
""" this buffer was supposed to be used with audio albums
|
""" this buffer was supposed to be used with audio albums
|
||||||
but is deprecated as VK removed its audio support for third party apps."""
|
but is deprecated as VK removed its audio support for third party apps."""
|
||||||
@ -1186,8 +1213,7 @@ class chatBuffer(baseBuffer):
|
|||||||
a = presenters.displayAudioPresenter(session=self.session, postObject=[attachment["audio"]], interactor=interactors.displayAudioInteractor(), view=views.displayAudio())
|
a = presenters.displayAudioPresenter(session=self.session, postObject=[attachment["audio"]], interactor=interactors.displayAudioInteractor(), view=views.displayAudio())
|
||||||
elif attachment["type"] == "audio_message":
|
elif attachment["type"] == "audio_message":
|
||||||
link = attachment["audio_message"]["link_mp3"]
|
link = attachment["audio_message"]["link_mp3"]
|
||||||
output.speak(_("Playing..."))
|
pub.sendMessage("play-message", message_url=link)
|
||||||
pub.sendMessage("play", object=dict(url=link), set_info=False)
|
|
||||||
elif attachment["type"] == "link":
|
elif attachment["type"] == "link":
|
||||||
output.speak(_("Opening URL..."), True)
|
output.speak(_("Opening URL..."), True)
|
||||||
webbrowser.open_new_tab(attachment["link"]["url"])
|
webbrowser.open_new_tab(attachment["link"]["url"])
|
||||||
|
@ -171,6 +171,13 @@ class displayAudioInteractor(base.baseInteractor):
|
|||||||
getattr(self.view, control).Append(i)
|
getattr(self.view, control).Append(i)
|
||||||
getattr(self.view, control).SetSelection(0)
|
getattr(self.view, control).SetSelection(0)
|
||||||
|
|
||||||
|
def change_label(self, stopped):
|
||||||
|
|
||||||
|
if stopped == False:
|
||||||
|
self.view.play.SetLabel(_("P&ause"))
|
||||||
|
else:
|
||||||
|
self.view.play.SetLabel(_("P&lay"))
|
||||||
|
|
||||||
def install(self, *args, **kwargs):
|
def install(self, *args, **kwargs):
|
||||||
super(displayAudioInteractor, self).install(*args, **kwargs)
|
super(displayAudioInteractor, self).install(*args, **kwargs)
|
||||||
widgetUtils.connect_event(self.view.list, widgetUtils.LISTBOX_CHANGED, self.on_change)
|
widgetUtils.connect_event(self.view.list, widgetUtils.LISTBOX_CHANGED, self.on_change)
|
||||||
@ -180,11 +187,13 @@ class displayAudioInteractor(base.baseInteractor):
|
|||||||
widgetUtils.connect_event(self.view.remove, widgetUtils.BUTTON_PRESSED, self.on_remove_from_library)
|
widgetUtils.connect_event(self.view.remove, widgetUtils.BUTTON_PRESSED, self.on_remove_from_library)
|
||||||
pub.subscribe(self.set, self.modulename+"_set")
|
pub.subscribe(self.set, self.modulename+"_set")
|
||||||
pub.subscribe(self.add_items, self.modulename+"_add_items")
|
pub.subscribe(self.add_items, self.modulename+"_add_items")
|
||||||
|
pub.subscribe(self.change_label, "playback-changed")
|
||||||
|
|
||||||
def uninstall(self):
|
def uninstall(self):
|
||||||
super(displayAudioInteractor, self).uninstall()
|
super(displayAudioInteractor, self).uninstall()
|
||||||
pub.unsubscribe(self.set, self.modulename+"_set")
|
pub.unsubscribe(self.set, self.modulename+"_set")
|
||||||
pub.unsubscribe(self.add_items, self.modulename+"_add_items")
|
pub.unsubscribe(self.add_items, self.modulename+"_add_items")
|
||||||
|
pub.unsubscribe(self.change_label, "playback-changed")
|
||||||
|
|
||||||
def on_change(self, *args, **kwargs):
|
def on_change(self, *args, **kwargs):
|
||||||
post = self.view.get_audio()
|
post = self.view.get_audio()
|
||||||
|
@ -3,7 +3,7 @@ import logging
|
|||||||
from sessionmanager import utils
|
from sessionmanager import utils
|
||||||
from pubsub import pub
|
from pubsub import pub
|
||||||
from mysc.thread_utils import call_threaded
|
from mysc.thread_utils import call_threaded
|
||||||
from presenters import base
|
from presenters import base, player
|
||||||
|
|
||||||
log = logging.getLogger(__file__)
|
log = logging.getLogger(__file__)
|
||||||
|
|
||||||
@ -82,6 +82,8 @@ class displayAudioPresenter(base.basePresenter):
|
|||||||
|
|
||||||
def play(self, audio_index):
|
def play(self, audio_index):
|
||||||
post = self.post[audio_index]
|
post = self.post[audio_index]
|
||||||
|
if player.player.stream != None:
|
||||||
|
return player.player.pause()
|
||||||
pub.sendMessage("play", object=post)
|
pub.sendMessage("play", object=post)
|
||||||
|
|
||||||
def load_audios(self):
|
def load_audios(self):
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
As this player does not have (still) an associated GUI, I have decided to place here the code for the interactor, which connects a bunch of pubsub events, and the presenter itself.
|
As this player does not have (still) an associated GUI, I have decided to place here the code for the interactor, which connects a bunch of pubsub events, and the presenter itself.
|
||||||
"""
|
"""
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
import random
|
import random
|
||||||
import logging
|
import logging
|
||||||
import sound_lib
|
import sound_lib
|
||||||
@ -19,7 +20,6 @@ from sessionmanager import utils
|
|||||||
player = None
|
player = None
|
||||||
log = logging.getLogger("player")
|
log = logging.getLogger("player")
|
||||||
|
|
||||||
# This function will be deprecated when the player works with pubsub events, as will no longer be needed to instantiate and import the player directly.
|
|
||||||
def setup():
|
def setup():
|
||||||
global player
|
global player
|
||||||
if player == None:
|
if player == None:
|
||||||
@ -34,6 +34,7 @@ class audioPlayer(object):
|
|||||||
self.is_playing = False
|
self.is_playing = False
|
||||||
# This will be the URLStream handler
|
# This will be the URLStream handler
|
||||||
self.stream = None
|
self.stream = None
|
||||||
|
self.message = None
|
||||||
self.vol = config.app["sound"]["volume"]
|
self.vol = config.app["sound"]["volume"]
|
||||||
# this variable is set to true when the URLPlayer is decoding something, thus it will block other calls to the play method.
|
# this variable is set to true when the URLPlayer is decoding something, thus it will block other calls to the play method.
|
||||||
self.is_working = False
|
self.is_working = False
|
||||||
@ -41,13 +42,18 @@ class audioPlayer(object):
|
|||||||
self.queue = []
|
self.queue = []
|
||||||
# Index of the currently playing track.
|
# Index of the currently playing track.
|
||||||
self.playing_track = 0
|
self.playing_track = 0
|
||||||
|
self.playing_all = False
|
||||||
|
self.worker = RepeatingTimer(5, self.player_function)
|
||||||
|
self.worker.start()
|
||||||
# Status of the player.
|
# Status of the player.
|
||||||
self.stopped = True
|
self.stopped = True
|
||||||
# Modify some default settings present in Bass so it will increase timeout connection, thus causing less "connection timed out" errors when playing.
|
# Modify some default settings present in Bass so it will increase timeout connection, thus causing less "connection timed out" errors when playing.
|
||||||
bassconfig = BassConfig()
|
bassconfig = BassConfig()
|
||||||
# Set timeout connection to 30 seconds.
|
# Set timeout connection to 30 seconds.
|
||||||
bassconfig["net_timeout"] = 30000
|
bassconfig["net_timeout"] = 30000
|
||||||
|
# subscribe all pubsub events.
|
||||||
pub.subscribe(self.play, "play")
|
pub.subscribe(self.play, "play")
|
||||||
|
pub.subscribe(self.play_message, "play-message")
|
||||||
pub.subscribe(self.play_all, "play-all")
|
pub.subscribe(self.play_all, "play-all")
|
||||||
pub.subscribe(self.pause, "pause")
|
pub.subscribe(self.pause, "pause")
|
||||||
pub.subscribe(self.stop, "stop")
|
pub.subscribe(self.stop, "stop")
|
||||||
@ -55,6 +61,18 @@ class audioPlayer(object):
|
|||||||
pub.subscribe(self.play_previous, "play-previous")
|
pub.subscribe(self.play_previous, "play-previous")
|
||||||
pub.subscribe(self.seek, "seek")
|
pub.subscribe(self.seek, "seek")
|
||||||
|
|
||||||
|
# Stopped has a special function here, hence the decorator
|
||||||
|
# when stopped will be set to True, it will send a pubsub event to inform other parts of the application about the status change.
|
||||||
|
# this is useful for changing labels between play and pause, and so on, in buttons.
|
||||||
|
@property
|
||||||
|
def stopped(self):
|
||||||
|
return self._stopped
|
||||||
|
|
||||||
|
@stopped.setter
|
||||||
|
def stopped(self, value):
|
||||||
|
self._stopped = value
|
||||||
|
pub.sendMessage("playback-changed", stopped=value)
|
||||||
|
|
||||||
def play(self, object, set_info=True, fresh=False):
|
def play(self, object, set_info=True, fresh=False):
|
||||||
""" Play an URl Stream.
|
""" Play an URl Stream.
|
||||||
@object dict: typically an audio object as returned by VK, with a "url" component which must be a valid URL to a media file.
|
@object dict: typically an audio object as returned by VK, with a "url" component which must be a valid URL to a media file.
|
||||||
@ -70,9 +88,7 @@ class audioPlayer(object):
|
|||||||
log.exception("error when stopping the file")
|
log.exception("error when stopping the file")
|
||||||
self.stream = None
|
self.stream = None
|
||||||
self.stopped = True
|
self.stopped = True
|
||||||
if fresh == True and hasattr(self, "worker") and self.worker != None:
|
if fresh == True:
|
||||||
self.worker.cancel()
|
|
||||||
self.worker = None
|
|
||||||
self.queue = []
|
self.queue = []
|
||||||
# Make sure that there are no other sounds trying to be played.
|
# Make sure that there are no other sounds trying to be played.
|
||||||
if self.is_working == False:
|
if self.is_working == False:
|
||||||
@ -94,16 +110,41 @@ class audioPlayer(object):
|
|||||||
self.stopped = False
|
self.stopped = False
|
||||||
self.is_working = False
|
self.is_working = False
|
||||||
|
|
||||||
|
def play_message(self, message_url):
|
||||||
|
if self.message != None and (self.message.is_playing == True or self.message.is_stalled == True):
|
||||||
|
return self.stop_message()
|
||||||
|
output.speak(_("Playing..."))
|
||||||
|
url_ = utils.transform_audio_url(message_url)
|
||||||
|
url_ = bytes(url_, "utf-8")
|
||||||
|
try:
|
||||||
|
self.message = URLStream(url=url_)
|
||||||
|
except:
|
||||||
|
log.error("Unable to play URL %s" % (url_))
|
||||||
|
return
|
||||||
|
self.message.volume = self.vol/100.0
|
||||||
|
self.message.play()
|
||||||
|
volume_percent = self.volume*0.25
|
||||||
|
volume_step = self.volume*0.15
|
||||||
|
while self.stream.volume*100 > volume_percent:
|
||||||
|
self.stream.volume = self.stream.volume-(volume_step/100)
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
""" Stop audio playback. """
|
""" Stop audio playback. """
|
||||||
if self.stream != None and self.stream.is_playing == True:
|
if self.stream != None and self.stream.is_playing == True:
|
||||||
self.stream.stop()
|
self.stream.stop()
|
||||||
self.stopped = True
|
self.stopped = True
|
||||||
if hasattr(self, "worker") and self.worker != None:
|
|
||||||
self.worker.cancel()
|
|
||||||
self.worker = None
|
|
||||||
self.queue = []
|
self.queue = []
|
||||||
|
|
||||||
|
def stop_message(self):
|
||||||
|
if hasattr(self, "message") and self.message != None and self.message.is_playing == True:
|
||||||
|
self.message.stop()
|
||||||
|
volume_step = self.volume*0.15
|
||||||
|
while self.stream.volume*100 < self.volume:
|
||||||
|
self.stream.volume = self.stream.volume+(volume_step/100)
|
||||||
|
time.sleep(0.1)
|
||||||
|
self.message = None
|
||||||
|
|
||||||
def pause(self):
|
def pause(self):
|
||||||
""" pause the current playback, without destroying the queue or the current stream. If the stream is already paused this function will resume the playback. """
|
""" pause the current playback, without destroying the queue or the current stream. If the stream is already paused this function will resume the playback. """
|
||||||
if self.stream != None:
|
if self.stream != None:
|
||||||
@ -116,6 +157,8 @@ class audioPlayer(object):
|
|||||||
self.stopped = False
|
self.stopped = False
|
||||||
except BassError:
|
except BassError:
|
||||||
pass
|
pass
|
||||||
|
if self.playing_all == False and len(self.queue) > 0:
|
||||||
|
self.playing_all == True
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def volume(self):
|
def volume(self):
|
||||||
@ -130,7 +173,11 @@ class audioPlayer(object):
|
|||||||
elif vol > 100:
|
elif vol > 100:
|
||||||
self.vol = 100
|
self.vol = 100
|
||||||
if self.stream != None:
|
if self.stream != None:
|
||||||
self.stream.volume = self.vol/100.0
|
if self.message != None and self.message.is_playing:
|
||||||
|
self.stream.volume = (self.vol*0.25)/100.0
|
||||||
|
self.message.volume = self.vol/100.0
|
||||||
|
else:
|
||||||
|
self.stream.volume = self.vol/100.0
|
||||||
|
|
||||||
def play_all(self, list_of_songs, shuffle=False):
|
def play_all(self, list_of_songs, shuffle=False):
|
||||||
""" Play all passed songs and adds all of those to the queue.
|
""" Play all passed songs and adds all of those to the queue.
|
||||||
@ -145,16 +192,24 @@ class audioPlayer(object):
|
|||||||
if shuffle:
|
if shuffle:
|
||||||
random.shuffle(self.queue)
|
random.shuffle(self.queue)
|
||||||
call_threaded(self.play, self.queue[self.playing_track])
|
call_threaded(self.play, self.queue[self.playing_track])
|
||||||
self.worker = RepeatingTimer(5, self.player_function)
|
self.playing_all = True
|
||||||
self.worker.start()
|
|
||||||
|
|
||||||
def player_function(self):
|
def player_function(self):
|
||||||
""" Check if the stream has reached the end of the file so it will play the next song. """
|
""" Check if the stream has reached the end of the file so it will play the next song. """
|
||||||
|
if self.message != None and self.message.is_playing == False and len(self.message) == self.message.position:
|
||||||
|
volume_step = self.volume*0.15
|
||||||
|
while self.stream.volume*100 < self.volume:
|
||||||
|
self.stream.volume = self.stream.volume+(volume_step/100)
|
||||||
|
time.sleep(0.1)
|
||||||
if self.stream != None and self.stream.is_playing == False and self.stopped == False and len(self.stream) == self.stream.position:
|
if self.stream != None and self.stream.is_playing == False and self.stopped == False and len(self.stream) == self.stream.position:
|
||||||
if len(self.queue) == 0 or self.playing_track >= len(self.queue):
|
if self.playing_track >= len(self.queue):
|
||||||
self.worker.cancel()
|
self.stopped = True
|
||||||
|
self.playing_all = False
|
||||||
return
|
return
|
||||||
if self.playing_track < len(self.queue):
|
elif self.playing_all == False:
|
||||||
|
self.stopped = True
|
||||||
|
return
|
||||||
|
elif self.playing_track < len(self.queue):
|
||||||
self.playing_track += 1
|
self.playing_track += 1
|
||||||
self.play(self.queue[self.playing_track])
|
self.play(self.queue[self.playing_track])
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user