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