changed indent style
This commit is contained in:
parent
de28e80327
commit
ab264d4100
@ -1,9 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import sys
|
||||
if sys.version[0] == "3":
|
||||
raise ImportError()
|
||||
raise ImportError()
|
||||
import os
|
||||
import paths
|
||||
|
||||
def get():
|
||||
return os.path.join(paths.app_path(), "cacerts.txt")
|
||||
return os.path.join(paths.app_path(), "cacerts.txt")
|
||||
|
@ -12,7 +12,6 @@ MAINSPEC = "app-configuration.defaults"
|
||||
|
||||
app = None
|
||||
def setup ():
|
||||
global app
|
||||
log.debug("Loading global app settings...")
|
||||
app = config_utils.load_config(os.path.join(storage.data_directory, MAINFILE), os.path.join(paths.app_path(), MAINSPEC))
|
||||
|
||||
global app
|
||||
log.debug("Loading global app settings...")
|
||||
app = config_utils.load_config(os.path.join(storage.data_directory, MAINFILE), os.path.join(paths.app_path(), MAINSPEC))
|
||||
|
@ -8,66 +8,66 @@ class ConfigLoadError(Exception): pass
|
||||
def load_config(config_path, configspec_path=None, *args, **kwargs):
|
||||
# if os.path.exists(config_path):
|
||||
# clean_config(config_path)
|
||||
spec = ConfigObj(configspec_path, encoding='UTF8', list_values=False, _inspec=True)
|
||||
try:
|
||||
config = ConfigObj(infile=config_path, configspec=spec, create_empty=True, encoding='UTF8', *args, **kwargs)
|
||||
except ParseError:
|
||||
raise ConfigLoadError("Unable to load %r" % config_path)
|
||||
validator = Validator()
|
||||
validated = config.validate(validator, copy=True)
|
||||
if validated == True:
|
||||
config.write()
|
||||
return config
|
||||
spec = ConfigObj(configspec_path, encoding='UTF8', list_values=False, _inspec=True)
|
||||
try:
|
||||
config = ConfigObj(infile=config_path, configspec=spec, create_empty=True, encoding='UTF8', *args, **kwargs)
|
||||
except ParseError:
|
||||
raise ConfigLoadError("Unable to load %r" % config_path)
|
||||
validator = Validator()
|
||||
validated = config.validate(validator, copy=True)
|
||||
if validated == True:
|
||||
config.write()
|
||||
return config
|
||||
|
||||
def is_blank(arg):
|
||||
"Check if a line is blank."
|
||||
for c in arg:
|
||||
if c not in string.whitespace:
|
||||
return False
|
||||
return True
|
||||
"Check if a line is blank."
|
||||
for c in arg:
|
||||
if c not in string.whitespace:
|
||||
return False
|
||||
return True
|
||||
def get_keys(path):
|
||||
"Gets the keys of a configobj config file."
|
||||
res=[]
|
||||
fin=open(path)
|
||||
for line in fin:
|
||||
if not is_blank(line):
|
||||
res.append(line[0:line.find('=')].strip())
|
||||
fin.close()
|
||||
return res
|
||||
"Gets the keys of a configobj config file."
|
||||
res=[]
|
||||
fin=open(path)
|
||||
for line in fin:
|
||||
if not is_blank(line):
|
||||
res.append(line[0:line.find('=')].strip())
|
||||
fin.close()
|
||||
return res
|
||||
|
||||
def hist(keys):
|
||||
"Generates a histogram of an iterable."
|
||||
res={}
|
||||
for k in keys:
|
||||
res[k]=res.setdefault(k,0)+1
|
||||
return res
|
||||
"Generates a histogram of an iterable."
|
||||
res={}
|
||||
for k in keys:
|
||||
res[k]=res.setdefault(k,0)+1
|
||||
return res
|
||||
|
||||
def find_problems(hist):
|
||||
"Takes a histogram and returns a list of items occurring more than once."
|
||||
res=[]
|
||||
for k,v in hist.items():
|
||||
if v>1:
|
||||
res.append(k)
|
||||
return res
|
||||
"Takes a histogram and returns a list of items occurring more than once."
|
||||
res=[]
|
||||
for k,v in hist.items():
|
||||
if v>1:
|
||||
res.append(k)
|
||||
return res
|
||||
|
||||
def clean_config(path):
|
||||
"Cleans a config file. If duplicate values are found, delete all of them and just use the default."
|
||||
orig=[]
|
||||
cleaned=[]
|
||||
fin=open(path)
|
||||
for line in fin:
|
||||
orig.append(line)
|
||||
fin.close()
|
||||
for p in find_problems(hist(get_keys(path))):
|
||||
for o in orig:
|
||||
o.strip()
|
||||
if p not in o:
|
||||
cleaned.append(o)
|
||||
if len(cleaned) != 0:
|
||||
cam=open(path,'w')
|
||||
for c in cleaned:
|
||||
cam.write(c)
|
||||
cam.close()
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
"Cleans a config file. If duplicate values are found, delete all of them and just use the default."
|
||||
orig=[]
|
||||
cleaned=[]
|
||||
fin=open(path)
|
||||
for line in fin:
|
||||
orig.append(line)
|
||||
fin.close()
|
||||
for p in find_problems(hist(get_keys(path))):
|
||||
for o in orig:
|
||||
o.strip()
|
||||
if p not in o:
|
||||
cleaned.append(o)
|
||||
if len(cleaned) != 0:
|
||||
cam=open(path,'w')
|
||||
for c in cleaned:
|
||||
cam.write(c)
|
||||
cam.close()
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
@ -6,47 +6,47 @@ from . import player
|
||||
|
||||
class configuration(object):
|
||||
|
||||
def __init__(self):
|
||||
self.view = configurationDialog(_("Settings"))
|
||||
self.create_config()
|
||||
self.view.get_response()
|
||||
self.save()
|
||||
def __init__(self):
|
||||
self.view = configurationDialog(_("Settings"))
|
||||
self.create_config()
|
||||
self.view.get_response()
|
||||
self.save()
|
||||
|
||||
def create_config(self):
|
||||
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
|
||||
self.view.realize()
|
||||
extractors = get_services(import_all=True)
|
||||
for i in extractors:
|
||||
if hasattr(i, "settings"):
|
||||
panel = getattr(i, "settings")(self.view.notebook)
|
||||
self.view.notebook.InsertSubPage(1, panel, panel.name)
|
||||
panel.load()
|
||||
if hasattr(panel, "on_enabled"):
|
||||
panel.on_enabled()
|
||||
def create_config(self):
|
||||
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
|
||||
self.view.realize()
|
||||
extractors = get_services(import_all=True)
|
||||
for i in extractors:
|
||||
if hasattr(i, "settings"):
|
||||
panel = getattr(i, "settings")(self.view.notebook)
|
||||
self.view.notebook.InsertSubPage(1, panel, panel.name)
|
||||
panel.load()
|
||||
if hasattr(panel, "on_enabled"):
|
||||
panel.on_enabled()
|
||||
|
||||
|
||||
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()):
|
||||
page = self.view.notebook.GetPage(i)
|
||||
if hasattr(page, "save"):
|
||||
page.save()
|
||||
config.app.write()
|
||||
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()):
|
||||
page = self.view.notebook.GetPage(i)
|
||||
if hasattr(page, "save"):
|
||||
page.save()
|
||||
config.app.write()
|
||||
|
@ -20,252 +20,252 @@ log = logging.getLogger("controller.main")
|
||||
|
||||
class Controller(object):
|
||||
|
||||
def __init__(self):
|
||||
super(Controller, self).__init__()
|
||||
log.debug("Starting main controller...")
|
||||
# Setting up the player object
|
||||
player.setup()
|
||||
# Get main window
|
||||
self.window = mainWindow.mainWindow(extractors=[i.interface.name for i in get_services()])
|
||||
log.debug("Main window created")
|
||||
self.window.change_status(_(u"Ready"))
|
||||
# Here we will save results for searches as song objects.
|
||||
self.results = []
|
||||
self.connect_events()
|
||||
self.timer = wx.Timer(self.window)
|
||||
self.window.Bind(wx.EVT_TIMER, self.on_timer, self.timer)
|
||||
self.timer.Start(75)
|
||||
self.window.vol_slider.SetValue(player.player.volume)
|
||||
# Shows window.
|
||||
utils.call_threaded(updater.do_update)
|
||||
log.debug("Music DL is ready")
|
||||
self.window.Show()
|
||||
def __init__(self):
|
||||
super(Controller, self).__init__()
|
||||
log.debug("Starting main controller...")
|
||||
# Setting up the player object
|
||||
player.setup()
|
||||
# Get main window
|
||||
self.window = mainWindow.mainWindow(extractors=[i.interface.name for i in get_services()])
|
||||
log.debug("Main window created")
|
||||
self.window.change_status(_(u"Ready"))
|
||||
# Here we will save results for searches as song objects.
|
||||
self.results = []
|
||||
self.connect_events()
|
||||
self.timer = wx.Timer(self.window)
|
||||
self.window.Bind(wx.EVT_TIMER, self.on_timer, self.timer)
|
||||
self.timer.Start(75)
|
||||
self.window.vol_slider.SetValue(player.player.volume)
|
||||
# Shows window.
|
||||
utils.call_threaded(updater.do_update)
|
||||
log.debug("Music DL is ready")
|
||||
self.window.Show()
|
||||
|
||||
def get_status_info(self):
|
||||
""" Formatting string for status bar messages """
|
||||
if len(self.results) > 0:
|
||||
results = _(u"Showing {0} results.").format(len(self.results))
|
||||
else:
|
||||
results = u""
|
||||
if player.player.shuffle:
|
||||
shuffle = _(u"Shuffle on")
|
||||
else:
|
||||
shuffle = u""
|
||||
final = u"{0} {1}".format(results, shuffle)
|
||||
return final
|
||||
def get_status_info(self):
|
||||
""" Formatting string for status bar messages """
|
||||
if len(self.results) > 0:
|
||||
results = _(u"Showing {0} results.").format(len(self.results))
|
||||
else:
|
||||
results = u""
|
||||
if player.player.shuffle:
|
||||
shuffle = _(u"Shuffle on")
|
||||
else:
|
||||
shuffle = u""
|
||||
final = u"{0} {1}".format(results, shuffle)
|
||||
return final
|
||||
|
||||
def connect_events(self):
|
||||
""" connects all widgets to their corresponding events."""
|
||||
log.debug("Binding events...")
|
||||
widgetUtils.connect_event(self.window.search, widgetUtils.BUTTON_PRESSED, self.on_search)
|
||||
widgetUtils.connect_event(self.window.list, widgetUtils.LISTBOX_ITEM_ACTIVATED, self.on_activated)
|
||||
widgetUtils.connect_event(self.window.list, widgetUtils.KEYPRESS, self.on_keypress)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_play, menuitem=self.window.player_play)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_settings, menuitem=self.window.settings)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_next, menuitem=self.window.player_next)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_previous, menuitem=self.window.player_previous)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_stop, menuitem=self.window.player_stop)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_volume_down, menuitem=self.window.player_volume_down)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_volume_up, menuitem=self.window.player_volume_up)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_mute, menuitem=self.window.player_mute)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_shuffle, menuitem=self.window.player_shuffle)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.window.about_dialog, menuitem=self.window.about)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_check_for_updates, menuitem=self.window.check_for_updates)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_visit_changelog, menuitem=self.window.changelog)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_visit_website, menuitem=self.window.website)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_report_error, menuitem=self.window.report)
|
||||
widgetUtils.connect_event(self.window.previous, widgetUtils.BUTTON_PRESSED, self.on_previous)
|
||||
widgetUtils.connect_event(self.window.play, widgetUtils.BUTTON_PRESSED, self.on_play_pause)
|
||||
widgetUtils.connect_event(self.window.stop, widgetUtils.BUTTON_PRESSED, self.on_stop)
|
||||
widgetUtils.connect_event(self.window.next, widgetUtils.BUTTON_PRESSED, self.on_next)
|
||||
self.window.Bind(wx.EVT_COMMAND_SCROLL_THUMBTRACK, self.on_set_volume, self.window.vol_slider)
|
||||
self.window.Bind(wx.EVT_COMMAND_SCROLL_CHANGED, self.on_set_volume, self.window.vol_slider)
|
||||
self.window.Bind(wx.EVT_COMMAND_SCROLL_THUMBTRACK, self.on_time_change, self.window.time_slider)
|
||||
self.window.Bind(wx.EVT_COMMAND_SCROLL_CHANGED, self.on_time_change, self.window.time_slider)
|
||||
self.window.list.Bind(wx.EVT_LISTBOX_DCLICK, self.on_play)
|
||||
self.window.list.Bind(wx.EVT_CONTEXT_MENU, self.on_context)
|
||||
self.window.Bind(wx.EVT_CLOSE, self.on_close)
|
||||
pub.subscribe(self.change_status, "change_status")
|
||||
pub.subscribe(self.on_download_finished, "download_finished")
|
||||
pub.subscribe(self.on_notify, "notify")
|
||||
pub.subscribe(self.on_update_progress, "update-progress")
|
||||
def connect_events(self):
|
||||
""" connects all widgets to their corresponding events."""
|
||||
log.debug("Binding events...")
|
||||
widgetUtils.connect_event(self.window.search, widgetUtils.BUTTON_PRESSED, self.on_search)
|
||||
widgetUtils.connect_event(self.window.list, widgetUtils.LISTBOX_ITEM_ACTIVATED, self.on_activated)
|
||||
widgetUtils.connect_event(self.window.list, widgetUtils.KEYPRESS, self.on_keypress)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_play, menuitem=self.window.player_play)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_settings, menuitem=self.window.settings)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_next, menuitem=self.window.player_next)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_previous, menuitem=self.window.player_previous)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_stop, menuitem=self.window.player_stop)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_volume_down, menuitem=self.window.player_volume_down)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_volume_up, menuitem=self.window.player_volume_up)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_mute, menuitem=self.window.player_mute)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_shuffle, menuitem=self.window.player_shuffle)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.window.about_dialog, menuitem=self.window.about)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_check_for_updates, menuitem=self.window.check_for_updates)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_visit_changelog, menuitem=self.window.changelog)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_visit_website, menuitem=self.window.website)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_report_error, menuitem=self.window.report)
|
||||
widgetUtils.connect_event(self.window.previous, widgetUtils.BUTTON_PRESSED, self.on_previous)
|
||||
widgetUtils.connect_event(self.window.play, widgetUtils.BUTTON_PRESSED, self.on_play_pause)
|
||||
widgetUtils.connect_event(self.window.stop, widgetUtils.BUTTON_PRESSED, self.on_stop)
|
||||
widgetUtils.connect_event(self.window.next, widgetUtils.BUTTON_PRESSED, self.on_next)
|
||||
self.window.Bind(wx.EVT_COMMAND_SCROLL_THUMBTRACK, self.on_set_volume, self.window.vol_slider)
|
||||
self.window.Bind(wx.EVT_COMMAND_SCROLL_CHANGED, self.on_set_volume, self.window.vol_slider)
|
||||
self.window.Bind(wx.EVT_COMMAND_SCROLL_THUMBTRACK, self.on_time_change, self.window.time_slider)
|
||||
self.window.Bind(wx.EVT_COMMAND_SCROLL_CHANGED, self.on_time_change, self.window.time_slider)
|
||||
self.window.list.Bind(wx.EVT_LISTBOX_DCLICK, self.on_play)
|
||||
self.window.list.Bind(wx.EVT_CONTEXT_MENU, self.on_context)
|
||||
self.window.Bind(wx.EVT_CLOSE, self.on_close)
|
||||
pub.subscribe(self.change_status, "change_status")
|
||||
pub.subscribe(self.on_download_finished, "download_finished")
|
||||
pub.subscribe(self.on_notify, "notify")
|
||||
pub.subscribe(self.on_update_progress, "update-progress")
|
||||
|
||||
# Event functions. These functions will call other functions in a thread and are bound to widget events.
|
||||
# Event functions. These functions will call other functions in a thread and are bound to widget events.
|
||||
|
||||
def on_update_progress(self, value):
|
||||
wx.CallAfter(self.window.progressbar.SetValue, value)
|
||||
def on_update_progress(self, value):
|
||||
wx.CallAfter(self.window.progressbar.SetValue, value)
|
||||
|
||||
def on_settings(self, *args, **kwargs):
|
||||
settings = configuration.configuration()
|
||||
self.reload_extractors()
|
||||
def on_settings(self, *args, **kwargs):
|
||||
settings = configuration.configuration()
|
||||
self.reload_extractors()
|
||||
|
||||
def on_search(self, *args, **kwargs):
|
||||
text = self.window.get_text()
|
||||
if text == "":
|
||||
return
|
||||
extractor = self.window.extractor.GetValue()
|
||||
self.change_status(_(u"Searching {0}... ").format(text))
|
||||
utils.call_threaded(self.search, text=text, extractor=extractor)
|
||||
def on_search(self, *args, **kwargs):
|
||||
text = self.window.get_text()
|
||||
if text == "":
|
||||
return
|
||||
extractor = self.window.extractor.GetValue()
|
||||
self.change_status(_(u"Searching {0}... ").format(text))
|
||||
utils.call_threaded(self.search, text=text, extractor=extractor)
|
||||
|
||||
def on_activated(self, *args, **kwargs):
|
||||
self.on_play()
|
||||
def on_activated(self, *args, **kwargs):
|
||||
self.on_play()
|
||||
|
||||
def on_keypress(self, ev):
|
||||
if ev.GetKeyCode() == wx.WXK_RETURN:
|
||||
return self.on_play()
|
||||
elif ev.GetKeyCode() == wx.WXK_SPACE:
|
||||
return self.on_play_pause()
|
||||
elif ev.GetKeyCode() == wx.WXK_LEFT and ev.ShiftDown():
|
||||
position = player.player.player.get_time()
|
||||
if position > 5000:
|
||||
player.player.player.set_time(position-5000)
|
||||
else:
|
||||
player.player.player.set_time(0)
|
||||
return
|
||||
elif ev.GetKeyCode() == wx.WXK_RIGHT and ev.ShiftDown():
|
||||
position = player.player.player.get_time()
|
||||
player.player.player.set_time(position+5000)
|
||||
return
|
||||
elif ev.GetKeyCode() == wx.WXK_UP and ev.ControlDown():
|
||||
return self.on_volume_up()
|
||||
elif ev.GetKeyCode() == wx.WXK_DOWN and ev.ControlDown():
|
||||
return self.on_volume_down()
|
||||
elif ev.GetKeyCode() == wx.WXK_LEFT and ev.AltDown():
|
||||
return self.on_previous()
|
||||
elif ev.GetKeyCode() == wx.WXK_RIGHT and ev.AltDown():
|
||||
return self.on_next()
|
||||
ev.Skip()
|
||||
def on_keypress(self, ev):
|
||||
if ev.GetKeyCode() == wx.WXK_RETURN:
|
||||
return self.on_play()
|
||||
elif ev.GetKeyCode() == wx.WXK_SPACE:
|
||||
return self.on_play_pause()
|
||||
elif ev.GetKeyCode() == wx.WXK_LEFT and ev.ShiftDown():
|
||||
position = player.player.player.get_time()
|
||||
if position > 5000:
|
||||
player.player.player.set_time(position-5000)
|
||||
else:
|
||||
player.player.player.set_time(0)
|
||||
return
|
||||
elif ev.GetKeyCode() == wx.WXK_RIGHT and ev.ShiftDown():
|
||||
position = player.player.player.get_time()
|
||||
player.player.player.set_time(position+5000)
|
||||
return
|
||||
elif ev.GetKeyCode() == wx.WXK_UP and ev.ControlDown():
|
||||
return self.on_volume_up()
|
||||
elif ev.GetKeyCode() == wx.WXK_DOWN and ev.ControlDown():
|
||||
return self.on_volume_down()
|
||||
elif ev.GetKeyCode() == wx.WXK_LEFT and ev.AltDown():
|
||||
return self.on_previous()
|
||||
elif ev.GetKeyCode() == wx.WXK_RIGHT and ev.AltDown():
|
||||
return self.on_next()
|
||||
ev.Skip()
|
||||
|
||||
def on_play_pause(self, *args, **kwargs):
|
||||
if player.player.player.is_playing() == 1:
|
||||
self.window.play.SetLabel(_(u"Play"))
|
||||
return player.player.pause()
|
||||
else:
|
||||
self.window.play.SetLabel(_(u"Pause"))
|
||||
return player.player.player.play()
|
||||
def on_play_pause(self, *args, **kwargs):
|
||||
if player.player.player.is_playing() == 1:
|
||||
self.window.play.SetLabel(_(u"Play"))
|
||||
return player.player.pause()
|
||||
else:
|
||||
self.window.play.SetLabel(_(u"Pause"))
|
||||
return player.player.player.play()
|
||||
|
||||
def on_next(self, *args, **kwargs):
|
||||
return utils.call_threaded(player.player.next)
|
||||
def on_next(self, *args, **kwargs):
|
||||
return utils.call_threaded(player.player.next)
|
||||
|
||||
def on_previous(self, *args, **kwargs):
|
||||
return utils.call_threaded(player.player.previous)
|
||||
def on_previous(self, *args, **kwargs):
|
||||
return utils.call_threaded(player.player.previous)
|
||||
|
||||
def on_play(self, *args, **kwargs):
|
||||
items = self.results[::]
|
||||
playing_item = self.window.get_item()
|
||||
self.window.play.SetLabel(_(u"Pause"))
|
||||
return utils.call_threaded(player.player.play_all, items, playing=playing_item, shuffle=self.window.player_shuffle.IsChecked())
|
||||
def on_play(self, *args, **kwargs):
|
||||
items = self.results[::]
|
||||
playing_item = self.window.get_item()
|
||||
self.window.play.SetLabel(_(u"Pause"))
|
||||
return utils.call_threaded(player.player.play_all, items, playing=playing_item, shuffle=self.window.player_shuffle.IsChecked())
|
||||
|
||||
def on_stop(self, *args, **kwargs):
|
||||
player.player.stop()
|
||||
self.window.play.SetLabel(_(u"Play"))
|
||||
def on_stop(self, *args, **kwargs):
|
||||
player.player.stop()
|
||||
self.window.play.SetLabel(_(u"Play"))
|
||||
|
||||
def on_volume_down(self, *args, **kwargs):
|
||||
self.window.vol_slider.SetValue(self.window.vol_slider.GetValue()-5)
|
||||
self.on_set_volume()
|
||||
def on_volume_down(self, *args, **kwargs):
|
||||
self.window.vol_slider.SetValue(self.window.vol_slider.GetValue()-5)
|
||||
self.on_set_volume()
|
||||
|
||||
def on_volume_up(self, *args, **kwargs):
|
||||
self.window.vol_slider.SetValue(self.window.vol_slider.GetValue()+5)
|
||||
self.on_set_volume()
|
||||
def on_volume_up(self, *args, **kwargs):
|
||||
self.window.vol_slider.SetValue(self.window.vol_slider.GetValue()+5)
|
||||
self.on_set_volume()
|
||||
|
||||
def on_mute(self, *args, **kwargs):
|
||||
self.window.vol_slider.SetValue(0)
|
||||
self.on_set_volume()
|
||||
def on_mute(self, *args, **kwargs):
|
||||
self.window.vol_slider.SetValue(0)
|
||||
self.on_set_volume()
|
||||
|
||||
def on_shuffle(self, *args, **kwargs):
|
||||
player.player.shuffle = self.window.player_shuffle.IsChecked()
|
||||
def on_shuffle(self, *args, **kwargs):
|
||||
player.player.shuffle = self.window.player_shuffle.IsChecked()
|
||||
|
||||
def on_context(self, *args, **kwargs):
|
||||
item = self.window.get_item()
|
||||
if item == -1:
|
||||
return wx.Bell()
|
||||
menu = menus.contextMenu()
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.on_play, menuitem=menu.play)
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.on_download, menuitem=menu.download)
|
||||
self.window.PopupMenu(menu, wx.GetMousePosition())
|
||||
menu.Destroy()
|
||||
def on_context(self, *args, **kwargs):
|
||||
item = self.window.get_item()
|
||||
if item == -1:
|
||||
return wx.Bell()
|
||||
menu = menus.contextMenu()
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.on_play, menuitem=menu.play)
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.on_download, menuitem=menu.download)
|
||||
self.window.PopupMenu(menu, wx.GetMousePosition())
|
||||
menu.Destroy()
|
||||
|
||||
def on_download(self, *args, **kwargs):
|
||||
item = self.results[self.window.get_item()]
|
||||
if item.download_url == "":
|
||||
item.get_download_url()
|
||||
log.debug("Starting requested download: {0} (using extractor: {1})".format(item.title, self.extractor.name))
|
||||
f = "{item_name}.{item_extension}".format(item_name=item.format_track(), item_extension=item.extractor.get_file_format())
|
||||
path = self.window.get_destination_path(utils.safe_filename(f))
|
||||
if path != None:
|
||||
log.debug("User has requested the following path: {0}".format(path,))
|
||||
if self.extractor.transcoder_enabled() == True: # Send download to vlc based transcoder
|
||||
utils.call_threaded(player.player.transcode_audio, item, path, _format=item.extractor.get_file_format(), metadata=item.get_metadata())
|
||||
else:
|
||||
log.debug("downloading %s URL to %s filename" % (item.download_url, path,))
|
||||
utils.call_threaded(utils.download_file, item.download_url, path, metadata=item.get_metadata())
|
||||
def on_download(self, *args, **kwargs):
|
||||
item = self.results[self.window.get_item()]
|
||||
if item.download_url == "":
|
||||
item.get_download_url()
|
||||
log.debug("Starting requested download: {0} (using extractor: {1})".format(item.title, self.extractor.name))
|
||||
f = "{item_name}.{item_extension}".format(item_name=item.format_track(), item_extension=item.extractor.get_file_format())
|
||||
path = self.window.get_destination_path(utils.safe_filename(f))
|
||||
if path != None:
|
||||
log.debug("User has requested the following path: {0}".format(path,))
|
||||
if self.extractor.transcoder_enabled() == True: # Send download to vlc based transcoder
|
||||
utils.call_threaded(player.player.transcode_audio, item, path, _format=item.extractor.get_file_format(), metadata=item.get_metadata())
|
||||
else:
|
||||
log.debug("downloading %s URL to %s filename" % (item.download_url, path,))
|
||||
utils.call_threaded(utils.download_file, item.download_url, path, metadata=item.get_metadata())
|
||||
|
||||
def on_set_volume(self, *args, **kwargs):
|
||||
volume = self.window.vol_slider.GetValue()
|
||||
player.player.volume = volume
|
||||
def on_set_volume(self, *args, **kwargs):
|
||||
volume = self.window.vol_slider.GetValue()
|
||||
player.player.volume = volume
|
||||
|
||||
def on_time_change(self, event, *args, **kwargs):
|
||||
p = event.GetPosition()
|
||||
player.player.player.set_position(p/100.0)
|
||||
event.Skip()
|
||||
def on_time_change(self, event, *args, **kwargs):
|
||||
p = event.GetPosition()
|
||||
player.player.player.set_position(p/100.0)
|
||||
event.Skip()
|
||||
|
||||
def on_timer(self, *args, **kwargs):
|
||||
if not self.window.time_slider.HasFocus():
|
||||
progress = player.player.player.get_position()*100
|
||||
self.window.time_slider.SetValue(progress)
|
||||
def on_timer(self, *args, **kwargs):
|
||||
if not self.window.time_slider.HasFocus():
|
||||
progress = player.player.player.get_position()*100
|
||||
self.window.time_slider.SetValue(progress)
|
||||
|
||||
def on_close(self, event):
|
||||
log.debug("Exiting...")
|
||||
self.timer.Stop()
|
||||
pub.unsubscribe(self.on_download_finished, "download_finished")
|
||||
config.app.write()
|
||||
event.Skip()
|
||||
widgetUtils.exit_application()
|
||||
def on_close(self, event):
|
||||
log.debug("Exiting...")
|
||||
self.timer.Stop()
|
||||
pub.unsubscribe(self.on_download_finished, "download_finished")
|
||||
config.app.write()
|
||||
event.Skip()
|
||||
widgetUtils.exit_application()
|
||||
|
||||
def change_status(self, status):
|
||||
""" Function used for changing the status bar from outside the main controller module."""
|
||||
self.window.change_status(u"{0} {1}".format(status, self.get_status_info()))
|
||||
def change_status(self, status):
|
||||
""" Function used for changing the status bar from outside the main controller module."""
|
||||
self.window.change_status(u"{0} {1}".format(status, self.get_status_info()))
|
||||
|
||||
def on_visit_website(self, *args, **kwargs):
|
||||
webbrowser.open_new_tab(application.url)
|
||||
def on_visit_website(self, *args, **kwargs):
|
||||
webbrowser.open_new_tab(application.url)
|
||||
|
||||
def on_report_error(self, *args, **kwargs):
|
||||
r = issueReporter.reportBug()
|
||||
def on_report_error(self, *args, **kwargs):
|
||||
r = issueReporter.reportBug()
|
||||
|
||||
def on_visit_changelog(self, *args, **kwargs):
|
||||
webbrowser.open_new_tab(application.url+"/news")
|
||||
def on_visit_changelog(self, *args, **kwargs):
|
||||
webbrowser.open_new_tab(application.url+"/news")
|
||||
|
||||
def on_check_for_updates(self, *args, **kwargs):
|
||||
utils.call_threaded(updater.do_update)
|
||||
def on_check_for_updates(self, *args, **kwargs):
|
||||
utils.call_threaded(updater.do_update)
|
||||
|
||||
def on_download_finished(self, file):
|
||||
title = "MusicDL"
|
||||
msg = _(u"File downloaded: {0}").format(file,)
|
||||
self.window.notify(title, msg)
|
||||
def on_download_finished(self, file):
|
||||
title = "MusicDL"
|
||||
msg = _(u"File downloaded: {0}").format(file,)
|
||||
self.window.notify(title, msg)
|
||||
|
||||
def on_notify(self, title, message):
|
||||
self.window.notify(title, message)
|
||||
def on_notify(self, title, message):
|
||||
self.window.notify(title, message)
|
||||
|
||||
# real functions. These functions really are doing the work.
|
||||
def search(self, text, extractor, *args, **kwargs):
|
||||
extractors = get_services()
|
||||
for i in extractors:
|
||||
if extractor == i.interface.name:
|
||||
self.extractor = i.interface()
|
||||
break
|
||||
log.debug("Started search for {0} (selected extractor: {1})".format(text, self.extractor.name))
|
||||
wx.CallAfter(self.window.list.Clear)
|
||||
self.extractor.search(text)
|
||||
self.results = self.extractor.results
|
||||
for i in self.results:
|
||||
wx.CallAfter(self.window.list.Append, i.format_track())
|
||||
if len(self.results) == 0:
|
||||
wx.CallAfter(self.change_status, _(u"No results found. "))
|
||||
else:
|
||||
wx.CallAfter(self.change_status, u"")
|
||||
wx.CallAfter(self.window.list.SetFocus)
|
||||
# real functions. These functions really are doing the work.
|
||||
def search(self, text, extractor, *args, **kwargs):
|
||||
extractors = get_services()
|
||||
for i in extractors:
|
||||
if extractor == i.interface.name:
|
||||
self.extractor = i.interface()
|
||||
break
|
||||
log.debug("Started search for {0} (selected extractor: {1})".format(text, self.extractor.name))
|
||||
wx.CallAfter(self.window.list.Clear)
|
||||
self.extractor.search(text)
|
||||
self.results = self.extractor.results
|
||||
for i in self.results:
|
||||
wx.CallAfter(self.window.list.Append, i.format_track())
|
||||
if len(self.results) == 0:
|
||||
wx.CallAfter(self.change_status, _(u"No results found. "))
|
||||
else:
|
||||
wx.CallAfter(self.change_status, u"")
|
||||
wx.CallAfter(self.window.list.SetFocus)
|
||||
|
||||
def reload_extractors(self):
|
||||
extractors = [i.interface.name for i in get_services()]
|
||||
self.window.extractor.SetItems(extractors)
|
||||
self.window.extractor.SetValue(extractors[0])
|
||||
def reload_extractors(self):
|
||||
extractors = [i.interface.name for i in get_services()]
|
||||
self.window.extractor.SetItems(extractors)
|
||||
self.window.extractor.SetValue(extractors[0])
|
||||
|
@ -12,158 +12,158 @@ player = None
|
||||
log = logging.getLogger("controller.player")
|
||||
|
||||
def setup():
|
||||
global player
|
||||
if player == None:
|
||||
player = audioPlayer()
|
||||
global player
|
||||
if player == None:
|
||||
player = audioPlayer()
|
||||
|
||||
class audioPlayer(object):
|
||||
|
||||
def __init__(self):
|
||||
self.is_playing = False
|
||||
self.vol = config.app["main"]["volume"]
|
||||
self.is_working = False
|
||||
self.queue = []
|
||||
self.stopped = True
|
||||
self.queue_pos = 0
|
||||
self.shuffle = False
|
||||
self.instance = vlc.Instance()
|
||||
log.debug("Instantiating Media player with the following information: VLC version detected={}. VLC bindings for python version{}".format(vlc.libvlc_get_version(), vlc.__version__))
|
||||
self.player = self.instance.media_player_new()
|
||||
log.debug("Media player instantiated.")
|
||||
self.event_manager = self.player.event_manager()
|
||||
self.event_manager.event_attach(vlc.EventType.MediaPlayerEndReached, self.end_callback)
|
||||
self.event_manager.event_attach(vlc.EventType.MediaPlayerEncounteredError, self.playback_error)
|
||||
log.debug("Bound media playback events.")
|
||||
# configure output device
|
||||
self.set_output_device(config.app["main"]["output_device"])
|
||||
def __init__(self):
|
||||
self.is_playing = False
|
||||
self.vol = config.app["main"]["volume"]
|
||||
self.is_working = False
|
||||
self.queue = []
|
||||
self.stopped = True
|
||||
self.queue_pos = 0
|
||||
self.shuffle = False
|
||||
self.instance = vlc.Instance()
|
||||
log.debug("Instantiating Media player with the following information: VLC version detected={}. VLC bindings for python version{}".format(vlc.libvlc_get_version(), vlc.__version__))
|
||||
self.player = self.instance.media_player_new()
|
||||
log.debug("Media player instantiated.")
|
||||
self.event_manager = self.player.event_manager()
|
||||
self.event_manager.event_attach(vlc.EventType.MediaPlayerEndReached, self.end_callback)
|
||||
self.event_manager.event_attach(vlc.EventType.MediaPlayerEncounteredError, self.playback_error)
|
||||
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 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 used in LibVLC"""
|
||||
log.debug("Setting output audio device to {device}...".format(device=device_id,))
|
||||
self.player.audio_output_device_set(None, device_id)
|
||||
def set_output_device(self, device_id):
|
||||
""" Set Output device to be used 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):
|
||||
self.stopped = True
|
||||
if self.is_working == False:
|
||||
self.is_working = True
|
||||
if item.download_url == "":
|
||||
item.get_download_url()
|
||||
log.debug("playing {0}...".format(item.download_url,))
|
||||
self.stream_new = self.instance.media_new(item.download_url)
|
||||
self.player.set_media(self.stream_new)
|
||||
if self.player.play() == -1:
|
||||
log.debug("Error when playing the file {0}".format(item.title,))
|
||||
pub.sendMessage("change_status", status=_("Error playing {0}. {1}.").format(item.title, e.description))
|
||||
self.stopped = True
|
||||
self.is_working = False
|
||||
self.next()
|
||||
return
|
||||
self.player.audio_set_volume(self.vol)
|
||||
pub.sendMessage("change_status", status=_("Playing {0}.").format(item.title))
|
||||
self.stopped = False
|
||||
self.is_working = False
|
||||
def play(self, item):
|
||||
self.stopped = True
|
||||
if self.is_working == False:
|
||||
self.is_working = True
|
||||
if item.download_url == "":
|
||||
item.get_download_url()
|
||||
log.debug("playing {0}...".format(item.download_url,))
|
||||
self.stream_new = self.instance.media_new(item.download_url)
|
||||
self.player.set_media(self.stream_new)
|
||||
if self.player.play() == -1:
|
||||
log.debug("Error when playing the file {0}".format(item.title,))
|
||||
pub.sendMessage("change_status", status=_("Error playing {0}. {1}.").format(item.title, e.description))
|
||||
self.stopped = True
|
||||
self.is_working = False
|
||||
self.next()
|
||||
return
|
||||
self.player.audio_set_volume(self.vol)
|
||||
pub.sendMessage("change_status", status=_("Playing {0}.").format(item.title))
|
||||
self.stopped = False
|
||||
self.is_working = False
|
||||
|
||||
def next(self):
|
||||
if len(self.queue) > 0:
|
||||
if self.shuffle:
|
||||
self.queue_pos = random.randint(0, len(self.queue)-1)
|
||||
else:
|
||||
if self.queue_pos < len(self.queue)-1:
|
||||
self.queue_pos += 1
|
||||
else:
|
||||
self.queue_pos = 0
|
||||
self.play(self.queue[self.queue_pos])
|
||||
def next(self):
|
||||
if len(self.queue) > 0:
|
||||
if self.shuffle:
|
||||
self.queue_pos = random.randint(0, len(self.queue)-1)
|
||||
else:
|
||||
if self.queue_pos < len(self.queue)-1:
|
||||
self.queue_pos += 1
|
||||
else:
|
||||
self.queue_pos = 0
|
||||
self.play(self.queue[self.queue_pos])
|
||||
|
||||
def previous(self):
|
||||
if len(self.queue) > 0:
|
||||
if self.shuffle:
|
||||
self.queue_pos = random.randint(0, len(self.queue)-1)
|
||||
else:
|
||||
if self.queue_pos > 0:
|
||||
self.queue_pos -= 1
|
||||
else:
|
||||
self.queue_pos = len(self.queue)-1
|
||||
self.play(self.queue[self.queue_pos])
|
||||
def previous(self):
|
||||
if len(self.queue) > 0:
|
||||
if self.shuffle:
|
||||
self.queue_pos = random.randint(0, len(self.queue)-1)
|
||||
else:
|
||||
if self.queue_pos > 0:
|
||||
self.queue_pos -= 1
|
||||
else:
|
||||
self.queue_pos = len(self.queue)-1
|
||||
self.play(self.queue[self.queue_pos])
|
||||
|
||||
def stop(self):
|
||||
self.player.stop()
|
||||
self.stopped = True
|
||||
def stop(self):
|
||||
self.player.stop()
|
||||
self.stopped = True
|
||||
|
||||
def pause(self):
|
||||
self.player.pause()
|
||||
if self.stopped == True:
|
||||
self.stopped = False
|
||||
else:
|
||||
self.stopped = True
|
||||
def pause(self):
|
||||
self.player.pause()
|
||||
if self.stopped == True:
|
||||
self.stopped = False
|
||||
else:
|
||||
self.stopped = True
|
||||
|
||||
@property
|
||||
def volume(self):
|
||||
return self.vol
|
||||
@property
|
||||
def volume(self):
|
||||
return self.vol
|
||||
|
||||
@volume.setter
|
||||
def volume(self, vol):
|
||||
if vol <= 100 and vol >= 0:
|
||||
config.app["main"]["volume"] = vol
|
||||
self.vol = vol
|
||||
self.player.audio_set_volume(self.vol)
|
||||
@volume.setter
|
||||
def volume(self, vol):
|
||||
if vol <= 100 and vol >= 0:
|
||||
config.app["main"]["volume"] = vol
|
||||
self.vol = vol
|
||||
self.player.audio_set_volume(self.vol)
|
||||
|
||||
def play_all(self, list_of_items, playing=0, shuffle=False):
|
||||
if list_of_items != self.queue:
|
||||
self.queue = list_of_items
|
||||
self.shuffle = shuffle
|
||||
self.queue_pos = playing
|
||||
self.play(self.queue[self.queue_pos])
|
||||
def play_all(self, list_of_items, playing=0, shuffle=False):
|
||||
if list_of_items != self.queue:
|
||||
self.queue = list_of_items
|
||||
self.shuffle = shuffle
|
||||
self.queue_pos = playing
|
||||
self.play(self.queue[self.queue_pos])
|
||||
|
||||
def end_callback(self, event, *args, **kwargs):
|
||||
#https://github.com/ZeBobo5/Vlc.DotNet/issues/4
|
||||
call_threaded(self.next)
|
||||
def end_callback(self, event, *args, **kwargs):
|
||||
#https://github.com/ZeBobo5/Vlc.DotNet/issues/4
|
||||
call_threaded(self.next)
|
||||
|
||||
def transcode_audio(self, item, path, _format="mp3", bitrate=320, metadata=dict()):
|
||||
""" Converts given item to mp3. This method will be available when needed automatically."""
|
||||
if item.download_url == "":
|
||||
item.get_download_url()
|
||||
log.debug("Download started: filename={0}, url={1}".format(path, item.download_url))
|
||||
temporary_filename = "chunk_{0}".format(random.randint(0,2000000))
|
||||
temporary_path = os.path.join(os.path.dirname(path), temporary_filename)
|
||||
# Let's get a new VLC instance for transcoding this file.
|
||||
transcoding_instance = vlc.Instance(*["--sout=#transcode{acodec=%s,ab=%d}:file{mux=raw,dst=\"%s\"}"% (_format, bitrate, temporary_path,)])
|
||||
transcoder = transcoding_instance.media_player_new()
|
||||
transcoder.set_mrl(item.download_url)
|
||||
pub.sendMessage("change_status", status=_(u"Downloading {0}.").format(item.title,))
|
||||
media = transcoder.get_media()
|
||||
transcoder.play()
|
||||
while True:
|
||||
state = media.get_state()
|
||||
pub.sendMessage("change_status", status=_("Downloading {0} ({1}%).").format(item.title, int(transcoder.get_position()*100)))
|
||||
pub.sendMessage("update-progress", value=int(transcoder.get_position()*100))
|
||||
if str(state) == 'State.Ended':
|
||||
break
|
||||
elif str(state) == 'state.error':
|
||||
os.remove(temporary_path)
|
||||
break
|
||||
transcoder.release()
|
||||
os.rename(temporary_path, path)
|
||||
log.debug("Download finished sucsessfully.")
|
||||
pub.sendMessage("download_finished", file=os.path.basename(path))
|
||||
def transcode_audio(self, item, path, _format="mp3", bitrate=320, metadata=dict()):
|
||||
""" Converts given item to mp3. This method will be available when needed automatically."""
|
||||
if item.download_url == "":
|
||||
item.get_download_url()
|
||||
log.debug("Download started: filename={0}, url={1}".format(path, item.download_url))
|
||||
temporary_filename = "chunk_{0}".format(random.randint(0,2000000))
|
||||
temporary_path = os.path.join(os.path.dirname(path), temporary_filename)
|
||||
# Let's get a new VLC instance for transcoding this file.
|
||||
transcoding_instance = vlc.Instance(*["--sout=#transcode{acodec=%s,ab=%d}:file{mux=raw,dst=\"%s\"}"% (_format, bitrate, temporary_path,)])
|
||||
transcoder = transcoding_instance.media_player_new()
|
||||
transcoder.set_mrl(item.download_url)
|
||||
pub.sendMessage("change_status", status=_(u"Downloading {0}.").format(item.title,))
|
||||
media = transcoder.get_media()
|
||||
transcoder.play()
|
||||
while True:
|
||||
state = media.get_state()
|
||||
pub.sendMessage("change_status", status=_("Downloading {0} ({1}%).").format(item.title, int(transcoder.get_position()*100)))
|
||||
pub.sendMessage("update-progress", value=int(transcoder.get_position()*100))
|
||||
if str(state) == 'State.Ended':
|
||||
break
|
||||
elif str(state) == 'state.error':
|
||||
os.remove(temporary_path)
|
||||
break
|
||||
transcoder.release()
|
||||
os.rename(temporary_path, path)
|
||||
log.debug("Download finished sucsessfully.")
|
||||
pub.sendMessage("download_finished", file=os.path.basename(path))
|
||||
|
||||
def playback_error(self, event):
|
||||
pub.sendMessage("notify", title=_("Error"), message=_("There was an error while trying to access the file you have requested."))
|
||||
def playback_error(self, event):
|
||||
pub.sendMessage("notify", title=_("Error"), message=_("There was an error while trying to access the file you have requested."))
|
||||
|
||||
def __del__(self):
|
||||
self.event_manager.event_detach(vlc.EventType.MediaPlayerEndReached)
|
||||
if hasattr(self, "event_manager"):
|
||||
self.event_manager.event_detach(vlc.EventType.MediaPlayerEncounteredError, self.playback_error)
|
||||
def __del__(self):
|
||||
self.event_manager.event_detach(vlc.EventType.MediaPlayerEndReached)
|
||||
if hasattr(self, "event_manager"):
|
||||
self.event_manager.event_detach(vlc.EventType.MediaPlayerEncounteredError, self.playback_error)
|
||||
|
@ -4,4 +4,4 @@ from . import fix_requests
|
||||
from .import fix_winpaths
|
||||
|
||||
def setup():
|
||||
fix_requests.fix()
|
||||
fix_requests.fix()
|
||||
|
@ -7,6 +7,6 @@ import paths
|
||||
log = logging.getLogger("fixes.fix_requests")
|
||||
|
||||
def fix():
|
||||
log.debug("Applying fix for requests...")
|
||||
os.environ["REQUESTS_CA_BUNDLE"] = os.path.join(paths.app_path(), "cacerts.txt")
|
||||
log.debug("Changed CA path to %s" % (os.environ["REQUESTS_CA_BUNDLE"],))
|
||||
log.debug("Applying fix for requests...")
|
||||
os.environ["REQUESTS_CA_BUNDLE"] = os.path.join(paths.app_path(), "cacerts.txt")
|
||||
log.debug("Changed CA path to %s" % (os.environ["REQUESTS_CA_BUNDLE"],))
|
||||
|
@ -4,9 +4,9 @@ import winpaths
|
||||
from ctypes import wintypes
|
||||
|
||||
def _get_path_buf(csidl):
|
||||
path_buf = ctypes.create_unicode_buffer(wintypes.MAX_PATH)
|
||||
result = winpaths._SHGetFolderPath(0, csidl, 0, 0, path_buf)
|
||||
return path_buf.value
|
||||
path_buf = ctypes.create_unicode_buffer(wintypes.MAX_PATH)
|
||||
result = winpaths._SHGetFolderPath(0, csidl, 0, 0, path_buf)
|
||||
return path_buf.value
|
||||
|
||||
def fix():
|
||||
winpaths._get_path_buf = _get_path_buf
|
||||
winpaths._get_path_buf = _get_path_buf
|
||||
|
14
src/i18n.py
14
src/i18n.py
@ -9,10 +9,10 @@ import paths
|
||||
log = logging.getLogger("i18n")
|
||||
|
||||
def setup():
|
||||
lang = locale.getdefaultlocale()[0]
|
||||
os.environ["lang"] = lang
|
||||
log.debug("System detected language: {0}".format(lang,))
|
||||
if sys.version[0] == "3":
|
||||
gettext.install("musicdl", localedir=os.path.join(paths.app_path(), "locales"))
|
||||
else:
|
||||
gettext.install("musicdl", localedir=os.path.join(paths.app_path(), "locales"), unicode=True)
|
||||
lang = locale.getdefaultlocale()[0]
|
||||
os.environ["lang"] = lang
|
||||
log.debug("System detected language: {0}".format(lang,))
|
||||
if sys.version[0] == "3":
|
||||
gettext.install("musicdl", localedir=os.path.join(paths.app_path(), "locales"))
|
||||
else:
|
||||
gettext.install("musicdl", localedir=os.path.join(paths.app_path(), "locales"), unicode=True)
|
||||
|
@ -27,37 +27,37 @@ from utils import call_threaded
|
||||
from . import wx_ui
|
||||
|
||||
class reportBug(object):
|
||||
def __init__(self):
|
||||
self.dialog = wx_ui.reportBugDialog()
|
||||
widgetUtils.connect_event(self.dialog.ok, widgetUtils.BUTTON_PRESSED, self.send)
|
||||
self.dialog.get_response()
|
||||
def __init__(self):
|
||||
self.dialog = wx_ui.reportBugDialog()
|
||||
widgetUtils.connect_event(self.dialog.ok, widgetUtils.BUTTON_PRESSED, self.send)
|
||||
self.dialog.get_response()
|
||||
|
||||
def do_report(self, *args, **kwargs):
|
||||
r = requests.post(*args, **kwargs)
|
||||
if r.status_code > 300:
|
||||
wx.CallAfter(self.dialog.error)
|
||||
wx.CallAfter(self.dialog.progress.Destroy)
|
||||
wx.CallAfter(self.dialog.success, r.json()["data"]["issue"]["id"])
|
||||
def do_report(self, *args, **kwargs):
|
||||
r = requests.post(*args, **kwargs)
|
||||
if r.status_code > 300:
|
||||
wx.CallAfter(self.dialog.error)
|
||||
wx.CallAfter(self.dialog.progress.Destroy)
|
||||
wx.CallAfter(self.dialog.success, r.json()["data"]["issue"]["id"])
|
||||
|
||||
def send(self, *args, **kwargs):
|
||||
if self.dialog.get("summary") == "" or self.dialog.get("description") == "" or self.dialog.get("first_name") == "" or self.dialog.get("last_name") == "":
|
||||
self.dialog.no_filled()
|
||||
return
|
||||
if self.dialog.get("agree") == False:
|
||||
self.dialog.no_checkbox()
|
||||
return
|
||||
title = self.dialog.get("summary")
|
||||
body = self.dialog.get("description")
|
||||
issue_type = "issue" # for now just have issue
|
||||
app_type = storage.app_type
|
||||
app_version = application.version
|
||||
reporter_name = "{first_name} {last_name}".format(first_name=self.dialog.get("first_name"), last_name=self.dialog.get("last_name"))
|
||||
reporter_contact_type = "email" # For now just email is supported in the issue reporter
|
||||
reporter_contact_handle = self.dialog.get("email")
|
||||
operating_system = platform.platform()
|
||||
json = dict(title=title, issue_type=issue_type, body=body, operating_system=operating_system, app_type=app_type, app_version=app_version, reporter_name=reporter_name, reporter_contact_handle=reporter_contact_handle, reporter_contact_type=reporter_contact_type)
|
||||
auth=HTTPBasicAuth(application.bts_name, application.bts_access_token)
|
||||
url = "{bts_url}/issue/new".format(bts_url=application.bts_url)
|
||||
call_threaded(self.do_report, url, json=json, auth=auth)
|
||||
self.dialog.show_progress()
|
||||
self.dialog.EndModal(wx.ID_OK)
|
||||
def send(self, *args, **kwargs):
|
||||
if self.dialog.get("summary") == "" or self.dialog.get("description") == "" or self.dialog.get("first_name") == "" or self.dialog.get("last_name") == "":
|
||||
self.dialog.no_filled()
|
||||
return
|
||||
if self.dialog.get("agree") == False:
|
||||
self.dialog.no_checkbox()
|
||||
return
|
||||
title = self.dialog.get("summary")
|
||||
body = self.dialog.get("description")
|
||||
issue_type = "issue" # for now just have issue
|
||||
app_type = storage.app_type
|
||||
app_version = application.version
|
||||
reporter_name = "{first_name} {last_name}".format(first_name=self.dialog.get("first_name"), last_name=self.dialog.get("last_name"))
|
||||
reporter_contact_type = "email" # For now just email is supported in the issue reporter
|
||||
reporter_contact_handle = self.dialog.get("email")
|
||||
operating_system = platform.platform()
|
||||
json = dict(title=title, issue_type=issue_type, body=body, operating_system=operating_system, app_type=app_type, app_version=app_version, reporter_name=reporter_name, reporter_contact_handle=reporter_contact_handle, reporter_contact_type=reporter_contact_type)
|
||||
auth=HTTPBasicAuth(application.bts_name, application.bts_access_token)
|
||||
url = "{bts_url}/issue/new".format(bts_url=application.bts_url)
|
||||
call_threaded(self.do_report, url, json=json, auth=auth)
|
||||
self.dialog.show_progress()
|
||||
self.dialog.EndModal(wx.ID_OK)
|
||||
|
@ -21,89 +21,89 @@ import widgetUtils
|
||||
import application
|
||||
|
||||
class reportBugDialog(widgetUtils.BaseDialog):
|
||||
def __init__(self):
|
||||
super(reportBugDialog, self).__init__(parent=None, id=wx.NewId())
|
||||
self.SetTitle(_(u"Report an error"))
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
def __init__(self):
|
||||
super(reportBugDialog, self).__init__(parent=None, id=wx.NewId())
|
||||
self.SetTitle(_(u"Report an error"))
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
summaryLabel = wx.StaticText(panel, -1, _(u"Briefly describe what happened. You will be able to thoroughly explain it later"), size=wx.DefaultSize)
|
||||
self.summary = wx.TextCtrl(panel, -1)
|
||||
dc = wx.WindowDC(self.summary)
|
||||
dc.SetFont(self.summary.GetFont())
|
||||
self.summary.SetSize(dc.GetTextExtent("a"*80))
|
||||
summaryB = wx.BoxSizer(wx.HORIZONTAL)
|
||||
summaryB.Add(summaryLabel, 0, wx.ALL, 5)
|
||||
summaryB.Add(self.summary, 0, wx.ALL, 5)
|
||||
sizer.Add(summaryB, 0, wx.ALL, 5)
|
||||
summaryLabel = wx.StaticText(panel, -1, _(u"Briefly describe what happened. You will be able to thoroughly explain it later"), size=wx.DefaultSize)
|
||||
self.summary = wx.TextCtrl(panel, -1)
|
||||
dc = wx.WindowDC(self.summary)
|
||||
dc.SetFont(self.summary.GetFont())
|
||||
self.summary.SetSize(dc.GetTextExtent("a"*80))
|
||||
summaryB = wx.BoxSizer(wx.HORIZONTAL)
|
||||
summaryB.Add(summaryLabel, 0, wx.ALL, 5)
|
||||
summaryB.Add(self.summary, 0, wx.ALL, 5)
|
||||
sizer.Add(summaryB, 0, wx.ALL, 5)
|
||||
|
||||
first_nameLabel = wx.StaticText(panel, -1, _(u"First Name"), size=wx.DefaultSize)
|
||||
self.first_name = wx.TextCtrl(panel, -1)
|
||||
dc = wx.WindowDC(self.first_name)
|
||||
dc.SetFont(self.first_name.GetFont())
|
||||
self.first_name.SetSize(dc.GetTextExtent("a"*40))
|
||||
first_nameB = wx.BoxSizer(wx.HORIZONTAL)
|
||||
first_nameB.Add(first_nameLabel, 0, wx.ALL, 5)
|
||||
first_nameB.Add(self.first_name, 0, wx.ALL, 5)
|
||||
sizer.Add(first_nameB, 0, wx.ALL, 5)
|
||||
first_nameLabel = wx.StaticText(panel, -1, _(u"First Name"), size=wx.DefaultSize)
|
||||
self.first_name = wx.TextCtrl(panel, -1)
|
||||
dc = wx.WindowDC(self.first_name)
|
||||
dc.SetFont(self.first_name.GetFont())
|
||||
self.first_name.SetSize(dc.GetTextExtent("a"*40))
|
||||
first_nameB = wx.BoxSizer(wx.HORIZONTAL)
|
||||
first_nameB.Add(first_nameLabel, 0, wx.ALL, 5)
|
||||
first_nameB.Add(self.first_name, 0, wx.ALL, 5)
|
||||
sizer.Add(first_nameB, 0, wx.ALL, 5)
|
||||
|
||||
last_nameLabel = wx.StaticText(panel, -1, _(u"Last Name"), size=wx.DefaultSize)
|
||||
self.last_name = wx.TextCtrl(panel, -1)
|
||||
dc = wx.WindowDC(self.last_name)
|
||||
dc.SetFont(self.last_name.GetFont())
|
||||
self.last_name.SetSize(dc.GetTextExtent("a"*40))
|
||||
last_nameB = wx.BoxSizer(wx.HORIZONTAL)
|
||||
last_nameB.Add(last_nameLabel, 0, wx.ALL, 5)
|
||||
last_nameB.Add(self.last_name, 0, wx.ALL, 5)
|
||||
sizer.Add(last_nameB, 0, wx.ALL, 5)
|
||||
last_nameLabel = wx.StaticText(panel, -1, _(u"Last Name"), size=wx.DefaultSize)
|
||||
self.last_name = wx.TextCtrl(panel, -1)
|
||||
dc = wx.WindowDC(self.last_name)
|
||||
dc.SetFont(self.last_name.GetFont())
|
||||
self.last_name.SetSize(dc.GetTextExtent("a"*40))
|
||||
last_nameB = wx.BoxSizer(wx.HORIZONTAL)
|
||||
last_nameB.Add(last_nameLabel, 0, wx.ALL, 5)
|
||||
last_nameB.Add(self.last_name, 0, wx.ALL, 5)
|
||||
sizer.Add(last_nameB, 0, wx.ALL, 5)
|
||||
|
||||
emailLabel = wx.StaticText(panel, -1, _(u"Email address (Will not be public)"), size=wx.DefaultSize)
|
||||
self.email = wx.TextCtrl(panel, -1)
|
||||
dc = wx.WindowDC(self.email)
|
||||
dc.SetFont(self.email.GetFont())
|
||||
self.email.SetSize(dc.GetTextExtent("a"*30))
|
||||
emailB = wx.BoxSizer(wx.HORIZONTAL)
|
||||
emailB.Add(emailLabel, 0, wx.ALL, 5)
|
||||
emailB.Add(self.email, 0, wx.ALL, 5)
|
||||
sizer.Add(emailB, 0, wx.ALL, 5)
|
||||
emailLabel = wx.StaticText(panel, -1, _(u"Email address (Will not be public)"), size=wx.DefaultSize)
|
||||
self.email = wx.TextCtrl(panel, -1)
|
||||
dc = wx.WindowDC(self.email)
|
||||
dc.SetFont(self.email.GetFont())
|
||||
self.email.SetSize(dc.GetTextExtent("a"*30))
|
||||
emailB = wx.BoxSizer(wx.HORIZONTAL)
|
||||
emailB.Add(emailLabel, 0, wx.ALL, 5)
|
||||
emailB.Add(self.email, 0, wx.ALL, 5)
|
||||
sizer.Add(emailB, 0, wx.ALL, 5)
|
||||
|
||||
descriptionLabel = wx.StaticText(panel, -1, _(u"Here, you can describe the bug in detail"), size=wx.DefaultSize)
|
||||
self.description = wx.TextCtrl(panel, -1, style=wx.TE_MULTILINE)
|
||||
dc = wx.WindowDC(self.description)
|
||||
dc.SetFont(self.description.GetFont())
|
||||
(x, y) = dc.GetMultiLineTextExtent("0"*2000)
|
||||
self.description.SetSize((x, y))
|
||||
descBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
descBox.Add(descriptionLabel, 0, wx.ALL, 5)
|
||||
descBox.Add(self.description, 0, wx.ALL, 5)
|
||||
sizer.Add(descBox, 0, wx.ALL, 5)
|
||||
self.agree = wx.CheckBox(panel, -1, _(u"I know that the {0} bug system will get my email address to contact me and fix the bug quickly").format(application.name,))
|
||||
self.agree.SetValue(False)
|
||||
sizer.Add(self.agree, 0, wx.ALL, 5)
|
||||
self.ok = wx.Button(panel, wx.ID_OK, _(u"Send report"))
|
||||
self.ok.SetDefault()
|
||||
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"Cancel"))
|
||||
btnBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
btnBox.Add(self.ok, 0, wx.ALL, 5)
|
||||
btnBox.Add(cancel, 0, wx.ALL, 5)
|
||||
sizer.Add(btnBox, 0, wx.ALL, 5)
|
||||
panel.SetSizer(sizer)
|
||||
self.SetClientSize(sizer.CalcMin())
|
||||
descriptionLabel = wx.StaticText(panel, -1, _(u"Here, you can describe the bug in detail"), size=wx.DefaultSize)
|
||||
self.description = wx.TextCtrl(panel, -1, style=wx.TE_MULTILINE)
|
||||
dc = wx.WindowDC(self.description)
|
||||
dc.SetFont(self.description.GetFont())
|
||||
(x, y) = dc.GetMultiLineTextExtent("0"*2000)
|
||||
self.description.SetSize((x, y))
|
||||
descBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
descBox.Add(descriptionLabel, 0, wx.ALL, 5)
|
||||
descBox.Add(self.description, 0, wx.ALL, 5)
|
||||
sizer.Add(descBox, 0, wx.ALL, 5)
|
||||
self.agree = wx.CheckBox(panel, -1, _(u"I know that the {0} bug system will get my email address to contact me and fix the bug quickly").format(application.name,))
|
||||
self.agree.SetValue(False)
|
||||
sizer.Add(self.agree, 0, wx.ALL, 5)
|
||||
self.ok = wx.Button(panel, wx.ID_OK, _(u"Send report"))
|
||||
self.ok.SetDefault()
|
||||
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"Cancel"))
|
||||
btnBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
btnBox.Add(self.ok, 0, wx.ALL, 5)
|
||||
btnBox.Add(cancel, 0, wx.ALL, 5)
|
||||
sizer.Add(btnBox, 0, wx.ALL, 5)
|
||||
panel.SetSizer(sizer)
|
||||
self.SetClientSize(sizer.CalcMin())
|
||||
|
||||
def no_filled(self):
|
||||
wx.MessageDialog(self, _(u"You must fill out the following fields: first name, last name, email address and issue information."), _(u"Error"), wx.OK|wx.ICON_ERROR).ShowModal()
|
||||
def no_filled(self):
|
||||
wx.MessageDialog(self, _(u"You must fill out the following fields: first name, last name, email address and issue information."), _(u"Error"), wx.OK|wx.ICON_ERROR).ShowModal()
|
||||
|
||||
def no_checkbox(self):
|
||||
wx.MessageDialog(self, _(u"You need to mark the checkbox to provide us your email address to contact you if it is necessary."), _(u"Error"), wx.ICON_ERROR).ShowModal()
|
||||
def no_checkbox(self):
|
||||
wx.MessageDialog(self, _(u"You need to mark the checkbox to provide us your email address to contact you if it is necessary."), _(u"Error"), wx.ICON_ERROR).ShowModal()
|
||||
|
||||
def success(self, id):
|
||||
wx.MessageDialog(self, _(u"Thanks for reporting this bug! In future versions, you may be able to find it in the changes list. You have received an email with more information regarding your report. You've reported the bug number %i") % (id), _(u"reported"), wx.OK).ShowModal()
|
||||
self.Destroy()
|
||||
def success(self, id):
|
||||
wx.MessageDialog(self, _(u"Thanks for reporting this bug! In future versions, you may be able to find it in the changes list. You have received an email with more information regarding your report. You've reported the bug number %i") % (id), _(u"reported"), wx.OK).ShowModal()
|
||||
self.Destroy()
|
||||
|
||||
def error(self):
|
||||
wx.MessageDialog(self, _(u"Something unexpected occurred while trying to report the bug. Please, try again later"), _(u"Error while reporting"), wx.ICON_ERROR|wx.OK).ShowModal()
|
||||
self.Destroy()
|
||||
def error(self):
|
||||
wx.MessageDialog(self, _(u"Something unexpected occurred while trying to report the bug. Please, try again later"), _(u"Error while reporting"), wx.ICON_ERROR|wx.OK).ShowModal()
|
||||
self.Destroy()
|
||||
|
||||
def show_progress(self):
|
||||
self.progress = wx.ProgressDialog(title=_(u"Sending report..."), message=_(u"Please wait while your report is being send."), maximum=100, parent=self)
|
||||
self.progress.ShowModal()
|
||||
def show_progress(self):
|
||||
self.progress = wx.ProgressDialog(title=_(u"Sending report..."), message=_(u"Please wait while your report is being send."), maximum=100, parent=self)
|
||||
self.progress.ShowModal()
|
||||
|
22
src/main.py
22
src/main.py
@ -19,10 +19,10 @@ log = logging.getLogger("main")
|
||||
log.debug("Logger initialized. Saving debug to {0}".format(storage.data_directory,))
|
||||
log.debug("Using Python version {0}".format(sys.version,))
|
||||
if sys.version[0] == "2":
|
||||
if hasattr(sys, "frozen"):
|
||||
log.debug("Applying fixes for Python 2 frozen executables.")
|
||||
import fixes
|
||||
fixes.setup()
|
||||
if hasattr(sys, "frozen"):
|
||||
log.debug("Applying fixes for Python 2 frozen executables.")
|
||||
import fixes
|
||||
fixes.setup()
|
||||
import i18n
|
||||
i18n.setup()
|
||||
config.setup()
|
||||
@ -31,12 +31,12 @@ import widgetUtils
|
||||
import paths
|
||||
|
||||
def setup():
|
||||
log.debug("Starting music-dl %s" % (application.version,))
|
||||
log.debug("Application path is %s" % (paths.app_path(),))
|
||||
from controller import mainController
|
||||
app = widgetUtils.mainLoopObject()
|
||||
log.debug("Created Application mainloop object")
|
||||
r = mainController.Controller()
|
||||
app.run()
|
||||
log.debug("Starting music-dl %s" % (application.version,))
|
||||
log.debug("Application path is %s" % (paths.app_path(),))
|
||||
from controller import mainController
|
||||
app = widgetUtils.mainLoopObject()
|
||||
log.debug("Created Application mainloop object")
|
||||
r = mainController.Controller()
|
||||
app.run()
|
||||
|
||||
setup()
|
||||
|
82
src/paths.py
82
src/paths.py
@ -13,58 +13,58 @@ directory = None
|
||||
fsencoding = sys.getfilesystemencoding()
|
||||
|
||||
if len(glob.glob("Uninstall.exe")) > 0: # installed copy
|
||||
mode= "installed"
|
||||
mode= "installed"
|
||||
|
||||
def app_path():
|
||||
return paths_.app_path()
|
||||
return paths_.app_path()
|
||||
|
||||
def config_path():
|
||||
global mode, directory
|
||||
if mode == "portable":
|
||||
if directory != None: path = os.path.join(directory, "config")
|
||||
elif directory == None: path = os.path.join(app_path(), "config")
|
||||
elif mode == "installed":
|
||||
path = os.path.join(data_path(), "config")
|
||||
if not os.path.exists(path):
|
||||
# log.debug("%s path does not exist, creating..." % (path,))
|
||||
os.mkdir(path)
|
||||
return path
|
||||
global mode, directory
|
||||
if mode == "portable":
|
||||
if directory != None: path = os.path.join(directory, "config")
|
||||
elif directory == None: path = os.path.join(app_path(), "config")
|
||||
elif mode == "installed":
|
||||
path = os.path.join(data_path(), "config")
|
||||
if not os.path.exists(path):
|
||||
# log.debug("%s path does not exist, creating..." % (path,))
|
||||
os.mkdir(path)
|
||||
return path
|
||||
|
||||
def logs_path():
|
||||
global mode, directory
|
||||
if mode == "portable":
|
||||
if directory != None: path = os.path.join(directory, "logs")
|
||||
elif directory == None: path = os.path.join(app_path(), "logs")
|
||||
elif mode == "installed":
|
||||
path = os.path.join(data_path(), "logs")
|
||||
if not os.path.exists(path):
|
||||
# log.debug("%s path does not exist, creating..." % (path,))
|
||||
os.mkdir(path)
|
||||
return path
|
||||
global mode, directory
|
||||
if mode == "portable":
|
||||
if directory != None: path = os.path.join(directory, "logs")
|
||||
elif directory == None: path = os.path.join(app_path(), "logs")
|
||||
elif mode == "installed":
|
||||
path = os.path.join(data_path(), "logs")
|
||||
if not os.path.exists(path):
|
||||
# log.debug("%s path does not exist, creating..." % (path,))
|
||||
os.mkdir(path)
|
||||
return path
|
||||
|
||||
def data_path(app_name='socializer'):
|
||||
if platform.system() == "Windows":
|
||||
data_path = os.path.join(os.getenv("AppData"), app_name)
|
||||
else:
|
||||
data_path = os.path.join(os.environ['HOME'], ".%s" % app_name)
|
||||
if not os.path.exists(data_path):
|
||||
os.mkdir(data_path)
|
||||
return data_path
|
||||
if platform.system() == "Windows":
|
||||
data_path = os.path.join(os.getenv("AppData"), app_name)
|
||||
else:
|
||||
data_path = os.path.join(os.environ['HOME'], ".%s" % app_name)
|
||||
if not os.path.exists(data_path):
|
||||
os.mkdir(data_path)
|
||||
return data_path
|
||||
|
||||
def locale_path():
|
||||
return os.path.join(app_path(), "locales")
|
||||
return os.path.join(app_path(), "locales")
|
||||
|
||||
def sound_path():
|
||||
return os.path.join(app_path(), "sounds")
|
||||
return os.path.join(app_path(), "sounds")
|
||||
|
||||
def com_path():
|
||||
global mode, directory
|
||||
if mode == "portable":
|
||||
if directory != None: path = os.path.join(directory, "com_cache")
|
||||
elif directory == None: path = os.path.join(app_path(), "com_cache")
|
||||
elif mode == "installed":
|
||||
path = os.path.join(data_path(), "com_cache")
|
||||
if not os.path.exists(path):
|
||||
# log.debug("%s path does not exist, creating..." % (path,))
|
||||
os.mkdir(path)
|
||||
return path
|
||||
global mode, directory
|
||||
if mode == "portable":
|
||||
if directory != None: path = os.path.join(directory, "com_cache")
|
||||
elif directory == None: path = os.path.join(app_path(), "com_cache")
|
||||
elif mode == "installed":
|
||||
path = os.path.join(data_path(), "com_cache")
|
||||
if not os.path.exists(path):
|
||||
# log.debug("%s path does not exist, creating..." % (path,))
|
||||
os.mkdir(path)
|
||||
return path
|
||||
|
@ -10,168 +10,168 @@ from .import base
|
||||
log = logging.getLogger("extractors.funkwhale")
|
||||
|
||||
class interface(base.baseInterface):
|
||||
name = "Funkwhale"
|
||||
enabled = config.app["services"]["funkwhale"].get("enabled")
|
||||
# This should not be enabled if credentials are not in config.
|
||||
if config.app["services"]["funkwhale"]["username"] == "" or config.app["services"]["funkwhale"]["password"] == "":
|
||||
enabled = False
|
||||
name = "Funkwhale"
|
||||
enabled = config.app["services"]["funkwhale"].get("enabled")
|
||||
# This should not be enabled if credentials are not in config.
|
||||
if config.app["services"]["funkwhale"]["username"] == "" or config.app["services"]["funkwhale"]["password"] == "":
|
||||
enabled = False
|
||||
|
||||
def __init__(self):
|
||||
super(interface, self).__init__()
|
||||
self.setup()
|
||||
def __init__(self):
|
||||
super(interface, self).__init__()
|
||||
self.setup()
|
||||
|
||||
def setup(self):
|
||||
endpoint = config.app["services"]["funkwhale"]["endpoint"]
|
||||
username = config.app["services"]["funkwhale"]["username"]
|
||||
password = config.app["services"]["funkwhale"]["password"]
|
||||
self.session = session.Session(instance_endpoint=endpoint, username=username, password=password)
|
||||
self.session.login()
|
||||
self.api = self.session.get_api()
|
||||
def setup(self):
|
||||
endpoint = config.app["services"]["funkwhale"]["endpoint"]
|
||||
username = config.app["services"]["funkwhale"]["username"]
|
||||
password = config.app["services"]["funkwhale"]["password"]
|
||||
self.session = session.Session(instance_endpoint=endpoint, username=username, password=password)
|
||||
self.session.login()
|
||||
self.api = self.session.get_api()
|
||||
|
||||
def get_file_format(self):
|
||||
self.file_extension = "flac"
|
||||
return self.file_extension
|
||||
def get_file_format(self):
|
||||
self.file_extension = "flac"
|
||||
return self.file_extension
|
||||
|
||||
def transcoder_enabled(self):
|
||||
return False
|
||||
def transcoder_enabled(self):
|
||||
return False
|
||||
|
||||
def search(self, text, page=1):
|
||||
if text == "" or text == None:
|
||||
raise ValueError("Text must be passed and should not be blank.")
|
||||
log.debug("Retrieving data from Tidal...")
|
||||
fieldtypes = ["artist", "album", "playlist"]
|
||||
field = "track"
|
||||
for i in fieldtypes:
|
||||
if text.startswith(i+"://"):
|
||||
field = i
|
||||
text = text.replace(i+"://", "")
|
||||
log.debug("Searching for %s..." % (field))
|
||||
search_response = self.session.search(value=text, field=field)
|
||||
self.results = []
|
||||
if field == "track":
|
||||
data = search_response.tracks
|
||||
elif field == "artist":
|
||||
data = []
|
||||
artist = search_response.artists[0].id
|
||||
if config.app["services"]["tidal"]["include_albums"]:
|
||||
albums = self.session.get_artist_albums(artist)
|
||||
for album in albums:
|
||||
tracks = self.session.get_album_tracks(album.id)
|
||||
for track in tracks:
|
||||
data.append(track)
|
||||
if config.app["services"]["tidal"]["include_compilations"]:
|
||||
compilations = self.session.get_artist_albums_other(artist)
|
||||
for album in compilations:
|
||||
tracks = self.session.get_album_tracks(album.id)
|
||||
for track in tracks:
|
||||
data.append(track)
|
||||
if config.app["services"]["tidal"]["include_singles"]:
|
||||
singles = self.session.get_artist_albums_ep_singles(artist)
|
||||
for album in singles:
|
||||
tracks = self.session.get_album_tracks(album.id)
|
||||
for track in tracks:
|
||||
track.single = True
|
||||
data.append(track)
|
||||
for search_result in data:
|
||||
s = base.song(self)
|
||||
if not hasattr(search_result, "single"):
|
||||
s.title = "{0}. {1}".format(self.format_number(search_result.track_num), search_result.name)
|
||||
else:
|
||||
s.title = search_result.name
|
||||
s.artist = search_result.artist.name
|
||||
s.duration = seconds_to_string(search_result.duration)
|
||||
s.url = search_result.id
|
||||
s.info = search_result
|
||||
self.results.append(s)
|
||||
log.debug("{0} results found.".format(len(self.results)))
|
||||
def search(self, text, page=1):
|
||||
if text == "" or text == None:
|
||||
raise ValueError("Text must be passed and should not be blank.")
|
||||
log.debug("Retrieving data from Tidal...")
|
||||
fieldtypes = ["artist", "album", "playlist"]
|
||||
field = "track"
|
||||
for i in fieldtypes:
|
||||
if text.startswith(i+"://"):
|
||||
field = i
|
||||
text = text.replace(i+"://", "")
|
||||
log.debug("Searching for %s..." % (field))
|
||||
search_response = self.session.search(value=text, field=field)
|
||||
self.results = []
|
||||
if field == "track":
|
||||
data = search_response.tracks
|
||||
elif field == "artist":
|
||||
data = []
|
||||
artist = search_response.artists[0].id
|
||||
if config.app["services"]["tidal"]["include_albums"]:
|
||||
albums = self.session.get_artist_albums(artist)
|
||||
for album in albums:
|
||||
tracks = self.session.get_album_tracks(album.id)
|
||||
for track in tracks:
|
||||
data.append(track)
|
||||
if config.app["services"]["tidal"]["include_compilations"]:
|
||||
compilations = self.session.get_artist_albums_other(artist)
|
||||
for album in compilations:
|
||||
tracks = self.session.get_album_tracks(album.id)
|
||||
for track in tracks:
|
||||
data.append(track)
|
||||
if config.app["services"]["tidal"]["include_singles"]:
|
||||
singles = self.session.get_artist_albums_ep_singles(artist)
|
||||
for album in singles:
|
||||
tracks = self.session.get_album_tracks(album.id)
|
||||
for track in tracks:
|
||||
track.single = True
|
||||
data.append(track)
|
||||
for search_result in data:
|
||||
s = base.song(self)
|
||||
if not hasattr(search_result, "single"):
|
||||
s.title = "{0}. {1}".format(self.format_number(search_result.track_num), search_result.name)
|
||||
else:
|
||||
s.title = search_result.name
|
||||
s.artist = search_result.artist.name
|
||||
s.duration = seconds_to_string(search_result.duration)
|
||||
s.url = search_result.id
|
||||
s.info = search_result
|
||||
self.results.append(s)
|
||||
log.debug("{0} results found.".format(len(self.results)))
|
||||
|
||||
def format_number(self, number):
|
||||
if number < 10:
|
||||
return "0%d" % (number)
|
||||
else:
|
||||
return number
|
||||
def format_number(self, number):
|
||||
if number < 10:
|
||||
return "0%d" % (number)
|
||||
else:
|
||||
return number
|
||||
|
||||
def get_download_url(self, url):
|
||||
url = self.session.get_media_url(url)
|
||||
if url.startswith("https://") or url.startswith("http://") == False:
|
||||
url = "rtmp://"+url
|
||||
return url
|
||||
def get_download_url(self, url):
|
||||
url = self.session.get_media_url(url)
|
||||
if url.startswith("https://") or url.startswith("http://") == False:
|
||||
url = "rtmp://"+url
|
||||
return url
|
||||
|
||||
def format_track(self, item):
|
||||
return "{title}. {artist}. {duration}".format(title=item.title, duration=item.duration, artist=item.artist)
|
||||
def format_track(self, item):
|
||||
return "{title}. {artist}. {duration}".format(title=item.title, duration=item.duration, artist=item.artist)
|
||||
|
||||
class settings(base.baseSettings):
|
||||
name = _("Tidal")
|
||||
config_section = "tidal"
|
||||
name = _("Tidal")
|
||||
config_section = "tidal"
|
||||
|
||||
def get_quality_list(self):
|
||||
results = dict(low=_("Low"), high=_("High"), lossless=_("Lossless"))
|
||||
return results
|
||||
def get_quality_list(self):
|
||||
results = dict(low=_("Low"), high=_("High"), lossless=_("Lossless"))
|
||||
return results
|
||||
|
||||
def get_quality_value(self, *args, **kwargs):
|
||||
q = self.get_quality_list()
|
||||
for i in q.keys():
|
||||
if q.get(i) == self.quality.GetStringSelection():
|
||||
return i
|
||||
def get_quality_value(self, *args, **kwargs):
|
||||
q = self.get_quality_list()
|
||||
for i in q.keys():
|
||||
if q.get(i) == self.quality.GetStringSelection():
|
||||
return i
|
||||
|
||||
def set_quality_value(self, value, *args, **kwargs):
|
||||
q = self.get_quality_list()
|
||||
for i in q.keys():
|
||||
if i == value:
|
||||
self.quality.SetStringSelection(q.get(i))
|
||||
break
|
||||
def set_quality_value(self, value, *args, **kwargs):
|
||||
q = self.get_quality_list()
|
||||
for i in q.keys():
|
||||
if i == value:
|
||||
self.quality.SetStringSelection(q.get(i))
|
||||
break
|
||||
|
||||
def __init__(self, parent):
|
||||
super(settings, self).__init__(parent=parent)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.enabled = wx.CheckBox(self, wx.NewId(), _("Enable this service"))
|
||||
self.enabled.Bind(wx.EVT_CHECKBOX, self.on_enabled)
|
||||
self.map.append(("enabled", self.enabled))
|
||||
sizer.Add(self.enabled, 0, wx.ALL, 5)
|
||||
username = wx.StaticText(self, wx.NewId(), _("Tidal username or email address"))
|
||||
self.username = wx.TextCtrl(self, wx.NewId())
|
||||
usernamebox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
usernamebox.Add(username, 0, wx.ALL, 5)
|
||||
usernamebox.Add(self.username, 0, wx.ALL, 5)
|
||||
sizer.Add(usernamebox, 0, wx.ALL, 5)
|
||||
self.map.append(("username", self.username))
|
||||
password = wx.StaticText(self, wx.NewId(), _("Password"))
|
||||
self.password = wx.TextCtrl(self, wx.NewId(), style=wx.TE_PASSWORD)
|
||||
passwordbox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
passwordbox.Add(password, 0, wx.ALL, 5)
|
||||
passwordbox.Add(self.password, 0, wx.ALL, 5)
|
||||
sizer.Add(passwordbox, 0, wx.ALL, 5)
|
||||
self.map.append(("password", self.password))
|
||||
self.get_account = wx.Button(self, wx.NewId(), _("You can subscribe for a tidal account here"))
|
||||
self.get_account.Bind(wx.EVT_BUTTON, self.on_get_account)
|
||||
sizer.Add(self.get_account, 0, wx.ALL, 5)
|
||||
quality = wx.StaticText(self, wx.NewId(), _("Audio quality"))
|
||||
self.quality = wx.ComboBox(self, wx.NewId(), choices=[i for i in self.get_quality_list().values()], value=_("High"), style=wx.CB_READONLY)
|
||||
qualitybox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
qualitybox.Add(quality, 0, wx.ALL, 5)
|
||||
qualitybox.Add(self.quality, 0, wx.ALL, 5)
|
||||
sizer.Add(qualitybox, 0, wx.ALL, 5)
|
||||
# Monkeypatch for getting the right quality value here.
|
||||
self.quality.GetValue = self.get_quality_value
|
||||
self.quality.SetValue = self.set_quality_value
|
||||
self.map.append(("quality", self.quality))
|
||||
include = wx.StaticBoxSizer(parent=self, orient=wx.HORIZONTAL, label=_("Search by artist"))
|
||||
self.include_albums = wx.CheckBox(include.GetStaticBox(), wx.NewId(), _("Include albums"))
|
||||
self.include_compilations = wx.CheckBox(include.GetStaticBox(), wx.NewId(), _("Include compilations"))
|
||||
self.include_singles = wx.CheckBox(include.GetStaticBox(), wx.NewId(), _("Include singles"))
|
||||
sizer.Add(include, 0, wx.ALL, 5)
|
||||
self.map.append(("include_albums", self.include_albums))
|
||||
self.map.append(("include_compilations", self.include_compilations))
|
||||
self.map.append(("include_singles", self.include_singles))
|
||||
self.SetSizer(sizer)
|
||||
def __init__(self, parent):
|
||||
super(settings, self).__init__(parent=parent)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.enabled = wx.CheckBox(self, wx.NewId(), _("Enable this service"))
|
||||
self.enabled.Bind(wx.EVT_CHECKBOX, self.on_enabled)
|
||||
self.map.append(("enabled", self.enabled))
|
||||
sizer.Add(self.enabled, 0, wx.ALL, 5)
|
||||
username = wx.StaticText(self, wx.NewId(), _("Tidal username or email address"))
|
||||
self.username = wx.TextCtrl(self, wx.NewId())
|
||||
usernamebox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
usernamebox.Add(username, 0, wx.ALL, 5)
|
||||
usernamebox.Add(self.username, 0, wx.ALL, 5)
|
||||
sizer.Add(usernamebox, 0, wx.ALL, 5)
|
||||
self.map.append(("username", self.username))
|
||||
password = wx.StaticText(self, wx.NewId(), _("Password"))
|
||||
self.password = wx.TextCtrl(self, wx.NewId(), style=wx.TE_PASSWORD)
|
||||
passwordbox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
passwordbox.Add(password, 0, wx.ALL, 5)
|
||||
passwordbox.Add(self.password, 0, wx.ALL, 5)
|
||||
sizer.Add(passwordbox, 0, wx.ALL, 5)
|
||||
self.map.append(("password", self.password))
|
||||
self.get_account = wx.Button(self, wx.NewId(), _("You can subscribe for a tidal account here"))
|
||||
self.get_account.Bind(wx.EVT_BUTTON, self.on_get_account)
|
||||
sizer.Add(self.get_account, 0, wx.ALL, 5)
|
||||
quality = wx.StaticText(self, wx.NewId(), _("Audio quality"))
|
||||
self.quality = wx.ComboBox(self, wx.NewId(), choices=[i for i in self.get_quality_list().values()], value=_("High"), style=wx.CB_READONLY)
|
||||
qualitybox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
qualitybox.Add(quality, 0, wx.ALL, 5)
|
||||
qualitybox.Add(self.quality, 0, wx.ALL, 5)
|
||||
sizer.Add(qualitybox, 0, wx.ALL, 5)
|
||||
# Monkeypatch for getting the right quality value here.
|
||||
self.quality.GetValue = self.get_quality_value
|
||||
self.quality.SetValue = self.set_quality_value
|
||||
self.map.append(("quality", self.quality))
|
||||
include = wx.StaticBoxSizer(parent=self, orient=wx.HORIZONTAL, label=_("Search by artist"))
|
||||
self.include_albums = wx.CheckBox(include.GetStaticBox(), wx.NewId(), _("Include albums"))
|
||||
self.include_compilations = wx.CheckBox(include.GetStaticBox(), wx.NewId(), _("Include compilations"))
|
||||
self.include_singles = wx.CheckBox(include.GetStaticBox(), wx.NewId(), _("Include singles"))
|
||||
sizer.Add(include, 0, wx.ALL, 5)
|
||||
self.map.append(("include_albums", self.include_albums))
|
||||
self.map.append(("include_compilations", self.include_compilations))
|
||||
self.map.append(("include_singles", self.include_singles))
|
||||
self.SetSizer(sizer)
|
||||
|
||||
def on_enabled(self, *args, **kwargs):
|
||||
for i in self.map:
|
||||
if i[1] != self.enabled:
|
||||
if self.enabled.GetValue() == True:
|
||||
i[1].Enable(True)
|
||||
else:
|
||||
i[1].Enable(False)
|
||||
def on_enabled(self, *args, **kwargs):
|
||||
for i in self.map:
|
||||
if i[1] != self.enabled:
|
||||
if self.enabled.GetValue() == True:
|
||||
i[1].Enable(True)
|
||||
else:
|
||||
i[1].Enable(False)
|
||||
|
||||
def on_get_account(self, *args, **kwargs):
|
||||
webbrowser.open_new_tab("https://tidal.com")
|
||||
def on_get_account(self, *args, **kwargs):
|
||||
webbrowser.open_new_tab("https://tidal.com")
|
||||
|
@ -7,75 +7,75 @@ import config
|
||||
log = logging.getLogger("extractors.config")
|
||||
|
||||
class baseInterface(object):
|
||||
name = "base"
|
||||
enabled = False
|
||||
needs_transcode = False
|
||||
results = []
|
||||
name = "base"
|
||||
enabled = False
|
||||
needs_transcode = False
|
||||
results = []
|
||||
|
||||
def __init__(self):
|
||||
super(baseInterface, self).__init__()
|
||||
log.debug("started extraction service for {0}".format(self.name,))
|
||||
def __init__(self):
|
||||
super(baseInterface, self).__init__()
|
||||
log.debug("started extraction service for {0}".format(self.name,))
|
||||
|
||||
def search(self, text, *args, **kwargs):
|
||||
raise NotImplementedError()
|
||||
def search(self, text, *args, **kwargs):
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_download_url(self, url):
|
||||
raise NotImplementedError()
|
||||
def get_download_url(self, url):
|
||||
raise NotImplementedError()
|
||||
|
||||
def format_track(self, item):
|
||||
raise NotImplementedError()
|
||||
def format_track(self, item):
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_file_format(self):
|
||||
return "mp3"
|
||||
def get_file_format(self):
|
||||
return "mp3"
|
||||
|
||||
def transcoder_enabled(self):
|
||||
return False
|
||||
def transcoder_enabled(self):
|
||||
return False
|
||||
|
||||
def get_metadata(self, item):
|
||||
data = dict()
|
||||
keys = ["title", "album", "artist", "tracknumber"]
|
||||
for k in keys:
|
||||
if hasattr(item, k):
|
||||
data[k] = getattr(item, k)
|
||||
return data
|
||||
def get_metadata(self, item):
|
||||
data = dict()
|
||||
keys = ["title", "album", "artist", "tracknumber"]
|
||||
for k in keys:
|
||||
if hasattr(item, k):
|
||||
data[k] = getattr(item, k)
|
||||
return data
|
||||
|
||||
class song(object):
|
||||
""" Represents a song in all services. Data will be filled by the service itself"""
|
||||
""" Represents a song in all services. Data will be filled by the service itself"""
|
||||
|
||||
def __init__(self, extractor):
|
||||
self.extractor = extractor
|
||||
self.bitrate = 0
|
||||
self.title = ""
|
||||
self.artist = ""
|
||||
self.duration = ""
|
||||
self.size = 0
|
||||
self.url = ""
|
||||
self.download_url = ""
|
||||
self.info = None
|
||||
def __init__(self, extractor):
|
||||
self.extractor = extractor
|
||||
self.bitrate = 0
|
||||
self.title = ""
|
||||
self.artist = ""
|
||||
self.duration = ""
|
||||
self.size = 0
|
||||
self.url = ""
|
||||
self.download_url = ""
|
||||
self.info = None
|
||||
|
||||
def format_track(self):
|
||||
return self.extractor.format_track(self)
|
||||
def format_track(self):
|
||||
return self.extractor.format_track(self)
|
||||
|
||||
def get_download_url(self):
|
||||
self.download_url = self.extractor.get_download_url(self.url)
|
||||
def get_download_url(self):
|
||||
self.download_url = self.extractor.get_download_url(self.url)
|
||||
|
||||
def get_metadata(self):
|
||||
return self.extractor.get_metadata(self)
|
||||
def get_metadata(self):
|
||||
return self.extractor.get_metadata(self)
|
||||
|
||||
class baseSettings(wx.Panel):
|
||||
config_section = "base"
|
||||
config_section = "base"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(baseSettings, self).__init__(*args, **kwargs)
|
||||
self.map = []
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(baseSettings, self).__init__(*args, **kwargs)
|
||||
self.map = []
|
||||
|
||||
def save(self):
|
||||
for i in self.map:
|
||||
config.app["services"][self.config_section][i[0]] = i[1].GetValue()
|
||||
def save(self):
|
||||
for i in self.map:
|
||||
config.app["services"][self.config_section][i[0]] = i[1].GetValue()
|
||||
|
||||
def load(self):
|
||||
for i in self.map:
|
||||
if i[0] in config.app["services"][self.config_section]:
|
||||
i[1].SetValue(config.app["services"][self.config_section][i[0]])
|
||||
else:
|
||||
log.error("No key available: {key} on extractor {extractor}".format(key=i[0], extractor=self.config_section))
|
||||
def load(self):
|
||||
for i in self.map:
|
||||
if i[0] in config.app["services"][self.config_section]:
|
||||
i[1].SetValue(config.app["services"][self.config_section][i[0]])
|
||||
else:
|
||||
log.error("No key available: {key} on extractor {extractor}".format(key=i[0], extractor=self.config_section))
|
||||
|
@ -15,221 +15,221 @@ from .import base
|
||||
log = logging.getLogger("services.tidal")
|
||||
|
||||
class interface(base.baseInterface):
|
||||
name = "tidal"
|
||||
if config.app != None: # Workaround for cx_freeze 6.2 in python 3.7.
|
||||
enabled = config.app["services"]["tidal"].get("enabled")
|
||||
# This should not be enabled if credentials are not set in config.
|
||||
if config.app["services"]["tidal"]["username"] == "" or config.app["services"]["tidal"]["password"] == "":
|
||||
enabled = False
|
||||
else:
|
||||
enabled = False
|
||||
name = "tidal"
|
||||
if config.app != None: # Workaround for cx_freeze 6.2 in python 3.7.
|
||||
enabled = config.app["services"]["tidal"].get("enabled")
|
||||
# This should not be enabled if credentials are not set in config.
|
||||
if config.app["services"]["tidal"]["username"] == "" or config.app["services"]["tidal"]["password"] == "":
|
||||
enabled = False
|
||||
else:
|
||||
enabled = False
|
||||
|
||||
def __init__(self):
|
||||
super(interface, self).__init__()
|
||||
self.setup()
|
||||
def __init__(self):
|
||||
super(interface, self).__init__()
|
||||
self.setup()
|
||||
|
||||
def setup(self):
|
||||
# Assign quality or switch to high if not specified/not found.
|
||||
if hasattr(tidalapi.Quality, config.app["services"]["tidal"]["quality"]):
|
||||
quality = getattr(tidalapi.Quality, config.app["services"]["tidal"]["quality"])
|
||||
else:
|
||||
quality = tidalapi.Quality.high
|
||||
# We need to instantiate a config object to pass quality settings.
|
||||
_config = tidalapi.Config(quality=quality)
|
||||
username = config.app["services"]["tidal"]["username"]
|
||||
password = config.app["services"]["tidal"]["password"]
|
||||
log.debug("Using quality: %s" % (quality,))
|
||||
self.session = tidalapi.Session(config=_config)
|
||||
self.session.login(username=username, password=password)
|
||||
def setup(self):
|
||||
# Assign quality or switch to high if not specified/not found.
|
||||
if hasattr(tidalapi.Quality, config.app["services"]["tidal"]["quality"]):
|
||||
quality = getattr(tidalapi.Quality, config.app["services"]["tidal"]["quality"])
|
||||
else:
|
||||
quality = tidalapi.Quality.high
|
||||
# We need to instantiate a config object to pass quality settings.
|
||||
_config = tidalapi.Config(quality=quality)
|
||||
username = config.app["services"]["tidal"]["username"]
|
||||
password = config.app["services"]["tidal"]["password"]
|
||||
log.debug("Using quality: %s" % (quality,))
|
||||
self.session = tidalapi.Session(config=_config)
|
||||
self.session.login(username=username, password=password)
|
||||
|
||||
def get_file_format(self):
|
||||
""" Returns the file format (mp3 or flac) depending in quality set. """
|
||||
if config.app["services"]["tidal"]["quality"] == "lossless":
|
||||
self.file_extension = "flac"
|
||||
elif config.app["services"]["tidal"]["avoid_transcoding"] == True:
|
||||
self.file_extension = "m4a"
|
||||
else:
|
||||
self.file_extension = "mp3"
|
||||
return self.file_extension
|
||||
def get_file_format(self):
|
||||
""" Returns the file format (mp3 or flac) depending in quality set. """
|
||||
if config.app["services"]["tidal"]["quality"] == "lossless":
|
||||
self.file_extension = "flac"
|
||||
elif config.app["services"]["tidal"]["avoid_transcoding"] == True:
|
||||
self.file_extension = "m4a"
|
||||
else:
|
||||
self.file_extension = "mp3"
|
||||
return self.file_extension
|
||||
|
||||
def transcoder_enabled(self):
|
||||
# If quality is set to high, tidal returns audio in AAC format at 256 KBPS. So we convert it with vlc to mp3 at 320KBPS.
|
||||
# toDo: Shall this be a setting and allow MusicDL to spit out the m4a file directly?
|
||||
if config.app["services"]["tidal"]["quality"] == "lossless":
|
||||
return False
|
||||
elif config.app["services"]["tidal"]["avoid_transcoding"]:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
def transcoder_enabled(self):
|
||||
# If quality is set to high, tidal returns audio in AAC format at 256 KBPS. So we convert it with vlc to mp3 at 320KBPS.
|
||||
# toDo: Shall this be a setting and allow MusicDL to spit out the m4a file directly?
|
||||
if config.app["services"]["tidal"]["quality"] == "lossless":
|
||||
return False
|
||||
elif config.app["services"]["tidal"]["avoid_transcoding"]:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def search(self, text, page=1):
|
||||
if text == "" or text == None:
|
||||
raise ValueError("Text must be passed and should not be blank.")
|
||||
log.debug("Retrieving data from Tidal...")
|
||||
# Check for top:// protocol.
|
||||
if text.startswith("top://"):
|
||||
text = text.replace("top://", "")
|
||||
return self.search_for_top(text)
|
||||
fieldtypes = ["artist", "album", "playlist"]
|
||||
field = "track"
|
||||
for i in fieldtypes:
|
||||
if text.startswith(i+"://"):
|
||||
field = i
|
||||
text = text.replace(i+"://", "")
|
||||
log.debug("Searching for %s..." % (field))
|
||||
search_response = self.session.search(value=text, field=field)
|
||||
self.results = []
|
||||
if field == "track":
|
||||
data = search_response.tracks
|
||||
elif field == "artist":
|
||||
data = []
|
||||
artist = search_response.artists[0].id
|
||||
if config.app["services"]["tidal"]["include_albums"]:
|
||||
albums = self.session.get_artist_albums(artist)
|
||||
for album in albums:
|
||||
tracks = self.session.get_album_tracks(album.id)
|
||||
for track in tracks:
|
||||
track.album = album
|
||||
data.append(track)
|
||||
if config.app["services"]["tidal"]["include_compilations"]:
|
||||
compilations = self.session.get_artist_albums_other(artist)
|
||||
for album in compilations:
|
||||
tracks = self.session.get_album_tracks(album.id)
|
||||
for track in tracks:
|
||||
data.append(track)
|
||||
if config.app["services"]["tidal"]["include_singles"]:
|
||||
singles = self.session.get_artist_albums_ep_singles(artist)
|
||||
for album in singles:
|
||||
tracks = self.session.get_album_tracks(album.id)
|
||||
for track in tracks:
|
||||
# track.single = True
|
||||
data.append(track)
|
||||
for search_result in data:
|
||||
s = base.song(self)
|
||||
s.title = search_result.name
|
||||
s.artist = search_result.artist.name
|
||||
s.duration = seconds_to_string(search_result.duration)
|
||||
s.url = search_result.id
|
||||
s.tracknumber = str(search_result.track_num)
|
||||
s.album = search_result.album.name
|
||||
if search_result.album.num_tracks == None:
|
||||
s.single = True
|
||||
s.info = search_result
|
||||
self.results.append(s)
|
||||
log.debug("{0} results found.".format(len(self.results)))
|
||||
def search(self, text, page=1):
|
||||
if text == "" or text == None:
|
||||
raise ValueError("Text must be passed and should not be blank.")
|
||||
log.debug("Retrieving data from Tidal...")
|
||||
# Check for top:// protocol.
|
||||
if text.startswith("top://"):
|
||||
text = text.replace("top://", "")
|
||||
return self.search_for_top(text)
|
||||
fieldtypes = ["artist", "album", "playlist"]
|
||||
field = "track"
|
||||
for i in fieldtypes:
|
||||
if text.startswith(i+"://"):
|
||||
field = i
|
||||
text = text.replace(i+"://", "")
|
||||
log.debug("Searching for %s..." % (field))
|
||||
search_response = self.session.search(value=text, field=field)
|
||||
self.results = []
|
||||
if field == "track":
|
||||
data = search_response.tracks
|
||||
elif field == "artist":
|
||||
data = []
|
||||
artist = search_response.artists[0].id
|
||||
if config.app["services"]["tidal"]["include_albums"]:
|
||||
albums = self.session.get_artist_albums(artist)
|
||||
for album in albums:
|
||||
tracks = self.session.get_album_tracks(album.id)
|
||||
for track in tracks:
|
||||
track.album = album
|
||||
data.append(track)
|
||||
if config.app["services"]["tidal"]["include_compilations"]:
|
||||
compilations = self.session.get_artist_albums_other(artist)
|
||||
for album in compilations:
|
||||
tracks = self.session.get_album_tracks(album.id)
|
||||
for track in tracks:
|
||||
data.append(track)
|
||||
if config.app["services"]["tidal"]["include_singles"]:
|
||||
singles = self.session.get_artist_albums_ep_singles(artist)
|
||||
for album in singles:
|
||||
tracks = self.session.get_album_tracks(album.id)
|
||||
for track in tracks:
|
||||
# track.single = True
|
||||
data.append(track)
|
||||
for search_result in data:
|
||||
s = base.song(self)
|
||||
s.title = search_result.name
|
||||
s.artist = search_result.artist.name
|
||||
s.duration = seconds_to_string(search_result.duration)
|
||||
s.url = search_result.id
|
||||
s.tracknumber = str(search_result.track_num)
|
||||
s.album = search_result.album.name
|
||||
if search_result.album.num_tracks == None:
|
||||
s.single = True
|
||||
s.info = search_result
|
||||
self.results.append(s)
|
||||
log.debug("{0} results found.".format(len(self.results)))
|
||||
|
||||
def search_for_top(self, artist):
|
||||
search_response = self.session.search(value=artist, field="artist")
|
||||
self.results = []
|
||||
artist = search_response.artists[0].id
|
||||
results = self.session.get_artist_top_tracks(artist)
|
||||
for search_result in results:
|
||||
s = base.song(self)
|
||||
s.title = search_result.name
|
||||
s.artist = search_result.artist.name
|
||||
s.duration = seconds_to_string(search_result.duration)
|
||||
s.url = search_result.id
|
||||
s.tracknumber = str(search_result.track_num)
|
||||
s.album = search_result.album.name
|
||||
if search_result.album.num_tracks == None:
|
||||
s.single = True
|
||||
s.info = search_result
|
||||
self.results.append(s)
|
||||
log.debug("{0} results found.".format(len(self.results)))
|
||||
def search_for_top(self, artist):
|
||||
search_response = self.session.search(value=artist, field="artist")
|
||||
self.results = []
|
||||
artist = search_response.artists[0].id
|
||||
results = self.session.get_artist_top_tracks(artist)
|
||||
for search_result in results:
|
||||
s = base.song(self)
|
||||
s.title = search_result.name
|
||||
s.artist = search_result.artist.name
|
||||
s.duration = seconds_to_string(search_result.duration)
|
||||
s.url = search_result.id
|
||||
s.tracknumber = str(search_result.track_num)
|
||||
s.album = search_result.album.name
|
||||
if search_result.album.num_tracks == None:
|
||||
s.single = True
|
||||
s.info = search_result
|
||||
self.results.append(s)
|
||||
log.debug("{0} results found.".format(len(self.results)))
|
||||
|
||||
def format_number(self, number):
|
||||
if number < 10:
|
||||
return "0%d" % (number)
|
||||
else:
|
||||
return number
|
||||
def format_number(self, number):
|
||||
if number < 10:
|
||||
return "0%d" % (number)
|
||||
else:
|
||||
return number
|
||||
|
||||
def get_download_url(self, url):
|
||||
url = self.session.get_media_url(url)
|
||||
if url.startswith("https://") or url.startswith("http://") == False:
|
||||
url = "rtmp://"+url
|
||||
return url
|
||||
def get_download_url(self, url):
|
||||
url = self.session.get_media_url(url)
|
||||
if url.startswith("https://") or url.startswith("http://") == False:
|
||||
url = "rtmp://"+url
|
||||
return url
|
||||
|
||||
def format_track(self, item):
|
||||
if not hasattr(item, "single"):
|
||||
return "{0}. {1}".format(self.format_number(int(item.tracknumber)), item.title)
|
||||
else:
|
||||
return "{title}. {artist}. {duration}".format(title=item.title, duration=item.duration, artist=item.artist)
|
||||
def format_track(self, item):
|
||||
if not hasattr(item, "single"):
|
||||
return "{0}. {1}".format(self.format_number(int(item.tracknumber)), item.title)
|
||||
else:
|
||||
return "{title}. {artist}. {duration}".format(title=item.title, duration=item.duration, artist=item.artist)
|
||||
|
||||
class settings(base.baseSettings):
|
||||
name = _("Tidal")
|
||||
config_section = "tidal"
|
||||
name = _("Tidal")
|
||||
config_section = "tidal"
|
||||
|
||||
def get_quality_list(self):
|
||||
results = dict(low=_("Low"), high=_("High"), lossless=_("Lossless"))
|
||||
return results
|
||||
def get_quality_list(self):
|
||||
results = dict(low=_("Low"), high=_("High"), lossless=_("Lossless"))
|
||||
return results
|
||||
|
||||
def get_quality_value(self, *args, **kwargs):
|
||||
q = self.get_quality_list()
|
||||
for i in q.keys():
|
||||
if q.get(i) == self.quality.GetStringSelection():
|
||||
return i
|
||||
def get_quality_value(self, *args, **kwargs):
|
||||
q = self.get_quality_list()
|
||||
for i in q.keys():
|
||||
if q.get(i) == self.quality.GetStringSelection():
|
||||
return i
|
||||
|
||||
def set_quality_value(self, value, *args, **kwargs):
|
||||
q = self.get_quality_list()
|
||||
for i in q.keys():
|
||||
if i == value:
|
||||
self.quality.SetStringSelection(q.get(i))
|
||||
break
|
||||
def set_quality_value(self, value, *args, **kwargs):
|
||||
q = self.get_quality_list()
|
||||
for i in q.keys():
|
||||
if i == value:
|
||||
self.quality.SetStringSelection(q.get(i))
|
||||
break
|
||||
|
||||
def __init__(self, parent):
|
||||
super(settings, self).__init__(parent=parent)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.enabled = wx.CheckBox(self, wx.NewId(), _("Enable this service"))
|
||||
self.enabled.Bind(wx.EVT_CHECKBOX, self.on_enabled)
|
||||
self.map.append(("enabled", self.enabled))
|
||||
sizer.Add(self.enabled, 0, wx.ALL, 5)
|
||||
self.avoid_transcoding = wx.CheckBox(self, wx.NewId(), _("Avoid transcoding when downloading"))
|
||||
self.map.append(("avoid_transcoding", self.avoid_transcoding))
|
||||
sizer.Add(self.avoid_transcoding, 0, wx.ALL, 5)
|
||||
username = wx.StaticText(self, wx.NewId(), _("Tidal username or email address"))
|
||||
self.username = wx.TextCtrl(self, wx.NewId())
|
||||
usernamebox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
usernamebox.Add(username, 0, wx.ALL, 5)
|
||||
usernamebox.Add(self.username, 0, wx.ALL, 5)
|
||||
sizer.Add(usernamebox, 0, wx.ALL, 5)
|
||||
self.map.append(("username", self.username))
|
||||
password = wx.StaticText(self, wx.NewId(), _("Password"))
|
||||
self.password = wx.TextCtrl(self, wx.NewId(), style=wx.TE_PASSWORD)
|
||||
passwordbox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
passwordbox.Add(password, 0, wx.ALL, 5)
|
||||
passwordbox.Add(self.password, 0, wx.ALL, 5)
|
||||
sizer.Add(passwordbox, 0, wx.ALL, 5)
|
||||
self.map.append(("password", self.password))
|
||||
self.get_account = wx.Button(self, wx.NewId(), _("You can subscribe for a tidal account here"))
|
||||
self.get_account.Bind(wx.EVT_BUTTON, self.on_get_account)
|
||||
sizer.Add(self.get_account, 0, wx.ALL, 5)
|
||||
quality = wx.StaticText(self, wx.NewId(), _("Audio quality"))
|
||||
self.quality = wx.ComboBox(self, wx.NewId(), choices=[i for i in self.get_quality_list().values()], value=_("High"), style=wx.CB_READONLY)
|
||||
qualitybox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
qualitybox.Add(quality, 0, wx.ALL, 5)
|
||||
qualitybox.Add(self.quality, 0, wx.ALL, 5)
|
||||
sizer.Add(qualitybox, 0, wx.ALL, 5)
|
||||
# Monkeypatch for getting the right quality value here.
|
||||
self.quality.GetValue = self.get_quality_value
|
||||
self.quality.SetValue = self.set_quality_value
|
||||
self.map.append(("quality", self.quality))
|
||||
include = wx.StaticBoxSizer(parent=self, orient=wx.HORIZONTAL, label=_("Search by artist"))
|
||||
self.include_albums = wx.CheckBox(include.GetStaticBox(), wx.NewId(), _("Include albums"))
|
||||
self.include_compilations = wx.CheckBox(include.GetStaticBox(), wx.NewId(), _("Include compilations"))
|
||||
self.include_singles = wx.CheckBox(include.GetStaticBox(), wx.NewId(), _("Include singles"))
|
||||
sizer.Add(include, 0, wx.ALL, 5)
|
||||
self.map.append(("include_albums", self.include_albums))
|
||||
self.map.append(("include_compilations", self.include_compilations))
|
||||
self.map.append(("include_singles", self.include_singles))
|
||||
self.SetSizer(sizer)
|
||||
def __init__(self, parent):
|
||||
super(settings, self).__init__(parent=parent)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.enabled = wx.CheckBox(self, wx.NewId(), _("Enable this service"))
|
||||
self.enabled.Bind(wx.EVT_CHECKBOX, self.on_enabled)
|
||||
self.map.append(("enabled", self.enabled))
|
||||
sizer.Add(self.enabled, 0, wx.ALL, 5)
|
||||
self.avoid_transcoding = wx.CheckBox(self, wx.NewId(), _("Avoid transcoding when downloading"))
|
||||
self.map.append(("avoid_transcoding", self.avoid_transcoding))
|
||||
sizer.Add(self.avoid_transcoding, 0, wx.ALL, 5)
|
||||
username = wx.StaticText(self, wx.NewId(), _("Tidal username or email address"))
|
||||
self.username = wx.TextCtrl(self, wx.NewId())
|
||||
usernamebox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
usernamebox.Add(username, 0, wx.ALL, 5)
|
||||
usernamebox.Add(self.username, 0, wx.ALL, 5)
|
||||
sizer.Add(usernamebox, 0, wx.ALL, 5)
|
||||
self.map.append(("username", self.username))
|
||||
password = wx.StaticText(self, wx.NewId(), _("Password"))
|
||||
self.password = wx.TextCtrl(self, wx.NewId(), style=wx.TE_PASSWORD)
|
||||
passwordbox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
passwordbox.Add(password, 0, wx.ALL, 5)
|
||||
passwordbox.Add(self.password, 0, wx.ALL, 5)
|
||||
sizer.Add(passwordbox, 0, wx.ALL, 5)
|
||||
self.map.append(("password", self.password))
|
||||
self.get_account = wx.Button(self, wx.NewId(), _("You can subscribe for a tidal account here"))
|
||||
self.get_account.Bind(wx.EVT_BUTTON, self.on_get_account)
|
||||
sizer.Add(self.get_account, 0, wx.ALL, 5)
|
||||
quality = wx.StaticText(self, wx.NewId(), _("Audio quality"))
|
||||
self.quality = wx.ComboBox(self, wx.NewId(), choices=[i for i in self.get_quality_list().values()], value=_("High"), style=wx.CB_READONLY)
|
||||
qualitybox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
qualitybox.Add(quality, 0, wx.ALL, 5)
|
||||
qualitybox.Add(self.quality, 0, wx.ALL, 5)
|
||||
sizer.Add(qualitybox, 0, wx.ALL, 5)
|
||||
# Monkeypatch for getting the right quality value here.
|
||||
self.quality.GetValue = self.get_quality_value
|
||||
self.quality.SetValue = self.set_quality_value
|
||||
self.map.append(("quality", self.quality))
|
||||
include = wx.StaticBoxSizer(parent=self, orient=wx.HORIZONTAL, label=_("Search by artist"))
|
||||
self.include_albums = wx.CheckBox(include.GetStaticBox(), wx.NewId(), _("Include albums"))
|
||||
self.include_compilations = wx.CheckBox(include.GetStaticBox(), wx.NewId(), _("Include compilations"))
|
||||
self.include_singles = wx.CheckBox(include.GetStaticBox(), wx.NewId(), _("Include singles"))
|
||||
sizer.Add(include, 0, wx.ALL, 5)
|
||||
self.map.append(("include_albums", self.include_albums))
|
||||
self.map.append(("include_compilations", self.include_compilations))
|
||||
self.map.append(("include_singles", self.include_singles))
|
||||
self.SetSizer(sizer)
|
||||
|
||||
def on_enabled(self, *args, **kwargs):
|
||||
for i in self.map:
|
||||
if i[1] != self.enabled:
|
||||
if self.enabled.GetValue() == True:
|
||||
i[1].Enable(True)
|
||||
else:
|
||||
i[1].Enable(False)
|
||||
def on_enabled(self, *args, **kwargs):
|
||||
for i in self.map:
|
||||
if i[1] != self.enabled:
|
||||
if self.enabled.GetValue() == True:
|
||||
i[1].Enable(True)
|
||||
else:
|
||||
i[1].Enable(False)
|
||||
|
||||
def on_get_account(self, *args, **kwargs):
|
||||
webbrowser.open_new_tab("https://tidal.com")
|
||||
def on_get_account(self, *args, **kwargs):
|
||||
webbrowser.open_new_tab("https://tidal.com")
|
||||
|
@ -16,81 +16,81 @@ application_name = "music_dl"
|
||||
access_token = "e2237f17af545a4ba0bf6cb0b1a662e6"
|
||||
|
||||
class interface(base.baseInterface):
|
||||
""" Class downloader for VK audios. """
|
||||
name = "vk"
|
||||
if config.app != None: # Workaround for cx_freeze 6.2 in python 3.7.
|
||||
enabled = config.app["services"]["vk"].get("enabled")
|
||||
else:
|
||||
enabled = False
|
||||
""" Class downloader for VK audios. """
|
||||
name = "vk"
|
||||
if config.app != None: # Workaround for cx_freeze 6.2 in python 3.7.
|
||||
enabled = config.app["services"]["vk"].get("enabled")
|
||||
else:
|
||||
enabled = False
|
||||
|
||||
#util functions.
|
||||
def get_auth(self):
|
||||
# Authentication object
|
||||
self.auth=HTTPBasicAuth(application_name, access_token)
|
||||
#util functions.
|
||||
def get_auth(self):
|
||||
# Authentication object
|
||||
self.auth=HTTPBasicAuth(application_name, access_token)
|
||||
|
||||
def get(self, endpoint, *args, **kwargs):
|
||||
response = requests.get(url+endpoint, auth=self.auth, *args, **kwargs)
|
||||
return response
|
||||
def get(self, endpoint, *args, **kwargs):
|
||||
response = requests.get(url+endpoint, auth=self.auth, *args, **kwargs)
|
||||
return response
|
||||
|
||||
def __init__(self):
|
||||
super(interface, self).__init__()
|
||||
self.get_auth()
|
||||
def __init__(self):
|
||||
super(interface, self).__init__()
|
||||
self.get_auth()
|
||||
|
||||
def get_file_format(self):
|
||||
# Only mp3 audio is supported in VK so return it without further checks.
|
||||
return "mp3"
|
||||
def get_file_format(self):
|
||||
# Only mp3 audio is supported in VK so return it without further checks.
|
||||
return "mp3"
|
||||
|
||||
def transcoder_enabled(self):
|
||||
return False
|
||||
def transcoder_enabled(self):
|
||||
return False
|
||||
|
||||
def search(self, text):
|
||||
if text == "" or text == None:
|
||||
raise ValueError("Text must be passed and should not be blank.")
|
||||
log.debug("Retrieving data from vk...")
|
||||
self.results = []
|
||||
results = self.get("/vk/search", params=dict(text=text, maxresults=config.app["services"]["vk"]["max_results"]))
|
||||
if results.status_code != 200:
|
||||
return
|
||||
results = results.json()
|
||||
for search_result in results:
|
||||
s = base.song(self)
|
||||
s.title = search_result["title"]
|
||||
s.artist = search_result["artist"]
|
||||
s.duration = seconds_to_string(search_result["duration"])
|
||||
s.url = search_result["url"]
|
||||
s.info = search_result
|
||||
self.results.append(s)
|
||||
def search(self, text):
|
||||
if text == "" or text == None:
|
||||
raise ValueError("Text must be passed and should not be blank.")
|
||||
log.debug("Retrieving data from vk...")
|
||||
self.results = []
|
||||
results = self.get("/vk/search", params=dict(text=text, maxresults=config.app["services"]["vk"]["max_results"]))
|
||||
if results.status_code != 200:
|
||||
return
|
||||
results = results.json()
|
||||
for search_result in results:
|
||||
s = base.song(self)
|
||||
s.title = search_result["title"]
|
||||
s.artist = search_result["artist"]
|
||||
s.duration = seconds_to_string(search_result["duration"])
|
||||
s.url = search_result["url"]
|
||||
s.info = search_result
|
||||
self.results.append(s)
|
||||
|
||||
def get_download_url(self, file_url):
|
||||
return "{url}/vk/download/?url={url2}".format(url=url, url2=file_url)
|
||||
def get_download_url(self, file_url):
|
||||
return "{url}/vk/download/?url={url2}".format(url=url, url2=file_url)
|
||||
|
||||
def format_track(self, item):
|
||||
return "{title}. {artist}. {duration}".format(title=item.title, duration=item.duration, artist=item.artist)
|
||||
def format_track(self, item):
|
||||
return "{title}. {artist}. {duration}".format(title=item.title, duration=item.duration, artist=item.artist)
|
||||
|
||||
class settings(base.baseSettings):
|
||||
name = _("VK")
|
||||
config_section = "vk"
|
||||
name = _("VK")
|
||||
config_section = "vk"
|
||||
|
||||
def __init__(self, parent):
|
||||
super(settings, self).__init__(parent=parent)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.enabled = wx.CheckBox(self, wx.NewId(), _("Enable this service"))
|
||||
self.enabled.Bind(wx.EVT_CHECKBOX, self.on_enabled)
|
||||
self.map.append(("enabled", self.enabled))
|
||||
sizer.Add(self.enabled, 0, wx.ALL, 5)
|
||||
max_results_label = wx.StaticText(self, wx.NewId(), _("Max results per page"))
|
||||
self.max_results = wx.SpinCtrl(self, wx.NewId())
|
||||
self.max_results.SetRange(1, 300)
|
||||
max_results_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
max_results_sizer.Add(max_results_label, 0, wx.ALL, 5)
|
||||
max_results_sizer.Add(self.max_results, 0, wx.ALL, 5)
|
||||
self.map.append(("max_results", self.max_results))
|
||||
self.SetSizer(sizer)
|
||||
def __init__(self, parent):
|
||||
super(settings, self).__init__(parent=parent)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.enabled = wx.CheckBox(self, wx.NewId(), _("Enable this service"))
|
||||
self.enabled.Bind(wx.EVT_CHECKBOX, self.on_enabled)
|
||||
self.map.append(("enabled", self.enabled))
|
||||
sizer.Add(self.enabled, 0, wx.ALL, 5)
|
||||
max_results_label = wx.StaticText(self, wx.NewId(), _("Max results per page"))
|
||||
self.max_results = wx.SpinCtrl(self, wx.NewId())
|
||||
self.max_results.SetRange(1, 300)
|
||||
max_results_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
max_results_sizer.Add(max_results_label, 0, wx.ALL, 5)
|
||||
max_results_sizer.Add(self.max_results, 0, wx.ALL, 5)
|
||||
self.map.append(("max_results", self.max_results))
|
||||
self.SetSizer(sizer)
|
||||
|
||||
def on_enabled(self, *args, **kwargs):
|
||||
for i in self.map:
|
||||
if i[1] != self.enabled:
|
||||
if self.enabled.GetValue() == True:
|
||||
i[1].Enable(True)
|
||||
else:
|
||||
i[1].Enable(False)
|
||||
def on_enabled(self, *args, **kwargs):
|
||||
for i in self.map:
|
||||
if i[1] != self.enabled:
|
||||
if self.enabled.GetValue() == True:
|
||||
i[1].Enable(True)
|
||||
else:
|
||||
i[1].Enable(False)
|
||||
|
@ -9,126 +9,126 @@ from .import base
|
||||
log = logging.getLogger("extractors.youtube.com")
|
||||
|
||||
class interface(base.baseInterface):
|
||||
name = "YouTube"
|
||||
if config.app != None: # Workaround for cx_freeze 6.2 in python 3.7.
|
||||
enabled = config.app["services"]["youtube"].get("enabled")
|
||||
else:
|
||||
enabled = False
|
||||
name = "YouTube"
|
||||
if config.app != None: # Workaround for cx_freeze 6.2 in python 3.7.
|
||||
enabled = config.app["services"]["youtube"].get("enabled")
|
||||
else:
|
||||
enabled = False
|
||||
|
||||
def search(self, text, page=1):
|
||||
if text == "" or text == None:
|
||||
raise ValueError("Text must be passed and should not be blank.")
|
||||
if text.startswith("https") or text.startswith("http"):
|
||||
return self.search_from_url(text)
|
||||
type = "video"
|
||||
max_results = config.app["services"]["youtube"]["max_results"]
|
||||
log.debug("Retrieving data from Youtube...")
|
||||
ydl = youtube_dl.YoutubeDL({'quiet': True, 'ignore_errors': True, 'no_warnings': True, 'logger': log, 'format': 'bestaudio/best', 'outtmpl': u'%(id)s%(ext)s'})
|
||||
with ydl:
|
||||
search_param = "ytsearch{}:{}".format(max_results, text)
|
||||
result = ydl.extract_info(search_param, download=False)
|
||||
self.results = []
|
||||
for search_result in result["entries"]:
|
||||
s = base.song(self)
|
||||
s.title = search_result["title"]
|
||||
s.url = "https://www.youtube.com/watch?v="+search_result["id"]
|
||||
s.duration = seconds_to_string(search_result["duration"])
|
||||
if search_result.get("track") != None:
|
||||
s.title = search_result["track"]
|
||||
if search_result.get("album") != None:
|
||||
s.album = search_result["album"]
|
||||
if search_result.get("artist") != None:
|
||||
s.artist = search_result["artist"]
|
||||
self.results.append(s)
|
||||
log.debug("{0} results found.".format(len(self.results)))
|
||||
def search(self, text, page=1):
|
||||
if text == "" or text == None:
|
||||
raise ValueError("Text must be passed and should not be blank.")
|
||||
if text.startswith("https") or text.startswith("http"):
|
||||
return self.search_from_url(text)
|
||||
type = "video"
|
||||
max_results = config.app["services"]["youtube"]["max_results"]
|
||||
log.debug("Retrieving data from Youtube...")
|
||||
ydl = youtube_dl.YoutubeDL({'quiet': True, 'ignore_errors': True, 'no_warnings': True, 'logger': log, 'format': 'bestaudio/best', 'outtmpl': u'%(id)s%(ext)s'})
|
||||
with ydl:
|
||||
search_param = "ytsearch{}:{}".format(max_results, text)
|
||||
result = ydl.extract_info(search_param, download=False)
|
||||
self.results = []
|
||||
for search_result in result["entries"]:
|
||||
s = base.song(self)
|
||||
s.title = search_result["title"]
|
||||
s.url = "https://www.youtube.com/watch?v="+search_result["id"]
|
||||
s.duration = seconds_to_string(search_result["duration"])
|
||||
if search_result.get("track") != None:
|
||||
s.title = search_result["track"]
|
||||
if search_result.get("album") != None:
|
||||
s.album = search_result["album"]
|
||||
if search_result.get("artist") != None:
|
||||
s.artist = search_result["artist"]
|
||||
self.results.append(s)
|
||||
log.debug("{0} results found.".format(len(self.results)))
|
||||
|
||||
def search_from_url(self, url):
|
||||
log.debug("Getting download URL for {0}".format(url,))
|
||||
if "playlist?list=" in url:
|
||||
return self.search_from_playlist(url)
|
||||
ydl = youtube_dl.YoutubeDL({'quiet': True, 'ignore_errors': True, 'no_warnings': True, 'logger': log, 'prefer-free-formats': True, 'format': 'bestaudio', 'outtmpl': u'%(id)s%(ext)s'})
|
||||
with ydl:
|
||||
result = ydl.extract_info(url, download=False)
|
||||
if 'entries' in result:
|
||||
videos = result['entries']
|
||||
else:
|
||||
videos = [result]
|
||||
for video in videos:
|
||||
s = base.song(self)
|
||||
s.title = video["title"]
|
||||
s.url = video["webpage_url"] # Cannot use direct URL here cause Youtube URLS expire after a minute.
|
||||
s.duration = seconds_to_string(video["duration"])
|
||||
self.results.append(s)
|
||||
log.debug("{0} results found.".format(len(self.results)))
|
||||
def search_from_url(self, url):
|
||||
log.debug("Getting download URL for {0}".format(url,))
|
||||
if "playlist?list=" in url:
|
||||
return self.search_from_playlist(url)
|
||||
ydl = youtube_dl.YoutubeDL({'quiet': True, 'ignore_errors': True, 'no_warnings': True, 'logger': log, 'prefer-free-formats': True, 'format': 'bestaudio', 'outtmpl': u'%(id)s%(ext)s'})
|
||||
with ydl:
|
||||
result = ydl.extract_info(url, download=False)
|
||||
if 'entries' in result:
|
||||
videos = result['entries']
|
||||
else:
|
||||
videos = [result]
|
||||
for video in videos:
|
||||
s = base.song(self)
|
||||
s.title = video["title"]
|
||||
s.url = video["webpage_url"] # Cannot use direct URL here cause Youtube URLS expire after a minute.
|
||||
s.duration = seconds_to_string(video["duration"])
|
||||
self.results.append(s)
|
||||
log.debug("{0} results found.".format(len(self.results)))
|
||||
|
||||
def search_from_playlist(self, url):
|
||||
id = url.split("=")[1]
|
||||
max_results = 50
|
||||
log.debug("Retrieving data from Youtube...")
|
||||
ydl = youtube_dl.YoutubeDL({'quiet': True, 'ignore_errors': True, 'no_warnings': True, 'logger': log, 'format': 'bestaudio/best', 'outtmpl': u'%(id)s%(ext)s'})
|
||||
with ydl:
|
||||
result = ydl.extract_info(url, download=False)
|
||||
self.results = []
|
||||
for search_result in result["entries"]:
|
||||
s = base.song(self)
|
||||
s.title = search_result["title"]
|
||||
s.url = "https://www.youtube.com/watch?v="+search_result["id"]
|
||||
s.duration = seconds_to_string(search_result["duration"])
|
||||
if search_result.get("track") != None:
|
||||
s.title = search_result["track"]
|
||||
if search_result.get("album") != None:
|
||||
s.album = search_result["album"]
|
||||
if search_result.get("artist") != None:
|
||||
s.artist = search_result["artist"]
|
||||
self.results.append(s)
|
||||
log.debug("{0} results found.".format(len(self.results)))
|
||||
def search_from_playlist(self, url):
|
||||
id = url.split("=")[1]
|
||||
max_results = 50
|
||||
log.debug("Retrieving data from Youtube...")
|
||||
ydl = youtube_dl.YoutubeDL({'quiet': True, 'ignore_errors': True, 'no_warnings': True, 'logger': log, 'format': 'bestaudio/best', 'outtmpl': u'%(id)s%(ext)s'})
|
||||
with ydl:
|
||||
result = ydl.extract_info(url, download=False)
|
||||
self.results = []
|
||||
for search_result in result["entries"]:
|
||||
s = base.song(self)
|
||||
s.title = search_result["title"]
|
||||
s.url = "https://www.youtube.com/watch?v="+search_result["id"]
|
||||
s.duration = seconds_to_string(search_result["duration"])
|
||||
if search_result.get("track") != None:
|
||||
s.title = search_result["track"]
|
||||
if search_result.get("album") != None:
|
||||
s.album = search_result["album"]
|
||||
if search_result.get("artist") != None:
|
||||
s.artist = search_result["artist"]
|
||||
self.results.append(s)
|
||||
log.debug("{0} results found.".format(len(self.results)))
|
||||
|
||||
def get_download_url(self, url):
|
||||
log.debug("Getting download URL for {0}".format(url,))
|
||||
ydl = youtube_dl.YoutubeDL({'quiet': True, 'no_warnings': True, 'logger': log, 'prefer_insecure': True, 'format': 'bestaudio/best', 'outtmpl': u'%(id)s%(ext)s'})
|
||||
with ydl:
|
||||
result = ydl.extract_info(url, download=False)
|
||||
if 'entries' in result:
|
||||
video = result['entries'][0]
|
||||
else:
|
||||
video = result
|
||||
# From here we should extract the first format so it will contain audio only.
|
||||
log.debug("Download URL: {0}".format(video["formats"][0]["url"],))
|
||||
return video["formats"][0]["url"]
|
||||
def get_download_url(self, url):
|
||||
log.debug("Getting download URL for {0}".format(url,))
|
||||
ydl = youtube_dl.YoutubeDL({'quiet': True, 'no_warnings': True, 'logger': log, 'prefer_insecure': True, 'format': 'bestaudio/best', 'outtmpl': u'%(id)s%(ext)s'})
|
||||
with ydl:
|
||||
result = ydl.extract_info(url, download=False)
|
||||
if 'entries' in result:
|
||||
video = result['entries'][0]
|
||||
else:
|
||||
video = result
|
||||
# From here we should extract the first format so it will contain audio only.
|
||||
log.debug("Download URL: {0}".format(video["formats"][0]["url"],))
|
||||
return video["formats"][0]["url"]
|
||||
|
||||
def format_track(self, item):
|
||||
return "{0} {1}".format(item.title, item.duration)
|
||||
def format_track(self, item):
|
||||
return "{0} {1}".format(item.title, item.duration)
|
||||
|
||||
def transcoder_enabled(self):
|
||||
return config.app["services"]["youtube"]["transcode"]
|
||||
def transcoder_enabled(self):
|
||||
return config.app["services"]["youtube"]["transcode"]
|
||||
|
||||
class settings(base.baseSettings):
|
||||
name = _("Youtube")
|
||||
config_section = "youtube"
|
||||
name = _("Youtube")
|
||||
config_section = "youtube"
|
||||
|
||||
def __init__(self, parent):
|
||||
super(settings, self).__init__(parent=parent)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.enabled = wx.CheckBox(self, wx.NewId(), _("Enable this service"))
|
||||
self.enabled.Bind(wx.EVT_CHECKBOX, self.on_enabled)
|
||||
self.map.append(("enabled", self.enabled))
|
||||
sizer.Add(self.enabled, 0, wx.ALL, 5)
|
||||
max_results_label = wx.StaticText(self, wx.NewId(), _("Max results per page"))
|
||||
self.max_results = wx.SpinCtrl(self, wx.NewId())
|
||||
self.max_results.SetRange(1, 50)
|
||||
max_results_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
max_results_sizer.Add(max_results_label, 0, wx.ALL, 5)
|
||||
max_results_sizer.Add(self.max_results, 0, wx.ALL, 5)
|
||||
self.map.append(("max_results", self.max_results))
|
||||
# self.transcode = wx.CheckBox(self, wx.NewId(), _("Enable transcode when downloading"))
|
||||
# self.map.append(("transcode", self.transcode))
|
||||
# sizer.Add(self.transcode, 0, wx.ALL, 5)
|
||||
self.SetSizer(sizer)
|
||||
def __init__(self, parent):
|
||||
super(settings, self).__init__(parent=parent)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.enabled = wx.CheckBox(self, wx.NewId(), _("Enable this service"))
|
||||
self.enabled.Bind(wx.EVT_CHECKBOX, self.on_enabled)
|
||||
self.map.append(("enabled", self.enabled))
|
||||
sizer.Add(self.enabled, 0, wx.ALL, 5)
|
||||
max_results_label = wx.StaticText(self, wx.NewId(), _("Max results per page"))
|
||||
self.max_results = wx.SpinCtrl(self, wx.NewId())
|
||||
self.max_results.SetRange(1, 50)
|
||||
max_results_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
max_results_sizer.Add(max_results_label, 0, wx.ALL, 5)
|
||||
max_results_sizer.Add(self.max_results, 0, wx.ALL, 5)
|
||||
self.map.append(("max_results", self.max_results))
|
||||
# self.transcode = wx.CheckBox(self, wx.NewId(), _("Enable transcode when downloading"))
|
||||
# self.map.append(("transcode", self.transcode))
|
||||
# sizer.Add(self.transcode, 0, wx.ALL, 5)
|
||||
self.SetSizer(sizer)
|
||||
|
||||
def on_enabled(self, *args, **kwargs):
|
||||
for i in self.map:
|
||||
if i[1] != self.enabled:
|
||||
if self.enabled.GetValue() == True:
|
||||
i[1].Enable(True)
|
||||
else:
|
||||
i[1].Enable(False)
|
||||
def on_enabled(self, *args, **kwargs):
|
||||
for i in self.map:
|
||||
if i[1] != self.enabled:
|
||||
if self.enabled.GetValue() == True:
|
||||
i[1].Enable(True)
|
||||
else:
|
||||
i[1].Enable(False)
|
||||
|
@ -12,53 +12,53 @@ from . import base
|
||||
log = logging.getLogger("extractors.zaycev.net")
|
||||
|
||||
class interface(base.baseInterface):
|
||||
name = "zaycev.net"
|
||||
if config.app != None: # Workaround for cx_freeze 6.2 in python 3.7.
|
||||
enabled = config.app["services"]["zaycev"].get("enabled")
|
||||
else:
|
||||
enabled = False
|
||||
name = "zaycev.net"
|
||||
if config.app != None: # Workaround for cx_freeze 6.2 in python 3.7.
|
||||
enabled = config.app["services"]["zaycev"].get("enabled")
|
||||
else:
|
||||
enabled = False
|
||||
|
||||
def search(self, text, page=1):
|
||||
if text == "" or text == None:
|
||||
raise ValueError("Text must be passed and should not be blank.")
|
||||
site = "http://zaycev.net/search.html?query_search=%s" % (text,)
|
||||
log.debug("Retrieving data from {0}...".format(site,))
|
||||
r = requests.get(site)
|
||||
soup = BeautifulSoup(r.text, 'html.parser')
|
||||
search_results = soup.find_all("div", {"class": "musicset-track__title track-geo__title"})
|
||||
self.results = []
|
||||
for i in search_results:
|
||||
# The easiest method to get artist and song names is to fetch links. There are only two links per result here.
|
||||
data = i.find_all("a")
|
||||
# from here, data[0] contains artist info and data[1] contains info of the retrieved song.
|
||||
s = base.song(self)
|
||||
s.title = data[1].text
|
||||
s.artist = data[0].text
|
||||
s.url = "http://zaycev.net%s" % (data[1].attrs["href"])
|
||||
# s.duration = self.hd[i]["duration"]
|
||||
# s.size = self.hd[i]["size"]
|
||||
# s.bitrate = self.hd[i]["bitrate"]
|
||||
self.results.append(s)
|
||||
log.debug("{0} results found.".format(len(self.results)))
|
||||
def search(self, text, page=1):
|
||||
if text == "" or text == None:
|
||||
raise ValueError("Text must be passed and should not be blank.")
|
||||
site = "http://zaycev.net/search.html?query_search=%s" % (text,)
|
||||
log.debug("Retrieving data from {0}...".format(site,))
|
||||
r = requests.get(site)
|
||||
soup = BeautifulSoup(r.text, 'html.parser')
|
||||
search_results = soup.find_all("div", {"class": "musicset-track__title track-geo__title"})
|
||||
self.results = []
|
||||
for i in search_results:
|
||||
# The easiest method to get artist and song names is to fetch links. There are only two links per result here.
|
||||
data = i.find_all("a")
|
||||
# from here, data[0] contains artist info and data[1] contains info of the retrieved song.
|
||||
s = base.song(self)
|
||||
s.title = data[1].text
|
||||
s.artist = data[0].text
|
||||
s.url = "http://zaycev.net%s" % (data[1].attrs["href"])
|
||||
# s.duration = self.hd[i]["duration"]
|
||||
# s.size = self.hd[i]["size"]
|
||||
# s.bitrate = self.hd[i]["bitrate"]
|
||||
self.results.append(s)
|
||||
log.debug("{0} results found.".format(len(self.results)))
|
||||
|
||||
def get_download_url(self, url):
|
||||
log.debug("Getting download URL for {0}".format(url,))
|
||||
soups = BeautifulSoup(requests.get(url).text, 'html.parser')
|
||||
data = json.loads(requests.get('http://zaycev.net' + soups.find('div', {'class':"musicset-track"}).get('data-url')).text)
|
||||
log.debug("Download URL: {0}".format(data["url"]))
|
||||
return data["url"]
|
||||
def get_download_url(self, url):
|
||||
log.debug("Getting download URL for {0}".format(url,))
|
||||
soups = BeautifulSoup(requests.get(url).text, 'html.parser')
|
||||
data = json.loads(requests.get('http://zaycev.net' + soups.find('div', {'class':"musicset-track"}).get('data-url')).text)
|
||||
log.debug("Download URL: {0}".format(data["url"]))
|
||||
return data["url"]
|
||||
|
||||
def format_track(self, item):
|
||||
return "{0}. {1}. {2}".format(item.title, item.duration, item.size)
|
||||
def format_track(self, item):
|
||||
return "{0}. {1}. {2}".format(item.title, item.duration, item.size)
|
||||
|
||||
class settings(base.baseSettings):
|
||||
name = _("zaycev.net")
|
||||
config_section = "zaycev"
|
||||
name = _("zaycev.net")
|
||||
config_section = "zaycev"
|
||||
|
||||
def __init__(self, parent):
|
||||
super(settings, self).__init__(parent=parent)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.enabled = wx.CheckBox(self, wx.NewId(), _("Enable this service (works only in the Russian Federation)"))
|
||||
self.map.append(("enabled", self.enabled))
|
||||
sizer.Add(self.enabled, 0, wx.ALL, 5)
|
||||
self.SetSizer(sizer)
|
||||
def __init__(self, parent):
|
||||
super(settings, self).__init__(parent=parent)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.enabled = wx.CheckBox(self, wx.NewId(), _("Enable this service (works only in the Russian Federation)"))
|
||||
self.map.append(("enabled", self.enabled))
|
||||
sizer.Add(self.enabled, 0, wx.ALL, 5)
|
||||
self.SetSizer(sizer)
|
||||
|
22
src/setup.py
22
src/setup.py
@ -10,23 +10,23 @@ i18n.setup()
|
||||
import application
|
||||
|
||||
def find_accessible_output2_datafiles():
|
||||
import accessible_output2
|
||||
path = os.path.join(accessible_output2.__path__[0], 'lib')
|
||||
dest_dir = os.path.join('accessible_output2', 'lib')
|
||||
return (path, dest_dir)
|
||||
import accessible_output2
|
||||
path = os.path.join(accessible_output2.__path__[0], 'lib')
|
||||
dest_dir = os.path.join('accessible_output2', 'lib')
|
||||
return (path, dest_dir)
|
||||
|
||||
base = None
|
||||
if sys.platform == 'win32':
|
||||
base = 'Win32GUI'
|
||||
|
||||
build_exe_options = dict(
|
||||
build_exe="dist",
|
||||
optimize=1,
|
||||
include_msvcr=True,
|
||||
zip_include_packages=["accessible_output2"],
|
||||
replace_paths = [("*", "")],
|
||||
include_files=["bootstrap.exe", "app-configuration.defaults", "cacerts.txt", "locales", "plugins", "libvlc.dll", "libvlccore.dll", find_accessible_output2_datafiles()],
|
||||
)
|
||||
build_exe="dist",
|
||||
optimize=1,
|
||||
include_msvcr=True,
|
||||
zip_include_packages=["accessible_output2"],
|
||||
replace_paths = [("*", "")],
|
||||
include_files=["bootstrap.exe", "app-configuration.defaults", "cacerts.txt", "locales", "plugins", "libvlc.dll", "libvlccore.dll", find_accessible_output2_datafiles()],
|
||||
)
|
||||
|
||||
executables = [
|
||||
Executable('main.py', base=base, targetName="MusicDL")
|
||||
|
@ -8,14 +8,14 @@ data_directory = None
|
||||
app_type = ""
|
||||
|
||||
def setup():
|
||||
global data_directory, app_type
|
||||
if len(glob.glob("Uninstall.exe")) > 0: # installed copy
|
||||
if os.path.exists(paths.data_path("musicDL")) == False:
|
||||
os.mkdir(paths.data_path("musicDL"))
|
||||
data_directory = paths.data_path("musicDL")
|
||||
app_type = "installed"
|
||||
else:
|
||||
app_type = "portable"
|
||||
data_directory = os.path.join(paths.app_path(), "data")
|
||||
if os.path.exists(data_directory) == False:
|
||||
os.mkdir(data_directory)
|
||||
global data_directory, app_type
|
||||
if len(glob.glob("Uninstall.exe")) > 0: # installed copy
|
||||
if os.path.exists(paths.data_path("musicDL")) == False:
|
||||
os.mkdir(paths.data_path("musicDL"))
|
||||
data_directory = paths.data_path("musicDL")
|
||||
app_type = "installed"
|
||||
else:
|
||||
app_type = "portable"
|
||||
data_directory = os.path.join(paths.app_path(), "data")
|
||||
if os.path.exists(data_directory) == False:
|
||||
os.mkdir(data_directory)
|
||||
|
@ -9,24 +9,24 @@ from fixes import fix_requests
|
||||
|
||||
# Let's import the reload function
|
||||
if sys.version[0] == "3":
|
||||
from imp import reload
|
||||
from imp import reload
|
||||
|
||||
class fixesTestCase(unittest.TestCase):
|
||||
|
||||
# def test_winpaths_error_in_python3(self):
|
||||
# """ Testing the winpaths error happening only in Python 3 due to changes introduced to ctypes. """
|
||||
# # If this test fails, it means winpaths has been updated to fix the ctypes issue already.
|
||||
# # Therefore this test and the corresponding issue should be removed.
|
||||
# if sys.version[0] != "3":
|
||||
# return
|
||||
# # A reload of winpaths is needed to rever the fix of winpaths, if has been applied before
|
||||
# reload(winpaths)
|
||||
# self.assertRaises(AttributeError, winpaths.get_appdata)
|
||||
# def test_winpaths_error_in_python3(self):
|
||||
# """ Testing the winpaths error happening only in Python 3 due to changes introduced to ctypes. """
|
||||
# # If this test fails, it means winpaths has been updated to fix the ctypes issue already.
|
||||
# # Therefore this test and the corresponding issue should be removed.
|
||||
# if sys.version[0] != "3":
|
||||
# return
|
||||
# # A reload of winpaths is needed to rever the fix of winpaths, if has been applied before
|
||||
# reload(winpaths)
|
||||
# self.assertRaises(AttributeError, winpaths.get_appdata)
|
||||
|
||||
def test_requests_fix(self):
|
||||
""" Testing the requests fix and check if the certificates file exists in the provided path. """
|
||||
fix_requests.fix()
|
||||
self.assertTrue(os.path.exists(os.environ["REQUESTS_CA_BUNDLE"]))
|
||||
def test_requests_fix(self):
|
||||
""" Testing the requests fix and check if the certificates file exists in the provided path. """
|
||||
fix_requests.fix()
|
||||
self.assertTrue(os.path.exists(os.environ["REQUESTS_CA_BUNDLE"]))
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
unittest.main()
|
||||
|
@ -15,80 +15,80 @@ from services import base
|
||||
|
||||
# Pytohn 2/3 compat
|
||||
if sys.version[0] == "2":
|
||||
strtype = unicode
|
||||
strtype = unicode
|
||||
else:
|
||||
strtype = str
|
||||
strtype = str
|
||||
|
||||
class servicesTestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
""" Configure i18n functions for avoiding a traceback later. """
|
||||
i18n.setup()
|
||||
def setUp(self):
|
||||
""" Configure i18n functions for avoiding a traceback later. """
|
||||
i18n.setup()
|
||||
|
||||
def search(self, service_name, search_query="piano", skip_validation=False):
|
||||
""" Search a video in the passed service name. """
|
||||
# Test basic instance stuff.
|
||||
service_instance = getattr(services, service_name).interface()
|
||||
service_instance.search(search_query)
|
||||
self.assertIsInstance(service_instance.results, list)
|
||||
self.assertNotEqual(len(service_instance.results), 0)
|
||||
self.assertIsInstance(len(service_instance.results), int)
|
||||
# Take and test validity of the first item.
|
||||
item = service_instance.results[0]
|
||||
self.assertIsInstance(item, base.song)
|
||||
self.assertIsInstance(item.title, strtype)
|
||||
self.assertNotEqual(item.title, "")
|
||||
if service_name == "youtube": # Duration is only available for youtube.
|
||||
self.assertIsInstance(item.duration, strtype)
|
||||
self.assertNotEqual(item.duration, "")
|
||||
self.assertIsInstance(item.url, strtype)
|
||||
self.assertNotEqual(item.url, "")
|
||||
if service_name == "youtube" and skip_validation == False:
|
||||
match = re.search("((?<=(v|V)/)|(?<=be/)|(?<=(\?|\&)v=)|(?<=embed/))([\w-]+)", item.url)
|
||||
self.assertNotEqual(match, None)
|
||||
formatted_track = item.format_track()
|
||||
self.assertIsInstance(formatted_track, strtype)
|
||||
self.assertNotEquals(formatted_track, "")
|
||||
item.get_download_url()
|
||||
self.assertIsInstance(item.download_url, strtype)
|
||||
self.assertNotEquals(item.download_url, "")
|
||||
def search(self, service_name, search_query="piano", skip_validation=False):
|
||||
""" Search a video in the passed service name. """
|
||||
# Test basic instance stuff.
|
||||
service_instance = getattr(services, service_name).interface()
|
||||
service_instance.search(search_query)
|
||||
self.assertIsInstance(service_instance.results, list)
|
||||
self.assertNotEqual(len(service_instance.results), 0)
|
||||
self.assertIsInstance(len(service_instance.results), int)
|
||||
# Take and test validity of the first item.
|
||||
item = service_instance.results[0]
|
||||
self.assertIsInstance(item, base.song)
|
||||
self.assertIsInstance(item.title, strtype)
|
||||
self.assertNotEqual(item.title, "")
|
||||
if service_name == "youtube": # Duration is only available for youtube.
|
||||
self.assertIsInstance(item.duration, strtype)
|
||||
self.assertNotEqual(item.duration, "")
|
||||
self.assertIsInstance(item.url, strtype)
|
||||
self.assertNotEqual(item.url, "")
|
||||
if service_name == "youtube" and skip_validation == False:
|
||||
match = re.search("((?<=(v|V)/)|(?<=be/)|(?<=(\?|\&)v=)|(?<=embed/))([\w-]+)", item.url)
|
||||
self.assertNotEqual(match, None)
|
||||
formatted_track = item.format_track()
|
||||
self.assertIsInstance(formatted_track, strtype)
|
||||
self.assertNotEquals(formatted_track, "")
|
||||
item.get_download_url()
|
||||
self.assertIsInstance(item.download_url, strtype)
|
||||
self.assertNotEquals(item.download_url, "")
|
||||
|
||||
def search_blank(self, extractor_name, search_query=""):
|
||||
""" Attempt to search in any extractor by passing a blank string. """
|
||||
extractor_instance = getattr(services, extractor_name).interface()
|
||||
self.assertRaises(ValueError, extractor_instance.search, search_query)
|
||||
def search_blank(self, extractor_name, search_query=""):
|
||||
""" Attempt to search in any extractor by passing a blank string. """
|
||||
extractor_instance = getattr(services, extractor_name).interface()
|
||||
self.assertRaises(ValueError, extractor_instance.search, search_query)
|
||||
|
||||
def test_youtube_search(self):
|
||||
""" Testing a Youtube search. """
|
||||
self.search("youtube")
|
||||
def test_youtube_search(self):
|
||||
""" Testing a Youtube search. """
|
||||
self.search("youtube")
|
||||
|
||||
def test_youtube_search_unicode(self):
|
||||
""" Testing a Youtube search using unicode characters. """
|
||||
self.search("youtube", "Пианино")
|
||||
def test_youtube_search_unicode(self):
|
||||
""" Testing a Youtube search using unicode characters. """
|
||||
self.search("youtube", "Пианино")
|
||||
|
||||
def test_youtube_search_blank(self):
|
||||
""" Testing a youtube search when text is blank or not passed. """
|
||||
self.search_blank("youtube")
|
||||
def test_youtube_search_blank(self):
|
||||
""" Testing a youtube search when text is blank or not passed. """
|
||||
self.search_blank("youtube")
|
||||
|
||||
def test_youtube_direct_link(self):
|
||||
""" Testing a search in youtube by passing a direct link. """
|
||||
self.search("youtube", "https://www.youtube.com/watch?v=XkeU8w2Y-2Y")
|
||||
def test_youtube_direct_link(self):
|
||||
""" Testing a search in youtube by passing a direct link. """
|
||||
self.search("youtube", "https://www.youtube.com/watch?v=XkeU8w2Y-2Y")
|
||||
|
||||
def test_youtube_playlist(self):
|
||||
""" Testing a youtube search by passing a link to a playlist. """
|
||||
self.search("youtube", "https://www.youtube.com/channel/UCPTYdUGtBMuqGg6ZtvYC1zQ", skip_validation=True)
|
||||
# Uncomment the following test only if you live or test this in Russia.
|
||||
# def test_zaycev_search(self):
|
||||
# """ Testing a search made in zaycev.net """
|
||||
# self.search("zaycev")
|
||||
def test_youtube_playlist(self):
|
||||
""" Testing a youtube search by passing a link to a playlist. """
|
||||
self.search("youtube", "https://www.youtube.com/channel/UCPTYdUGtBMuqGg6ZtvYC1zQ", skip_validation=True)
|
||||
# Uncomment the following test only if you live or test this in Russia.
|
||||
# def test_zaycev_search(self):
|
||||
# """ Testing a search made in zaycev.net """
|
||||
# self.search("zaycev")
|
||||
|
||||
# def test_zaycev_search_unicode(self):
|
||||
# """ Testing a search made in zaycev.net with unicode characters. """
|
||||
# self.search("zaycev", "Пианино")
|
||||
# def test_zaycev_search_unicode(self):
|
||||
# """ Testing a search made in zaycev.net with unicode characters. """
|
||||
# self.search("zaycev", "Пианино")
|
||||
|
||||
# def test_zaycev_search_blank(self):
|
||||
# """ Testing a search in zaycev.net when text is blank. """
|
||||
# self.search_blank("zaycev")
|
||||
# def test_zaycev_search_blank(self):
|
||||
# """ Testing a search in zaycev.net when text is blank. """
|
||||
# self.search_blank("zaycev")
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
unittest.main()
|
||||
|
@ -11,31 +11,31 @@ from fixes import fix_winpaths
|
||||
|
||||
class storageTestCase(unittest.TestCase):
|
||||
|
||||
def test_portable_path(self):
|
||||
""" Testing if paths are generated appropiately. """
|
||||
storage.setup()
|
||||
self.assertEquals(storage.app_type, "portable")
|
||||
self.assertTrue(os.path.exists(storage.data_directory))
|
||||
self.assertEquals(storage.data_directory, os.path.join(paths.app_path(), "data"))
|
||||
def test_portable_path(self):
|
||||
""" Testing if paths are generated appropiately. """
|
||||
storage.setup()
|
||||
self.assertEquals(storage.app_type, "portable")
|
||||
self.assertTrue(os.path.exists(storage.data_directory))
|
||||
self.assertEquals(storage.data_directory, os.path.join(paths.app_path(), "data"))
|
||||
|
||||
def test_installer_path(self):
|
||||
""" Testing if paths are generated appropiately. """
|
||||
# this is a temporary fix for winpaths.
|
||||
fake_installer_file = open(os.path.join(paths.app_path(), "Uninstall.exe"), "w")
|
||||
fake_installer_file.close()
|
||||
fix_winpaths.fix()
|
||||
storage.setup()
|
||||
self.assertEquals(storage.app_type, "installed")
|
||||
self.assertTrue(os.path.exists(storage.data_directory))
|
||||
self.assertEquals(storage.data_directory, paths.data_path("musicDL"))
|
||||
def test_installer_path(self):
|
||||
""" Testing if paths are generated appropiately. """
|
||||
# this is a temporary fix for winpaths.
|
||||
fake_installer_file = open(os.path.join(paths.app_path(), "Uninstall.exe"), "w")
|
||||
fake_installer_file.close()
|
||||
fix_winpaths.fix()
|
||||
storage.setup()
|
||||
self.assertEquals(storage.app_type, "installed")
|
||||
self.assertTrue(os.path.exists(storage.data_directory))
|
||||
self.assertEquals(storage.data_directory, paths.data_path("musicDL"))
|
||||
|
||||
def tearDown(self):
|
||||
""" Removes uninstall.exe created for tests and data path."""
|
||||
fix_winpaths.fix()
|
||||
if os.path.exists(paths.data_path("musicDL")):
|
||||
shutil.rmtree(paths.data_path("musicDL"))
|
||||
if os.path.exists(os.path.join(paths.app_path(), "uninstall.exe")):
|
||||
os.remove(os.path.join(paths.app_path(), "uninstall.exe"))
|
||||
def tearDown(self):
|
||||
""" Removes uninstall.exe created for tests and data path."""
|
||||
fix_winpaths.fix()
|
||||
if os.path.exists(paths.data_path("musicDL")):
|
||||
shutil.rmtree(paths.data_path("musicDL"))
|
||||
if os.path.exists(os.path.join(paths.app_path(), "uninstall.exe")):
|
||||
os.remove(os.path.join(paths.app_path(), "uninstall.exe"))
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
unittest.main()
|
||||
|
@ -3,10 +3,10 @@ import os.path
|
||||
import platform
|
||||
|
||||
def find_datafiles():
|
||||
system = platform.system()
|
||||
if system == 'Windows':
|
||||
file_ext = '*.exe'
|
||||
else:
|
||||
file_ext = '*.sh'
|
||||
path = os.path.abspath(os.path.join(__path__[0], 'bootstrappers', file_ext))
|
||||
return [('', glob.glob(path))]
|
||||
system = platform.system()
|
||||
if system == 'Windows':
|
||||
file_ext = '*.exe'
|
||||
else:
|
||||
file_ext = '*.sh'
|
||||
path = os.path.abspath(os.path.join(__path__[0], 'bootstrappers', file_ext))
|
||||
return [('', glob.glob(path))]
|
||||
|
@ -11,123 +11,123 @@ import tempfile
|
||||
import widgetUtils
|
||||
import webbrowser
|
||||
try:
|
||||
import czipfile as zipfile
|
||||
import czipfile as zipfile
|
||||
except ImportError:
|
||||
import zipfile
|
||||
import zipfile
|
||||
|
||||
from platform_utils import paths
|
||||
|
||||
def perform_update(endpoint, current_version, update_type="stable", app_name='', password=None, update_available_callback=None, progress_callback=None, update_complete_callback=None):
|
||||
requests_session = create_requests_session(app_name=app_name, version=current_version)
|
||||
available_update = find_update(endpoint, requests_session=requests_session)
|
||||
if not available_update:
|
||||
logger.debug("No update available")
|
||||
return False
|
||||
available_version, available_description, update_url = find_version_data(update_type, current_version, available_update)
|
||||
if available_version == False:
|
||||
return False
|
||||
logger.info("A new update is available. Version %s" % available_version)
|
||||
if callable(update_available_callback) and not update_available_callback(version=available_version, description=available_description): #update_available_callback should return a falsy value to stop the process
|
||||
logger.info("User canceled update.")
|
||||
return
|
||||
base_path = tempfile.mkdtemp()
|
||||
download_path = os.path.join(base_path, 'update.zip')
|
||||
update_path = os.path.join(base_path, 'update')
|
||||
downloaded = download_update(update_url, download_path, requests_session=requests_session, progress_callback=progress_callback)
|
||||
extracted = extract_update(downloaded, update_path, password=password)
|
||||
bootstrap_path = move_bootstrap(extracted)
|
||||
execute_bootstrap(bootstrap_path, extracted)
|
||||
logger.info("Update prepared for installation.")
|
||||
if callable(update_complete_callback):
|
||||
update_complete_callback()
|
||||
requests_session = create_requests_session(app_name=app_name, version=current_version)
|
||||
available_update = find_update(endpoint, requests_session=requests_session)
|
||||
if not available_update:
|
||||
logger.debug("No update available")
|
||||
return False
|
||||
available_version, available_description, update_url = find_version_data(update_type, current_version, available_update)
|
||||
if available_version == False:
|
||||
return False
|
||||
logger.info("A new update is available. Version %s" % available_version)
|
||||
if callable(update_available_callback) and not update_available_callback(version=available_version, description=available_description): #update_available_callback should return a falsy value to stop the process
|
||||
logger.info("User canceled update.")
|
||||
return
|
||||
base_path = tempfile.mkdtemp()
|
||||
download_path = os.path.join(base_path, 'update.zip')
|
||||
update_path = os.path.join(base_path, 'update')
|
||||
downloaded = download_update(update_url, download_path, requests_session=requests_session, progress_callback=progress_callback)
|
||||
extracted = extract_update(downloaded, update_path, password=password)
|
||||
bootstrap_path = move_bootstrap(extracted)
|
||||
execute_bootstrap(bootstrap_path, extracted)
|
||||
logger.info("Update prepared for installation.")
|
||||
if callable(update_complete_callback):
|
||||
update_complete_callback()
|
||||
|
||||
def create_requests_session(app_name=None, version=None):
|
||||
user_agent = ''
|
||||
session = requests.session()
|
||||
if app_name:
|
||||
user_agent = ' %s/%r' % (app_name, version)
|
||||
session.headers['User-Agent'] = session.headers['User-Agent'] + user_agent
|
||||
return session
|
||||
user_agent = ''
|
||||
session = requests.session()
|
||||
if app_name:
|
||||
user_agent = ' %s/%r' % (app_name, version)
|
||||
session.headers['User-Agent'] = session.headers['User-Agent'] + user_agent
|
||||
return session
|
||||
|
||||
def find_update(endpoint, requests_session):
|
||||
response = requests_session.get(endpoint)
|
||||
response.raise_for_status()
|
||||
content = response.json()
|
||||
return content
|
||||
response = requests_session.get(endpoint)
|
||||
response.raise_for_status()
|
||||
content = response.json()
|
||||
return content
|
||||
|
||||
def find_version_data(update_type, current_version, available_update):
|
||||
if update_type == "stable":
|
||||
available_version = float(available_update['current_version'])
|
||||
if not float(available_version) > float(current_version) or platform.system()+platform.architecture()[0][:2] not in available_update['downloads']:
|
||||
logger.debug("No update for this architecture")
|
||||
return (False, False, False)
|
||||
available_description = available_update.get('description', None)
|
||||
update_url = available_update ['downloads'][platform.system()+platform.architecture()[0][:2]]
|
||||
return (available_version, available_description, update_url)
|
||||
else: # Unstable versions, based in commits instead of version numbers.
|
||||
available_version = available_update["current_version"]
|
||||
if available_version == current_version:
|
||||
return (False, False, False)
|
||||
available_description = available_update["description"]
|
||||
update_url = available_update ['downloads'][platform.system()+platform.architecture()[0][:2]]
|
||||
return (available_version, available_description, update_url)
|
||||
if update_type == "stable":
|
||||
available_version = float(available_update['current_version'])
|
||||
if not float(available_version) > float(current_version) or platform.system()+platform.architecture()[0][:2] not in available_update['downloads']:
|
||||
logger.debug("No update for this architecture")
|
||||
return (False, False, False)
|
||||
available_description = available_update.get('description', None)
|
||||
update_url = available_update ['downloads'][platform.system()+platform.architecture()[0][:2]]
|
||||
return (available_version, available_description, update_url)
|
||||
else: # Unstable versions, based in commits instead of version numbers.
|
||||
available_version = available_update["current_version"]
|
||||
if available_version == current_version:
|
||||
return (False, False, False)
|
||||
available_description = available_update["description"]
|
||||
update_url = available_update ['downloads'][platform.system()+platform.architecture()[0][:2]]
|
||||
return (available_version, available_description, update_url)
|
||||
|
||||
def download_update(update_url, update_destination, requests_session, progress_callback=None, chunk_size=io.DEFAULT_BUFFER_SIZE):
|
||||
total_downloaded = total_size = 0
|
||||
with io.open(update_destination, 'w+b') as outfile:
|
||||
download = requests_session.get(update_url, stream=True)
|
||||
total_size = int(download.headers.get('content-length', 0))
|
||||
logger.debug("Total update size: %d" % total_size)
|
||||
download.raise_for_status()
|
||||
for chunk in download.iter_content(chunk_size):
|
||||
outfile.write(chunk)
|
||||
total_downloaded += len(chunk)
|
||||
if callable(progress_callback):
|
||||
call_callback(progress_callback, total_downloaded, total_size)
|
||||
logger.debug("Update downloaded")
|
||||
return update_destination
|
||||
total_downloaded = total_size = 0
|
||||
with io.open(update_destination, 'w+b') as outfile:
|
||||
download = requests_session.get(update_url, stream=True)
|
||||
total_size = int(download.headers.get('content-length', 0))
|
||||
logger.debug("Total update size: %d" % total_size)
|
||||
download.raise_for_status()
|
||||
for chunk in download.iter_content(chunk_size):
|
||||
outfile.write(chunk)
|
||||
total_downloaded += len(chunk)
|
||||
if callable(progress_callback):
|
||||
call_callback(progress_callback, total_downloaded, total_size)
|
||||
logger.debug("Update downloaded")
|
||||
return update_destination
|
||||
|
||||
def extract_update(update_archive, destination, password=None):
|
||||
"""Given an update archive, extracts it. Returns the directory to which it has been extracted"""
|
||||
with contextlib.closing(zipfile.ZipFile(update_archive)) as archive:
|
||||
if password:
|
||||
archive.setpassword(password)
|
||||
archive.extractall(path=destination)
|
||||
logger.debug("Update extracted")
|
||||
return destination
|
||||
"""Given an update archive, extracts it. Returns the directory to which it has been extracted"""
|
||||
with contextlib.closing(zipfile.ZipFile(update_archive)) as archive:
|
||||
if password:
|
||||
archive.setpassword(password)
|
||||
archive.extractall(path=destination)
|
||||
logger.debug("Update extracted")
|
||||
return destination
|
||||
|
||||
def move_bootstrap(extracted_path):
|
||||
working_path = os.path.abspath(os.path.join(extracted_path, '..'))
|
||||
if platform.system() == 'Darwin':
|
||||
extracted_path = os.path.join(extracted_path, 'Contents', 'Resources')
|
||||
downloaded_bootstrap = os.path.join(extracted_path, bootstrap_name())
|
||||
new_bootstrap_path = os.path.join(working_path, bootstrap_name())
|
||||
os.rename(downloaded_bootstrap, new_bootstrap_path)
|
||||
return new_bootstrap_path
|
||||
working_path = os.path.abspath(os.path.join(extracted_path, '..'))
|
||||
if platform.system() == 'Darwin':
|
||||
extracted_path = os.path.join(extracted_path, 'Contents', 'Resources')
|
||||
downloaded_bootstrap = os.path.join(extracted_path, bootstrap_name())
|
||||
new_bootstrap_path = os.path.join(working_path, bootstrap_name())
|
||||
os.rename(downloaded_bootstrap, new_bootstrap_path)
|
||||
return new_bootstrap_path
|
||||
|
||||
def execute_bootstrap(bootstrap_path, source_path):
|
||||
arguments = r'"%s" "%s" "%s" "%s"' % (os.getpid(), source_path, paths.app_path(), paths.get_executable())
|
||||
if platform.system() == 'Windows':
|
||||
import win32api
|
||||
win32api.ShellExecute(0, 'open', bootstrap_path, arguments, '', 5)
|
||||
else:
|
||||
import subprocess
|
||||
make_executable(bootstrap_path)
|
||||
subprocess.Popen(['%s %s' % (bootstrap_path, arguments)], shell=True)
|
||||
logger.info("Bootstrap executed")
|
||||
arguments = r'"%s" "%s" "%s" "%s"' % (os.getpid(), source_path, paths.app_path(), paths.get_executable())
|
||||
if platform.system() == 'Windows':
|
||||
import win32api
|
||||
win32api.ShellExecute(0, 'open', bootstrap_path, arguments, '', 5)
|
||||
else:
|
||||
import subprocess
|
||||
make_executable(bootstrap_path)
|
||||
subprocess.Popen(['%s %s' % (bootstrap_path, arguments)], shell=True)
|
||||
logger.info("Bootstrap executed")
|
||||
|
||||
def bootstrap_name():
|
||||
if platform.system() == 'Windows': return 'bootstrap.exe'
|
||||
if platform.system() == 'Darwin': return 'bootstrap-mac.sh'
|
||||
return 'bootstrap-lin.sh'
|
||||
if platform.system() == 'Windows': return 'bootstrap.exe'
|
||||
if platform.system() == 'Darwin': return 'bootstrap-mac.sh'
|
||||
return 'bootstrap-lin.sh'
|
||||
|
||||
def make_executable(path):
|
||||
import stat
|
||||
st = os.stat(path)
|
||||
os.chmod(path, st.st_mode | stat.S_IEXEC)
|
||||
import stat
|
||||
st = os.stat(path)
|
||||
os.chmod(path, st.st_mode | stat.S_IEXEC)
|
||||
|
||||
def call_callback(callback, *args, **kwargs):
|
||||
# try:
|
||||
callback(*args, **kwargs)
|
||||
callback(*args, **kwargs)
|
||||
# except:
|
||||
# logger.exception("Failed calling callback %r with args %r and kwargs %r" % (callback, args, kwargs))
|
||||
|
@ -9,12 +9,12 @@ from .wxUpdater import *
|
||||
logger = logging.getLogger("updater")
|
||||
|
||||
def do_update(update_type="alpha"):
|
||||
# Updates cannot be performed in the source code version.
|
||||
if hasattr(sys, "frozen") == False:
|
||||
return
|
||||
endpoint = application.update_url
|
||||
version = application.update_next_version
|
||||
try:
|
||||
return update.perform_update(endpoint=endpoint, current_version=version, app_name=application.name, update_type=update_type, update_available_callback=available_update_dialog, progress_callback=progress_callback, update_complete_callback=update_finished)
|
||||
except ConnectionError:
|
||||
logger.exception("Update failed.")
|
||||
# Updates cannot be performed in the source code version.
|
||||
if hasattr(sys, "frozen") == False:
|
||||
return
|
||||
endpoint = application.update_url
|
||||
version = application.update_next_version
|
||||
try:
|
||||
return update.perform_update(endpoint=endpoint, current_version=version, app_name=application.name, update_type=update_type, update_available_callback=available_update_dialog, progress_callback=progress_callback, update_complete_callback=update_finished)
|
||||
except ConnectionError:
|
||||
logger.exception("Update failed.")
|
||||
|
@ -2,42 +2,42 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
def convert_bytes(n):
|
||||
K, M, G, T, P = 1 << 10, 1 << 20, 1 << 30, 1 << 40, 1 << 50
|
||||
if n >= P:
|
||||
return '%.2fPb' % (float(n) / T)
|
||||
elif n >= T:
|
||||
return '%.2fTb' % (float(n) / T)
|
||||
elif n >= G:
|
||||
return '%.2fGb' % (float(n) / G)
|
||||
elif n >= M:
|
||||
return '%.2fMb' % (float(n) / M)
|
||||
elif n >= K:
|
||||
return '%.2fKb' % (float(n) / K)
|
||||
else:
|
||||
return '%d' % n
|
||||
K, M, G, T, P = 1 << 10, 1 << 20, 1 << 30, 1 << 40, 1 << 50
|
||||
if n >= P:
|
||||
return '%.2fPb' % (float(n) / T)
|
||||
elif n >= T:
|
||||
return '%.2fTb' % (float(n) / T)
|
||||
elif n >= G:
|
||||
return '%.2fGb' % (float(n) / G)
|
||||
elif n >= M:
|
||||
return '%.2fMb' % (float(n) / M)
|
||||
elif n >= K:
|
||||
return '%.2fKb' % (float(n) / K)
|
||||
else:
|
||||
return '%d' % n
|
||||
|
||||
def seconds_to_string(seconds, precision=0):
|
||||
day = seconds // 86400
|
||||
hour = seconds // 3600
|
||||
min = (seconds // 60) % 60
|
||||
sec = seconds - (hour * 3600) - (min * 60)
|
||||
sec_spec = "." + str(precision) + "f"
|
||||
sec_string = sec.__format__(sec_spec)
|
||||
string = ""
|
||||
if day == 1:
|
||||
string += _("%d day, ") % day
|
||||
elif day >= 2:
|
||||
string += _("%d days, ") % day
|
||||
if (hour == 1):
|
||||
string += _("%d hour, ") % hour
|
||||
elif (hour >= 2):
|
||||
string += _("%d hours, ") % hour
|
||||
if (min == 1):
|
||||
string += _("%d minute, ") % min
|
||||
elif (min >= 2):
|
||||
string += _("%d minutes, ") % min
|
||||
if sec >= 0 and sec <= 2:
|
||||
string += _("%s second") % sec_string
|
||||
else:
|
||||
string += _("%s seconds") % sec_string
|
||||
return string
|
||||
day = seconds // 86400
|
||||
hour = seconds // 3600
|
||||
min = (seconds // 60) % 60
|
||||
sec = seconds - (hour * 3600) - (min * 60)
|
||||
sec_spec = "." + str(precision) + "f"
|
||||
sec_string = sec.__format__(sec_spec)
|
||||
string = ""
|
||||
if day == 1:
|
||||
string += _("%d day, ") % day
|
||||
elif day >= 2:
|
||||
string += _("%d days, ") % day
|
||||
if (hour == 1):
|
||||
string += _("%d hour, ") % hour
|
||||
elif (hour >= 2):
|
||||
string += _("%d hours, ") % hour
|
||||
if (min == 1):
|
||||
string += _("%d minute, ") % min
|
||||
elif (min >= 2):
|
||||
string += _("%d minutes, ") % min
|
||||
if sec >= 0 and sec <= 2:
|
||||
string += _("%s second") % sec_string
|
||||
else:
|
||||
string += _("%s seconds") % sec_string
|
||||
return string
|
||||
|
@ -8,27 +8,27 @@ from . import utils
|
||||
progress_dialog = None
|
||||
|
||||
def available_update_dialog(version, description):
|
||||
dialog = wx.MessageDialog(None, _("There's a new {app_name} version available. Would you like to download it now?\n\n {app_name} version: {app_version}\n\nChanges:\n{changes}").format(app_name=application.name, app_version=version, changes=description), _("New version for %s") % application.name, style=wx.YES|wx.NO|wx.ICON_WARNING)
|
||||
if dialog.ShowModal() == wx.ID_YES:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
dialog = wx.MessageDialog(None, _("There's a new {app_name} version available. Would you like to download it now?\n\n {app_name} version: {app_version}\n\nChanges:\n{changes}").format(app_name=application.name, app_version=version, changes=description), _("New version for %s") % application.name, style=wx.YES|wx.NO|wx.ICON_WARNING)
|
||||
if dialog.ShowModal() == wx.ID_YES:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def create_progress_dialog():
|
||||
return wx.ProgressDialog(_("Download in Progress"), _("Downloading the new version..."), parent=None, maximum=100)
|
||||
return wx.ProgressDialog(_("Download in Progress"), _("Downloading the new version..."), parent=None, maximum=100)
|
||||
|
||||
def progress_callback(total_downloaded, total_size):
|
||||
wx.CallAfter(_progress_callback, total_downloaded, total_size)
|
||||
wx.CallAfter(_progress_callback, total_downloaded, total_size)
|
||||
|
||||
def _progress_callback(total_downloaded, total_size):
|
||||
global progress_dialog
|
||||
if progress_dialog == None:
|
||||
progress_dialog = create_progress_dialog()
|
||||
progress_dialog.Show()
|
||||
if total_downloaded == total_size:
|
||||
progress_dialog.Destroy()
|
||||
else:
|
||||
progress_dialog.Update((total_downloaded*100)/total_size, _("Updating... {total_transferred} of {total_size}").format(total_transferred=utils.convert_bytes(total_downloaded), total_size=utils.convert_bytes(total_size)))
|
||||
global progress_dialog
|
||||
if progress_dialog == None:
|
||||
progress_dialog = create_progress_dialog()
|
||||
progress_dialog.Show()
|
||||
if total_downloaded == total_size:
|
||||
progress_dialog.Destroy()
|
||||
else:
|
||||
progress_dialog.Update((total_downloaded*100)/total_size, _("Updating... {total_transferred} of {total_size}").format(total_transferred=utils.convert_bytes(total_downloaded), total_size=utils.convert_bytes(total_size)))
|
||||
|
||||
def update_finished():
|
||||
return wx.MessageDialog(None, _("The update has been downloaded and installed successfully. Press OK to continue."), _("Done!")).ShowModal()
|
||||
return wx.MessageDialog(None, _("The update has been downloaded and installed successfully. Press OK to continue."), _("Done!")).ShowModal()
|
||||
|
164
src/utils.py
164
src/utils.py
@ -11,99 +11,99 @@ from pubsub import pub
|
||||
log = logging.getLogger("utils")
|
||||
|
||||
def call_threaded(func, *args, **kwargs):
|
||||
#Call the given function in a daemonized thread and return the thread.
|
||||
def new_func(*a, **k):
|
||||
func(*a, **k)
|
||||
thread = threading.Thread(target=new_func, args=args, kwargs=kwargs)
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
return thread
|
||||
#Call the given function in a daemonized thread and return the thread.
|
||||
def new_func(*a, **k):
|
||||
func(*a, **k)
|
||||
thread = threading.Thread(target=new_func, args=args, kwargs=kwargs)
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
return thread
|
||||
|
||||
class RepeatingTimer(threading.Thread):
|
||||
"""Call a function after a specified number of seconds, it will then repeat again after the specified number of seconds
|
||||
Note: If the function provided takes time to execute, this time is NOT taken from the next wait period
|
||||
"""Call a function after a specified number of seconds, it will then repeat again after the specified number of seconds
|
||||
Note: If the function provided takes time to execute, this time is NOT taken from the next wait period
|
||||
|
||||
t = RepeatingTimer(30.0, f, args=[], kwargs={})
|
||||
t.start()
|
||||
t.cancel() # stop the timer's actions
|
||||
"""
|
||||
t = RepeatingTimer(30.0, f, args=[], kwargs={})
|
||||
t.start()
|
||||
t.cancel() # stop the timer's actions
|
||||
"""
|
||||
|
||||
def __init__(self, interval, function, daemon=True, *args, **kwargs):
|
||||
threading.Thread.__init__(self)
|
||||
self.daemon = daemon
|
||||
self.interval = float(interval)
|
||||
self.function = function
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
self.finished = threading.Event()
|
||||
def __init__(self, interval, function, daemon=True, *args, **kwargs):
|
||||
threading.Thread.__init__(self)
|
||||
self.daemon = daemon
|
||||
self.interval = float(interval)
|
||||
self.function = function
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
self.finished = threading.Event()
|
||||
|
||||
def cancel(self):
|
||||
"""Stop the timer if it hasn't finished yet"""
|
||||
log.debug("Stopping repeater for %s" % (self.function,))
|
||||
self.finished.set()
|
||||
stop = cancel
|
||||
def cancel(self):
|
||||
"""Stop the timer if it hasn't finished yet"""
|
||||
log.debug("Stopping repeater for %s" % (self.function,))
|
||||
self.finished.set()
|
||||
stop = cancel
|
||||
|
||||
def run(self):
|
||||
while not self.finished.is_set():
|
||||
self.finished.wait(self.interval)
|
||||
if not self.finished.is_set(): #In case someone has canceled while waiting
|
||||
try:
|
||||
self.function(*self.args, **self.kwargs)
|
||||
except:
|
||||
log.exception("Execution failed. Function: %r args: %r and kwargs: %r" % (self.function, self.args, self.kwargs))
|
||||
def run(self):
|
||||
while not self.finished.is_set():
|
||||
self.finished.wait(self.interval)
|
||||
if not self.finished.is_set(): #In case someone has canceled while waiting
|
||||
try:
|
||||
self.function(*self.args, **self.kwargs)
|
||||
except:
|
||||
log.exception("Execution failed. Function: %r args: %r and kwargs: %r" % (self.function, self.args, self.kwargs))
|
||||
|
||||
def download_file(url, local_filename, metadata=dict()):
|
||||
log.debug("Download started: filename={0}, url={1}".format(local_filename, url))
|
||||
r = requests.get(url, stream=True)
|
||||
pub.sendMessage("change_status", status=_(u"Downloading {0}.").format(local_filename,))
|
||||
total_length = r.headers.get("content-length")
|
||||
dl = 0
|
||||
total_length = int(total_length)
|
||||
log.debug("Downloading file of {0} bytes".format(total_length))
|
||||
with open(local_filename, 'wb') as f:
|
||||
for chunk in r.iter_content(chunk_size=512*1024):
|
||||
if chunk: # filter out keep-alive new chunks
|
||||
dl += len(chunk)
|
||||
f.write(chunk)
|
||||
done = int(100 * dl / total_length)
|
||||
msg = _(u"Downloading {0} ({1}%).").format(os.path.basename(local_filename), done)
|
||||
pub.sendMessage("change_status", status=msg)
|
||||
pub.sendMessage("update-progress", value=done)
|
||||
pub.sendMessage("download_finished", file=os.path.basename(local_filename))
|
||||
log.debug("Download finished successfully")
|
||||
apply_metadata(local_filename, metadata)
|
||||
return local_filename
|
||||
log.debug("Download started: filename={0}, url={1}".format(local_filename, url))
|
||||
r = requests.get(url, stream=True)
|
||||
pub.sendMessage("change_status", status=_(u"Downloading {0}.").format(local_filename,))
|
||||
total_length = r.headers.get("content-length")
|
||||
dl = 0
|
||||
total_length = int(total_length)
|
||||
log.debug("Downloading file of {0} bytes".format(total_length))
|
||||
with open(local_filename, 'wb') as f:
|
||||
for chunk in r.iter_content(chunk_size=512*1024):
|
||||
if chunk: # filter out keep-alive new chunks
|
||||
dl += len(chunk)
|
||||
f.write(chunk)
|
||||
done = int(100 * dl / total_length)
|
||||
msg = _(u"Downloading {0} ({1}%).").format(os.path.basename(local_filename), done)
|
||||
pub.sendMessage("change_status", status=msg)
|
||||
pub.sendMessage("update-progress", value=done)
|
||||
pub.sendMessage("download_finished", file=os.path.basename(local_filename))
|
||||
log.debug("Download finished successfully")
|
||||
apply_metadata(local_filename, metadata)
|
||||
return local_filename
|
||||
|
||||
def get_services(import_all=False):
|
||||
""" Function for importing everything wich is located in the services package and has a class named interface."""
|
||||
module_type = types.ModuleType
|
||||
# first of all, import all classes for the package so we can reload everything if they have changes in config.
|
||||
_classes = [m for m in services.__dict__.values() if type(m) == module_type and hasattr(m, 'interface')]
|
||||
for cls in _classes:
|
||||
reload(cls)
|
||||
if not import_all:
|
||||
classes = [m for m in services.__dict__.values() if type(m) == module_type and hasattr(m, 'interface') and m.interface.enabled != False]
|
||||
else:
|
||||
classes = [m for m in services.__dict__.values() if type(m) == module_type and hasattr(m, 'interface')]
|
||||
return classes
|
||||
""" Function for importing everything wich is located in the services package and has a class named interface."""
|
||||
module_type = types.ModuleType
|
||||
# first of all, import all classes for the package so we can reload everything if they have changes in config.
|
||||
_classes = [m for m in services.__dict__.values() if type(m) == module_type and hasattr(m, 'interface')]
|
||||
for cls in _classes:
|
||||
reload(cls)
|
||||
if not import_all:
|
||||
classes = [m for m in services.__dict__.values() if type(m) == module_type and hasattr(m, 'interface') and m.interface.enabled != False]
|
||||
else:
|
||||
classes = [m for m in services.__dict__.values() if type(m) == module_type and hasattr(m, 'interface')]
|
||||
return classes
|
||||
|
||||
def apply_metadata(local_filename, metadata):
|
||||
if local_filename.endswith(".mp3"):
|
||||
from mutagen.easyid3 import EasyID3 as metadataeditor
|
||||
elif local_filename.endswith(".flac"):
|
||||
from mutagen.flac import FLAC as metadataeditor
|
||||
elif local_filename.endswith(".m4a"):
|
||||
from mutagen.mp4 import MP4 as metadataeditor
|
||||
audio = metadataeditor(local_filename)
|
||||
if local_filename.endswith(".m4a") == False:
|
||||
for k in metadata.keys():
|
||||
audio[k] = metadata[k]
|
||||
else:
|
||||
audio["\xa9nam"] = metadata["title"]
|
||||
audio["\xa9alb"] = metadata["album"]
|
||||
audio["\xa9ART"] = metadata["artist"]
|
||||
audio.save()
|
||||
if local_filename.endswith(".mp3"):
|
||||
from mutagen.easyid3 import EasyID3 as metadataeditor
|
||||
elif local_filename.endswith(".flac"):
|
||||
from mutagen.flac import FLAC as metadataeditor
|
||||
elif local_filename.endswith(".m4a"):
|
||||
from mutagen.mp4 import MP4 as metadataeditor
|
||||
audio = metadataeditor(local_filename)
|
||||
if local_filename.endswith(".m4a") == False:
|
||||
for k in metadata.keys():
|
||||
audio[k] = metadata[k]
|
||||
else:
|
||||
audio["\xa9nam"] = metadata["title"]
|
||||
audio["\xa9alb"] = metadata["album"]
|
||||
audio["\xa9ART"] = metadata["artist"]
|
||||
audio.save()
|
||||
|
||||
def safe_filename(filename):
|
||||
allowed_symbols = ["_", ".", ",", "-", "(", ")"]
|
||||
return "".join([c for c in filename if c.isalpha() or c.isdigit() or c==' ' or c in allowed_symbols]).rstrip()
|
||||
allowed_symbols = ["_", ".", ",", "-", "(", ")"]
|
||||
return "".join([c for c in filename if c.isalpha() or c.isdigit() or c==' ' or c in allowed_symbols]).rstrip()
|
||||
|
@ -55,122 +55,122 @@ LISTBOX_CHANGED = wx.EVT_LISTBOX
|
||||
LISTBOX_ITEM_ACTIVATED = wx.EVT_LIST_ITEM_ACTIVATED
|
||||
|
||||
def exit_application():
|
||||
""" Closes the current window cleanly. """
|
||||
wx.GetApp().ExitMainLoop()
|
||||
""" Closes the current window cleanly. """
|
||||
wx.GetApp().ExitMainLoop()
|
||||
|
||||
def connect_event(parent, event, func, menuitem=None, *args, **kwargs):
|
||||
""" Connects an event to a function.
|
||||
parent wx.window: The widget that will listen for the event.
|
||||
event widgetUtils.event: The event that will be listened for the parent. The event should be one of the widgetUtils events.
|
||||
function func: The function that will be connected to the event."""
|
||||
if menuitem == None:
|
||||
return getattr(parent, "Bind")(event, func, *args, **kwargs)
|
||||
else:
|
||||
return getattr(parent, "Bind")(event, func, menuitem, *args, **kwargs)
|
||||
""" Connects an event to a function.
|
||||
parent wx.window: The widget that will listen for the event.
|
||||
event widgetUtils.event: The event that will be listened for the parent. The event should be one of the widgetUtils events.
|
||||
function func: The function that will be connected to the event."""
|
||||
if menuitem == None:
|
||||
return getattr(parent, "Bind")(event, func, *args, **kwargs)
|
||||
else:
|
||||
return getattr(parent, "Bind")(event, func, menuitem, *args, **kwargs)
|
||||
|
||||
def connectExitFunction(exitFunction):
|
||||
""" This connect the events in WX when an user is turning off the machine."""
|
||||
wx.GetApp().Bind(wx.EVT_QUERY_END_SESSION, exitFunction)
|
||||
wx.GetApp().Bind(wx.EVT_END_SESSION, exitFunction)
|
||||
""" This connect the events in WX when an user is turning off the machine."""
|
||||
wx.GetApp().Bind(wx.EVT_QUERY_END_SESSION, exitFunction)
|
||||
wx.GetApp().Bind(wx.EVT_END_SESSION, exitFunction)
|
||||
|
||||
class BaseDialog(wx.Dialog):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(BaseDialog, self).__init__(*args, **kwargs)
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(BaseDialog, self).__init__(*args, **kwargs)
|
||||
|
||||
def get_response(self):
|
||||
return self.ShowModal()
|
||||
def get_response(self):
|
||||
return self.ShowModal()
|
||||
|
||||
def get(self, control):
|
||||
if hasattr(self, control):
|
||||
control = getattr(self, control)
|
||||
if hasattr(control, "GetValue"): return getattr(control, "GetValue")()
|
||||
elif hasattr(control, "GetLabel"): return getattr(control, "GetLabel")()
|
||||
else: return -1
|
||||
else: return 0
|
||||
def get(self, control):
|
||||
if hasattr(self, control):
|
||||
control = getattr(self, control)
|
||||
if hasattr(control, "GetValue"): return getattr(control, "GetValue")()
|
||||
elif hasattr(control, "GetLabel"): return getattr(control, "GetLabel")()
|
||||
else: return -1
|
||||
else: return 0
|
||||
|
||||
def set(self, control, text):
|
||||
if hasattr(self, control):
|
||||
control = getattr(self, control)
|
||||
if hasattr(control, "SetValue"): return getattr(control, "SetValue")(text)
|
||||
elif hasattr(control, "SetLabel"): return getattr(control, "SetLabel")(text)
|
||||
elif hasattr(control, "ChangeValue"): return getattr(control, "ChangeValue")(text)
|
||||
else: return -1
|
||||
else: return 0
|
||||
def set(self, control, text):
|
||||
if hasattr(self, control):
|
||||
control = getattr(self, control)
|
||||
if hasattr(control, "SetValue"): return getattr(control, "SetValue")(text)
|
||||
elif hasattr(control, "SetLabel"): return getattr(control, "SetLabel")(text)
|
||||
elif hasattr(control, "ChangeValue"): return getattr(control, "ChangeValue")(text)
|
||||
else: return -1
|
||||
else: return 0
|
||||
|
||||
def destroy(self):
|
||||
self.Destroy()
|
||||
def destroy(self):
|
||||
self.Destroy()
|
||||
|
||||
def set_title(self, title):
|
||||
self.SetTitle(title)
|
||||
def set_title(self, title):
|
||||
self.SetTitle(title)
|
||||
|
||||
def get_title(self):
|
||||
return self.GetTitle()
|
||||
def get_title(self):
|
||||
return self.GetTitle()
|
||||
|
||||
def enable(self, control):
|
||||
getattr(self, control).Enable(True)
|
||||
def enable(self, control):
|
||||
getattr(self, control).Enable(True)
|
||||
|
||||
def disable(self, control):
|
||||
getattr(self, control).Enable(False)
|
||||
def disable(self, control):
|
||||
getattr(self, control).Enable(False)
|
||||
|
||||
class mainLoopObject(wx.App):
|
||||
|
||||
def __init__(self):
|
||||
self.app = wx.App()
|
||||
# self.lc = wx.Locale()
|
||||
# lang=languageHandler.getLanguage()
|
||||
# wxLang=self.lc.FindLanguageInfo(lang)
|
||||
# if not wxLang and '_' in lang:
|
||||
# wxLang=self.lc.FindLanguageInfo(lang.split('_')[0])
|
||||
# if hasattr(sys,'frozen'):
|
||||
# self.lc.AddCatalogLookupPathPrefix(paths.app_path("locales"))
|
||||
# if wxLang:
|
||||
# self.lc.Init(wxLang.Language)
|
||||
def __init__(self):
|
||||
self.app = wx.App()
|
||||
# self.lc = wx.Locale()
|
||||
# lang=languageHandler.getLanguage()
|
||||
# wxLang=self.lc.FindLanguageInfo(lang)
|
||||
# if not wxLang and '_' in lang:
|
||||
# wxLang=self.lc.FindLanguageInfo(lang.split('_')[0])
|
||||
# if hasattr(sys,'frozen'):
|
||||
# self.lc.AddCatalogLookupPathPrefix(paths.app_path("locales"))
|
||||
# if wxLang:
|
||||
# self.lc.Init(wxLang.Language)
|
||||
|
||||
def run(self):
|
||||
self.app.MainLoop()
|
||||
def run(self):
|
||||
self.app.MainLoop()
|
||||
|
||||
class list(object):
|
||||
def __init__(self, parent, *columns, **listArguments):
|
||||
self.columns = columns
|
||||
self.listArguments = listArguments
|
||||
self.create_list(parent)
|
||||
def __init__(self, parent, *columns, **listArguments):
|
||||
self.columns = columns
|
||||
self.listArguments = listArguments
|
||||
self.create_list(parent)
|
||||
|
||||
def set_windows_size(self, column, characters_max):
|
||||
self.list.SetColumnWidth(column, characters_max*2)
|
||||
def set_windows_size(self, column, characters_max):
|
||||
self.list.SetColumnWidth(column, characters_max*2)
|
||||
|
||||
def set_size(self):
|
||||
self.list.SetSize((self.list.GetBestSize()[0], 1000))
|
||||
def set_size(self):
|
||||
self.list.SetSize((self.list.GetBestSize()[0], 1000))
|
||||
|
||||
def create_list(self, parent):
|
||||
self.list = wx.ListCtrl(parent, -1, **self.listArguments)
|
||||
for i in range(0, len(self.columns)):
|
||||
self.list.InsertColumn(i, u"%s" % (self.columns[i]))
|
||||
def create_list(self, parent):
|
||||
self.list = wx.ListCtrl(parent, -1, **self.listArguments)
|
||||
for i in range(0, len(self.columns)):
|
||||
self.list.InsertColumn(i, u"%s" % (self.columns[i]))
|
||||
|
||||
def insert_item(self, reversed, *item):
|
||||
""" Inserts an item on the list."""
|
||||
if reversed == False: items = self.list.GetItemCount()
|
||||
else: items = 0
|
||||
self.list.InsertItem(items, item[0])
|
||||
for i in range(1, len(self.columns)):
|
||||
self.list.SetItem(items, i, item[i])
|
||||
def insert_item(self, reversed, *item):
|
||||
""" Inserts an item on the list."""
|
||||
if reversed == False: items = self.list.GetItemCount()
|
||||
else: items = 0
|
||||
self.list.InsertItem(items, item[0])
|
||||
for i in range(1, len(self.columns)):
|
||||
self.list.SetItem(items, i, item[i])
|
||||
|
||||
def remove_item(self, pos):
|
||||
""" Deletes an item from the list."""
|
||||
if pos > 0: self.list.Focus(pos-1)
|
||||
self.list.DeleteItem(pos)
|
||||
def remove_item(self, pos):
|
||||
""" Deletes an item from the list."""
|
||||
if pos > 0: self.list.Focus(pos-1)
|
||||
self.list.DeleteItem(pos)
|
||||
|
||||
def clear(self):
|
||||
self.list.DeleteAllItems()
|
||||
def clear(self):
|
||||
self.list.DeleteAllItems()
|
||||
|
||||
def get_selected(self):
|
||||
return self.list.GetFocusedItem()
|
||||
def get_selected(self):
|
||||
return self.list.GetFocusedItem()
|
||||
|
||||
def select_item(self, pos):
|
||||
self.list.Focus(pos)
|
||||
def select_item(self, pos):
|
||||
self.list.Focus(pos)
|
||||
|
||||
def get_count(self):
|
||||
selected = self.list.GetItemCount()
|
||||
if selected == -1:
|
||||
return 0
|
||||
else:
|
||||
return selected
|
||||
def get_count(self):
|
||||
selected = self.list.GetItemCount()
|
||||
if selected == -1:
|
||||
return 0
|
||||
else:
|
||||
return selected
|
||||
|
@ -3,49 +3,49 @@ import wx
|
||||
import widgetUtils
|
||||
|
||||
class general(wx.Panel, widgetUtils.BaseDialog):
|
||||
def __init__(self, panel, output_devices=[]):
|
||||
super(general, self).__init__(panel)
|
||||
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)
|
||||
def __init__(self, panel, output_devices=[]):
|
||||
super(general, self).__init__(panel)
|
||||
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)
|
||||
|
||||
class configurationDialog(widgetUtils.BaseDialog):
|
||||
|
||||
def __init__(self, title):
|
||||
super(configurationDialog, self).__init__(None, -1, title=title)
|
||||
self.panel = wx.Panel(self)
|
||||
self.sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.notebook = wx.Treebook(self.panel)
|
||||
def __init__(self, title):
|
||||
super(configurationDialog, self).__init__(None, -1, title=title)
|
||||
self.panel = wx.Panel(self)
|
||||
self.sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.notebook = wx.Treebook(self.panel)
|
||||
|
||||
def create_general(self, output_devices=[]):
|
||||
self.general = general(self.notebook, output_devices=output_devices)
|
||||
self.notebook.AddPage(self.general, _("General"))
|
||||
self.general.SetFocus()
|
||||
def create_general(self, output_devices=[]):
|
||||
self.general = general(self.notebook, output_devices=output_devices)
|
||||
self.notebook.AddPage(self.general, _("General"))
|
||||
self.general.SetFocus()
|
||||
|
||||
def realize(self):
|
||||
self.notebook.AddPage(wx.Panel(self.notebook, wx.NewId()), _("Services"))
|
||||
self.sizer.Add(self.notebook, 0, wx.ALL, 5)
|
||||
ok_cancel_box = wx.BoxSizer(wx.HORIZONTAL)
|
||||
ok = wx.Button(self.panel, wx.ID_OK, _("Save"))
|
||||
ok.SetDefault()
|
||||
cancel = wx.Button(self.panel, wx.ID_CANCEL, _("Close"))
|
||||
self.SetEscapeId(cancel.GetId())
|
||||
ok_cancel_box.Add(ok, 0, wx.ALL, 5)
|
||||
ok_cancel_box.Add(cancel, 0, wx.ALL, 5)
|
||||
self.sizer.Add(ok_cancel_box, 0, wx.ALL, 5)
|
||||
self.panel.SetSizer(self.sizer)
|
||||
self.SetClientSize(self.sizer.CalcMin())
|
||||
def realize(self):
|
||||
self.notebook.AddPage(wx.Panel(self.notebook, wx.NewId()), _("Services"))
|
||||
self.sizer.Add(self.notebook, 0, wx.ALL, 5)
|
||||
ok_cancel_box = wx.BoxSizer(wx.HORIZONTAL)
|
||||
ok = wx.Button(self.panel, wx.ID_OK, _("Save"))
|
||||
ok.SetDefault()
|
||||
cancel = wx.Button(self.panel, wx.ID_CANCEL, _("Close"))
|
||||
self.SetEscapeId(cancel.GetId())
|
||||
ok_cancel_box.Add(ok, 0, wx.ALL, 5)
|
||||
ok_cancel_box.Add(cancel, 0, wx.ALL, 5)
|
||||
self.sizer.Add(ok_cancel_box, 0, wx.ALL, 5)
|
||||
self.panel.SetSizer(self.sizer)
|
||||
self.SetClientSize(self.sizer.CalcMin())
|
||||
|
||||
def get_value(self, panel, key):
|
||||
p = getattr(self, panel)
|
||||
return getattr(p, key).GetValue()
|
||||
def get_value(self, panel, key):
|
||||
p = getattr(self, panel)
|
||||
return getattr(p, key).GetValue()
|
||||
|
||||
def set_value(self, panel, key, value):
|
||||
p = getattr(self, panel)
|
||||
control = getattr(p, key)
|
||||
getattr(control, "SetValue")(value)
|
||||
def set_value(self, panel, key, value):
|
||||
p = getattr(self, panel)
|
||||
control = getattr(p, key)
|
||||
getattr(control, "SetValue")(value)
|
||||
|
@ -2,123 +2,123 @@
|
||||
from __future__ import unicode_literals # at top of module
|
||||
import wx
|
||||
try:
|
||||
import wx.adv
|
||||
import wx.adv
|
||||
except ImportError:
|
||||
pass
|
||||
pass
|
||||
import application
|
||||
import widgetUtils
|
||||
|
||||
class mainWindow(wx.Frame):
|
||||
def makeMenu(self):
|
||||
mb = wx.MenuBar()
|
||||
app_ = wx.Menu()
|
||||
self.settings = app_.Append(wx.NewId(), _("Settings"))
|
||||
mb.Append(app_, _("Application"))
|
||||
player = wx.Menu()
|
||||
self.player_play = player.Append(wx.NewId(), _(u"Play"))
|
||||
self.player_stop = player.Append(wx.NewId(), _(u"Stop"))
|
||||
self.player_previous = player.Append(wx.NewId(), _(u"Previous"))
|
||||
self.player_next = player.Append(wx.NewId(), _(u"Next"))
|
||||
self.player_shuffle = player.AppendCheckItem(wx.NewId(), _(u"Shuffle"))
|
||||
self.player_volume_down = player.Append(wx.NewId(), _(u"Volume down"))
|
||||
self.player_volume_up = player.Append(wx.NewId(), _(u"Volume up"))
|
||||
self.player_mute = player.Append(wx.NewId(), _(u"Mute"))
|
||||
help_ = wx.Menu()
|
||||
self.about = help_.Append(wx.NewId(), _(u"About {0}").format(application.name,))
|
||||
self.check_for_updates = help_.Append(wx.NewId(), _(u"Check for updates"))
|
||||
self.changelog = help_.Append(wx.NewId(), _(u"What's new in this version?"))
|
||||
self.website = help_.Append(wx.NewId(), _(u"Visit website"))
|
||||
self.report = help_.Append(wx.NewId(), _(u"Report an error"))
|
||||
mb.Append(player, _(u"Player"))
|
||||
mb.Append(help_, _(u"Help"))
|
||||
self.SetMenuBar(mb)
|
||||
def makeMenu(self):
|
||||
mb = wx.MenuBar()
|
||||
app_ = wx.Menu()
|
||||
self.settings = app_.Append(wx.NewId(), _("Settings"))
|
||||
mb.Append(app_, _("Application"))
|
||||
player = wx.Menu()
|
||||
self.player_play = player.Append(wx.NewId(), _(u"Play"))
|
||||
self.player_stop = player.Append(wx.NewId(), _(u"Stop"))
|
||||
self.player_previous = player.Append(wx.NewId(), _(u"Previous"))
|
||||
self.player_next = player.Append(wx.NewId(), _(u"Next"))
|
||||
self.player_shuffle = player.AppendCheckItem(wx.NewId(), _(u"Shuffle"))
|
||||
self.player_volume_down = player.Append(wx.NewId(), _(u"Volume down"))
|
||||
self.player_volume_up = player.Append(wx.NewId(), _(u"Volume up"))
|
||||
self.player_mute = player.Append(wx.NewId(), _(u"Mute"))
|
||||
help_ = wx.Menu()
|
||||
self.about = help_.Append(wx.NewId(), _(u"About {0}").format(application.name,))
|
||||
self.check_for_updates = help_.Append(wx.NewId(), _(u"Check for updates"))
|
||||
self.changelog = help_.Append(wx.NewId(), _(u"What's new in this version?"))
|
||||
self.website = help_.Append(wx.NewId(), _(u"Visit website"))
|
||||
self.report = help_.Append(wx.NewId(), _(u"Report an error"))
|
||||
mb.Append(player, _(u"Player"))
|
||||
mb.Append(help_, _(u"Help"))
|
||||
self.SetMenuBar(mb)
|
||||
|
||||
def __init__(self, extractors=[]):
|
||||
super(mainWindow, self).__init__(parent=None, id=wx.NewId(), title=application.name)
|
||||
self.Maximize(True)
|
||||
self.makeMenu()
|
||||
self.panel = wx.Panel(self)
|
||||
self.sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.sb = self.CreateStatusBar()
|
||||
lbl2 = wx.StaticText(self.panel, wx.NewId(), _(u"S&earch"))
|
||||
self.text = wx.TextCtrl(self.panel, wx.NewId())
|
||||
box = wx.BoxSizer(wx.HORIZONTAL)
|
||||
box.Add(lbl2, 0, wx.GROW)
|
||||
box.Add(self.text, 1, wx.GROW)
|
||||
box.Add(wx.StaticText(self.panel, wx.NewId(), _(u"&Search in")), 0, wx.GROW)
|
||||
self.extractor = wx.ComboBox(self.panel, wx.NewId(), choices=extractors, value=extractors[0], style=wx.CB_READONLY)
|
||||
box.Add(self.extractor, 1, wx.GROW)
|
||||
self.search = wx.Button(self.panel, wx.NewId(), _(u"Sear&ch"))
|
||||
self.search.SetDefault()
|
||||
box.Add(self.search, 0, wx.GROW)
|
||||
self.sizer.Add(box, 0, wx.GROW)
|
||||
lbl = wx.StaticText(self.panel, wx.NewId(), _(u"&Results"))
|
||||
self.list = wx.ListBox(self.panel, wx.NewId())
|
||||
self.sizer.Add(lbl, 0, wx.GROW)
|
||||
self.sizer.Add(self.list, 1, wx.GROW)
|
||||
box1 = wx.BoxSizer(wx.HORIZONTAL)
|
||||
box2 = wx.BoxSizer(wx.HORIZONTAL)
|
||||
box1.Add(wx.StaticText(self.panel, wx.NewId(), _(u"P&osition")), 0, wx.GROW)
|
||||
self.time_slider = wx.Slider(self.panel, -1)
|
||||
box1.Add(self.time_slider, 1, wx.GROW)
|
||||
box1.Add(wx.StaticText(self.panel, wx.NewId(), _(u"&Volume")), 0, wx.GROW)
|
||||
self.vol_slider = wx.Slider(self.panel, -1, 0, 0, 100, size=(100, -1))
|
||||
box1.Add(self.vol_slider, 1, wx.GROW)
|
||||
self.previous = wx.Button(self.panel, wx.NewId(), _(u"Pre&vious"))
|
||||
self.play = wx.Button(self.panel, wx.NewId(), _(u"&Play"))
|
||||
self.stop = wx.Button(self.panel, wx.NewId(), _(u"Stop"))
|
||||
self.next = wx.Button(self.panel, wx.NewId(), _(u"&Next"))
|
||||
box2.Add(self.previous)
|
||||
box2.Add(self.play, flag=wx.RIGHT, border=5)
|
||||
box2.Add(self.stop)
|
||||
box2.Add(self.next)
|
||||
self.sizer.Add(box1, 0, wx.GROW)
|
||||
self.sizer.Add(box2, 1, wx.GROW)
|
||||
self.progressbar = wx.Gauge(self.panel, wx.NewId(), range=100, style=wx.GA_HORIZONTAL)
|
||||
self.sizer.Add(self.progressbar, 0, wx.ALL, 5)
|
||||
self.panel.SetSizerAndFit(self.sizer)
|
||||
# self.SetClientSize(self.sizer.CalcMin())
|
||||
# self.Layout()
|
||||
# self.SetSize(self.GetBestSize())
|
||||
def __init__(self, extractors=[]):
|
||||
super(mainWindow, self).__init__(parent=None, id=wx.NewId(), title=application.name)
|
||||
self.Maximize(True)
|
||||
self.makeMenu()
|
||||
self.panel = wx.Panel(self)
|
||||
self.sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.sb = self.CreateStatusBar()
|
||||
lbl2 = wx.StaticText(self.panel, wx.NewId(), _(u"S&earch"))
|
||||
self.text = wx.TextCtrl(self.panel, wx.NewId())
|
||||
box = wx.BoxSizer(wx.HORIZONTAL)
|
||||
box.Add(lbl2, 0, wx.GROW)
|
||||
box.Add(self.text, 1, wx.GROW)
|
||||
box.Add(wx.StaticText(self.panel, wx.NewId(), _(u"&Search in")), 0, wx.GROW)
|
||||
self.extractor = wx.ComboBox(self.panel, wx.NewId(), choices=extractors, value=extractors[0], style=wx.CB_READONLY)
|
||||
box.Add(self.extractor, 1, wx.GROW)
|
||||
self.search = wx.Button(self.panel, wx.NewId(), _(u"Sear&ch"))
|
||||
self.search.SetDefault()
|
||||
box.Add(self.search, 0, wx.GROW)
|
||||
self.sizer.Add(box, 0, wx.GROW)
|
||||
lbl = wx.StaticText(self.panel, wx.NewId(), _(u"&Results"))
|
||||
self.list = wx.ListBox(self.panel, wx.NewId())
|
||||
self.sizer.Add(lbl, 0, wx.GROW)
|
||||
self.sizer.Add(self.list, 1, wx.GROW)
|
||||
box1 = wx.BoxSizer(wx.HORIZONTAL)
|
||||
box2 = wx.BoxSizer(wx.HORIZONTAL)
|
||||
box1.Add(wx.StaticText(self.panel, wx.NewId(), _(u"P&osition")), 0, wx.GROW)
|
||||
self.time_slider = wx.Slider(self.panel, -1)
|
||||
box1.Add(self.time_slider, 1, wx.GROW)
|
||||
box1.Add(wx.StaticText(self.panel, wx.NewId(), _(u"&Volume")), 0, wx.GROW)
|
||||
self.vol_slider = wx.Slider(self.panel, -1, 0, 0, 100, size=(100, -1))
|
||||
box1.Add(self.vol_slider, 1, wx.GROW)
|
||||
self.previous = wx.Button(self.panel, wx.NewId(), _(u"Pre&vious"))
|
||||
self.play = wx.Button(self.panel, wx.NewId(), _(u"&Play"))
|
||||
self.stop = wx.Button(self.panel, wx.NewId(), _(u"Stop"))
|
||||
self.next = wx.Button(self.panel, wx.NewId(), _(u"&Next"))
|
||||
box2.Add(self.previous)
|
||||
box2.Add(self.play, flag=wx.RIGHT, border=5)
|
||||
box2.Add(self.stop)
|
||||
box2.Add(self.next)
|
||||
self.sizer.Add(box1, 0, wx.GROW)
|
||||
self.sizer.Add(box2, 1, wx.GROW)
|
||||
self.progressbar = wx.Gauge(self.panel, wx.NewId(), range=100, style=wx.GA_HORIZONTAL)
|
||||
self.sizer.Add(self.progressbar, 0, wx.ALL, 5)
|
||||
self.panel.SetSizerAndFit(self.sizer)
|
||||
# self.SetClientSize(self.sizer.CalcMin())
|
||||
# self.Layout()
|
||||
# self.SetSize(self.GetBestSize())
|
||||
|
||||
def change_status(self, status):
|
||||
self.sb.SetStatusText(status)
|
||||
def change_status(self, status):
|
||||
self.sb.SetStatusText(status)
|
||||
|
||||
def about_dialog(self, *args, **kwargs):
|
||||
try:
|
||||
info = wx.adv.AboutDialogInfo()
|
||||
except:
|
||||
info = wx.AboutDialogInfo()
|
||||
info.SetName(application.name)
|
||||
info.SetVersion(application.version)
|
||||
info.SetDescription(application.description)
|
||||
info.SetCopyright(application.copyright)
|
||||
info.SetWebSite(application.url)
|
||||
info.SetTranslators(application.translators)
|
||||
def about_dialog(self, *args, **kwargs):
|
||||
try:
|
||||
info = wx.adv.AboutDialogInfo()
|
||||
except:
|
||||
info = wx.AboutDialogInfo()
|
||||
info.SetName(application.name)
|
||||
info.SetVersion(application.version)
|
||||
info.SetDescription(application.description)
|
||||
info.SetCopyright(application.copyright)
|
||||
info.SetWebSite(application.url)
|
||||
info.SetTranslators(application.translators)
|
||||
# info.SetLicence(application.licence)
|
||||
info.AddDeveloper(application.author)
|
||||
try:
|
||||
wx.adv.AboutBox(info)
|
||||
except:
|
||||
wx.AboutBox(info)
|
||||
info.AddDeveloper(application.author)
|
||||
try:
|
||||
wx.adv.AboutBox(info)
|
||||
except:
|
||||
wx.AboutBox(info)
|
||||
|
||||
def get_text(self):
|
||||
t = self.text.GetValue()
|
||||
self.text.ChangeValue("")
|
||||
return t
|
||||
def get_text(self):
|
||||
t = self.text.GetValue()
|
||||
self.text.ChangeValue("")
|
||||
return t
|
||||
|
||||
def get_item(self):
|
||||
return self.list.GetSelection()
|
||||
def get_item(self):
|
||||
return self.list.GetSelection()
|
||||
|
||||
def get_destination_path(self, filename):
|
||||
saveFileDialog = wx.FileDialog(self, _(u"Save this file"), "", filename, _(u"Audio Files(*.mp3)|*.mp3"), wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
|
||||
if saveFileDialog.ShowModal() == wx.ID_OK:
|
||||
return saveFileDialog.GetPath()
|
||||
saveFileDialog.Destroy()
|
||||
def get_destination_path(self, filename):
|
||||
saveFileDialog = wx.FileDialog(self, _(u"Save this file"), "", filename, _(u"Audio Files(*.mp3)|*.mp3"), wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
|
||||
if saveFileDialog.ShowModal() == wx.ID_OK:
|
||||
return saveFileDialog.GetPath()
|
||||
saveFileDialog.Destroy()
|
||||
|
||||
def notify(self, title, text):
|
||||
try:
|
||||
self.notification = wx.adv.NotificationMessage(title, text, parent=self)
|
||||
except AttributeError:
|
||||
self.notification = wx.NotificationMessage(title, text)
|
||||
self.notification.Show()
|
||||
def notify(self, title, text):
|
||||
try:
|
||||
self.notification = wx.adv.NotificationMessage(title, text, parent=self)
|
||||
except AttributeError:
|
||||
self.notification = wx.NotificationMessage(title, text)
|
||||
self.notification.Show()
|
||||
|
@ -2,9 +2,9 @@
|
||||
import wx
|
||||
|
||||
class contextMenu(wx.Menu):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(contextMenu, self).__init__(*args, **kwargs)
|
||||
self.play = wx.MenuItem(self, wx.NewId(), _(u"Play/Pause"))
|
||||
self.download = wx.MenuItem(self, wx.NewId(), _(u"Download"))
|
||||
self.Append(self.play)
|
||||
self.Append(self.download)
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(contextMenu, self).__init__(*args, **kwargs)
|
||||
self.play = wx.MenuItem(self, wx.NewId(), _(u"Play/Pause"))
|
||||
self.download = wx.MenuItem(self, wx.NewId(), _(u"Download"))
|
||||
self.Append(self.play)
|
||||
self.Append(self.download)
|
||||
|
Loading…
Reference in New Issue
Block a user