Added setting to control the output device in libVLC

This commit is contained in:
Manuel Cortez 2019-06-21 13:07:50 -05:00
parent b254a4eb1b
commit 1fcdd51358
5 changed files with 60 additions and 9 deletions

View File

@ -2,14 +2,16 @@
## Version 0.6 ## Version 0.6
* Updated VLC libraries and plugins to version 3.0.7. * Added a settings dialog for the application, from this dialog you will be able to find some general settings, available for MusicDL, and service's settings. Every service defines certain specific settings.
* When transcoding to mp3, the default bitrate now will be 320 KBPS instead of 192.
* Added a new and experimental extractor for supporting tidal. * Added a new and experimental extractor for supporting tidal.
* Take into account that this extractor requires you to have a paid account on tidal. Depending in the account level, you will be able to play and download music in high quality or lossless audio. MusicDL will handle both, though at the current moment, only downloading of lossless audio is implemented. * Take into account that this extractor requires you to have a paid account on tidal. Depending in the account level, you will be able to play and download music in high quality or lossless audio. MusicDL will handle both. Lossless audio will be downloaded as flac files, and high quality audio will be downloaded as transcoded 320 KBPS mp3.
* There is a new search mode supported in this service. You can retrieve all work for a certain artist by using the protocol artist://, plus the name of the artist you want to retrieve. For example, artist://The beatles will retrieve everything made by the beatles available in the service. The search results will be grouped by albums, compilations and singles, in this order. * There is a new search mode supported in this service. You can retrieve all work for a certain artist by using the protocol artist://, plus the name of the artist you want to retrieve. For example, artist://The beatles will retrieve everything made by the beatles available in the service. The search results will be grouped by albums, compilations and singles, in this order. Depending in the amount of results to display, this may take a long time.
* Due to recent problems with mail.ru and unavailable content in most cases, the service has been removed from MusicDL. * Due to recent problems with mail.ru and unavailable content in most cases, the service has been removed from MusicDL.
* YouTube: * YouTube:
* Fixed a long standing issue with playback of some elements, due to Youtube sending encrypted versions of these videos. Now playback should be better. * Fixed a long standing issue with playback of some elements, due to Youtube sending encrypted versions of these videos. Now playback should be better.
* Updated YoutubeDL to version 2019.6.7 * Updated YoutubeDL to version 2019.6.7
* Now it is possible to load 50 items for searches as opposed to the previous 20 items limit. This setting can be controlled in the service's preferences
* zaycev.net: * zaycev.net:
* Fixed extractor for searching and playing music in zaycev.net. * Fixed extractor for searching and playing music in zaycev.net.
* Unfortunately, it seems this service works only in the russian Federation and some other CIS countries due to copyright reasons. * Unfortunately, it seems this service works only in the russian Federation and some other CIS countries due to copyright reasons.

View File

@ -1,6 +1,7 @@
[main] [main]
volume = integer(default=50) volume = integer(default=50)
language = string(default="system") language = string(default="system")
output_device = string(default="")
[services] [services]
[[tidal]] [[tidal]]

View File

@ -2,6 +2,7 @@
import config import config
from utils import get_extractors from utils import get_extractors
from wxUI.configuration import configurationDialog from wxUI.configuration import configurationDialog
from . import player
class configuration(object): class configuration(object):
@ -12,10 +13,16 @@ class configuration(object):
self.save() self.save()
def create_config(self): def create_config(self):
self.view.create_general() self.output_devices = player.player.get_output_devices()
self.view.create_general(output_devices=[i["name"] for i in self.output_devices])
current_output_device = config.app["main"]["output_device"]
for i in self.output_devices:
# here we must compare against the str version of the vlc's device identifier.
if str(i["id"]) == current_output_device:
self.view.set_value("general", "output_device", i["name"])
break
extractors = get_extractors() extractors = get_extractors()
for i in extractors: for i in extractors:
print(i)
if hasattr(i, "settings"): if hasattr(i, "settings"):
panel = getattr(i, "settings")(self.view.notebook) panel = getattr(i, "settings")(self.view.notebook)
self.view.notebook.AddPage(panel, panel.name) self.view.notebook.AddPage(panel, panel.name)
@ -25,6 +32,18 @@ class configuration(object):
self.view.realize() self.view.realize()
def save(self): def save(self):
selected_output_device = self.view.get_value("general", "output_device")
selected_device_id = None
for i in self.output_devices:
# Vlc returns everything as bytes object whereas WX works with string objects, so I need to convert the wx returned string to bytes before
# Otherwise the comparison will be false.
# toDo: Check if utf-8 would be enough or we'd have to use the fylesystem encode for handling this.
if i["name"] == bytes(selected_output_device, "utf-8"):
selected_device_id = i["id"]
break
if config.app["main"]["output_device"] != selected_device_id:
config.app["main"]["output_device"] = selected_device_id
player.player.set_output_device(config.app["main"]["output_device"])
for i in range(0, self.view.notebook.GetPageCount()): for i in range(0, self.view.notebook.GetPageCount()):
page = self.view.notebook.GetPage(i) page = self.view.notebook.GetPage(i)
if hasattr(page, "save"): if hasattr(page, "save"):

View File

@ -5,6 +5,7 @@ import random
import vlc import vlc
import logging import logging
import config import config
import time
from pubsub import pub from pubsub import pub
from utils import call_threaded from utils import call_threaded
@ -33,6 +34,27 @@ class audioPlayer(object):
self.event_manager.event_attach(vlc.EventType.MediaPlayerEndReached, self.end_callback) self.event_manager.event_attach(vlc.EventType.MediaPlayerEndReached, self.end_callback)
self.event_manager.event_attach(vlc.EventType.MediaPlayerEncounteredError, self.playback_error) self.event_manager.event_attach(vlc.EventType.MediaPlayerEncounteredError, self.playback_error)
log.debug("Bound media playback events.") log.debug("Bound media playback events.")
# configure output device
self.set_output_device(config.app["main"]["output_device"])
def get_output_devices(self):
""" Retrieve enabled output devices so we can switch or use those later. """
log.debug("Retrieving output devices...")
devices = []
mods = self.player.audio_output_device_enum()
if mods:
mod = mods
while mod:
mod = mod.contents
devices.append(dict(id=mod.device, name=mod.description))
mod = mod.next
vlc.libvlc_audio_output_device_list_release(mods)
return devices
def set_output_device(self, device_id):
""" Set Output device to be ued in LibVLC"""
log.debug("Setting output audio device to {device}...".format(device=device_id,))
self.player.audio_output_device_set(None, device_id)
def play(self, item): def play(self, item):
self.stopped = True self.stopped = True
@ -142,4 +164,5 @@ class audioPlayer(object):
def __del__(self): def __del__(self):
self.event_manager.event_detach(vlc.EventType.MediaPlayerEndReached) self.event_manager.event_detach(vlc.EventType.MediaPlayerEndReached)
if hasattr(self, "event_manager"):
self.event_manager.event_detach(vlc.EventType.MediaPlayerEncounteredError, self.playback_error) self.event_manager.event_detach(vlc.EventType.MediaPlayerEncounteredError, self.playback_error)

View File

@ -3,9 +3,15 @@ import wx
import widgetUtils import widgetUtils
class general(wx.Panel, widgetUtils.BaseDialog): class general(wx.Panel, widgetUtils.BaseDialog):
def __init__(self, panel): def __init__(self, panel, output_devices=[]):
super(general, self).__init__(panel) super(general, self).__init__(panel)
sizer = wx.BoxSizer(wx.VERTICAL) sizer = wx.BoxSizer(wx.VERTICAL)
output_device_label = wx.StaticText(self, wx.NewId(), _("Output device"))
self.output_device = wx.ComboBox(self, wx.NewId(), choices=output_devices, value=output_devices[0], style=wx.CB_READONLY)
output_device_box = wx.BoxSizer(wx.HORIZONTAL)
output_device_box.Add(output_device_label, 0, wx.ALL, 5)
output_device_box.Add(self.output_device, 0, wx.ALL, 5)
sizer.Add(output_device_box, 0, wx.ALL, 5)
self.SetSizer(sizer) self.SetSizer(sizer)
class configurationDialog(widgetUtils.BaseDialog): class configurationDialog(widgetUtils.BaseDialog):
@ -16,8 +22,8 @@ class configurationDialog(widgetUtils.BaseDialog):
self.sizer = wx.BoxSizer(wx.VERTICAL) self.sizer = wx.BoxSizer(wx.VERTICAL)
self.notebook = wx.Treebook(self.panel) self.notebook = wx.Treebook(self.panel)
def create_general(self): def create_general(self, output_devices=[]):
self.general = general(self.notebook) self.general = general(self.notebook, output_devices=output_devices)
self.notebook.AddPage(self.general, _("General")) self.notebook.AddPage(self.general, _("General"))
self.general.SetFocus() self.general.SetFocus()