Merge branch 'next'

This commit is contained in:
Manuel Cortez 2019-04-25 17:42:16 -05:00
commit b914e4b548
5 changed files with 130 additions and 21 deletions

View File

@ -4,6 +4,23 @@
### 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)
* 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.

View File

@ -304,11 +304,7 @@ class baseBuffer(object):
def get_event(self, ev):
""" 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"
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"
if ev.GetKeyCode() == wx.WXK_RETURN: event = "open_post"
else:
event = None
ev.Skip()
@ -629,12 +625,33 @@ class audioBuffer(feedBuffer):
if self.name == "me_audio":
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):
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)
pub.subscribe(self.change_label, "playback-changed")
super(audioBuffer, self).connect_events()
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()
if selected == -1:
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"])
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):
""" this buffer was supposed to be used with audio albums
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())
elif attachment["type"] == "audio_message":
link = attachment["audio_message"]["link_mp3"]
output.speak(_("Playing..."))
pub.sendMessage("play", object=dict(url=link), set_info=False)
pub.sendMessage("play-message", message_url=link)
elif attachment["type"] == "link":
output.speak(_("Opening URL..."), True)
webbrowser.open_new_tab(attachment["link"]["url"])

View File

@ -171,6 +171,13 @@ class displayAudioInteractor(base.baseInteractor):
getattr(self.view, control).Append(i)
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):
super(displayAudioInteractor, self).install(*args, **kwargs)
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)
pub.subscribe(self.set, self.modulename+"_set")
pub.subscribe(self.add_items, self.modulename+"_add_items")
pub.subscribe(self.change_label, "playback-changed")
def uninstall(self):
super(displayAudioInteractor, self).uninstall()
pub.unsubscribe(self.set, self.modulename+"_set")
pub.unsubscribe(self.add_items, self.modulename+"_add_items")
pub.unsubscribe(self.change_label, "playback-changed")
def on_change(self, *args, **kwargs):
post = self.view.get_audio()

View File

@ -3,7 +3,7 @@ import logging
from sessionmanager import utils
from pubsub import pub
from mysc.thread_utils import call_threaded
from presenters import base
from presenters import base, player
log = logging.getLogger(__file__)
@ -82,6 +82,8 @@ class displayAudioPresenter(base.basePresenter):
def play(self, audio_index):
post = self.post[audio_index]
if player.player.stream != None:
return player.player.pause()
pub.sendMessage("play", object=post)
def load_audios(self):

View File

@ -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.
"""
import sys
import time
import random
import logging
import sound_lib
@ -19,7 +20,6 @@ from sessionmanager import utils
player = None
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():
global player
if player == None:
@ -34,6 +34,7 @@ class audioPlayer(object):
self.is_playing = False
# This will be the URLStream handler
self.stream = None
self.message = None
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.
self.is_working = False
@ -41,13 +42,18 @@ class audioPlayer(object):
self.queue = []
# Index of the currently playing track.
self.playing_track = 0
self.playing_all = False
self.worker = RepeatingTimer(5, self.player_function)
self.worker.start()
# Status of the player.
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.
bassconfig = BassConfig()
# Set timeout connection to 30 seconds.
bassconfig["net_timeout"] = 30000
# subscribe all pubsub events.
pub.subscribe(self.play, "play")
pub.subscribe(self.play_message, "play-message")
pub.subscribe(self.play_all, "play-all")
pub.subscribe(self.pause, "pause")
pub.subscribe(self.stop, "stop")
@ -55,6 +61,18 @@ class audioPlayer(object):
pub.subscribe(self.play_previous, "play-previous")
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):
""" 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.
@ -70,9 +88,7 @@ class audioPlayer(object):
log.exception("error when stopping the file")
self.stream = None
self.stopped = True
if fresh == True and hasattr(self, "worker") and self.worker != None:
self.worker.cancel()
self.worker = None
if fresh == True:
self.queue = []
# Make sure that there are no other sounds trying to be played.
if self.is_working == False:
@ -94,16 +110,41 @@ class audioPlayer(object):
self.stopped = 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):
""" Stop audio playback. """
if self.stream != None and self.stream.is_playing == True:
self.stream.stop()
self.stopped = True
if hasattr(self, "worker") and self.worker != None:
self.worker.cancel()
self.worker = None
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):
""" 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:
@ -116,6 +157,8 @@ class audioPlayer(object):
self.stopped = False
except BassError:
pass
if self.playing_all == False and len(self.queue) > 0:
self.playing_all == True
@property
def volume(self):
@ -130,7 +173,11 @@ class audioPlayer(object):
elif vol > 100:
self.vol = 100
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):
""" Play all passed songs and adds all of those to the queue.
@ -145,16 +192,24 @@ class audioPlayer(object):
if shuffle:
random.shuffle(self.queue)
call_threaded(self.play, self.queue[self.playing_track])
self.worker = RepeatingTimer(5, self.player_function)
self.worker.start()
self.playing_all = True
def player_function(self):
""" 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 len(self.queue) == 0 or self.playing_track >= len(self.queue):
self.worker.cancel()
if self.playing_track >= len(self.queue):
self.stopped = True
self.playing_all = False
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.play(self.queue[self.playing_track])