From f45b0a941e7595fedee174fb2dd626deb9fddbcb Mon Sep 17 00:00:00 2001 From: oleksis <“oleksis.fraga@gmail.com”> Date: Thu, 23 Jul 2020 13:31:40 -0400 Subject: [PATCH 01/34] Migration to Python 3 --- AUTHORS | 4 ++ ChangeLog | 17 ++--- README.md | 28 ++------ TODO | 3 +- requirements.txt | 3 + youtube-dl-gui.1 | 3 +- youtube_dl_gui/__init__.py | 6 +- youtube_dl_gui/__main__.py | 2 - youtube_dl_gui/downloaders.py | 29 ++++---- youtube_dl_gui/downloadmanager.py | 23 +++---- youtube_dl_gui/formats.py | 5 +- youtube_dl_gui/info.py | 1 - youtube_dl_gui/logmanager.py | 7 +- youtube_dl_gui/mainframe.py | 107 ++++++++++++++++-------------- youtube_dl_gui/optionsframe.py | 51 +++++++------- youtube_dl_gui/optionsmanager.py | 6 +- youtube_dl_gui/parsers.py | 15 +++-- youtube_dl_gui/updatemanager.py | 37 ++++++++--- youtube_dl_gui/utils.py | 63 +++++++++--------- youtube_dl_gui/version.py | 3 +- youtube_dl_gui/widgets.py | 26 +++----- 21 files changed, 215 insertions(+), 224 deletions(-) create mode 100644 requirements.txt diff --git a/AUTHORS b/AUTHORS index 58d18d67..6d5b1ffb 100644 --- a/AUTHORS +++ b/AUTHORS @@ -19,5 +19,9 @@ Swyter Sebastian Wagner saad snosi cleitonme +luigino +fat115 +Mitsuya Tsujikawa +pavreh # Generated by update-authors.sh script diff --git a/ChangeLog b/ChangeLog index 6fb3cd3e..caee166c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -6,20 +6,13 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) ## [Unreleased] ### Added +- requirements.txt - ChangeLog -- 'flac' in available audio formats (#234) -- French translation (#203) -- Japanese translation (#226) -- Italian translation (#231) -- Czech translation (#233) -- Automatic locale detection during first run (#235) -- Man page (#259) -- Option to disable youtube-dl updates (#21) +- Man page ### Fixed -- Bug in utils.convert_item function -- Bug in downloaders.YoutubeDLDownloader (#244) -### Changed -- Update timeout from 20 to 10 seconds (#244) +### Changed +- Migration to Python 3.* +- README.md diff --git a/README.md b/README.md index 80ecd87e..ad4739aa 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ -[![Donations Badge](https://yourdonation.rocks/images/badge.svg)](https://mrs0m30n3.github.io/youtube-dl-gui/donate.html) - # youtube-dlG A cross platform front-end GUI of the popular [youtube-dl](https://rg3.github.io/youtube-dl/) media downloader written in wxPython. [Supported sites](https://rg3.github.io/youtube-dl/supportedsites.html) @@ -7,40 +5,26 @@ A cross platform front-end GUI of the popular [youtube-dl](https://rg3.github.io ![youtube-dl-gui main window](https://raw.githubusercontent.com/MrS0m30n3/youtube-dl-gui/gh-pages/images/ydlg_ui.gif) ## Requirements -* [Python 2.7.3+](https://www.python.org/downloads) -* [wxPython 3](https://wxpython.org/download.php) +* [Python 3](https://www.python.org/downloads) +* [wxPython Phoenix 4](https://wxpython.org/download.php) * [TwoDict](https://pypi.python.org/pypi/twodict) * [GNU gettext](https://www.gnu.org/software/gettext/) (to build the package) * [FFmpeg](https://ffmpeg.org/download.html) (optional, to post process video files) ## Downloads -* [Source (.zip)](https://github.com/MrS0m30n3/youtube-dl-gui/archive/0.4.zip) -* [Source (.tar.gz)](https://github.com/MrS0m30n3/youtube-dl-gui/archive/0.4.tar.gz) -* [PyPi](https://pypi.python.org/pypi/youtube-dlg/0.4) -* [Ubuntu PPA](http://ppa.launchpad.net/nilarimogard/webupd8/ubuntu/pool/main/y/youtube-dlg/) -* [Arch AUR](https://aur.archlinux.org/packages/youtube-dl-gui-git/) -* [Slackware SlackBuild](https://slackbuilds.org/repository/14.2/network/youtube-dl-gui/) -* [openSUSE](https://software.opensuse.org/package/youtube-dl-gui) -* [Windows Installer](https://github.com/MrS0m30n3/youtube-dl-gui/releases/download/0.4/youtube-dl-gui-0.4-win-setup.zip) -* [Windows Portable](https://github.com/MrS0m30n3/youtube-dl-gui/releases/download/0.4/youtube-dl-gui-0.4-win-portable.zip) +* [Source (.zip)](https://github.com/oleksis/youtube-dl-gui/archive/1.0.zip) +* [Source (.tar.gz)](https://github.com/oleksis/youtube-dl-gui/archive/1.0.tar.gz) ## Installation ### Install From Source 1. Download & extract the source -2. Change directory into *youtube-dl-gui-0.4* +2. Change directory into *youtube-dl-gui-1.0* 3. Run `python setup.py install` -### Install PyPi -1. Run `pip install youtube-dlg` - -### Install Windows Installer -1. Download & extract the Windows installer -2. Run the `setup.exe` file - ## Contributing * **Add support for new language:** See [localization howto](docs/localization_howto.md) -* **Report a bug:** See [issues](https://github.com/MrS0m30n3/youtube-dl-gui/issues) +* **Report a bug:** See [issues](https://github.com/oleksis/youtube-dl-gui/issues) ## Authors See [AUTHORS](AUTHORS) file diff --git a/TODO b/TODO index c7355022..3b3238ec 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,4 @@ -Release 0.4.1 +Release 1.0.1 ============= * Intergrity check youtube-dl bin * Non-Windows shutdown using D-Bus instead of 'shutdown' @@ -29,7 +29,6 @@ Other * Refactor * Review - rewrite threads communications -* Add support for Python 3.* * Logging system using the Python 'logging' module * Use youtube-dl directly from python instead of using the subprocess module diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..0068fcd7 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +Pypubsub==4.0.3 +twodict==1.2 +wxPython==4.0.4 \ No newline at end of file diff --git a/youtube-dl-gui.1 b/youtube-dl-gui.1 index bb03f43f..2799b8c9 100644 --- a/youtube-dl-gui.1 +++ b/youtube-dl-gui.1 @@ -1,5 +1,5 @@ .\" [program name] [section] [date YYYY-MM-DD] [version] [empty] -.TH YOUTUBE\-DL\-GUI 1 "2018-01-13" "Version 0.4" "" +.TH YOUTUBE\-DL\-GUI 1 "2020-07-23" "Version 1.0" "" .SH NAME youtube\-dl\-gui \- cross platform graphical user interface for youtube\-dl. @@ -23,6 +23,7 @@ Log file. .SH NOTES FAQS: https://github.com/MrS0m30n3/youtube-dl-gui/blob/master/docs/faqs.md +MAINTAINER: https://github.com/oleksis/youtube-dl-gui/blob/master/README.md .SH SEE ALSO .BR youtube-dl (1) diff --git a/youtube_dl_gui/__init__.py b/youtube_dl_gui/__init__.py index d8c0fe2b..46747b98 100644 --- a/youtube_dl_gui/__init__.py +++ b/youtube_dl_gui/__init__.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python2 # -*- coding: utf-8 -*- """Youtubedlg __init__ file. @@ -14,7 +13,6 @@ """ -from __future__ import unicode_literals import sys import gettext @@ -23,7 +21,7 @@ try: import wx except ImportError as error: - print error + print(error) sys.exit(1) __packagename__ = "youtube_dl_gui" @@ -54,6 +52,8 @@ YOUTUBEDL_BIN ) +_ = gettext.gettext + # Set config path and create options and log managers config_path = get_config_path() diff --git a/youtube_dl_gui/__main__.py b/youtube_dl_gui/__main__.py index 38b8acff..d19bc5e4 100644 --- a/youtube_dl_gui/__main__.py +++ b/youtube_dl_gui/__main__.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python2 # -*- coding: utf-8 -*- """Youtubedlg __main__ file. @@ -22,7 +21,6 @@ """ -from __future__ import unicode_literals import sys diff --git a/youtube_dl_gui/downloaders.py b/youtube_dl_gui/downloaders.py index 159ed6c0..d34c096b 100644 --- a/youtube_dl_gui/downloaders.py +++ b/youtube_dl_gui/downloaders.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python2 # -*- coding: utf-8 -*- """Python module to download videos. @@ -8,7 +7,6 @@ """ -from __future__ import unicode_literals import re import os @@ -18,10 +16,10 @@ import subprocess from time import sleep -from Queue import Queue +from queue import Queue from threading import Thread -from .utils import convert_item +from .utils import get_encoding class PipeReader(Thread): @@ -59,11 +57,12 @@ def run(self): while self._running: if self._filedescriptor is not None: for line in iter(self._filedescriptor.readline, str('')): + line = bytes(line).decode(encoding=get_encoding(), errors='ignore') # Ignore ffmpeg stderr if str('ffmpeg version') in line: ignore_line = True - if not ignore_line: + if not ignore_line and line != "": self._queue.put_nowait(line) self._filedescriptor = None @@ -167,7 +166,6 @@ def download(self, url, options): while self._proc_is_alive(): stdout = self._proc.stdout.readline().rstrip() - stdout = convert_item(stdout, to_unicode=True) if stdout: data_dict = extract_data(stdout) @@ -177,8 +175,7 @@ def download(self, url, options): # Read stderr after download process has been completed # We don't need to read stderr in real time while not self._stderr_queue.empty(): - stderr = self._stderr_queue.get_nowait().rstrip() - stderr = convert_item(stderr, to_unicode=True) + stderr = str(self._stderr_queue.get_nowait()).rstrip() self._log(stderr) @@ -234,7 +231,7 @@ def _set_returncode(self, code): self._return_code = code def _is_warning(self, stderr): - return stderr.split(':')[0] == 'WARNING' + return str(stderr).split(':')[0] == 'WARNING' def _last_data_hook(self): """Set the last data information based on the return code. """ @@ -328,7 +325,7 @@ def _create_process(self, cmd): info = preexec = None # Keep a unicode copy of cmd for the log - ucmd = cmd + # ucmd = cmd if os.name == 'nt': # Hide subprocess window @@ -341,8 +338,8 @@ def _create_process(self, cmd): # Encode command for subprocess # Refer to http://stackoverflow.com/a/9951851/35070 - if sys.version_info < (3, 0): - cmd = convert_item(cmd, to_unicode=False) + # if sys.version_info < (3, 0): + # cmd = convert_item(cmd, to_unicode=False) try: self._proc = subprocess.Popen(cmd, @@ -350,9 +347,10 @@ def _create_process(self, cmd): stderr=subprocess.PIPE, preexec_fn=preexec, startupinfo=info) - except (ValueError, OSError) as error: - self._log('Failed to start process: {}'.format(ucmd)) - self._log(convert_item(str(error), to_unicode=True)) + except (ValueError, OSError, FileNotFoundError) as error: + self._log('Failed to start process: {}'.format(str(cmd))) + self._log(str(error)) + print(str(error)) def extract_data(stdout): @@ -393,6 +391,7 @@ def extract_filename(input_data): # We want to keep the spaces in order to extract filenames with # multiple whitespaces correctly. We also keep a copy of the old # 'stdout' for backward compatibility with the old code + stdout = bytes(stdout).decode(encoding=get_encoding(), errors='ignore') stdout_with_spaces = stdout.split(' ') stdout = stdout.split() diff --git a/youtube_dl_gui/downloadmanager.py b/youtube_dl_gui/downloadmanager.py index a344829f..2e927c06 100644 --- a/youtube_dl_gui/downloadmanager.py +++ b/youtube_dl_gui/downloadmanager.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python2 # -*- coding: utf-8 -*- """Youtubedlg module for managing the download process. @@ -19,7 +18,6 @@ """ -from __future__ import unicode_literals import time import os.path @@ -31,8 +29,7 @@ ) from wx import CallAfter -from wx.lib.pubsub import setuparg1 -from wx.lib.pubsub import pub as Publisher +from pubsub import pub as Publisher from .parsers import OptionsParser from .updatemanager import UpdateThread @@ -166,7 +163,7 @@ def update_stats(self, stats_dict): if key in self.progress_stats: value = stats_dict[key] - if not isinstance(value, basestring) or not value: + if not value: self.progress_stats[key] = self.default_values[key] else: self.progress_stats[key] = value @@ -380,7 +377,7 @@ def __init__(self, parent, download_list, opt_manager, log_manager=None): # Init the custom workers thread pool log_lock = None if log_manager is None else Lock() wparams = (opt_manager, self._youtubedl_path(), log_manager, log_lock) - self._workers = [Worker(*wparams) for _ in xrange(opt_manager.options["workers_number"])] + self._workers = [Worker(*wparams) for _ in range(opt_manager.options["workers_number"])] self.start() @@ -415,13 +412,9 @@ def run(self): time.sleep(self.WAIT_TIME) - # Close all the workers + # Close all the workers and collect for worker in self._workers: worker.close() - - # Join and collect - for worker in self._workers: - worker.join() self._successful += worker.successful self._time_it_took = time.time() - self._time_it_took @@ -486,7 +479,7 @@ def send_to_worker(self, data): if worker.has_index(data['index']): worker.update_data(data) - def _talk_to_gui(self, data): + def _talk_to_gui(self, signal, data=None): """Send data back to the GUI using wxCallAfter and wxPublisher. Args: @@ -502,7 +495,7 @@ def _talk_to_gui(self, data): downloads using the active() method. """ - CallAfter(Publisher.sendMessage, MANAGER_PUB_TOPIC, data) + CallAfter(Publisher.sendMessage, MANAGER_PUB_TOPIC, signal=signal, data=data) def _check_youtubedl(self): """Check if youtube-dl binary exists. If not try to download it. """ @@ -563,6 +556,8 @@ class Worker(Thread): def __init__(self, opt_manager, youtubedl, log_manager=None, log_lock=None): super(Worker, self).__init__() + # Use Daemon ? + # self.setDaemon(True) self.opt_manager = opt_manager self.log_manager = log_manager self.log_lock = log_lock @@ -755,5 +750,5 @@ def _talk_to_gui(self, signal, data): if signal == 'receive': self._wait_for_reply = True - CallAfter(Publisher.sendMessage, WORKER_PUB_TOPIC, (signal, data)) + CallAfter(Publisher.sendMessage, WORKER_PUB_TOPIC, signal=signal, data=data) diff --git a/youtube_dl_gui/formats.py b/youtube_dl_gui/formats.py index e41e3df1..c14d6687 100644 --- a/youtube_dl_gui/formats.py +++ b/youtube_dl_gui/formats.py @@ -3,6 +3,7 @@ import gettext from .utils import TwoWayOrderedDict as tdict +_ = gettext.gettext OUTPUT_FORMATS = tdict([ @@ -78,7 +79,6 @@ ("flac", "flac") ]) - FORMATS = DEFAULT_FORMATS.copy() FORMATS.update(VIDEO_FORMATS) FORMATS.update(AUDIO_FORMATS) @@ -96,7 +96,7 @@ def reload_strings(): # # # - #NOTE Remove + # NOTE Remove # Code is so messed up that i need to reload strings else # the translations wont work on the about gettext tags global OUTPUT_FORMATS @@ -178,7 +178,6 @@ def reload_strings(): ("flac", "flac") ]) - FORMATS = DEFAULT_FORMATS.copy() FORMATS.update(VIDEO_FORMATS) FORMATS.update(AUDIO_FORMATS) diff --git a/youtube_dl_gui/info.py b/youtube_dl_gui/info.py index 8b9e3bd3..4ffc7ccd 100644 --- a/youtube_dl_gui/info.py +++ b/youtube_dl_gui/info.py @@ -8,7 +8,6 @@ """ -from __future__ import unicode_literals __author__ = 'Sotiris Papadopoulos' __contact__ = 'ytubedlg@gmail.com' diff --git a/youtube_dl_gui/logmanager.py b/youtube_dl_gui/logmanager.py index 86e5834c..77a11303 100644 --- a/youtube_dl_gui/logmanager.py +++ b/youtube_dl_gui/logmanager.py @@ -1,9 +1,7 @@ -#!/usr/bin/env python2 # -*- coding: utf-8 -*- """Youtubedlg module responsible for handling the log stuff. """ -from __future__ import unicode_literals import os.path from time import strftime @@ -64,8 +62,7 @@ def log(self, data): data (string): String to write to the log file. """ - if isinstance(data, basestring): - self._write(data + '\n', 'a') + self._write(str(data) + '\n', 'a') def _write(self, data, mode): """Write data to the log file. @@ -85,7 +82,7 @@ def _write(self, data, mode): else: msg = data - log.write(msg.encode(self._encoding, 'ignore')) + log.write(msg) def _init_log(self): """Initialize the log file if not exist. """ diff --git a/youtube_dl_gui/mainframe.py b/youtube_dl_gui/mainframe.py index e0aceaf7..fd6f8994 100644 --- a/youtube_dl_gui/mainframe.py +++ b/youtube_dl_gui/mainframe.py @@ -1,16 +1,15 @@ -#!/usr/bin/env python2 # -*- coding: utf-8 -*- """Youtubedlg module responsible for the main app window. """ -from __future__ import unicode_literals import os import gettext +_ = gettext.gettext import wx -from wx.lib.pubsub import setuparg1 #NOTE Should remove deprecated -from wx.lib.pubsub import pub as Publisher +import wx.adv +from pubsub import pub as Publisher from wx.lib.mixins.listctrl import ListCtrlAutoWidthMixin @@ -169,7 +168,7 @@ def __init__(self, opt_manager, log_manager, parent=None): self.log_manager = log_manager self.download_manager = None self.update_thread = None - self.app_icon = None #REFACTOR Get and set on __init__.py + self.app_icon = None # REFACTOR Get and set on __init__.py self._download_list = DownloadList() @@ -246,7 +245,7 @@ def __init__(self, opt_manager, log_manager, parent=None): self._url_text = self._create_statictext(self.URLS_LABEL) - #REFACTOR Move to buttons_data + # REFACTOR Move to buttons_data self._settings_button = self._create_bitmap_button(self._bitmaps["settings"], (30, 30), self._on_settings) self._url_list = self._create_textctrl(wx.TE_MULTILINE | wx.TE_DONTWRAP, self._on_urllist_edit) @@ -517,7 +516,7 @@ def _on_delete(self, event): ret_code = dlg.ShowModal() dlg.Destroy() - #REFACTOR Maybe add this functionality directly to DownloadList? + # REFACTOR Maybe add this functionality directly to DownloadList? if ret_code == 1: for ditem in self._download_list.get_items(): if ditem.stage != "Active": @@ -645,7 +644,7 @@ def _on_pause(self, event): selected_rows = self._status_list.get_all_selected() if selected_rows: - #REFACTOR Use DoubleStageButton for this and check stage + # REFACTOR Use DoubleStageButton for this and check stage if self._buttons["pause"].GetLabel() == _("Pause"): new_state = "Paused" else: @@ -722,7 +721,7 @@ def _on_viewlog(self, event): log_window.Show() def _on_about(self, event): - info = wx.AboutDialogInfo() + info = wx.adv.AboutDialogInfo() if self.app_icon is not None: info.SetIcon(self.app_icon) @@ -734,7 +733,7 @@ def _on_about(self, event): info.SetLicense(__licensefull__) info.AddDeveloper(__author__) - wx.AboutBox(info) + wx.adv.AboutBox(info) def _set_publisher(self, handler, topic): """Sets a handler for the given topic. @@ -801,7 +800,7 @@ def _set_layout(self): top_sizer = wx.BoxSizer(wx.HORIZONTAL) top_sizer.Add(self._url_text, 0, wx.ALIGN_BOTTOM | wx.BOTTOM, 5) - top_sizer.AddSpacer((-1, -1), 1) + top_sizer.AddStretchSpacer(1) top_sizer.Add(self._settings_button) panel_sizer.Add(top_sizer, 0, wx.EXPAND) @@ -809,13 +808,13 @@ def _set_layout(self): mid_sizer = wx.BoxSizer(wx.HORIZONTAL) mid_sizer.Add(self._folder_icon) - mid_sizer.AddSpacer((3, -1)) + mid_sizer.AddSpacer(3) mid_sizer.Add(self._path_combobox, 2, wx.ALIGN_CENTER_VERTICAL) - mid_sizer.AddSpacer((5, -1)) + mid_sizer.AddSpacer(5) mid_sizer.Add(self._buttons["savepath"], flag=wx.ALIGN_CENTER_VERTICAL) - mid_sizer.AddSpacer((10, -1), 1) + mid_sizer.AddSpacer(10) mid_sizer.Add(self._videoformat_combobox, 1, wx.ALIGN_CENTER_VERTICAL) - mid_sizer.AddSpacer((5, -1)) + mid_sizer.AddSpacer(5) mid_sizer.Add(self._buttons["add"], flag=wx.ALIGN_CENTER_VERTICAL) panel_sizer.Add(mid_sizer, 0, wx.EXPAND | wx.ALL, 10) @@ -824,17 +823,17 @@ def _set_layout(self): bottom_sizer = wx.BoxSizer(wx.HORIZONTAL) bottom_sizer.Add(self._buttons["delete"]) - bottom_sizer.AddSpacer((5, -1)) + bottom_sizer.AddSpacer(5) bottom_sizer.Add(self._buttons["play"]) - bottom_sizer.AddSpacer((5, -1)) + bottom_sizer.AddSpacer(5) bottom_sizer.Add(self._buttons["up"]) - bottom_sizer.AddSpacer((5, -1)) + bottom_sizer.AddSpacer(5) bottom_sizer.Add(self._buttons["down"]) - bottom_sizer.AddSpacer((5, -1)) + bottom_sizer.AddSpacer(5) bottom_sizer.Add(self._buttons["reload"]) - bottom_sizer.AddSpacer((5, -1)) + bottom_sizer.AddSpacer(5) bottom_sizer.Add(self._buttons["pause"]) - bottom_sizer.AddSpacer((10, -1), 1) + bottom_sizer.AddStretchSpacer(1) bottom_sizer.Add(self._buttons["start"]) panel_sizer.Add(bottom_sizer, 0, wx.EXPAND | wx.TOP, 5) @@ -904,7 +903,7 @@ def _after_download(self): if self.opt_manager.options["show_completion_popup"]: self._create_popup(self.DL_COMPLETED_MSG, self.INFO_LABEL, wx.OK | wx.ICON_INFORMATION) - def _download_worker_handler(self, msg): + def _download_worker_handler(self, signal, data=None): """downloadmanager.Worker thread handler. Handles messages from the Worker thread. @@ -913,15 +912,14 @@ def _download_worker_handler(self, msg): See downloadmanager.Worker _talk_to_gui() method. """ - signal, data = msg.data + # signal, data = msg.data download_item = self._download_list.get_item(data["index"]) download_item.update_stats(data) row = self._download_list.index(data["index"]) - self._status_list._update_from_item(row, download_item) - def _download_manager_handler(self, msg): + def _download_manager_handler(self, signal, data=None): """downloadmanager.DownloadManager thread handler. Handles messages from the DownloadManager thread. @@ -930,27 +928,27 @@ def _download_manager_handler(self, msg): See downloadmanager.DownloadManager _talk_to_gui() method. """ - data = msg.data + # TODO: REFACTOR Manage better the signal stage - if data == 'finished': + if signal == 'finished': self._print_stats() self._reset_widgets() self.download_manager = None self._app_timer.Stop() self._after_download() - elif data == 'closed': + elif signal == 'closed': self._status_bar_write(self.CLOSED_MSG) self._reset_widgets() self.download_manager = None self._app_timer.Stop() - elif data == 'closing': + elif signal == 'closing': self._status_bar_write(self.CLOSING_MSG) - elif data == 'report_active': + elif signal == 'report_active': pass - #NOTE Remove from here and downloadmanager - #since now we have the wx.Timer to check progress + # NOTE Remove from here and downloadmanager + # since now we have the wx.Timer to check progress - def _update_handler(self, msg): + def _update_handler(self, signal, data=None): """updatemanager.UpdateThread thread handler. Handles messages from the UpdateThread thread. @@ -959,13 +957,12 @@ def _update_handler(self, msg): See updatemanager.UpdateThread _talk_to_gui() method. """ - data = msg.data - if data[0] == 'download': + if signal == 'download': self._status_bar_write(self.UPDATING_MSG) - elif data[0] == 'error': - self._status_bar_write(self.UPDATE_ERR_MSG.format(data[1])) - elif data[0] == 'correct': + elif signal == 'error': + self._status_bar_write(self.UPDATE_ERR_MSG.format(data)) + elif signal == 'correct': self._status_bar_write(self.UPDATE_SUCC_MSG) else: self._reset_widgets() @@ -1004,7 +1001,7 @@ def _paste_from_clipboard(self): data = data.GetText() - if data[-1] != '\n': + if data != "" and data[-1] != '\n': data += '\n' self._url_list.WriteText(data) @@ -1106,11 +1103,16 @@ def __init__(self, columns, *args, **kwargs): ListCtrlAutoWidthMixin.__init__(self) self.columns = columns self._list_index = 0 + self._map_id = {} self._url_list = set() self._set_columns() def remove_row(self, row_number): self.DeleteItem(row_number) + total = len(self._map_id) + for row in range(row_number, total - 1): + self._map_id[row] = self._map_id[row + 1] + del self._map_id[total - 1] self._list_index -= 1 def move_item_up(self, row_number): @@ -1126,9 +1128,12 @@ def _move_item(self, cur_row, new_row): item.SetId(new_row) self.InsertItem(item) + # Swap Data associated (Python Data Mixing) + self._map_id[new_row], self._map_id[cur_row] = self._map_id[cur_row], self._map_id[new_row] self.Select(new_row) self.Thaw() + # self.SetFocus() def has_url(self, url): """Returns True if the url is aleady in the ListCtrl else False. @@ -1140,13 +1145,18 @@ def has_url(self, url): return url in self._url_list def bind_item(self, download_item): - self.InsertStringItem(self._list_index, download_item.url) + self.InsertItem(self._list_index, download_item.url) self.SetItemData(self._list_index, download_item.object_id) + self._map_id[self._list_index] = download_item.object_id self._update_from_item(self._list_index, download_item) self._list_index += 1 + + def GetItemData(self, row_index_selected): + return self._map_id.get(row_index_selected, None) + def _update_from_item(self, row, download_item): progress_stats = download_item.progress_stats @@ -1160,9 +1170,9 @@ def _update_from_item(self, row, download_item): progress_stats["playlist_index"], progress_stats["playlist_size"]) - self.SetStringItem(row, column, status) + self.SetItem(row, column, status) else: - self.SetStringItem(row, column, progress_stats[key]) + self.SetItem(row, column, progress_stats[key]) def clear(self): """Clear the ListCtrl widget & reset self._list_index and @@ -1179,10 +1189,10 @@ def get_selected(self): return self.GetNextItem(-1, wx.LIST_NEXT_ALL, wx.LIST_STATE_SELECTED) def get_all_selected(self): - return [index for index in xrange(self._list_index) if self.IsSelected(index)] + return [index for index in range(self._list_index) if self.IsSelected(index)] def deselect_all(self): - for index in xrange(self._list_index): + for index in range(self._list_index): self.Select(index, on=0) def get_next_selected(self, start=-1, reverse=False): @@ -1198,7 +1208,7 @@ def get_next_selected(self, start=-1, reverse=False): end = -1 if reverse else self._list_index step = -1 if reverse else 1 - for index in xrange(start, end, step): + for index in range(start, end, step): if self.IsSelected(index): return index @@ -1222,6 +1232,7 @@ def _set_columns(self): # REFACTOR Extra widgets below should move to other module with widgets + class ExtComboBox(wx.ComboBox): def __init__(self, parent, max_items=-1, *args, **kwargs): @@ -1337,7 +1348,7 @@ def __init__(self, parent, choices, message, *args, **kwargs): message_sizer = wx.BoxSizer(wx.HORIZONTAL) message_sizer.Add(info_icon) - message_sizer.AddSpacer((10, 10)) + message_sizer.AddSpacer(10) message_sizer.Add(msg_text, flag=wx.EXPAND) vertical_sizer.Add(message_sizer, 1, wx.ALL, border=self.BORDER) @@ -1345,9 +1356,9 @@ def __init__(self, parent, choices, message, *args, **kwargs): buttons_sizer = wx.BoxSizer(wx.HORIZONTAL) for button in buttons[1:]: buttons_sizer.Add(button) - buttons_sizer.AddSpacer((5, -1)) + buttons_sizer.AddSpacer(5) - buttons_sizer.AddSpacer((-1, -1), 1) + buttons_sizer.AddSpacer(1) buttons_sizer.Add(buttons[0], flag=wx.ALIGN_RIGHT) vertical_sizer.Add(buttons_sizer, flag=wx.EXPAND | wx.ALL, border=self.BORDER) diff --git a/youtube_dl_gui/optionsframe.py b/youtube_dl_gui/optionsframe.py index ab278ebe..56ee2366 100644 --- a/youtube_dl_gui/optionsframe.py +++ b/youtube_dl_gui/optionsframe.py @@ -1,15 +1,14 @@ -#!/usr/bin/env python2 # -*- coding: utf-8 -*- """Youtubedlg module responsible for the options window. """ -from __future__ import unicode_literals import os import gettext +_ = gettext.gettext import wx -import wx.combo +import wx.adv from wx.lib.art import flagart from .utils import ( @@ -26,7 +25,7 @@ VIDEO_FORMATS, AUDIO_FORMATS ) -#REFACTOR Move all formats, etc to formats.py +# REFACTOR Move all formats, etc to formats.py class OptionsFrame(wx.Frame): @@ -49,7 +48,7 @@ def __init__(self, parent): self.app_icon = None # Set the app icon - #REFACTOR Get icon from parent + # REFACTOR Get icon from parent app_icon_path = get_icon_file() if app_icon_path is not None: self.app_icon = wx.Icon(app_icon_path, wx.BITMAP_TYPE_PNG) @@ -98,7 +97,7 @@ def _set_layout(self): buttons_sizer = wx.BoxSizer(wx.HORIZONTAL) buttons_sizer.Add(self.reset_button) - buttons_sizer.AddSpacer((5, -1)) + buttons_sizer.AddSpacer(5) buttons_sizer.Add(self.close_button) main_sizer.Add(buttons_sizer, flag=wx.ALIGN_RIGHT | wx.ALL, border=5) @@ -110,7 +109,7 @@ def _set_layout(self): def _on_close(self, event): """Event handler for wx.EVT_CLOSE event.""" self.save_all_options() - #REFACTOR Parent create specific callback + # REFACTOR Parent create specific callback self.GetParent()._update_videoformat_combobox() self.Hide() @@ -126,12 +125,12 @@ def reset(self): def load_all_options(self): """Load all the options on each tab.""" - for tab, _ in self.tabs: + for tab, label in self.tabs: tab.load_options() def save_all_options(self): """Save all the options from all the tabs back to the OptionsManager.""" - for tab, _ in self.tabs: + for tab, label in self.tabs: tab.save_options() def Show(self, *args, **kwargs): @@ -172,9 +171,9 @@ class TabPanel(wx.Panel): def __init__(self, parent, notebook): super(TabPanel, self).__init__(notebook) - #REFACTOR Maybe add methods to access those - #save_options(key, value) - #load_options(key) + # REFACTOR Maybe add methods to access those + # save_options(key, value) + # load_options(key) self.opt_manager = parent.opt_manager self.log_manager = parent.log_manager self.app_icon = parent.app_icon @@ -216,7 +215,7 @@ def crt_combobox(self, choices, size=(-1, -1), event_handler=None): return combobox def crt_bitmap_combobox(self, choices, size=(-1, -1), event_handler=None): - combobox = wx.combo.BitmapComboBox(self, size=size, style=wx.CB_READONLY) + combobox = wx.adv.BitmapComboBox(self, size=size, style=wx.CB_READONLY) for item in choices: lang_code, lang_name = item @@ -348,7 +347,7 @@ def _set_layout(self): custom_format_sizer = wx.BoxSizer(wx.HORIZONTAL) custom_format_sizer.Add(self.filename_custom_format, 1, wx.ALIGN_CENTER_VERTICAL) - custom_format_sizer.AddSpacer((5, -1)) + custom_format_sizer.AddSpacer(5) custom_format_sizer.Add(self.filename_custom_format_button) vertical_sizer.Add(custom_format_sizer, flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5) @@ -363,7 +362,7 @@ def _set_layout(self): shutdown_sizer = wx.BoxSizer(wx.HORIZONTAL) shutdown_sizer.Add(self.shutdown_checkbox) - shutdown_sizer.AddSpacer((-1, -1), 1) + shutdown_sizer.AddSpacer(-1) shutdown_sizer.Add(self.sudo_textctrl, 1) vertical_sizer.Add(shutdown_sizer, flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5) @@ -500,7 +499,7 @@ def _set_layout(self): audio_quality_sizer = wx.BoxSizer(wx.HORIZONTAL) audio_quality_sizer.Add(self.audio_quality_label, flag=wx.ALIGN_CENTER_VERTICAL) - audio_quality_sizer.AddSpacer((20, -1)) + audio_quality_sizer.AddSpacer(20) audio_quality_sizer.Add(self.audio_quality_combobox) vertical_sizer.Add(audio_quality_sizer, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5) @@ -610,7 +609,7 @@ def _set_layout(self): plist_and_fsize_sizer = wx.BoxSizer(wx.HORIZONTAL) plist_and_fsize_sizer.Add(self._build_playlist_sizer(), 1, wx.EXPAND) - plist_and_fsize_sizer.AddSpacer((5, -1)) + plist_and_fsize_sizer.AddSpacer(5) plist_and_fsize_sizer.Add(self._build_filesize_sizer(), 1, wx.EXPAND) vertical_sizer.Add(plist_and_fsize_sizer, 1, wx.EXPAND | wx.TOP, border=5) @@ -620,7 +619,7 @@ def _set_layout(self): def _build_playlist_sizer(self): playlist_box_sizer = wx.StaticBoxSizer(self.playlist_box, wx.VERTICAL) - playlist_box_sizer.AddSpacer((-1, 10)) + playlist_box_sizer.AddSpacer(10) border = wx.GridBagSizer(5, 40) @@ -760,7 +759,7 @@ def _set_layout(self): # Set up retries box retries_sizer = wx.BoxSizer(wx.HORIZONTAL) retries_sizer.Add(self.retries_label, flag=wx.ALIGN_CENTER_VERTICAL) - retries_sizer.AddSpacer((20, -1)) + retries_sizer.AddSpacer(20) retries_sizer.Add(self.retries_spinctrl) vertical_sizer.Add(retries_sizer, flag=wx.ALIGN_RIGHT | wx.TOP | wx.RIGHT, border=5) @@ -801,9 +800,9 @@ def _set_layout(self): logging_sizer = wx.BoxSizer(wx.HORIZONTAL) logging_sizer.Add(self.enable_log_checkbox) - logging_sizer.AddSpacer((-1, -1), 1) + logging_sizer.AddSpacer(-1) logging_sizer.Add(self.view_log_button) - logging_sizer.AddSpacer((5, -1)) + logging_sizer.AddSpacer(5) logging_sizer.Add(self.clear_log_button) vertical_sizer.Add(logging_sizer, flag=wx.EXPAND | wx.ALL, border=5) @@ -856,7 +855,7 @@ def __init__(self, *args, **kwargs): super(ExtraTab, self).__init__(*args, **kwargs) self.cmdline_args_label = self.crt_statictext(_("Youtube-dl command line options (e.g. --help)")) - self.cmdline_args_textctrl = self.crt_textctrl(wx.TE_MULTILINE | wx.TE_LINEWRAP) + self.cmdline_args_textctrl = self.crt_textctrl(wx.TE_MULTILINE) self.extra_opts_label = self.crt_statictext(_("Extra options")) @@ -879,13 +878,13 @@ def _set_layout(self): extra_opts_sizer = wx.WrapSizer() extra_opts_sizer.Add(self.youtube_dl_debug_checkbox) - extra_opts_sizer.AddSpacer((5, -1)) + extra_opts_sizer.AddSpacer(5) extra_opts_sizer.Add(self.ignore_errors_checkbox) - extra_opts_sizer.AddSpacer((5, -1)) + extra_opts_sizer.AddSpacer(5) extra_opts_sizer.Add(self.ignore_config_checkbox) - extra_opts_sizer.AddSpacer((5, -1)) + extra_opts_sizer.AddSpacer(5) extra_opts_sizer.Add(self.no_mtime_checkbox) - extra_opts_sizer.AddSpacer((5, -1)) + extra_opts_sizer.AddSpacer(5) extra_opts_sizer.Add(self.native_hls_checkbox) vertical_sizer.Add(extra_opts_sizer, flag=wx.ALL, border=5) diff --git a/youtube_dl_gui/optionsmanager.py b/youtube_dl_gui/optionsmanager.py index fc354e4f..1b3ae340 100644 --- a/youtube_dl_gui/optionsmanager.py +++ b/youtube_dl_gui/optionsmanager.py @@ -1,9 +1,7 @@ -#!/usr/bin/env python2 # -*- coding: utf-8 -*- """Youtubedlg module to handle settings. """ -from __future__ import unicode_literals import os import json @@ -314,7 +312,7 @@ def load_from_file(self): if not os_path_exists(self.settings_file): return - with open(self.settings_file, 'rb') as settings_file: + with open(self.settings_file, 'r') as settings_file: try: options = json.load(settings_file) @@ -327,7 +325,7 @@ def save_to_file(self): """Save options to settings file. """ check_path(self.config_path) - with open(self.settings_file, 'wb') as settings_file: + with open(self.settings_file, 'w') as settings_file: options = self._get_options() json.dump(options, settings_file, diff --git a/youtube_dl_gui/parsers.py b/youtube_dl_gui/parsers.py index dea0189f..b5ce9700 100644 --- a/youtube_dl_gui/parsers.py +++ b/youtube_dl_gui/parsers.py @@ -1,9 +1,7 @@ -#!/usr/bin/env python2 # -*- coding: utf-8 -*- """Youtubedlg module responsible for parsing the options. """ -from __future__ import unicode_literals import os.path @@ -135,7 +133,7 @@ def parse(self, options_dictionary): # Parse basic youtube-dl command line options for option in self._ydl_options: - #NOTE Special case should be removed + # NOTE Special case should be removed if option.name == "to_audio": if options_dict["audio_format"] == "": value = options_dict[option.name] @@ -150,7 +148,7 @@ def parse(self, options_dictionary): options_list.append(option.flag) options_list.append(to_string(value)) - #NOTE Temp fix + # NOTE Temp fix # If current 'audio_quality' is not the default one ('5') # then append the audio quality flag and value to the # options list @@ -212,7 +210,8 @@ def parse(self, options_dictionary): return options_list - def _build_savepath(self, options_dict): + @staticmethod + def _build_savepath(options_dict): """Build the save path. We use this method to build the value of the 'save_path' option and @@ -239,7 +238,8 @@ def _build_savepath(self, options_dict): options_dict["save_path"] = os.path.join(save_path, template) - def _build_videoformat(self, options_dict): + @staticmethod + def _build_videoformat(options_dict): """Build the video format. We use this method to build the value of the 'video_format' option and @@ -252,7 +252,8 @@ def _build_videoformat(self, options_dict): if options_dict['video_format'] != '0' and options_dict['second_video_format'] != '0': options_dict['video_format'] = options_dict['video_format'] + '+' + options_dict['second_video_format'] - def _build_filesizes(self, options_dict): + @staticmethod + def _build_filesizes(options_dict): """Build the filesize options values. We use this method to build the values of 'min_filesize' and diff --git a/youtube_dl_gui/updatemanager.py b/youtube_dl_gui/updatemanager.py index d0c5a8a4..068b81ad 100644 --- a/youtube_dl_gui/updatemanager.py +++ b/youtube_dl_gui/updatemanager.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python2 # -*- coding: utf-8 -*- """Youtubedlg module to update youtube-dl binary. @@ -9,15 +8,16 @@ """ -from __future__ import unicode_literals +import json import os.path from threading import Thread -from urllib2 import urlopen, URLError, HTTPError +from urllib.request import urlopen +from urllib.error import URLError, HTTPError from wx import CallAfter -from wx.lib.pubsub import setuparg1 -from wx.lib.pubsub import pub as Publisher +from pubsub import pub as Publisher + from .utils import ( YOUTUBEDL_BIN, @@ -46,6 +46,9 @@ class UpdateThread(Thread): """ LATEST_YOUTUBE_DL = 'https://yt-dl.org/latest/' + GITHUB_API = "https://api.github.com/" + LATEST_YOUTUBE_DL_API = GITHUB_API + 'repos/ytdl-org/youtube-dl/releases/latest' + # LATEST_PICTA_DL_API = GITHUB_API + 'repos/oleksis/youtube-dl/releases/latest' DOWNLOAD_TIMEOUT = 10 def __init__(self, download_path, quiet=False): @@ -54,10 +57,28 @@ def __init__(self, download_path, quiet=False): self.quiet = quiet self.start() + def get_latest_sourcefile(self): + try: + source_file = self.GITHUB_API + stream = urlopen(self.LATEST_YOUTUBE_DL_API, timeout=self.DOWNLOAD_TIMEOUT) + + latest_json = json.load(stream) + latest_assets = latest_json["assets"] + + for asset in latest_assets: + if asset["name"] == YOUTUBEDL_BIN: + source_file = asset["browser_download_url"] + break + + return source_file + except (HTTPError, URLError, json.JSONDecodeError) as error: + self._talk_to_gui('error', error) + def run(self): self._talk_to_gui('download') - source_file = self.LATEST_YOUTUBE_DL + YOUTUBEDL_BIN + # source_file = self.LATEST_YOUTUBE_DL + YOUTUBEDL_BIN + source_file = self.get_latest_sourcefile() destination_file = os.path.join(self.download_path, YOUTUBEDL_BIN) check_path(self.download_path) @@ -70,7 +91,7 @@ def run(self): self._talk_to_gui('correct') except (HTTPError, URLError, IOError) as error: - self._talk_to_gui('error', unicode(error)) + self._talk_to_gui('error', error) if not self.quiet: self._talk_to_gui('finish') @@ -93,4 +114,4 @@ def _talk_to_gui(self, signal, data=None): 4) finish: The update thread is ready to join """ - CallAfter(Publisher.sendMessage, UPDATE_PUB_TOPIC, (signal, data)) + CallAfter(Publisher.sendMessage, UPDATE_PUB_TOPIC, signal=signal, data=data) diff --git a/youtube_dl_gui/utils.py b/youtube_dl_gui/utils.py index 729c0079..07fccd62 100644 --- a/youtube_dl_gui/utils.py +++ b/youtube_dl_gui/utils.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python2 # -*- coding: utf-8 -*- """Youtubedlg module that contains util functions. @@ -10,8 +9,6 @@ """ -from __future__ import unicode_literals - import os import sys import json @@ -22,21 +19,18 @@ try: from twodict import TwoWayOrderedDict except ImportError as error: - print error + print(error) sys.exit(1) from .info import __appname__ from .version import __version__ - _RANDOM_OBJECT = object() - YOUTUBEDL_BIN = 'youtube-dl' if os.name == 'nt': YOUTUBEDL_BIN += '.exe' - FILESIZE_METRICS = ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"] KILO_SIZE = 1024.0 @@ -47,7 +41,7 @@ def get_encoding(): try: encoding = locale.getpreferredencoding() 'TEST'.encode(encoding) - except: + except locale.Error: encoding = 'UTF-8' return encoding @@ -66,11 +60,11 @@ def convert_item(item, to_unicode=False): """ if to_unicode and isinstance(item, str): # Convert str to unicode - return item.decode(get_encoding(), 'ignore') + return str(bytes(item), encoding=get_encoding(), errors="ignore") - if not to_unicode and isinstance(item, unicode): - # Convert unicode to str - return item.encode(get_encoding(), 'ignore') + if not to_unicode and isinstance(item, bytes): + # Convert bytes to str + return bytes(item).decode(encoding=get_encoding(), errors="ignore") if hasattr(item, '__iter__'): # Handle iterables @@ -96,6 +90,7 @@ def convert_on_bounds(func): returned strings values back to 'unicode'. """ + def wrapper(*args, **kwargs): returned_value = func(*convert_item(args), **convert_item(kwargs)) @@ -106,22 +101,22 @@ def wrapper(*args, **kwargs): # See: https://github.com/MrS0m30n3/youtube-dl-gui/issues/57 # Patch os functions to convert between 'str' and 'unicode' on app bounds -os_sep = unicode(os.sep) -os_getenv = convert_on_bounds(os.getenv) -os_makedirs = convert_on_bounds(os.makedirs) -os_path_isdir = convert_on_bounds(os.path.isdir) -os_path_exists = convert_on_bounds(os.path.exists) -os_path_dirname = convert_on_bounds(os.path.dirname) -os_path_abspath = convert_on_bounds(os.path.abspath) -os_path_realpath = convert_on_bounds(os.path.realpath) -os_path_expanduser = convert_on_bounds(os.path.expanduser) - -# Patch locale functions -locale_getdefaultlocale = convert_on_bounds(locale.getdefaultlocale) +os_sep = str(os.sep) +os_getenv = os.getenv +os_makedirs = os.makedirs +os_path_isdir = os.path.isdir +os_path_exists = os.path.exists +os_path_dirname = os.path.dirname +os_path_abspath = os.path.abspath +os_path_realpath = os.path.realpath +os_path_expanduser = os.path.expanduser + +locale_getdefaultlocale = locale.getdefaultlocale # Patch Windows specific functions if os.name == 'nt': - os_startfile = convert_on_bounds(os.startfile) + os_startfile = os.startfile + def remove_file(filename): if os_path_exists(filename): @@ -130,6 +125,7 @@ def remove_file(filename): return False + def remove_shortcuts(path): """Return given path after removing the shortcuts. """ return path.replace('~', os_path_expanduser('~')) @@ -316,12 +312,14 @@ def get_pixmaps_dir(): Paths we search: __main__ dir, library dir """ - search_dirs = [ - os.path.join(absolute_path(sys.argv[0]), "data"), - os.path.join(os_path_dirname(__file__), "data") + DIR_NAME = "data" + + SEARCH_DIRS = [ + os.path.join(absolute_path(sys.argv[0]), DIR_NAME), + os.path.join(os_path_dirname(__file__), DIR_NAME), ] - for directory in search_dirs: + for directory in SEARCH_DIRS: pixmaps_dir = os.path.join(directory, "pixmaps") if os_path_exists(pixmaps_dir): @@ -333,6 +331,7 @@ def get_pixmaps_dir(): def to_bytes(string): """Convert given youtube-dl size string to bytes.""" value = 0.0 + index = 0 for index, metric in enumerate(reversed(FILESIZE_METRICS)): if metric in string: @@ -344,15 +343,15 @@ def to_bytes(string): return round(value * (KILO_SIZE ** exponent), 2) -def format_bytes(bytes): +def format_bytes(bytes_): """Format bytes to youtube-dl size output strings.""" if bytes == 0.0: exponent = 0 else: - exponent = int(math.log(bytes, KILO_SIZE)) + exponent = int(math.log(bytes_, KILO_SIZE)) suffix = FILESIZE_METRICS[exponent] - output_value = bytes / (KILO_SIZE ** exponent) + output_value = bytes_ / (KILO_SIZE ** exponent) return "%.2f%s" % (output_value, suffix) diff --git a/youtube_dl_gui/version.py b/youtube_dl_gui/version.py index 0c1fe4a4..5f5321e3 100644 --- a/youtube_dl_gui/version.py +++ b/youtube_dl_gui/version.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals -__version__ = '0.4' +__version__ = '1.0' diff --git a/youtube_dl_gui/widgets.py b/youtube_dl_gui/widgets.py index 4783016b..51b6ebe8 100644 --- a/youtube_dl_gui/widgets.py +++ b/youtube_dl_gui/widgets.py @@ -1,14 +1,12 @@ -#!/usr/bin/env python # -*- coding: UTF-8 -*- -from __future__ import unicode_literals import sys try: import wx except ImportError as error: - print error + print(error) sys.exit(1) @@ -18,7 +16,6 @@ def crt_command_event(event_type, event_id=0): class ListBoxWithHeaders(wx.ListBox): - """Custom ListBox object that supports 'headers'. Attributes: @@ -45,7 +42,7 @@ class ListBoxWithHeaders(wx.ListBox): ] def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, - size=wx.DefaultSize, choices=[], style=0, validator=wx.DefaultValidator, name=NAME): + size=wx.DefaultSize, choices=[], style=0, validator=wx.DefaultValidator, name=NAME): super(ListBoxWithHeaders, self).__init__(parent, id, pos, size, [], style, validator, name) self.__headers = set() @@ -135,9 +132,6 @@ def SetStringSelection(self, string): # wx.ItemContainer methods - def Append(self, string): - super(ListBoxWithHeaders, self).Append(self._add_prefix(string)) - def AppendItems(self, strings): strings = [self._add_prefix(string) for string in strings] super(ListBoxWithHeaders, self).AppendItems(strings) @@ -174,7 +168,6 @@ def add_items(self, items, with_prefix=True): class ListBoxPopup(wx.PopupTransientWindow): - """ListBoxWithHeaders as a popup. This class uses the wx.PopupTransientWindow to create the popup and the @@ -188,7 +181,7 @@ class ListBoxPopup(wx.PopupTransientWindow): EVENTS_TABLE = { "EVT_COMBOBOX": crt_command_event(wx.EVT_COMBOBOX), - "EVT_COMBOBOX_DROPDOWN" : crt_command_event(wx.EVT_COMBOBOX_DROPDOWN), + "EVT_COMBOBOX_DROPDOWN": crt_command_event(wx.EVT_COMBOBOX_DROPDOWN), "EVT_COMBOBOX_CLOSEUP": crt_command_event(wx.EVT_COMBOBOX_CLOSEUP) } @@ -255,12 +248,11 @@ def GetControl(self): def GetStringValue(self): return self.__listbox.GetString(self.value) - #def SetStringValue(self, string): - #self.__listbox.SetStringSelection(string) + # def SetStringValue(self, string): + # self.__listbox.SetStringSelection(string) class CustomComboBox(wx.Panel): - """Custom combobox. Attributes: @@ -270,15 +262,15 @@ class CustomComboBox(wx.Panel): NAME (string): Default name for the name argument of the __init__. """ - #NOTE wx.ComboBox does not support EVT_MOTION inside the popup - #NOTE Tried with ComboCtrl but i was not able to draw the button + # NOTE wx.ComboBox does not support EVT_MOTION inside the popup + # NOTE Tried with ComboCtrl but i was not able to draw the button CB_READONLY = wx.TE_READONLY NAME = "customComboBox" def __init__(self, parent, id=wx.ID_ANY, value="", pos=wx.DefaultPosition, - size=wx.DefaultSize, choices=[], style=0, validator=wx.DefaultValidator, name=NAME): + size=wx.DefaultSize, choices=[], style=0, validator=wx.DefaultValidator, name=NAME): super(CustomComboBox, self).__init__(parent, id, pos, size, 0, name) assert style == self.CB_READONLY or style == 0 @@ -343,7 +335,7 @@ def Dismiss(self): self.listbox.Dismiss() def FindString(self, string, caseSensitive=False): - #TODO handle caseSensitive + # TODO handle caseSensitive return self.listbox.GetControl().FindString(string) def GetCount(self): From a494f0f91e5321d7a08b3a0b74cad0b0100aa876 Mon Sep 17 00:00:00 2001 From: oleksis <“oleksis.fraga@gmail.com”> Date: Thu, 23 Jul 2020 13:53:53 -0400 Subject: [PATCH 02/34] Update AUTHORS ad .gitignore --- .gitignore | 2 ++ AUTHORS | 1 + youtube_dl_gui/downloaders.py | 1 - 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 3ce5e81a..3c495862 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ MANIFEST dist/ build/ + +.idea diff --git a/AUTHORS b/AUTHORS index 6d5b1ffb..f6bfa461 100644 --- a/AUTHORS +++ b/AUTHORS @@ -23,5 +23,6 @@ luigino fat115 Mitsuya Tsujikawa pavreh +oleksis # Generated by update-authors.sh script diff --git a/youtube_dl_gui/downloaders.py b/youtube_dl_gui/downloaders.py index d34c096b..ebb787e8 100644 --- a/youtube_dl_gui/downloaders.py +++ b/youtube_dl_gui/downloaders.py @@ -350,7 +350,6 @@ def _create_process(self, cmd): except (ValueError, OSError, FileNotFoundError) as error: self._log('Failed to start process: {}'.format(str(cmd))) self._log(str(error)) - print(str(error)) def extract_data(stdout): From 83965c672b9c47f8815d1763c41c47d02d15d95d Mon Sep 17 00:00:00 2001 From: oleksis <“oleksis.fraga@gmail.com”> Date: Thu, 23 Jul 2020 16:25:42 -0400 Subject: [PATCH 03/34] Prepare for PyInstaller --- .gitignore | 1 + youtube_dl_gui/__init__.py | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 3c495862..a7b0da17 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ dist/ build/ .idea +*.spec diff --git a/youtube_dl_gui/__init__.py b/youtube_dl_gui/__init__.py index 46747b98..a4de1670 100644 --- a/youtube_dl_gui/__init__.py +++ b/youtube_dl_gui/__init__.py @@ -15,9 +15,16 @@ import sys -import gettext import os.path +if __package__ is None and not hasattr(sys, 'frozen'): + # direct call of __main__.py + root_path = os.path.realpath(os.path.abspath(__file__)) + sys.path.insert(0, os.path.dirname(os.path.dirname(root_path))) + +import youtube_dl_gui +import gettext + try: import wx except ImportError as error: From 4c8995dd990a8e988e77374b100a49228674ee68 Mon Sep 17 00:00:00 2001 From: oleksis <“oleksis.fraga@gmail.com”> Date: Sat, 25 Jul 2020 02:20:15 -0400 Subject: [PATCH 04/34] Refactored setup.py, Internationalization, PyInstaller --- .gitignore | 1 + ChangeLog | 10 +- README.md | 20 +- TODO | 2 +- requirements.txt | 3 +- setup.py | 409 ++++++------ youtube_dl_gui/__init__.py | 28 +- youtube_dl_gui/formats.py | 11 +- youtube_dl_gui/info.py | 6 +- .../es_CU/LC_MESSAGES/youtube_dl_gui.po | 604 ++++++++++++++++++ youtube_dl_gui/mainframe.py | 28 +- youtube_dl_gui/optionsframe.py | 32 +- youtube_dl_gui/optionsmanager.py | 2 +- youtube_dl_gui/updatemanager.py | 6 +- youtube_dl_gui/version.py | 2 +- 15 files changed, 929 insertions(+), 235 deletions(-) create mode 100644 youtube_dl_gui/locale/es_CU/LC_MESSAGES/youtube_dl_gui.po diff --git a/.gitignore b/.gitignore index a7b0da17..13ac2526 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ build/ .idea *.spec +*.egg-info diff --git a/ChangeLog b/ChangeLog index caee166c..009c1988 100644 --- a/ChangeLog +++ b/ChangeLog @@ -9,10 +9,18 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) - requirements.txt - ChangeLog - Man page +- Add locale (.mo) es_CU +- Add Flag art for Cuba ### Fixed - +- Imports for Internationalization with gettext ### Changed - Migration to Python 3.* - README.md +- Refactored setup.py + - Build the translation files (*.mo) using Python 3 Tools i18n msgfmt + - Build the binaries using PyInstaller + - Build with updates disabled + - Set up for Windows/Linux +- Ignore wxPyDeprecationWarning diff --git a/README.md b/README.md index ad4739aa..75678a6a 100644 --- a/README.md +++ b/README.md @@ -8,19 +8,29 @@ A cross platform front-end GUI of the popular [youtube-dl](https://rg3.github.io * [Python 3](https://www.python.org/downloads) * [wxPython Phoenix 4](https://wxpython.org/download.php) * [TwoDict](https://pypi.python.org/pypi/twodict) -* [GNU gettext](https://www.gnu.org/software/gettext/) (to build the package) -* [FFmpeg](https://ffmpeg.org/download.html) (optional, to post process video files) +* [FFmpeg](https://ffmpeg.org/download.html) (optional, to postprocess video files) + +### Requirement for build Binaries/Executables +* [PyInstaller](https://www.pyinstaller.org/) + +### Optionals +* [GNU gettext](https://www.gnu.org/software/gettext/) ## Downloads -* [Source (.zip)](https://github.com/oleksis/youtube-dl-gui/archive/1.0.zip) -* [Source (.tar.gz)](https://github.com/oleksis/youtube-dl-gui/archive/1.0.tar.gz) +* [Source (.zip)](https://github.com/oleksis/youtube-dl-gui/archive/1.0.0.zip) +* [Source (.tar.gz)](https://github.com/oleksis/youtube-dl-gui/archive/1.0.0.tar.gz) ## Installation ### Install From Source 1. Download & extract the source 2. Change directory into *youtube-dl-gui-1.0* -3. Run `python setup.py install` +3. Run `python setup.py build_trans` +4. Run `python setup.py install` + +## Binaries +Create binaries using [PyInstaller](https://www.pyinstaller.org/) +1. Run `python setup.py pyinstaller` ## Contributing * **Add support for new language:** See [localization howto](docs/localization_howto.md) diff --git a/TODO b/TODO index 3b3238ec..f0a022d8 100644 --- a/TODO +++ b/TODO @@ -25,7 +25,7 @@ Localization Other ===== -* Re-structure package +* Re-structure package (Avoid Circular Imports) * Refactor * Review - rewrite threads communications diff --git a/requirements.txt b/requirements.txt index 0068fcd7..c099900a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ Pypubsub==4.0.3 twodict==1.2 -wxPython==4.0.4 \ No newline at end of file +wxPython==4.0.4 +pyinstaller==3.6 \ No newline at end of file diff --git a/setup.py b/setup.py index ac68b9e9..bc1bac0b 100644 --- a/setup.py +++ b/setup.py @@ -1,12 +1,11 @@ -#!/usr/bin/env python2 # -*- coding: utf-8 -*- """Youtube-dlg setup file. Examples: - Windows:: + Windows/Linux:: - python setup.py py2exe + python setup.py pyinstaller Linux:: @@ -26,17 +25,8 @@ Build with updates disabled:: - python setup.py build --no-updates + python setup.py no_updates -Requirements: - - * GNU gettext utilities - -Notes: - If you get 'TypeError: decoding Unicode is not supported' when you run - py2exe then apply the following patch:: - - http://sourceforge.net/p/py2exe/patches/28/ Basic steps of the setup:: @@ -51,133 +41,136 @@ """ -from distutils import cmd, log -from distutils.core import setup -from distutils.command.build import build - +import glob import os import sys -import glob -from shutil import copyfile -from subprocess import call +from setuptools import setup, Command +from distutils import log +from distutils.spawn import spawn +from distutils.errors import DistutilsExecError +import time -PY2EXE = len(sys.argv) >= 2 and sys.argv[1] == "py2exe" -if PY2EXE: - try: - import py2exe - except ImportError as error: - print(error) - sys.exit(1) - -from youtube_dl_gui import ( +from youtube_dl_gui import __packagename__ +from youtube_dl_gui.info import ( __author__, __appname__, __contact__, - __version__, + __maintainer__, + __maintainer_contact__, __license__, __projecturl__, __description__, - __packagename__, __descriptionfull__ ) -# Setup can not handle unicode -__packagename__ = str(__packagename__) +DESCRIPTION = __description__ +LONG_DESCRIPTION = __descriptionfull__ -def on_windows(): - """Returns True if OS is Windows.""" - return os.name == "nt" +PYINSTALLER = len(sys.argv) >= 2 and sys.argv[1] == "pyinstaller" +try: + from PyInstaller import compat as pyi_compat -class BuildBin(cmd.Command): + if pyi_compat.is_win: + # noinspection PyUnresolvedReferences + from PyInstaller.utils.win32.versioninfo import ( + VarStruct, VarFileInfo, StringStruct, StringTable, + StringFileInfo, FixedFileInfo, VSVersionInfo, SetVersion, + ) +except ImportError: + pyi_compat = None + if PYINSTALLER: + print("Cannot import pyinstaller", file=sys.stderr) + exit(1) - description = "build the youtube-dl-gui binary file" - user_options = [] +# Get the version from youtube_dl_gui/version.py without importing the package +exec(compile(open(__packagename__+"/version.py").read(), __packagename__+"/version.py", "exec")) - def initialize_options(self): - self.scripts_dir = None - def finalize_options(self): - self.scripts_dir = os.path.join("build", "_scripts") +def on_windows(): + """Returns True if OS is Windows.""" + return os.name == "nt" - def run(self): - if not os.path.exists(self.scripts_dir): - os.makedirs(self.scripts_dir) - copyfile(os.path.join(__packagename__, "__main__.py"), - os.path.join(self.scripts_dir, "youtube-dl-gui")) +def version2tuple(commit=0): + version_list = str(__version__).split(".") + if len(version_list) > 3: + _commit = int(version_list[3]) + del version_list[3] + else: + _commit = commit + + _year, _month, _day = [int(value) for value in version_list] + return _year, _month, _day, _commit -class BuildTranslations(cmd.Command): +def version2str(commit=0): + version_tuple = version2tuple(commit) + return "%s.%s.%s.%s" % version_tuple - description = "build the translation files" + +# noinspection PyAttributeOutsideInit +class BuildTranslations(Command): + description = "Build the translation files" user_options = [] def initialize_options(self): - self.exec_name = None self.search_pattern = None def finalize_options(self): - if on_windows(): - self.exec_name = "msgfmt.exe" - else: - self.exec_name = "msgfmt" - self.search_pattern = os.path.join(__packagename__, "locale", "*", "LC_MESSAGES", "youtube_dl_gui.po") def run(self): - for po_file in glob.glob(self.search_pattern): - mo_file = po_file.replace(".po", ".mo") - - try: - log.info("building MO file for '{}'".format(po_file)) - call([self.exec_name, "-o", mo_file, po_file]) - except OSError: - log.error("could not locate file '{}', exiting...".format(self.exec_name)) - sys.exit(1) - - -class Build(build): - - """Overwrite the default 'build' behaviour.""" + tools_path = os.path.join(sys.exec_prefix, 'Tools') + msgfmt_file = os.path.join(tools_path, 'i18n', 'msgfmt.py') + + if os.path.isfile(msgfmt_file): + for po_file in glob.glob(self.search_pattern): + mo_file = po_file.replace('.po', '.mo') + + try: + log.info("Building MO file for '{}'".format(po_file)) + spawn([sys.executable, msgfmt_file, '-o', mo_file, po_file]) + except DistutilsExecError as error: + log.error("Error '{}', exiting...".format(error)) + sys.exit(1) + else: + log.error("Could not locate file '{}', exiting...".format(msgfmt_file)) + sys.exit(1) - sub_commands = [ - ("build_bin", None), - ("build_trans", None) - ] + build.sub_commands - build.user_options.append(("no-updates", None, "build with updates disabled")) +# noinspection PyAttributeOutsideInit +class BuildNoUpdate(Command): + description = "Build with updates disabled" + user_options = [] def initialize_options(self): - build.initialize_options(self) - self.no_updates = None + self.build_lib = os.path.dirname(os.path.abspath(__file__)) - def run(self): - build.run(self) + def finalize_options(self): + pass - if self.no_updates: - self.__disable_updates() + def run(self): + self.__disable_updates() def __disable_updates(self): lib_dir = os.path.join(self.build_lib, __packagename__) target_file = "optionsmanager.py" - # Options file should be available from previous build commands optionsfile = os.path.join(lib_dir, target_file) - data = None with open(optionsfile, "r") as input_file: data = input_file.readlines() if data is None: - log.error("building with updates disabled failed!") + log.error("Building with updates disabled failed!") sys.exit(1) for index, line in enumerate(data): if "'disable_update': False" in line: - log.info("disabling updates") + log.info("Disabling updates...") data[index] = line.replace("False", "True") break @@ -185,19 +178,95 @@ def __disable_updates(self): output_file.writelines(data) -# Overwrite cmds +class BuildPyinstallerBin(Command): + description = "Build the executable" + user_options = [] + version_file = None + if pyi_compat and pyi_compat.is_win: + version_file = VSVersionInfo( + ffi=FixedFileInfo( + filevers=version2tuple(), + prodvers=version2tuple(), + mask=0x3F, + flags=0x0, + OS=0x4, + fileType=0x1, + subtype=0x0, + date=(0, 0), + ), + kids=[ + VarFileInfo([VarStruct("Translation", [0, 1200])]), + StringFileInfo( + [ + StringTable( + "000004b0", + [ + StringStruct("CompanyName", "oleksis.fraga@gmail.com"), + StringStruct("FileDescription", DESCRIPTION), + StringStruct("FileVersion", version2str()), + StringStruct("InternalName", "youtube-dl-gui.exe"), + StringStruct( + "LegalCopyright", + __projecturl__ + "LICENSE", + ), + StringStruct("OriginalFilename", "youtube-dl-gui.exe"), + StringStruct("ProductName", __appname__), + StringStruct("ProductVersion", version2str()), + ], + ) + ] + ), + ], + ) + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self, version=version_file): + spawn( + [ + "pyinstaller", + "-w", + "-F", + "--icon="+__packagename__+"/data/pixmaps/youtube-dl-gui.ico", + "--add-data="+__packagename__+"/data;"+__packagename__+"/data", + "--add-data="+__packagename__+"/locale;"+__packagename__+"/locale", + "--exclude-module=tests", + "--name=youtube-dl-gui", + ""+__packagename__+"/__main__.py", + ], + dry_run=self.dry_run) + + if version: + time.sleep(3) + SetVersion("./dist/youtube-dl-gui.exe", version) + + +pyinstaller_cmd = dict() +pyinstaller_console = [ + { + "script": "./"+__packagename__+"/__main__.py", + "dest_base": __packagename__, + "version": __version__, + "description": DESCRIPTION, + "comments": LONG_DESCRIPTION, + "product_name": __appname__, + "product_version": __version__, + } +] + cmdclass = { - "build": Build, - "build_bin": BuildBin, - "build_trans": BuildTranslations + "build_trans": BuildTranslations, + "no_updates": BuildNoUpdate, } -def linux_setup(): - scripts = [] - data_files = [] - package_data = {} - +def setup_linux(): + """Setup params for Linux""" + data_files_linux = [] # Add hicolor icons for path in glob.glob("youtube_dl_gui/data/icons/hicolor/*x*"): size = os.path.basename(path) @@ -205,120 +274,92 @@ def linux_setup(): dst = "share/icons/hicolor/{size}/apps".format(size=size) src = "{icon_path}/apps/youtube-dl-gui.png".format(icon_path=path) - data_files.append((dst, [src])) - + data_files_linux.append((dst, [src])) # Add fallback icon, see issue #14 - data_files.append( + data_files_linux.append( ("share/pixmaps", ["youtube_dl_gui/data/pixmaps/youtube-dl-gui.png"]) ) - # Add man page - data_files.append( + data_files_linux.append( ("share/man/man1", ["youtube-dl-gui.1"]) ) - # Add pixmaps icons (*.png) & i18n files - package_data[__packagename__] = [ + package_data_linux = {__packagename__: [ "data/pixmaps/*.png", "locale/*/LC_MESSAGES/*.mo" - ] - - # Add scripts - scripts.append("build/_scripts/youtube-dl-gui") - + ]} setup_params = { - "scripts": scripts, - "data_files": data_files, - "package_data": package_data + "data_files": data_files_linux, + "package_data": package_data_linux, } return setup_params -def windows_setup(): - def normal_setup(): - package_data = {} - - # Add pixmaps icons (*.png) & i18n files - package_data[__packagename__] = [ - "data\\pixmaps\\*.png", - "locale\\*\\LC_MESSAGES\\*.mo" - ] - - setup_params = { - "package_data": package_data - } - - return setup_params - - def py2exe_setup(): - windows = [] - data_files = [] - - # py2exe dependencies & options - # TODO change directory for ffmpeg.exe & ffprobe.exe - dependencies = [ - "C:\\Windows\\System32\\ffmpeg.exe", - "C:\\Windows\\System32\\ffprobe.exe", - "C:\\python27\\DLLs\\MSVCP90.dll" - ] - - options = { - "includes": ["wx.lib.pubsub.*", - "wx.lib.pubsub.core.*", - "wx.lib.pubsub.core.arg1.*"] - } - ############################################# - - # Add py2exe deps & pixmaps icons (*.png) - data_files.extend([ - ("", dependencies), - ("data\\pixmaps", glob.glob("youtube_dl_gui\\data\\pixmaps\\*.png")), - ]) - - # We have to manually add the translation files since py2exe cant do it - for lang in os.listdir("youtube_dl_gui\\locale"): - dst = os.path.join("locale", lang, "LC_MESSAGES") - src = os.path.join("youtube_dl_gui", dst, "youtube_dl_gui.mo") +def setup_windows(): + """Setup params for Windows""" + package_data_windows = {__packagename__: [ + "data\\pixmaps\\*.png", + "locale\\*\\LC_MESSAGES\\*.mo" + ]} + # Add pixmaps icons (*.png) & i18n files + setup_params = { + "package_data": package_data_windows, + } - data_files.append((dst, [src])) + return setup_params - # Add GUI executable details - windows.append({ - "script": "build\\_scripts\\youtube-dl-gui", - "icon_resources": [(0, "youtube_dl_gui\\data\\pixmaps\\youtube-dl-gui.ico")] - }) - setup_params = { - "windows": windows, - "data_files": data_files, - "options": {"py2exe": options} - } +params = dict() - return setup_params +if PYINSTALLER: + make_executable = True + pyinstaller_cmd.update({"pyinstaller": BuildPyinstallerBin}) +else: + make_executable = False - if PY2EXE: - return py2exe_setup() + if on_windows(): + params = setup_windows() + else: + params = setup_linux() - return normal_setup() + params["entry_points"] = { + "console_scripts": ["youtube-dl-gui = " + __packagename__ + ":main"] + } -if on_windows(): - params = windows_setup() -else: - params = linux_setup() +if make_executable: + cmdclass.update(pyinstaller_cmd) setup( - author = __author__, - name = __appname__, - version = __version__, - license = __license__, - author_email = __contact__, - url = __projecturl__, - description = __description__, - long_description = __descriptionfull__, - packages = [__packagename__], - cmdclass = cmdclass, - + name=__packagename__, + version=__version__, + description=DESCRIPTION, + long_description=LONG_DESCRIPTION, + url=__projecturl__, + author=__author__, + author_email=__contact__, + maintainer=__maintainer__, + maintainer_email=__maintainer_contact__, + license=__license__, + packages=[__packagename__], + classifiers=[ + "Topic :: Multimedia :: Video", + "Development Status :: 5 - Production/Stable", + "Environment :: Console", + "License :: Public Domain", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.2", + "Programming Language :: Python :: 3.3", + "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: Implementation", + "Programming Language :: Python :: Implementation :: CPython", + ], + cmdclass=cmdclass, **params ) diff --git a/youtube_dl_gui/__init__.py b/youtube_dl_gui/__init__.py index a4de1670..3740d940 100644 --- a/youtube_dl_gui/__init__.py +++ b/youtube_dl_gui/__init__.py @@ -15,7 +15,7 @@ import sys -import os.path +import os if __package__ is None and not hasattr(sys, 'frozen'): # direct call of __main__.py @@ -31,9 +31,7 @@ print(error) sys.exit(1) -__packagename__ = "youtube_dl_gui" - -# For package use +from .formats import * from .version import __version__ from .info import ( __author__, @@ -46,12 +44,8 @@ __descriptionfull__, ) -gettext.install(__packagename__) -from .formats import reload_strings - from .logmanager import LogManager from .optionsmanager import OptionsManager - from .utils import ( get_config_path, get_locale_file, @@ -59,8 +53,7 @@ YOUTUBEDL_BIN ) -_ = gettext.gettext - +__packagename__ = "youtube_dl_gui" # Set config path and create options and log managers config_path = get_config_path() @@ -74,14 +67,23 @@ # Set gettext before MainFrame import # because the GUI strings are class level attributes locale_dir = get_locale_file() +lang = gettext.gettext try: - gettext.translation(__packagename__, locale_dir, [opt_manager.options['locale_name']]).install(unicode=True) + lang = gettext.translation(__packagename__, + locale_dir, + [opt_manager.options['locale_name']]) except IOError: opt_manager.options['locale_name'] = 'en_US' - gettext.install(__packagename__) + lang = gettext.translation(__packagename__, + locale_dir, + [opt_manager.options['locale_name']]) + + +# Redefine _ to gettext in builtins +_ = lang.gettext +OUTPUT_FORMATS, DEFAULT_FORMATS, AUDIO_FORMATS, VIDEO_FORMATS, FORMATS = reload_strings(_) -reload_strings() from .mainframe import MainFrame diff --git a/youtube_dl_gui/formats.py b/youtube_dl_gui/formats.py index c14d6687..09c36308 100644 --- a/youtube_dl_gui/formats.py +++ b/youtube_dl_gui/formats.py @@ -84,7 +84,8 @@ FORMATS.update(AUDIO_FORMATS) -def reload_strings(): +# noinspection PyPep8Naming +def reload_strings(func): # IF YOU DONT WANT YOUR EYES TO BLEED STOP HERE # YOU HAVE BEEN WARNED # DO NOT LOOK THE CODE BELOW @@ -99,11 +100,7 @@ def reload_strings(): # NOTE Remove # Code is so messed up that i need to reload strings else # the translations wont work on the about gettext tags - global OUTPUT_FORMATS - global DEFAULT_FORMATS - global VIDEO_FORMATS - global AUDIO_FORMATS - global FORMATS + _ = func OUTPUT_FORMATS = tdict([ (0, _("ID")), @@ -181,3 +178,5 @@ def reload_strings(): FORMATS = DEFAULT_FORMATS.copy() FORMATS.update(VIDEO_FORMATS) FORMATS.update(AUDIO_FORMATS) + + return OUTPUT_FORMATS, DEFAULT_FORMATS, AUDIO_FORMATS, VIDEO_FORMATS, FORMATS diff --git a/youtube_dl_gui/info.py b/youtube_dl_gui/info.py index 4ffc7ccd..6d0fd26b 100644 --- a/youtube_dl_gui/info.py +++ b/youtube_dl_gui/info.py @@ -11,7 +11,9 @@ __author__ = 'Sotiris Papadopoulos' __contact__ = 'ytubedlg@gmail.com' -__projecturl__ = 'https://mrs0m30n3.github.io/youtube-dl-gui/' +__maintainer__ = 'Oleksis Fraga' +__maintainer_contact__ = 'oleksis.fraga@gmail.com' +__projecturl__ = 'https://github.com/oleksis/youtube-dl-gui/' __appname__ = 'Youtube-DLG' __license__ = 'UNLICENSE' @@ -19,7 +21,7 @@ __description__ = 'Youtube-dl GUI' __descriptionfull__ = '''A cross platform front-end GUI of the popular -youtube-dl written in wxPython''' +youtube-dl written in wxPython Phoenix''' __licensefull__ = ''' This is free and unencumbered software released into the public domain. diff --git a/youtube_dl_gui/locale/es_CU/LC_MESSAGES/youtube_dl_gui.po b/youtube_dl_gui/locale/es_CU/LC_MESSAGES/youtube_dl_gui.po new file mode 100644 index 00000000..fc8e06d0 --- /dev/null +++ b/youtube_dl_gui/locale/es_CU/LC_MESSAGES/youtube_dl_gui.po @@ -0,0 +1,604 @@ +# Youtube-dlG localization file. +# FIRST AUTHOR: Sotiris Papadopoulos , 2015. +# Ismael Ferreras Morezuelas , 2016-2017. +# Oleksis Fraga Menéndez , 2020. +# +msgid "" +msgstr "" +"Project-Id-Version: youtube-dlg 1.0\n" +"POT-Creation-Date: 2020-07-24 22:54+EET\n" +"PO-Revision-Date: 2017-06-15 22:54+EEST\n" +"Last-Translator: Oleksis Fraga Menéndez \n" +"Language-Team: Spanish <>\n" +"Language: es_CU\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: pygettext.py 1.5\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Gtranslator 2.91.7\n" + +#: youtube_dl_gui/__init__.py:91 +msgid "Error" +msgstr "Error" + +#: youtube_dl_gui/__init__.py:91 +msgid "Failed to locate youtube-dl and updates are disabled" +msgstr "Fallido en localizar youtube-dl y las actualizaciones estan deshabilitadas" + +#: youtube_dl_gui/formats.py:9 youtube_dl_gui/formats.py:109 +msgid "ID" +msgstr "Identificador" + +#: youtube_dl_gui/formats.py:10 youtube_dl_gui/formats.py:110 +#: youtube_dl_gui/mainframe.py:140 +msgid "Title" +msgstr "Título" + +#: youtube_dl_gui/formats.py:11 youtube_dl_gui/formats.py:111 +msgid "Title + ID" +msgstr "Título + Identificador" + +#: youtube_dl_gui/formats.py:12 youtube_dl_gui/formats.py:112 +msgid "Title + Quality" +msgstr "Título + Calidad" + +#: youtube_dl_gui/formats.py:13 youtube_dl_gui/formats.py:113 +msgid "Title + ID + Quality" +msgstr "Título + Identificador + Calidad" + +#: youtube_dl_gui/formats.py:14 youtube_dl_gui/formats.py:114 +msgid "Custom" +msgstr "Personalizado" + +#: youtube_dl_gui/formats.py:19 youtube_dl_gui/formats.py:119 +#: youtube_dl_gui/mainframe.py:503 youtube_dl_gui/mainframe.py:506 +msgid "default" +msgstr "predeterminada" + +#: youtube_dl_gui/mainframe.py:97 +msgid "Enter URLs below" +msgstr "Pega direcciones URL en la parte inferior" + +#: youtube_dl_gui/mainframe.py:98 +msgid "Update" +msgstr "Actualizar" + +#: youtube_dl_gui/mainframe.py:99 youtube_dl_gui/optionsframe.py:41 +msgid "Options" +msgstr "Ajustes" + +#: youtube_dl_gui/mainframe.py:100 youtube_dl_gui/optionsframe.py:584 +msgid "Stop" +msgstr "Detener" + +#: youtube_dl_gui/mainframe.py:101 +msgid "Info" +msgstr "Info" + +#: youtube_dl_gui/mainframe.py:102 +msgid "Welcome" +msgstr "Bienvenid@" + +#: youtube_dl_gui/mainframe.py:103 +msgid "Warning" +msgstr "Advertencia" + +#: youtube_dl_gui/mainframe.py:105 +msgid "Add" +msgstr "Añadir" + +#: youtube_dl_gui/mainframe.py:106 +msgid "Download list" +msgstr "Descargar lista" + +#: youtube_dl_gui/mainframe.py:107 youtube_dl_gui/mainframe.py:516 +#: youtube_dl_gui/mainframe.py:534 +msgid "Delete" +msgstr "Borrar" + +#: youtube_dl_gui/mainframe.py:108 +msgid "Play" +msgstr "Reproducir" + +#: youtube_dl_gui/mainframe.py:109 +msgid "Up" +msgstr "Subir" + +#: youtube_dl_gui/mainframe.py:110 +msgid "Down" +msgstr "Bajar" + +#: youtube_dl_gui/mainframe.py:111 +msgid "Reload" +msgstr "Recargar" + +#: youtube_dl_gui/mainframe.py:112 youtube_dl_gui/mainframe.py:448 +#: youtube_dl_gui/mainframe.py:649 +msgid "Pause" +msgstr "Pausar" + +#: youtube_dl_gui/mainframe.py:113 youtube_dl_gui/mainframe.py:865 +#: youtube_dl_gui/mainframe.py:866 youtube_dl_gui/optionsframe.py:582 +msgid "Start" +msgstr "Iniciar" + +#: youtube_dl_gui/mainframe.py:114 +msgid "About" +msgstr "Acerca de" + +#: youtube_dl_gui/mainframe.py:115 +msgid "View Log" +msgstr "Ver registro" + +#: youtube_dl_gui/mainframe.py:117 +msgid "Successfully downloaded {0} URL(s) in {1} day(s) {2} hour(s) {3} minute(s) {4} second(s)" +msgstr "Se han terminado de descargar {0} elementos en {1} días, {2} horas, {3} minutos y {4} segundos." + +#: youtube_dl_gui/mainframe.py:119 +msgid "Downloads completed" +msgstr "Se han completado todas las descargas." + +#: youtube_dl_gui/mainframe.py:120 +msgid "Total Progress: {0:.1f}% | Queued ({1}) Paused ({2}) Active ({3}) Completed ({4}) Error ({5})" +msgstr "Progreso total: {0:.1f}% | Restantes ({1}) En pausa ({2}) Activas ({3}) Terminadas ({4}) Fallidas ({5})" + +#: youtube_dl_gui/mainframe.py:121 +msgid "Stopping downloads" +msgstr "Parando descargas en curso." + +#: youtube_dl_gui/mainframe.py:122 +msgid "Downloads stopped" +msgstr "Se han detenido todas las descargas en curso." + +#: youtube_dl_gui/mainframe.py:123 +msgid "You need to provide at least one URL" +msgstr "Es necesario proporcionar al menos una dirección de descarga para poder empezar." + +#: youtube_dl_gui/mainframe.py:124 +msgid "Downloads started" +msgstr "Comenzando descargas." + +#: youtube_dl_gui/mainframe.py:125 +msgid "Choose Directory" +msgstr "Elegir carpeta" + +#: youtube_dl_gui/mainframe.py:127 +msgid "Download in progress. Please wait for all downloads to complete" +msgstr "Descarga en progreso. Por favor espere por todas las descargas se completen." + +#: youtube_dl_gui/mainframe.py:128 +msgid "Update already in progress" +msgstr "Actualizacion está en progreso." + +#: youtube_dl_gui/mainframe.py:130 +msgid "Downloading latest youtube-dl. Please wait..." +msgstr "Descargando última versión de youtube-dl. Por favor espere..." + +#: youtube_dl_gui/mainframe.py:131 +msgid "Youtube-dl download failed [{0}]" +msgstr "Descarga de Youtube-dl a fallado [{0}]." + +#: youtube_dl_gui/mainframe.py:132 +msgid "Successfully downloaded youtube-dl" +msgstr "Exitosamente descargado youtube-dl." + +#: youtube_dl_gui/mainframe.py:134 +msgid "Unable to open directory: '{dir}'. The specified path does not exist" +msgstr "No se ha podido abrir la carpeta: «{dir}». La ruta especificada no existe." + +#: youtube_dl_gui/mainframe.py:136 +msgid "Error while shutting down. Make sure you typed the correct password" +msgstr "Error mientras se apagaba el equipo. Asegúrate de que la contraseña escrita sea la correcta." + +#: youtube_dl_gui/mainframe.py:138 +msgid "Shutting down system" +msgstr "Apagando el equipo" + +#: youtube_dl_gui/mainframe.py:141 +msgid "Extension" +msgstr "Extensión" + +#: youtube_dl_gui/mainframe.py:142 +msgid "Size" +msgstr "Tamaño" + +#: youtube_dl_gui/mainframe.py:143 +msgid "Percent" +msgstr "Porcentaje" + +#: youtube_dl_gui/mainframe.py:144 +msgid "ETA" +msgstr "Tiempo estimado" + +#: youtube_dl_gui/mainframe.py:145 +msgid "Speed" +msgstr "Velocidad" + +#: youtube_dl_gui/mainframe.py:146 +msgid "Status" +msgstr "Estado" + +#: youtube_dl_gui/mainframe.py:235 +msgid "Get URL" +msgstr "Copiar dirección URL al portapapeles" + +#: youtube_dl_gui/mainframe.py:236 +msgid "Get command" +msgstr "Copiar los argumentos de descarga al portapapeles" + +#: youtube_dl_gui/mainframe.py:237 +msgid "Open destination" +msgstr "Abrir carpeta de descarga" + +#: youtube_dl_gui/mainframe.py:238 +msgid "Re-enter" +msgstr "Reintroducir" + +#: youtube_dl_gui/mainframe.py:458 +msgid "Resume" +msgstr "Reanudar" + +#: youtube_dl_gui/mainframe.py:480 +msgid "Video" +msgstr "Vídeo" + +#: youtube_dl_gui/mainframe.py:484 +msgid "Audio" +msgstr "Audio" + +#: youtube_dl_gui/mainframe.py:516 +msgid "No items selected. Please pick an action" +msgstr "No ha seleccionado elementos. Por favor elige una acción." + +#: youtube_dl_gui/mainframe.py:516 +msgid "Remove all" +msgstr "Borrar todo" + +#: youtube_dl_gui/mainframe.py:516 +msgid "Remove completed" +msgstr "Borrado completado." + +#: youtube_dl_gui/mainframe.py:534 +msgid "Are you sure you want to remove selected items?" +msgstr "¿Seguro que quieres borrar los elementos seleccionados?" + +#: youtube_dl_gui/mainframe.py:546 +msgid "Item is active, cannot remove" +msgstr "Elemento está activo, no se puede borrar." + +#: youtube_dl_gui/mainframe.py:579 +msgid "Item is not completed" +msgstr "Elemento no se ha completado." + +#: youtube_dl_gui/mainframe.py:668 +msgid "Update in progress. Please wait for the update to complete" +msgstr "Actualización en progreso. Por favor espera que la actualización se complete." + +#: youtube_dl_gui/mainframe.py:716 +msgid "Logging is disabled" +msgstr "El registro de errores está desactivado." + +#: youtube_dl_gui/mainframe.py:891 +msgid "Shutdown" +msgstr "Apagar" + +#: youtube_dl_gui/mainframe.py:891 +msgid "Shutting down in {0} second(s)" +msgstr "Apagando el equipo en {0} seg(s)." + +#: youtube_dl_gui/mainframe.py:980 +msgid "No items to download" +msgstr "Sin elementos para descargar." + +#: youtube_dl_gui/mainframe.py:1040 +msgid "Updates are disabled for your system. Please use the system's package manager to update youtube-dl." +msgstr "Actualizaciones están deshabilitadas por su sistema. Por favor use el administrador de paquetes de su sistema para actualizar youtube-dl." + +#: youtube_dl_gui/mainframe.py:1065 +msgid "Are you sure you want to exit?" +msgstr "¿Seguro que deseas salir?" + +#: youtube_dl_gui/mainframe.py:1065 +msgid "Exit" +msgstr "Salir" + +#: youtube_dl_gui/mainframe.py:1306 youtube_dl_gui/mainframe.py:1456 +msgid "Cancel" +msgstr "Cancelar" + +#: youtube_dl_gui/mainframe.py:1455 +msgid "OK" +msgstr "Aceptar" + +#: youtube_dl_gui/optionsframe.py:65 +msgid "Reset" +msgstr "Reiniciar" + +#: youtube_dl_gui/optionsframe.py:66 +msgid "Close" +msgstr "Cerrar" + +#: youtube_dl_gui/optionsframe.py:72 +msgid "General" +msgstr "General" + +#: youtube_dl_gui/optionsframe.py:73 +msgid "Formats" +msgstr "Formatos" + +#: youtube_dl_gui/optionsframe.py:74 +msgid "Downloads" +msgstr "Descargas" + +#: youtube_dl_gui/optionsframe.py:75 +msgid "Advanced" +msgstr "Avanzado" + +#: youtube_dl_gui/optionsframe.py:76 +msgid "Extra" +msgstr "Extra" + +#: youtube_dl_gui/optionsframe.py:310 +msgid "Language" +msgstr "Idioma" + +#: youtube_dl_gui/optionsframe.py:313 +msgid "Filename format" +msgstr "Nomenclatura de archivos" + +#: youtube_dl_gui/optionsframe.py:318 +msgid "Filename options" +msgstr "Opciones de nomenclatura" + +#: youtube_dl_gui/optionsframe.py:319 +msgid "Restrict filenames to ASCII" +msgstr "Limitar nombres de archivo a ASCII" + +#: youtube_dl_gui/optionsframe.py:321 +msgid "More options" +msgstr "Más opciones" + +#: youtube_dl_gui/optionsframe.py:322 +msgid "Confirm on exit" +msgstr "Confirmar salida" + +#: youtube_dl_gui/optionsframe.py:323 +msgid "Confirm item deletion" +msgstr "Confirmar al borrar archivos" + +#: youtube_dl_gui/optionsframe.py:324 +msgid "Inform me on download completion" +msgstr "Avísarme cuando las descargas se completen" + +#: youtube_dl_gui/optionsframe.py:326 +msgid "Shutdown on download completion" +msgstr "Apagar al completar las descargas" + +#: youtube_dl_gui/optionsframe.py:337 +msgid "SUDO password" +msgstr "Contraseña de SuperUsuario" + +#: youtube_dl_gui/optionsframe.py:415 youtube_dl_gui/optionsframe.py:816 +msgid "In order for the changes to take effect please restart {0}" +msgstr "Reinicia {0} para que los cambios surtan efecto." + +#: youtube_dl_gui/optionsframe.py:416 youtube_dl_gui/optionsframe.py:817 +msgid "Restart" +msgstr "Reiniciar" + +#: youtube_dl_gui/optionsframe.py:463 +msgid "high" +msgstr "Alta" + +#: youtube_dl_gui/optionsframe.py:463 +msgid "low" +msgstr "Baja" + +#: youtube_dl_gui/optionsframe.py:463 +msgid "mid" +msgstr "Intermedia" + +#: youtube_dl_gui/optionsframe.py:468 +msgid "Video formats" +msgstr "Formatos de vídeo" + +#: youtube_dl_gui/optionsframe.py:471 +msgid "Audio formats" +msgstr "Formatos de audio" + +#: youtube_dl_gui/optionsframe.py:474 +msgid "Post-Process options" +msgstr "Opciones de posproceso" + +#: youtube_dl_gui/optionsframe.py:475 +msgid "Keep original files" +msgstr "Conservar los archivos originales" + +#: youtube_dl_gui/optionsframe.py:476 +msgid "Extract audio from video file" +msgstr "Extraer el audio del vídeo" + +#: youtube_dl_gui/optionsframe.py:477 +msgid "Embed thumbnail in audio file" +msgstr "Incluir una miniatura en el archivo de audio" + +#: youtube_dl_gui/optionsframe.py:478 +msgid "Add metadata to file" +msgstr "Incluir metadatos en el archivo" + +#: youtube_dl_gui/optionsframe.py:480 +msgid "Audio quality" +msgstr "Calidad del audio" + +#: youtube_dl_gui/optionsframe.py:538 +msgid "English" +msgstr "Inglés" + +#: youtube_dl_gui/optionsframe.py:539 +msgid "French" +msgstr "Francés" + +#: youtube_dl_gui/optionsframe.py:540 +msgid "German" +msgstr "Alemán" + +#: youtube_dl_gui/optionsframe.py:541 +msgid "Greek" +msgstr "Griego" + +#: youtube_dl_gui/optionsframe.py:542 +msgid "Hebrew" +msgstr "Hebreo" + +#: youtube_dl_gui/optionsframe.py:543 +msgid "Italian" +msgstr "Italiano" + +#: youtube_dl_gui/optionsframe.py:544 +msgid "Portuguese" +msgstr "Portugués" + +#: youtube_dl_gui/optionsframe.py:545 +msgid "Russian" +msgstr "Ruso" + +#: youtube_dl_gui/optionsframe.py:546 +msgid "Spanish" +msgstr "Español" + +#: youtube_dl_gui/optionsframe.py:547 +msgid "Swedish" +msgstr "Sueco" + +#: youtube_dl_gui/optionsframe.py:548 +msgid "Turkish" +msgstr "Turco" + +#: youtube_dl_gui/optionsframe.py:564 +msgid "None" +msgstr "Ninguno" + +#: youtube_dl_gui/optionsframe.py:565 +msgid "Automatic subtitles (YOUTUBE ONLY)" +msgstr "Descargar subtítulos en archivos automáticamente (sólo en YouTube)" + +#: youtube_dl_gui/optionsframe.py:566 +msgid "All available subtitles" +msgstr "Descargar todos los subtítulos disponibles" + +#: youtube_dl_gui/optionsframe.py:567 +msgid "Subtitles by language" +msgstr "Idioma de subtítulos" + +#: youtube_dl_gui/optionsframe.py:573 +msgid "Subtitles" +msgstr "Subtítulos" + +#: youtube_dl_gui/optionsframe.py:577 +msgid "Subtitles options" +msgstr "Opciones de subtítulos" + +#: youtube_dl_gui/optionsframe.py:578 +msgid "Embed subtitles into video file (mp4 ONLY)" +msgstr "Insertar los subtítulos en el vídeo (sólo para vídeos en mp4)" + +#: youtube_dl_gui/optionsframe.py:580 +msgid "Playlist" +msgstr "Lista de reproducción" + +#: youtube_dl_gui/optionsframe.py:586 youtube_dl_gui/optionsframe.py:591 +msgid "Max" +msgstr "Máximo" + +#: youtube_dl_gui/optionsframe.py:589 +msgid "Filesize" +msgstr "Tamaño de archivo" + +#: youtube_dl_gui/optionsframe.py:594 +msgid "Min" +msgstr "Mínimo" + +#: youtube_dl_gui/optionsframe.py:723 +msgid "Retries" +msgstr "Reintentos" + +#: youtube_dl_gui/optionsframe.py:726 +msgid "Authentication" +msgstr "Autenticación" + +#: youtube_dl_gui/optionsframe.py:728 +msgid "Username" +msgstr "Nombre de usuario" + +#: youtube_dl_gui/optionsframe.py:730 +msgid "Password" +msgstr "Contraseña" + +#: youtube_dl_gui/optionsframe.py:732 +msgid "Video password" +msgstr "Contraseña del vídeo" + +#: youtube_dl_gui/optionsframe.py:735 +msgid "Network" +msgstr "Red" + +#: youtube_dl_gui/optionsframe.py:737 +msgid "Proxy" +msgstr "Proxy" + +#: youtube_dl_gui/optionsframe.py:739 +msgid "User agent" +msgstr "Agente de usuario" + +#: youtube_dl_gui/optionsframe.py:741 +msgid "Referer" +msgstr "Página de origen" + +#: youtube_dl_gui/optionsframe.py:744 +msgid "Logging" +msgstr "Registro de errores" + +#: youtube_dl_gui/optionsframe.py:746 +msgid "Enable log" +msgstr "Registrar los errores del programa" + +#: youtube_dl_gui/optionsframe.py:747 +msgid "View" +msgstr "Ver registro" + +#: youtube_dl_gui/optionsframe.py:748 +msgid "Clear" +msgstr "Borrar registro" + +#: youtube_dl_gui/optionsframe.py:858 +msgid "Youtube-dl command line options (e.g. --help)" +msgstr "Argumentos adicionales para Youtube-dl (p. ej. --help)" + +#: youtube_dl_gui/optionsframe.py:861 +msgid "Extra options" +msgstr "Otras opciones" + +#: youtube_dl_gui/optionsframe.py:863 +msgid "Debug youtube-dl" +msgstr "Depurar youtube-dl" + +#: youtube_dl_gui/optionsframe.py:864 +msgid "Ignore errors" +msgstr "Ignorar errores" + +#: youtube_dl_gui/optionsframe.py:865 +msgid "Ignore youtube-dl config" +msgstr "Ignorar la configuración de youtube-dl" + +#: youtube_dl_gui/optionsframe.py:866 +msgid "No mtime" +msgstr "Sin mtime" + +#: youtube_dl_gui/optionsframe.py:867 +msgid "Prefer native HLS" +msgstr "Utilizar HLS nativo cuando sea posible" + +#: youtube_dl_gui/optionsframe.py:928 +msgid "Log Viewer" +msgstr "Visor de errores" diff --git a/youtube_dl_gui/mainframe.py b/youtube_dl_gui/mainframe.py index fd6f8994..d612a6b7 100644 --- a/youtube_dl_gui/mainframe.py +++ b/youtube_dl_gui/mainframe.py @@ -4,8 +4,15 @@ import os -import gettext -_ = gettext.gettext +from youtube_dl_gui import lang +from youtube_dl_gui import ( + DEFAULT_FORMATS, + VIDEO_FORMATS, + AUDIO_FORMATS, + FORMATS, + OUTPUT_FORMATS +) + import wx import wx.adv @@ -45,23 +52,19 @@ from .widgets import CustomComboBox -from .formats import ( - DEFAULT_FORMATS, - VIDEO_FORMATS, - AUDIO_FORMATS, - FORMATS -) - from .info import ( __descriptionfull__, __licensefull__, __projecturl__, __appname__, - __author__ + __author__, + __maintainer__, + __maintainer_contact__ ) from .version import __version__ +_ = lang.gettext class MainFrame(wx.Frame): @@ -731,7 +734,10 @@ def _on_about(self, event): info.SetDescription(__descriptionfull__) info.SetWebSite(__projecturl__) info.SetLicense(__licensefull__) - info.AddDeveloper(__author__) + info.AddDeveloper( + (__author__ + "\n" + __maintainer__ + "\n" + + "see AUTHORS file for the complete list.") + ) wx.adv.AboutBox(info) diff --git a/youtube_dl_gui/optionsframe.py b/youtube_dl_gui/optionsframe.py index 56ee2366..2f72598d 100644 --- a/youtube_dl_gui/optionsframe.py +++ b/youtube_dl_gui/optionsframe.py @@ -4,12 +4,12 @@ import os -import gettext -_ = gettext.gettext +import warnings import wx import wx.adv from wx.lib.art import flagart +from wx.lib.embeddedimage import PyEmbeddedImage from .utils import ( TwoWayOrderedDict as twodict, @@ -19,12 +19,15 @@ ) from .info import __appname__ - -from .formats import ( +from youtube_dl_gui import lang +from youtube_dl_gui import ( OUTPUT_FORMATS, VIDEO_FORMATS, AUDIO_FORMATS ) + +_ = lang.gettext + # REFACTOR Move all formats, etc to formats.py @@ -219,11 +222,27 @@ def crt_bitmap_combobox(self, choices, size=(-1, -1), event_handler=None): for item in choices: lang_code, lang_name = item + _lang, country = lang_code.split('_') - _, country = lang_code.split('_') + warnings.filterwarnings("ignore") if country in flagart.catalog: flag_bmp = flagart.catalog[country].getBitmap() + elif country == 'CU': + # Cuba Flag .png in base64encode.org + # Me dicen Cuba ;) + flag_bmp = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAABGdBTUEAAK/INwWK6QAAABl0R" + "Vh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHFSURBVHjaYrzNwKDquJyBjYfhxz" + "eGfwwMf/4w/PnH8AuI/sBIMPoBRL8Y2NgAAohFhYHhk831pRr+H7iF/v/7/+/f/z8Q8u8/IOP" + "Pn39///37/ef/73//gCLzc/YABBDjZwYG7uqqT8+e8yUl/LawYWD4D9T2/z8DFIOpf2CakZHx" + "/cePAAHEAnTF73//OX///jx9Bs/Xrwyu7v8ZGaAKYYgJTDIyMrAxMQAEEONHBgb29nZmM7Pfs" + "2f//P5jH5/WdvMwoON///kHcgaQ/AslpQQ5lhRuBQggkA0srq4MurrMv/+wTppk9/LMp11f56" + "gF/f4FhH9//fn7+/e/X0ANf/79lOBiYLgHEEAgDX927GD69On31Mk/f//ay6uz2ypa/B8DxFQ" + "Q+gOyAehjCREOBgYZgABifMvAwJWV9f/+/e9//vAmxP0PCfuPDTAwAP3A+ObNG4AAAjvpz58P" + "f/5wZaT/9vL9/+f/f2iogEhg+ILDiwESSt9+/AEIIBYeBoaPf/+3RfT9esvwZ+FNiO3AGPgNY" + "fwFxcPfv////vv/9z/DvuY5AAHEeJqBwVR0JjRSgdH5/w/QUzD0C0z+A5MMYJIJIMAA5qlT7L" + "92ZXAAAAAASUVORK5CYII=").getBitmap() else: flag_bmp = flagart.catalog["BLANK"].getBitmap() @@ -268,6 +287,7 @@ class GeneralTab(TabPanel): # Lang code = _ LOCALE_NAMES = twodict([ ('ar_SA', 'Arabic'), + ('es_CU', 'Cuba'), ('cs_CZ', 'Czech'), ('en_US', 'English'), ('fr_FR', 'French'), @@ -276,7 +296,7 @@ class GeneralTab(TabPanel): ('ko_KR', 'Korean'), ('pt_BR', 'Portuguese'), ('ru_RU', 'Russian'), - ('es_ES', 'Spanish') + ('es_ES', 'Spanish'), ]) OUTPUT_TEMPLATES = [ diff --git a/youtube_dl_gui/optionsmanager.py b/youtube_dl_gui/optionsmanager.py index 1b3ae340..9533fd89 100644 --- a/youtube_dl_gui/optionsmanager.py +++ b/youtube_dl_gui/optionsmanager.py @@ -15,7 +15,7 @@ get_default_lang ) -from .formats import ( +from youtube_dl_gui import ( OUTPUT_FORMATS, FORMATS ) diff --git a/youtube_dl_gui/updatemanager.py b/youtube_dl_gui/updatemanager.py index 068b81ad..3682dabe 100644 --- a/youtube_dl_gui/updatemanager.py +++ b/youtube_dl_gui/updatemanager.py @@ -58,8 +58,8 @@ def __init__(self, download_path, quiet=False): self.start() def get_latest_sourcefile(self): + source_file = self.GITHUB_API try: - source_file = self.GITHUB_API stream = urlopen(self.LATEST_YOUTUBE_DL_API, timeout=self.DOWNLOAD_TIMEOUT) latest_json = json.load(stream) @@ -69,11 +69,11 @@ def get_latest_sourcefile(self): if asset["name"] == YOUTUBEDL_BIN: source_file = asset["browser_download_url"] break - - return source_file except (HTTPError, URLError, json.JSONDecodeError) as error: self._talk_to_gui('error', error) + return source_file + def run(self): self._talk_to_gui('download') diff --git a/youtube_dl_gui/version.py b/youtube_dl_gui/version.py index 5f5321e3..cd9ba077 100644 --- a/youtube_dl_gui/version.py +++ b/youtube_dl_gui/version.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- -__version__ = '1.0' +__version__ = '1.0.0' From dc74175f1347d3927cd6223f63dd460b86a5e94c Mon Sep 17 00:00:00 2001 From: oleksis <“oleksis.fraga@gmail.com”> Date: Sat, 25 Jul 2020 13:49:44 -0400 Subject: [PATCH 05/34] Update setup.py and locale es_CU youtube_dl_gui.po --- setup.py | 23 +- .../es_CU/LC_MESSAGES/youtube_dl_gui.po | 326 +++++++++--------- 2 files changed, 171 insertions(+), 178 deletions(-) diff --git a/setup.py b/setup.py index bc1bac0b..90f3348d 100644 --- a/setup.py +++ b/setup.py @@ -111,7 +111,7 @@ def version2str(commit=0): return "%s.%s.%s.%s" % version_tuple -# noinspection PyAttributeOutsideInit +# noinspection PyAttributeOutsideInit,PyArgumentList class BuildTranslations(Command): description = "Build the translation files" user_options = [] @@ -141,7 +141,7 @@ def run(self): sys.exit(1) -# noinspection PyAttributeOutsideInit +# noinspection PyAttributeOutsideInit,PyArgumentList class BuildNoUpdate(Command): description = "Build with updates disabled" user_options = [] @@ -245,7 +245,6 @@ def run(self, version=version_file): SetVersion("./dist/youtube-dl-gui.exe", version) -pyinstaller_cmd = dict() pyinstaller_console = [ { "script": "./"+__packagename__+"/__main__.py", @@ -313,11 +312,8 @@ def setup_windows(): params = dict() if PYINSTALLER: - make_executable = True - pyinstaller_cmd.update({"pyinstaller": BuildPyinstallerBin}) + cmdclass.update({"pyinstaller": BuildPyinstallerBin}) else: - make_executable = False - if on_windows(): params = setup_windows() else: @@ -328,9 +324,6 @@ def setup_windows(): } -if make_executable: - cmdclass.update(pyinstaller_cmd) - setup( name=__packagename__, version=__version__, @@ -344,19 +337,17 @@ def setup_windows(): license=__license__, packages=[__packagename__], classifiers=[ - "Topic :: Multimedia :: Video", + "Topic :: Multimedia :: Video :: User Interfaces", "Development Status :: 5 - Production/Stable", - "Environment :: Console", + "Environment :: MacOS X :: Cocoa", + "Environment :: Win32 (MS Windows)", + "Environment :: X11 Applications :: GTK", "License :: Public Domain", "Programming Language :: Python", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.2", - "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: Implementation", "Programming Language :: Python :: Implementation :: CPython", ], diff --git a/youtube_dl_gui/locale/es_CU/LC_MESSAGES/youtube_dl_gui.po b/youtube_dl_gui/locale/es_CU/LC_MESSAGES/youtube_dl_gui.po index fc8e06d0..0473b62d 100644 --- a/youtube_dl_gui/locale/es_CU/LC_MESSAGES/youtube_dl_gui.po +++ b/youtube_dl_gui/locale/es_CU/LC_MESSAGES/youtube_dl_gui.po @@ -6,8 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: youtube-dlg 1.0\n" -"POT-Creation-Date: 2020-07-24 22:54+EET\n" -"PO-Revision-Date: 2017-06-15 22:54+EEST\n" +"POT-Creation-Date: 2020-07-25 10:26-0400\n" +"PO-Revision-Date: 2020-07-25 10:26+UTC-05\n" "Last-Translator: Oleksis Fraga Menéndez \n" "Language-Team: Spanish <>\n" "Language: es_CU\n" @@ -18,587 +18,589 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Gtranslator 2.91.7\n" -#: youtube_dl_gui/__init__.py:91 + +#: youtube_dl_gui\__init__.py:100 msgid "Error" msgstr "Error" -#: youtube_dl_gui/__init__.py:91 +#: youtube_dl_gui\__init__.py:100 msgid "Failed to locate youtube-dl and updates are disabled" msgstr "Fallido en localizar youtube-dl y las actualizaciones estan deshabilitadas" -#: youtube_dl_gui/formats.py:9 youtube_dl_gui/formats.py:109 +#: youtube_dl_gui\formats.py:10 youtube_dl_gui\formats.py:106 msgid "ID" msgstr "Identificador" -#: youtube_dl_gui/formats.py:10 youtube_dl_gui/formats.py:110 -#: youtube_dl_gui/mainframe.py:140 +#: youtube_dl_gui\formats.py:11 youtube_dl_gui\formats.py:107 +#: youtube_dl_gui\mainframe.py:142 msgid "Title" msgstr "Título" -#: youtube_dl_gui/formats.py:11 youtube_dl_gui/formats.py:111 +#: youtube_dl_gui\formats.py:12 youtube_dl_gui\formats.py:108 msgid "Title + ID" msgstr "Título + Identificador" -#: youtube_dl_gui/formats.py:12 youtube_dl_gui/formats.py:112 +#: youtube_dl_gui\formats.py:13 youtube_dl_gui\formats.py:109 msgid "Title + Quality" msgstr "Título + Calidad" -#: youtube_dl_gui/formats.py:13 youtube_dl_gui/formats.py:113 +#: youtube_dl_gui\formats.py:14 youtube_dl_gui\formats.py:110 msgid "Title + ID + Quality" msgstr "Título + Identificador + Calidad" -#: youtube_dl_gui/formats.py:14 youtube_dl_gui/formats.py:114 +#: youtube_dl_gui\formats.py:15 youtube_dl_gui\formats.py:111 msgid "Custom" msgstr "Personalizado" -#: youtube_dl_gui/formats.py:19 youtube_dl_gui/formats.py:119 -#: youtube_dl_gui/mainframe.py:503 youtube_dl_gui/mainframe.py:506 +#: youtube_dl_gui\formats.py:20 youtube_dl_gui\formats.py:116 +#: youtube_dl_gui\mainframe.py:505 youtube_dl_gui\mainframe.py:508 msgid "default" msgstr "predeterminada" -#: youtube_dl_gui/mainframe.py:97 +#: youtube_dl_gui\mainframe.py:99 msgid "Enter URLs below" msgstr "Pega direcciones URL en la parte inferior" -#: youtube_dl_gui/mainframe.py:98 +#: youtube_dl_gui\mainframe.py:100 msgid "Update" msgstr "Actualizar" -#: youtube_dl_gui/mainframe.py:99 youtube_dl_gui/optionsframe.py:41 +#: youtube_dl_gui\mainframe.py:101 youtube_dl_gui\optionsframe.py:43 msgid "Options" msgstr "Ajustes" -#: youtube_dl_gui/mainframe.py:100 youtube_dl_gui/optionsframe.py:584 +#: youtube_dl_gui\mainframe.py:102 youtube_dl_gui\optionsframe.py:603 msgid "Stop" msgstr "Detener" -#: youtube_dl_gui/mainframe.py:101 +#: youtube_dl_gui\mainframe.py:103 msgid "Info" msgstr "Info" -#: youtube_dl_gui/mainframe.py:102 +#: youtube_dl_gui\mainframe.py:104 msgid "Welcome" msgstr "Bienvenid@" -#: youtube_dl_gui/mainframe.py:103 +#: youtube_dl_gui\mainframe.py:105 msgid "Warning" msgstr "Advertencia" -#: youtube_dl_gui/mainframe.py:105 +#: youtube_dl_gui\mainframe.py:107 msgid "Add" msgstr "Añadir" -#: youtube_dl_gui/mainframe.py:106 +#: youtube_dl_gui\mainframe.py:108 msgid "Download list" msgstr "Descargar lista" -#: youtube_dl_gui/mainframe.py:107 youtube_dl_gui/mainframe.py:516 -#: youtube_dl_gui/mainframe.py:534 +#: youtube_dl_gui\mainframe.py:109 youtube_dl_gui\mainframe.py:518 +#: youtube_dl_gui\mainframe.py:536 msgid "Delete" msgstr "Borrar" -#: youtube_dl_gui/mainframe.py:108 +#: youtube_dl_gui\mainframe.py:110 msgid "Play" msgstr "Reproducir" -#: youtube_dl_gui/mainframe.py:109 +#: youtube_dl_gui\mainframe.py:111 msgid "Up" msgstr "Subir" -#: youtube_dl_gui/mainframe.py:110 +#: youtube_dl_gui\mainframe.py:112 msgid "Down" msgstr "Bajar" -#: youtube_dl_gui/mainframe.py:111 +#: youtube_dl_gui\mainframe.py:113 msgid "Reload" msgstr "Recargar" -#: youtube_dl_gui/mainframe.py:112 youtube_dl_gui/mainframe.py:448 -#: youtube_dl_gui/mainframe.py:649 +#: youtube_dl_gui\mainframe.py:114 youtube_dl_gui\mainframe.py:450 +#: youtube_dl_gui\mainframe.py:651 msgid "Pause" msgstr "Pausar" -#: youtube_dl_gui/mainframe.py:113 youtube_dl_gui/mainframe.py:865 -#: youtube_dl_gui/mainframe.py:866 youtube_dl_gui/optionsframe.py:582 +#: youtube_dl_gui\mainframe.py:115 youtube_dl_gui\mainframe.py:870 +#: youtube_dl_gui\mainframe.py:871 youtube_dl_gui\optionsframe.py:601 msgid "Start" msgstr "Iniciar" -#: youtube_dl_gui/mainframe.py:114 +#: youtube_dl_gui\mainframe.py:116 msgid "About" msgstr "Acerca de" -#: youtube_dl_gui/mainframe.py:115 +#: youtube_dl_gui\mainframe.py:117 msgid "View Log" msgstr "Ver registro" -#: youtube_dl_gui/mainframe.py:117 +#: youtube_dl_gui\mainframe.py:119 msgid "Successfully downloaded {0} URL(s) in {1} day(s) {2} hour(s) {3} minute(s) {4} second(s)" msgstr "Se han terminado de descargar {0} elementos en {1} días, {2} horas, {3} minutos y {4} segundos." -#: youtube_dl_gui/mainframe.py:119 +#: youtube_dl_gui\mainframe.py:121 msgid "Downloads completed" msgstr "Se han completado todas las descargas." -#: youtube_dl_gui/mainframe.py:120 +#: youtube_dl_gui\mainframe.py:122 msgid "Total Progress: {0:.1f}% | Queued ({1}) Paused ({2}) Active ({3}) Completed ({4}) Error ({5})" msgstr "Progreso total: {0:.1f}% | Restantes ({1}) En pausa ({2}) Activas ({3}) Terminadas ({4}) Fallidas ({5})" -#: youtube_dl_gui/mainframe.py:121 +#: youtube_dl_gui\mainframe.py:123 msgid "Stopping downloads" msgstr "Parando descargas en curso." -#: youtube_dl_gui/mainframe.py:122 +#: youtube_dl_gui\mainframe.py:124 msgid "Downloads stopped" msgstr "Se han detenido todas las descargas en curso." -#: youtube_dl_gui/mainframe.py:123 +#: youtube_dl_gui\mainframe.py:125 msgid "You need to provide at least one URL" -msgstr "Es necesario proporcionar al menos una dirección de descarga para poder empezar." +msgstr "Es necesario proporcionar al menos una URL" -#: youtube_dl_gui/mainframe.py:124 +#: youtube_dl_gui\mainframe.py:126 msgid "Downloads started" -msgstr "Comenzando descargas." +msgstr "Descargas iniciadas" -#: youtube_dl_gui/mainframe.py:125 +#: youtube_dl_gui\mainframe.py:127 msgid "Choose Directory" msgstr "Elegir carpeta" -#: youtube_dl_gui/mainframe.py:127 +#: youtube_dl_gui\mainframe.py:129 msgid "Download in progress. Please wait for all downloads to complete" -msgstr "Descarga en progreso. Por favor espere por todas las descargas se completen." +msgstr "Descarga en progreso. Por favor espere que todas las descargas se completen." -#: youtube_dl_gui/mainframe.py:128 +#: youtube_dl_gui\mainframe.py:130 msgid "Update already in progress" msgstr "Actualizacion está en progreso." -#: youtube_dl_gui/mainframe.py:130 +#: youtube_dl_gui\mainframe.py:132 msgid "Downloading latest youtube-dl. Please wait..." msgstr "Descargando última versión de youtube-dl. Por favor espere..." -#: youtube_dl_gui/mainframe.py:131 +#: youtube_dl_gui\mainframe.py:133 msgid "Youtube-dl download failed [{0}]" msgstr "Descarga de Youtube-dl a fallado [{0}]." -#: youtube_dl_gui/mainframe.py:132 +#: youtube_dl_gui\mainframe.py:134 msgid "Successfully downloaded youtube-dl" msgstr "Exitosamente descargado youtube-dl." -#: youtube_dl_gui/mainframe.py:134 +#: youtube_dl_gui\mainframe.py:136 msgid "Unable to open directory: '{dir}'. The specified path does not exist" msgstr "No se ha podido abrir la carpeta: «{dir}». La ruta especificada no existe." -#: youtube_dl_gui/mainframe.py:136 +#: youtube_dl_gui\mainframe.py:138 msgid "Error while shutting down. Make sure you typed the correct password" msgstr "Error mientras se apagaba el equipo. Asegúrate de que la contraseña escrita sea la correcta." -#: youtube_dl_gui/mainframe.py:138 +#: youtube_dl_gui\mainframe.py:140 msgid "Shutting down system" msgstr "Apagando el equipo" -#: youtube_dl_gui/mainframe.py:141 +#: youtube_dl_gui\mainframe.py:143 msgid "Extension" msgstr "Extensión" -#: youtube_dl_gui/mainframe.py:142 +#: youtube_dl_gui\mainframe.py:144 msgid "Size" msgstr "Tamaño" -#: youtube_dl_gui/mainframe.py:143 +#: youtube_dl_gui\mainframe.py:145 msgid "Percent" msgstr "Porcentaje" -#: youtube_dl_gui/mainframe.py:144 +#: youtube_dl_gui\mainframe.py:146 msgid "ETA" msgstr "Tiempo estimado" -#: youtube_dl_gui/mainframe.py:145 +#: youtube_dl_gui\mainframe.py:147 msgid "Speed" msgstr "Velocidad" -#: youtube_dl_gui/mainframe.py:146 +#: youtube_dl_gui\mainframe.py:148 msgid "Status" msgstr "Estado" -#: youtube_dl_gui/mainframe.py:235 +#: youtube_dl_gui\mainframe.py:237 msgid "Get URL" msgstr "Copiar dirección URL al portapapeles" -#: youtube_dl_gui/mainframe.py:236 +#: youtube_dl_gui\mainframe.py:238 msgid "Get command" msgstr "Copiar los argumentos de descarga al portapapeles" -#: youtube_dl_gui/mainframe.py:237 +#: youtube_dl_gui\mainframe.py:239 msgid "Open destination" msgstr "Abrir carpeta de descarga" -#: youtube_dl_gui/mainframe.py:238 +#: youtube_dl_gui\mainframe.py:240 msgid "Re-enter" msgstr "Reintroducir" -#: youtube_dl_gui/mainframe.py:458 +#: youtube_dl_gui\mainframe.py:460 msgid "Resume" msgstr "Reanudar" -#: youtube_dl_gui/mainframe.py:480 +#: youtube_dl_gui\mainframe.py:482 msgid "Video" -msgstr "Vídeo" +msgstr "Video" -#: youtube_dl_gui/mainframe.py:484 +#: youtube_dl_gui\mainframe.py:486 msgid "Audio" msgstr "Audio" -#: youtube_dl_gui/mainframe.py:516 +#: youtube_dl_gui\mainframe.py:518 msgid "No items selected. Please pick an action" msgstr "No ha seleccionado elementos. Por favor elige una acción." -#: youtube_dl_gui/mainframe.py:516 +#: youtube_dl_gui\mainframe.py:518 msgid "Remove all" msgstr "Borrar todo" -#: youtube_dl_gui/mainframe.py:516 +#: youtube_dl_gui\mainframe.py:518 msgid "Remove completed" msgstr "Borrado completado." -#: youtube_dl_gui/mainframe.py:534 +#: youtube_dl_gui\mainframe.py:536 msgid "Are you sure you want to remove selected items?" msgstr "¿Seguro que quieres borrar los elementos seleccionados?" -#: youtube_dl_gui/mainframe.py:546 +#: youtube_dl_gui\mainframe.py:548 msgid "Item is active, cannot remove" msgstr "Elemento está activo, no se puede borrar." -#: youtube_dl_gui/mainframe.py:579 +#: youtube_dl_gui\mainframe.py:581 msgid "Item is not completed" msgstr "Elemento no se ha completado." -#: youtube_dl_gui/mainframe.py:668 +#: youtube_dl_gui\mainframe.py:670 msgid "Update in progress. Please wait for the update to complete" msgstr "Actualización en progreso. Por favor espera que la actualización se complete." -#: youtube_dl_gui/mainframe.py:716 +#: youtube_dl_gui\mainframe.py:718 msgid "Logging is disabled" msgstr "El registro de errores está desactivado." -#: youtube_dl_gui/mainframe.py:891 +#: youtube_dl_gui\mainframe.py:896 msgid "Shutdown" msgstr "Apagar" -#: youtube_dl_gui/mainframe.py:891 +#: youtube_dl_gui\mainframe.py:896 msgid "Shutting down in {0} second(s)" msgstr "Apagando el equipo en {0} seg(s)." -#: youtube_dl_gui/mainframe.py:980 +#: youtube_dl_gui\mainframe.py:983 msgid "No items to download" msgstr "Sin elementos para descargar." -#: youtube_dl_gui/mainframe.py:1040 +#: youtube_dl_gui\mainframe.py:1043 msgid "Updates are disabled for your system. Please use the system's package manager to update youtube-dl." msgstr "Actualizaciones están deshabilitadas por su sistema. Por favor use el administrador de paquetes de su sistema para actualizar youtube-dl." -#: youtube_dl_gui/mainframe.py:1065 +#: youtube_dl_gui\mainframe.py:1068 msgid "Are you sure you want to exit?" msgstr "¿Seguro que deseas salir?" -#: youtube_dl_gui/mainframe.py:1065 +#: youtube_dl_gui\mainframe.py:1068 msgid "Exit" msgstr "Salir" -#: youtube_dl_gui/mainframe.py:1306 youtube_dl_gui/mainframe.py:1456 +#: youtube_dl_gui\mainframe.py:1323 youtube_dl_gui\mainframe.py:1473 msgid "Cancel" msgstr "Cancelar" -#: youtube_dl_gui/mainframe.py:1455 +#: youtube_dl_gui\mainframe.py:1472 msgid "OK" msgstr "Aceptar" -#: youtube_dl_gui/optionsframe.py:65 +#: youtube_dl_gui\optionsframe.py:67 msgid "Reset" msgstr "Reiniciar" -#: youtube_dl_gui/optionsframe.py:66 +#: youtube_dl_gui\optionsframe.py:68 msgid "Close" msgstr "Cerrar" -#: youtube_dl_gui/optionsframe.py:72 +#: youtube_dl_gui\optionsframe.py:74 msgid "General" msgstr "General" -#: youtube_dl_gui/optionsframe.py:73 +#: youtube_dl_gui\optionsframe.py:75 msgid "Formats" msgstr "Formatos" -#: youtube_dl_gui/optionsframe.py:74 +#: youtube_dl_gui\optionsframe.py:76 msgid "Downloads" msgstr "Descargas" -#: youtube_dl_gui/optionsframe.py:75 +#: youtube_dl_gui\optionsframe.py:77 msgid "Advanced" msgstr "Avanzado" -#: youtube_dl_gui/optionsframe.py:76 +#: youtube_dl_gui\optionsframe.py:78 msgid "Extra" msgstr "Extra" -#: youtube_dl_gui/optionsframe.py:310 +#: youtube_dl_gui\optionsframe.py:329 msgid "Language" msgstr "Idioma" -#: youtube_dl_gui/optionsframe.py:313 +#: youtube_dl_gui\optionsframe.py:332 msgid "Filename format" msgstr "Nomenclatura de archivos" -#: youtube_dl_gui/optionsframe.py:318 +#: youtube_dl_gui\optionsframe.py:337 msgid "Filename options" msgstr "Opciones de nomenclatura" -#: youtube_dl_gui/optionsframe.py:319 +#: youtube_dl_gui\optionsframe.py:338 msgid "Restrict filenames to ASCII" msgstr "Limitar nombres de archivo a ASCII" -#: youtube_dl_gui/optionsframe.py:321 +#: youtube_dl_gui\optionsframe.py:340 msgid "More options" msgstr "Más opciones" -#: youtube_dl_gui/optionsframe.py:322 +#: youtube_dl_gui\optionsframe.py:341 msgid "Confirm on exit" msgstr "Confirmar salida" -#: youtube_dl_gui/optionsframe.py:323 +#: youtube_dl_gui\optionsframe.py:342 msgid "Confirm item deletion" msgstr "Confirmar al borrar archivos" -#: youtube_dl_gui/optionsframe.py:324 +#: youtube_dl_gui\optionsframe.py:343 msgid "Inform me on download completion" msgstr "Avísarme cuando las descargas se completen" -#: youtube_dl_gui/optionsframe.py:326 +#: youtube_dl_gui\optionsframe.py:345 msgid "Shutdown on download completion" msgstr "Apagar al completar las descargas" -#: youtube_dl_gui/optionsframe.py:337 +#: youtube_dl_gui\optionsframe.py:356 msgid "SUDO password" msgstr "Contraseña de SuperUsuario" -#: youtube_dl_gui/optionsframe.py:415 youtube_dl_gui/optionsframe.py:816 +#: youtube_dl_gui\optionsframe.py:434 youtube_dl_gui\optionsframe.py:835 msgid "In order for the changes to take effect please restart {0}" msgstr "Reinicia {0} para que los cambios surtan efecto." -#: youtube_dl_gui/optionsframe.py:416 youtube_dl_gui/optionsframe.py:817 +#: youtube_dl_gui\optionsframe.py:435 youtube_dl_gui\optionsframe.py:836 msgid "Restart" msgstr "Reiniciar" -#: youtube_dl_gui/optionsframe.py:463 +#: youtube_dl_gui\optionsframe.py:482 msgid "high" msgstr "Alta" -#: youtube_dl_gui/optionsframe.py:463 +#: youtube_dl_gui\optionsframe.py:482 msgid "low" msgstr "Baja" -#: youtube_dl_gui/optionsframe.py:463 +#: youtube_dl_gui\optionsframe.py:482 msgid "mid" msgstr "Intermedia" -#: youtube_dl_gui/optionsframe.py:468 +#: youtube_dl_gui\optionsframe.py:487 msgid "Video formats" -msgstr "Formatos de vídeo" +msgstr "Formatos de video" -#: youtube_dl_gui/optionsframe.py:471 +#: youtube_dl_gui\optionsframe.py:490 msgid "Audio formats" msgstr "Formatos de audio" -#: youtube_dl_gui/optionsframe.py:474 +#: youtube_dl_gui\optionsframe.py:493 msgid "Post-Process options" msgstr "Opciones de posproceso" -#: youtube_dl_gui/optionsframe.py:475 +#: youtube_dl_gui\optionsframe.py:494 msgid "Keep original files" msgstr "Conservar los archivos originales" -#: youtube_dl_gui/optionsframe.py:476 +#: youtube_dl_gui\optionsframe.py:495 msgid "Extract audio from video file" -msgstr "Extraer el audio del vídeo" +msgstr "Extraer el audio del video" -#: youtube_dl_gui/optionsframe.py:477 +#: youtube_dl_gui\optionsframe.py:496 msgid "Embed thumbnail in audio file" msgstr "Incluir una miniatura en el archivo de audio" -#: youtube_dl_gui/optionsframe.py:478 +#: youtube_dl_gui\optionsframe.py:497 msgid "Add metadata to file" msgstr "Incluir metadatos en el archivo" -#: youtube_dl_gui/optionsframe.py:480 +#: youtube_dl_gui\optionsframe.py:499 msgid "Audio quality" msgstr "Calidad del audio" -#: youtube_dl_gui/optionsframe.py:538 +#: youtube_dl_gui\optionsframe.py:557 msgid "English" msgstr "Inglés" -#: youtube_dl_gui/optionsframe.py:539 +#: youtube_dl_gui\optionsframe.py:558 msgid "French" msgstr "Francés" -#: youtube_dl_gui/optionsframe.py:540 +#: youtube_dl_gui\optionsframe.py:559 msgid "German" msgstr "Alemán" -#: youtube_dl_gui/optionsframe.py:541 +#: youtube_dl_gui\optionsframe.py:560 msgid "Greek" msgstr "Griego" -#: youtube_dl_gui/optionsframe.py:542 +#: youtube_dl_gui\optionsframe.py:561 msgid "Hebrew" msgstr "Hebreo" -#: youtube_dl_gui/optionsframe.py:543 +#: youtube_dl_gui\optionsframe.py:562 msgid "Italian" msgstr "Italiano" -#: youtube_dl_gui/optionsframe.py:544 +#: youtube_dl_gui\optionsframe.py:563 msgid "Portuguese" msgstr "Portugués" -#: youtube_dl_gui/optionsframe.py:545 +#: youtube_dl_gui\optionsframe.py:564 msgid "Russian" msgstr "Ruso" -#: youtube_dl_gui/optionsframe.py:546 +#: youtube_dl_gui\optionsframe.py:565 msgid "Spanish" msgstr "Español" -#: youtube_dl_gui/optionsframe.py:547 +#: youtube_dl_gui\optionsframe.py:566 msgid "Swedish" msgstr "Sueco" -#: youtube_dl_gui/optionsframe.py:548 +#: youtube_dl_gui\optionsframe.py:567 msgid "Turkish" msgstr "Turco" -#: youtube_dl_gui/optionsframe.py:564 +#: youtube_dl_gui\optionsframe.py:583 msgid "None" msgstr "Ninguno" -#: youtube_dl_gui/optionsframe.py:565 +#: youtube_dl_gui\optionsframe.py:584 msgid "Automatic subtitles (YOUTUBE ONLY)" msgstr "Descargar subtítulos en archivos automáticamente (sólo en YouTube)" -#: youtube_dl_gui/optionsframe.py:566 +#: youtube_dl_gui\optionsframe.py:585 msgid "All available subtitles" msgstr "Descargar todos los subtítulos disponibles" -#: youtube_dl_gui/optionsframe.py:567 +#: youtube_dl_gui\optionsframe.py:586 msgid "Subtitles by language" -msgstr "Idioma de subtítulos" +msgstr "Subtítulos por idioma" -#: youtube_dl_gui/optionsframe.py:573 +#: youtube_dl_gui\optionsframe.py:592 msgid "Subtitles" msgstr "Subtítulos" -#: youtube_dl_gui/optionsframe.py:577 +#: youtube_dl_gui\optionsframe.py:596 msgid "Subtitles options" msgstr "Opciones de subtítulos" -#: youtube_dl_gui/optionsframe.py:578 +#: youtube_dl_gui\optionsframe.py:597 msgid "Embed subtitles into video file (mp4 ONLY)" -msgstr "Insertar los subtítulos en el vídeo (sólo para vídeos en mp4)" +msgstr "Insertar los subtítulos en el video (SÓLO para videos en mp4)" -#: youtube_dl_gui/optionsframe.py:580 +#: youtube_dl_gui\optionsframe.py:599 msgid "Playlist" msgstr "Lista de reproducción" -#: youtube_dl_gui/optionsframe.py:586 youtube_dl_gui/optionsframe.py:591 +#: youtube_dl_gui\optionsframe.py:605 youtube_dl_gui\optionsframe.py:610 msgid "Max" msgstr "Máximo" -#: youtube_dl_gui/optionsframe.py:589 +#: youtube_dl_gui\optionsframe.py:608 msgid "Filesize" msgstr "Tamaño de archivo" -#: youtube_dl_gui/optionsframe.py:594 +#: youtube_dl_gui\optionsframe.py:613 msgid "Min" msgstr "Mínimo" -#: youtube_dl_gui/optionsframe.py:723 +#: youtube_dl_gui\optionsframe.py:742 msgid "Retries" msgstr "Reintentos" -#: youtube_dl_gui/optionsframe.py:726 +#: youtube_dl_gui\optionsframe.py:745 msgid "Authentication" msgstr "Autenticación" -#: youtube_dl_gui/optionsframe.py:728 +#: youtube_dl_gui\optionsframe.py:747 msgid "Username" msgstr "Nombre de usuario" -#: youtube_dl_gui/optionsframe.py:730 +#: youtube_dl_gui\optionsframe.py:749 msgid "Password" msgstr "Contraseña" -#: youtube_dl_gui/optionsframe.py:732 +#: youtube_dl_gui\optionsframe.py:751 msgid "Video password" -msgstr "Contraseña del vídeo" +msgstr "Contraseña del video" -#: youtube_dl_gui/optionsframe.py:735 +#: youtube_dl_gui\optionsframe.py:754 msgid "Network" msgstr "Red" -#: youtube_dl_gui/optionsframe.py:737 +#: youtube_dl_gui\optionsframe.py:756 msgid "Proxy" msgstr "Proxy" -#: youtube_dl_gui/optionsframe.py:739 +#: youtube_dl_gui\optionsframe.py:758 msgid "User agent" msgstr "Agente de usuario" -#: youtube_dl_gui/optionsframe.py:741 +#: youtube_dl_gui\optionsframe.py:760 msgid "Referer" msgstr "Página de origen" -#: youtube_dl_gui/optionsframe.py:744 +#: youtube_dl_gui\optionsframe.py:763 msgid "Logging" msgstr "Registro de errores" -#: youtube_dl_gui/optionsframe.py:746 +#: youtube_dl_gui\optionsframe.py:765 msgid "Enable log" msgstr "Registrar los errores del programa" -#: youtube_dl_gui/optionsframe.py:747 +#: youtube_dl_gui\optionsframe.py:766 msgid "View" msgstr "Ver registro" -#: youtube_dl_gui/optionsframe.py:748 +#: youtube_dl_gui\optionsframe.py:767 msgid "Clear" -msgstr "Borrar registro" +msgstr "Limpiar registro" -#: youtube_dl_gui/optionsframe.py:858 +#: youtube_dl_gui\optionsframe.py:877 msgid "Youtube-dl command line options (e.g. --help)" msgstr "Argumentos adicionales para Youtube-dl (p. ej. --help)" -#: youtube_dl_gui/optionsframe.py:861 +#: youtube_dl_gui\optionsframe.py:880 msgid "Extra options" msgstr "Otras opciones" -#: youtube_dl_gui/optionsframe.py:863 +#: youtube_dl_gui\optionsframe.py:882 msgid "Debug youtube-dl" msgstr "Depurar youtube-dl" -#: youtube_dl_gui/optionsframe.py:864 +#: youtube_dl_gui\optionsframe.py:883 msgid "Ignore errors" msgstr "Ignorar errores" -#: youtube_dl_gui/optionsframe.py:865 +#: youtube_dl_gui\optionsframe.py:884 msgid "Ignore youtube-dl config" msgstr "Ignorar la configuración de youtube-dl" -#: youtube_dl_gui/optionsframe.py:866 +#: youtube_dl_gui\optionsframe.py:885 msgid "No mtime" msgstr "Sin mtime" -#: youtube_dl_gui/optionsframe.py:867 +#: youtube_dl_gui\optionsframe.py:886 msgid "Prefer native HLS" msgstr "Utilizar HLS nativo cuando sea posible" -#: youtube_dl_gui/optionsframe.py:928 +#: youtube_dl_gui\optionsframe.py:947 msgid "Log Viewer" msgstr "Visor de errores" + From 4f1279e8adf4f8eba4c85328ef3479693598c8c4 Mon Sep 17 00:00:00 2001 From: oleksis Date: Tue, 28 Jul 2020 14:20:36 -0400 Subject: [PATCH 06/34] Add polib, fix setup for Linux. Update requirements --- .gitignore | 1 + TODO | 421 ++++++++++++++++++++++++++++++++++++++++++++++- requirements.txt | 5 +- setup.py | 85 ++++++---- 4 files changed, 481 insertions(+), 31 deletions(-) diff --git a/.gitignore b/.gitignore index 13ac2526..25441494 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ MANIFEST dist/ build/ +env/ .idea *.spec diff --git a/TODO b/TODO index f0a022d8..13ee7dee 100644 --- a/TODO +++ b/TODO @@ -27,12 +27,22 @@ Other ===== * Re-structure package (Avoid Circular Imports) -* Refactor +* Refactor to Python3 * Review - rewrite threads communications * Logging system using the Python 'logging' module * Use youtube-dl directly from python instead of using the subprocess module +Extras +====== +* En GNU/Linux requiere gtk3-devel - Development files for the GTK+ toolkit library v3 +* freeglut-devel (Freeglut is a completely open source alternative to the OpenGL Utility Toolkit (GLUT) library - alternative to OpenGL headers) +python3-gst +* gstreamer-plugins-base-devel +* gstreamer-devel (GStreamer 1.0) +* Opcional libwx_gtk3 + + Probably wont add ================= * ListCtrl double click to "Rename" @@ -41,3 +51,412 @@ Probably wont add * Auto video format detection * Change 'Warning' status to 'Finished (*)' or something similar? (see: issue #131) * Use proxy during update phase (see: issue #244) + + +COMMANDS +======== +git submodule update --recursive + +OR + +git clone https://github.com/wxWidgets/wxWidgets.git /home/oleksis/Phoenix/ext/wxWidgets +git clone https://github.com/wxWidgets/Catch.git /home/oleksis/Phoenix/ext/wxWidgets/3rdparty/catch + + +python3 -m venv env +source env/bin/activate +pip install -r requirements.txt +python build.py dox etg --nodoc sip build +python build.py build_py -v + + +ERRORS +====== + + +=== CON ERROR en wx-config unicode ===== +(env) oleksis@linux-fkos:~/Phoenix> python build.py build_py -v +Will build using: "/home/oleksis/Phoenix/env/bin/python" +3.6.5 (default, Mar 31 2018, 19:45:04) [GCC] +Python's architecture is 64bit +cfg.VERSION: 4.1.1a1 + +Running command: build_py +Checking for /home/oleksis/Phoenix/bin/waf-2.0.19... +"/home/oleksis/Phoenix/env/bin/python" /home/oleksis/Phoenix/bin/waf-2.0.19 --verbose --wx_config=/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config --verbose --gtk3 --python="/home/oleksis/Phoenix/env/bin/python" --out=build/waf/3.6/gtk3 configure build +Setting top to : /home/oleksis/Phoenix +Setting out to : /home/oleksis/Phoenix/build/waf/3.6/gtk3 +Checking for 'gcc' (C compiler) : 22:14:36 runner ['/usr/bin/gcc', '-dM', '-E', '-'] +/usr/bin/gcc +Checking for 'g++' (C++ compiler) : 22:14:37 runner ['/usr/bin/g++', '-dM', '-E', '-'] +/usr/bin/g++ +Checking for program 'python' : /home/oleksis/Phoenix/env/bin/python +22:14:37 runner ['/home/oleksis/Phoenix/env/bin/python', '-c', 'import sys\ntry:\n print(sys.implementation.cache_tag)\nexcept AttributeError:\n import imp\n print(imp.get_tag())\n'] +22:14:38 runner ['/home/oleksis/Phoenix/env/bin/python', '-c', 'import sys\nfor x in sys.version_info: print(str(x))'] +22:14:38 runner ['/home/oleksis/Phoenix/env/bin/python', '-c', "from distutils.sysconfig import get_config_var, get_python_lib\n\nprint(repr(get_python_lib(standard_lib=0, prefix='/usr/local') or ''))"] +22:14:39 runner ['/home/oleksis/Phoenix/env/bin/python', '-c', "from distutils.sysconfig import get_config_var, get_python_lib\n\nprint(repr(get_python_lib(plat_specific=1, standard_lib=0, prefix='/usr/local') or ''))"] +Checking for python version >= 2.7.0 : 3.6.5 +22:14:39 runner ['/home/oleksis/Phoenix/env/bin/python', '-c', "from distutils.sysconfig import get_config_var, get_python_lib\n\nprint(repr(get_config_var('prefix') or ''))\nprint(repr(get_config_var('SO') or ''))\nprint(repr(get_config_var('LDFLAGS') or ''))\nprint(repr(get_config_var('LIBDIR') or ''))\nprint(repr(get_config_var('LIBPL') or ''))\nprint(repr(get_config_var('INCLUDEPY') or ''))\nprint(repr(get_config_var('Py_ENABLE_SHARED') or ''))\nprint(repr(get_config_var('MACOSX_DEPLOYMENT_TARGET') or ''))\nprint(repr(get_config_var('LDSHARED') or ''))\nprint(repr(get_config_var('CFLAGS') or ''))\nprint(repr(get_config_var('LDVERSION') or ''))"] +python-config : /usr/bin/python3.6-config +Asking python-config for pyext '--cflags --libs --ldflags' flags : 22:14:39 runner ['/usr/bin/python3.6-config', '--cflags', '--libs', '--ldflags'] +yes +Testing pyext configuration : 22:14:40 runner ['/usr/bin/g++', '-fPIC', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-I/usr/include/python3.6m', '-DPYTHONDIR="/usr/local/lib/python3.6/site-packages"', '-DPYTHONARCHDIR="/usr/local/lib64/python3.6/site-packages"', '-D_FORTIFY_SOURCE=2', '-DNDEBUG', '-D_FORTIFY_SOURCE=2', '-DOPENSSL_LOAD_CONF', '../test.cpp', '-c', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/.conf_check_eaab107a0ad0c09fe99b0e743a890f00/testbuild/test.cpp.1.o'] +22:14:41 runner ['/usr/bin/g++', '-shared', '-Xlinker', '-export-dynamic', 'test.cpp.1.o', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/.conf_check_eaab107a0ad0c09fe99b0e743a890f00/testbuild/testprog.cpython-36m-x86_64-linux-gnu.so', '-Wl,-Bstatic', '-Wl,-Bdynamic', '-L/usr/lib64', '-lpython3.6m', '-lpthread', '-ldl', '-lutil', '-lm', '-lpython3.6m', '-lpthread', '-ldl', '-lutil', '-lm'] +yes +Finding libs for WX : 22:14:43 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'core,net', '--no-rpath'] +yes +Finding libs for WXADV : 22:14:43 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'adv,core,net', '--no-rpath'] +yes +Finding libs for WXSTC : 22:14:44 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'stc,core,net', '--no-rpath'] +yes +Finding libs for WXHTML : 22:14:44 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'html,core,net', '--no-rpath'] +yes +WARNING: Unable to find setup.h in /home/oleksis/Phoenix/build/wxbld/gtk3/lib64/wx/include/gtk3-unicode-3.1, assuming wxUSE_GLCANVAS is not available. +Finding libs for WXGL : 22:14:44 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'core,net', '--no-rpath'] +yes +WARNING: Unable to find setup.h in /home/oleksis/Phoenix/build/wxbld/gtk3/lib64/wx/include/gtk3-unicode-3.1, assuming wxUSE_WEBVIEW is not available. +Finding libs for WXWEBVIEW : 22:14:45 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'core,net', '--no-rpath'] +yes +Finding libs for WXXML : 22:14:45 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'xml,core,net', '--no-rpath'] +yes +Finding libs for WXXRC : 22:14:45 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'xrc,xml,core,net', '--no-rpath'] +yes +Finding libs for WXRICHTEXT : 22:14:45 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'richtext,core,net', '--no-rpath'] +yes +WARNING: Unable to find setup.h in /home/oleksis/Phoenix/build/wxbld/gtk3/lib64/wx/include/gtk3-unicode-3.1, assuming wxUSE_MEDIACTRL is not available. +Finding libs for WXMEDIA : 22:14:46 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'core,net', '--no-rpath'] +yes +Finding libs for WXRIBBON : 22:14:46 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'ribbon,core,net', '--no-rpath'] +yes +Finding libs for WXPROPGRID : 22:14:46 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'propgrid,core', '--no-rpath'] +yes +Finding libs for WXAUI : 22:14:47 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'aui,core', '--no-rpath'] +yes +'configure' finished successfully (11.443s) +Waf: Entering directory `/home/oleksis/Phoenix/build/waf/3.6/gtk3' +**** Compiler: /usr/bin/gcc --version + gcc (SUSE Linux) 7.3.1 20180323 [gcc-7-branch revision 258812] + Copyright (C) 2017 Free Software Foundation, Inc. + This is free software; see the source for copying conditions. There is NO + warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +Erroneous order constraint after='siplib' on bld(source='siplib.cpython-36m-x86_64-linux-gnu.so', target='pkg.siplib.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=2, tg_idx_count=2, rule=, after='siplib', posted=True) in /home/oleksis/Phoenix (no such class) +Erroneous order constraint after='_core' on bld(source='_core.cpython-36m-x86_64-linux-gnu.so', target='pkg._core.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=4, tg_idx_count=4, rule=, after='_core', posted=True) in /home/oleksis/Phoenix (no such class) +Erroneous order constraint after='_adv' on bld(source='_adv.cpython-36m-x86_64-linux-gnu.so', target='pkg._adv.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=6, tg_idx_count=6, rule=, after='_adv', posted=True) in /home/oleksis/Phoenix (no such class) +Erroneous order constraint after='_dataview' on bld(source='_dataview.cpython-36m-x86_64-linux-gnu.so', target='pkg._dataview.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=8, tg_idx_count=8, rule=, after='_dataview', posted=True) in /home/oleksis/Phoenix (no such class) +Erroneous order constraint after='_grid' on bld(source='_grid.cpython-36m-x86_64-linux-gnu.so', target='pkg._grid.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=10, tg_idx_count=10, rule=, after='_grid', posted=True) in /home/oleksis/Phoenix (no such class) +Erroneous order constraint after='_stc' on bld(source='_stc.cpython-36m-x86_64-linux-gnu.so', target='pkg._stc.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=12, tg_idx_count=12, rule=, after='_stc', posted=True) in /home/oleksis/Phoenix (no such class) +Erroneous order constraint after='_html' on bld(source='_html.cpython-36m-x86_64-linux-gnu.so', target='pkg._html.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=14, tg_idx_count=14, rule=, after='_html', posted=True) in /home/oleksis/Phoenix (no such class) +Erroneous order constraint after='_glcanvas' on bld(source='_glcanvas.cpython-36m-x86_64-linux-gnu.so', target='pkg._glcanvas.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=16, tg_idx_count=16, rule=, after='_glcanvas', posted=True) in /home/oleksis/Phoenix (no such class) +Erroneous order constraint after='_html2' on bld(source='_html2.cpython-36m-x86_64-linux-gnu.so', target='pkg._html2.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=18, tg_idx_count=18, rule=, after='_html2', posted=True) in /home/oleksis/Phoenix (no such class) +Erroneous order constraint after='_xml' on bld(source='_xml.cpython-36m-x86_64-linux-gnu.so', target='pkg._xml.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=20, tg_idx_count=20, rule=, after='_xml', posted=True) in /home/oleksis/Phoenix (no such class) +Erroneous order constraint after='_xrc' on bld(source='_xrc.cpython-36m-x86_64-linux-gnu.so', target='pkg._xrc.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=22, tg_idx_count=22, rule=, after='_xrc', posted=True) in /home/oleksis/Phoenix (no such class) +Erroneous order constraint after='_richtext' on bld(source='_richtext.cpython-36m-x86_64-linux-gnu.so', target='pkg._richtext.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=24, tg_idx_count=24, rule=, after='_richtext', posted=True) in /home/oleksis/Phoenix (no such class) +Erroneous order constraint after='_media' on bld(source='_media.cpython-36m-x86_64-linux-gnu.so', target='pkg._media.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=26, tg_idx_count=26, rule=, after='_media', posted=True) in /home/oleksis/Phoenix (no such class) +Erroneous order constraint after='_ribbon' on bld(source='_ribbon.cpython-36m-x86_64-linux-gnu.so', target='pkg._ribbon.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=28, tg_idx_count=28, rule=, after='_ribbon', posted=True) in /home/oleksis/Phoenix (no such class) +Erroneous order constraint after='_propgrid' on bld(source='_propgrid.cpython-36m-x86_64-linux-gnu.so', target='pkg._propgrid.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=30, tg_idx_count=30, rule=, after='_propgrid', posted=True) in /home/oleksis/Phoenix (no such class) +Erroneous order constraint after='_aui' on bld(source='_aui.cpython-36m-x86_64-linux-gnu.so', target='pkg._aui.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=32, tg_idx_count=32, rule=, after='_aui', posted=True) in /home/oleksis/Phoenix (no such class) +[ 11/918] Linking build/waf/3.6/gtk3/siplib.cpython-36m-x86_64-linux-gnu.so +22:14:50 runner ['/usr/bin/g++', '-shared', '-shared', '-pthread', '-pthread', '-Xlinker', '-export-dynamic', 'sip/siplib/apiversions.c.1.o', 'sip/siplib/array.c.1.o', 'sip/siplib/bool.cpp.1.o', 'sip/siplib/descriptors.c.1.o', 'sip/siplib/int_convertors.c.1.o', 'sip/siplib/objmap.c.1.o', 'sip/siplib/qtlib.c.1.o', 'sip/siplib/siplib.c.1.o', 'sip/siplib/threads.c.1.o', 'sip/siplib/voidptr.c.1.o', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/siplib.cpython-36m-x86_64-linux-gnu.so', '-Wl,-Bstatic', '-Wl,-Bdynamic', '-L/home/oleksis/Phoenix/build/wxbld/gtk3/lib64', '-lwx_gtk3u_core-3.1', '-lwx_baseu_net-3.1', '-lwx_baseu-3.1'] +/usr/lib64/gcc/x86_64-suse-linux/7/../../../../x86_64-suse-linux/bin/ld: no se puede encontrar -lwx_gtk3u_core-3.1 +/usr/lib64/gcc/x86_64-suse-linux/7/../../../../x86_64-suse-linux/bin/ld: no se puede encontrar -lwx_baseu_net-3.1 +/usr/lib64/gcc/x86_64-suse-linux/7/../../../../x86_64-suse-linux/bin/ld: no se puede encontrar -lwx_baseu-3.1 +collect2: error: ld returned 1 exit status + +Waf: Leaving directory `/home/oleksis/Phoenix/build/waf/3.6/gtk3' + File "/home/oleksis/Phoenix/bin/waf-2.0.19", line 168, in + Scripting.waf_entry_point(cwd, VERSION, wafdir) + File "/home/oleksis/Phoenix/bin/.waf3-2.0.19-1f3c580272b15a03d2566843c5fe872a/waflib/Scripting.py", line 119, in waf_entry_point + run_commands() + File "/home/oleksis/Phoenix/bin/.waf3-2.0.19-1f3c580272b15a03d2566843c5fe872a/waflib/Scripting.py", line 182, in run_commands + ctx=run_command(cmd_name) + File "/home/oleksis/Phoenix/bin/.waf3-2.0.19-1f3c580272b15a03d2566843c5fe872a/waflib/Scripting.py", line 173, in run_command + ctx.execute() + File "/home/oleksis/Phoenix/bin/.waf3-2.0.19-1f3c580272b15a03d2566843c5fe872a/waflib/Scripting.py", line 375, in execute + return execute_method(self) + File "/home/oleksis/Phoenix/bin/.waf3-2.0.19-1f3c580272b15a03d2566843c5fe872a/waflib/Build.py", line 93, in execute + self.execute_build() + File "/home/oleksis/Phoenix/bin/.waf3-2.0.19-1f3c580272b15a03d2566843c5fe872a/waflib/Build.py", line 100, in execute_build + self.compile() + File "/home/oleksis/Phoenix/bin/.waf3-2.0.19-1f3c580272b15a03d2566843c5fe872a/waflib/Tools/errcheck.py", line 140, in check_compile + ret=self.orig_compile() + File "/home/oleksis/Phoenix/bin/.waf3-2.0.19-1f3c580272b15a03d2566843c5fe872a/waflib/Build.py", line 176, in compile + raise Errors.BuildError(self.producer.error) + File "/home/oleksis/Phoenix/bin/.waf3-2.0.19-1f3c580272b15a03d2566843c5fe872a/waflib/Errors.py", line 26, in __init__ + WafError.__init__(self,self.format_error()) + +Build failed + -> task in 'siplib' failed with exit status 1: + {task 140433775912328: cxxshlib apiversions.c.1.o,array.c.1.o,bool.cpp.1.o,descriptors.c.1.o,int_convertors.c.1.o,objmap.c.1.o,qtlib.c.1.o,siplib.c.1.o,threads.c.1.o,voidptr.c.1.o -> siplib.cpython-36m-x86_64-linux-gnu.so} +['/usr/bin/g++', '-shared', '-shared', '-pthread', '-pthread', '-Xlinker', '-export-dynamic', 'sip/siplib/apiversions.c.1.o', 'sip/siplib/array.c.1.o', 'sip/siplib/bool.cpp.1.o', 'sip/siplib/descriptors.c.1.o', 'sip/siplib/int_convertors.c.1.o', 'sip/siplib/objmap.c.1.o', 'sip/siplib/qtlib.c.1.o', 'sip/siplib/siplib.c.1.o', 'sip/siplib/threads.c.1.o', 'sip/siplib/voidptr.c.1.o', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/siplib.cpython-36m-x86_64-linux-gnu.so', '-Wl,-Bstatic', '-Wl,-Bdynamic', '-L/home/oleksis/Phoenix/build/wxbld/gtk3/lib64', '-lwx_gtk3u_core-3.1', '-lwx_baseu_net-3.1', '-lwx_baseu-3.1'] +Command '"/home/oleksis/Phoenix/env/bin/python" /home/oleksis/Phoenix/bin/waf-2.0.19 --verbose --wx_config=/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config --verbose --gtk3 --python="/home/oleksis/Phoenix/env/bin/python" --out=build/waf/3.6/gtk3 configure build ' failed with exit code 1. +Finished command: build_py (0m25.717s) + + +==== SIN ERROR MODIFICANDO GTK3-UNICODE-3.1 (./Phoenix/build/wxbld/gtk3/lib/wx/config/gtk3-unicode-3.1) ==== +(env) oleksis@linux-fkos:~/Phoenix> python build.py build_py -v +Will build using: "/home/oleksis/Phoenix/env/bin/python" +3.6.5 (default, Mar 31 2018, 19:45:04) [GCC] +Python's architecture is 64bit +cfg.VERSION: 4.1.1a1 + +Running command: build_py +Checking for /home/oleksis/Phoenix/bin/waf-2.0.19... +"/home/oleksis/Phoenix/env/bin/python" /home/oleksis/Phoenix/bin/waf-2.0.19 --verbose --wx_config=/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config --verbose --gtk3 --python="/home/oleksis/Phoenix/env/bin/python" --out=build/waf/3.6/gtk3 configure build +Setting top to : /home/oleksis/Phoenix +Setting out to : /home/oleksis/Phoenix/build/waf/3.6/gtk3 +Checking for 'gcc' (C compiler) : 00:27:25 runner ['/usr/bin/gcc', '-dM', '-E', '-'] +/usr/bin/gcc +Checking for 'g++' (C++ compiler) : 00:27:26 runner ['/usr/bin/g++', '-dM', '-E', '-'] +/usr/bin/g++ +Checking for program 'python' : /home/oleksis/Phoenix/env/bin/python +00:27:27 runner ['/home/oleksis/Phoenix/env/bin/python', '-c', 'import sys\ntry:\n print(sys.implementation.cache_tag)\nexcept AttributeError:\n import imp\n print(imp.get_tag())\n'] +00:27:27 runner ['/home/oleksis/Phoenix/env/bin/python', '-c', 'import sys\nfor x in sys.version_info: print(str(x))'] +00:27:28 runner ['/home/oleksis/Phoenix/env/bin/python', '-c', "from distutils.sysconfig import get_config_var, get_python_lib\n\nprint(repr(get_python_lib(standard_lib=0, prefix='/usr/local') or ''))"] +00:27:28 runner ['/home/oleksis/Phoenix/env/bin/python', '-c', "from distutils.sysconfig import get_config_var, get_python_lib\n\nprint(repr(get_python_lib(plat_specific=1, standard_lib=0, prefix='/usr/local') or ''))"] +Checking for python version >= 2.7.0 : 3.6.5 +00:27:29 runner ['/home/oleksis/Phoenix/env/bin/python', '-c', "from distutils.sysconfig import get_config_var, get_python_lib\n\nprint(repr(get_config_var('prefix') or ''))\nprint(repr(get_config_var('SO') or ''))\nprint(repr(get_config_var('LDFLAGS') or ''))\nprint(repr(get_config_var('LIBDIR') or ''))\nprint(repr(get_config_var('LIBPL') or ''))\nprint(repr(get_config_var('INCLUDEPY') or ''))\nprint(repr(get_config_var('Py_ENABLE_SHARED') or ''))\nprint(repr(get_config_var('MACOSX_DEPLOYMENT_TARGET') or ''))\nprint(repr(get_config_var('LDSHARED') or ''))\nprint(repr(get_config_var('CFLAGS') or ''))\nprint(repr(get_config_var('LDVERSION') or ''))"] +python-config : /usr/bin/python3.6-config +Asking python-config for pyext '--cflags --libs --ldflags' flags : 00:27:29 runner ['/usr/bin/python3.6-config', '--cflags', '--libs', '--ldflags'] +yes +Testing pyext configuration : 00:27:29 runner ['/usr/bin/g++', '-fPIC', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-I/usr/include/python3.6m', '-DPYTHONDIR="/usr/local/lib/python3.6/site-packages"', '-DPYTHONARCHDIR="/usr/local/lib64/python3.6/site-packages"', '-D_FORTIFY_SOURCE=2', '-DNDEBUG', '-D_FORTIFY_SOURCE=2', '-DOPENSSL_LOAD_CONF', '../test.cpp', '-c', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/.conf_check_eaab107a0ad0c09fe99b0e743a890f00/testbuild/test.cpp.1.o'] +00:27:31 runner ['/usr/bin/g++', '-shared', '-Xlinker', '-export-dynamic', 'test.cpp.1.o', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/.conf_check_eaab107a0ad0c09fe99b0e743a890f00/testbuild/testprog.cpython-36m-x86_64-linux-gnu.so', '-Wl,-Bstatic', '-Wl,-Bdynamic', '-L/usr/lib64', '-lpython3.6m', '-lpthread', '-ldl', '-lutil', '-lm', '-lpython3.6m', '-lpthread', '-ldl', '-lutil', '-lm'] +yes +Finding libs for WX : 00:27:33 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'core,net', '--no-rpath'] +yes +Finding libs for WXADV : 00:27:33 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'adv,core,net', '--no-rpath'] +yes +Finding libs for WXSTC : 00:27:33 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'stc,core,net', '--no-rpath'] +yes +Finding libs for WXHTML : 00:27:34 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'html,core,net', '--no-rpath'] +yes +Finding libs for WXGL : 00:27:34 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'gl,core,net', '--no-rpath'] +yes +Finding libs for WXWEBVIEW : 00:27:34 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'webview,core,net', '--no-rpath'] +yes +Finding libs for WXXML : 00:27:35 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'xml,core,net', '--no-rpath'] +yes +Finding libs for WXXRC : 00:27:35 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'xrc,xml,core,net', '--no-rpath'] +yes +Finding libs for WXRICHTEXT : 00:27:35 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'richtext,core,net', '--no-rpath'] +yes +Finding libs for WXMEDIA : 00:27:35 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'media,core,net', '--no-rpath'] +yes +Finding libs for WXRIBBON : 00:27:36 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'ribbon,core,net', '--no-rpath'] +yes +Finding libs for WXPROPGRID : 00:27:36 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'propgrid,core', '--no-rpath'] +yes +Finding libs for WXAUI : 00:27:36 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'aui,core', '--no-rpath'] +yes +'configure' finished successfully (11.528s) +Waf: Entering directory `/home/oleksis/Phoenix/build/waf/3.6/gtk3' +**** Compiler: /usr/bin/gcc --version + gcc (SUSE Linux) 7.3.1 20180323 [gcc-7-branch revision 258812] + Copyright (C) 2017 Free Software Foundation, Inc. + This is free software; see the source for copying conditions. There is NO + warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +Erroneous order constraint after='siplib' on bld(source='siplib.cpython-36m-x86_64-linux-gnu.so', target='pkg.siplib.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=2, tg_idx_count=2, rule=, after='siplib', posted=True) in /home/oleksis/Phoenix (no such class) +Erroneous order constraint after='_core' on bld(source='_core.cpython-36m-x86_64-linux-gnu.so', target='pkg._core.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=4, tg_idx_count=4, rule=, after='_core', posted=True) in /home/oleksis/Phoenix (no such class) +Erroneous order constraint after='_adv' on bld(source='_adv.cpython-36m-x86_64-linux-gnu.so', target='pkg._adv.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=6, tg_idx_count=6, rule=, after='_adv', posted=True) in /home/oleksis/Phoenix (no such class) +Erroneous order constraint after='_dataview' on bld(source='_dataview.cpython-36m-x86_64-linux-gnu.so', target='pkg._dataview.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=8, tg_idx_count=8, rule=, after='_dataview', posted=True) in /home/oleksis/Phoenix (no such class) +Erroneous order constraint after='_grid' on bld(source='_grid.cpython-36m-x86_64-linux-gnu.so', target='pkg._grid.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=10, tg_idx_count=10, rule=, after='_grid', posted=True) in /home/oleksis/Phoenix (no such class) +Erroneous order constraint after='_stc' on bld(source='_stc.cpython-36m-x86_64-linux-gnu.so', target='pkg._stc.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=12, tg_idx_count=12, rule=, after='_stc', posted=True) in /home/oleksis/Phoenix (no such class) +Erroneous order constraint after='_html' on bld(source='_html.cpython-36m-x86_64-linux-gnu.so', target='pkg._html.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=14, tg_idx_count=14, rule=, after='_html', posted=True) in /home/oleksis/Phoenix (no such class) +Erroneous order constraint after='_glcanvas' on bld(source='_glcanvas.cpython-36m-x86_64-linux-gnu.so', target='pkg._glcanvas.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=16, tg_idx_count=16, rule=, after='_glcanvas', posted=True) in /home/oleksis/Phoenix (no such class) +Erroneous order constraint after='_html2' on bld(source='_html2.cpython-36m-x86_64-linux-gnu.so', target='pkg._html2.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=18, tg_idx_count=18, rule=, after='_html2', posted=True) in /home/oleksis/Phoenix (no such class) +Erroneous order constraint after='_xml' on bld(source='_xml.cpython-36m-x86_64-linux-gnu.so', target='pkg._xml.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=20, tg_idx_count=20, rule=, after='_xml', posted=True) in /home/oleksis/Phoenix (no such class) +Erroneous order constraint after='_xrc' on bld(source='_xrc.cpython-36m-x86_64-linux-gnu.so', target='pkg._xrc.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=22, tg_idx_count=22, rule=, after='_xrc', posted=True) in /home/oleksis/Phoenix (no such class) +Erroneous order constraint after='_richtext' on bld(source='_richtext.cpython-36m-x86_64-linux-gnu.so', target='pkg._richtext.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=24, tg_idx_count=24, rule=, after='_richtext', posted=True) in /home/oleksis/Phoenix (no such class) +Erroneous order constraint after='_media' on bld(source='_media.cpython-36m-x86_64-linux-gnu.so', target='pkg._media.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=26, tg_idx_count=26, rule=, after='_media', posted=True) in /home/oleksis/Phoenix (no such class) +Erroneous order constraint after='_ribbon' on bld(source='_ribbon.cpython-36m-x86_64-linux-gnu.so', target='pkg._ribbon.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=28, tg_idx_count=28, rule=, after='_ribbon', posted=True) in /home/oleksis/Phoenix (no such class) +Erroneous order constraint after='_propgrid' on bld(source='_propgrid.cpython-36m-x86_64-linux-gnu.so', target='pkg._propgrid.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=30, tg_idx_count=30, rule=, after='_propgrid', posted=True) in /home/oleksis/Phoenix (no such class) +Erroneous order constraint after='_aui' on bld(source='_aui.cpython-36m-x86_64-linux-gnu.so', target='pkg._aui.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=32, tg_idx_count=32, rule=, after='_aui', posted=True) in /home/oleksis/Phoenix (no such class) +[ 1/918] Compiling sip/siplib/qtlib.c +00:27:39 runner ['/usr/bin/gcc', '-fPIC', '-pthread', '-pthread', '-pthread', '-I/usr/include/gtk-3.0', '-I/usr/include/at-spi2-atk/2.0', '-I/usr/include/at-spi-2.0', '-I/usr/include/dbus-1.0', '-I/usr/lib64/dbus-1.0/include', '-I/usr/include/gtk-3.0', '-I/usr/include/gio-unix-2.0/', '-I/usr/include/libxkbcommon', '-I/usr/include/wayland', '-I/usr/include/cairo', '-I/usr/include/pango-1.0', '-I/usr/include/harfbuzz', '-I/usr/include/pango-1.0', '-I/usr/include/atk-1.0', '-I/usr/include/cairo', '-I/usr/include/pixman-1', '-I/usr/include/freetype2', '-I/usr/include/libdrm', '-I/usr/include/libpng16', '-I/usr/include/gdk-pixbuf-2.0', '-I/usr/include/glib-2.0', '-I/usr/lib64/glib-2.0/include', '-UNDEBUG', '-DSIP_MODULE_NAME=wx.siplib', '-DSIP_MODULE_BASENAME=siplib', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-I../../../wxbld/gtk3/lib/wx/include/gtk3-unicode-3.1', '-I../../../../ext/wxWidgets/include', '-Isip/siplib', '-I../../../../sip/siplib', '-Iwx/include', '-I../../../../wx/include', '-Isrc', '-I../../../../src', '-I/usr/include/python3.6m', '-DPYTHONDIR="/usr/local/lib/python3.6/site-packages"', '-DPYTHONARCHDIR="/usr/local/lib64/python3.6/site-packages"', '-DHAVE_PYEXT=1', '-DHAVE_PYTHON_H=1', '-DHAVE_WX=1', '-DHAVE_WXADV=1', '-DHAVE_WXSTC=1', '-DHAVE_WXHTML=1', '-DHAVE_WXGL=1', '-DHAVE_WXWEBVIEW=1', '-DHAVE_WXXML=1', '-DHAVE_WXXRC=1', '-DHAVE_WXRICHTEXT=1', '-DHAVE_WXMEDIA=1', '-DHAVE_WXRIBBON=1', '-DHAVE_WXPROPGRID=1', '-DHAVE_WXAUI=1', '-D_FILE_OFFSET_BITS=64', '-D__WXGTK__', '-D_FORTIFY_SOURCE=2', '-D_FORTIFY_SOURCE=2', '-DOPENSSL_LOAD_CONF', '../../../../sip/siplib/qtlib.c', '-c', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/sip/siplib/qtlib.c.1.o'] +[ 2/918] Compiling sip/siplib/bool.cpp +00:27:39 runner ['/usr/bin/g++', '-fPIC', '-pthread', '-pthread', '-pthread', '-I/usr/include/gtk-3.0', '-I/usr/include/at-spi2-atk/2.0', '-I/usr/include/at-spi-2.0', '-I/usr/include/dbus-1.0', '-I/usr/lib64/dbus-1.0/include', '-I/usr/include/gtk-3.0', '-I/usr/include/gio-unix-2.0/', '-I/usr/include/libxkbcommon', '-I/usr/include/wayland', '-I/usr/include/cairo', '-I/usr/include/pango-1.0', '-I/usr/include/harfbuzz', '-I/usr/include/pango-1.0', '-I/usr/include/atk-1.0', '-I/usr/include/cairo', '-I/usr/include/pixman-1', '-I/usr/include/freetype2', '-I/usr/include/libdrm', '-I/usr/include/libpng16', '-I/usr/include/gdk-pixbuf-2.0', '-I/usr/include/glib-2.0', '-I/usr/lib64/glib-2.0/include', '-UNDEBUG', '-DSIP_MODULE_NAME=wx.siplib', '-DSIP_MODULE_BASENAME=siplib', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-I../../../wxbld/gtk3/lib/wx/include/gtk3-unicode-3.1', '-I../../../../ext/wxWidgets/include', '-Isip/siplib', '-I../../../../sip/siplib', '-Iwx/include', '-I../../../../wx/include', '-Isrc', '-I../../../../src', '-I/usr/include/python3.6m', '-DPYTHONDIR="/usr/local/lib/python3.6/site-packages"', '-DPYTHONARCHDIR="/usr/local/lib64/python3.6/site-packages"', '-DHAVE_PYEXT=1', '-DHAVE_PYTHON_H=1', '-DHAVE_WX=1', '-DHAVE_WXADV=1', '-DHAVE_WXSTC=1', '-DHAVE_WXHTML=1', '-DHAVE_WXGL=1', '-DHAVE_WXWEBVIEW=1', '-DHAVE_WXXML=1', '-DHAVE_WXXRC=1', '-DHAVE_WXRICHTEXT=1', '-DHAVE_WXMEDIA=1', '-DHAVE_WXRIBBON=1', '-DHAVE_WXPROPGRID=1', '-DHAVE_WXAUI=1', '-D_FILE_OFFSET_BITS=64', '-D__WXGTK__', '-D_FORTIFY_SOURCE=2', '-D_FORTIFY_SOURCE=2', '-DOPENSSL_LOAD_CONF', '../../../../sip/siplib/bool.cpp', '-c', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/sip/siplib/bool.cpp.1.o'] +[ 3/918] Compiling sip/siplib/apiversions.c +00:27:40 runner ['/usr/bin/gcc', '-fPIC', '-pthread', '-pthread', '-pthread', '-I/usr/include/gtk-3.0', '-I/usr/include/at-spi2-atk/2.0', '-I/usr/include/at-spi-2.0', '-I/usr/include/dbus-1.0', '-I/usr/lib64/dbus-1.0/include', '-I/usr/include/gtk-3.0', '-I/usr/include/gio-unix-2.0/', '-I/usr/include/libxkbcommon', '-I/usr/include/wayland', '-I/usr/include/cairo', '-I/usr/include/pango-1.0', '-I/usr/include/harfbuzz', '-I/usr/include/pango-1.0', '-I/usr/include/atk-1.0', '-I/usr/include/cairo', '-I/usr/include/pixman-1', '-I/usr/include/freetype2', '-I/usr/include/libdrm', '-I/usr/include/libpng16', '-I/usr/include/gdk-pixbuf-2.0', '-I/usr/include/glib-2.0', '-I/usr/lib64/glib-2.0/include', '-UNDEBUG', '-DSIP_MODULE_NAME=wx.siplib', '-DSIP_MODULE_BASENAME=siplib', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-I../../../wxbld/gtk3/lib/wx/include/gtk3-unicode-3.1', '-I../../../../ext/wxWidgets/include', '-Isip/siplib', '-I../../../../sip/siplib', '-Iwx/include', '-I../../../../wx/include', '-Isrc', '-I../../../../src', '-I/usr/include/python3.6m', '-DPYTHONDIR="/usr/local/lib/python3.6/site-packages"', '-DPYTHONARCHDIR="/usr/local/lib64/python3.6/site-packages"', '-DHAVE_PYEXT=1', '-DHAVE_PYTHON_H=1', '-DHAVE_WX=1', '-DHAVE_WXADV=1', '-DHAVE_WXSTC=1', '-DHAVE_WXHTML=1', '-DHAVE_WXGL=1', '-DHAVE_WXWEBVIEW=1', '-DHAVE_WXXML=1', '-DHAVE_WXXRC=1', '-DHAVE_WXRICHTEXT=1', '-DHAVE_WXMEDIA=1', '-DHAVE_WXRIBBON=1', '-DHAVE_WXPROPGRID=1', '-DHAVE_WXAUI=1', '-D_FILE_OFFSET_BITS=64', '-D__WXGTK__', '-D_FORTIFY_SOURCE=2', '-D_FORTIFY_SOURCE=2', '-DOPENSSL_LOAD_CONF', '../../../../sip/siplib/apiversions.c', '-c', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/sip/siplib/apiversions.c.1.o'] +[ 4/918] Compiling sip/siplib/objmap.c +00:27:42 runner ['/usr/bin/gcc', '-fPIC', '-pthread', '-pthread', '-pthread', '-I/usr/include/gtk-3.0', '-I/usr/include/at-spi2-atk/2.0', '-I/usr/include/at-spi-2.0', '-I/usr/include/dbus-1.0', '-I/usr/lib64/dbus-1.0/include', '-I/usr/include/gtk-3.0', '-I/usr/include/gio-unix-2.0/', '-I/usr/include/libxkbcommon', '-I/usr/include/wayland', '-I/usr/include/cairo', '-I/usr/include/pango-1.0', '-I/usr/include/harfbuzz', '-I/usr/include/pango-1.0', '-I/usr/include/atk-1.0', '-I/usr/include/cairo', '-I/usr/include/pixman-1', '-I/usr/include/freetype2', '-I/usr/include/libdrm', '-I/usr/include/libpng16', '-I/usr/include/gdk-pixbuf-2.0', '-I/usr/include/glib-2.0', '-I/usr/lib64/glib-2.0/include', '-UNDEBUG', '-DSIP_MODULE_NAME=wx.siplib', '-DSIP_MODULE_BASENAME=siplib', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-I../../../wxbld/gtk3/lib/wx/include/gtk3-unicode-3.1', '-I../../../../ext/wxWidgets/include', '-Isip/siplib', '-I../../../../sip/siplib', '-Iwx/include', '-I../../../../wx/include', '-Isrc', '-I../../../../src', '-I/usr/include/python3.6m', '-DPYTHONDIR="/usr/local/lib/python3.6/site-packages"', '-DPYTHONARCHDIR="/usr/local/lib64/python3.6/site-packages"', '-DHAVE_PYEXT=1', '-DHAVE_PYTHON_H=1', '-DHAVE_WX=1', '-DHAVE_WXADV=1', '-DHAVE_WXSTC=1', '-DHAVE_WXHTML=1', '-DHAVE_WXGL=1', '-DHAVE_WXWEBVIEW=1', '-DHAVE_WXXML=1', '-DHAVE_WXXRC=1', '-DHAVE_WXRICHTEXT=1', '-DHAVE_WXMEDIA=1', '-DHAVE_WXRIBBON=1', '-DHAVE_WXPROPGRID=1', '-DHAVE_WXAUI=1', '-D_FILE_OFFSET_BITS=64', '-D__WXGTK__', '-D_FORTIFY_SOURCE=2', '-D_FORTIFY_SOURCE=2', '-DOPENSSL_LOAD_CONF', '../../../../sip/siplib/objmap.c', '-c', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/sip/siplib/objmap.c.1.o'] +[ 5/918] Compiling sip/siplib/voidptr.c +00:27:43 runner ['/usr/bin/gcc', '-fPIC', '-pthread', '-pthread', '-pthread', '-I/usr/include/gtk-3.0', '-I/usr/include/at-spi2-atk/2.0', '-I/usr/include/at-spi-2.0', '-I/usr/include/dbus-1.0', '-I/usr/lib64/dbus-1.0/include', '-I/usr/include/gtk-3.0', '-I/usr/include/gio-unix-2.0/', '-I/usr/include/libxkbcommon', '-I/usr/include/wayland', '-I/usr/include/cairo', '-I/usr/include/pango-1.0', '-I/usr/include/harfbuzz', '-I/usr/include/pango-1.0', '-I/usr/include/atk-1.0', '-I/usr/include/cairo', '-I/usr/include/pixman-1', '-I/usr/include/freetype2', '-I/usr/include/libdrm', '-I/usr/include/libpng16', '-I/usr/include/gdk-pixbuf-2.0', '-I/usr/include/glib-2.0', '-I/usr/lib64/glib-2.0/include', '-UNDEBUG', '-DSIP_MODULE_NAME=wx.siplib', '-DSIP_MODULE_BASENAME=siplib', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-I../../../wxbld/gtk3/lib/wx/include/gtk3-unicode-3.1', '-I../../../../ext/wxWidgets/include', '-Isip/siplib', '-I../../../../sip/siplib', '-Iwx/include', '-I../../../../wx/include', '-Isrc', '-I../../../../src', '-I/usr/include/python3.6m', '-DPYTHONDIR="/usr/local/lib/python3.6/site-packages"', '-DPYTHONARCHDIR="/usr/local/lib64/python3.6/site-packages"', '-DHAVE_PYEXT=1', '-DHAVE_PYTHON_H=1', '-DHAVE_WX=1', '-DHAVE_WXADV=1', '-DHAVE_WXSTC=1', '-DHAVE_WXHTML=1', '-DHAVE_WXGL=1', '-DHAVE_WXWEBVIEW=1', '-DHAVE_WXXML=1', '-DHAVE_WXXRC=1', '-DHAVE_WXRICHTEXT=1', '-DHAVE_WXMEDIA=1', '-DHAVE_WXRIBBON=1', '-DHAVE_WXPROPGRID=1', '-DHAVE_WXAUI=1', '-D_FILE_OFFSET_BITS=64', '-D__WXGTK__', '-D_FORTIFY_SOURCE=2', '-D_FORTIFY_SOURCE=2', '-DOPENSSL_LOAD_CONF', '../../../../sip/siplib/voidptr.c', '-c', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/sip/siplib/voidptr.c.1.o'] +[ 6/918] Compiling sip/siplib/array.c +00:27:45 runner ['/usr/bin/gcc', '-fPIC', '-pthread', '-pthread', '-pthread', '-I/usr/include/gtk-3.0', '-I/usr/include/at-spi2-atk/2.0', '-I/usr/include/at-spi-2.0', '-I/usr/include/dbus-1.0', '-I/usr/lib64/dbus-1.0/include', '-I/usr/include/gtk-3.0', '-I/usr/include/gio-unix-2.0/', '-I/usr/include/libxkbcommon', '-I/usr/include/wayland', '-I/usr/include/cairo', '-I/usr/include/pango-1.0', '-I/usr/include/harfbuzz', '-I/usr/include/pango-1.0', '-I/usr/include/atk-1.0', '-I/usr/include/cairo', '-I/usr/include/pixman-1', '-I/usr/include/freetype2', '-I/usr/include/libdrm', '-I/usr/include/libpng16', '-I/usr/include/gdk-pixbuf-2.0', '-I/usr/include/glib-2.0', '-I/usr/lib64/glib-2.0/include', '-UNDEBUG', '-DSIP_MODULE_NAME=wx.siplib', '-DSIP_MODULE_BASENAME=siplib', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-I../../../wxbld/gtk3/lib/wx/include/gtk3-unicode-3.1', '-I../../../../ext/wxWidgets/include', '-Isip/siplib', '-I../../../../sip/siplib', '-Iwx/include', '-I../../../../wx/include', '-Isrc', '-I../../../../src', '-I/usr/include/python3.6m', '-DPYTHONDIR="/usr/local/lib/python3.6/site-packages"', '-DPYTHONARCHDIR="/usr/local/lib64/python3.6/site-packages"', '-DHAVE_PYEXT=1', '-DHAVE_PYTHON_H=1', '-DHAVE_WX=1', '-DHAVE_WXADV=1', '-DHAVE_WXSTC=1', '-DHAVE_WXHTML=1', '-DHAVE_WXGL=1', '-DHAVE_WXWEBVIEW=1', '-DHAVE_WXXML=1', '-DHAVE_WXXRC=1', '-DHAVE_WXRICHTEXT=1', '-DHAVE_WXMEDIA=1', '-DHAVE_WXRIBBON=1', '-DHAVE_WXPROPGRID=1', '-DHAVE_WXAUI=1', '-D_FILE_OFFSET_BITS=64', '-D__WXGTK__', '-D_FORTIFY_SOURCE=2', '-D_FORTIFY_SOURCE=2', '-DOPENSSL_LOAD_CONF', '../../../../sip/siplib/array.c', '-c', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/sip/siplib/array.c.1.o'] +[ 7/918] Compiling sip/siplib/descriptors.c +00:27:46 runner ['/usr/bin/gcc', '-fPIC', '-pthread', '-pthread', '-pthread', '-I/usr/include/gtk-3.0', '-I/usr/include/at-spi2-atk/2.0', '-I/usr/include/at-spi-2.0', '-I/usr/include/dbus-1.0', '-I/usr/lib64/dbus-1.0/include', '-I/usr/include/gtk-3.0', '-I/usr/include/gio-unix-2.0/', '-I/usr/include/libxkbcommon', '-I/usr/include/wayland', '-I/usr/include/cairo', '-I/usr/include/pango-1.0', '-I/usr/include/harfbuzz', '-I/usr/include/pango-1.0', '-I/usr/include/atk-1.0', '-I/usr/include/cairo', '-I/usr/include/pixman-1', '-I/usr/include/freetype2', '-I/usr/include/libdrm', '-I/usr/include/libpng16', '-I/usr/include/gdk-pixbuf-2.0', '-I/usr/include/glib-2.0', '-I/usr/lib64/glib-2.0/include', '-UNDEBUG', '-DSIP_MODULE_NAME=wx.siplib', '-DSIP_MODULE_BASENAME=siplib', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-I../../../wxbld/gtk3/lib/wx/include/gtk3-unicode-3.1', '-I../../../../ext/wxWidgets/include', '-Isip/siplib', '-I../../../../sip/siplib', '-Iwx/include', '-I../../../../wx/include', '-Isrc', '-I../../../../src', '-I/usr/include/python3.6m', '-DPYTHONDIR="/usr/local/lib/python3.6/site-packages"', '-DPYTHONARCHDIR="/usr/local/lib64/python3.6/site-packages"', '-DHAVE_PYEXT=1', '-DHAVE_PYTHON_H=1', '-DHAVE_WX=1', '-DHAVE_WXADV=1', '-DHAVE_WXSTC=1', '-DHAVE_WXHTML=1', '-DHAVE_WXGL=1', '-DHAVE_WXWEBVIEW=1', '-DHAVE_WXXML=1', '-DHAVE_WXXRC=1', '-DHAVE_WXRICHTEXT=1', '-DHAVE_WXMEDIA=1', '-DHAVE_WXRIBBON=1', '-DHAVE_WXPROPGRID=1', '-DHAVE_WXAUI=1', '-D_FILE_OFFSET_BITS=64', '-D__WXGTK__', '-D_FORTIFY_SOURCE=2', '-D_FORTIFY_SOURCE=2', '-DOPENSSL_LOAD_CONF', '../../../../sip/siplib/descriptors.c', '-c', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/sip/siplib/descriptors.c.1.o'] +[ 8/918] Compiling sip/siplib/siplib.c +00:27:49 runner ['/usr/bin/gcc', '-fPIC', '-pthread', '-pthread', '-pthread', '-I/usr/include/gtk-3.0', '-I/usr/include/at-spi2-atk/2.0', '-I/usr/include/at-spi-2.0', '-I/usr/include/dbus-1.0', '-I/usr/lib64/dbus-1.0/include', '-I/usr/include/gtk-3.0', '-I/usr/include/gio-unix-2.0/', '-I/usr/include/libxkbcommon', '-I/usr/include/wayland', '-I/usr/include/cairo', '-I/usr/include/pango-1.0', '-I/usr/include/harfbuzz', '-I/usr/include/pango-1.0', '-I/usr/include/atk-1.0', '-I/usr/include/cairo', '-I/usr/include/pixman-1', '-I/usr/include/freetype2', '-I/usr/include/libdrm', '-I/usr/include/libpng16', '-I/usr/include/gdk-pixbuf-2.0', '-I/usr/include/glib-2.0', '-I/usr/lib64/glib-2.0/include', '-UNDEBUG', '-DSIP_MODULE_NAME=wx.siplib', '-DSIP_MODULE_BASENAME=siplib', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-I../../../wxbld/gtk3/lib/wx/include/gtk3-unicode-3.1', '-I../../../../ext/wxWidgets/include', '-Isip/siplib', '-I../../../../sip/siplib', '-Iwx/include', '-I../../../../wx/include', '-Isrc', '-I../../../../src', '-I/usr/include/python3.6m', '-DPYTHONDIR="/usr/local/lib/python3.6/site-packages"', '-DPYTHONARCHDIR="/usr/local/lib64/python3.6/site-packages"', '-DHAVE_PYEXT=1', '-DHAVE_PYTHON_H=1', '-DHAVE_WX=1', '-DHAVE_WXADV=1', '-DHAVE_WXSTC=1', '-DHAVE_WXHTML=1', '-DHAVE_WXGL=1', '-DHAVE_WXWEBVIEW=1', '-DHAVE_WXXML=1', '-DHAVE_WXXRC=1', '-DHAVE_WXRICHTEXT=1', '-DHAVE_WXMEDIA=1', '-DHAVE_WXRIBBON=1', '-DHAVE_WXPROPGRID=1', '-DHAVE_WXAUI=1', '-D_FILE_OFFSET_BITS=64', '-D__WXGTK__', '-D_FORTIFY_SOURCE=2', '-D_FORTIFY_SOURCE=2', '-DOPENSSL_LOAD_CONF', '../../../../sip/siplib/siplib.c', '-c', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/sip/siplib/siplib.c.1.o'] +[ 9/918] Compiling sip/siplib/int_convertors.c +00:27:49 runner ['/usr/bin/gcc', '-fPIC', '-pthread', '-pthread', '-pthread', '-I/usr/include/gtk-3.0', '-I/usr/include/at-spi2-atk/2.0', '-I/usr/include/at-spi-2.0', '-I/usr/include/dbus-1.0', '-I/usr/lib64/dbus-1.0/include', '-I/usr/include/gtk-3.0', '-I/usr/include/gio-unix-2.0/', '-I/usr/include/libxkbcommon', '-I/usr/include/wayland', '-I/usr/include/cairo', '-I/usr/include/pango-1.0', '-I/usr/include/harfbuzz', '-I/usr/include/pango-1.0', '-I/usr/include/atk-1.0', '-I/usr/include/cairo', '-I/usr/include/pixman-1', '-I/usr/include/freetype2', '-I/usr/include/libdrm', '-I/usr/include/libpng16', '-I/usr/include/gdk-pixbuf-2.0', '-I/usr/include/glib-2.0', '-I/usr/lib64/glib-2.0/include', '-UNDEBUG', '-DSIP_MODULE_NAME=wx.siplib', '-DSIP_MODULE_BASENAME=siplib', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-I../../../wxbld/gtk3/lib/wx/include/gtk3-unicode-3.1', '-I../../../../ext/wxWidgets/include', '-Isip/siplib', '-I../../../../sip/siplib', '-Iwx/include', '-I../../../../wx/include', '-Isrc', '-I../../../../src', '-I/usr/include/python3.6m', '-DPYTHONDIR="/usr/local/lib/python3.6/site-packages"', '-DPYTHONARCHDIR="/usr/local/lib64/python3.6/site-packages"', '-DHAVE_PYEXT=1', '-DHAVE_PYTHON_H=1', '-DHAVE_WX=1', '-DHAVE_WXADV=1', '-DHAVE_WXSTC=1', '-DHAVE_WXHTML=1', '-DHAVE_WXGL=1', '-DHAVE_WXWEBVIEW=1', '-DHAVE_WXXML=1', '-DHAVE_WXXRC=1', '-DHAVE_WXRICHTEXT=1', '-DHAVE_WXMEDIA=1', '-DHAVE_WXRIBBON=1', '-DHAVE_WXPROPGRID=1', '-DHAVE_WXAUI=1', '-D_FILE_OFFSET_BITS=64', '-D__WXGTK__', '-D_FORTIFY_SOURCE=2', '-D_FORTIFY_SOURCE=2', '-DOPENSSL_LOAD_CONF', '../../../../sip/siplib/int_convertors.c', '-c', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/sip/siplib/int_convertors.c.1.o'] +[ 10/918] Compiling sip/siplib/threads.c +00:27:51 runner ['/usr/bin/gcc', '-fPIC', '-pthread', '-pthread', '-pthread', '-I/usr/include/gtk-3.0', '-I/usr/include/at-spi2-atk/2.0', '-I/usr/include/at-spi-2.0', '-I/usr/include/dbus-1.0', '-I/usr/lib64/dbus-1.0/include', '-I/usr/include/gtk-3.0', '-I/usr/include/gio-unix-2.0/', '-I/usr/include/libxkbcommon', '-I/usr/include/wayland', '-I/usr/include/cairo', '-I/usr/include/pango-1.0', '-I/usr/include/harfbuzz', '-I/usr/include/pango-1.0', '-I/usr/include/atk-1.0', '-I/usr/include/cairo', '-I/usr/include/pixman-1', '-I/usr/include/freetype2', '-I/usr/include/libdrm', '-I/usr/include/libpng16', '-I/usr/include/gdk-pixbuf-2.0', '-I/usr/include/glib-2.0', '-I/usr/lib64/glib-2.0/include', '-UNDEBUG', '-DSIP_MODULE_NAME=wx.siplib', '-DSIP_MODULE_BASENAME=siplib', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-I../../../wxbld/gtk3/lib/wx/include/gtk3-unicode-3.1', '-I../../../../ext/wxWidgets/include', '-Isip/siplib', '-I../../../../sip/siplib', '-Iwx/include', '-I../../../../wx/include', '-Isrc', '-I../../../../src', '-I/usr/include/python3.6m', '-DPYTHONDIR="/usr/local/lib/python3.6/site-packages"', '-DPYTHONARCHDIR="/usr/local/lib64/python3.6/site-packages"', '-DHAVE_PYEXT=1', '-DHAVE_PYTHON_H=1', '-DHAVE_WX=1', '-DHAVE_WXADV=1', '-DHAVE_WXSTC=1', '-DHAVE_WXHTML=1', '-DHAVE_WXGL=1', '-DHAVE_WXWEBVIEW=1', '-DHAVE_WXXML=1', '-DHAVE_WXXRC=1', '-DHAVE_WXRICHTEXT=1', '-DHAVE_WXMEDIA=1', '-DHAVE_WXRIBBON=1', '-DHAVE_WXPROPGRID=1', '-DHAVE_WXAUI=1', '-D_FILE_OFFSET_BITS=64', '-D__WXGTK__', '-D_FORTIFY_SOURCE=2', '-D_FORTIFY_SOURCE=2', '-DOPENSSL_LOAD_CONF', '../../../../sip/siplib/threads.c', '-c', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/sip/siplib/threads.c.1.o'] +[ 11/918] Compiling sip/cpp/sip_corewxLogTextCtrl.cpp +00:27:53 runner ['/usr/bin/g++', '-fPIC', '-pthread', '-pthread', '-pthread', '-I/usr/include/gtk-3.0', '-I/usr/include/at-spi2-atk/2.0', '-I/usr/include/at-spi-2.0', '-I/usr/include/dbus-1.0', '-I/usr/lib64/dbus-1.0/include', '-I/usr/include/gtk-3.0', '-I/usr/include/gio-unix-2.0/', '-I/usr/include/libxkbcommon', '-I/usr/include/wayland', '-I/usr/include/cairo', '-I/usr/include/pango-1.0', '-I/usr/include/harfbuzz', '-I/usr/include/pango-1.0', '-I/usr/include/atk-1.0', '-I/usr/include/cairo', '-I/usr/include/pixman-1', '-I/usr/include/freetype2', '-I/usr/include/libdrm', '-I/usr/include/libpng16', '-I/usr/include/gdk-pixbuf-2.0', '-I/usr/include/glib-2.0', '-I/usr/lib64/glib-2.0/include', '-UNDEBUG', '-DSIP_MODULE_NAME=wx.siplib', '-DSIP_MODULE_BASENAME=siplib', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-I../../../wxbld/gtk3/lib/wx/include/gtk3-unicode-3.1', '-I../../../../ext/wxWidgets/include', '-Isip/siplib', '-I../../../../sip/siplib', '-Iwx/include', '-I../../../../wx/include', '-Isrc', '-I../../../../src', '-I/usr/include/python3.6m', '-DPYTHONDIR="/usr/local/lib/python3.6/site-packages"', '-DPYTHONARCHDIR="/usr/local/lib64/python3.6/site-packages"', '-DHAVE_PYEXT=1', '-DHAVE_PYTHON_H=1', '-DHAVE_WX=1', '-DHAVE_WXADV=1', '-DHAVE_WXSTC=1', '-DHAVE_WXHTML=1', '-DHAVE_WXGL=1', '-DHAVE_WXWEBVIEW=1', '-DHAVE_WXXML=1', '-DHAVE_WXXRC=1', '-DHAVE_WXRICHTEXT=1', '-DHAVE_WXMEDIA=1', '-DHAVE_WXRIBBON=1', '-DHAVE_WXPROPGRID=1', '-DHAVE_WXAUI=1', '-D_FILE_OFFSET_BITS=64', '-D__WXGTK__', '-D_FORTIFY_SOURCE=2', '-D_FORTIFY_SOURCE=2', '-DOPENSSL_LOAD_CONF', '../../../../sip/cpp/sip_corewxLogTextCtrl.cpp', '-c', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/sip/cpp/sip_corewxLogTextCtrl.cpp.3.o'] +[ 12/918] Compiling sip/cpp/sip_corewxEvtHandler.cpp +00:28:32 runner ['/usr/bin/g++', '-fPIC', '-pthread', '-pthread', '-pthread', '-I/usr/include/gtk-3.0', '-I/usr/include/at-spi2-atk/2.0', '-I/usr/include/at-spi-2.0', '-I/usr/include/dbus-1.0', '-I/usr/lib64/dbus-1.0/include', '-I/usr/include/gtk-3.0', '-I/usr/include/gio-unix-2.0/', '-I/usr/include/libxkbcommon', '-I/usr/include/wayland', '-I/usr/include/cairo', '-I/usr/include/pango-1.0', '-I/usr/include/harfbuzz', '-I/usr/include/pango-1.0', '-I/usr/include/atk-1.0', '-I/usr/include/cairo', '-I/usr/include/pixman-1', '-I/usr/include/freetype2', '-I/usr/include/libdrm', '-I/usr/include/libpng16', '-I/usr/include/gdk-pixbuf-2.0', '-I/usr/include/glib-2.0', '-I/usr/lib64/glib-2.0/include', '-UNDEBUG', '-DSIP_MODULE_NAME=wx.siplib', '-DSIP_MODULE_BASENAME=siplib', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-I../../../wxbld/gtk3/lib/wx/include/gtk3-unicode-3.1', '-I../../../../ext/wxWidgets/include', '-Isip/siplib', '-I../../../../sip/siplib', '-Iwx/include', '-I../../../../wx/include', '-Isrc', '-I../../../../src', '-I/usr/include/python3.6m', '-DPYTHONDIR="/usr/local/lib/python3.6/site-packages"', '-DPYTHONARCHDIR="/usr/local/lib64/python3.6/site-packages"', '-DHAVE_PYEXT=1', '-DHAVE_PYTHON_H=1', '-DHAVE_WX=1', '-DHAVE_WXADV=1', '-DHAVE_WXSTC=1', '-DHAVE_WXHTML=1', '-DHAVE_WXGL=1', '-DHAVE_WXWEBVIEW=1', '-DHAVE_WXXML=1', '-DHAVE_WXXRC=1', '-DHAVE_WXRICHTEXT=1', '-DHAVE_WXMEDIA=1', '-DHAVE_WXRIBBON=1', '-DHAVE_WXPROPGRID=1', '-DHAVE_WXAUI=1', '-D_FILE_OFFSET_BITS=64', '-D__WXGTK__', '-D_FORTIFY_SOURCE=2', '-D_FORTIFY_SOURCE=2', '-DOPENSSL_LOAD_CONF', '../../../../sip/cpp/sip_corewxEvtHandler.cpp', '-c', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/sip/cpp/sip_corewxEvtHandler.cpp.3.o'] +[ 13/918] Compiling sip/cpp/sip_corewxCollapsiblePaneEvent.cpp +00:28:51 runner ['/usr/bin/g++', '-fPIC', '-pthread', '-pthread', '-pthread', '-I/usr/include/gtk-3.0', '-I/usr/include/at-spi2-atk/2.0', '-I/usr/include/at-spi-2.0', '-I/usr/include/dbus-1.0', '-I/usr/lib64/dbus-1.0/include', '-I/usr/include/gtk-3.0', '-I/usr/include/gio-unix-2.0/', '-I/usr/include/libxkbcommon', '-I/usr/include/wayland', '-I/usr/include/cairo', '-I/usr/include/pango-1.0', '-I/usr/include/harfbuzz', '-I/usr/include/pango-1.0', '-I/usr/include/atk-1.0', '-I/usr/include/cairo', '-I/usr/include/pixman-1', '-I/usr/include/freetype2', '-I/usr/include/libdrm', '-I/usr/include/libpng16', '-I/usr/include/gdk-pixbuf-2.0', '-I/usr/include/glib-2.0', '-I/usr/lib64/glib-2.0/include', '-UNDEBUG', '-DSIP_MODULE_NAME=wx.siplib', '-DSIP_MODULE_BASENAME=siplib', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-I../../../wxbld/gtk3/lib/wx/include/gtk3-unicode-3.1', '-I../../../../ext/wxWidgets/include', '-Isip/siplib', '-I../../../../sip/siplib', '-Iwx/include', '-I../../../../wx/include', '-Isrc', '-I../../../../src', '-I/usr/include/python3.6m', '-DPYTHONDIR="/usr/local/lib/python3.6/site-packages"', '-DPYTHONARCHDIR="/usr/local/lib64/python3.6/site-packages"', '-DHAVE_PYEXT=1', '-DHAVE_PYTHON_H=1', '-DHAVE_WX=1', '-DHAVE_WXADV=1', '-DHAVE_WXSTC=1', '-DHAVE_WXHTML=1', '-DHAVE_WXGL=1', '-DHAVE_WXWEBVIEW=1', '-DHAVE_WXXML=1', '-DHAVE_WXXRC=1', '-DHAVE_WXRICHTEXT=1', '-DHAVE_WXMEDIA=1', '-DHAVE_WXRIBBON=1', '-DHAVE_WXPROPGRID=1', '-DHAVE_WXAUI=1', '-D_FILE_OFFSET_BITS=64', '-D__WXGTK__', '-D_FORTIFY_SOURCE=2', '-D_FORTIFY_SOURCE=2', '-DOPENSSL_LOAD_CONF', '../../../../sip/cpp/sip_corewxCollapsiblePaneEvent.cpp', '-c', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/sip/cpp/sip_corewxCollapsiblePaneEvent.cpp.3.o'] +[ 14/918] Linking build/waf/3.6/gtk3/siplib.cpython-36m-x86_64-linux-gnu.so +00:29:24 runner ['/usr/bin/g++', '-shared', '-shared', '-pthread', '-pthread', '-Xlinker', '-export-dynamic', 'sip/siplib/apiversions.c.1.o', 'sip/siplib/array.c.1.o', 'sip/siplib/bool.cpp.1.o', 'sip/siplib/descriptors.c.1.o', 'sip/siplib/int_convertors.c.1.o', 'sip/siplib/objmap.c.1.o', 'sip/siplib/qtlib.c.1.o', 'sip/siplib/siplib.c.1.o', 'sip/siplib/threads.c.1.o', 'sip/siplib/voidptr.c.1.o', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/siplib.cpython-36m-x86_64-linux-gnu.so', '-Wl,-Bstatic', '-Wl,-Bdynamic', '-L/home/oleksis/Phoenix/build/wxbld/gtk3/lib', '-lwx_gtk3u_core-3.1', '-lwx_baseu_net-3.1', '-lwx_baseu-3.1'] +[ 15/918] Compiling sip/cpp/sip_corewxAccessible.cpp +00:29:29 runner ['/usr/bin/g++', '-fPIC', '-pthread', '-pthread', '-pthread', '-I/usr/include/gtk-3.0', '-I/usr/include/at-spi2-atk/2.0', '-I/usr/include/at-spi-2.0', '-I/usr/include/dbus-1.0', '-I/usr/lib64/dbus-1.0/include', '-I/usr/include/gtk-3.0', '-I/usr/include/gio-unix-2.0/', '-I/usr/include/libxkbcommon', '-I/usr/include/wayland', '-I/usr/include/cairo', '-I/usr/include/pango-1.0', '-I/usr/include/harfbuzz', '-I/usr/include/pango-1.0', '-I/usr/include/atk-1.0', '-I/usr/include/cairo', '-I/usr/include/pixman-1', '-I/usr/include/freetype2', '-I/usr/include/libdrm', '-I/usr/include/libpng16', '-I/usr/include/gdk-pixbuf-2.0', '-I/usr/include/glib-2.0', '-I/usr/lib64/glib-2.0/include', '-UNDEBUG', '-DSIP_MODULE_NAME=wx.siplib', '-DSIP_MODULE_BASENAME=siplib', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-I../../../wxbld/gtk3/lib/wx/include/gtk3-unicode-3.1', '-I../../../../ext/wxWidgets/include', '-Isip/siplib', '-I../../../../sip/siplib', '-Iwx/include', '-I../../../../wx/include', '-Isrc', '-I../../../../src', '-I/usr/include/python3.6m', '-DPYTHONDIR="/usr/local/lib/python3.6/site-packages"', '-DPYTHONARCHDIR="/usr/local/lib64/python3.6/site-packages"', '-DHAVE_PYEXT=1', '-DHAVE_PYTHON_H=1', '-DHAVE_WX=1', '-DHAVE_WXADV=1', '-DHAVE_WXSTC=1', '-DHAVE_WXHTML=1', '-DHAVE_WXGL=1', '-DHAVE_WXWEBVIEW=1', '-DHAVE_WXXML=1', '-DHAVE_WXXRC=1', '-DHAVE_WXRICHTEXT=1', '-DHAVE_WXMEDIA=1', '-DHAVE_WXRIBBON=1', '-DHAVE_WXPROPGRID=1', '-DHAVE_WXAUI=1', '-D_FILE_OFFSET_BITS=64', '-D__WXGTK__', '-D_FORTIFY_SOURCE=2', '-D_FORTIFY_SOURCE=2', '-DOPENSSL_LOAD_CONF', '../../../../sip/cpp/sip_corewxAccessible.cpp', '-c', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/sip/cpp/sip_corewxAccessible.cpp.3.o'] + + +... MORE OUTPUT ... + + + + + + + + + +====== +(env) oleksis@linux-fkos:~/Phoenix> python build.py build_py +Will build using: "/home/oleksis/Phoenix/env/bin/python" +3.6.5 (default, Mar 31 2018, 19:45:04) [GCC] +Python's architecture is 64bit +cfg.VERSION: 4.1.1a1 + +Running command: build_py +Checking for /home/oleksis/Phoenix/bin/waf-2.0.19... +Not found. Attempting to download... +Connection successful... +Data downloaded... +Checking for /home/oleksis/Phoenix/bin/waf-2.0.19... +"/home/oleksis/Phoenix/env/bin/python" /home/oleksis/Phoenix/bin/waf-2.0.19 --wx_config=/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config --gtk3 --python="/home/oleksis/Phoenix/env/bin/python" --out=build/waf/3.6/gtk3 configure build +Setting top to : /home/oleksis/Phoenix +Setting out to : /home/oleksis/Phoenix/build/waf/3.6/gtk3 +Checking for 'gcc' (C compiler) : /usr/bin/gcc +Checking for 'g++' (C++ compiler) : /usr/bin/g++ +Checking for program 'python' : /home/oleksis/Phoenix/env/bin/python +Checking for python version >= 2.7.0 : 3.6.5 +python-config : /usr/bin/python3.6-config +Asking python-config for pyext '--cflags --libs --ldflags' flags : yes +Testing pyext configuration : yes +Finding libs for WX : yes +Finding libs for WXADV : yes +Finding libs for WXSTC : yes +Finding libs for WXHTML : yes +WARNING: Unable to find setup.h in /home/oleksis/Phoenix/build/wxbld/gtk3/lib64/wx/include/gtk3-unicode-3.1, assuming wxUSE_GLCANVAS is not available. +Finding libs for WXGL : yes +WARNING: Unable to find setup.h in /home/oleksis/Phoenix/build/wxbld/gtk3/lib64/wx/include/gtk3-unicode-3.1, assuming wxUSE_WEBVIEW is not available. +Finding libs for WXWEBVIEW : yes +Finding libs for WXXML : yes +Finding libs for WXXRC : yes +Finding libs for WXRICHTEXT : yes +WARNING: Unable to find setup.h in /home/oleksis/Phoenix/build/wxbld/gtk3/lib64/wx/include/gtk3-unicode-3.1, assuming wxUSE_MEDIACTRL is not available. +Finding libs for WXMEDIA : yes +Finding libs for WXRIBBON : yes +Finding libs for WXPROPGRID : yes +Finding libs for WXAUI : yes +'configure' finished successfully (12.577s) +Waf: Entering directory `/home/oleksis/Phoenix/build/waf/3.6/gtk3' +**** Compiler: /usr/bin/gcc --version + gcc (SUSE Linux) 7.3.1 20180323 [gcc-7-branch revision 258812] + Copyright (C) 2017 Free Software Foundation, Inc. + This is free software; see the source for copying conditions. There is NO + warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +[ 1/918] Compiling sip/siplib/qtlib.c +[ 2/918] Compiling sip/siplib/bool.cpp +[ 3/918] Compiling sip/siplib/apiversions.c +[ 4/918] Compiling sip/siplib/objmap.c +[ 5/918] Compiling sip/siplib/voidptr.c +[ 6/918] Compiling sip/siplib/array.c +[ 7/918] Compiling sip/siplib/siplib.c +[ 8/918] Compiling sip/siplib/descriptors.c +[ 9/918] Compiling sip/siplib/int_convertors.c +[ 10/918] Compiling sip/siplib/threads.c +[ 11/918] Compiling sip/cpp/sip_corewxLogTextCtrl.cpp +In file included from ../../../../ext/wxWidgets/include/wx/defs.h:45:0, + from ../../../../ext/wxWidgets/include/wx/wx.h:14, + from ../../../../wx/include/wxPython/wxpy_api.h:41, + from ../../../../sip/cpp/sipAPI_core.h:20778, + from ../../../../sip/cpp/sip_corewxLogTextCtrl.cpp:10: +../../../../ext/wxWidgets/include/wx/platform.h:160:10: fatal error: wx/setup.h: No existe el fichero o el directorio + #include "wx/setup.h" + ^~~~~~~~~~~~ +compilation terminated. + +Waf: Leaving directory `/home/oleksis/Phoenix/build/waf/3.6/gtk3' +Build failed + -> task in '_core' failed with exit status 1 (run with -v to display more information) +Command '"/home/oleksis/Phoenix/env/bin/python" /home/oleksis/Phoenix/bin/waf-2.0.19 --wx_config=/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config --gtk3 --python="/home/oleksis/Phoenix/env/bin/python" --out=build/waf/3.6/gtk3 configure build ' failed with exit code 1. +Finished command: build_py (1m21.708s) + + + + + +====== + + 1743 translated messages, 54 fuzzy translations, 56 untranslated messages. + make[1]: se sale del directorio '/tmp/pip-install-ilickw8p/wxPython/ext/wxWidgets/locale' + make[1]: se entra en el directorio '/tmp/pip-install-ilickw8p/wxPython/ext/wxWidgets/locale' + msgfmt --verbose -c -o zh_TW.mo zh_TW.po + 1710 translated messages, 82 fuzzy translations, 61 untranslated messages. + make[1]: se sale del directorio '/tmp/pip-install-ilickw8p/wxPython/ext/wxWidgets/locale' + Finished command: build_wx (229m10.920s) + Running command: build_py + Checking for /tmp/pip-install-ilickw8p/wxPython/bin/waf-2.0.8... + "/usr/bin/python3" /tmp/pip-install-ilickw8p/wxPython/bin/waf-2.0.8 --wx_config=/tmp/pip-install-ilickw8p/wxPython/build/wxbld/gtk3/wx-config --gtk3 --python="/usr/bin/python3" --out=build/waf/3.6/gtk3 configure build + Setting top to : /tmp/pip-install-ilickw8p/wxPython + Setting out to : /tmp/pip-install-ilickw8p/wxPython/build/waf/3.6/gtk3 + Checking for 'gcc' (C compiler) : /usr/bin/gcc + Checking for 'g++' (C++ compiler) : /usr/bin/g++ + Checking for program 'python' : /usr/bin/python3 + Checking for python version >= 2.7.0 : 3.6.5 + python-config : /usr/bin/python3-config + Asking python-config for pyembed '--cflags --libs --ldflags' flags : yes + Testing pyembed configuration : yes + Asking python-config for pyext '--cflags --libs --ldflags' flags : yes + Testing pyext configuration : yes + Finding libs for WX : yes + Finding libs for WXADV : yes + Finding libs for WXSTC : yes + Finding libs for WXHTML : yes + Finding libs for WXGL : yes + Finding libs for WXWEBVIEW : yes + Finding libs for WXXML : yes + Finding libs for WXXRC : yes + Finding libs for WXRICHTEXT : yes + Finding libs for WXMEDIA : yes + Finding libs for WXRIBBON : yes + Finding libs for WXPROPGRID : yes + Finding libs for WXAUI : yes + 'configure' finished successfully (43.155s) + Waf: Entering directory `/tmp/pip-install-ilickw8p/wxPython/build/waf/3.6/gtk3' + [ 1/869] Compiling sip/siplib/qtlib.c + [ 2/869] Compiling sip/siplib/bool.cpp + [ 3/869] Compiling sip/siplib/apiversions.c + [ 4/869] Compiling sip/siplib/objmap.c + [ 5/869] Compiling sip/siplib/voidptr.c + [ 6/869] Compiling sip/siplib/array.c + [ 7/869] Compiling sip/siplib/siplib.c + [ 8/869] Compiling sip/siplib/descriptors.c + [ 9/869] Compiling sip/siplib/int_convertors.c + [ 10/869] Compiling sip/siplib/threads.c + [ 11/869] Compiling sip/cpp/sip_corewxMaximizeEvent.cpp + In file included from ../../../../ext/wxWidgets/include/wx/defs.h:27:0, + from ../../../../ext/wxWidgets/include/wx/wx.h:14, + from ../../../../wx/include/wxPython/wxpy_api.h:41, + from ../../../../sip/cpp/sipAPI_core.h:19840, + from ../../../../sip/cpp/sip_corewxMaximizeEvent.cpp:10: + ../../../../ext/wxWidgets/include/wx/platform.h:183:10: fatal error: wx/setup.h: No existe el fichero o el directorio + #include "wx/setup.h" + ^~~~~~~~~~~~ + compilation terminated. + + Waf: Leaving directory `/tmp/pip-install-ilickw8p/wxPython/build/waf/3.6/gtk3' + Build failed + -> task in '_core' failed with exit status 1 (run with -v to display more information) + Command '"/usr/bin/python3" /tmp/pip-install-ilickw8p/wxPython/bin/waf-2.0.8 --wx_config=/tmp/pip-install-ilickw8p/wxPython/build/wxbld/gtk3/wx-config --gtk3 --python="/usr/bin/python3" --out=build/waf/3.6/gtk3 configure build ' failed with exit code 1. + Finished command: build_py (2m57.71s) + Finished command: build (232m7.996s) + Command '"/usr/bin/python3" -u build.py build' failed with exit code 1. + + ---------------------------------------- +Command "/usr/bin/python3 -u -c "import setuptools, tokenize;__file__='/tmp/pip-install-ilickw8p/wxPython/setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" install --record /tmp/pip-record-pdbovxhc/install-record.txt --single-version-externally-managed --compile --user --prefix=" failed with error code 1 in /tmp/pip-install-ilickw8p/wxPython/ +You are using pip version 18.1, however version 20.2b1 is available. +You should consider upgrading via the 'pip install --upgrade pip' command. diff --git a/requirements.txt b/requirements.txt index c099900a..e522d52c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ Pypubsub==4.0.3 twodict==1.2 -wxPython==4.0.4 -pyinstaller==3.6 \ No newline at end of file +wxPython>=4.0.1 +pyinstaller==3.6 +polib==1.1.0 \ No newline at end of file diff --git a/setup.py b/setup.py index 90f3348d..0373c6c7 100644 --- a/setup.py +++ b/setup.py @@ -50,21 +50,45 @@ from distutils.errors import DistutilsExecError import time - -from youtube_dl_gui import __packagename__ -from youtube_dl_gui.info import ( - __author__, - __appname__, - __contact__, - __maintainer__, - __maintainer_contact__, - __license__, - __projecturl__, - __description__, - __descriptionfull__ -) - - +import polib + + +__author__ = 'Sotiris Papadopoulos' +__contact__ = 'ytubedlg@gmail.com' +__maintainer__ = 'Oleksis Fraga' +__maintainer_contact__ = 'oleksis.fraga@gmail.com' +__projecturl__ = 'https://github.com/oleksis/youtube-dl-gui/' +__appname__ = 'Youtube-DLG' +__license__ = 'UNLICENSE' +__description__ = 'Youtube-dl GUI' +__descriptionfull__ = '''A cross platform front-end GUI of the popular +youtube-dl written in wxPython Phoenix''' +__licensefull__ = ''' +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to ''' +__packagename__ = "youtube_dl_gui" DESCRIPTION = __description__ LONG_DESCRIPTION = __descriptionfull__ @@ -123,21 +147,18 @@ def finalize_options(self): self.search_pattern = os.path.join(__packagename__, "locale", "*", "LC_MESSAGES", "youtube_dl_gui.po") def run(self): - tools_path = os.path.join(sys.exec_prefix, 'Tools') - msgfmt_file = os.path.join(tools_path, 'i18n', 'msgfmt.py') + tools_path = os.path.join(sys.exec_prefix, 'bin') + msgfmt_file = os.path.join(tools_path, 'msgfmt') - if os.path.isfile(msgfmt_file): + try: for po_file in glob.glob(self.search_pattern): mo_file = po_file.replace('.po', '.mo') + po = polib.pofile(po_file) - try: - log.info("Building MO file for '{}'".format(po_file)) - spawn([sys.executable, msgfmt_file, '-o', mo_file, po_file]) - except DistutilsExecError as error: - log.error("Error '{}', exiting...".format(error)) - sys.exit(1) - else: - log.error("Could not locate file '{}', exiting...".format(msgfmt_file)) + log.info("Building MO file for '{}'".format(po_file)) + po.save_as_mofile(mo_file) + except: + log.error("Could not locate file '{}', exiting...".format(po_file)) sys.exit(1) @@ -226,14 +247,22 @@ def finalize_options(self): pass def run(self, version=version_file): + # --add-data + # ``os.pathsep`` which is ``;`` on Windows + # and ``:`` on most unix systems) is used. + if on_windows(): + path_sep = ";" + else: + path_sep = ":" + spawn( [ "pyinstaller", "-w", "-F", "--icon="+__packagename__+"/data/pixmaps/youtube-dl-gui.ico", - "--add-data="+__packagename__+"/data;"+__packagename__+"/data", - "--add-data="+__packagename__+"/locale;"+__packagename__+"/locale", + "--add-data="+__packagename__+"/data"+path_sep+__packagename__+"/data", + "--add-data="+__packagename__+"/locale"+path_sep+__packagename__+"/locale", "--exclude-module=tests", "--name=youtube-dl-gui", ""+__packagename__+"/__main__.py", From 4ba13b3a58ae7d06a06201c337aa4e507953c118 Mon Sep 17 00:00:00 2001 From: oleksis <“oleksis.fraga@gmail.com”> Date: Tue, 28 Jul 2020 18:26:46 -0400 Subject: [PATCH 07/34] Update setup.py, requirements.txt --- ChangeLog | 4 +- TODO | 434 ++++------------------------------------------- requirements.txt | 2 +- setup.py | 56 ++---- 4 files changed, 44 insertions(+), 452 deletions(-) diff --git a/ChangeLog b/ChangeLog index 009c1988..aac35487 100644 --- a/ChangeLog +++ b/ChangeLog @@ -19,8 +19,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) - Migration to Python 3.* - README.md - Refactored setup.py - - Build the translation files (*.mo) using Python 3 Tools i18n msgfmt - - Build the binaries using PyInstaller + - Build the translation files (*.mo) using polib + - Build the binaries using PyInstaller. Fix path sep for each OS - Build with updates disabled - Set up for Windows/Linux - Ignore wxPyDeprecationWarning diff --git a/TODO b/TODO index 13ee7dee..f4464195 100644 --- a/TODO +++ b/TODO @@ -33,16 +33,6 @@ Other * Use youtube-dl directly from python instead of using the subprocess module -Extras -====== -* En GNU/Linux requiere gtk3-devel - Development files for the GTK+ toolkit library v3 -* freeglut-devel (Freeglut is a completely open source alternative to the OpenGL Utility Toolkit (GLUT) library - alternative to OpenGL headers) -python3-gst -* gstreamer-plugins-base-devel -* gstreamer-devel (GStreamer 1.0) -* Opcional libwx_gtk3 - - Probably wont add ================= * ListCtrl double click to "Rename" @@ -53,410 +43,48 @@ Probably wont add * Use proxy during update phase (see: issue #244) -COMMANDS -======== -git submodule update --recursive - -OR - -git clone https://github.com/wxWidgets/wxWidgets.git /home/oleksis/Phoenix/ext/wxWidgets -git clone https://github.com/wxWidgets/Catch.git /home/oleksis/Phoenix/ext/wxWidgets/3rdparty/catch - - -python3 -m venv env -source env/bin/activate -pip install -r requirements.txt -python build.py dox etg --nodoc sip build -python build.py build_py -v - - -ERRORS +Extras ====== +* Prerequisites + gcc-c++, make, gtk3-devel, webkit2gtk3-devel, gstreamer-plugins-base-devel, + libtiff-devel, libjpeg-devel, libpng-devel, SDL2-devel, libSM-devel, + python3-devel, zlib-devel, libexpat-devel, glu-devel -=== CON ERROR en wx-config unicode ===== -(env) oleksis@linux-fkos:~/Phoenix> python build.py build_py -v -Will build using: "/home/oleksis/Phoenix/env/bin/python" -3.6.5 (default, Mar 31 2018, 19:45:04) [GCC] -Python's architecture is 64bit -cfg.VERSION: 4.1.1a1 - -Running command: build_py -Checking for /home/oleksis/Phoenix/bin/waf-2.0.19... -"/home/oleksis/Phoenix/env/bin/python" /home/oleksis/Phoenix/bin/waf-2.0.19 --verbose --wx_config=/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config --verbose --gtk3 --python="/home/oleksis/Phoenix/env/bin/python" --out=build/waf/3.6/gtk3 configure build -Setting top to : /home/oleksis/Phoenix -Setting out to : /home/oleksis/Phoenix/build/waf/3.6/gtk3 -Checking for 'gcc' (C compiler) : 22:14:36 runner ['/usr/bin/gcc', '-dM', '-E', '-'] -/usr/bin/gcc -Checking for 'g++' (C++ compiler) : 22:14:37 runner ['/usr/bin/g++', '-dM', '-E', '-'] -/usr/bin/g++ -Checking for program 'python' : /home/oleksis/Phoenix/env/bin/python -22:14:37 runner ['/home/oleksis/Phoenix/env/bin/python', '-c', 'import sys\ntry:\n print(sys.implementation.cache_tag)\nexcept AttributeError:\n import imp\n print(imp.get_tag())\n'] -22:14:38 runner ['/home/oleksis/Phoenix/env/bin/python', '-c', 'import sys\nfor x in sys.version_info: print(str(x))'] -22:14:38 runner ['/home/oleksis/Phoenix/env/bin/python', '-c', "from distutils.sysconfig import get_config_var, get_python_lib\n\nprint(repr(get_python_lib(standard_lib=0, prefix='/usr/local') or ''))"] -22:14:39 runner ['/home/oleksis/Phoenix/env/bin/python', '-c', "from distutils.sysconfig import get_config_var, get_python_lib\n\nprint(repr(get_python_lib(plat_specific=1, standard_lib=0, prefix='/usr/local') or ''))"] -Checking for python version >= 2.7.0 : 3.6.5 -22:14:39 runner ['/home/oleksis/Phoenix/env/bin/python', '-c', "from distutils.sysconfig import get_config_var, get_python_lib\n\nprint(repr(get_config_var('prefix') or ''))\nprint(repr(get_config_var('SO') or ''))\nprint(repr(get_config_var('LDFLAGS') or ''))\nprint(repr(get_config_var('LIBDIR') or ''))\nprint(repr(get_config_var('LIBPL') or ''))\nprint(repr(get_config_var('INCLUDEPY') or ''))\nprint(repr(get_config_var('Py_ENABLE_SHARED') or ''))\nprint(repr(get_config_var('MACOSX_DEPLOYMENT_TARGET') or ''))\nprint(repr(get_config_var('LDSHARED') or ''))\nprint(repr(get_config_var('CFLAGS') or ''))\nprint(repr(get_config_var('LDVERSION') or ''))"] -python-config : /usr/bin/python3.6-config -Asking python-config for pyext '--cflags --libs --ldflags' flags : 22:14:39 runner ['/usr/bin/python3.6-config', '--cflags', '--libs', '--ldflags'] -yes -Testing pyext configuration : 22:14:40 runner ['/usr/bin/g++', '-fPIC', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-I/usr/include/python3.6m', '-DPYTHONDIR="/usr/local/lib/python3.6/site-packages"', '-DPYTHONARCHDIR="/usr/local/lib64/python3.6/site-packages"', '-D_FORTIFY_SOURCE=2', '-DNDEBUG', '-D_FORTIFY_SOURCE=2', '-DOPENSSL_LOAD_CONF', '../test.cpp', '-c', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/.conf_check_eaab107a0ad0c09fe99b0e743a890f00/testbuild/test.cpp.1.o'] -22:14:41 runner ['/usr/bin/g++', '-shared', '-Xlinker', '-export-dynamic', 'test.cpp.1.o', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/.conf_check_eaab107a0ad0c09fe99b0e743a890f00/testbuild/testprog.cpython-36m-x86_64-linux-gnu.so', '-Wl,-Bstatic', '-Wl,-Bdynamic', '-L/usr/lib64', '-lpython3.6m', '-lpthread', '-ldl', '-lutil', '-lm', '-lpython3.6m', '-lpthread', '-ldl', '-lutil', '-lm'] -yes -Finding libs for WX : 22:14:43 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'core,net', '--no-rpath'] -yes -Finding libs for WXADV : 22:14:43 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'adv,core,net', '--no-rpath'] -yes -Finding libs for WXSTC : 22:14:44 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'stc,core,net', '--no-rpath'] -yes -Finding libs for WXHTML : 22:14:44 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'html,core,net', '--no-rpath'] -yes -WARNING: Unable to find setup.h in /home/oleksis/Phoenix/build/wxbld/gtk3/lib64/wx/include/gtk3-unicode-3.1, assuming wxUSE_GLCANVAS is not available. -Finding libs for WXGL : 22:14:44 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'core,net', '--no-rpath'] -yes -WARNING: Unable to find setup.h in /home/oleksis/Phoenix/build/wxbld/gtk3/lib64/wx/include/gtk3-unicode-3.1, assuming wxUSE_WEBVIEW is not available. -Finding libs for WXWEBVIEW : 22:14:45 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'core,net', '--no-rpath'] -yes -Finding libs for WXXML : 22:14:45 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'xml,core,net', '--no-rpath'] -yes -Finding libs for WXXRC : 22:14:45 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'xrc,xml,core,net', '--no-rpath'] -yes -Finding libs for WXRICHTEXT : 22:14:45 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'richtext,core,net', '--no-rpath'] -yes -WARNING: Unable to find setup.h in /home/oleksis/Phoenix/build/wxbld/gtk3/lib64/wx/include/gtk3-unicode-3.1, assuming wxUSE_MEDIACTRL is not available. -Finding libs for WXMEDIA : 22:14:46 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'core,net', '--no-rpath'] -yes -Finding libs for WXRIBBON : 22:14:46 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'ribbon,core,net', '--no-rpath'] -yes -Finding libs for WXPROPGRID : 22:14:46 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'propgrid,core', '--no-rpath'] -yes -Finding libs for WXAUI : 22:14:47 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'aui,core', '--no-rpath'] -yes -'configure' finished successfully (11.443s) -Waf: Entering directory `/home/oleksis/Phoenix/build/waf/3.6/gtk3' -**** Compiler: /usr/bin/gcc --version - gcc (SUSE Linux) 7.3.1 20180323 [gcc-7-branch revision 258812] - Copyright (C) 2017 Free Software Foundation, Inc. - This is free software; see the source for copying conditions. There is NO - warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -Erroneous order constraint after='siplib' on bld(source='siplib.cpython-36m-x86_64-linux-gnu.so', target='pkg.siplib.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=2, tg_idx_count=2, rule=, after='siplib', posted=True) in /home/oleksis/Phoenix (no such class) -Erroneous order constraint after='_core' on bld(source='_core.cpython-36m-x86_64-linux-gnu.so', target='pkg._core.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=4, tg_idx_count=4, rule=, after='_core', posted=True) in /home/oleksis/Phoenix (no such class) -Erroneous order constraint after='_adv' on bld(source='_adv.cpython-36m-x86_64-linux-gnu.so', target='pkg._adv.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=6, tg_idx_count=6, rule=, after='_adv', posted=True) in /home/oleksis/Phoenix (no such class) -Erroneous order constraint after='_dataview' on bld(source='_dataview.cpython-36m-x86_64-linux-gnu.so', target='pkg._dataview.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=8, tg_idx_count=8, rule=, after='_dataview', posted=True) in /home/oleksis/Phoenix (no such class) -Erroneous order constraint after='_grid' on bld(source='_grid.cpython-36m-x86_64-linux-gnu.so', target='pkg._grid.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=10, tg_idx_count=10, rule=, after='_grid', posted=True) in /home/oleksis/Phoenix (no such class) -Erroneous order constraint after='_stc' on bld(source='_stc.cpython-36m-x86_64-linux-gnu.so', target='pkg._stc.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=12, tg_idx_count=12, rule=, after='_stc', posted=True) in /home/oleksis/Phoenix (no such class) -Erroneous order constraint after='_html' on bld(source='_html.cpython-36m-x86_64-linux-gnu.so', target='pkg._html.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=14, tg_idx_count=14, rule=, after='_html', posted=True) in /home/oleksis/Phoenix (no such class) -Erroneous order constraint after='_glcanvas' on bld(source='_glcanvas.cpython-36m-x86_64-linux-gnu.so', target='pkg._glcanvas.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=16, tg_idx_count=16, rule=, after='_glcanvas', posted=True) in /home/oleksis/Phoenix (no such class) -Erroneous order constraint after='_html2' on bld(source='_html2.cpython-36m-x86_64-linux-gnu.so', target='pkg._html2.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=18, tg_idx_count=18, rule=, after='_html2', posted=True) in /home/oleksis/Phoenix (no such class) -Erroneous order constraint after='_xml' on bld(source='_xml.cpython-36m-x86_64-linux-gnu.so', target='pkg._xml.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=20, tg_idx_count=20, rule=, after='_xml', posted=True) in /home/oleksis/Phoenix (no such class) -Erroneous order constraint after='_xrc' on bld(source='_xrc.cpython-36m-x86_64-linux-gnu.so', target='pkg._xrc.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=22, tg_idx_count=22, rule=, after='_xrc', posted=True) in /home/oleksis/Phoenix (no such class) -Erroneous order constraint after='_richtext' on bld(source='_richtext.cpython-36m-x86_64-linux-gnu.so', target='pkg._richtext.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=24, tg_idx_count=24, rule=, after='_richtext', posted=True) in /home/oleksis/Phoenix (no such class) -Erroneous order constraint after='_media' on bld(source='_media.cpython-36m-x86_64-linux-gnu.so', target='pkg._media.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=26, tg_idx_count=26, rule=, after='_media', posted=True) in /home/oleksis/Phoenix (no such class) -Erroneous order constraint after='_ribbon' on bld(source='_ribbon.cpython-36m-x86_64-linux-gnu.so', target='pkg._ribbon.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=28, tg_idx_count=28, rule=, after='_ribbon', posted=True) in /home/oleksis/Phoenix (no such class) -Erroneous order constraint after='_propgrid' on bld(source='_propgrid.cpython-36m-x86_64-linux-gnu.so', target='pkg._propgrid.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=30, tg_idx_count=30, rule=, after='_propgrid', posted=True) in /home/oleksis/Phoenix (no such class) -Erroneous order constraint after='_aui' on bld(source='_aui.cpython-36m-x86_64-linux-gnu.so', target='pkg._aui.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=32, tg_idx_count=32, rule=, after='_aui', posted=True) in /home/oleksis/Phoenix (no such class) -[ 11/918] Linking build/waf/3.6/gtk3/siplib.cpython-36m-x86_64-linux-gnu.so -22:14:50 runner ['/usr/bin/g++', '-shared', '-shared', '-pthread', '-pthread', '-Xlinker', '-export-dynamic', 'sip/siplib/apiversions.c.1.o', 'sip/siplib/array.c.1.o', 'sip/siplib/bool.cpp.1.o', 'sip/siplib/descriptors.c.1.o', 'sip/siplib/int_convertors.c.1.o', 'sip/siplib/objmap.c.1.o', 'sip/siplib/qtlib.c.1.o', 'sip/siplib/siplib.c.1.o', 'sip/siplib/threads.c.1.o', 'sip/siplib/voidptr.c.1.o', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/siplib.cpython-36m-x86_64-linux-gnu.so', '-Wl,-Bstatic', '-Wl,-Bdynamic', '-L/home/oleksis/Phoenix/build/wxbld/gtk3/lib64', '-lwx_gtk3u_core-3.1', '-lwx_baseu_net-3.1', '-lwx_baseu-3.1'] -/usr/lib64/gcc/x86_64-suse-linux/7/../../../../x86_64-suse-linux/bin/ld: no se puede encontrar -lwx_gtk3u_core-3.1 -/usr/lib64/gcc/x86_64-suse-linux/7/../../../../x86_64-suse-linux/bin/ld: no se puede encontrar -lwx_baseu_net-3.1 -/usr/lib64/gcc/x86_64-suse-linux/7/../../../../x86_64-suse-linux/bin/ld: no se puede encontrar -lwx_baseu-3.1 -collect2: error: ld returned 1 exit status - -Waf: Leaving directory `/home/oleksis/Phoenix/build/waf/3.6/gtk3' - File "/home/oleksis/Phoenix/bin/waf-2.0.19", line 168, in - Scripting.waf_entry_point(cwd, VERSION, wafdir) - File "/home/oleksis/Phoenix/bin/.waf3-2.0.19-1f3c580272b15a03d2566843c5fe872a/waflib/Scripting.py", line 119, in waf_entry_point - run_commands() - File "/home/oleksis/Phoenix/bin/.waf3-2.0.19-1f3c580272b15a03d2566843c5fe872a/waflib/Scripting.py", line 182, in run_commands - ctx=run_command(cmd_name) - File "/home/oleksis/Phoenix/bin/.waf3-2.0.19-1f3c580272b15a03d2566843c5fe872a/waflib/Scripting.py", line 173, in run_command - ctx.execute() - File "/home/oleksis/Phoenix/bin/.waf3-2.0.19-1f3c580272b15a03d2566843c5fe872a/waflib/Scripting.py", line 375, in execute - return execute_method(self) - File "/home/oleksis/Phoenix/bin/.waf3-2.0.19-1f3c580272b15a03d2566843c5fe872a/waflib/Build.py", line 93, in execute - self.execute_build() - File "/home/oleksis/Phoenix/bin/.waf3-2.0.19-1f3c580272b15a03d2566843c5fe872a/waflib/Build.py", line 100, in execute_build - self.compile() - File "/home/oleksis/Phoenix/bin/.waf3-2.0.19-1f3c580272b15a03d2566843c5fe872a/waflib/Tools/errcheck.py", line 140, in check_compile - ret=self.orig_compile() - File "/home/oleksis/Phoenix/bin/.waf3-2.0.19-1f3c580272b15a03d2566843c5fe872a/waflib/Build.py", line 176, in compile - raise Errors.BuildError(self.producer.error) - File "/home/oleksis/Phoenix/bin/.waf3-2.0.19-1f3c580272b15a03d2566843c5fe872a/waflib/Errors.py", line 26, in __init__ - WafError.__init__(self,self.format_error()) - -Build failed - -> task in 'siplib' failed with exit status 1: - {task 140433775912328: cxxshlib apiversions.c.1.o,array.c.1.o,bool.cpp.1.o,descriptors.c.1.o,int_convertors.c.1.o,objmap.c.1.o,qtlib.c.1.o,siplib.c.1.o,threads.c.1.o,voidptr.c.1.o -> siplib.cpython-36m-x86_64-linux-gnu.so} -['/usr/bin/g++', '-shared', '-shared', '-pthread', '-pthread', '-Xlinker', '-export-dynamic', 'sip/siplib/apiversions.c.1.o', 'sip/siplib/array.c.1.o', 'sip/siplib/bool.cpp.1.o', 'sip/siplib/descriptors.c.1.o', 'sip/siplib/int_convertors.c.1.o', 'sip/siplib/objmap.c.1.o', 'sip/siplib/qtlib.c.1.o', 'sip/siplib/siplib.c.1.o', 'sip/siplib/threads.c.1.o', 'sip/siplib/voidptr.c.1.o', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/siplib.cpython-36m-x86_64-linux-gnu.so', '-Wl,-Bstatic', '-Wl,-Bdynamic', '-L/home/oleksis/Phoenix/build/wxbld/gtk3/lib64', '-lwx_gtk3u_core-3.1', '-lwx_baseu_net-3.1', '-lwx_baseu-3.1'] -Command '"/home/oleksis/Phoenix/env/bin/python" /home/oleksis/Phoenix/bin/waf-2.0.19 --verbose --wx_config=/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config --verbose --gtk3 --python="/home/oleksis/Phoenix/env/bin/python" --out=build/waf/3.6/gtk3 configure build ' failed with exit code 1. -Finished command: build_py (0m25.717s) - - -==== SIN ERROR MODIFICANDO GTK3-UNICODE-3.1 (./Phoenix/build/wxbld/gtk3/lib/wx/config/gtk3-unicode-3.1) ==== -(env) oleksis@linux-fkos:~/Phoenix> python build.py build_py -v -Will build using: "/home/oleksis/Phoenix/env/bin/python" -3.6.5 (default, Mar 31 2018, 19:45:04) [GCC] -Python's architecture is 64bit -cfg.VERSION: 4.1.1a1 - -Running command: build_py -Checking for /home/oleksis/Phoenix/bin/waf-2.0.19... -"/home/oleksis/Phoenix/env/bin/python" /home/oleksis/Phoenix/bin/waf-2.0.19 --verbose --wx_config=/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config --verbose --gtk3 --python="/home/oleksis/Phoenix/env/bin/python" --out=build/waf/3.6/gtk3 configure build -Setting top to : /home/oleksis/Phoenix -Setting out to : /home/oleksis/Phoenix/build/waf/3.6/gtk3 -Checking for 'gcc' (C compiler) : 00:27:25 runner ['/usr/bin/gcc', '-dM', '-E', '-'] -/usr/bin/gcc -Checking for 'g++' (C++ compiler) : 00:27:26 runner ['/usr/bin/g++', '-dM', '-E', '-'] -/usr/bin/g++ -Checking for program 'python' : /home/oleksis/Phoenix/env/bin/python -00:27:27 runner ['/home/oleksis/Phoenix/env/bin/python', '-c', 'import sys\ntry:\n print(sys.implementation.cache_tag)\nexcept AttributeError:\n import imp\n print(imp.get_tag())\n'] -00:27:27 runner ['/home/oleksis/Phoenix/env/bin/python', '-c', 'import sys\nfor x in sys.version_info: print(str(x))'] -00:27:28 runner ['/home/oleksis/Phoenix/env/bin/python', '-c', "from distutils.sysconfig import get_config_var, get_python_lib\n\nprint(repr(get_python_lib(standard_lib=0, prefix='/usr/local') or ''))"] -00:27:28 runner ['/home/oleksis/Phoenix/env/bin/python', '-c', "from distutils.sysconfig import get_config_var, get_python_lib\n\nprint(repr(get_python_lib(plat_specific=1, standard_lib=0, prefix='/usr/local') or ''))"] -Checking for python version >= 2.7.0 : 3.6.5 -00:27:29 runner ['/home/oleksis/Phoenix/env/bin/python', '-c', "from distutils.sysconfig import get_config_var, get_python_lib\n\nprint(repr(get_config_var('prefix') or ''))\nprint(repr(get_config_var('SO') or ''))\nprint(repr(get_config_var('LDFLAGS') or ''))\nprint(repr(get_config_var('LIBDIR') or ''))\nprint(repr(get_config_var('LIBPL') or ''))\nprint(repr(get_config_var('INCLUDEPY') or ''))\nprint(repr(get_config_var('Py_ENABLE_SHARED') or ''))\nprint(repr(get_config_var('MACOSX_DEPLOYMENT_TARGET') or ''))\nprint(repr(get_config_var('LDSHARED') or ''))\nprint(repr(get_config_var('CFLAGS') or ''))\nprint(repr(get_config_var('LDVERSION') or ''))"] -python-config : /usr/bin/python3.6-config -Asking python-config for pyext '--cflags --libs --ldflags' flags : 00:27:29 runner ['/usr/bin/python3.6-config', '--cflags', '--libs', '--ldflags'] -yes -Testing pyext configuration : 00:27:29 runner ['/usr/bin/g++', '-fPIC', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-I/usr/include/python3.6m', '-DPYTHONDIR="/usr/local/lib/python3.6/site-packages"', '-DPYTHONARCHDIR="/usr/local/lib64/python3.6/site-packages"', '-D_FORTIFY_SOURCE=2', '-DNDEBUG', '-D_FORTIFY_SOURCE=2', '-DOPENSSL_LOAD_CONF', '../test.cpp', '-c', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/.conf_check_eaab107a0ad0c09fe99b0e743a890f00/testbuild/test.cpp.1.o'] -00:27:31 runner ['/usr/bin/g++', '-shared', '-Xlinker', '-export-dynamic', 'test.cpp.1.o', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/.conf_check_eaab107a0ad0c09fe99b0e743a890f00/testbuild/testprog.cpython-36m-x86_64-linux-gnu.so', '-Wl,-Bstatic', '-Wl,-Bdynamic', '-L/usr/lib64', '-lpython3.6m', '-lpthread', '-ldl', '-lutil', '-lm', '-lpython3.6m', '-lpthread', '-ldl', '-lutil', '-lm'] -yes -Finding libs for WX : 00:27:33 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'core,net', '--no-rpath'] -yes -Finding libs for WXADV : 00:27:33 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'adv,core,net', '--no-rpath'] -yes -Finding libs for WXSTC : 00:27:33 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'stc,core,net', '--no-rpath'] -yes -Finding libs for WXHTML : 00:27:34 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'html,core,net', '--no-rpath'] -yes -Finding libs for WXGL : 00:27:34 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'gl,core,net', '--no-rpath'] -yes -Finding libs for WXWEBVIEW : 00:27:34 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'webview,core,net', '--no-rpath'] -yes -Finding libs for WXXML : 00:27:35 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'xml,core,net', '--no-rpath'] -yes -Finding libs for WXXRC : 00:27:35 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'xrc,xml,core,net', '--no-rpath'] -yes -Finding libs for WXRICHTEXT : 00:27:35 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'richtext,core,net', '--no-rpath'] -yes -Finding libs for WXMEDIA : 00:27:35 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'media,core,net', '--no-rpath'] -yes -Finding libs for WXRIBBON : 00:27:36 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'ribbon,core,net', '--no-rpath'] -yes -Finding libs for WXPROPGRID : 00:27:36 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'propgrid,core', '--no-rpath'] -yes -Finding libs for WXAUI : 00:27:36 runner ['/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config', '--cxxflags', '--libs', 'aui,core', '--no-rpath'] -yes -'configure' finished successfully (11.528s) -Waf: Entering directory `/home/oleksis/Phoenix/build/waf/3.6/gtk3' -**** Compiler: /usr/bin/gcc --version - gcc (SUSE Linux) 7.3.1 20180323 [gcc-7-branch revision 258812] - Copyright (C) 2017 Free Software Foundation, Inc. - This is free software; see the source for copying conditions. There is NO - warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -Erroneous order constraint after='siplib' on bld(source='siplib.cpython-36m-x86_64-linux-gnu.so', target='pkg.siplib.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=2, tg_idx_count=2, rule=, after='siplib', posted=True) in /home/oleksis/Phoenix (no such class) -Erroneous order constraint after='_core' on bld(source='_core.cpython-36m-x86_64-linux-gnu.so', target='pkg._core.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=4, tg_idx_count=4, rule=, after='_core', posted=True) in /home/oleksis/Phoenix (no such class) -Erroneous order constraint after='_adv' on bld(source='_adv.cpython-36m-x86_64-linux-gnu.so', target='pkg._adv.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=6, tg_idx_count=6, rule=, after='_adv', posted=True) in /home/oleksis/Phoenix (no such class) -Erroneous order constraint after='_dataview' on bld(source='_dataview.cpython-36m-x86_64-linux-gnu.so', target='pkg._dataview.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=8, tg_idx_count=8, rule=, after='_dataview', posted=True) in /home/oleksis/Phoenix (no such class) -Erroneous order constraint after='_grid' on bld(source='_grid.cpython-36m-x86_64-linux-gnu.so', target='pkg._grid.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=10, tg_idx_count=10, rule=, after='_grid', posted=True) in /home/oleksis/Phoenix (no such class) -Erroneous order constraint after='_stc' on bld(source='_stc.cpython-36m-x86_64-linux-gnu.so', target='pkg._stc.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=12, tg_idx_count=12, rule=, after='_stc', posted=True) in /home/oleksis/Phoenix (no such class) -Erroneous order constraint after='_html' on bld(source='_html.cpython-36m-x86_64-linux-gnu.so', target='pkg._html.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=14, tg_idx_count=14, rule=, after='_html', posted=True) in /home/oleksis/Phoenix (no such class) -Erroneous order constraint after='_glcanvas' on bld(source='_glcanvas.cpython-36m-x86_64-linux-gnu.so', target='pkg._glcanvas.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=16, tg_idx_count=16, rule=, after='_glcanvas', posted=True) in /home/oleksis/Phoenix (no such class) -Erroneous order constraint after='_html2' on bld(source='_html2.cpython-36m-x86_64-linux-gnu.so', target='pkg._html2.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=18, tg_idx_count=18, rule=, after='_html2', posted=True) in /home/oleksis/Phoenix (no such class) -Erroneous order constraint after='_xml' on bld(source='_xml.cpython-36m-x86_64-linux-gnu.so', target='pkg._xml.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=20, tg_idx_count=20, rule=, after='_xml', posted=True) in /home/oleksis/Phoenix (no such class) -Erroneous order constraint after='_xrc' on bld(source='_xrc.cpython-36m-x86_64-linux-gnu.so', target='pkg._xrc.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=22, tg_idx_count=22, rule=, after='_xrc', posted=True) in /home/oleksis/Phoenix (no such class) -Erroneous order constraint after='_richtext' on bld(source='_richtext.cpython-36m-x86_64-linux-gnu.so', target='pkg._richtext.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=24, tg_idx_count=24, rule=, after='_richtext', posted=True) in /home/oleksis/Phoenix (no such class) -Erroneous order constraint after='_media' on bld(source='_media.cpython-36m-x86_64-linux-gnu.so', target='pkg._media.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=26, tg_idx_count=26, rule=, after='_media', posted=True) in /home/oleksis/Phoenix (no such class) -Erroneous order constraint after='_ribbon' on bld(source='_ribbon.cpython-36m-x86_64-linux-gnu.so', target='pkg._ribbon.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=28, tg_idx_count=28, rule=, after='_ribbon', posted=True) in /home/oleksis/Phoenix (no such class) -Erroneous order constraint after='_propgrid' on bld(source='_propgrid.cpython-36m-x86_64-linux-gnu.so', target='pkg._propgrid.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=30, tg_idx_count=30, rule=, after='_propgrid', posted=True) in /home/oleksis/Phoenix (no such class) -Erroneous order constraint after='_aui' on bld(source='_aui.cpython-36m-x86_64-linux-gnu.so', target='pkg._aui.cpython-36m-x86_64-linux-gnu', meths=['check_err_features', 'check_err_order', 'process_rule', 'process_source'], features=[], path=/home/oleksis/Phoenix, idx=32, tg_idx_count=32, rule=, after='_aui', posted=True) in /home/oleksis/Phoenix (no such class) -[ 1/918] Compiling sip/siplib/qtlib.c -00:27:39 runner ['/usr/bin/gcc', '-fPIC', '-pthread', '-pthread', '-pthread', '-I/usr/include/gtk-3.0', '-I/usr/include/at-spi2-atk/2.0', '-I/usr/include/at-spi-2.0', '-I/usr/include/dbus-1.0', '-I/usr/lib64/dbus-1.0/include', '-I/usr/include/gtk-3.0', '-I/usr/include/gio-unix-2.0/', '-I/usr/include/libxkbcommon', '-I/usr/include/wayland', '-I/usr/include/cairo', '-I/usr/include/pango-1.0', '-I/usr/include/harfbuzz', '-I/usr/include/pango-1.0', '-I/usr/include/atk-1.0', '-I/usr/include/cairo', '-I/usr/include/pixman-1', '-I/usr/include/freetype2', '-I/usr/include/libdrm', '-I/usr/include/libpng16', '-I/usr/include/gdk-pixbuf-2.0', '-I/usr/include/glib-2.0', '-I/usr/lib64/glib-2.0/include', '-UNDEBUG', '-DSIP_MODULE_NAME=wx.siplib', '-DSIP_MODULE_BASENAME=siplib', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-I../../../wxbld/gtk3/lib/wx/include/gtk3-unicode-3.1', '-I../../../../ext/wxWidgets/include', '-Isip/siplib', '-I../../../../sip/siplib', '-Iwx/include', '-I../../../../wx/include', '-Isrc', '-I../../../../src', '-I/usr/include/python3.6m', '-DPYTHONDIR="/usr/local/lib/python3.6/site-packages"', '-DPYTHONARCHDIR="/usr/local/lib64/python3.6/site-packages"', '-DHAVE_PYEXT=1', '-DHAVE_PYTHON_H=1', '-DHAVE_WX=1', '-DHAVE_WXADV=1', '-DHAVE_WXSTC=1', '-DHAVE_WXHTML=1', '-DHAVE_WXGL=1', '-DHAVE_WXWEBVIEW=1', '-DHAVE_WXXML=1', '-DHAVE_WXXRC=1', '-DHAVE_WXRICHTEXT=1', '-DHAVE_WXMEDIA=1', '-DHAVE_WXRIBBON=1', '-DHAVE_WXPROPGRID=1', '-DHAVE_WXAUI=1', '-D_FILE_OFFSET_BITS=64', '-D__WXGTK__', '-D_FORTIFY_SOURCE=2', '-D_FORTIFY_SOURCE=2', '-DOPENSSL_LOAD_CONF', '../../../../sip/siplib/qtlib.c', '-c', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/sip/siplib/qtlib.c.1.o'] -[ 2/918] Compiling sip/siplib/bool.cpp -00:27:39 runner ['/usr/bin/g++', '-fPIC', '-pthread', '-pthread', '-pthread', '-I/usr/include/gtk-3.0', '-I/usr/include/at-spi2-atk/2.0', '-I/usr/include/at-spi-2.0', '-I/usr/include/dbus-1.0', '-I/usr/lib64/dbus-1.0/include', '-I/usr/include/gtk-3.0', '-I/usr/include/gio-unix-2.0/', '-I/usr/include/libxkbcommon', '-I/usr/include/wayland', '-I/usr/include/cairo', '-I/usr/include/pango-1.0', '-I/usr/include/harfbuzz', '-I/usr/include/pango-1.0', '-I/usr/include/atk-1.0', '-I/usr/include/cairo', '-I/usr/include/pixman-1', '-I/usr/include/freetype2', '-I/usr/include/libdrm', '-I/usr/include/libpng16', '-I/usr/include/gdk-pixbuf-2.0', '-I/usr/include/glib-2.0', '-I/usr/lib64/glib-2.0/include', '-UNDEBUG', '-DSIP_MODULE_NAME=wx.siplib', '-DSIP_MODULE_BASENAME=siplib', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-I../../../wxbld/gtk3/lib/wx/include/gtk3-unicode-3.1', '-I../../../../ext/wxWidgets/include', '-Isip/siplib', '-I../../../../sip/siplib', '-Iwx/include', '-I../../../../wx/include', '-Isrc', '-I../../../../src', '-I/usr/include/python3.6m', '-DPYTHONDIR="/usr/local/lib/python3.6/site-packages"', '-DPYTHONARCHDIR="/usr/local/lib64/python3.6/site-packages"', '-DHAVE_PYEXT=1', '-DHAVE_PYTHON_H=1', '-DHAVE_WX=1', '-DHAVE_WXADV=1', '-DHAVE_WXSTC=1', '-DHAVE_WXHTML=1', '-DHAVE_WXGL=1', '-DHAVE_WXWEBVIEW=1', '-DHAVE_WXXML=1', '-DHAVE_WXXRC=1', '-DHAVE_WXRICHTEXT=1', '-DHAVE_WXMEDIA=1', '-DHAVE_WXRIBBON=1', '-DHAVE_WXPROPGRID=1', '-DHAVE_WXAUI=1', '-D_FILE_OFFSET_BITS=64', '-D__WXGTK__', '-D_FORTIFY_SOURCE=2', '-D_FORTIFY_SOURCE=2', '-DOPENSSL_LOAD_CONF', '../../../../sip/siplib/bool.cpp', '-c', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/sip/siplib/bool.cpp.1.o'] -[ 3/918] Compiling sip/siplib/apiversions.c -00:27:40 runner ['/usr/bin/gcc', '-fPIC', '-pthread', '-pthread', '-pthread', '-I/usr/include/gtk-3.0', '-I/usr/include/at-spi2-atk/2.0', '-I/usr/include/at-spi-2.0', '-I/usr/include/dbus-1.0', '-I/usr/lib64/dbus-1.0/include', '-I/usr/include/gtk-3.0', '-I/usr/include/gio-unix-2.0/', '-I/usr/include/libxkbcommon', '-I/usr/include/wayland', '-I/usr/include/cairo', '-I/usr/include/pango-1.0', '-I/usr/include/harfbuzz', '-I/usr/include/pango-1.0', '-I/usr/include/atk-1.0', '-I/usr/include/cairo', '-I/usr/include/pixman-1', '-I/usr/include/freetype2', '-I/usr/include/libdrm', '-I/usr/include/libpng16', '-I/usr/include/gdk-pixbuf-2.0', '-I/usr/include/glib-2.0', '-I/usr/lib64/glib-2.0/include', '-UNDEBUG', '-DSIP_MODULE_NAME=wx.siplib', '-DSIP_MODULE_BASENAME=siplib', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-I../../../wxbld/gtk3/lib/wx/include/gtk3-unicode-3.1', '-I../../../../ext/wxWidgets/include', '-Isip/siplib', '-I../../../../sip/siplib', '-Iwx/include', '-I../../../../wx/include', '-Isrc', '-I../../../../src', '-I/usr/include/python3.6m', '-DPYTHONDIR="/usr/local/lib/python3.6/site-packages"', '-DPYTHONARCHDIR="/usr/local/lib64/python3.6/site-packages"', '-DHAVE_PYEXT=1', '-DHAVE_PYTHON_H=1', '-DHAVE_WX=1', '-DHAVE_WXADV=1', '-DHAVE_WXSTC=1', '-DHAVE_WXHTML=1', '-DHAVE_WXGL=1', '-DHAVE_WXWEBVIEW=1', '-DHAVE_WXXML=1', '-DHAVE_WXXRC=1', '-DHAVE_WXRICHTEXT=1', '-DHAVE_WXMEDIA=1', '-DHAVE_WXRIBBON=1', '-DHAVE_WXPROPGRID=1', '-DHAVE_WXAUI=1', '-D_FILE_OFFSET_BITS=64', '-D__WXGTK__', '-D_FORTIFY_SOURCE=2', '-D_FORTIFY_SOURCE=2', '-DOPENSSL_LOAD_CONF', '../../../../sip/siplib/apiversions.c', '-c', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/sip/siplib/apiversions.c.1.o'] -[ 4/918] Compiling sip/siplib/objmap.c -00:27:42 runner ['/usr/bin/gcc', '-fPIC', '-pthread', '-pthread', '-pthread', '-I/usr/include/gtk-3.0', '-I/usr/include/at-spi2-atk/2.0', '-I/usr/include/at-spi-2.0', '-I/usr/include/dbus-1.0', '-I/usr/lib64/dbus-1.0/include', '-I/usr/include/gtk-3.0', '-I/usr/include/gio-unix-2.0/', '-I/usr/include/libxkbcommon', '-I/usr/include/wayland', '-I/usr/include/cairo', '-I/usr/include/pango-1.0', '-I/usr/include/harfbuzz', '-I/usr/include/pango-1.0', '-I/usr/include/atk-1.0', '-I/usr/include/cairo', '-I/usr/include/pixman-1', '-I/usr/include/freetype2', '-I/usr/include/libdrm', '-I/usr/include/libpng16', '-I/usr/include/gdk-pixbuf-2.0', '-I/usr/include/glib-2.0', '-I/usr/lib64/glib-2.0/include', '-UNDEBUG', '-DSIP_MODULE_NAME=wx.siplib', '-DSIP_MODULE_BASENAME=siplib', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-I../../../wxbld/gtk3/lib/wx/include/gtk3-unicode-3.1', '-I../../../../ext/wxWidgets/include', '-Isip/siplib', '-I../../../../sip/siplib', '-Iwx/include', '-I../../../../wx/include', '-Isrc', '-I../../../../src', '-I/usr/include/python3.6m', '-DPYTHONDIR="/usr/local/lib/python3.6/site-packages"', '-DPYTHONARCHDIR="/usr/local/lib64/python3.6/site-packages"', '-DHAVE_PYEXT=1', '-DHAVE_PYTHON_H=1', '-DHAVE_WX=1', '-DHAVE_WXADV=1', '-DHAVE_WXSTC=1', '-DHAVE_WXHTML=1', '-DHAVE_WXGL=1', '-DHAVE_WXWEBVIEW=1', '-DHAVE_WXXML=1', '-DHAVE_WXXRC=1', '-DHAVE_WXRICHTEXT=1', '-DHAVE_WXMEDIA=1', '-DHAVE_WXRIBBON=1', '-DHAVE_WXPROPGRID=1', '-DHAVE_WXAUI=1', '-D_FILE_OFFSET_BITS=64', '-D__WXGTK__', '-D_FORTIFY_SOURCE=2', '-D_FORTIFY_SOURCE=2', '-DOPENSSL_LOAD_CONF', '../../../../sip/siplib/objmap.c', '-c', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/sip/siplib/objmap.c.1.o'] -[ 5/918] Compiling sip/siplib/voidptr.c -00:27:43 runner ['/usr/bin/gcc', '-fPIC', '-pthread', '-pthread', '-pthread', '-I/usr/include/gtk-3.0', '-I/usr/include/at-spi2-atk/2.0', '-I/usr/include/at-spi-2.0', '-I/usr/include/dbus-1.0', '-I/usr/lib64/dbus-1.0/include', '-I/usr/include/gtk-3.0', '-I/usr/include/gio-unix-2.0/', '-I/usr/include/libxkbcommon', '-I/usr/include/wayland', '-I/usr/include/cairo', '-I/usr/include/pango-1.0', '-I/usr/include/harfbuzz', '-I/usr/include/pango-1.0', '-I/usr/include/atk-1.0', '-I/usr/include/cairo', '-I/usr/include/pixman-1', '-I/usr/include/freetype2', '-I/usr/include/libdrm', '-I/usr/include/libpng16', '-I/usr/include/gdk-pixbuf-2.0', '-I/usr/include/glib-2.0', '-I/usr/lib64/glib-2.0/include', '-UNDEBUG', '-DSIP_MODULE_NAME=wx.siplib', '-DSIP_MODULE_BASENAME=siplib', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-I../../../wxbld/gtk3/lib/wx/include/gtk3-unicode-3.1', '-I../../../../ext/wxWidgets/include', '-Isip/siplib', '-I../../../../sip/siplib', '-Iwx/include', '-I../../../../wx/include', '-Isrc', '-I../../../../src', '-I/usr/include/python3.6m', '-DPYTHONDIR="/usr/local/lib/python3.6/site-packages"', '-DPYTHONARCHDIR="/usr/local/lib64/python3.6/site-packages"', '-DHAVE_PYEXT=1', '-DHAVE_PYTHON_H=1', '-DHAVE_WX=1', '-DHAVE_WXADV=1', '-DHAVE_WXSTC=1', '-DHAVE_WXHTML=1', '-DHAVE_WXGL=1', '-DHAVE_WXWEBVIEW=1', '-DHAVE_WXXML=1', '-DHAVE_WXXRC=1', '-DHAVE_WXRICHTEXT=1', '-DHAVE_WXMEDIA=1', '-DHAVE_WXRIBBON=1', '-DHAVE_WXPROPGRID=1', '-DHAVE_WXAUI=1', '-D_FILE_OFFSET_BITS=64', '-D__WXGTK__', '-D_FORTIFY_SOURCE=2', '-D_FORTIFY_SOURCE=2', '-DOPENSSL_LOAD_CONF', '../../../../sip/siplib/voidptr.c', '-c', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/sip/siplib/voidptr.c.1.o'] -[ 6/918] Compiling sip/siplib/array.c -00:27:45 runner ['/usr/bin/gcc', '-fPIC', '-pthread', '-pthread', '-pthread', '-I/usr/include/gtk-3.0', '-I/usr/include/at-spi2-atk/2.0', '-I/usr/include/at-spi-2.0', '-I/usr/include/dbus-1.0', '-I/usr/lib64/dbus-1.0/include', '-I/usr/include/gtk-3.0', '-I/usr/include/gio-unix-2.0/', '-I/usr/include/libxkbcommon', '-I/usr/include/wayland', '-I/usr/include/cairo', '-I/usr/include/pango-1.0', '-I/usr/include/harfbuzz', '-I/usr/include/pango-1.0', '-I/usr/include/atk-1.0', '-I/usr/include/cairo', '-I/usr/include/pixman-1', '-I/usr/include/freetype2', '-I/usr/include/libdrm', '-I/usr/include/libpng16', '-I/usr/include/gdk-pixbuf-2.0', '-I/usr/include/glib-2.0', '-I/usr/lib64/glib-2.0/include', '-UNDEBUG', '-DSIP_MODULE_NAME=wx.siplib', '-DSIP_MODULE_BASENAME=siplib', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-I../../../wxbld/gtk3/lib/wx/include/gtk3-unicode-3.1', '-I../../../../ext/wxWidgets/include', '-Isip/siplib', '-I../../../../sip/siplib', '-Iwx/include', '-I../../../../wx/include', '-Isrc', '-I../../../../src', '-I/usr/include/python3.6m', '-DPYTHONDIR="/usr/local/lib/python3.6/site-packages"', '-DPYTHONARCHDIR="/usr/local/lib64/python3.6/site-packages"', '-DHAVE_PYEXT=1', '-DHAVE_PYTHON_H=1', '-DHAVE_WX=1', '-DHAVE_WXADV=1', '-DHAVE_WXSTC=1', '-DHAVE_WXHTML=1', '-DHAVE_WXGL=1', '-DHAVE_WXWEBVIEW=1', '-DHAVE_WXXML=1', '-DHAVE_WXXRC=1', '-DHAVE_WXRICHTEXT=1', '-DHAVE_WXMEDIA=1', '-DHAVE_WXRIBBON=1', '-DHAVE_WXPROPGRID=1', '-DHAVE_WXAUI=1', '-D_FILE_OFFSET_BITS=64', '-D__WXGTK__', '-D_FORTIFY_SOURCE=2', '-D_FORTIFY_SOURCE=2', '-DOPENSSL_LOAD_CONF', '../../../../sip/siplib/array.c', '-c', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/sip/siplib/array.c.1.o'] -[ 7/918] Compiling sip/siplib/descriptors.c -00:27:46 runner ['/usr/bin/gcc', '-fPIC', '-pthread', '-pthread', '-pthread', '-I/usr/include/gtk-3.0', '-I/usr/include/at-spi2-atk/2.0', '-I/usr/include/at-spi-2.0', '-I/usr/include/dbus-1.0', '-I/usr/lib64/dbus-1.0/include', '-I/usr/include/gtk-3.0', '-I/usr/include/gio-unix-2.0/', '-I/usr/include/libxkbcommon', '-I/usr/include/wayland', '-I/usr/include/cairo', '-I/usr/include/pango-1.0', '-I/usr/include/harfbuzz', '-I/usr/include/pango-1.0', '-I/usr/include/atk-1.0', '-I/usr/include/cairo', '-I/usr/include/pixman-1', '-I/usr/include/freetype2', '-I/usr/include/libdrm', '-I/usr/include/libpng16', '-I/usr/include/gdk-pixbuf-2.0', '-I/usr/include/glib-2.0', '-I/usr/lib64/glib-2.0/include', '-UNDEBUG', '-DSIP_MODULE_NAME=wx.siplib', '-DSIP_MODULE_BASENAME=siplib', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-I../../../wxbld/gtk3/lib/wx/include/gtk3-unicode-3.1', '-I../../../../ext/wxWidgets/include', '-Isip/siplib', '-I../../../../sip/siplib', '-Iwx/include', '-I../../../../wx/include', '-Isrc', '-I../../../../src', '-I/usr/include/python3.6m', '-DPYTHONDIR="/usr/local/lib/python3.6/site-packages"', '-DPYTHONARCHDIR="/usr/local/lib64/python3.6/site-packages"', '-DHAVE_PYEXT=1', '-DHAVE_PYTHON_H=1', '-DHAVE_WX=1', '-DHAVE_WXADV=1', '-DHAVE_WXSTC=1', '-DHAVE_WXHTML=1', '-DHAVE_WXGL=1', '-DHAVE_WXWEBVIEW=1', '-DHAVE_WXXML=1', '-DHAVE_WXXRC=1', '-DHAVE_WXRICHTEXT=1', '-DHAVE_WXMEDIA=1', '-DHAVE_WXRIBBON=1', '-DHAVE_WXPROPGRID=1', '-DHAVE_WXAUI=1', '-D_FILE_OFFSET_BITS=64', '-D__WXGTK__', '-D_FORTIFY_SOURCE=2', '-D_FORTIFY_SOURCE=2', '-DOPENSSL_LOAD_CONF', '../../../../sip/siplib/descriptors.c', '-c', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/sip/siplib/descriptors.c.1.o'] -[ 8/918] Compiling sip/siplib/siplib.c -00:27:49 runner ['/usr/bin/gcc', '-fPIC', '-pthread', '-pthread', '-pthread', '-I/usr/include/gtk-3.0', '-I/usr/include/at-spi2-atk/2.0', '-I/usr/include/at-spi-2.0', '-I/usr/include/dbus-1.0', '-I/usr/lib64/dbus-1.0/include', '-I/usr/include/gtk-3.0', '-I/usr/include/gio-unix-2.0/', '-I/usr/include/libxkbcommon', '-I/usr/include/wayland', '-I/usr/include/cairo', '-I/usr/include/pango-1.0', '-I/usr/include/harfbuzz', '-I/usr/include/pango-1.0', '-I/usr/include/atk-1.0', '-I/usr/include/cairo', '-I/usr/include/pixman-1', '-I/usr/include/freetype2', '-I/usr/include/libdrm', '-I/usr/include/libpng16', '-I/usr/include/gdk-pixbuf-2.0', '-I/usr/include/glib-2.0', '-I/usr/lib64/glib-2.0/include', '-UNDEBUG', '-DSIP_MODULE_NAME=wx.siplib', '-DSIP_MODULE_BASENAME=siplib', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-I../../../wxbld/gtk3/lib/wx/include/gtk3-unicode-3.1', '-I../../../../ext/wxWidgets/include', '-Isip/siplib', '-I../../../../sip/siplib', '-Iwx/include', '-I../../../../wx/include', '-Isrc', '-I../../../../src', '-I/usr/include/python3.6m', '-DPYTHONDIR="/usr/local/lib/python3.6/site-packages"', '-DPYTHONARCHDIR="/usr/local/lib64/python3.6/site-packages"', '-DHAVE_PYEXT=1', '-DHAVE_PYTHON_H=1', '-DHAVE_WX=1', '-DHAVE_WXADV=1', '-DHAVE_WXSTC=1', '-DHAVE_WXHTML=1', '-DHAVE_WXGL=1', '-DHAVE_WXWEBVIEW=1', '-DHAVE_WXXML=1', '-DHAVE_WXXRC=1', '-DHAVE_WXRICHTEXT=1', '-DHAVE_WXMEDIA=1', '-DHAVE_WXRIBBON=1', '-DHAVE_WXPROPGRID=1', '-DHAVE_WXAUI=1', '-D_FILE_OFFSET_BITS=64', '-D__WXGTK__', '-D_FORTIFY_SOURCE=2', '-D_FORTIFY_SOURCE=2', '-DOPENSSL_LOAD_CONF', '../../../../sip/siplib/siplib.c', '-c', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/sip/siplib/siplib.c.1.o'] -[ 9/918] Compiling sip/siplib/int_convertors.c -00:27:49 runner ['/usr/bin/gcc', '-fPIC', '-pthread', '-pthread', '-pthread', '-I/usr/include/gtk-3.0', '-I/usr/include/at-spi2-atk/2.0', '-I/usr/include/at-spi-2.0', '-I/usr/include/dbus-1.0', '-I/usr/lib64/dbus-1.0/include', '-I/usr/include/gtk-3.0', '-I/usr/include/gio-unix-2.0/', '-I/usr/include/libxkbcommon', '-I/usr/include/wayland', '-I/usr/include/cairo', '-I/usr/include/pango-1.0', '-I/usr/include/harfbuzz', '-I/usr/include/pango-1.0', '-I/usr/include/atk-1.0', '-I/usr/include/cairo', '-I/usr/include/pixman-1', '-I/usr/include/freetype2', '-I/usr/include/libdrm', '-I/usr/include/libpng16', '-I/usr/include/gdk-pixbuf-2.0', '-I/usr/include/glib-2.0', '-I/usr/lib64/glib-2.0/include', '-UNDEBUG', '-DSIP_MODULE_NAME=wx.siplib', '-DSIP_MODULE_BASENAME=siplib', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-I../../../wxbld/gtk3/lib/wx/include/gtk3-unicode-3.1', '-I../../../../ext/wxWidgets/include', '-Isip/siplib', '-I../../../../sip/siplib', '-Iwx/include', '-I../../../../wx/include', '-Isrc', '-I../../../../src', '-I/usr/include/python3.6m', '-DPYTHONDIR="/usr/local/lib/python3.6/site-packages"', '-DPYTHONARCHDIR="/usr/local/lib64/python3.6/site-packages"', '-DHAVE_PYEXT=1', '-DHAVE_PYTHON_H=1', '-DHAVE_WX=1', '-DHAVE_WXADV=1', '-DHAVE_WXSTC=1', '-DHAVE_WXHTML=1', '-DHAVE_WXGL=1', '-DHAVE_WXWEBVIEW=1', '-DHAVE_WXXML=1', '-DHAVE_WXXRC=1', '-DHAVE_WXRICHTEXT=1', '-DHAVE_WXMEDIA=1', '-DHAVE_WXRIBBON=1', '-DHAVE_WXPROPGRID=1', '-DHAVE_WXAUI=1', '-D_FILE_OFFSET_BITS=64', '-D__WXGTK__', '-D_FORTIFY_SOURCE=2', '-D_FORTIFY_SOURCE=2', '-DOPENSSL_LOAD_CONF', '../../../../sip/siplib/int_convertors.c', '-c', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/sip/siplib/int_convertors.c.1.o'] -[ 10/918] Compiling sip/siplib/threads.c -00:27:51 runner ['/usr/bin/gcc', '-fPIC', '-pthread', '-pthread', '-pthread', '-I/usr/include/gtk-3.0', '-I/usr/include/at-spi2-atk/2.0', '-I/usr/include/at-spi-2.0', '-I/usr/include/dbus-1.0', '-I/usr/lib64/dbus-1.0/include', '-I/usr/include/gtk-3.0', '-I/usr/include/gio-unix-2.0/', '-I/usr/include/libxkbcommon', '-I/usr/include/wayland', '-I/usr/include/cairo', '-I/usr/include/pango-1.0', '-I/usr/include/harfbuzz', '-I/usr/include/pango-1.0', '-I/usr/include/atk-1.0', '-I/usr/include/cairo', '-I/usr/include/pixman-1', '-I/usr/include/freetype2', '-I/usr/include/libdrm', '-I/usr/include/libpng16', '-I/usr/include/gdk-pixbuf-2.0', '-I/usr/include/glib-2.0', '-I/usr/lib64/glib-2.0/include', '-UNDEBUG', '-DSIP_MODULE_NAME=wx.siplib', '-DSIP_MODULE_BASENAME=siplib', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-I../../../wxbld/gtk3/lib/wx/include/gtk3-unicode-3.1', '-I../../../../ext/wxWidgets/include', '-Isip/siplib', '-I../../../../sip/siplib', '-Iwx/include', '-I../../../../wx/include', '-Isrc', '-I../../../../src', '-I/usr/include/python3.6m', '-DPYTHONDIR="/usr/local/lib/python3.6/site-packages"', '-DPYTHONARCHDIR="/usr/local/lib64/python3.6/site-packages"', '-DHAVE_PYEXT=1', '-DHAVE_PYTHON_H=1', '-DHAVE_WX=1', '-DHAVE_WXADV=1', '-DHAVE_WXSTC=1', '-DHAVE_WXHTML=1', '-DHAVE_WXGL=1', '-DHAVE_WXWEBVIEW=1', '-DHAVE_WXXML=1', '-DHAVE_WXXRC=1', '-DHAVE_WXRICHTEXT=1', '-DHAVE_WXMEDIA=1', '-DHAVE_WXRIBBON=1', '-DHAVE_WXPROPGRID=1', '-DHAVE_WXAUI=1', '-D_FILE_OFFSET_BITS=64', '-D__WXGTK__', '-D_FORTIFY_SOURCE=2', '-D_FORTIFY_SOURCE=2', '-DOPENSSL_LOAD_CONF', '../../../../sip/siplib/threads.c', '-c', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/sip/siplib/threads.c.1.o'] -[ 11/918] Compiling sip/cpp/sip_corewxLogTextCtrl.cpp -00:27:53 runner ['/usr/bin/g++', '-fPIC', '-pthread', '-pthread', '-pthread', '-I/usr/include/gtk-3.0', '-I/usr/include/at-spi2-atk/2.0', '-I/usr/include/at-spi-2.0', '-I/usr/include/dbus-1.0', '-I/usr/lib64/dbus-1.0/include', '-I/usr/include/gtk-3.0', '-I/usr/include/gio-unix-2.0/', '-I/usr/include/libxkbcommon', '-I/usr/include/wayland', '-I/usr/include/cairo', '-I/usr/include/pango-1.0', '-I/usr/include/harfbuzz', '-I/usr/include/pango-1.0', '-I/usr/include/atk-1.0', '-I/usr/include/cairo', '-I/usr/include/pixman-1', '-I/usr/include/freetype2', '-I/usr/include/libdrm', '-I/usr/include/libpng16', '-I/usr/include/gdk-pixbuf-2.0', '-I/usr/include/glib-2.0', '-I/usr/lib64/glib-2.0/include', '-UNDEBUG', '-DSIP_MODULE_NAME=wx.siplib', '-DSIP_MODULE_BASENAME=siplib', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-I../../../wxbld/gtk3/lib/wx/include/gtk3-unicode-3.1', '-I../../../../ext/wxWidgets/include', '-Isip/siplib', '-I../../../../sip/siplib', '-Iwx/include', '-I../../../../wx/include', '-Isrc', '-I../../../../src', '-I/usr/include/python3.6m', '-DPYTHONDIR="/usr/local/lib/python3.6/site-packages"', '-DPYTHONARCHDIR="/usr/local/lib64/python3.6/site-packages"', '-DHAVE_PYEXT=1', '-DHAVE_PYTHON_H=1', '-DHAVE_WX=1', '-DHAVE_WXADV=1', '-DHAVE_WXSTC=1', '-DHAVE_WXHTML=1', '-DHAVE_WXGL=1', '-DHAVE_WXWEBVIEW=1', '-DHAVE_WXXML=1', '-DHAVE_WXXRC=1', '-DHAVE_WXRICHTEXT=1', '-DHAVE_WXMEDIA=1', '-DHAVE_WXRIBBON=1', '-DHAVE_WXPROPGRID=1', '-DHAVE_WXAUI=1', '-D_FILE_OFFSET_BITS=64', '-D__WXGTK__', '-D_FORTIFY_SOURCE=2', '-D_FORTIFY_SOURCE=2', '-DOPENSSL_LOAD_CONF', '../../../../sip/cpp/sip_corewxLogTextCtrl.cpp', '-c', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/sip/cpp/sip_corewxLogTextCtrl.cpp.3.o'] -[ 12/918] Compiling sip/cpp/sip_corewxEvtHandler.cpp -00:28:32 runner ['/usr/bin/g++', '-fPIC', '-pthread', '-pthread', '-pthread', '-I/usr/include/gtk-3.0', '-I/usr/include/at-spi2-atk/2.0', '-I/usr/include/at-spi-2.0', '-I/usr/include/dbus-1.0', '-I/usr/lib64/dbus-1.0/include', '-I/usr/include/gtk-3.0', '-I/usr/include/gio-unix-2.0/', '-I/usr/include/libxkbcommon', '-I/usr/include/wayland', '-I/usr/include/cairo', '-I/usr/include/pango-1.0', '-I/usr/include/harfbuzz', '-I/usr/include/pango-1.0', '-I/usr/include/atk-1.0', '-I/usr/include/cairo', '-I/usr/include/pixman-1', '-I/usr/include/freetype2', '-I/usr/include/libdrm', '-I/usr/include/libpng16', '-I/usr/include/gdk-pixbuf-2.0', '-I/usr/include/glib-2.0', '-I/usr/lib64/glib-2.0/include', '-UNDEBUG', '-DSIP_MODULE_NAME=wx.siplib', '-DSIP_MODULE_BASENAME=siplib', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-I../../../wxbld/gtk3/lib/wx/include/gtk3-unicode-3.1', '-I../../../../ext/wxWidgets/include', '-Isip/siplib', '-I../../../../sip/siplib', '-Iwx/include', '-I../../../../wx/include', '-Isrc', '-I../../../../src', '-I/usr/include/python3.6m', '-DPYTHONDIR="/usr/local/lib/python3.6/site-packages"', '-DPYTHONARCHDIR="/usr/local/lib64/python3.6/site-packages"', '-DHAVE_PYEXT=1', '-DHAVE_PYTHON_H=1', '-DHAVE_WX=1', '-DHAVE_WXADV=1', '-DHAVE_WXSTC=1', '-DHAVE_WXHTML=1', '-DHAVE_WXGL=1', '-DHAVE_WXWEBVIEW=1', '-DHAVE_WXXML=1', '-DHAVE_WXXRC=1', '-DHAVE_WXRICHTEXT=1', '-DHAVE_WXMEDIA=1', '-DHAVE_WXRIBBON=1', '-DHAVE_WXPROPGRID=1', '-DHAVE_WXAUI=1', '-D_FILE_OFFSET_BITS=64', '-D__WXGTK__', '-D_FORTIFY_SOURCE=2', '-D_FORTIFY_SOURCE=2', '-DOPENSSL_LOAD_CONF', '../../../../sip/cpp/sip_corewxEvtHandler.cpp', '-c', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/sip/cpp/sip_corewxEvtHandler.cpp.3.o'] -[ 13/918] Compiling sip/cpp/sip_corewxCollapsiblePaneEvent.cpp -00:28:51 runner ['/usr/bin/g++', '-fPIC', '-pthread', '-pthread', '-pthread', '-I/usr/include/gtk-3.0', '-I/usr/include/at-spi2-atk/2.0', '-I/usr/include/at-spi-2.0', '-I/usr/include/dbus-1.0', '-I/usr/lib64/dbus-1.0/include', '-I/usr/include/gtk-3.0', '-I/usr/include/gio-unix-2.0/', '-I/usr/include/libxkbcommon', '-I/usr/include/wayland', '-I/usr/include/cairo', '-I/usr/include/pango-1.0', '-I/usr/include/harfbuzz', '-I/usr/include/pango-1.0', '-I/usr/include/atk-1.0', '-I/usr/include/cairo', '-I/usr/include/pixman-1', '-I/usr/include/freetype2', '-I/usr/include/libdrm', '-I/usr/include/libpng16', '-I/usr/include/gdk-pixbuf-2.0', '-I/usr/include/glib-2.0', '-I/usr/lib64/glib-2.0/include', '-UNDEBUG', '-DSIP_MODULE_NAME=wx.siplib', '-DSIP_MODULE_BASENAME=siplib', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-I../../../wxbld/gtk3/lib/wx/include/gtk3-unicode-3.1', '-I../../../../ext/wxWidgets/include', '-Isip/siplib', '-I../../../../sip/siplib', '-Iwx/include', '-I../../../../wx/include', '-Isrc', '-I../../../../src', '-I/usr/include/python3.6m', '-DPYTHONDIR="/usr/local/lib/python3.6/site-packages"', '-DPYTHONARCHDIR="/usr/local/lib64/python3.6/site-packages"', '-DHAVE_PYEXT=1', '-DHAVE_PYTHON_H=1', '-DHAVE_WX=1', '-DHAVE_WXADV=1', '-DHAVE_WXSTC=1', '-DHAVE_WXHTML=1', '-DHAVE_WXGL=1', '-DHAVE_WXWEBVIEW=1', '-DHAVE_WXXML=1', '-DHAVE_WXXRC=1', '-DHAVE_WXRICHTEXT=1', '-DHAVE_WXMEDIA=1', '-DHAVE_WXRIBBON=1', '-DHAVE_WXPROPGRID=1', '-DHAVE_WXAUI=1', '-D_FILE_OFFSET_BITS=64', '-D__WXGTK__', '-D_FORTIFY_SOURCE=2', '-D_FORTIFY_SOURCE=2', '-DOPENSSL_LOAD_CONF', '../../../../sip/cpp/sip_corewxCollapsiblePaneEvent.cpp', '-c', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/sip/cpp/sip_corewxCollapsiblePaneEvent.cpp.3.o'] -[ 14/918] Linking build/waf/3.6/gtk3/siplib.cpython-36m-x86_64-linux-gnu.so -00:29:24 runner ['/usr/bin/g++', '-shared', '-shared', '-pthread', '-pthread', '-Xlinker', '-export-dynamic', 'sip/siplib/apiversions.c.1.o', 'sip/siplib/array.c.1.o', 'sip/siplib/bool.cpp.1.o', 'sip/siplib/descriptors.c.1.o', 'sip/siplib/int_convertors.c.1.o', 'sip/siplib/objmap.c.1.o', 'sip/siplib/qtlib.c.1.o', 'sip/siplib/siplib.c.1.o', 'sip/siplib/threads.c.1.o', 'sip/siplib/voidptr.c.1.o', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/siplib.cpython-36m-x86_64-linux-gnu.so', '-Wl,-Bstatic', '-Wl,-Bdynamic', '-L/home/oleksis/Phoenix/build/wxbld/gtk3/lib', '-lwx_gtk3u_core-3.1', '-lwx_baseu_net-3.1', '-lwx_baseu-3.1'] -[ 15/918] Compiling sip/cpp/sip_corewxAccessible.cpp -00:29:29 runner ['/usr/bin/g++', '-fPIC', '-pthread', '-pthread', '-pthread', '-I/usr/include/gtk-3.0', '-I/usr/include/at-spi2-atk/2.0', '-I/usr/include/at-spi-2.0', '-I/usr/include/dbus-1.0', '-I/usr/lib64/dbus-1.0/include', '-I/usr/include/gtk-3.0', '-I/usr/include/gio-unix-2.0/', '-I/usr/include/libxkbcommon', '-I/usr/include/wayland', '-I/usr/include/cairo', '-I/usr/include/pango-1.0', '-I/usr/include/harfbuzz', '-I/usr/include/pango-1.0', '-I/usr/include/atk-1.0', '-I/usr/include/cairo', '-I/usr/include/pixman-1', '-I/usr/include/freetype2', '-I/usr/include/libdrm', '-I/usr/include/libpng16', '-I/usr/include/gdk-pixbuf-2.0', '-I/usr/include/glib-2.0', '-I/usr/lib64/glib-2.0/include', '-UNDEBUG', '-DSIP_MODULE_NAME=wx.siplib', '-DSIP_MODULE_BASENAME=siplib', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-fmessage-length=0', '-grecord-gcc-switches', '-O2', '-fstack-protector-strong', '-funwind-tables', '-fasynchronous-unwind-tables', '-fstack-clash-protection', '-g', '-I../../../wxbld/gtk3/lib/wx/include/gtk3-unicode-3.1', '-I../../../../ext/wxWidgets/include', '-Isip/siplib', '-I../../../../sip/siplib', '-Iwx/include', '-I../../../../wx/include', '-Isrc', '-I../../../../src', '-I/usr/include/python3.6m', '-DPYTHONDIR="/usr/local/lib/python3.6/site-packages"', '-DPYTHONARCHDIR="/usr/local/lib64/python3.6/site-packages"', '-DHAVE_PYEXT=1', '-DHAVE_PYTHON_H=1', '-DHAVE_WX=1', '-DHAVE_WXADV=1', '-DHAVE_WXSTC=1', '-DHAVE_WXHTML=1', '-DHAVE_WXGL=1', '-DHAVE_WXWEBVIEW=1', '-DHAVE_WXXML=1', '-DHAVE_WXXRC=1', '-DHAVE_WXRICHTEXT=1', '-DHAVE_WXMEDIA=1', '-DHAVE_WXRIBBON=1', '-DHAVE_WXPROPGRID=1', '-DHAVE_WXAUI=1', '-D_FILE_OFFSET_BITS=64', '-D__WXGTK__', '-D_FORTIFY_SOURCE=2', '-D_FORTIFY_SOURCE=2', '-DOPENSSL_LOAD_CONF', '../../../../sip/cpp/sip_corewxAccessible.cpp', '-c', '-o/home/oleksis/Phoenix/build/waf/3.6/gtk3/sip/cpp/sip_corewxAccessible.cpp.3.o'] - - -... MORE OUTPUT ... - - - - - - - +* https://www.wxpython.org/blog/2017-08-17-builds-for-linux-with-pip/index.html +* https://github.com/wxWidgets/Phoenix/blob/master/README.rst#prerequisites +* https://stackoverflow.com/questions/31594104/wxpython-phoenix-source-build-fails-on-build-py-step +Others ====== -(env) oleksis@linux-fkos:~/Phoenix> python build.py build_py -Will build using: "/home/oleksis/Phoenix/env/bin/python" -3.6.5 (default, Mar 31 2018, 19:45:04) [GCC] -Python's architecture is 64bit -cfg.VERSION: 4.1.1a1 +* https://realpython.com/python-gui-with-wxpython/ +* https://blog.wizardsoftheweb.pro/installing-wxpython-on-fedora/amp/ -Running command: build_py -Checking for /home/oleksis/Phoenix/bin/waf-2.0.19... -Not found. Attempting to download... -Connection successful... -Data downloaded... -Checking for /home/oleksis/Phoenix/bin/waf-2.0.19... -"/home/oleksis/Phoenix/env/bin/python" /home/oleksis/Phoenix/bin/waf-2.0.19 --wx_config=/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config --gtk3 --python="/home/oleksis/Phoenix/env/bin/python" --out=build/waf/3.6/gtk3 configure build -Setting top to : /home/oleksis/Phoenix -Setting out to : /home/oleksis/Phoenix/build/waf/3.6/gtk3 -Checking for 'gcc' (C compiler) : /usr/bin/gcc -Checking for 'g++' (C++ compiler) : /usr/bin/g++ -Checking for program 'python' : /home/oleksis/Phoenix/env/bin/python -Checking for python version >= 2.7.0 : 3.6.5 -python-config : /usr/bin/python3.6-config -Asking python-config for pyext '--cflags --libs --ldflags' flags : yes -Testing pyext configuration : yes -Finding libs for WX : yes -Finding libs for WXADV : yes -Finding libs for WXSTC : yes -Finding libs for WXHTML : yes -WARNING: Unable to find setup.h in /home/oleksis/Phoenix/build/wxbld/gtk3/lib64/wx/include/gtk3-unicode-3.1, assuming wxUSE_GLCANVAS is not available. -Finding libs for WXGL : yes -WARNING: Unable to find setup.h in /home/oleksis/Phoenix/build/wxbld/gtk3/lib64/wx/include/gtk3-unicode-3.1, assuming wxUSE_WEBVIEW is not available. -Finding libs for WXWEBVIEW : yes -Finding libs for WXXML : yes -Finding libs for WXXRC : yes -Finding libs for WXRICHTEXT : yes -WARNING: Unable to find setup.h in /home/oleksis/Phoenix/build/wxbld/gtk3/lib64/wx/include/gtk3-unicode-3.1, assuming wxUSE_MEDIACTRL is not available. -Finding libs for WXMEDIA : yes -Finding libs for WXRIBBON : yes -Finding libs for WXPROPGRID : yes -Finding libs for WXAUI : yes -'configure' finished successfully (12.577s) -Waf: Entering directory `/home/oleksis/Phoenix/build/waf/3.6/gtk3' -**** Compiler: /usr/bin/gcc --version - gcc (SUSE Linux) 7.3.1 20180323 [gcc-7-branch revision 258812] - Copyright (C) 2017 Free Software Foundation, Inc. - This is free software; see the source for copying conditions. There is NO - warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -[ 1/918] Compiling sip/siplib/qtlib.c -[ 2/918] Compiling sip/siplib/bool.cpp -[ 3/918] Compiling sip/siplib/apiversions.c -[ 4/918] Compiling sip/siplib/objmap.c -[ 5/918] Compiling sip/siplib/voidptr.c -[ 6/918] Compiling sip/siplib/array.c -[ 7/918] Compiling sip/siplib/siplib.c -[ 8/918] Compiling sip/siplib/descriptors.c -[ 9/918] Compiling sip/siplib/int_convertors.c -[ 10/918] Compiling sip/siplib/threads.c -[ 11/918] Compiling sip/cpp/sip_corewxLogTextCtrl.cpp -In file included from ../../../../ext/wxWidgets/include/wx/defs.h:45:0, - from ../../../../ext/wxWidgets/include/wx/wx.h:14, - from ../../../../wx/include/wxPython/wxpy_api.h:41, - from ../../../../sip/cpp/sipAPI_core.h:20778, - from ../../../../sip/cpp/sip_corewxLogTextCtrl.cpp:10: -../../../../ext/wxWidgets/include/wx/platform.h:160:10: fatal error: wx/setup.h: No existe el fichero o el directorio - #include "wx/setup.h" - ^~~~~~~~~~~~ -compilation terminated. +* GNU/Linux + - wxPython on openSuse Leap 15.0 (glibc-2.26, Python 3.6 and packages) + * https://extras.wxpython.org/wxPython4/extras/linux/gtk3/fedora-27/wxPython-4.0.1-cp36-cp36m-linux_x86_64.whl +* Windows + * https://extras.wxpython.org/wxPython4/extras/4.1.0/ -Waf: Leaving directory `/home/oleksis/Phoenix/build/waf/3.6/gtk3' -Build failed - -> task in '_core' failed with exit status 1 (run with -v to display more information) -Command '"/home/oleksis/Phoenix/env/bin/python" /home/oleksis/Phoenix/bin/waf-2.0.19 --wx_config=/home/oleksis/Phoenix/build/wxbld/gtk3/wx-config --gtk3 --python="/home/oleksis/Phoenix/env/bin/python" --out=build/waf/3.6/gtk3 configure build ' failed with exit code 1. -Finished command: build_py (1m21.708s) +NOTES: Compile wxPython from GitHub source +========================================= +git clone https://github.com/wxWidgets/Phoenix +cd Phoenix +git submodule update --recursive +OR +git clone https://github.com/wxWidgets/wxWidgets.git ./ext/wxWidgets +git clone https://github.com/wxWidgets/Catch.git ./ext/wxWidgets/3rdparty/catch -====== +python3 -m venv env +source env/bin/activate +pip install -r requirements.txt +python build.py dox etg --nodoc sip build - 1743 translated messages, 54 fuzzy translations, 56 untranslated messages. - make[1]: se sale del directorio '/tmp/pip-install-ilickw8p/wxPython/ext/wxWidgets/locale' - make[1]: se entra en el directorio '/tmp/pip-install-ilickw8p/wxPython/ext/wxWidgets/locale' - msgfmt --verbose -c -o zh_TW.mo zh_TW.po - 1710 translated messages, 82 fuzzy translations, 61 untranslated messages. - make[1]: se sale del directorio '/tmp/pip-install-ilickw8p/wxPython/ext/wxWidgets/locale' - Finished command: build_wx (229m10.920s) - Running command: build_py - Checking for /tmp/pip-install-ilickw8p/wxPython/bin/waf-2.0.8... - "/usr/bin/python3" /tmp/pip-install-ilickw8p/wxPython/bin/waf-2.0.8 --wx_config=/tmp/pip-install-ilickw8p/wxPython/build/wxbld/gtk3/wx-config --gtk3 --python="/usr/bin/python3" --out=build/waf/3.6/gtk3 configure build - Setting top to : /tmp/pip-install-ilickw8p/wxPython - Setting out to : /tmp/pip-install-ilickw8p/wxPython/build/waf/3.6/gtk3 - Checking for 'gcc' (C compiler) : /usr/bin/gcc - Checking for 'g++' (C++ compiler) : /usr/bin/g++ - Checking for program 'python' : /usr/bin/python3 - Checking for python version >= 2.7.0 : 3.6.5 - python-config : /usr/bin/python3-config - Asking python-config for pyembed '--cflags --libs --ldflags' flags : yes - Testing pyembed configuration : yes - Asking python-config for pyext '--cflags --libs --ldflags' flags : yes - Testing pyext configuration : yes - Finding libs for WX : yes - Finding libs for WXADV : yes - Finding libs for WXSTC : yes - Finding libs for WXHTML : yes - Finding libs for WXGL : yes - Finding libs for WXWEBVIEW : yes - Finding libs for WXXML : yes - Finding libs for WXXRC : yes - Finding libs for WXRICHTEXT : yes - Finding libs for WXMEDIA : yes - Finding libs for WXRIBBON : yes - Finding libs for WXPROPGRID : yes - Finding libs for WXAUI : yes - 'configure' finished successfully (43.155s) - Waf: Entering directory `/tmp/pip-install-ilickw8p/wxPython/build/waf/3.6/gtk3' - [ 1/869] Compiling sip/siplib/qtlib.c - [ 2/869] Compiling sip/siplib/bool.cpp - [ 3/869] Compiling sip/siplib/apiversions.c - [ 4/869] Compiling sip/siplib/objmap.c - [ 5/869] Compiling sip/siplib/voidptr.c - [ 6/869] Compiling sip/siplib/array.c - [ 7/869] Compiling sip/siplib/siplib.c - [ 8/869] Compiling sip/siplib/descriptors.c - [ 9/869] Compiling sip/siplib/int_convertors.c - [ 10/869] Compiling sip/siplib/threads.c - [ 11/869] Compiling sip/cpp/sip_corewxMaximizeEvent.cpp - In file included from ../../../../ext/wxWidgets/include/wx/defs.h:27:0, - from ../../../../ext/wxWidgets/include/wx/wx.h:14, - from ../../../../wx/include/wxPython/wxpy_api.h:41, - from ../../../../sip/cpp/sipAPI_core.h:19840, - from ../../../../sip/cpp/sip_corewxMaximizeEvent.cpp:10: - ../../../../ext/wxWidgets/include/wx/platform.h:183:10: fatal error: wx/setup.h: No existe el fichero o el directorio - #include "wx/setup.h" - ^~~~~~~~~~~~ - compilation terminated. - - Waf: Leaving directory `/tmp/pip-install-ilickw8p/wxPython/build/waf/3.6/gtk3' - Build failed - -> task in '_core' failed with exit status 1 (run with -v to display more information) - Command '"/usr/bin/python3" /tmp/pip-install-ilickw8p/wxPython/bin/waf-2.0.8 --wx_config=/tmp/pip-install-ilickw8p/wxPython/build/wxbld/gtk3/wx-config --gtk3 --python="/usr/bin/python3" --out=build/waf/3.6/gtk3 configure build ' failed with exit code 1. - Finished command: build_py (2m57.71s) - Finished command: build (232m7.996s) - Command '"/usr/bin/python3" -u build.py build' failed with exit code 1. - - ---------------------------------------- -Command "/usr/bin/python3 -u -c "import setuptools, tokenize;__file__='/tmp/pip-install-ilickw8p/wxPython/setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" install --record /tmp/pip-record-pdbovxhc/install-record.txt --single-version-externally-managed --compile --user --prefix=" failed with error code 1 in /tmp/pip-install-ilickw8p/wxPython/ -You are using pip version 18.1, however version 20.2b1 is available. -You should consider upgrading via the 'pip install --upgrade pip' command. +OR + +python build.py build_py -v diff --git a/requirements.txt b/requirements.txt index e522d52c..6a515515 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ Pypubsub==4.0.3 twodict==1.2 -wxPython>=4.0.1 +wxPython==4.0.4 pyinstaller==3.6 polib==1.1.0 \ No newline at end of file diff --git a/setup.py b/setup.py index 0373c6c7..7f49d97d 100644 --- a/setup.py +++ b/setup.py @@ -47,50 +47,12 @@ from setuptools import setup, Command from distutils import log from distutils.spawn import spawn -from distutils.errors import DistutilsExecError import time import polib -__author__ = 'Sotiris Papadopoulos' -__contact__ = 'ytubedlg@gmail.com' -__maintainer__ = 'Oleksis Fraga' -__maintainer_contact__ = 'oleksis.fraga@gmail.com' -__projecturl__ = 'https://github.com/oleksis/youtube-dl-gui/' -__appname__ = 'Youtube-DLG' -__license__ = 'UNLICENSE' -__description__ = 'Youtube-dl GUI' -__descriptionfull__ = '''A cross platform front-end GUI of the popular -youtube-dl written in wxPython Phoenix''' -__licensefull__ = ''' -This is free and unencumbered software released into the public domain. - -Anyone is free to copy, modify, publish, use, compile, sell, or -distribute this software, either in source code form or as a compiled -binary, for any purpose, commercial or non-commercial, and by any -means. - -In jurisdictions that recognize copyright laws, the author or authors -of this software dedicate any and all copyright interest in the -software to the public domain. We make this dedication for the benefit -of the public at large and to the detriment of our heirs and -successors. We intend this dedication to be an overt act of -relinquishment in perpetuity of all present and future rights to this -software under copyright law. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR -OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - -For more information, please refer to ''' __packagename__ = "youtube_dl_gui" -DESCRIPTION = __description__ -LONG_DESCRIPTION = __descriptionfull__ PYINSTALLER = len(sys.argv) >= 2 and sys.argv[1] == "pyinstaller" @@ -111,6 +73,11 @@ # Get the version from youtube_dl_gui/version.py without importing the package exec(compile(open(__packagename__+"/version.py").read(), __packagename__+"/version.py", "exec")) +# Get the info from youtube_dl_gui/info.py without importing the package +exec(compile(open(__packagename__+"/info.py").read(), __packagename__+"/info.py", "exec")) + +DESCRIPTION = __description__ +LONG_DESCRIPTION = __descriptionfull__ def on_windows(): @@ -147,8 +114,7 @@ def finalize_options(self): self.search_pattern = os.path.join(__packagename__, "locale", "*", "LC_MESSAGES", "youtube_dl_gui.po") def run(self): - tools_path = os.path.join(sys.exec_prefix, 'bin') - msgfmt_file = os.path.join(tools_path, 'msgfmt') + po_file = "" try: for po_file in glob.glob(self.search_pattern): @@ -157,8 +123,8 @@ def run(self): log.info("Building MO file for '{}'".format(po_file)) po.save_as_mofile(mo_file) - except: - log.error("Could not locate file '{}', exiting...".format(po_file)) + except IOError: + log.error("Error process locate file '{}', exiting...".format(po_file)) sys.exit(1) @@ -222,7 +188,7 @@ class BuildPyinstallerBin(Command): StringTable( "000004b0", [ - StringStruct("CompanyName", "oleksis.fraga@gmail.com"), + StringStruct("CompanyName", __maintainer_contact__), StringStruct("FileDescription", DESCRIPTION), StringStruct("FileVersion", version2str()), StringStruct("InternalName", "youtube-dl-gui.exe"), @@ -247,9 +213,7 @@ def finalize_options(self): pass def run(self, version=version_file): - # --add-data - # ``os.pathsep`` which is ``;`` on Windows - # and ``:`` on most unix systems) is used. + """Run pyinstaller""" if on_windows(): path_sep = ";" else: From 7186913d7a8b5534e7e3979647511e9391fad3fc Mon Sep 17 00:00:00 2001 From: oleksis <“oleksis.fraga@gmail.com”> Date: Tue, 28 Jul 2020 18:53:44 -0400 Subject: [PATCH 08/34] Update README.md --- README.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 75678a6a..358ec8d0 100644 --- a/README.md +++ b/README.md @@ -17,20 +17,23 @@ A cross platform front-end GUI of the popular [youtube-dl](https://rg3.github.io * [GNU gettext](https://www.gnu.org/software/gettext/) ## Downloads -* [Source (.zip)](https://github.com/oleksis/youtube-dl-gui/archive/1.0.0.zip) -* [Source (.tar.gz)](https://github.com/oleksis/youtube-dl-gui/archive/1.0.0.tar.gz) +* [Source (.zip)](https://github.com/oleksis/youtube-dl-gui/archive/v1.0.0-alpha.zip) +* [Source (.tar.gz)](https://github.com/oleksis/youtube-dl-gui/archive/v1.0.0-alpha.tar.gz) ## Installation ### Install From Source 1. Download & extract the source -2. Change directory into *youtube-dl-gui-1.0* -3. Run `python setup.py build_trans` -4. Run `python setup.py install` +2. Change directory into *youtube-dl-gui-1.0.0-alpha* +3. Run `pip install -r requirements.txt` +4. Run `python setup.py build_trans` +5. Run `python setup.py install` ## Binaries Create binaries using [PyInstaller](https://www.pyinstaller.org/) -1. Run `python setup.py pyinstaller` +1. Run `pip install -r requirements.txt` +2. Run `python setup.py build_trans` +3. Run `python setup.py pyinstaller` ## Contributing * **Add support for new language:** See [localization howto](docs/localization_howto.md) From 02bdfe920438ff10c7255d33e67cf1c706b63c5c Mon Sep 17 00:00:00 2001 From: oleksis Date: Thu, 30 Jul 2020 18:22:14 -0400 Subject: [PATCH 09/34] Fix command for subprocess in Linux --- youtube_dl_gui/downloaders.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/youtube_dl_gui/downloaders.py b/youtube_dl_gui/downloaders.py index ebb787e8..c37ef106 100644 --- a/youtube_dl_gui/downloaders.py +++ b/youtube_dl_gui/downloaders.py @@ -308,10 +308,7 @@ def _get_cmd(self, url, options): Python list that contains the command to execute. """ - if os.name == 'nt': - cmd = [self.youtubedl_path] + options + [url] - else: - cmd = ['python', self.youtubedl_path] + options + [url] + cmd = [self.youtubedl_path] + options + [url] return cmd From 55ce028b051caa683cc999b95399f190cd92ff83 Mon Sep 17 00:00:00 2001 From: oleksis <“oleksis.fraga@gmail.com”> Date: Thu, 30 Jul 2020 23:42:16 -0400 Subject: [PATCH 10/34] Update README.md --- README.md | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 358ec8d0..7a4439a0 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,9 @@ A cross platform front-end GUI of the popular [youtube-dl](https://rg3.github.io ## Requirements * [Python 3](https://www.python.org/downloads) * [wxPython Phoenix 4](https://wxpython.org/download.php) -* [TwoDict](https://pypi.python.org/pypi/twodict) +* [TwoDict](https://pypi.org/project/twodict) +* [PyPubSub](https://pypi.org/project/PyPubSub) +* [polib](https://pypi.org/project/polib) * [FFmpeg](https://ffmpeg.org/download.html) (optional, to postprocess video files) ### Requirement for build Binaries/Executables @@ -25,15 +27,23 @@ A cross platform front-end GUI of the popular [youtube-dl](https://rg3.github.io ### Install From Source 1. Download & extract the source 2. Change directory into *youtube-dl-gui-1.0.0-alpha* -3. Run `pip install -r requirements.txt` -4. Run `python setup.py build_trans` -5. Run `python setup.py install` +3. Create virtual environment `python3 -m venv env` +4. Activate virtual environment `source env/bin/activate` +5. Run `pip install -r requirements.txt` +6. Run `python setup.py build_trans` +7. Run `python setup.py install` ## Binaries Create binaries using [PyInstaller](https://www.pyinstaller.org/) -1. Run `pip install -r requirements.txt` -2. Run `python setup.py build_trans` -3. Run `python setup.py pyinstaller` +1. Create virtual environment `python3 -m venv env` +2. Activate virtual environment `source env/bin/activate` +3. Run `pip install -r requirements.txt` +4. Run `python setup.py build_trans` +5. Run `python setup.py pyinstaller` + +## Run Youtube-DLG +1. Activate virtual environment `source env/bin/activate` +2. Run `python -m youtube_dl_gui` ## Contributing * **Add support for new language:** See [localization howto](docs/localization_howto.md) From 5c280b2dd6a611f0f15afb3ee017b172b425ac13 Mon Sep 17 00:00:00 2001 From: oleksis <“oleksis.fraga@gmail.com”> Date: Fri, 31 Jul 2020 19:44:55 -0400 Subject: [PATCH 11/34] Add Notes to README.md --- README.md | 61 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 7a4439a0..ea15cc63 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ A cross platform front-end GUI of the popular [youtube-dl](https://rg3.github.io ## Requirements * [Python 3](https://www.python.org/downloads) -* [wxPython Phoenix 4](https://wxpython.org/download.php) +* [wxPython 4 Phoenix](https://wxpython.org/download.php) * [TwoDict](https://pypi.org/project/twodict) * [PyPubSub](https://pypi.org/project/PyPubSub) * [polib](https://pypi.org/project/polib) @@ -25,25 +25,56 @@ A cross platform front-end GUI of the popular [youtube-dl](https://rg3.github.io ## Installation ### Install From Source -1. Download & extract the source -2. Change directory into *youtube-dl-gui-1.0.0-alpha* -3. Create virtual environment `python3 -m venv env` -4. Activate virtual environment `source env/bin/activate` -5. Run `pip install -r requirements.txt` -6. Run `python setup.py build_trans` -7. Run `python setup.py install` +* Download & extract the source +* Change directory into *youtube-dl-gui-1.0.0-alpha* +* Create virtual environment +``` +python3 -m venv env +``` +* Activate virtual environment +``` +source env/bin/activate +``` +* Install requirements, build translations and install +``` +pip install -r requirements.txt +python setup.py build_trans +python setup.py install +``` ## Binaries Create binaries using [PyInstaller](https://www.pyinstaller.org/) -1. Create virtual environment `python3 -m venv env` -2. Activate virtual environment `source env/bin/activate` -3. Run `pip install -r requirements.txt` -4. Run `python setup.py build_trans` -5. Run `python setup.py pyinstaller` +* Create virtual environment +``` +python3 -m venv env +``` +* Activate virtual environment +``` +source env/bin/activate +``` +* Install requirements, build translations and create binaries +``` +pip install -r requirements.txt +python setup.py build_trans +python setup.py pyinstaller +``` ## Run Youtube-DLG -1. Activate virtual environment `source env/bin/activate` -2. Run `python -m youtube_dl_gui` +* Activate virtual environment and run +``` +source env/bin/activate +youtube-dl-gui +``` + +## Notes +An alternative to install wxPython 4 **Phoenix** from the Extras section + +For Ubuntu 20.04 + +``` +wget https://extras.wxpython.org/wxPython4/extras/linux/gtk3/ubuntu-20.04/wxPython-4.1.0-cp38-cp38-linux_x86_64.whl +pip3 install wxPython-4.1.0-cp38-cp38-linux_x86_64.whl +``` ## Contributing * **Add support for new language:** See [localization howto](docs/localization_howto.md) From 897b2c8aabb82cd5eef4a187d369b6ec6aef1e2c Mon Sep 17 00:00:00 2001 From: oleksis <“oleksis.fraga@gmail.com”> Date: Mon, 3 Aug 2020 03:18:53 -0400 Subject: [PATCH 12/34] Reformat source code --- tests/test_ditem.py | 16 +-- tests/test_dlist.py | 2 +- tests/test_parsers.py | 8 +- tests/test_utils.py | 13 +-- tests/test_widgets.py | 18 ++-- youtube_dl_gui/__init__.py | 22 ++--- youtube_dl_gui/downloaders.py | 30 +++--- youtube_dl_gui/downloadmanager.py | 159 +++++++++++++++--------------- youtube_dl_gui/formats.py | 9 +- youtube_dl_gui/logmanager.py | 21 ++-- youtube_dl_gui/mainframe.py | 119 +++++++++++++--------- youtube_dl_gui/optionsframe.py | 54 ++++++---- youtube_dl_gui/optionsmanager.py | 48 ++++----- youtube_dl_gui/updatemanager.py | 25 ++--- youtube_dl_gui/utils.py | 2 - youtube_dl_gui/widgets.py | 75 ++++++++------ 16 files changed, 324 insertions(+), 297 deletions(-) diff --git a/tests/test_ditem.py b/tests/test_ditem.py index c6f09e8b..27ddd4e6 100644 --- a/tests/test_ditem.py +++ b/tests/test_ditem.py @@ -15,12 +15,11 @@ try: from youtube_dl_gui.downloadmanager import DownloadItem except ImportError as error: - print error + print(error) sys.exit(1) class TestItemInit(unittest.TestCase): - """Test case for DownloadItem init.""" def test_init(self): @@ -32,7 +31,7 @@ def test_init(self): self.assertEqual(ditem.stage, "Queued") self.assertEqual(ditem.url, url) self.assertEqual(ditem.options, options) - self.assertEqual(ditem.object_id, hash(url + unicode(options))) + self.assertEqual(ditem.object_id, hash(url + str(options))) self.assertEqual(ditem.path, "") self.assertEqual(ditem.filenames, []) @@ -54,7 +53,6 @@ def test_init(self): class TestGetFiles(unittest.TestCase): - """Test case for DownloadItem get_files method.""" def setUp(self): @@ -67,14 +65,14 @@ def test_get_files(self): self.ditem.filenames = ["file1", "file2"] self.ditem.extensions = [".mp4", ".m4a"] - self.assertEqual(self.ditem.get_files(), [os.path.join(path, "file1" + ".mp4"), os.path.join(path, "file2" + ".m4a")]) + self.assertEqual(self.ditem.get_files(), + [os.path.join(path, "file1" + ".mp4"), os.path.join(path, "file2" + ".m4a")]) def test_get_files_no_data(self): self.assertEqual(self.ditem.get_files(), []) class TestItemComparison(unittest.TestCase): - """Test case for DownloadItem __eq__ method.""" def test_equal_true(self): @@ -96,7 +94,6 @@ def test_equal_false(self): class TestSetItemStage(unittest.TestCase): - """Test case for DownloadItem stage setter.""" def setUp(self): @@ -135,7 +132,6 @@ def test_set_stage_invalid(self): class TestUpdateStats(unittest.TestCase): - """Test case for DownloadItem update_stats method.""" def setUp(self): @@ -187,7 +183,6 @@ def test_update_stats(self): self.assertEqual(self.ditem.filesizes, [9909043.20]) - self.ditem.update_stats({"filename": "somefilename.f2", "extension": ".m4a", "filesize": "2.22MiB", @@ -265,7 +260,6 @@ def test_update_stats(self): self.assertEqual(self.ditem.filesizes, [10747904.0]) - self.ditem.update_stats({"filename": "someotherfilename.f2", "extension": ".m4a", "filesize": "3.33MiB", @@ -363,7 +357,6 @@ def test_update_stats_not_string(self): class TestDownloadItemPrivate(unittest.TestCase): - """Test case for private method of the DownloadItem.""" def test_set_stage(self): @@ -433,7 +426,6 @@ def test_calc_post_proc_size(self): class TestReset(unittest.TestCase): - """Test case for the DownloadItem reset method.""" def setUp(self): diff --git a/tests/test_dlist.py b/tests/test_dlist.py index 8d38fa7d..f1e34c5e 100644 --- a/tests/test_dlist.py +++ b/tests/test_dlist.py @@ -16,7 +16,7 @@ import mock from youtube_dl_gui.downloadmanager import DownloadList, synchronized except ImportError as error: - print error + print(error) sys.exit(1) diff --git a/tests/test_parsers.py b/tests/test_parsers.py index 0c98c6ca..fc70e3e3 100644 --- a/tests/test_parsers.py +++ b/tests/test_parsers.py @@ -15,18 +15,17 @@ try: from youtube_dl_gui.parsers import OptionsParser except ImportError as error: - print error + print(error) sys.exit(1) class TestParse(unittest.TestCase): - """Test case for OptionsParser parse method.""" def setUp(self): # Create the options_dict based on the OptionHolder # items inside the OptionsParser object - self.options_dict = {item.name:item.default_value for item in OptionsParser()._ydl_options} + self.options_dict = {item.name: item.default_value for item in OptionsParser()._ydl_options} # Add extra options used by the OptionsParser.parse method self.options_dict["save_path"] = "/home/user/Workplace/test/youtube" @@ -90,7 +89,6 @@ def test_parse_cmd_args_with_quotes(self): self.check_options_parse(expected_cmd_list) - # Test with two quoted 'cmd_args' self.options_dict["cmd_args"] = "--postprocessor-args \"-y -report\"" @@ -104,7 +102,6 @@ def test_parse_cmd_args_with_quotes(self): self.check_options_parse(expected_cmd_list) - # Test with one quoted 'cmd_arg' followed by other cmd line args self.options_dict["cmd_args"] = "--postprocessor-args \"-y\" -v" @@ -119,7 +116,6 @@ def test_parse_cmd_args_with_quotes(self): self.check_options_parse(expected_cmd_list) - # Test the example presented in issue #54 self.options_dict["cmd_args"] = "-f \"(mp4)[width<1300]\"" self.options_dict["video_format"] = "0" # Set video format to 'default' diff --git a/tests/test_utils.py b/tests/test_utils.py index 38936b57..09c47183 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -17,12 +17,11 @@ from youtube_dl_gui import utils except ImportError as error: - print error + print(error) sys.exit(1) class TestToBytes(unittest.TestCase): - """Test case for the to_bytes method.""" def test_to_bytes_bytes(self): @@ -46,7 +45,6 @@ def test_to_bytes_terabytes(self): class TestFormatBytes(unittest.TestCase): - """Test case for the format_bytes method.""" def test_format_bytes_bytes(self): @@ -66,7 +64,6 @@ def test_format_bytes_terabytes(self): class TestBuildCommand(unittest.TestCase): - """Test case for the build_command method.""" def setUp(self): @@ -113,7 +110,6 @@ def test_build_command_without_spaces_windows(self): class TestConvertItem(unittest.TestCase): - """Test case for the convert_item function.""" def setUp(self): @@ -127,7 +123,7 @@ def setUp(self): self.input_dict_s = {str("k1"): str("v1"), str("k2"): str("v2")} def check_iter(self, iterable, iter_type, is_unicode): - check_type = unicode if is_unicode else str + check_type = str iterable = utils.convert_item(iterable, is_unicode) @@ -143,10 +139,10 @@ def test_convert_item_unicode_str(self): self.assertIsInstance(utils.convert_item("test"), str) def test_convert_item_unicode_unicode(self): - self.assertIsInstance(utils.convert_item("test", True), unicode) + self.assertIsInstance(utils.convert_item("test", True), str) def test_convert_item_str_unicode(self): - self.assertIsInstance(utils.convert_item(str("test"), True), unicode) + self.assertIsInstance(utils.convert_item(str("test"), True), str) def test_convert_item_str_str(self): self.assertIsInstance(utils.convert_item(str("test")), str) @@ -177,7 +173,6 @@ def test_convert_item_dict_str_unicode(self): class TestGetDefaultLang(unittest.TestCase): - """Test case for the get_default_lang function.""" @mock.patch("youtube_dl_gui.utils.locale_getdefaultlocale") diff --git a/tests/test_widgets.py b/tests/test_widgets.py index 7c8cb035..70b1c7cb 100644 --- a/tests/test_widgets.py +++ b/tests/test_widgets.py @@ -12,7 +12,6 @@ PATH = os.path.realpath(os.path.abspath(__file__)) sys.path.insert(0, os.path.dirname(os.path.dirname(PATH))) - try: import wx import mock @@ -23,12 +22,11 @@ ListBoxPopup ) except ImportError as error: - print error + print(error) sys.exit(1) class TestListBoxWithHeaders(unittest.TestCase): - """Test cases for the ListBoxWithHeaders widget.""" def setUp(self): @@ -38,22 +36,22 @@ def setUp(self): self.listbox = ListBoxWithHeaders(self.frame) self.listbox.add_header("Header") - self.listbox.add_items(["item%s" % i for i in xrange(10)]) + self.listbox.add_items(["item%s" % i for i in range(10)]) def tearDown(self): self.frame.Destroy() def test_find_string_header_found(self): - self.assertEqual(self.listbox.FindString("Header"), 0) + self.assertEqual(self.listbox.FindString("Header", ), 0) def test_find_string_header_not_found(self): - self.assertEqual(self.listbox.FindString("Header2"), wx.NOT_FOUND) + self.assertEqual(self.listbox.FindString("Header2", ), wx.NOT_FOUND) def test_find_string_item_found(self): - self.assertEqual(self.listbox.FindString("item1"), 2) + self.assertEqual(self.listbox.FindString("item1", ), 2) def test_find_string_item_not_found(self): - self.assertEqual(self.listbox.FindString("item"), wx.NOT_FOUND) + self.assertEqual(self.listbox.FindString("item", ), wx.NOT_FOUND) def test_get_string_header(self): self.assertEqual(self.listbox.GetString(0), "Header") @@ -164,6 +162,7 @@ def test_add_items_with_prefix(self, mock_append): self.listbox.add_items(["new_item1", "new_item2"]) mock_append.assert_called_once_with([ListBoxWithHeaders.TEXT_PREFIX + "new_item1", ListBoxWithHeaders.TEXT_PREFIX + "new_item2"]) + @mock.patch("wx.ListBox.AppendItems") def test_add_items_without_prefix(self, mock_append): self.listbox.add_items(["new_item1", "new_item2"], with_prefix=False) @@ -171,7 +170,6 @@ def test_add_items_without_prefix(self, mock_append): class TestCustomComboBox(unittest.TestCase): - """Test cases for the CustomComboBox widget.""" def setUp(self): @@ -182,7 +180,7 @@ def setUp(self): # Call directly the ListBoxWithHeaders methods self.combobox.listbox.GetControl().add_header("Header") - self.combobox.listbox.GetControl().add_items(["item%s" % i for i in xrange(10)]) + self.combobox.listbox.GetControl().add_items(["item%s" % i for i in range(10)]) def tearDown(self): self.frame.Destroy() diff --git a/youtube_dl_gui/__init__.py b/youtube_dl_gui/__init__.py index 3740d940..be976b41 100644 --- a/youtube_dl_gui/__init__.py +++ b/youtube_dl_gui/__init__.py @@ -13,17 +13,10 @@ """ - +from gettext import translation import sys import os -if __package__ is None and not hasattr(sys, 'frozen'): - # direct call of __main__.py - root_path = os.path.realpath(os.path.abspath(__file__)) - sys.path.insert(0, os.path.dirname(os.path.dirname(root_path))) - -import youtube_dl_gui -import gettext try: import wx @@ -67,17 +60,16 @@ # Set gettext before MainFrame import # because the GUI strings are class level attributes locale_dir = get_locale_file() -lang = gettext.gettext try: - lang = gettext.translation(__packagename__, - locale_dir, - [opt_manager.options['locale_name']]) + lang = translation(__packagename__, + locale_dir, + [opt_manager.options['locale_name']]) except IOError: opt_manager.options['locale_name'] = 'en_US' - lang = gettext.translation(__packagename__, - locale_dir, - [opt_manager.options['locale_name']]) + lang = translation(__packagename__, + locale_dir, + [opt_manager.options['locale_name']]) # Redefine _ to gettext in builtins diff --git a/youtube_dl_gui/downloaders.py b/youtube_dl_gui/downloaders.py index c37ef106..5540f52b 100644 --- a/youtube_dl_gui/downloaders.py +++ b/youtube_dl_gui/downloaders.py @@ -8,10 +8,7 @@ """ -import re import os -import sys -import locale import signal import subprocess @@ -23,22 +20,23 @@ class PipeReader(Thread): + # noinspection PyUnresolvedReferences """Helper class to avoid deadlocks when reading from subprocess pipes. - This class uses python threads and queues in order to read from subprocess - pipes in an asynchronous way. + This class uses python threads and queues in order to read from subprocess + pipes in an asynchronous way. - Attributes: - WAIT_TIME (float): Time in seconds to sleep. + Attributes: + WAIT_TIME (float): Time in seconds to sleep. - Args: - queue (Queue.Queue): Python queue to store the output of the subprocess. + Args: + queue (Queue.Queue): Python queue to store the output of the subprocess. - Warnings: - All the operations are based on 'str' types. The caller has to convert - the queued items back to 'unicode' if he needs to. + Warnings: + All the operations are based on 'str' types. The caller has to convert + the queued items back to 'unicode' if he needs to. - """ + """ WAIT_TIME = 0.1 @@ -230,7 +228,8 @@ def _set_returncode(self, code): if code >= self._return_code: self._return_code = code - def _is_warning(self, stderr): + @staticmethod + def _is_warning(stderr): return str(stderr).split(':')[0] == 'WARNING' def _last_data_hook(self): @@ -373,6 +372,7 @@ def extract_data(stdout): """ # REFACTOR + # noinspection PyShadowingNames def extract_filename(input_data): path, fullname = os.path.split(input_data.strip("\"")) filename, extension = os.path.splitext(fullname) @@ -423,7 +423,7 @@ def extract_filename(input_data): data_dictionary['playlist_size'] = stdout[5] # Remove the 'and merged' part from stdout when using ffmpeg to merge the formats - if stdout[-3] == 'downloaded' and stdout [-1] == 'merged': + if stdout[-3] == 'downloaded' and stdout[-1] == 'merged': stdout = stdout[:-2] stdout_with_spaces = stdout_with_spaces[:-2] diff --git a/youtube_dl_gui/downloadmanager.py b/youtube_dl_gui/downloadmanager.py index 2e927c06..88ff2e49 100644 --- a/youtube_dl_gui/downloadmanager.py +++ b/youtube_dl_gui/downloadmanager.py @@ -29,6 +29,7 @@ ) from wx import CallAfter +# noinspection PyPep8Naming from pubsub import pub as Publisher from .parsers import OptionsParser @@ -49,6 +50,7 @@ _SYNC_LOCK = RLock() + # Decorator that adds thread synchronization to a function def synchronized(lock): def _decorator(func): @@ -63,23 +65,24 @@ def _wrapper(*args, **kwargs): class DownloadItem(object): + # noinspection PyUnresolvedReferences """Object that represents a download. - Attributes: - STAGES (tuple): Main stages of the download item. + Attributes: + STAGES (tuple): Main stages of the download item. - ACTIVE_STAGES (tuple): Sub stages of the 'Active' stage. + ACTIVE_STAGES (tuple): Sub stages of the 'Active' stage. - COMPLETED_STAGES (tuple): Sub stages of the 'Completed' stage. + COMPLETED_STAGES (tuple): Sub stages of the 'Completed' stage. - ERROR_STAGES (tuple): Sub stages of the 'Error' stage. + ERROR_STAGES (tuple): Sub stages of the 'Error' stage. - Args: - url (string): URL that corresponds to the download item. + Args: + url (string): URL that corresponds to the download item. - options (list): Options list to use during the download phase. + options (list): Options list to use during the download phase. - """ + """ STAGES = ("Queued", "Active", "Paused", "Completed", "Error") @@ -116,8 +119,10 @@ def stage(self, value): if value == "Error": self.progress_stats["status"] = self.ERROR_STAGES[0] + # noinspection PyAttributeOutsideInit self._stage = value + # noinspection PyAttributeOutsideInit def reset(self): if hasattr(self, "_stage") and self._stage == self.STAGES[1]: raise RuntimeError("Cannot reset an 'Active' item") @@ -155,6 +160,7 @@ def get_files(self): return files + # noinspection PyAttributeOutsideInit def update_stats(self, stats_dict): """Updates the progress_stats dict from the given dictionary.""" assert isinstance(stats_dict, dict) @@ -220,9 +226,7 @@ def __eq__(self, other): return self.object_id == other.object_id - class DownloadList(object): - """List like data structure that contains DownloadItems. Args: @@ -344,22 +348,22 @@ def _swap(self, index1, index2): class DownloadManager(Thread): - + # noinspection PyUnresolvedReferences """Manages the download process. - Attributes: - WAIT_TIME (float): Time in seconds to sleep. + Attributes: + WAIT_TIME (float): Time in seconds to sleep. - Args: - download_list (DownloadList): List that contains items to download. + Args: + download_list (DownloadList): List that contains items to download. - opt_manager (optionsmanager.OptionsManager): Object responsible for - managing the youtubedlg options. + opt_manager (optionsmanager.OptionsManager): Object responsible for + managing the youtubedlg options. - log_manager (logmanager.LogManager): Object responsible for writing - errors to the log. + log_manager (logmanager.LogManager): Object responsible for writing + errors to the log. - """ + """ WAIT_TIME = 0.1 @@ -431,12 +435,12 @@ def active(self): active_items = (workers that work) + (items waiting in the url_list). """ - #counter = 0 - #for worker in self._workers: - #if not worker.available(): - #counter += 1 - - #counter += len(self.download_list) + # counter = 0 + # for worker in self._workers: + # if not worker.available(): + # counter += 1 + # + # counter += len(self.download_list) return len(self.download_list) @@ -462,6 +466,7 @@ def add_url(self, url): download process. """ + # noinspection PyUnresolvedReferences self.download_list.append(url) def send_to_worker(self, data): @@ -479,7 +484,8 @@ def send_to_worker(self, data): if worker.has_index(data['index']): worker.update_data(data) - def _talk_to_gui(self, signal, data=None): + @staticmethod + def _talk_to_gui(signal, data=None): """Send data back to the GUI using wxCallAfter and wxPublisher. Args: @@ -527,30 +533,30 @@ def _youtubedl_path(self): class Worker(Thread): - + # noinspection PyUnresolvedReferences """Simple worker which downloads the given url using a downloader - from the downloaders.py module. + from the downloaders.py module. - Attributes: - WAIT_TIME (float): Time in seconds to sleep. + Attributes: + WAIT_TIME (float): Time in seconds to sleep. - Args: - opt_manager (optionsmanager.OptionsManager): Check DownloadManager - description. + Args: + opt_manager (optionsmanager.OptionsManager): Check DownloadManager + description. - youtubedl (string): Absolute path to youtube-dl binary. + youtubedl (string): Absolute path to youtube-dl binary. - log_manager (logmanager.LogManager): Check DownloadManager - description. + log_manager (logmanager.LogManager): Check DownloadManager + description. - log_lock (threading.Lock): Synchronization lock for the log_manager. - If the log_manager is set (not None) then the caller has to make - sure that the log_lock is also set. + log_lock (threading.Lock): Synchronization lock for the log_manager. + If the log_manager is set (not None) then the caller has to make + sure that the log_lock is also set. - Note: - For available data keys see self._data under the __init__() method. + Note: + For available data keys see self._data under the __init__() method. - """ + """ WAIT_TIME = 0.1 @@ -591,7 +597,6 @@ def __init__(self, opt_manager, youtubedl, log_manager=None, log_lock=None): def run(self): while self._running: if self._data['url'] is not None: - #options = self._options_parser.parse(self.opt_manager.options) ret_code = self._downloader.download(self._data['url'], self._options) if (ret_code == YoutubeDLDownloader.OK or @@ -599,13 +604,6 @@ def run(self): ret_code == YoutubeDLDownloader.WARNING): self._successful += 1 - # Ask GUI for name updates - #self._talk_to_gui('receive', {'source': 'filename', 'dest': 'new_filename'}) - - # Wait until you get a reply - #while self._wait_for_reply: - #time.sleep(self.WAIT_TIME) - self._reset() time.sleep(self.WAIT_TIME) @@ -613,16 +611,18 @@ def run(self): # Call the destructor function of YoutubeDLDownloader object self._downloader.close() + # noinspection PyIncorrectDocstring def download(self, url, options, object_id): + # noinspection PyUnresolvedReferences """Download given item. - Args: - item (dict): Python dictionary that contains two keys. - The url and the index of the corresponding row in which - the worker should send back the information about the - download process. + Args: + item (dict): Python dictionary that contains two keys. + The url and the index of the corresponding row in which + the worker should send back the information about the + download process. - """ + """ self._data['url'] = url self._options = options self._data['index'] = object_id @@ -690,27 +690,27 @@ def _data_hook(self, data): extract_data() function under the downloaders.py module. """ - ## Temp dictionary which holds the updates - #temp_dict = {} - - ## Update each key - #for key in data: - #if self._data[key] != data[key]: - #self._data[key] = data[key] - #temp_dict[key] = data[key] - - ## Build the playlist status if there is an update - ## REFACTOR re-implement this on DownloadItem or ListCtrl level? - ##if self._data['playlist_index'] is not None: - ##if 'status' in temp_dict or 'playlist_index' in temp_dict: - ##temp_dict['status'] = '{status} {index}/{size}'.format( - ##status=self._data['status'], - ##index=self._data['playlist_index'], - ##size=self._data['playlist_size'] - ##) - - #if len(temp_dict): - #self._talk_to_gui('send', temp_dict) + # Temp dictionary which holds the updates + # temp_dict = {} + + # Update each key + # for key in data: + # if self._data[key] != data[key]: + # self._data[key] = data[key] + # temp_dict[key] = data[key] + + # Build the playlist status if there is an update + # REFACTOR re-implement this on DownloadItem or ListCtrl level? + # if self._data['playlist_index'] is not None: + # if 'status' in temp_dict or 'playlist_index' in temp_dict: + # temp_dict['status'] = '{status} {index}/{size}'.format( + # status=self._data['status'], + # index=self._data['playlist_index'], + # size=self._data['playlist_size'] + # ) + + # if len(temp_dict): + # self._talk_to_gui('send', temp_dict) self._talk_to_gui('send', data) def _talk_to_gui(self, signal, data): @@ -751,4 +751,3 @@ def _talk_to_gui(self, signal, data): self._wait_for_reply = True CallAfter(Publisher.sendMessage, WORKER_PUB_TOPIC, signal=signal, data=data) - diff --git a/youtube_dl_gui/formats.py b/youtube_dl_gui/formats.py index 09c36308..981a19ab 100644 --- a/youtube_dl_gui/formats.py +++ b/youtube_dl_gui/formats.py @@ -2,9 +2,10 @@ import gettext +# noinspection PyPep8Naming from .utils import TwoWayOrderedDict as tdict -_ = gettext.gettext +_ = gettext.gettext OUTPUT_FORMATS = tdict([ (0, _("ID")), @@ -15,12 +16,10 @@ (3, _("Custom")) ]) - DEFAULT_FORMATS = tdict([ ("0", _("default")) ]) - VIDEO_FORMATS = tdict([ ("3gp", "3gp"), ("17", "3gp [144p]"), @@ -68,7 +67,6 @@ ("172", "webm 256k (DASH Audio)") ]) - AUDIO_FORMATS = tdict([ ("mp3", "mp3"), ("wav", "wav"), @@ -111,12 +109,10 @@ def reload_strings(func): (3, _("Custom")) ]) - DEFAULT_FORMATS = tdict([ ("0", _("default")) ]) - VIDEO_FORMATS = tdict([ ("3gp", "3gp"), ("17", "3gp [144p]"), @@ -164,7 +160,6 @@ def reload_strings(func): ("172", "webm 256k (DASH Audio)") ]) - AUDIO_FORMATS = tdict([ ("mp3", "mp3"), ("wav", "wav"), diff --git a/youtube_dl_gui/logmanager.py b/youtube_dl_gui/logmanager.py index 77a11303..ecf3b453 100644 --- a/youtube_dl_gui/logmanager.py +++ b/youtube_dl_gui/logmanager.py @@ -15,22 +15,23 @@ class LogManager(object): + # noinspection PyUnresolvedReferences """Simple log manager for youtube-dl. - This class is mainly used to log the youtube-dl STDERR. + This class is mainly used to log the youtube-dl STDERR. - Attributes: - LOG_FILENAME (string): Filename of the log file. - TIME_TEMPLATE (string): Custom template to log the time. - MAX_LOGSIZE (int): Maximum size(Bytes) of the log file. + Attributes: + LOG_FILENAME (string): Filename of the log file. + TIME_TEMPLATE (string): Custom template to log the time. + MAX_LOGSIZE (int): Maximum size(Bytes) of the log file. - Args: - config_path (string): Absolute path where LogManager should - store the log file. + Args: + config_path (string): Absolute path where LogManager should + store the log file. - add_time (boolean): If True LogManager will also log the time. + add_time (boolean): If True LogManager will also log the time. - """ + """ LOG_FILENAME = "log" TIME_TEMPLATE = "[{time}] {error_msg}" diff --git a/youtube_dl_gui/mainframe.py b/youtube_dl_gui/mainframe.py index d612a6b7..b6a5cd38 100644 --- a/youtube_dl_gui/mainframe.py +++ b/youtube_dl_gui/mainframe.py @@ -9,16 +9,16 @@ DEFAULT_FORMATS, VIDEO_FORMATS, AUDIO_FORMATS, - FORMATS, - OUTPUT_FORMATS + FORMATS ) import wx import wx.adv +from wx.lib.mixins.listctrl import ListCtrlAutoWidthMixin +# noinspection PyPep8Naming from pubsub import pub as Publisher -from wx.lib.mixins.listctrl import ListCtrlAutoWidthMixin from .parsers import OptionsParser @@ -45,7 +45,6 @@ build_command, get_icon_file, shutdown_sys, - remove_file, open_file, get_time ) @@ -58,40 +57,41 @@ __projecturl__, __appname__, __author__, - __maintainer__, - __maintainer_contact__ + __maintainer__ ) from .version import __version__ _ = lang.gettext + class MainFrame(wx.Frame): + # noinspection PyUnresolvedReferences """Main window class. - This class is responsible for creating the main app window - and binding the events. + This class is responsible for creating the main app window + and binding the events. - Attributes: - FRAMES_MIN_SIZE (tuple): Tuple that contains the minumum width, height of the frame. + Attributes: + FRAMES_MIN_SIZE (tuple): Tuple that contains the minumum width, height of the frame. - Labels area (strings): Strings for the widgets labels. + Labels area (strings): Strings for the widgets labels. - STATUSLIST_COLUMNS (dict): Python dictionary which holds informations - about the wxListCtrl columns. For more informations read the - comments above the STATUSLIST_COLUMNS declaration. + STATUSLIST_COLUMNS (dict): Python dictionary which holds informations + about the wxListCtrl columns. For more informations read the + comments above the STATUSLIST_COLUMNS declaration. - Args: - opt_manager (optionsmanager.OptionsManager): Object responsible for - handling the settings. + Args: + opt_manager (optionsmanager.OptionsManager): Object responsible for + handling the settings. - log_manager (logmanager.LogManager): Object responsible for handling - the log stuff. + log_manager (logmanager.LogManager): Object responsible for handling + the log stuff. - parent (wx.Window): Frame parent. + parent (wx.Window): Frame parent. - """ + """ FRAMES_MIN_SIZE = (560, 360) @@ -117,7 +117,7 @@ class MainFrame(wx.Frame): VIEWLOG_LABEL = _("View Log") SUCC_REPORT_MSG = _("Successfully downloaded {0} URL(s) in {1} " - "day(s) {2} hour(s) {3} minute(s) {4} second(s)") + "day(s) {2} hour(s) {3} minute(s) {4} second(s)") DL_COMPLETED_MSG = _("Downloads completed") URL_REPORT_MSG = _("Total Progress: {0:.1f}% | Queued ({1}) Paused ({2}) Active ({3}) Completed ({4}) Error ({5})") CLOSING_MSG = _("Stopping downloads") @@ -134,9 +134,9 @@ class MainFrame(wx.Frame): UPDATE_SUCC_MSG = _("Successfully downloaded youtube-dl") OPEN_DIR_ERR = _("Unable to open directory: '{dir}'. " - "The specified path does not exist") + "The specified path does not exist") SHUTDOWN_ERR = _("Error while shutting down. " - "Make sure you typed the correct password") + "Make sure you typed the correct password") SHUTDOWN_MSG = _("Shutting down system") VIDEO_LABEL = _("Title") @@ -322,7 +322,8 @@ def __init__(self, opt_manager, log_manager, parent=None): self._url_list.SetFocus() - def _create_menu_item(self, items): + @staticmethod + def _create_menu_item(items): menu = wx.Menu() for item in items: @@ -342,6 +343,7 @@ def _on_statuslist_right_click(self, event): self.PopupMenu(self._statuslist_menu) + # noinspection PyUnusedLocal def _on_reenter(self, event): selected = self._status_list.get_selected() @@ -367,6 +369,7 @@ def reset(self): self._path_combobox.LoadMultiple(self.opt_manager.options["save_path_dirs"]) self._path_combobox.SetValue(self.opt_manager.options["save_path"]) + # noinspection PyUnusedLocal def _on_open_dest(self, event): selected = self._status_list.get_selected() @@ -377,9 +380,11 @@ def _on_open_dest(self, event): if download_item.path: open_file(download_item.path) + # noinspection PyUnusedLocal def _on_open_path(self, event): open_file(self._path_combobox.GetValue()) + # noinspection PyUnusedLocal def _on_geturl(self, event): selected = self._status_list.get_selected() @@ -396,6 +401,7 @@ def _on_geturl(self, event): wx.TheClipboard.SetData(clipdata) wx.TheClipboard.Close() + # noinspection PyUnusedLocal def _on_getcmd(self, event): selected = self._status_list.get_selected() @@ -412,6 +418,7 @@ def _on_getcmd(self, event): wx.TheClipboard.SetData(clipdata) wx.TheClipboard.Close() + # noinspection PyUnusedLocal def _on_timer(self, event): total_percentage = 0.0 queued = paused = active = completed = error = 0 @@ -444,6 +451,7 @@ def _on_timer(self, event): # Dont overwrite the update messages self._status_bar_write(msg) + # noinspection PyUnusedLocal def _update_pause_button(self, event): selected_rows = self._status_list.get_all_selected() @@ -495,12 +503,13 @@ def _update_videoformat_combobox(self): self._update_videoformat(None) + # noinspection PyUnusedLocal def _update_videoformat(self, event): self.opt_manager.options["selected_format"] = selected_format = FORMATS[self._videoformat_combobox.GetValue()] if selected_format in VIDEO_FORMATS: self.opt_manager.options["video_format"] = selected_format - self.opt_manager.options["audio_format"] = "" #NOTE Set to default value, check parsers.py + self.opt_manager.options["audio_format"] = "" # NOTE Set to default value, check parsers.py elif selected_format in AUDIO_FORMATS: self.opt_manager.options["video_format"] = DEFAULT_FORMATS[_("default")] self.opt_manager.options["audio_format"] = selected_format @@ -508,14 +517,19 @@ def _update_videoformat(self, event): self.opt_manager.options["video_format"] = DEFAULT_FORMATS[_("default")] self.opt_manager.options["audio_format"] = "" + # noinspection PyUnusedLocal def _update_savepath(self, event): self.opt_manager.options["save_path"] = self._path_combobox.GetValue() + # noinspection PyUnusedLocal def _on_delete(self, event): index = self._status_list.get_next_selected() if index == -1: - dlg = ButtonsChoiceDialog(self, [_("Remove all"), _("Remove completed")], _("No items selected. Please pick an action"), _("Delete")) + dlg = ButtonsChoiceDialog(self, + [_("Remove all"), _("Remove completed")], + _("No items selected. Please pick an action"), + _("Delete")) ret_code = dlg.ShowModal() dlg.Destroy() @@ -533,7 +547,10 @@ def _on_delete(self, event): self._download_list.remove(ditem.object_id) else: if self.opt_manager.options["confirm_deletion"]: - dlg = wx.MessageDialog(self, _("Are you sure you want to remove selected items?"), _("Delete"), wx.YES_NO | wx.ICON_QUESTION) + dlg = wx.MessageDialog(self, + _("Are you sure you want to remove selected items?"), + _("Delete"), + wx.YES_NO | wx.ICON_QUESTION) result = dlg.ShowModal() == wx.ID_YES dlg.Destroy() else: @@ -545,18 +562,10 @@ def _on_delete(self, event): selected_download_item = self._download_list.get_item(object_id) if selected_download_item.stage == "Active": - self._create_popup(_("Item is active, cannot remove"), self.WARNING_LABEL, wx.OK | wx.ICON_EXCLAMATION) + self._create_popup(_("Item is active, cannot remove"), + self.WARNING_LABEL, + wx.OK | wx.ICON_EXCLAMATION) else: - #if selected_download_item.stage == "Completed": - #dlg = wx.MessageDialog(self, "Do you want to remove the files associated with this item?", "Remove files", wx.YES_NO | wx.ICON_QUESTION) - - #result = dlg.ShowModal() == wx.ID_YES - #dlg.Destroy() - - #if result: - #for cur_file in selected_download_item.get_files(): - #remove_file(cur_file) - self._status_list.remove_row(index) self._download_list.remove(object_id) index -= 1 @@ -565,6 +574,7 @@ def _on_delete(self, event): self._update_pause_button(None) + # noinspection PyUnusedLocal def _on_play(self, event): selected_rows = self._status_list.get_all_selected() @@ -580,6 +590,7 @@ def _on_play(self, event): else: self._create_popup(_("Item is not completed"), self.INFO_LABEL, wx.OK | wx.ICON_INFORMATION) + # noinspection PyUnusedLocal,PyProtectedMember def _on_arrow_up(self, event): index = self._status_list.get_next_selected() @@ -599,6 +610,7 @@ def _on_arrow_up(self, event): index = self._status_list.get_next_selected(index) + # noinspection PyUnusedLocal,PyProtectedMember def _on_arrow_down(self, event): index = self._status_list.get_next_selected(reverse=True) @@ -618,6 +630,7 @@ def _on_arrow_down(self, event): index = self._status_list.get_next_selected(index, True) + # noinspection PyUnusedLocal,PyProtectedMember def _on_reload(self, event): selected_rows = self._status_list.get_all_selected() @@ -643,6 +656,7 @@ def _on_reload(self, event): self._update_pause_button(None) + # noinspection PyUnusedLocal,PyProtectedMember def _on_pause(self, event): selected_rows = self._status_list.get_all_selected() @@ -664,6 +678,7 @@ def _on_pause(self, event): self._update_pause_button(None) + # noinspection PyUnusedLocal def _on_start(self, event): if self.download_manager is None: if self.update_thread is not None and self.update_thread.is_alive(): @@ -675,6 +690,7 @@ def _on_start(self, event): else: self.download_manager.stop_downloads() + # noinspection PyUnusedLocal def _on_savepath(self, event): dlg = wx.DirDialog(self, self.CHOOSE_DIRECTORY, self._path_combobox.GetStringSelection()) @@ -687,6 +703,7 @@ def _on_savepath(self, event): dlg.Destroy() + # noinspection PyUnusedLocal def _on_add(self, event): urls = self._get_urls() @@ -706,13 +723,13 @@ def _on_add(self, event): self._status_list.bind_item(download_item) self._download_list.insert(download_item) - def _on_settings(self, event): event_object_pos = event.EventObject.GetPosition() event_object_height = event.EventObject.GetSize()[1] event_object_pos = (event_object_pos[0], event_object_pos[1] + event_object_height) self.PopupMenu(self._settings_menu, event_object_pos) + # noinspection PyUnusedLocal def _on_viewlog(self, event): if self.log_manager is None: self._create_popup(_("Logging is disabled"), @@ -723,6 +740,7 @@ def _on_viewlog(self, event): log_window.load(self.log_manager.log_file) log_window.Show() + # noinspection PyUnusedLocal def _on_about(self, event): info = wx.adv.AboutDialogInfo() @@ -741,7 +759,8 @@ def _on_about(self, event): wx.adv.AboutBox(info) - def _set_publisher(self, handler, topic): + @staticmethod + def _set_publisher(handler, topic): """Sets a handler for the given topic. Args: @@ -796,7 +815,8 @@ def win_ctrla_eventhandler(event): return textctrl - def _create_popup(self, text, title, style): + @staticmethod + def _create_popup(text, title, style): wx.MessageBox(text, title, style) def _set_layout(self): @@ -819,7 +839,8 @@ def _set_layout(self): mid_sizer.AddSpacer(5) mid_sizer.Add(self._buttons["savepath"], flag=wx.ALIGN_CENTER_VERTICAL) mid_sizer.AddSpacer(10) - mid_sizer.Add(self._videoformat_combobox, 1, wx.ALIGN_CENTER_VERTICAL) + mid_sizer.AddStretchSpacer(1) + mid_sizer.Add(self._videoformat_combobox, 0, wx.ALIGN_CENTER_VERTICAL) mid_sizer.AddSpacer(5) mid_sizer.Add(self._buttons["add"], flag=wx.ALIGN_CENTER_VERTICAL) panel_sizer.Add(mid_sizer, 0, wx.EXPAND | wx.ALL, 10) @@ -909,6 +930,7 @@ def _after_download(self): if self.opt_manager.options["show_completion_popup"]: self._create_popup(self.DL_COMPLETED_MSG, self.INFO_LABEL, wx.OK | wx.ICON_INFORMATION) + # noinspection PyUnusedLocal,PyProtectedMember def _download_worker_handler(self, signal, data=None): """downloadmanager.Worker thread handler. @@ -918,13 +940,13 @@ def _download_worker_handler(self, signal, data=None): See downloadmanager.Worker _talk_to_gui() method. """ - # signal, data = msg.data download_item = self._download_list.get_item(data["index"]) download_item.update_stats(data) row = self._download_list.index(data["index"]) self._status_list._update_from_item(row, download_item) + # noinspection PyUnusedLocal def _download_manager_handler(self, signal, data=None): """downloadmanager.DownloadManager thread handler. @@ -1029,6 +1051,7 @@ def _on_urllist_edit(self, event): self._paste_from_clipboard() wx.TheClipboard.UsePrimarySelection(False) + # noinspection PyUnusedLocal def _on_update(self, event): """Event handler of the self._update_btn widget. @@ -1040,12 +1063,14 @@ def _on_update(self, event): """ if self.opt_manager.options["disable_update"]: - self._create_popup(_("Updates are disabled for your system. Please use the system's package manager to update youtube-dl."), + self._create_popup(_("Updates are disabled for your system. " + "Please use the system's package manager to update youtube-dl."), self.INFO_LABEL, wx.OK | wx.ICON_INFORMATION) else: self._update_youtubedl() + # noinspection PyUnusedLocal def _on_options(self, event): """Event handler of the self._options_btn widget. @@ -1056,6 +1081,7 @@ def _on_options(self, event): self._options_frame.load_all_options() self._options_frame.Show() + # noinspection PyUnusedLocal def _on_close(self, event): """Event handler for the wx.EVT_CLOSE event. @@ -1162,7 +1188,6 @@ def bind_item(self, download_item): def GetItemData(self, row_index_selected): return self._map_id.get(row_index_selected, None) - def _update_from_item(self, row, download_item): progress_stats = download_item.progress_stats @@ -1239,6 +1264,7 @@ def _set_columns(self): # REFACTOR Extra widgets below should move to other module with widgets +# noinspection PyPep8Naming class ExtComboBox(wx.ComboBox): def __init__(self, parent, max_items=-1, *args, **kwargs): @@ -1504,6 +1530,7 @@ def __init__(self, parent, timeout, message, *args, **kwargs): def _get_message(self): return self.message.format(self.timeout) + # noinspection PyUnusedLocal def _on_timer(self, event): self.timeout -= 1 self.msg_text.SetLabel(self._get_message()) diff --git a/youtube_dl_gui/optionsframe.py b/youtube_dl_gui/optionsframe.py index 2f72598d..3e4bd2a0 100644 --- a/youtube_dl_gui/optionsframe.py +++ b/youtube_dl_gui/optionsframe.py @@ -11,6 +11,7 @@ from wx.lib.art import flagart from wx.lib.embeddedimage import PyEmbeddedImage +# noinspection PyPep8Naming from .utils import ( TwoWayOrderedDict as twodict, os_path_exists, @@ -109,6 +110,7 @@ def _set_layout(self): self.panel.Layout() + # noinspection PyProtectedMember,PyUnusedLocal def _on_close(self, event): """Event handler for wx.EVT_CLOSE event.""" self.save_all_options() @@ -116,6 +118,7 @@ def _on_close(self, event): self.GetParent()._update_videoformat_combobox() self.Hide() + # noinspection PyUnusedLocal def _on_reset(self, event): """Event handler for the reset button wx.EVT_BUTTON event.""" self.reset() @@ -327,10 +330,12 @@ def __init__(self, *args, **kwargs): super(GeneralTab, self).__init__(*args, **kwargs) self.language_label = self.crt_statictext(_("Language")) - self.language_combobox = self.crt_bitmap_combobox(list(self.LOCALE_NAMES.items()), event_handler=self._on_language) + self.language_combobox = self.crt_bitmap_combobox(list(self.LOCALE_NAMES.items()), + event_handler=self._on_language) self.filename_format_label = self.crt_statictext(_("Filename format")) - self.filename_format_combobox = self.crt_combobox(list(OUTPUT_FORMATS.values()), event_handler=self._on_filename) + self.filename_format_combobox = self.crt_combobox(list(OUTPUT_FORMATS.values()), + event_handler=self._on_filename) self.filename_custom_format = self.crt_textctrl() self.filename_custom_format_button = self.crt_button("...", self._on_format) @@ -342,7 +347,8 @@ def __init__(self, *args, **kwargs): self.confirm_deletion_checkbox = self.crt_checkbox(_("Confirm item deletion")) self.show_completion_popup_checkbox = self.crt_checkbox(_("Inform me on download completion")) - self.shutdown_checkbox = self.crt_checkbox(_("Shutdown on download completion"), event_handler=self._on_shutdown) + self.shutdown_checkbox = self.crt_checkbox(_("Shutdown on download completion"), + event_handler=self._on_shutdown) self.sudo_textctrl = self.crt_textctrl(wx.TE_PASSWORD) # Build the menu for the custom format button @@ -429,6 +435,7 @@ def _on_format(self, event): event_object_pos = (event_object_pos[0], event_object_pos[1] + event_object_height) self.PopupMenu(self.custom_format_menu, event_object_pos) + # noinspection PyUnusedLocal def _on_language(self, event): """Event handler for the wx.EVT_COMBOBOX of the language_combobox.""" wx.MessageBox(_("In order for the changes to take effect please restart {0}").format(__appname__), @@ -436,6 +443,7 @@ def _on_language(self, event): wx.OK | wx.ICON_INFORMATION, self) + # noinspection PyUnusedLocal def _on_filename(self, event): """Event handler for the wx.EVT_COMBOBOX of the filename_format_combobox.""" custom_selected = self.filename_format_combobox.GetValue() == OUTPUT_FORMATS[3] @@ -443,6 +451,7 @@ def _on_filename(self, event): self.filename_custom_format.Enable(custom_selected) self.filename_custom_format_button.Enable(custom_selected) + # noinspection PyUnusedLocal def _on_shutdown(self, event): """Event handler for the wx.EVT_CHECKBOX of the shutdown_checkbox.""" self.sudo_textctrl.Enable(self.shutdown_checkbox.GetValue()) @@ -458,10 +467,10 @@ def load_options(self): self.show_completion_popup_checkbox.SetValue(self.opt_manager.options["show_completion_popup"]) self.confirm_deletion_checkbox.SetValue(self.opt_manager.options["confirm_deletion"]) - #REFACTOR Automatically call on the new methods - #save_options - #load_options - #NOTE Maybe on init add callback? + # REFACTOR Automatically call on the new methods + # save_options + # load_options + # NOTE Maybe on init add callback? self._on_filename(None) self._on_shutdown(None) @@ -528,9 +537,11 @@ def _set_layout(self): self.SetSizer(main_sizer) def load_options(self): - checked_video_formats = [VIDEO_FORMATS[vformat] for vformat in self.opt_manager.options["selected_video_formats"]] + checked_video_formats = [VIDEO_FORMATS[vformat] + for vformat in self.opt_manager.options["selected_video_formats"]] self.video_formats_checklistbox.SetCheckedStrings(checked_video_formats) - checked_audio_formats = [AUDIO_FORMATS[aformat] for aformat in self.opt_manager.options["selected_audio_formats"]] + checked_audio_formats = [AUDIO_FORMATS[aformat] + for aformat in self.opt_manager.options["selected_audio_formats"]] self.audio_formats_checklistbox.SetCheckedStrings(checked_audio_formats) self.keep_video_checkbox.SetValue(self.opt_manager.options["keep_video"]) self.audio_quality_combobox.SetValue(self.AUDIO_QUALITY[self.opt_manager.options["audio_quality"]]) @@ -539,9 +550,11 @@ def load_options(self): self.add_metadata_checkbox.SetValue(self.opt_manager.options["add_metadata"]) def save_options(self): - checked_video_formats = [VIDEO_FORMATS[vformat] for vformat in self.video_formats_checklistbox.GetCheckedStrings()] + checked_video_formats = [VIDEO_FORMATS[vformat] + for vformat in self.video_formats_checklistbox.GetCheckedStrings()] self.opt_manager.options["selected_video_formats"] = checked_video_formats - checked_audio_formats = [AUDIO_FORMATS[aformat] for aformat in self.audio_formats_checklistbox.GetCheckedStrings()] + checked_audio_formats = [AUDIO_FORMATS[aformat] + for aformat in self.audio_formats_checklistbox.GetCheckedStrings()] self.opt_manager.options["selected_audio_formats"] = checked_audio_formats self.opt_manager.options["keep_video"] = self.keep_video_checkbox.GetValue() self.opt_manager.options["audio_quality"] = self.AUDIO_QUALITY[self.audio_quality_combobox.GetValue()] @@ -675,12 +688,13 @@ def _build_filesize_sizer(self): return filesize_box_sizer + # noinspection PyUnusedLocal def _on_subtitles(self, event): """Event handler for the wx.EVT_COMBOBOX of the subtitles_combobox.""" self.subtitles_lang_listbox.Enable(self.subtitles_combobox.GetValue() == self.SUBS_CHOICES[-1]) def load_options(self): - #NOTE Find a better way to do this + # NOTE Find a better way to do this if self.opt_manager.options["write_subs"]: self.subtitles_combobox.SetValue(self.SUBS_CHOICES[3]) elif self.opt_manager.options["write_all_subs"]: @@ -830,6 +844,7 @@ def _set_layout(self): main_sizer.Add(vertical_sizer, 1, wx.EXPAND | wx.ALL, border=5) self.SetSizer(main_sizer) + # noinspection PyUnusedLocal def _on_enable_log(self, event): """Event handler for the wx.EVT_CHECKBOX of the enable_log_checkbox.""" wx.MessageBox(_("In order for the changes to take effect please restart {0}").format(__appname__), @@ -837,12 +852,14 @@ def _on_enable_log(self, event): wx.OK | wx.ICON_INFORMATION, self) + # noinspection PyUnusedLocal def _on_view(self, event): """Event handler for the wx.EVT_BUTTON of the view_log_button.""" log_window = LogGUI(self) log_window.load(self.log_manager.log_file) log_window.Show() + # noinspection PyUnusedLocal def _on_clear(self, event): """Event handler for the wx.EVT_BUTTON of the clear_log_button.""" if self.log_manager is not None: @@ -931,16 +948,17 @@ def save_options(self): class LogGUI(wx.Frame): + # noinspection PyUnresolvedReferences """Simple window for reading the STDERR. - Attributes: - TITLE (string): Frame title. - FRAME_SIZE (tuple): Tuple that holds the frame size (width, height). + Attributes: + TITLE (string): Frame title. + FRAME_SIZE (tuple): Tuple that holds the frame size (width, height). - Args: - parent (wx.Window): Frame parent. + Args: + parent (wx.Window): Frame parent. - """ + """ # REFACTOR move it on widgets module diff --git a/youtube_dl_gui/optionsmanager.py b/youtube_dl_gui/optionsmanager.py index 9533fd89..998d62c3 100644 --- a/youtube_dl_gui/optionsmanager.py +++ b/youtube_dl_gui/optionsmanager.py @@ -2,7 +2,6 @@ """Youtubedlg module to handle settings. """ - import os import json @@ -22,30 +21,30 @@ class OptionsManager(object): - + # noinspection PyUnresolvedReferences """Handles youtubedlg options. - This class is responsible for storing and retrieving the options. + This class is responsible for storing and retrieving the options. - Attributes: - SETTINGS_FILENAME (string): Filename of the settings file. - SENSITIVE_KEYS (tuple): Contains the keys that we don't want - to store on the settings file. (SECURITY ISSUES). + Attributes: + SETTINGS_FILENAME (string): Filename of the settings file. + SENSITIVE_KEYS (tuple): Contains the keys that we don't want + to store on the settings file. (SECURITY ISSUES). - Args: - config_path (string): Absolute path where OptionsManager - should store the settings file. + Args: + config_path (string): Absolute path where OptionsManager + should store the settings file. - Note: - See load_default() method for available options. + Note: + See load_default() method for available options. - Example: - Access the options using the 'options' variable. + Example: + Access the options using the 'options' variable. - opt_manager = OptionsManager('.') - opt_manager.options['save_path'] = '~/Downloads' + opt_manager = OptionsManager('.') + opt_manager.options['save_path'] = '~/Downloads' - """ + """ SETTINGS_FILENAME = 'settings.json' SENSITIVE_KEYS = ('sudo_password', 'password', 'video_password') @@ -233,7 +232,7 @@ def load_default(self): disable_update (boolean): When True the update process will be disabled. """ - #REFACTOR Remove old options & check options validation + # REFACTOR Remove old options & check options validation self.options = { 'save_path': os_path_expanduser('~'), 'save_path_dirs': [ @@ -318,7 +317,7 @@ def load_from_file(self): if self._settings_are_valid(options): self.options = options - except: + except json.JSONDecodeError: self.load_default() def save_to_file(self): @@ -343,10 +342,12 @@ def _settings_are_valid(self, settings_dictionary): True if settings.json dictionary is valid, else False. """ - VALID_VIDEO_FORMAT = ('0', '17', '36', '5', '34', '35', '43', '44', '45', - '46', '18', '22', '37', '38', '160', '133', '134', '135', '136','137', + VALID_VIDEO_FORMAT = ( + '0', '17', '36', '5', '34', '35', '43', '44', '45', + '46', '18', '22', '37', '38', '160', '133', '134', '135', '136', '137', '264', '138', '242', '243', '244', '247', '248', '271', '272', '82', - '83', '84', '85', '100', '101', '102', '139', '140', '141', '171', '172') + '83', '84', '85', '100', '101', '102', '139', '140', '141', '171', '172' + ) VALID_AUDIO_FORMAT = ('mp3', 'wav', 'aac', 'm4a', 'vorbis', 'opus', 'flac', '') @@ -366,7 +367,7 @@ def _settings_are_valid(self, settings_dictionary): if key not in settings_dictionary: return False - if type(self.options[key]) != type(settings_dictionary[key]): + if not isinstance(self.options[key], type(settings_dictionary[key])): return False # Check if each key has a valid value @@ -412,4 +413,3 @@ def _get_options(self): temp_options['opts_win_size'] = encode_tuple(temp_options['opts_win_size']) return temp_options - diff --git a/youtube_dl_gui/updatemanager.py b/youtube_dl_gui/updatemanager.py index 3682dabe..b8254c05 100644 --- a/youtube_dl_gui/updatemanager.py +++ b/youtube_dl_gui/updatemanager.py @@ -16,6 +16,7 @@ from urllib.error import URLError, HTTPError from wx import CallAfter +# noinspection PyPep8Naming from pubsub import pub as Publisher @@ -29,21 +30,22 @@ class UpdateThread(Thread): + # noinspection PyUnresolvedReferences """Python Thread that downloads youtube-dl binary. - Attributes: - LATEST_YOUTUBE_DL (string): URL with the latest youtube-dl binary. - DOWNLOAD_TIMEOUT (int): Download timeout in seconds. + Attributes: + LATEST_YOUTUBE_DL (string): URL with the latest youtube-dl binary. + DOWNLOAD_TIMEOUT (int): Download timeout in seconds. - Args: - download_path (string): Absolute path where UpdateThread will download - the latest youtube-dl. + Args: + download_path (string): Absolute path where UpdateThread will download + the latest youtube-dl. - quiet (boolean): If True UpdateThread won't send the finish signal - back to the caller. Finish signal can be used to make sure that - the UpdateThread has been completed in an asynchronous way. + quiet (boolean): If True UpdateThread won't send the finish signal + back to the caller. Finish signal can be used to make sure that + the UpdateThread has been completed in an asynchronous way. - """ + """ LATEST_YOUTUBE_DL = 'https://yt-dl.org/latest/' GITHUB_API = "https://api.github.com/" @@ -96,7 +98,8 @@ def run(self): if not self.quiet: self._talk_to_gui('finish') - def _talk_to_gui(self, signal, data=None): + @staticmethod + def _talk_to_gui(signal, data=None): """Communicate with the GUI using wxCallAfter and wxPublisher. Args: diff --git a/youtube_dl_gui/utils.py b/youtube_dl_gui/utils.py index 07fccd62..8bbd900b 100644 --- a/youtube_dl_gui/utils.py +++ b/youtube_dl_gui/utils.py @@ -11,7 +11,6 @@ import os import sys -import json import math import locale import subprocess @@ -23,7 +22,6 @@ sys.exit(1) from .info import __appname__ -from .version import __version__ _RANDOM_OBJECT = object() diff --git a/youtube_dl_gui/widgets.py b/youtube_dl_gui/widgets.py index 51b6ebe8..3bd3f397 100644 --- a/youtube_dl_gui/widgets.py +++ b/youtube_dl_gui/widgets.py @@ -16,17 +16,18 @@ def crt_command_event(event_type, event_id=0): class ListBoxWithHeaders(wx.ListBox): + # noinspection PyUnresolvedReferences """Custom ListBox object that supports 'headers'. - Attributes: - NAME (string): Default name for the name argument of the __init__. + Attributes: + NAME (string): Default name for the name argument of the __init__. - TEXT_PREFIX (string): Text to add before normal items in order to - distinguish them (normal items) from headers. + TEXT_PREFIX (string): Text to add before normal items in order to + distinguish them (normal items) from headers. - EVENTS (list): List with events to overwrite to avoid header selection. + EVENTS (list): List with events to overwrite to avoid header selection. - """ + """ NAME = "listBoxWithHeaders" @@ -42,8 +43,10 @@ class ListBoxWithHeaders(wx.ListBox): ] def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, - size=wx.DefaultSize, choices=[], style=0, validator=wx.DefaultValidator, name=NAME): + size=wx.DefaultSize, choices=None, style=0, validator=wx.DefaultValidator, name=NAME): super(ListBoxWithHeaders, self).__init__(parent, id, pos, size, [], style, validator, name) + if choices is None: + choices = [] self.__headers = set() # Ignore all key events i'm bored to handle the header selection @@ -51,8 +54,8 @@ def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, # Make sure that a header is never selected self.Bind(wx.EVT_LISTBOX, self._on_listbox) - for event in self.EVENTS: - self.Bind(event, self._disable_header_selection) + for _event in self.EVENTS: + self.Bind(_event, self._disable_header_selection) # Append the items in our own way in order to add the TEXT_PREFIX self.AppendItems(choices) @@ -83,12 +86,12 @@ def _remove_prefix(self, string): # wx.ListBox methods - def FindString(self, string): - index = super(ListBoxWithHeaders, self).FindString(string) + def FindString(self, string, **kwargs): + index = super(ListBoxWithHeaders, self).FindString(string, **kwargs) if index == wx.NOT_FOUND: # This time try with prefix - index = super(ListBoxWithHeaders, self).FindString(self._add_prefix(string)) + index = super(ListBoxWithHeaders, self).FindString(self._add_prefix(string), **kwargs) return index @@ -127,7 +130,7 @@ def SetStringSelection(self, string): if string in self.__headers: return False - self.SetSelection(self.FindString(string)) + self.SetSelection(self.FindString(string, )) return True # wx.ItemContainer methods @@ -167,17 +170,19 @@ def add_items(self, items, with_prefix=True): super(ListBoxWithHeaders, self).AppendItems(items) +# noinspection PyPep8Naming class ListBoxPopup(wx.PopupTransientWindow): + # noinspection PyUnresolvedReferences """ListBoxWithHeaders as a popup. - This class uses the wx.PopupTransientWindow to create the popup and the - API is based on the wx.combo.ComboPopup class. + This class uses the wx.PopupTransientWindow to create the popup and the + API is based on the wx.combo.ComboPopup class. - Attributes: - EVENTS_TABLE (dict): Dictionary that contains all the events - that this class emits. + Attributes: + EVENTS_TABLE (dict): Dictionary that contains all the events + that this class emits. - """ + """ EVENTS_TABLE = { "EVT_COMBOBOX": crt_command_event(wx.EVT_COMBOBOX), @@ -198,6 +203,7 @@ def _on_motion(self, event): if self.__listbox.IsSelected(row): self.curitem = row + # noinspection PyUnusedLocal def _on_left_down(self, event): self.value = self.curitem self.Dismiss() @@ -205,8 +211,8 @@ def _on_left_down(self, event): # Send EVT_COMBOBOX to inform the parent for changes wx.PostEvent(self, self.EVENTS_TABLE["EVT_COMBOBOX"]) - def Popup(self): - super(ListBoxPopup, self).Popup() + def Popup(self, **kwargs): + super(ListBoxPopup, self).Popup(**kwargs) wx.PostEvent(self, self.EVENTS_TABLE["EVT_COMBOBOX_DROPDOWN"]) def OnDismiss(self): @@ -214,10 +220,11 @@ def OnDismiss(self): # wx.combo.ComboPopup methods + # noinspection PyAttributeOutsideInit def Init(self): self.value = self.curitem = -1 - def Create(self, parent): + def Create(self, parent, **kwargs): self.__listbox = ListBoxWithHeaders(parent, style=wx.LB_SINGLE) self.__listbox.Bind(wx.EVT_MOTION, self._on_motion) @@ -252,16 +259,18 @@ def GetStringValue(self): # self.__listbox.SetStringSelection(string) +# noinspection PyPep8Naming class CustomComboBox(wx.Panel): + # noinspection PyUnresolvedReferences """Custom combobox. - Attributes: - CB_READONLY (long): Read-only style. The only one supported from the - wx.ComboBox styles. + Attributes: + CB_READONLY (long): Read-only style. The only one supported from the + wx.ComboBox styles. - NAME (string): Default name for the name argument of the __init__. + NAME (string): Default name for the name argument of the __init__. - """ + """ # NOTE wx.ComboBox does not support EVT_MOTION inside the popup # NOTE Tried with ComboCtrl but i was not able to draw the button @@ -270,9 +279,11 @@ class CustomComboBox(wx.Panel): NAME = "customComboBox" def __init__(self, parent, id=wx.ID_ANY, value="", pos=wx.DefaultPosition, - size=wx.DefaultSize, choices=[], style=0, validator=wx.DefaultValidator, name=NAME): + size=wx.DefaultSize, choices=None, style=0, validator=wx.DefaultValidator, name=NAME): super(CustomComboBox, self).__init__(parent, id, pos, size, 0, name) + if choices is None: + choices = [] assert style == self.CB_READONLY or style == 0 # Create components @@ -284,7 +295,7 @@ def __init__(self, parent, id=wx.ID_ANY, value="", pos=wx.DefaultPosition, # Create the ListBoxPopup in two steps self.listbox = ListBoxPopup(self) self.listbox.Init() - self.listbox.Create(self.listbox) + self.listbox.Create(self.listbox, ) # Set layout sizer = wx.BoxSizer() @@ -308,6 +319,7 @@ def _propagate(self, event): wx.PostEvent(self, event) + # noinspection PyUnusedLocal def _on_button(self, event): self.Popup() @@ -334,9 +346,10 @@ def _calc_popup_size(self): def Dismiss(self): self.listbox.Dismiss() + # noinspection PyUnusedLocal def FindString(self, string, caseSensitive=False): # TODO handle caseSensitive - return self.listbox.GetControl().FindString(string) + return self.listbox.GetControl().FindString(string, ) def GetCount(self): return self.listbox.GetControl().GetCount() @@ -384,7 +397,7 @@ def SetTextSelection(self, from_, to_): self.textctrl.SetSelection(from_, to_) def SetStringSelection(self, string): - index = self.listbox.GetControl().FindString(string) + index = self.listbox.GetControl().FindString(string, ) self.listbox.GetControl().SetSelection(index) if index != wx.NOT_FOUND and self.listbox.GetControl().GetSelection() == index: From 0715907f2cc6fcfae206d0267e8007f653c450b7 Mon Sep 17 00:00:00 2001 From: oleksis <“oleksis.fraga@gmail.com”> Date: Mon, 3 Aug 2020 13:36:43 -0400 Subject: [PATCH 13/34] Fix bug subprocess for Windows --- ChangeLog | 1 + README.md | 6 +++--- tests/test_ditem.py | 1 - tests/test_dlist.py | 1 - tests/test_parsers.py | 1 - tests/test_utils.py | 1 - tests/test_widgets.py | 1 - youtube_dl_gui/downloaders.py | 17 ++++++----------- youtube_dl_gui/version.py | 2 +- 9 files changed, 11 insertions(+), 20 deletions(-) diff --git a/ChangeLog b/ChangeLog index aac35487..eedc4c39 100644 --- a/ChangeLog +++ b/ChangeLog @@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) ### Fixed - Imports for Internationalization with gettext +- Fix bug subprocess for Windows ([WinError 6] Handler no valid) ### Changed - Migration to Python 3.* diff --git a/README.md b/README.md index ea15cc63..d3a7c023 100644 --- a/README.md +++ b/README.md @@ -19,14 +19,14 @@ A cross platform front-end GUI of the popular [youtube-dl](https://rg3.github.io * [GNU gettext](https://www.gnu.org/software/gettext/) ## Downloads -* [Source (.zip)](https://github.com/oleksis/youtube-dl-gui/archive/v1.0.0-alpha.zip) -* [Source (.tar.gz)](https://github.com/oleksis/youtube-dl-gui/archive/v1.0.0-alpha.tar.gz) +* [Source (.zip)](https://github.com/oleksis/youtube-dl-gui/archive/v1.0.1-alpha.zip) +* [Source (.tar.gz)](https://github.com/oleksis/youtube-dl-gui/archive/v1.0.1-alpha.tar.gz) ## Installation ### Install From Source * Download & extract the source -* Change directory into *youtube-dl-gui-1.0.0-alpha* +* Change directory into *youtube-dl-gui-1.0.1-alpha* * Create virtual environment ``` python3 -m venv env diff --git a/tests/test_ditem.py b/tests/test_ditem.py index 27ddd4e6..fe773592 100644 --- a/tests/test_ditem.py +++ b/tests/test_ditem.py @@ -3,7 +3,6 @@ """Contains test cases for the DownloadItem object.""" -from __future__ import unicode_literals import sys import os.path diff --git a/tests/test_dlist.py b/tests/test_dlist.py index f1e34c5e..e2cb21ac 100644 --- a/tests/test_dlist.py +++ b/tests/test_dlist.py @@ -3,7 +3,6 @@ """Contains test cases for the DownloadList object.""" -from __future__ import unicode_literals import sys import os.path diff --git a/tests/test_parsers.py b/tests/test_parsers.py index fc70e3e3..da4ebe2f 100644 --- a/tests/test_parsers.py +++ b/tests/test_parsers.py @@ -3,7 +3,6 @@ """Contains test cases for the parsers module.""" -from __future__ import unicode_literals import sys import os.path diff --git a/tests/test_utils.py b/tests/test_utils.py index 09c47183..98439771 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -3,7 +3,6 @@ """Contains test cases for the utils.py module.""" -from __future__ import unicode_literals import sys import os.path diff --git a/tests/test_widgets.py b/tests/test_widgets.py index 70b1c7cb..66c57bf0 100644 --- a/tests/test_widgets.py +++ b/tests/test_widgets.py @@ -3,7 +3,6 @@ """Contains test cases for the widgets.py module.""" -from __future__ import unicode_literals import sys import os.path diff --git a/youtube_dl_gui/downloaders.py b/youtube_dl_gui/downloaders.py index 5540f52b..95e7f2f9 100644 --- a/youtube_dl_gui/downloaders.py +++ b/youtube_dl_gui/downloaders.py @@ -318,19 +318,13 @@ def _create_process(self, cmd): cmd (list): Python list that contains the command to execute. """ - info = preexec = None - - # Keep a unicode copy of cmd for the log - # ucmd = cmd + info = None if os.name == 'nt': # Hide subprocess window info = subprocess.STARTUPINFO() info.dwFlags |= subprocess.STARTF_USESHOWWINDOW - else: - # Make subprocess the process group leader - # in order to kill the whole process group with os.killpg - preexec = os.setsid + info.wShowWindow = subprocess.SW_HIDE # Encode command for subprocess # Refer to http://stackoverflow.com/a/9951851/35070 @@ -339,10 +333,11 @@ def _create_process(self, cmd): try: self._proc = subprocess.Popen(cmd, + stdin=subprocess.PIPE, stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - preexec_fn=preexec, - startupinfo=info) + stderr=subprocess.STDOUT, + startupinfo=info, + start_new_session=True) except (ValueError, OSError, FileNotFoundError) as error: self._log('Failed to start process: {}'.format(str(cmd))) self._log(str(error)) diff --git a/youtube_dl_gui/version.py b/youtube_dl_gui/version.py index cd9ba077..de094a2a 100644 --- a/youtube_dl_gui/version.py +++ b/youtube_dl_gui/version.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- -__version__ = '1.0.0' +__version__ = '1.0.1' From cdb2ff020e5e953ca0e5853a5ecebec59529f33f Mon Sep 17 00:00:00 2001 From: oleksis <“oleksis.fraga@gmail.com”> Date: Wed, 5 Aug 2020 12:04:44 -0400 Subject: [PATCH 14/34] Add Logging system using logging.handlers.RotatingFileHandler Add flagart module --- ChangeLog | 5 +- README.md | 6 +- TODO | 3 +- youtube_dl_gui/downloadmanager.py | 21 +- youtube_dl_gui/flagart.py | 1596 +++++++++++++++++++++++++++++ youtube_dl_gui/logmanager.py | 58 +- youtube_dl_gui/optionsframe.py | 27 +- youtube_dl_gui/version.py | 2 +- 8 files changed, 1642 insertions(+), 76 deletions(-) create mode 100644 youtube_dl_gui/flagart.py diff --git a/ChangeLog b/ChangeLog index eedc4c39..e275259d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -10,7 +10,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) - ChangeLog - Man page - Add locale (.mo) es_CU -- Add Flag art for Cuba +- Add flagart module + - Add Flag art for Cuba +- Add Logging system using the Python 'logging' module ### Fixed - Imports for Internationalization with gettext @@ -24,4 +26,3 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) - Build the binaries using PyInstaller. Fix path sep for each OS - Build with updates disabled - Set up for Windows/Linux -- Ignore wxPyDeprecationWarning diff --git a/README.md b/README.md index d3a7c023..0edc949c 100644 --- a/README.md +++ b/README.md @@ -19,14 +19,14 @@ A cross platform front-end GUI of the popular [youtube-dl](https://rg3.github.io * [GNU gettext](https://www.gnu.org/software/gettext/) ## Downloads -* [Source (.zip)](https://github.com/oleksis/youtube-dl-gui/archive/v1.0.1-alpha.zip) -* [Source (.tar.gz)](https://github.com/oleksis/youtube-dl-gui/archive/v1.0.1-alpha.tar.gz) +* [Source (.zip)](https://github.com/oleksis/youtube-dl-gui/archive/v1.1.0-alpha.zip) +* [Source (.tar.gz)](https://github.com/oleksis/youtube-dl-gui/archive/v1.1.0-alpha.tar.gz) ## Installation ### Install From Source * Download & extract the source -* Change directory into *youtube-dl-gui-1.0.1-alpha* +* Change directory into *youtube-dl-gui-1.1.0-alpha* * Create virtual environment ``` python3 -m venv env diff --git a/TODO b/TODO index f4464195..be2da04f 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,4 @@ -Release 1.0.1 +Release 1.2.0 ============= * Intergrity check youtube-dl bin * Non-Windows shutdown using D-Bus instead of 'shutdown' @@ -29,7 +29,6 @@ Other * Refactor to Python3 * Review - rewrite threads communications -* Logging system using the Python 'logging' module * Use youtube-dl directly from python instead of using the subprocess module diff --git a/youtube_dl_gui/downloadmanager.py b/youtube_dl_gui/downloadmanager.py index 88ff2e49..d040916e 100644 --- a/youtube_dl_gui/downloadmanager.py +++ b/youtube_dl_gui/downloadmanager.py @@ -24,8 +24,7 @@ from threading import ( Thread, - RLock, - Lock + RLock ) from wx import CallAfter @@ -379,10 +378,11 @@ def __init__(self, parent, download_list, opt_manager, log_manager=None): self._running = True # Init the custom workers thread pool - log_lock = None if log_manager is None else Lock() - wparams = (opt_manager, self._youtubedl_path(), log_manager, log_lock) - self._workers = [Worker(*wparams) for _ in range(opt_manager.options["workers_number"])] + wparams = (opt_manager, self._youtubedl_path(), log_manager) + self._workers = [Worker(*wparams, worker=worker) + for worker in range(1, int(opt_manager.options["workers_number"]) + 1)] + self.setName("DownloadManager") self.start() @property @@ -560,13 +560,18 @@ class Worker(Thread): WAIT_TIME = 0.1 - def __init__(self, opt_manager, youtubedl, log_manager=None, log_lock=None): + def __init__(self, opt_manager, youtubedl, log_manager=None, worker=None): super(Worker, self).__init__() # Use Daemon ? # self.setDaemon(True) self.opt_manager = opt_manager self.log_manager = log_manager - self.log_lock = log_lock + if worker: + self.worker = worker + else: + self.worker = 1 + + self.setName("Worker_" + str(worker)) self._downloader = YoutubeDLDownloader(youtubedl, self._data_hook, self._log_data) self._options_parser = OptionsParser() @@ -674,9 +679,7 @@ def _log_data(self, data): """ if self.log_manager is not None: - self.log_lock.acquire() self.log_manager.log(data) - self.log_lock.release() def _data_hook(self, data): """Callback method for self._downloader. diff --git a/youtube_dl_gui/flagart.py b/youtube_dl_gui/flagart.py new file mode 100644 index 00000000..b76b32dd --- /dev/null +++ b/youtube_dl_gui/flagart.py @@ -0,0 +1,1596 @@ +#Boa:PyImgResource: + +#---------------------------------------------------------------------- +# This file was generated by famfamfam_flags.py +# + +""" +See: http://www.famfamfam.com/lab/icons/flags + +FamFamFam Flag icons are available free for any purpose +Copyright Mark James (mjames@gmail.com) + +This module is (c) 2006 Riaan Booysen and licensed under the Python license. + +""" + +from wx.lib.embeddedimage import PyEmbeddedImage + +catalog = {} +index = [] + +index.append('AE') +catalog['AE'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAAA3UlE" + "QVQokYWOsU0DUBBD3ye/pKOKBF2YIiUd89BFTMEUabNJNiASTIBozr47igQBRcjTybrClj2a" + "HwoWz2AwFAQY4s8zATYboKtG1dNjujLLbrvsktIuqazU7mU3Aeh+eyeTzNcPZaZaSqmkVGRE" + "hVJ313enhq4+ulvaPmz7DGOMw/1hFoxM7LaRz7m7G6iqCeBs6XJgDNsT6HTbrfg/MLqBWTCk" + "jsDuiH/2dHdVzYKF3RJSS+v1OiIknVRShC07V6vVfr+fBVcyy2VLOG+tI7aPaju/AcYn1K+7" + "4QJfesZpBoG4DtcAAAAASUVORK5CYII=") + +index.append('AF') +catalog['AF'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABbUlE" + "QVQokVWQvU5VYRRE5+ABAX8KCQIlFeEFKKkt7WngNSzvG1hb8gR0xqixMrHSwoiJMcZOIMHk" + "JtyzZ2Z/37YgJroy7SpmDfv7+/P5HAAASb+urvrycrcBJADg2SaeHwMBCADG6+vr2WwGoPXW" + "W4c9ZLtjlb1id+npbnnvRk1sPH1xOo7jCODi8jLtzCy7pKJKLLKTv+/Wt+05HdvrOxDG3ntr" + "LW1nWiq7gH5yXC3rzbv68N6uyZNMJRFYkpSZskVKKrKOjvT2tc7O+uFhD5qkOWWwCQuMsVhk" + "piXJZJTU11bz4ABTDKsrFZHRp5zCZBKB8SbCtiSSJLvUfnxf2trp9+7n+ddhsbARGUoxCWFE" + "le2IuHV6RL18FY83y7ny+RxkqmhGhrshjLf5JUUEyYqoL+drHz91skc00kS0SS3d/gokH21s" + "3J4GBXGQBnKwl6SHW9h9sOF09sQCA/7nJ9CB/GdPgIs9IIAE1vEH+0VpUzSLjT4AAAAASUVO" + "RK5CYII=") + +index.append('AL') +catalog['AL'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABgElE" + "QVQokU3RP49NYRSF8XXe83IdF5mMiZhoFBKJRKX0MZCoVNOIZgqKaZSiEaLRjIbaF9AqNDq+" + "gj8Tk+u6M/fOefdae2/FkGif5il+3ecBNYACAFUQUYEROG4GCBAQQAAjUCuw+eI5gHSPiOq+" + "+2t5eyjF/fX+4f1p71InCzLIL6/eVAAAtPe9D387W90ZyvXl+Hi2lNndobP9ePZzsT0tMjt5" + "cdOAGoF0T/eUbvS582Mxtpa009LLg0ZrD4Y+jGEWooBSDOmeZIqfDo8uhYxcNvt6NLaxbbg+" + "LJbRLM3CWICCRLpCTOoKHOTDaddaa63tnC1rYVeTYc1bSxJADSAlUO681vnlSb6brSpN1t7P" + "Y2vSTZRhTBpIA4oBoJKm1gbZ073ftyY5H9t8bPdOdY++zQdZ0mAWrtXxIZxBCzOYPTnX9+LN" + "Pg9KnBF310+4WZLpCglABZBUv3GhkEGCFtT22vkkwxlSkUCPYLoC6D4CAOI/UQH2z/WvEmAA" + "gBH4A2X6QkAkcTlLAAAAAElFTkSuQmCC") + +index.append('AM') +catalog['AM'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABE0lE" + "QVQokVVPvU6bQRCc+3yV3VKgiJY3oMzTIapILtMktDwaEgqS9WEJEm7nJ8XZkKxWoxntzGq3" + "1fU11hWzbI9hEmNAMjAbZ2Kg43BY7u4AxEaykTY2yEiZKIEMCem433csC5D8eoYUGT6NQ6bq" + "FKtCVbu8JNCxrnEwN0mYjukmwwqZIlipMtAvtj9uefPEN8tkSJOq8r+kylX6UrufeO9j0LZo" + "0qLrP58os1ylKYE/fYzPKz5uqQrpqkhz/fwfANrv7+hfv+X1MWHMqD7RZVUsuGIuu6un+4fu" + "BTATxhV9ZCouT2nCjOkYHd1HZAibC4AAWyPaydEWtQhSLIN4p1e0lz3wBg+YZyR4JpiSgGEC" + "W/wFGOaHzQZ1JyAAAAAASUVORK5CYII=") + +index.append('AR') +catalog['AR'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABOUlE" + "QVQokU2RvU5VURCF1z5sDyihwBsMjYWVEUsTShqUxEfwAWxIKK2peR5ibLTz5w1oTLAmuZFo" + "LnKy78y3LM6B3NXMl8maZGZNeffDi9AoREOEQmpBwBA0aCMEfaf6N/T+2ei2XbCQwOmSrKW6" + "xOC00z75vqhDaImul8bCSjsRduDAYScTz9aLGvX06fzFk9kSWZKNZRls27LxiNgPinZezuvz" + "LWa9JNmWZdm3X3JxjqPbfOuNI9+pFO3P1I3nTr2xDp/hl/PS/z55VRJQV90aJ/oDDXOpeeNw" + "1V9soF78YW/LLW3rbt03fvQaG8zN5Ab11d/mWY9/Pv6wpqtbB7oPJO9TYkop0e5DnX3drh0U" + "dTvrU6BpJUpKWoGwQOmS2JJEefXx5nebHjkEQyhBgRoCjRxIKKRe/wH2AXDnJMhVrAAAAABJ" + "RU5ErkJggg==") + +index.append('AT') +catalog['AT'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAAA6UlE" + "QVQokX2RrW5CQRSEv21vatA3NwgcpjUoHgyN54EwvAGeF0C1glBca/b8TMXeFGhIJydfRkw2" + "Z+cUcVXe+4dTBKzXgDLJJEIehMsdd5nphl/bbQeA9P5BBBEyaxynmqyqVpmV2SyhY7nU6xv9" + "gFLuRChC7nK/Gvda68sw5G5XTqdT3/eAJITQH9VaGyeTyeVyaSuNqV/zWCCpu4Yevn+blzKz" + "2GJRViudz9xsLA/F3TeIKMPwudl0eTg87/c6HhlrGTsZ2YwbHsznQJfwZM50KjM8cMOsmOFO" + "o3trnIiE8n1/yH9u3PQDfNZaTYpscjsAAAAASUVORK5CYII=") + +index.append('AU') +catalog['AU'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAACC0lE" + "QVQokU3HT0hTcRwA8O/b3mhLndO5tmnPtuUi56YdYguzwrKB2SIiKOiUFR2joGSHLkXBQBE6" + "ZlHQJbBGBDGFKK1cRA0TTALdFhk129JtbHu/9/vbVfhcPtLHkdszxo7o1PpYMS5zfoWHr7f+" + "PLy3ffB+AapFAApABy8MJB6+BFgHwNJQZOxRtI/YFTSfEkiVdngsNsvrNPqy+JtRrmJyLhKQ" + "ZSmzupGYXXkyOi6lTCbnnbv6wl9SLnNNM9TXaVyqqThmPFCkEkZYcTaG97tjE3MupWny3rju" + "nf+4rqvH6OtkVhsorq3BIFHc6e3diIoT/R0A8D2bj03MRS/3Oax1ADVd8fQwWK0vuOtPvT1n" + "sMSbg3p/oGRuOXLQq9gbrg7vUxE5e8xnkKWjvW4ApPdLrfkGx0wWkRrKVnVTS6Xlit7C1efJ" + "X15Py2Ti21q+UthQmxqNT18trnx9r/+c6QwMdFeLlQwxZfAWmeJcvjKfI0yIZGp17V9FCHFm" + "qOtxfMFkNGQW3ugAKFBOKD91yHPzYggTRhkHzgimnHPBhKbR0QfJUrnGGQOgOgCMKRNChPa0" + "tTvNJ8O7NUwwoZhQTJiKSZvDPHKpVyOMMQFAZQDMOW9uNE1/SPfsss1++qE4zJrGmOCUChXh" + "a+dDvp3bNMyeTS8BIAkgAkABEADehG6ut//G8ttbABwA/gMK+Buk8wRxpgAAAABJRU5ErkJg" + "gg==") + +index.append('AZ') +catalog['AZ'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABbklE" + "QVQokU2QvWpUYRiE5zs5uyFrwEYkP0SzaqugtW1uIhewd2AVvIM0ptvgJWjjDSguqNgJWbtV" + "RBFt1MRE93zvzLwp1oDDw/DAdFPw6DXmxCIGaJggQIL+19UgUYm2aXHKvZ0hACecqUwasplJ" + "m0bItMMZymfjVy0GAyy1X85ChjLDljKkSIQznFWudshbl/ror5ZJKTcODuq372lBMpmkSZMZ" + "YdIRjkiyv7b2cjxu0TQgoUgJQc3/Xt3d7W1sHj9/0bu+dTyZ/JlOM2hGRm2AxpITlpMSuTy8" + "2dseft7fH9y7u3xte+X2HVEiRdlJoAFQFg9UllB39L6bfVwfjX4+eTqfzc7evC0dS2XpiEoD" + "bQUkSpGiGW7y6+PDrGHx1/TIEV5M5pJ5CrQ7DzG6rw+/FWJYVayqUaNCYVYzTIq0bl32u09o" + "cYIIrvevhEiTYpjRI82wKDEpSSl1wg8UPABOAAL1Av4nvBADBFZxDq7cZQ8Pjp14AAAAAElF" + "TkSuQmCC") + +index.append('BE') +catalog['BE'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAAA7klE" + "QVQokW2RQUqDYQxE36dVRFTEjXThxgt4IW/SW/U61V0FV4riKjNJXPT/WxWHIau8zEAGv/Xx" + "zkUAVFHX1Cd1S3HwAlitVkBVVdXZeXKV4NE+xkeX6ke31HZLX+v1AoDebl8yMzOrBNmtyane" + "REe0NO7upoSqzkxnWurO7uh2t7qjOzq0A5AmIDO9k3y4/R/ADrBT0g8gDkCpY6qEvU+w7VD8" + "BuY5J4x9JUkRYVsRfxNa+/Ntb+dK1qyp9377RB3RFk4yPSd4uVxKsnMMgcYQGITMvcncuWD8" + "+fTmmVjgoszNGfXG6wOA4RSe4BuvWmXvCEAfcgAAAABJRU5ErkJggg==") + +index.append('BG') +catalog['BG'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABB0lE" + "QVQokW1OMU5DUQzz+7wuldhYKhV1YeEenKkjCzfgMsyVmDgCqhg6dWEBFYnYcRh+Cx+EEzm2" + "nEhph8MBE9geeSq+IbsDmM/nAKpqvKka3WlMsN/v+2mppqL+Q2vNdt++bZfDMit/EpTLo/Sp" + "XJ4N/fnjueEO65v17n0nSxZNmkxGkhmRQTMUNFfnq839pgNQJYsqqUQzHDRZjIoo0hEVNFWC" + "0TEgB2lQVKiJjUREYyAIBoIDo4JFDkRHxyNW15y9hlJSKMkMiUpSIZEZMiUtL/S0Qf98wNml" + "/EKQRVYcHznyKEQo21XeAt3AQGGxKBJKiCAbCQkjS8gc20A7AJ40fts/EYAvPNBlhsfxqY8A" + "AAAASUVORK5CYII=") + +index.append('BH') +catalog['BH'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABIklE" + "QVQokV2RPW7CQBCFn5FbFzSxlIgbQJPchpvQkZQhSKRBSorkDjTcgaSDC9BQ2AqJTBDOzuy8" + "FOsfwdNoNSt92jezLyqKArWS5dKGQ3gfrgYYoHUTKiqKIkmSQNA88hxv78xyeKUqVO10oghV" + "KfK7WMQVSgLAeoPXF37t4RxFQtnxSOcoEvV6BnRaGsBgwPsHpGkg6ITOtSViQGxmzQ5crzF5" + "4s939bwTSktDBMEhmJBkv8/nGW+ua8idOagaEDc0AH5+4HHC/Z7iqnlEGodIxICOmbEWbu84" + "mzG94p+7oKFKVW12qPZerTidcrdr6XCqQH3IJ8qyrNvtViN5j+2W4zEPB9R/b2VJ7+E9vM83" + "m5hkWZbBx+ZzG41wHm2TdNA/d+tUbaz3axgAAAAASUVORK5CYII=") + +index.append('BI') +catalog['BI'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAAB/0lE" + "QVQokTXJTUiTcRwH8O/zbMl8HvfC1tzKaI4sGy1q7JCCdIhePEanLqVEVqNL0SnommKHFkR0" + "6VSSQW8g2EkQKTIhiqSpGLY2UsKtPXtrm//n9///OkS3D3w0zmQgjJW+HrUlnaGQIlJEEEK1" + "WsSsAAW0AADx6XnlrGoHJqNfz34H0FhYqM/MOIJBSEm1GojgcrGUdj5vnho0Txxn5vbRdoc+" + "UNHchr8jHO6Om8n+8qvnVCwGzg274gerb6ZFLhceu7sttjfTWr318ebi9KLT7/F52nwPvqQT" + "ncnh6FB4fJyZi6W8sCmUTjMzM99bSU9lp/pC/fBBJ6UAhDp2LBUzqfkrZW4ysypYWvE3M9dZ" + "nJk9/Xb9XbcvyhqjBb2hWpKlYmm4TEHiW2WZayLQEw/2JrjaXKutVpo1r+m1JZEiKOi/XKRY" + "ElP5j9VUzT3uQz9ePCpll6yN7Nrj+1EzJp2iUC6QsqWS6IAulbQl5axcl7vrycCEh3n3+cu+" + "yH5vOBJN3TCYZ47N9vr3rRSXbRJQcKKEjfr6SDx1xJtkxT8vjUDT/NevMWuF0dvCKkWevR49" + "PDYXeZ/+fAeb0PAQ9kWbmRvzH6ynE47AdkhpVyrKtnXDYKKtfM4/dMEcPAnAc9WjzWXndpU7" + "PyViVaANIEABBAhA/fe/Ojr5cmnn5l99aCZ0THLLKQAAAABJRU5ErkJggg==") + +index.append('BLANK') +catalog['BLANK'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAYAAAB24g05AAAABHNCSVQICAgIfAhkiAAAABlJ" + "REFUKJFjZGRiZqAEMFGke9SAUQOoZQAAMjgAHL4jfPwAAAAASUVORK5CYII=") + +index.append('BN') +catalog['BN'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABw0lE" + "QVQokS3MMWhTYRTF8fN976U+kxgNdojBoZAIDopUyZIOoougm4ug4CIdRASJFNRdaCdx6OAo" + "KMFZcLKbi2jBKKJRaRMpCVbb5L00eS/fvbnXIT3Tbzl/M2wCDrCYTgSQfQgDApF9s0AERlUB" + "TAYvwH9FepCJqsAwDAszwBBWJVFW5d2vr/xp2GZvALCATLYlfDre23ZRdDDvhAiGREnFmdRx" + "YXjpIKguLExvqmpMxgQXNnbO0juXLbVgVGQkTCLO2kPD7g/v7dpaHMf1en22WHTM+VxuEsfu" + "+RMNd+n0s5nCNT9702YuarIOg367aQDUavfb7RYzM/FK9aTn+dHl6+Wj+ejlqrhxofZYVVXV" + "WBv2+2bwDebIg18/Nx8uR0xM5K+eL9158z7taOXSmd7V26lM5tTcnKpaa3u9ngm/ICguCbVM" + "KhkPqPtbw3+Y/VRCsrf0+vv66I9zjojOzc/fWlysVCombODAsXtCLUgCjxQuCYViVuXOJq7c" + "7ZBzzMQ8KZfLjUbDF4YqG68gmiizgoPDZGcmEDpRpebH3DiSrQ5vdfnR8hCA2fkAYYiA3T6E" + "YX04howAgU2DA0AgjI3P+A9mXiz0vUkDDQAAAABJRU5ErkJggg==") + +index.append('BO') +catalog['BO'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABHElE" + "QVQokW1RPaoUYRCs2f008pmpCwaC4DG8h7G5eAkRQzHzDiYewnsYi4Esbx/jV38GMysGNkVT" + "1d10QffSR0dcIzImEGBFiuA/GADw4ROApkuMpA7sJT7aB7Fyrco1b99/HABQ9OcP2EgqYW+7" + "YqWKJSsuj08Bxt0r338x82RF1Lh2w0awatasFbPm4eHd+RnG7WvcPF3NSyvUjVo2TP8SNmzm" + "4d7Nr3cYCBq3aoW4ZcLuQwpmw4aJDlGCEaFgK4StcnXYd28mYaPCEsYX4aV50e+EKRN6I6Wz" + "y0auHhzXzyvGmzPe8vJ9nhkqYjgzWe5kK5aKn+Py7YwBQdFpnFQxcjQxFW3YTuvYtWEIC74C" + "AfQPNjmvv92K2PMfrSN8t7ZlHI0AAAAASUVORK5CYII=") + +index.append('BR') +catalog['BR'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABrUlE" + "QVQokU2RO2iTYRSG3/75Qq0oIYMS24JDFcFbBkcVlAoi4uAloMRJUSGTQ10KCkWx4KK03kAc" + "xMvQCirWwUXNpi62OAgOUhW8ECxNm1/id95zjkOU5OFweOHlcIanC8fRxgD+3xGIHaE1TQQA" + "I+URB9zNzNRVTbet/x3AqXcJjWpKihhFZeLsRADg8G8L37XVGe8cTN173GWw8Gvv3UykUCVq" + "7M/1//vg7mqkcc+mtNSrw8Nj01YQ+kDzx9OLhx58levVDFWEgogEBho1SW/tT0sDnP5QOlIp" + "3Bz6UynVZ7pXnrkwdHhNrB6rR4qYoIkERG+ueXufJC4Oufx88PQNv/oky7jk/AnPbyy6R/f4" + "5tR8sa+BFAkiZueSk8/oiO5xS/6t0Gc++ehkmK95cfnr1sGue/HVe0EOCQiScwvJgfth6nOs" + "HB1du5jWfrosevVRY/f2c49n44ax7JdaNrs0izoCIugUFVEZf5m51JO+uLLDpc9FvPvj5vEg" + "FqhCJY0AAiKELCxbRRUxIVeUJ6W8tZEJdu3hutV5qilVaWJqALqws22x02jbtKKTv/JDL0E5" + "0S0eAAAAAElFTkSuQmCC") + +index.append('BT') +catalog['BT'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABnklE" + "QVQokW3Kv2/McRzH8ef3e5+79tL2gtzFj5RGJcLAH+APYLIYDQYJ6R8gFpMwMHWiDFi7IGmE" + "ySAGgxCLgaQGOYk0vetdr+e+/Xzen/fLIGLxyHN8FpO3BMP5y3EHIzTwEq/wiIM38IA/IAQj" + "HL8BCEeOZ5HB1FhUtBA/q7BMKqLpVdp5uhY8AFL8gTKepQRZzRO+b6mqFKtx2bs26sbO6pfa" + "scNepwSE/t2eFaK2Pw4Go5hy3WPRXt5a+1nsiZpKnihx8IyblJCpjL9G5cbkkA9fD8fx/fOV" + "75tbncu3tBOVExBwUJaSZBSmKnVnVomTWl2hplPnr0I59+iSmolkDsENyeSmIlKz3t7rPqn2" + "Nwa5Sv1Jcy5t2MubGkTNpCInh9IdeVItTmYvrIe7/c12M2+nXrd17+LCh2fjufn+2TsaJcyU" + "zaD0CDXT9Mn+8PT0t+3Fh1eK6ebwzWOxq08r7dtnjtw/p3aUJXkGQjmLStO73sH1Zaayjs63" + "vr6oN2JxYIEy0TLcUCZnPDsUoyewhO/iHTyC4WOc//THb4UhI+oZPvWXAAAAAElFTkSuQmCC") + +index.append('BW') +catalog['BW'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABDklE" + "QVQokX1RsUqjYRCcjT+KYAicoJ2BA/EN8wRi6YvcA9jYpFMfIU0gWB0XuIBC2J1vxuIzIXeI" + "U8wu7O6wzMT1s/8SHZRSoJCCJUigQGHfA8OfxN1PGBBsRxME0JaCPmoeNZtyyc2+f3obRoCM" + "3+VmSKDdDMq0O5dcMuXLkwAVm81mPB73l2x3/hIRsVwuA8BsNlutViRZLFZHZlVl7kpVTafT" + "+Xw+ACBbVe0P+vif9cy+AGAA0BpJZuVOPzPrUHsvASDW6/VkMvn++z6NiMViMZy/nN3e4HXr" + "Eg496Uy5ul3C1Sl+Pf4YQMmji+NuJZpBoRS9aUIzmqPJAkAFHt6xPQiV+Iw2/8tYIHCMD2d1" + "fWGuzZK5AAAAAElFTkSuQmCC") + +index.append('BY') +catalog['BY'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABTElE" + "QVQokU2OsWpUYRCFv717sd1qCVuk8R3yEhbiC4RgqjRhGyux09ikixY+RTBBEFJY2vgQESxE" + "FpG9d6/unTPzj8W9yWY4HA7MfMMhuy6PjvL2NiEeSNDDP+ighTX8gd8wSaDvOTtLiVKISA/C" + "0x33lPKBb66va9Zrbm5yuWS5JCKlwUeZUpZmKU329wvUzGa52XB6urs2S/fxVJamAUAqUJXV" + "iqsrzs9TQj7s0mxUb7ssATXzOV2Xx8cj4OO/EbuHJdwLVKxWeXmZF+/uCtx53+/cLM2GSnV5" + "8iy/fO5Onh+euIpZSGEKWZGKKWRhHlLxxzP/+paqfPpY3rzefnjfTq2dqp1aM6pvKltX1kyt" + "qa2t1VXBI+qyN2fTVK9eLJ4uvISHVKQiL66QF/f0iIiMyGDLpPv56+9i7/uPbwcvDzAwcHYB" + "MNhCYZj/bzd1lgzDLI4AAAAASUVORK5CYII=") + +index.append('BZ') +catalog['BZ'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABpElE" + "QVQokVWRv2uTYRSFz/v1a5Jq2kRNJEJCsaU6KIIUXPxBJ0FxdHBycvZfcHARBPE/cBPH7oJQ" + "7SgFKzrZgrbW2LRJkybpl/eee69DKNSHMx14pieM8B8ECGQAgNzxaccDEDC78eLZWQAGN8Uo" + "X8gPhqcLUYyd3mR/Yiq0u1QInbQ3L7+mqNSyU7mdtqs6DQmxdKd1vp5lqlvN+H75SrdbErrQ" + "69UEuJQAcAPVxZAEffR49e7l6cWkccNqDxqVJ09XcpOjSI1UoQIxAUFzKpQ+d6FZz5eL3bD+" + "/dPal4/+a1QLxavXNqOEKCZ0wBLQ1FzVqahfbJb9TK5YybbpW83a7FwZ1dr8H1GLdNGxEE0V" + "oh7VmtvlQz84yo56+epPLOz19kcYtH9PS3QRJw2wFBGiLnRR//ytvnhvlVl6+/p8VNvo/Dic" + "aa18uBmpkUZLAEuRgQpRRGLA5PWrW/cfrs3kOyQOEN49X9rZh9BoiWoAkGJ3NwzPNaachNDj" + "RLr+dqFU6qvi716u7F6a6au5qbPvwGZonQg5zjwE+gCBAlA4UXrMPyh0GTDv/QqiAAAAAElF" + "TkSuQmCC") + +index.append('CA') +catalog['CA'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABoUlE" + "QVQokU2RvW4TQRhF78zOetc/YMsOFqJBQAEPQEidmpI3SJkOCckSVV6AhoKfElHnBRBvYJTK" + "EkUKIhNSRCTKkoWdmW/muxSWEKe6xSmudEzz9JleXVln/fnpcLnEf5AEEGM8m816QAQS4HR9" + "2l88B1BcXw4GA5IkY9MYRTG5SdXSlbf291010py/vX7lMKoB6s+LfHm+sZOyffNWfR6+fFEY" + "QzIeH2t/Yu/cjoBDUmalJKREMqy+Bo3FyRrR/15+cTlXT7bRddnUDDEBTmNEzhRhCCTbd+/d" + "aGCGY5Z1/vDx+vvJ/PBQuwDTmRgS4OC9SlIRjUISD+7y6IjlyLTeNmfFw0eqmkJnXV+jRMAm" + "75mEIho8ydHe3p/ZVmwakbadzseLBUnxniHmuLnUthTREDQEVUVdjw8Ous+fTFVPd3ZMXeec" + "GUIuI0QS4NLFD4owRPVeVUmaXontx7ZXo6pyzqqqvtNesClFwCVAg5jpxObOGLOJVU23DKmA" + "tdYYU967X0zmKkkBcwwokIBfwI3Vynv/L/BmiMh6d7cHKADgLxaxMt388uCoAAAAAElFTkSu" + "QmCC") + +index.append('CB') +catalog['CB'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABTklE" + "QVQokVWRL2/TcRyEr82P/UlaycKydCAIOIIhJAjI9AKC9AXMTPEOhsHxKta9BBI0eh5PBXSi" + "yC0hdJ+77x2ivwYwl0dc7sQzwPExzs6wtYXVCgBsSAAg9Wz3IGE4HAB4P5vh6Mi7uyZbIqDZ" + "SmQLoC2bCZPPl5cdAMy/Xc3nbTptd+9xxZbQYJ8upxzak+078KgDYKctfuj8/N3L5YunLa5E" + "MeNKagMc+PB69aUD0FqTpOXy4pMG4PMnTNj3XMkaiBDAEIDUSJL8ec2PM36/qgyrb5tpG26y" + "sH6QpGKNhjqdcrJfqX7134eBaWEIgGRVaTQ+fVuvnlVUaZv2JmEl0godAEnc2+PJyYfJg/pV" + "ZdP5m7FsOQ+z/bW77ADw8P7+m9ccj3V7q63Qpq2EGxstaXbLb9Syw8HBxeNHWCxwc9PrXHut" + "+s/x2v3Ozh9eL3jobZO0pwAAAABJRU5ErkJggg==") + +index.append('CD') +catalog['CD'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABoUlE" + "QVQokU2RPWjTARDFLzEoNhZt0cYU/gqCH4TiR4nB4sfkZGdnHVx1FAQXlw6CheLgIKKDk5tV" + "R4eiaA1RSiF2KBmSGm3VBkHU5u6993dIKo7H3eO9d79McnGydf3FvtuTK4sVk8xlgDnMZe62" + "AUvdzM1g5maescO3Hs2MN9YLSFMxpVJIpKAUEKggAQUV5OzNu7nkaPXS8e6dudJCe5RSSAQD" + "aUgBBuigQwEmu/Nmnl1ZLD/8MLHQHoUUFEFHGmQx/9X71/SABwIy82yhVL184lVppAUSpENp" + "6hPJ0tT5J8mO1W7Qoy8Lysyza7Xx+7VT79vFoAIK6Nz++o2zT4e3d6YvPC4MfOsLQgDNPJsf" + "e31lfG5spPkvwLOPRz79GEjVfb50qLG+09E3CcgMuV/z5XvvKtVWHlT0d3pQK883kwND7V7p" + "AAFRMvMtdtJnr27Uv2xtdgY3f8L66p7O79zy9yEPBggK1K78trXqy5y9qcy8Pf35z97iMEEB" + "DCjYIyCQYEqK6jkoY8cO/pxaHrxWsMaZHstNrvhvlBnMZGZ/AR0PdcdtApHFAAAAAElFTkSu" + "QmCC") + +index.append('CF') +catalog['CF'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABj0lE" + "QVQokU3KP0hVARiG8ffcc+x2K7qeweIqDWnSFEFrNdTqUK3V5ODQP5e7OkSTLS3h5OzgkhBc" + "XbMlCLpNIRQoaESbcU+D3/e939tgQb/tgafoXN0pu/Wbpd6d5z/Qgmeywa/PPQLjl/YxlgjC" + "EiQsYVGV3fGdtY2pqaX9a2vrHx4zoSOcevdUES9vTHgpUkx5iMrlZ9+qJLaG8/c6L16/fZSF" + "0pGUdnfl/v1yBuWhYHrofF2gQXFw8HNycoIEgKIQAECam5O5tjb1T6aKohgOP1WAAyhLSIIg" + "SJLMZK7/AAIwGo2qhfcLi9cXD48OKTLJVjLjoZncV7+uRjKNFCOjbtf9Qb8abA5mL87uNXuW" + "FgxPd9oDc5mtfFwxmtMtzenT3elmo6kQcLnRIsNplm40mcv9703zygPBMnACVX8e92fit3vK" + "M52wlMlcZq9uOdOTlvIUz4xx+QmquydxpQ6pJzlAySXHBUfE7Z5LIYVEgADP3USxvY1WC50O" + "2m2cPgsQmeAMEsgv4HESmTj2B5KxXKDSA72eAAAAAElFTkSuQmCC") + +index.append('CH') +catalog['CH'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAYAAAB24g05AAAABHNCSVQICAgIfAhkiAAAALBJ" + "REFUKJGFUsEVgzAI/VEPvk5gOoZrZIyM4SwePXp0Jydo/kXtIU1NClYuEODDh2BMVQMA9n07" + "8EeY2Y+qNsluiqxhUJAESLQfm+NYhBsBWFcgBGCaTp9zUVtbMAGAShQIQZsAJNWYZJB3TsB5" + "jg/vRbpk8Au+kbJABtDAJMUOviMQQEsCzkXwspxZfR+1tfcMLmlf+MUS266LhvdnsdRZ+QWT" + "LvG1b4fWQ/M9s0t8Ay6VSOU8nBloAAAAAElFTkSuQmCC") + +index.append('CL') +catalog['CL'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABC0lE" + "QVQokXWMPUpDURCFv3vfExOLIGqT0qxBRFyKhTaCZRZg5U5SuQcbWyE2toJIEOxifiSa5M7c" + "GYsEyTPmcDgwnO9M4OqBb0UhwszYiblzCti/gpKJXp8dXhzvdx4HWXKjXkAADyEURRFj9BVN" + "JpOIcXlygHN+tPf2MX8fzWFZr33HzCJw+zTI7nfPI1HXjG+WmZXUdse6dXM/VEe3a5rTJhoA" + "Sl5f4me9GM8sezRLjbpPpw6rb5eDGK3fDxlot73Xc0mIuohL8iSekkslY6s17HZLgyDiKaFa" + "gao0qq6qUBoUqi7CL7Q+U0EzObMYRFGaTRdBMyqIBBFUWaQqOS9sEL7AVkz1/FMBP4aNaAa6" + "+++rAAAAAElFTkSuQmCC") + +index.append('CN') +catalog['CN'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABJElE" + "QVQokV2RMUpDYRCEJ8mLYhGwElOkEAQvoEfwGOYQllp4HQuRmMLGUkG8gJ7AQkUQExOzM7tr" + "8b8XHsIWw7Iz+7HbeQECCKALANg+xvsd0DQFAFBLVwHsXpwPTjC7jP5ebB759qvPJoIppSRD" + "SrLo5+m0CmAwzt7O62Dsn2ceC24cev+A8xsmmcakhVmS1WikAjK/ypj5z637LzsDT5m/MY1h" + "llyFWV2kgApAfPnHqZDaOlK1z+UDl/eMVnaaBZnFEECsvLAun4QtziaWZJIlFU18SLUhXSmB" + "5gt9XzNphSfW2cVARm0oLSlbAP+nJaw3QCqIQYZZPdFwp1mIKU93AJWAoHrDYZJdeYp1fHP+" + "kOAe7ukuoPPYemRbqPXsaKoC/gA6rnMwXmwqoAAAAABJRU5ErkJggg==") + +index.append('CO') +catalog['CO'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABEUlE" + "QVQokWWOsa6OYRCEn48vR6MhIhQSci5A7Z60Kjegdiki0WuERknjiEKhUvrtzDuj+M+fHGwm" + "kye7O5vdfr4h4bJCQnIC/yfYE24/fg6U0NBVFnFr4qLGjVq3unj9ageg1Xe6yGpFV6NWJ59m" + "Gm03HsTsQOnf29O6UTPtnEBEHnYMLHA3g4u6qVVRt2mnqEwrEGHj1rtnLx5++zFelSNXvoRR" + "5MyRvR7du/H25fudAzZ2xz15RsdYR50rhyA7B36LX4OFzJgxUuWOLzsy9jYDsH/qk7ufn+ri" + "azUZVcoMUjSRjm9VyvLZ+flHPuxncN2Ha/fv1O5MfRyvSkfGzkqXuw43YfsCBmAgEPAV/0fA" + "H7CAa8EDIY7HAAAAAElFTkSuQmCC") + +index.append('CR') +catalog['CR'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABJElE" + "QVQokWVRMUpDURCcl/wiKKg38BCeQCy8gxfwAEJuEFKr2FhYBCzEU3gLWy2sFDSKkt2dGYuf" + "RMVhGBjYXYadhu0JAktIiIIF1C/GSgNQh43RxeWhARuyRZckqWjSpJJiqcQqn44v2sIeArAB" + "GIBtwH/R+9ba63zeYTbD3p6/Pi3DslakLNkCadJS29iM25v2AOyMx3x8dJWyXOlMZCrSGYpw" + "Zq/D3d37u7tOAIrOVJX7hQhnrucU4QhlDqoEdALEUpUynOVMZzjy9+2lZgpob9PJaP9AH+99" + "0F61ym0SpGSLg62tp/Oz9rVYdMOhpeWLbNv6/yWgtfb88tKAk8nVEUukiiqKVJYoslwSKclF" + "2b6ejhtwvGpxXefa/hQMFCBg8A0y93t3R/GzpwAAAABJRU5ErkJggg==") + +index.append('CU') +catalog['CU'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAABGdBTUEAAK/INwWK6QAAABl0R" + "Vh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHFSURBVHjaYrzNwKDquJyBjYfhxz" + "eGfwwMf/4w/PnH8AuI/sBIMPoBRL8Y2NgAAohFhYHhk831pRr+H7iF/v/7/+/f/z8Q8u8/IOP" + "Pn39///37/ef/73//gCLzc/YABBDjZwYG7uqqT8+e8yUl/LawYWD4D9T2/z8DFIOpf2CakZHx" + "/cePAAHEAnTF73//OX///jx9Bs/Xrwyu7v8ZGaAKYYgJTDIyMrAxMQAEEONHBgb29nZmM7Pfs" + "2f//P5jH5/WdvMwoON///kHcgaQ/AslpQQ5lhRuBQggkA0srq4MurrMv/+wTppk9/LMp11f56" + "gF/f4FhH9//fn7+/e/X0ANf/79lOBiYLgHEEAgDX927GD69On31Mk/f//ay6uz2ypa/B8DxFQ" + "Q+gOyAehjCREOBgYZgABifMvAwJWV9f/+/e9//vAmxP0PCfuPDTAwAP3A+ObNG4AAAjvpz58P" + "f/5wZaT/9vL9/+f/f2iogEhg+ILDiwESSt9+/AEIIBYeBoaPf/+3RfT9esvwZ+FNiO3AGPgNY" + "fwFxcPfv////vv/9z/DvuY5AAHEeJqBwVR0JjRSgdH5/w/QUzD0C0z+A5MMYJIJIMAA5qlT7L" + "92ZXAAAAAASUVORK5CYII=") + +index.append('CZ') +catalog['CZ'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABO0lE" + "QVQokXWRvWpUcRDFz978CWgUVkgQ/CjcUhtBbFcrC0GwSG9vkRfxRQIR8Va+QNrsRhDFF5C1" + "EBVSeGfOOWNxs0uQeIqZU/zODMNM8Orkzcvd1892AQCwPdaLZiPa7YbPtq/f/brSw3vXxkwV" + "gKpNu6DVatV1XbdTPDo9O/7yeyQ22D/jx4XNhlxXNfSfLHt+f1r/0XmAtKtctaPh3UdRfvrg" + "8gwAki1o21RJdUXD4Ylufjia7W2XZLIkSCWVNJlOcXDQSMqQiqqUX5y+vbN4P0RWhDIrojId" + "UZltNvs1DE20XClTNV/280V/DmUiApkVgUyQIAE0BDnSi/7Jsq+Icd4mVhFmgiqJQEOYquff" + "jh//WOLWbTO7TGSarMwJWeTW+gwADX/4ef/RPn5+BwwYAMC15/j+temAvzMZeRNn/GS2AAAA" + "AElFTkSuQmCC") + +index.append('DE') +catalog['DE'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABNUlE" + "QVQokXWQMaoUARBEa2ZnwUgUEwN/aqjgNzJUzD5o6D1+4C28iLCXMNHUowgy3V3dVQazaOQL" + "iqbgVdDL3d07zA4Jp61axbYICP9hI/f7+88AJEma6Zkj+wrZ090kebl83dZ1BfD9x88mj7qq" + "Kisr/xIRGfHh41sA2w3wSLohu7urmmQmM6uKEZWRmdwjK582AWyfmLczt6SrfGSmMxWpDGcq" + "wnuq4szegM0sz6DKRWe4SlchnKlM7fvhm2VgM4Dpf8NVjlDEVdt3V06kqzwcYOMb4Ll9kkei" + "xDkOt0W5MbQbov16iW/Y/AJ+Jj8eaaSxRtOWpD7erLElCX6y6D02FNzE+dWKWjRWLWqY1li0" + "2h677Ub1+hvL5cvLBwMVfELuEOCHWAMa1AlT0OC8QgGc8KvwBzggh2H1+79aAAAAAElFTkSu" + "QmCC") + +index.append('DK') +catalog['DK'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABKElE" + "QVQokWVSO04DUQyc93YLBIpEhZRiG27AQeAgtNDlVilziFwhAkFBBFKWBKR4bA/FW5AAy4Vl" + "j39jlxFITJLAeURut7v5PIE/6gCAPoGTxQIAMpFZSqmz2dn9XX58yl0k3EXKPcmX5bIHACif" + "nhGhCEmKiM1DHvYiZRRNZknWYcjWAakJTQKQlLRsUJqMaSaykDYlRMhd7qBLUq3a72EETWYi" + "YQZSZAIlIkopkiS18spUM35LqXXc7Xr8xCAILe8/WlKRMrPfdt3s9jY2G7nL7HS10jgerm9y" + "/97maQvIvb+8fFqvewCNPpEgp2Z2xPE4Oc3khAciJpZEr/O5SHkAQNfVYcDhUBr97jUCERkB" + "oDwD/n1IBy7e3vzx8fXq6sfZ/qCduQJfBq5Z9b21BQQAAAAASUVORK5CYII=") + +index.append('DO') +catalog['DO'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABN0lE" + "QVQokXVRMWqUYRSc798fixWEEIwsmCKN4gG8jYVN6lR2OYOFJxCCF0iX3gukiN1KakFZu+TN" + "vBmLfyOxcIrh8Zh5A/PG6s0FHtAyyrB/fnv/TLvvB4cGDOgRzwA+nb8NkMAdO7LXTzDm9fPT" + "05CRQloKub28nAEk+PHrrhd1uzudzG5utymG5aqQq+NjA3MbCbrTTrfVkewkcarCcjFVJidS" + "wAxZflBrb5hHMq1S5SqTqQppUsDo7jFGkiQA8n+Mafq9240tcHh2ptvbsEKZDOvg6qqn+d2H" + "r5Kplkz55YunXz5+ngxkHypXpSr3lfu76ixqypSl7jag2cC+OHJxuiqJk+L+trrVaf81UNNm" + "M8ioLQ5yTKtpjM3RuttLdbbVATxu/n3kwq9urnfro9cn54/Wy4A/xLJ3307zCyYAAAAASUVO" + "RK5CYII=") + +index.append('DZ') +catalog['DZ'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABmUlE" + "QVQokU2PPWtUURCG33PO3bu7lxWjZA2bsIUEFMk/0DqI6EJs9AfY2FhYGkFsBP0B1tqITSxD" + "GrHRRrAQO0VBtsr6lTWud8+Zr2NxF3WKYYrnnZnHnbw1msYZAAAkEok0zkJZfbqzU4YWgJyz" + "mZmZiJhZ8a2e3t+6kQHLpqaiKiJWhP5g0IZHzpb/1Xg8LgpfZOTJ4Q9RFRUyYab1nySr720w" + "sFbLbBGpqirnXMDMchZVVmGV4a/68sevXUo17/XOj9DvI4S/F8zMRyFRIRUWpiybn79XlO6e" + "OREujGa7z5z3jV4TEBEfiUSVhZJyIlr+PX8ntN8p5vdupzevJ9eueO9DCM65Rt1niqyShBNT" + "Up4Yn9qflgfT9s3tcv306qOdhg4hLF5CrEmFmBNTSvHpSmfewoNXH/jJ46NbVzORmQFwzgFQ" + "VY9Ys3ASSkyR0tsurm8sPVzrVZuX8nBonc7/xgAKxEMSXu4tkTIxJ2Fqp/FKaefO5uwc0Kxv" + "aDNzuHgMXw4QgRqLPgNKvHj+8nj3SAOpajMA+AMw503J6/1+rgAAAABJRU5ErkJggg==") + +index.append('EC') +catalog['EC'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABKklE" + "QVQokW2OPUqdYRCFz6evIFcJiCERK7lNmuwgq8kCbMTaStKkdUMuINiqlSI2MYkXo6LvnB+L" + "zysKDofhcOZhZobHky/wDcay7Q7T6HY3YwOGDRM2ADT43+KnfQCAkCxEiBONQpgQYeyEs+Of" + "DWhAwKs54YQJYyaEGVRcMYelz+5oMAC/S8eVEKmYScGk0drkZlh8RB5eHpjrmRvXJzW0++UJ" + "2sfdvR/fv/2+vqUiizJlSkVRLkkU6ZI31lcPti/a9enm5ezDxZ9Wcsmki+50yUUVMw/1XxNg" + "2vBgahy7+GI0ms5wPEVLBjwcAZs7O/3sLFWueu69u8rkmIQVamk6/XV42LaA5bsZ1lbCSmkk" + "wEoxrIghQ0HK7d+vwHAOEPBb8ZXBK7MAPAH8bIEjRK/4bgAAAABJRU5ErkJggg==") + +index.append('EE') +catalog['EE'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAAA8klE" + "QVQokYWRMUpDURBFz4MHLsAilVi5BBcgrsfWxl25gSC4gCwhBFPbhpk7dyy++cZCPM0beOde" + "GGbwuGXFEEZGJkSYMCcRIsTJpCbw8nQH2Li73DJlq1plFVlWOd1Z/fr0Ppfqj88sd7mzXNVZ" + "zuqUszrkKGf65voKNDE25Zat6qqOsqpz8eRQh5x2lkGT7UPdP+twkKRUKhciMjPi/GRm3t7C" + "2wSkysw18P19qUcsAjCBKkmKjHN/RORl91oBjOPxuNlsuhvovwHGGLvdbtpe1H8DgO1pe7XX" + "A44x+MWA/gns93vbGGNJtu3vYVnUZ4Av1C5+7JEmXZcAAAAASUVORK5CYII=") + +index.append('EG') +catalog['EG'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABI0lE" + "QVQokZ2PIU5DYRCE5388UCSoUipwNWCqkByCO5D6OhIsqUByABwXqEFyhl6gCkigwVQQ+s/s" + "DuIVAki+bCaTyewmW96AxIb8rd+jH2GbwO7lJQBnItMRVjhkCZJJS0laAvk8m7UADMfjEyIc" + "AdIRJk0m6UqzZq0gm8NDAe3OyUlzdIze/uZ8BCK45tbpe/OwnayWGslS6fV27u9LRJRSANiG" + "YTgzPpa34OP2wQXc+otSymq1arp3Nxlsu75ca73KWNfnK/8EyMzmT9u2Lcof3EvzV9/OzDIa" + "jSaTyXK5jAhJkkidn73aurnbk5QZkiKi3+9Pp9MCYDweLxYLkiRrrbXWzpDsnEQphsPhfD5v" + "AZAaDAYkpZC6RUrqtDvfgX/wCf7XTmN4WqTRAAAAAElFTkSuQmCC") + +index.append('ES') +catalog['ES'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABH0lE" + "QVQokX2RrU5cURSFv3PntghCJQkCBUgQ9RieAVmN6CtMJsFjeBZsVVUtg2dUK5owyYTOlLln" + "/yzEvRM6CenOypcl9l5i7SLeJrf9uyoCxmNAmWQSIQ/C5Y67zPQPl3d3LQDSz19EECGznoOq" + "yapqlVk5PExoCVQEAfF3kS8L7e3X0Y5LJlWpDiaNYvlMkwABvlx53MJNu3q6WHd1s70RVRjQ" + "ABCSje6/+G8vXycfHo+6edmKH248kwaQXPL27OTPSXI9GZ1+bvVR6qS6YZUqskyaTPqYH48P" + "u+d1fdl9n35bf1ptsgeCS+5O6WB0dZWzGUMtQycDe+OGRzk+nk+nbUJjzsGBzPDADbNihjs9" + "3fvGiUgoq+1H/ufH/bwCdrRKr4/GDeYAAAAASUVORK5CYII=") + +index.append('ESPERANTO') +catalog['ESPERANTO'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAYAAAB24g05AAAABHNCSVQICAgIfAhkiAAAALtJ" + "REFUKJHNzLEKglAYhuH3yCGEaDu41RhODjk4uUejV+A1eAVdhIObU12DEDjW7BK1BdFmNVRk" + "GdjqEFhOfePP/7winIUPy7RkVVUIIQCYLqe3dJ8+adqei7RMS7ojV4u3Mf7QRwiBcTZ6dBo5" + "ABpAtI4IVgHewiMv8u9kPTDuj3EMB1vZKF39FJAASlckk4Rref0JA8goi+6D16BbP2bH7PvA" + "fDMvKOk2v36e1hb+T0Cy40TJo5U+cHsD6JgyzFPQGaoAAAAASUVORK5CYII=") + +index.append('ET') +catalog['ET'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABhElE" + "QVQokV2LsWuTYRjE7/3yGSsWDbRKGgixnYRujg4d/A8KXerk4iK4OIiguDQ4Cy5uTk7ZRHB2" + "FXEqKFhqCUrBBFNjkn5573mec6iKevyG+3FcwvYqJmOcJAzZEBmZMMCACshA/l0WUGL87f7N" + "RwAiFHKPsHC3bOEWMne6mxvD6Xxx+3GJqg6mz6OBh3sEI3uyVLboMZ3usWI2ZCPJ9nITQIm1" + "wXJjzmJm4RE2J85dvFOr1T1iUlWH/QdSRae5Nc+fxQUkd6R0F+hLBtmTt93hbGmj3Ts4urQ7" + "uNKoH967ekPKElPqjMevCwCAS5QocJrrG+3ewdGqRVzrPB8eN6T8C1gECgCSSSZkKaeierW/" + "FcqtxQ8vP26fOTX6c4AYgUQgdW/pyyeYyXKeY2fz6ffTiwwt/Bh1n22VmoiEWWqvDXvvygBq" + "fdM+QYosc364c/19c93NL++9KThTzjLCHMcOoAygoGFlRSTMYSzIdX4VTJ2WzGAG9xMCSFMg" + "/gL/6n8TgJ8hCj2miB4d6gAAAABJRU5ErkJggg==") + +index.append('FI') +catalog['FI'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABN0lE" + "QVQokW2RvWqUYRCFz3x+ooQVFAIKCRaSIlYKtpZeR65C0moj8UpSCNYWBsRKOysF0ZA0ESQB" + "Q7JF8r5nnkmxy+KKD1PNz4EzJ46OzzVnWHv2WQ1Nhq+7j1duykjMMQCnZ9NxkO6tTiRV1avt" + "TYpMbT64XVWz5t98+9lGpNlA0q+TS6NM6n9ERDMjKJmL9VSCXQlVS/IqFNFM6MnHnecbRyct" + "k9bLWT0paC67etI6Ni1ZX73x5uX7URdu5rKTWU66y4lNz+qmGXda0ns1I12MwlVVFNBdNs0A" + "mZUJSQIJFCARPw7+3F+/BVRp6/V+N928fbHBsnNJEfHpy+E4jFwLRUih2XY3VSX9U5IEbRQs" + "Xn73znUntiJCS8TiJr7v/14E+ejhntSk4d2Hp5MVGWS8SBudTadX03Ro9+qwo6AAAAAASUVO" + "RK5CYII=") + +index.append('FJ') +catalog['FJ'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABzElE" + "QVQokU2RP2iTYRjE702+tDEl1mgS27TFarFoh9JWHVxcKkKd2kGqg4OTFFGRDoIViujmIg4F" + "FxUHRUGnWBCqoLSCQ0HBuFnt0qQojSY1Cd/7PM85BP9sd8PB7+7c0vmZhTA3e+fT7I73iZbI" + "5WL32c7K+PjBscpV5AxiaBjEYIaaQeCGey4+fzApO3ONwkfWaq4j157dNl+or6aHgzhCUIxi" + "FKUYb89tBnfliSv0b3n7MlouW73ekkrVBYeFiweGkKQqxeiVXtnR5hBakE8Mnenbnyiv1YVu" + "eybe17vxpViotnqlKqfzl2L1avqklO55dPfOhdORtZFjLh5//DlY8W1fa7GHP7siu3ZvsNUr" + "KTQvmVNCCbOnxbxAEKQH9uVLyaVIf6Ozy5m+Womu7h0YPCS+TFPi+q3YVpYmJzKPnmIkghM/" + "HK7pzVGsVZvN0CQWpTfWyHMvrvSUClrdZBD9Nnjk6OJUgNCUTpRiEKVXemNTEGyfvZFpZ3Fi" + "LPtsvlFxePM9gJhoVJT+b+DPMiE485rH93DU+/sfuLwOhAjQMI9IKgltIhGiUHOiEIMQyyW8" + "u7Bg61QDzBymfkEMoSFs/op/1v4TYjAA+A0AOEaMXbWFtAAAAABJRU5ErkJggg==") + +index.append('FO') +catalog['FO'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABQklE" + "QVQokW2RvUoDQRDHZ7kVm9wV4S4GycUPCD6FhYW+gYXvICjEj9pOW30CCx/ABxARsbFNIYci" + "Z9CQi0KKNGZmdsZiY4gmv2JYZnb+O/sfMxgM4Jew3n3uNxrzWb9bEREAkCksAJRKJQBQ1ePT" + "JC4O9stJFEWq6pOTdDod67V9uV0wf+RvQ9JZGGNGL/jbBEBOhB3yf2GPn9CGte7RWdzuOWZh" + "p0BITrcOP5GUWJCVSJCEWJeq9u7ixWQAyUmT33N1rMTKpIRKJERKKERC6A9z9ZXW9YNdC572" + "ypV8yOiUSS/bO9sLV0iCrMQjbR9XK/YRWrb/VQ3D0I+42ewp4hDl/jye/oAx5nV32Xq/fQpJ" + "hRBnmeTNZGYrImPLF+Mg+K6lSWCMgT8YgFGPKYpivMU0zW9hfQNusqw2uV0QYRHf8AN4MF/m" + "C35t4wAAAABJRU5ErkJggg==") + +index.append('FR') +catalog['FR'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABPElE" + "QVQokW2RPYpUURSE67x+BiM4iYK0Zi5AcANGxoIbMTExFjE0EWPd02xBzIycbu49t34MegQD" + "v6iCgiqq6t3XG/zF9NvX15LmnJJIVtWzVy98C04QMLADePPyCYA4do7HB0kAJLGd5N7Hz+Fy" + "d9b69f7DDgDB7/OSYvt0OiW5uKtK0vXPH+6Znnj4iMAGI44U2VS2bUtSVRdh2z08p9dMt4Gd" + "tGzJUmgl2bYtvtQJAM+Znu5V3Q3s3bQjedliDoeDbQP7BruqynNmtbuLNLA3TWXJlEnnHyQl" + "SU91p9d2SRhN2aSXzCX7ro10p9Wdnl4LXAb2MUh60aQW/R/mDJfXKorAfu4mff/qIILcLvtc" + "fqyqqjo8PposMVoG9vOZn77fjNHnwdvRT6+O3b3GaKm7xxjPv3wz7m4G8AfurXtmcb4hTgAA" + "AABJRU5ErkJggg==") + +index.append('GB') +catalog['GB'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAACAklE" + "QVQokQXBW0hTYQDA8f93XKtWlIdFmkaidoGkhyiokRDRzRFFPkSZRQkVQhJIIPQQlpYRXZ5S" + "KUPowSLohrMLuS5iuaAyy7UopqVZmYquXc7Ozrbv6/cTNbXt7oJxvB7l3t0RmCWVWrHMkX+n" + "TujO4IbKT4GIQm4vSqRbL80oP9zQpVNzqn3kr5RGTPo/yETyx28ZNaR891p+6Y3E5OBPKQ1T" + "PrgpwyGfX67f1qahNAVvgw6Rmyc62vMc8WhciP4+8S0YNkS+IyKOVwvXxqans+MGRKWGAEDw" + "Zlhny2a8j7MyogwMYI7lZIQ5WUPDmSvdztA/LAvA5tZ6crXVC+1TKBhRFC3nQj39QfoCvHpP" + "1TEGv1bNizHXUs4Fdi7alO8lJW4+BpAwOMTYLybGCUUwDUb/8LyMpIlpkUpxZM8k2ETBEvIW" + "49BB4XKRmUnFASIRTJOZdq63YBiYSRIWRYX6iy6bR9+ZL7OGE9kCigtD1NfR8YQ1K5k7nbvP" + "KCvlWvPRW9nRuFmSYb/KVk0qhQIoXjTB6VpO1E4m7Bgm8VTPpJPGVvbua9zkV1Y0nU6BpSFB" + "Ubw0rHyd6uz57pE5tmlKHSxV5TuydXV7KEve91g+741Kk3QSkjZU+vvDpkBbi1ZRfW7/PabS" + "63bNX9vpBS73rlKjn5uJHXL4Jx55cgBc/wFytP7Lx9suAgAAAABJRU5ErkJggg==") + +index.append('GE') +catalog['GE'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABtklE" + "QVQokUWRT2sTURTF73t5mUCptg0BjUhXuo60OouCf3cu/IPahfgBXPkNBMFVoRuhIPgN3HRR" + "BQMt1kXQEpVqcRvIIo1aEhOcSTLz5t13j4uJehYXLvdy4Jyf6vV69Fciks/gdJU8TTodERER" + "Zv53MkRUqVQwHPivX3TtnFooA5StrYHdXLUKAMMhv3urLoRUPdVqtXTu7b4dDD5/cAf7AIjg" + "v3f94dTe7n862q3bvQYRMYshIgDFi1fmj82a2nK+gh2cAwDAXL5WLgUmXAEgwmr86GHx9l38" + "jkgrZBm8R5Lw3ns4p5eWEZTgPRW0TKyeP/7zyWOTbrzQ5Yo/7IAZjsk5iMfMDDzz6y2kCRyL" + "c3CusLg46hwZ82BVnw/pzFlSSqyF90gm/LEJZn3pKgUBhAtK+9TqhXJQ36Zut8vMzrl4p55l" + "mbU2TdPB/Xu/7twYj8ej0SiO4x8b61EUxXHcaDR0XnD6anO4/Way+TIPCvc/dPz8WX93J1p/" + "CkBEjIgAKC2Fc/1+KVzJW1YnTlJmlVJENHtr1SRJ8frNHJxqt9tTkEJCU6hBrSZEo2aTmfO/" + "XET0B9K2TnP0V/LHAAAAAElFTkSuQmCC") + +index.append('GH') +catalog['GH'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABF0lE" + "QVQokW2OPYpUARCE6+kLhA2Ml2VMvIE38AaCiayB4GYewGQjT6ORsOM5PIOJHkBZ0KmfLoO3" + "g7NgUxRNV33QS/Fv5v7+Xy0FcH0NoDOYQVIHcW3YlXrit/v9CgBov/9AgqTS5neiKpastOx2" + "A6wAOr3XJmvfVcVSGwBpgHWCZQng1qgL3f7Wp4+idHnJs0ds1bIVFuEXtpfSqjXgVjefub/R" + "4Y+W8urt1t4Az+ABgNatiy3gq5d8csHdxeHNa7aHI0BUM1gfvse7F/r2kx4rZMShnkvRsy9U" + "xFCRx08f++sHrDAcK9JoixlqdHQp9MgTT0CsIDQ+PztX5Ik3cuSxRo5dJ0mTCQYLrgACBngi" + "n1x81ADAX4YyZWXkOpMSAAAAAElFTkSuQmCC") + +index.append('GL') +catalog['GL'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABJklE" + "QVQokWVPu2oCURA9V1cLC8GFBKwC/oKNH2JnbWtvn7QJWiSFjVr4G6IkNta2IhaL3e7i2zt3" + "TopdxMfhwDAz58zDxHGMG6gCUL2GJ3gAisUiAJIAEIY8neD7zOWS4i2CIPCSwSSx22Ew4HyO" + "45G+z0aDtdqt2hgjIplUTaLf52SC7ZaHA1crvn9wsXjYoKqZ9PYg4HQKK7xcaC0vF+73HI2e" + "DcY1m6ZeZxRRBKp0Sid07pEiplSK220PvR4LBary6xO8/3I8ZquVLrTWVCoahhkFaC2XS/z+" + "3amt5XB4VUOEIgJ4CmRFeD6j06Eqq1Vms9xs2O1yNksNYiEOziExZKygXKa1+P7B6wvyebNe" + "I4rw9gYRiMC5hAqYPaA3xH360ALwDxiKW0pwzWHQAAAAAElFTkSuQmCC") + +index.append('GR') +catalog['GR'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABTklE" + "QVQokWWRP0sDQRDF311ONGLgCBEb01jpgV1EwUIQyWcR8TtYCxYW4qcQRAWLdEYFEcUUAUnE" + "qIdICvEPBDXH7nsWe4kBYWZ4swxvfzvr5ZbOAVzshgBKq2+g951YJERCJCatPwY/RELIBACW" + "S2EURZJW5m4h7W1OSxAACXIpSRQe4xcvjuNisSgJAABJ5zfvFCiRkkSmOjvin1aeAzfUnwZQ" + "u/u2pLGylsbKGBlLWuXDYGOn7g0vnpbn8wdbM5KW1+qkjrejlAIpirvJ9/1mMw5M0mN0DFR5" + "vU5HQqUhURrLBrXqvdduvxYK4YDhoGA/Afm+32o9BfvVzsJs5qtL9ZzkvHswFJwYHcmcVTrB" + "yVV7ajL30UmslaWsZV/QtaSlLJXPDW0eNrzGw+d4flgS3PogSUgf+kcIkcLlddPDxFH6kV0C" + "5l8QcOdw9RfrG2tQxEeGNAAAAABJRU5ErkJggg==") + +index.append('GT') +catalog['GT'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABG0lE" + "QVQokWWPMWpWURSEv5t3i7+QYCDEwsIyy7DKHizsk8reFbgBV+Ai3IRrsLIIhCBJiI/HvXNm" + "LF5A8D/NwOE7M3Pacp3aADCs/vX14dBtG7D9tPryw2tkZDZzoNczXz4ClDPV3pyfLSdAkgCn" + "MzefYkcVOd8+P3c6wO1jVGzCybLjSRI5P+9iZ1benjXkjrEjU0aV5B+9z1DsyFHB5o5cbirK" + "Uf1PxxmVVOTMgkFHVKjKdKaODpKplx+mGuWOqBeDzKMEO0PJXskBd2SlzcoUU8cB2ZcqqgLq" + "DEvLVKYYdYSToZDIqAA6q6dPLk6ZlW3QGkDbBZbGu3NIs1MB3Lj6w28zzGpW//h+9+qAJNu2" + "75909f4CDN4t/gLVAHPNcoxygQAAAABJRU5ErkJggg==") + +index.append('HK') +catalog['HK'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABUElE" + "QVQokVWRsS6EcRDE5/vfR3wcIqJQ0OoUSjqPQucJJDyFwlOI4pQaEi+h4nJESET4jrCzu6O4" + "I2wmky0mmd38qmcgMZ788b9yAIADCRSgdqB7cAAAmcpUBDwUjgiRIuX+64Nery4AoLy7V4Qi" + "QCqiu7mlmem3k5M0iiYzkWVlBUABgNQ4HVFKKZ2Odne0tja1uFgVpNlIIh2oE1CE3OFeVVVz" + "eKiXF93caqrpHB3NPDy87+3ZcDg6L4EagDxEwj0Zur7W9rZ6Z2oaZej8PD8+0gyk3POnweEu" + "muivx8ezgDY2lKnppj09ZduOfyAdKAmAlJnoaTY5P6/1dZnp8kJNM9HtjtJyz98GuItMUuTX" + "42O1v//Z71vbzl1dDQcDmcGZHiUiRxySXi0vF1Iecn6SubTUWVgYPj3Vq6twz4gSoQgHqj5g" + "/7n+Xfw/+0ngG/KLPeZjxNoEAAAAAElFTkSuQmCC") + +index.append('HN') +catalog['HN'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABYklE" + "QVQokW2RMWsUcRTEZ+MSc14fIxhFCNjmCyjIgWI6BbvUfgkLEYuDQFC0SKHYiKBCIJ0g9oel" + "pY0WRi4KWpjdsPt/82Ysluvy+PGYx/CKYSpcfoN/xDAUCkEhBfA0UONvebRzDYAEWZlIKWmm" + "STPFFKmgmH63+7pGUQkd/uozlYmF7aAGSlFhRtH6hRHQ1dOnm7dvrJ10aTtlpwVbzoFwQpIl" + "jUc1m+uYz+eSJGXmcRN7b79HRN+Xru+7rpu++Pr7z3Hbtk3TtG07m82WhrS2bZ9dxp3Jedv7" + "n47efziyvb21NlquBteApCVJi9t90cv9H7bvTlbv3Vq1/ergZ9fnwjfJ6vHzz1uTq+0JJaVM" + "CnDKkpRmyoYkyeNz9fTZxwrYvf/w5rfDNkLBLHQpGZGFjlChI8gU6Y1L4y8HT2qAZ2pcuTgK" + "ihTpQQTFFNOZHh4sA6iAB8DKoshymhhaH/bKf9m9dMO0S+5pAAAAAElFTkSuQmCC") + +index.append('HR') +catalog['HR'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABbElE" + "QVQokW2RMWhTcRjE78VUO3QRRHAIiiCU7u3cDl3EVQQXETrpLAgFHQulgtDZxaHYybmb4JIh" + "Q0cHt/jImOTl/d9L/t939zmkjQg9juOG301XBP5J//cbXQSAw0MAIQFApxMK0YMqLMMs3OM6" + "6/PzLgAg4k8JEmSYvXt+4g/Df4c/0un712E5cg6zotcT0AEQihVt5PHXt5/Wp5+/HJyuT8wW" + "kfOVzQSApCRJJOls29YuL/Pe3mIwmO/sVFXVNE1Kqa7rlNJoNLoakHR3d2+aJu/uLum2359M" + "p0t6NpvVKZVlCZJL2tzMLKW0GAzm29ttv99sbY3H4yVdVVVd18PhsMCLn0evHpeThTGcMmo2" + "/HX2/UOYPXv68faDTaNcckbv7p1vb350kd0lo1aDW/efvDw4M2qNMspcHuEMKgB1keWKextr" + "TpEygZQzSJmCFCOcoQhXACqwfwEX5o7syEJ2zK9LdrgjC1wdjb8S03cdKh6exgAAAABJRU5E" + "rkJggg==") + +index.append('HU') +catalog['HU'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAAA7ElE" + "QVQokX2OKVJDURBF76v6OBQuIgaWwSLYBCoKn11lH8kWYsAiolJ9h0b8fGa4out01elh7PEl" + "AgBkAS08VwETgPV2C6CTTmBDjgUpUpMtNRkp5GG3mwAAzeeXtmE3GRtkyCZTDKurQl6t15kv" + "dPqz3VUtdTGssLqYqnm+gGF7jAGgu9Fo9F8ZY5xOp/mli/UOvwZjJJk+pP/3d4/uJAMP2Dxu" + "jq9HWXSVWC6aNMvFpZV1d3O7fzpMOEMSTeoilepim2XSJVO22gAmFBitrlcMJSukyVASm7IU" + "WTZsG8DAPVBAAecfUEDjW94AIuRtBo6TpQMAAAAASUVORK5CYII=") + +index.append('ID') +catalog['ID'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAAA9klE" + "QVQokX2OO0oEURBF7+vuwEBhxsBAtyK4GkPXYeYCDN2PuAADQ82aEZXh1f0Y9AyoMB4qKKh7" + "qqq9rEe8CwAACwAMGADAfe8f/YRZp3e3AGLDjhQ5IsyULIYMK8WQz/cPE0YA4NsrpEghd6Vy" + "MVVmpSq9j+cXBiYIkZe0yZBghWUyRVcPy73CaiSBiYC/PvXxHgq7fUx1V4XlqlS5d7Pa8UkH" + "ptXN9dHllTabSLHi5THFCmUxy3FpXK3Pnh6bJABJgiDIYYZhmOd5st1aS7LTDrBMSQ62/4/+" + "0SbbwzAsDva01vCLBgSA7cn2drv1AgzDB1jUb/9idu6XLw4+AAAAAElFTkSuQmCC") + +index.append('IE') +catalog['IE'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABFElE" + "QVQokW2PPYpUYRREz/d4gjhM5M8wgyaCGxGMXICJoamZgjCrcCsmsxiTCexAMNIWH9+tW2XQ" + "jYE9hxvWKeoOPsMGgEHs3u0WFsAHtD16+cLGE098n5U916+uj4n44vHFGAskCRArn94mFStd" + "+/dfVlYg337v2t3de+3P753nH39+5fvXZKZqPHxmWDF22i23unKKZnomRZdhBZpWJEvWSdyp" + "Gc+46AJWhNzVpb5TSDSTGRWSD0Jbsqan+kSw08dJ4zhJVGr2VKt6Ov7fqRmKVlo6TmpV1+Hu" + "+EEzFG7cwMpGWZdnl9Ul9TIWYIzBgWXlyXNounEbBh/hJwg22Lj5cHP14Mq2JNv6cfv09RuD" + "jwX8BUrDcqkVhANcAAAAAElFTkSuQmCC") + +index.append('IL') +catalog['IL'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABDUlE" + "QVQokW2RsUoDURBF54UEwcZusRDs/AlLBbuQykKsRBBBAiJ2aQQbEQWtLBQbBe3SaOtfhEBK" + "q/cNO3OPxYbdjTrVfcy53Jl5KedsrZIkqRaS3N3M3L16pqWdfD8szCwALGSShYggZB64CCeE" + "O9dH05RzLooCqBIqwX+VUppMJt0aatO3LwhO9xYM1ZzdauI5jT1/MP1mpcdqh+EdG2scDxqD" + "u3dqGsAYbOIlJ7v0+5Ql+1sLEZI6kmoaeP+idMZjbl4pg6fPFg/u3q0NVc5hH+DqkeUeD2dE" + "0O42O/w6zvlBfZnGMF96fVuXF7gsHJe5m4dFJA9zWYSFLCKFEPY2UprNZu2P/CvaZWY/KQh5" + "EgUg+FQAAAAASUVORK5CYII=") + +index.append('IN') +catalog['IN'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABQElE" + "QVQokU1NvWpUYRA93/WiGIv4s6YwMYgxIBaKgq8hPoLgI1hapcuTmN4mYJNGsBE7qyx2W1nI" + "Jm6xd86Pxd1dM5wZZs6ZOdNyhk14TAOGCRseG65HogeAw48AArcYUSCYLbxmdqiYcSVM6u/X" + "zz0AIKkZIlhJIYorqXUd4iGuduOhid47r9vNZ+h3EidCNP199/t0X9arR+eHk1nCmA5bf98P" + "TltJXWsJkozfPp3+Wl4sXjy992N6+e7NYydxnHSt/ZlfdB2AAMkGi8XyYHfry9n55WKZJE5G" + "CYDdja5A0AIkyfODO99+znl96+WT20lW6gpu+IDjt8ez+YwmVSWWi2KpaJaKIkWae9t7J0cn" + "PYiR2uz9XzU3l7RkYUCPATQntyajzaqaNGVJYihJkSwYDe+BASAwXAGvMFzDAPAP+jxNFrI5" + "ksMAAAAASUVORK5CYII=") + +index.append('IQ') +catalog['IQ'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABXElE" + "QVQokZ2OsWqTYRSG309/bCnqICYh1IBCFl0CpR30CvQCpO1lZO6eqygddS9CxE3U6Jq6KC0B" + "xRrBVmyaNMn3nnNeh9+iQicfXh6e5cBJwh/i375wSQC2tgAoAhFwlzncZAYzkfrL493dAgAg" + "fTmEO9xFlv69TDErZ5Gp0QigwNqa7t5DpQZBluEhN7lklFHuoilCeY5KJbpduHtEzMOefeqa" + "+czsjPnl4etXX99MZrPj6ejH2cn2+53ReDyZTIbDYfkSXnx+/uHnx/F8VF1ari7eePftbXHp" + "8kJaPJ2f7h3vHYz2FdpsbkREAUDSo1sPs+cH9fvufu3K1dXqioTb1xuSVm62nuw/Xb/zGFJE" + "pFar1W63j74fJSWS5kYyPGik0c1pTJGmnNZqtU6nU/T7/V6vNxgMSJLMOeecyyBZlhnNvNls" + "AigAkFav10mauVl5SDMrbWZ+Dv6DX3iOQn5NOXoqAAAAAElFTkSuQmCC") + +index.append('IR') +catalog['IR'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABVUlE" + "QVQokV1Pu05CURCcczkBlZioBYUIDVQW+gHW/oKFf0BHb/wH7CyNhbG2wVhqQ0FMSOxM8BGs" + "1BARgvfs2R0LHkEnk9nX7CbrcAL8YAoD4kwXGWbq4THE0f4RADMzmJpGU2WMNqGIxkgRjaJy" + "dXrl4eHgeqM3o85NkRpUokqwICpBg2gorZaRwDcPmxvZjbXsWm/U21zZfPl+2cqXnr6ftlaK" + "z6PX0nKxO+iW8+XHr8ft9e3hwdCpqnMOAEkQBDlOeXdLI/f2uJTlDM65wWCQTL6d9ibx8oIf" + "78x6np9xEYCZOVWdLkzvk6psNCjCep2ZzNyfJEm/388c53KoVtluIw3sdJim7HS4s0vvacZW" + "i2nKVgvjMR4ehjfXLgUytZp1uwiBIhThPFnIoeoqlc/7e29A8pOiUKAIoiIKRJwIYsREY4Qq" + "VBGCAW4E2ALxt/w3AvALz3tDHmqDQaIAAAAASUVORK5CYII=") + +index.append('IS') +catalog['IS'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABYUlE" + "QVQokW1RsWrUcRjL7/5/h9ap7VXhKLQ4uvgABXFXN8EX8AlcvB4FEScHQRwENxEHcRMnB/sI" + "DuImOPQG0bYcHBSO70u+OJzn1AxZQhJCGm68xgr6+uBn112Tur2XCCKIBRGECARAIHoAzw72" + "AVS5tTacHADt8MlN0SyTRVmlZKXqzfhdDwDwr7OFqgDw+Bjw9Pd50qkiFayUyBptrQHsUVWC" + "qiTbNtN2sEinlKykgiWJMhBNUmsNgG0Yp/fvbb7/4IvQWpvP5+0HMBw/4nRq0pmViUxnVoYj" + "K7MinCzGpd29b1+OmqRlvGEYp3dvb378dGHDYDCYzWZ9t/Ni8vTW9M95Um/H+xVp+87kKLkc" + "UMliiazdq5c/P3/VY0GqlrLtirCdVPwzmNTSQAFgjwiqrmyskQLQj0YARsN1qpIlWRKrJMgF" + "VEP3+P+RZ7PD7xtb109OtrcfAgHU6uBaMf4CWkFmlGgAUDYAAAAASUVORK5CYII=") + +index.append('IT') +catalog['IT'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAAA4klE" + "QVQokYWRQUoDURBE38jfeYIRAhLIObLyFll5BzfmFF4l98gJsjUXkAjD7+pqFyMjCaKPontT" + "Dxp64A0mAAzi/Hz2NQ+bjWFJ48L+aQ/Ydnkcxyqgalm7XUWUVBGXw6HRgHr/PKczM396C6dT" + "9V4Rw2plaBi70imnMm7bVdX7LBBhaECSKsmSddu+FoCGkDMylP8JkmchLVndXfmXMEQY7hBR" + "0bMrFdl/FyKQStL3SanImPO7oEBJJtCYCGu8HyNDSgAYhoGF9ZrMOYaBF/gAwQQTx9ejJNvz" + "BB632/nHM18uVneE/JK6zgAAAABJRU5ErkJggg==") + +index.append('JM') +catalog['JM'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAAByElE" + "QVQokVWRT0gTcBzF3/5EtZpmG4NyNYsyvYycEZF5SCIp81DQIUQooVMFHYPoEkg3b0XUIZiC" + "B7FDl+gfHcpIzCLaIGxtFGg5so05a/u+7/e3DjOoxzu8y+fw3vPU3mGhjFMfMZ0HADjAAfqP" + "BagAEbyPIxqA53QXkmNX123AdME9LrqSmsLMqdZUnSq53qv9jTwc0l9F7u544J+YQvf92pHE" + "/P49tt1r977zzbKxRjqSjAd4ISzRoMy+5tjTbUuAD8DezkOPXhTlt+1rYXfIfE4+rKhzHAjJ" + "5agEybvjcv0OqxbMZrN+AGZWqerEE51N62AfT+5g+xqasS0gqbSMjHImJSQ3kwC8AFSNJMl0" + "htduMvdVWoPS1iC5L3LpBmdSUpeqrgJmqqpCaY7IlbMS2yKZoswVqrGtMnyxuqt5FSAJwA+A" + "pKqc6NLB47I2yMl5ufWN4ng+LGcSHN0pI0nenlRTRb300Z6Oob6l/l7+JIdzksxL2Vg2eV5g" + "qsTORjl2kPGYflpomvu86GlvxNTLoY2b+OoHx/O2KKxvqk7pqNQmn54LW2/ECnmL9aQ8K8+w" + "UELiLZYz/78rf3MFUKAFDw+gtQF/ALzGPhZXojC7AAAAAElFTkSuQmCC") + +index.append('JO') +catalog['JO'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABLUlE" + "QVQokZWOMUubcRjE741vnYSCuGQIxGq/gFvWTqEFIZMgCG6FLo4dXF261OxuWUqhIIRSCIqQ" + "IWu3fIBAuxUkU+B/zz3noJEO7+KP47jhDg5XeBmVgX/n5xNgnpmZkiIkxRMk105yPB7XAHbg" + "4z9/76Rv0pKUxGcKC0sphWSn0wFQA3Aa0jtpl+z2P/j0xE1UVbVYLFoJQMLBgTO7DE9+eTRq" + "HNjOzBYA7O/77My9nkkvlx6N/PWyoQ5ERA3A87mHQ0+nYLFrHw786WPDJRtAKwGTvr3FarXY" + "Kn7fb2wDeLxUJ7ARYfLmLYf9Teq6/PxOkWJRYUYkKUZq7/Wb2cWsTuB+M34ctX/vvWorIoMi" + "k5ERCjmYIUmWUghUX4DPA2AbKGvFfyHWIYEAgAe4hlu5xLXqFgAAAABJRU5ErkJggg==") + +index.append('JP') +catalog['JP'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABA0lE" + "QVQokXWRPW4CMRCFx6utttgG0VBwDEoOQENBS4GUXAwlHeICFDTkGlsgEmmbRUKCImNrvhT+" + "CSnyZNkz1hu992z3eDzkCWYW9+eiIJjVItI0jYgAcQZil48n9H1fZxK/xfHI6QOvzGYsFoXt" + "nLOo8Ie937PdEgLqOZ34+uTlNQ5Eh1VxD8gwsNvhPappvb1zuRSREEIVwxWP3G6J6hVV7nfO" + "Z7KEmdVmlntkPKaq8Ip6VPEeESaTlAGSpcJnNGK55FsTW5XVium0vJiI1MVSmtxsaBoOB1SZ" + "z1mvS2IRCSG4YRjati23qYg+nctpRQQR6bquNrPr9Zo/Mliw/xBFfgAmk2yfBC2dxAAAAABJ" + "RU5ErkJggg==") + +index.append('KE') +catalog['KE'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABeklE" + "QVQokZWOP09TYRyFz3t70UJqdLHqUkg3Q5j0U/gRymBgRcNsGmUiYXdnYnGtiwvRgaXuRG8b" + "Y3JvA9II0qCXe9/z++NCE1efnOFJnuUA/0kA0O/3AdgcVSUpIqLqqlVdC0kRkoPBIAEA+GQy" + "KYoiz/P89LS3vt6ezR5c/NrY3PxWFOPxOBuNsiwjCSAcD4fdtTU38znldGoHBxCxXm+p3b4J" + "ZknaOBq8D2fAndc7OjlxERdCZKf6sx3SBdU3s4vd2qyqPUZXTZc7o8MPaQk0y0qvfruIRyYm" + "57zWhaZTp+W1Rvc6WqRLRFVHIL0FJElACBbgIQQPl0J4I1FtqQWKMYICtaAGIBx/+dhdeaqq" + "N1fdLn/keLufiNZbz+896ribu5t52kg/Hb0LeIUXz15+vcqopMVoftsWn4wZIg+7PNef0UpR" + "iunq3cef94YpKohwpdmhUkxopPH7qtD4UOW+tdxBE3UxNVQI2AYqQIA4n/wjMhcDBGjhL1ti" + "PSucHkU3AAAAAElFTkSuQmCC") + +index.append('KG') +catalog['KG'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABKklE" + "QVQokW1RMWqVYRCc78sTKwkGLAK+RhuDheI5cqPcxMoz5AKWYhHBwlqESIJdIIG8mdkdi/9/" + "iYXLsmyxuzOzM27wGL2v/zYPaQDABsDTszMA6UY3quJCOXbsSLEhtR3pz/n5BgCQvvyNqlRF" + "wpMaW2Gov6tvFbHJSHO7NbBpIJ3H6VGbU84PRmmccPeJfaeQkSI1MAGg6gF9vuF4LX+lv3C8" + "4sEJQ4YrSAOzAbiWA6HGC+WAuWZdsQfzck+JjO0FIYtEMWL9Iu6YQ+Jol1v25W6dJlvCqkFa" + "DoD0N423nO+VUv2gPis7Rmp72r0uLAJWVrz/yPlOuacv1DcK2RZcqdp/SR7Hx0OKq60h5adi" + "z+caz9z2XH5YBWBcAd4b2f9zd3F9sXkCfwH251SCACM16gAAAABJRU5ErkJggg==") + +index.append('KH') +catalog['KH'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABbUlE" + "QVQokV2RsWpUYRCFz/29d/eGda9rWMiquGhjKRYGKzsDbmETfAftFUFFsbA14CMIqQy+gcRC" + "3yI2ErUQqyAmO+fMjMVNIDh8DKcYZjhzKow/wQJ9CfAAenTSTwvUMLzaugYgAx7pAffwSCno" + "cIXU6yRj5+2XGvjTpv38tfTI8JBnP2EeYkhJBhkmv7TWAr/rz8t7V388tf39cE8yPdyWg9ks" + "PA6/7kHuy6M0S3Iwn9/GxwIA7ilVJFyjG9cHk3Oz588uvHzRjEdnb63DlWZpBhJAERDyIEVm" + "VVYfPBzdXaBdQdt2m/enjx5n3chMZpKOgBoAXJCSdmalK13Xbdwpk/Np1i0WZTIpg+Z4fX8h" + "gCTDDO4X32wBqJomM7Ouq+Ewgfn2dpUeUkgCKuDdk9c3v33/S4ZQSCeDdGNQQYU8eUi5X7k8" + "2t15XwPdAYfjaUP1XoLHjw8p3VO9iDzwBKYV8AGwkyD/43TSPeUfjDQzrVjF/Z4AAAAASUVO" + "RK5CYII=") + +index.append('KR') +catalog['KR'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAAByklE" + "QVQokU1RS08TYRS9X1uYdsa2004Z0o4kIMUJRlRSFq410oYfYHDnD/DB1hBt4p4FC/snhLia" + "hYnB16qJj6hx0UUJAVL6mIlTtSl1Pu9hMWI4yck9q3PPyRH94YDOgJmJiDlU/3gKycwRIkrF" + "1VRcbe3tf//8ZQwiFdd0VU1rWkZLJqLRbx8+HjSbE+m0qWeZORYa93o9x3H8H37ONGdnZtBs" + "QkoUi612Z3t7S89kjGz2/NSUDD8A8H0/CILFpdJsIY+NDayt4cE9PFmfNieWV1Zc1z1qtwEw" + "s+gPB0klQUTdbldNJsWrN/FnmwiC3zIyGv3R767+XL3T77kFqyCE2GsdxsKWAHK5HBG9dFMl" + "GQ0iiaeLD1sjZf3g01VF0awCABJCMseYCQAAz/POaerAXrhvP4qOj+0rpn88apaW7eEv1+vn" + "83kBEHOEmAE0Go1areY4TuVaYu568TCapeHxzcvjt25YOzuvq9VqvV4PO8SYiIhUVQXw9t37" + "+Uvzj29f/LqkyL9YmFZ2dxvPt14YhqHrOoAwEgOwLKtcLnc6HdPME9GVC2qYc3KyUKlUDMOw" + "bZuIiEkced7ZIf8fkixPhyYpJRExEfEJhTEZN2ZjOv0AAAAASUVORK5CYII=") + +index.append('KW') +catalog['KW'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABKElE" + "QVQokX1RP0odcRic3awi8WHxTMQnSBpPEN4BvISeIZ1t4J3APuoNcoBX5AYewNrWtCEQkeU3" + "f74U65b6McUwDHwzTAcA18AeMAIjoBmZSZshQBgAbLLBV+Rjwris2JFKihTSUsiI5vZmOwDA" + "cz39/O1z+9A0HTNkSJNmc2tpNE8XpxgxAEjK/6xfvry6OPh84DiJy6kkdmVS9nf2d7/tDgBs" + "S9Ko9XJ9sjqpN67ruvViPQCQTFJSkLfcVYWuS9IDmB40Nr9jr0JVkgEAydZaQbq/z3JpT/ld" + "9tQCSSX9YvF8dzdF0gB+f+GX29uX1qq1Iqu1kK9EhPzh7Ozvw8MAIJ1+fFodk6PcizX5pJ6c" + "xuht2GX3QAfgETgC/gCZ98VMJgWzCOA/nWhlKT+0ek8AAAAASUVORK5CYII=") + +index.append('KZ') +catalog['KZ'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABk0lE" + "QVQokU3R32vNcRzH8df3e75a7XA2u2CJNY3UslLIzYpyoZW7ccElhRu5FFfu3C21qxUllBsn" + "d9Kio/wqV3JBmnEzK3ScTZNzvt/35/lyg/wBj6tHVmv5g7Lxwv1SiNWQQkIKBAIFylGgQKWy" + "WsudnTPLGnmk6WRhJQko3O1R66pIOOGwK/vai7VC0rK23aqmcwsUdthbeb/HrTYbH/voGn0J" + "B97cl6kklyT5mOcS6tnJmGqf5ye5f5DmAR78whWu7MCCPIlhf7zL2bAzVzndutvjfmb1Glra" + "64d1VipcJYetUrmQnY57trQH+NJPexevXqf9jrITg29j90R6XuEKR5KCQqHPHrntk+Af1Ie9" + "OOEn9zi1wPZvDK3QOOw78xwJHFhBrsBOJ5gBtb1hlfqnNDblG5NujvrNeV9oMxQ4ULJFFBJb" + "WLjIFeFkLbLjHWPr+Trlm4MszaXLT30o+wMkVCjA1bl115s+HSisCgWbXvoM0PHAKE7OEk6W" + "gkzNn1ejcSn//u9S5d/X+G9aqCvl+g2/aVpzpMUQkwAAAABJRU5ErkJggg==") + +index.append('LA') +catalog['LA'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABZklE" + "QVQokWWRvWqUYRCFz/vtZ4RFTUxcFdQUbpne1k5IoRaprERIY7edhAi5BgnY2go2snfgfZgi" + "IvnBaDSJZL93zjljsRhWnGJ4DoeBgacI/4wB/4WLOMtFQNncBJA27JRAQQSZZEZcbEScjcft" + "qPd0bfDg5PA4ZcuWaieGRKUIWaRIBudvL25h3G7fH91bHO6ro1NCV7U03z57NMjMd+P9nb1J" + "ASoV9J0bl3fwpgEdQnUJlQldmvLq+d2VYX9l2N94sTw315zV7Fg6ls4FqA2qlTAlKjotXGmu" + "9nuZmZnXr/VuLfTqeTDIICWADWjJ1ajKMA6+x8lvTg+OfsXuQUegY1YmIwG2qKQywlTaOD7n" + "xtvd9Sc3AWx/2Dv8yaag0qKVBlwe4+H61ssfX78pJIrBOpn+IIWblCiJopeWB6OPr9v3+HTp" + "yxCMjEgqGXDYkcnMSBIkJEv4rFWgnP4nlTM8rTjj/g9Gz0RszhHEXAAAAABJRU5ErkJggg==") + +index.append('LB') +catalog['LB'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABUElE" + "QVQokVVRvUoeARCcPQ/tRDu5iILPoOYt8gwJMVY+QXwAbUVrIXU6LaIhpLIIEpD4RUyhRJTE" + "Dwsb8TPf7exOiuNOXZZhFmaW/bHfeIxssSFsebacQAlgan0VgDKRqQhFiFQQpOgiky4S9J8f" + "dkoAKMu4/oukGAqq1Ymu2tNdrFV7UU0nUI5eXY1VFQBIkgC7ub/ZPtz+N3x48/JtNV4pU5Ik" + "M4wuHRUoCpjBTGawQob98y/H/d73/tHe2b4rBbRpzCwys+0uQcf93ufTvduH29qHu72dX/0T" + "dQFkY+jUKW0dbIZCCQ+vw9e+rrPTSyTLztCssfFq48/d9fLHpVC8m3+9MLMY6YaRZgoAdgq8" + "eL8Slxdine5yz3ooOuuhkSLlFF3Bcnbu26fDkoAGA5uYBL1giG7uohfOpKO5coQYuh8AsB/P" + "H5lPHtyVaBHAfxBTROajm4qVAAAAAElFTkSuQmCC") + +index.append('LI') +catalog['LI'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABVklE" + "QVQokWWMMWqUYRiE5//20xAli0ZYCLJCIgYsAkLK3MAjCN7CCwh6iTQpxUrSBbXwAIKFGJBY" + "xEZRgmBgl+Sdmfe12KRQ4WGYgYcZMH2BBBqABgESYkHCCQgQEJdFHcLT59t3Vn/evfXtwfTL" + "+6/rh9/Xj09WpVKWlHJKSaWcL5/tdzQA9XBzf6mfNcXO9GhjvPLk42My6aRMJZWkb0+WATUA" + "WTj4tHX96nxpdCbr1YdtcqE6lEEHHUopgejIxhzeft56c7gp57mvUAibhogQ6CEECucG0Pr9" + "04N78/GN2W+pLFu0JKVo0pJNSZY8uXbzNXb7u9neyq8xfhyXo6gii/F/pjhqG48w63Ng2dTC" + "ViRZihRLTEWRpSixpJF1AnQAJRWZ5MUZo8i8+GYxSky72Qn0BEoaJmtNhJzkYCbZpBQhlZTp" + "ZsNOYDgCBORl4u/5DwD+AH37YQGY0d3gAAAAAElFTkSuQmCC") + +index.append('LT') +catalog['LT'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABIElE" + "QVQokVWNMU6cUQyE518eQjShBgpOgOi4QSROxAWi3CNtijSp03ACRKJUVFAgBaVCK4E845kU" + "b1mCZY0+22N74W9A2IYFAzDgDVuvLHiFAWF19AlA0oh30DsxomzTQpgI5tOvr2OsgAXRw5Ke" + "PqTnGJlZMWMuu8cAxs81jkosJj1P2kw0TU55gmt3r64fMc5+4PLj8/16rZYsmjTZLJNd1cXm" + "1JMPB1dfMPACudmUJYvNcrFJs7qq39bUwhoDQEeKyqWeH6q8WSsXJ4cMAYxvn3EaPt+Vqai6" + "aE0bzdpoM9T+ib7fYJwDB3/VfxjSZKpStYFtR4x67PcFMApoKoeHIaGOGBIkJJCLBCndS7e6" + "X4DlFhDgV/X7Ev/1DayAfyUid0PrCp4LAAAAAElFTkSuQmCC") + +index.append('LU') +catalog['LU'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABGklE" + "QVQokX2PMUqDURCE5yWvUETjD2IEQUERbKy9hZV4EgttAxaewnPoCSw8hpW/gagRw3s7MxZ/" + "TBp1WIZvh2Vhkk9P8D4BAEASSiBmKKFSVKoKVCEsJ2PyiusRAEvJQtAWyET1yV5UkyYd1eR0" + "dJsxK6jF7QtEUI4Aw6SDjuoajuqorjUNdwRknJ374Ahb26BsgjLpuYdJi44wicFA+8M0Ho+b" + "pgFgG4bhv5RSats2d3W7aAG/CilJysuj///byZaULx83Lo49+TIF2pQph7zgro6EZtVX9/18" + "96TDzf7zhysRcqUrHXSVYwFyCHvreHtABtRDb3cNYQdBgUJV6iDUJYm2BGSldPPpqRBCEYoQ" + "QPlZpTmrc2AF3+HEeWs6fQpmAAAAAElFTkSuQmCC") + +index.append('LV') +catalog['LV'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABGklE" + "QVQokXWRMWpCURBF75OHQhRSC2rhXtJkKfYhhQtInTLbcBNi87dgCEkRSKGSgMydO5Pi/y+m" + "yDBc7oOZN8Oc8gyc0UUA3ut1Wq8DoH4Dd+s1gIzIiJTCFfJ0D/cg5R5kuIt82WxqBYD8ef9I" + "KaQgWxUZpIyihZnI8XweQD0DHsnrajO5h1E00WSUmcgheQLK5243WSxklpmISCDa3TI7E92j" + "Doev222RVErJzMwEkP9HGQyOh0P9aprxbCYzdH9mXib0vh1VR6O33a48Aver1XG/D3eZBbuN" + "dTFm7ZVul8unpqkBhHuS0aZZmAWZZklma5zpSglANSDpN9NpkOGS9509hHSX1B7dgfIAnHqQ" + "dgXV/mJu8U+AX3wogUmU2HzTAAAAAElFTkSuQmCC") + +index.append('LY') +catalog['LY'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAAA3ElE" + "QVQokVVR223DQAxjAH+kE7RARul0QaboAP3IYpmhEB/qh2zHPgiCdEeKPOiCH5yOgADaYup6" + "5wXA4/vRQOB03E6iSC21FCliKIvN5+9zAdDo19/LsduDGNBEhXQxvH3cVoV0HM88xwwVlYtN" + "hjRpVkotFBYEhsfDoFdQyFRtdbloQkOI1WLkaIOyUjQrZGouHaOwQHBrbOzo8XAky2K4EmTT" + "3D866rsIm/MqCxlC62T9ML5ChTIZuw1hgcDw8/rliJHylpJlSLYit91GcMH9tMg1T5FDOysH" + "/gF/KJDzB2IFMQAAAABJRU5ErkJggg==") + +index.append('MA') +catalog['MA'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAAA7ElE" + "QVQokX1RMUoEQRCsORe5L5ywqWBiYuDj7lf3jENDQQMzzQ3uELuquwxmj90FsSmKYaaLrqlu" + "xly1Pv+JZgD7PQBXoQqZViJlCZJJL/h0OAwAAPvjE5nINNl5QtAMR5hs41jAAMDlVXdESSYR" + "YYaDXQCygKGAlgnJEiiL3+bzPUt8OMb1T5izAMAGALTwQL7fxHkbp228jpfuDuliKWXJDFAm" + "b9/iy0zF3Qu9mNBmS6QjIPWHDePxiSaLCz+SJXXBVf/iFMuUiSPmGxFKZKILNhR2O5NQQgTZ" + "SEjoLPUMkVlAO68X+c+Oe/0C+ctawBP+hnQAAAAASUVORK5CYII=") + +index.append('MC') +catalog['MC'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAAAyUlE" + "QVQokYWPS2oDQRBDXw/tC3hpmJP6Vr6BT+ELJGDwNqtutZRFD06GOIkoChUl1aeEL3jPX0YJ" + "cD4DsbEZIxoMRUJK7/mWPy6XCkDy9s4YjJHeZ96i9fSW1tJ7WVdDBeLs1K1F2qS9pfVpoHdD" + "NZQxkCLRFb2Y/TQAlcOBdU2tGZ6nbxf/eIDTyddreTwex+MxSQghv2NZltvtVm3PGv5Sz67t" + "ansW/xoASduGp2eilMIOBbZutX2/3yVJsj0XPrkkY4ykZVmAT1MWYm5ocuzlAAAAAElFTkSu" + "QmCC") + +index.append('MD') +catalog['MD'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABW0lE" + "QVQokT2Qu2pVYRSEZ++zTzTeQIIQIl664Bv4AsHCdGmigpa2AUtbGysfQHuxsbKLaLCxtROs" + "BI1gJKJED8k/s2YszjbDx1SzZmB1WNzGsYT6tIYB+AUbPgUbH1ZhQBh9APDoySqAMmx0Sw+T" + "4Cy71CQHfaZXthQxJVMfnz0fACDY/cGqVCX6gunBq5enXby5Pkt49JkhQw4rFxvQwyhjnlYF" + "qb3dunpp1g759dvfpIXNIzLQQ3CgisqUE+68Xjz/tF1/M9vZXoibG91atRZRwICyFM1dTljV" + "nbnRYup7HzdTJkP+P4DKJq2K6LhJ3cbbx6TvXNtKOKZJSwYGwJKpUC46aZu32I4elNrde39i" + "pimiyajGBRYozytiZvh5e4N2S8f4pDl/a7nGBUleXppKYRmTFfjC5Bx7K/mNyYmFy5UqV6HK" + "QAe8AHTM/vv77uFD2CPv1mCMAPgHcLpkVfGzF3MAAAAASUVORK5CYII=") + +index.append('MG') +catalog['MG'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABAElE" + "QVQokXWOPU5CARCE58lLiAlGEgoqGhsu5Dk8hVyIQ1h4DUQSMaGQhp3ZWYv3g8a42Uy2+GZm" + "m9PphGFszxeL/v5nWwDz+RxAVQHAZlPHT6QaaSLdkCXVoOfttu3yehqo/R6HQ5H9BotREUU2" + "q1XfcKWrIFVEST3KqGBnAGmgtX2lCxX6mz0aMBqqqus5TnmehcUizTBpRjFSnN7rZXyp+nw8" + "PfL9K6IYk2AyHExGBs2HO75e0Noe46sqFRcGzV90UlZOhI8fL3U65g1KZsiUU05oaBg9y9ul" + "0yzKoqmUSpmZlelEoNntdrZtS7K9fl7jDTBwAwgQEIAAAwJm+AYksGMfxpLrpwAAAABJRU5E" + "rkJggg==") + +index.append('MK') +catalog['MK'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABiElE" + "QVQokT3JPUiWYRiG4fP1e9vCpqAiJQpC+gENnGyoraGfKYiWmqwxajGCoFGniIYmCQKpKcqt" + "oqXBisihJSz74cOhwETy07qv+3muFgnO6Tix8SUMhvK/LuryFzagB79hFVZGWF6hZQJu3+Aq" + "flyblUopzkJk02RnPPtCzvRW+UJ6SGunn7ZMwg57dInzhZnid6IUL8qt/FkOeTh8ObygZnqg" + "vqIF/NPcK+wrPia2FT8Lz6eRCZ8IH5Anw2/EoCq0dZRmsPAnvZgspA/KR+RRucrL4V/hKXk9" + "vEsMiRe0vAaK1+W15Hv6vUx4TLb8IdyTb4b3hvtFf9brtBzFF9Mf0z+CjfSQ3AnPyQ5/k9fD" + "j2SHt6s5rHqHts7RjMhfg4H02fAX+UlYsuWX4ZPylfADMZ9ezYS2Qqc/PS4OydPy83CEh+Ut" + "4Yfyffl4eEq8LcwWoK2n6JtIujt9VywVdgup2S/6kj1CyafkVuFa4UypAzS9HvUcdZYKFYAK" + "dY5aqWObuLk6sMA/749Ehz/eU98AAAAASUVORK5CYII=") + +index.append('MM') +catalog['MM'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABF0lE" + "QVQokW2RsS5EURCGv3Ndi0i0KLZQUPEgHkWv3YfZnsYT6MQ7CMm2IuJirzv/nDOKw41FMplm" + "/u+ffzIJbsABmIBBgRY6cVrgb7Xgs9nB7t7k5Hjz4yOur7u+Tykr+dmaeyOFe3z3t6urFlhf" + "b44ON6bTSSn0/fZ8/owsdB9SmEIWZiGl6bRuoJSAiEgRpUSYleTlSyoLUwWQKtD0Aze3vYn3" + "Zb64fJEaVMJs9B4B6n0xDHcPw+Pitet8+ZyTUaQVdQXcC7RPnG7l89wvSickpJAa68OG0fvr" + "hhppB5KWYa+4/xz/UuMe7g5tgTX3+PYeA6xgLjyTMxVo5Ozvh4RnXEhJwp3a3cm5VoH0vvpI" + "/vvuOAI+ATPsVhKSc7rOAAAAAElFTkSuQmCC") + +index.append('MN') +catalog['MN'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABHklE" + "QVQokV2RsWqVQRSEv/3vHxSxiCSFUdKIknfIO1iFQArfJPggvkZewN7WwsJKREEQQbwJyc7s" + "Tor9b+7FwxR7DjNn5rAlbKvDil9wuOk63FzzrG+aDjPA5SWn5FMvt/19f/pPpbXYxV6hx3t+" + "Fyl2pPXV1QxAcvSD340/7bvbX0dakNq7vqbWSOX4uMME5G3Yb1y0SDK1RorqoPXUukDqMHco" + "HxtvnC9msm+X3UOWmgcpEsOB8xaUM+VQ2q570GxG9nJ0ivPamSuy1Ad1sNlxKNtIXXEFp9Za" + "c3e3TcVwkLBjewhWzfkpHinWf3kWgYUbrTEE0wezPsosaC9eTk+EVGwkiqfiV7Q20KFc7/xi" + "hwM+w/OdwfobJ+M16h6eGU4S17GA1gAAAABJRU5ErkJggg==") + +index.append('MO') +catalog['MO'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABj0lE" + "QVQokU2RMWtTcRTFz3v+CZQXaeoSGoliJlts1aVLqZP9CN0EQRRBQQSjgt38Burk7GqHdujk" + "IkjBSRdXoWrqEEig5JH37j33XocY6Bl+nOXAgV+GN0CF/3GAc8q8zPqMCQkT7G7vAvBwD7cw" + "0rKcACc1GapOuqpTTQ/eHyQkAPGnHFiYmXlokcxC77azd8flhHpai4SoabfZhSOHwz3MjW4e" + "eq+TlypFuvS5vClORP20C6EIRU0hyAEYjEGaRvDD33Jc8/bF7f76C7GCLm9/T4QiJuqzAUE3" + "NVXXO22strYeXL2/2lpBxLP1Jw9XXo0rvLyciwmNIM7hFjZ6G6N6JC5fx/L42qPN9mYjb5yU" + "J9cv3DjfaC41Wq9/fNLQxcbi4MsggdBQMaFTTfpH/YW0ULOecpqyNKpGw+lQXek0J4gEgsbZ" + "JTWdarVzZadTdNaW1g5/HX4bft/7+ZGhdKMbiIQK6lwultWUbqTuH++7O52VVXT2Wj0zszBz" + "gyDDc+B0LlLmUqszpuWM/ib+Ab9BPtFV+EpyAAAAAElFTkSuQmCC") + +index.append('MT') +catalog['MT'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAAA70lE" + "QVQokW2RsW7CUAxFb17crfQLYGKhZUAMZeGv+DT+JlLVMjEEBqLQ5dnPt8NLQtL2yrI83HNt" + "yUXTNHBHCBjJr9fn5dKBvyUi8iRSVdV6/YYiACABER4OZdsGVZqx7/fjUdzs4+uzPp9NdbPd" + "kgTAlHg68XZjVGpkjFQtFgsHJIisVq/t/fvhJunOmH2RUTMAVQfE3RHC+25HEgTBTqo98wDQ" + "Aehcw0By4s6AWbehc0zye2C0ocgn/R/vHHxDPM1sfNIEoE/iTWEJKWHYMPSsoiwxn2M2gxnM" + "kFIuB4rL5WJm3qub6/plv//146wfNaZa7RkAoEoAAAAASUVORK5CYII=") + +index.append('MX') +catalog['MX'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABcElE" + "QVQokUWQQYvNcRiFz/83/7lNSHfuNIRmalaUBRsbuhuz4SNI8gUsfAULCxtla+EL2CsWYqGk" + "xJQN6VIyZaKkG9f7nvcci/+UZ/HsTk+dDtcPovUYIL/dnTU0SQAkcbHY29oCQECAgB5ot67e" + "ATqponKyuta6Btg2AGcu37gJWUVnfnhwvwcAY/fXXqn+ZNhG5//IMftsy5lLx48NBckqiSqK" + "hm3P3j2TtHHqwpKtDEgiG5NAg1gWXazKou0v719+//T24b3bH988NuAIZSpSSQE9IkpiMYus" + "sj0aH5m3wwc2T49PnLRkUiWRJgk00FRlMYuhtP309YuvK5PVs2eevHouWxHKNNNMAA0ExaEQ" + "GYYvntvW/Aerts9fsl2RzlSkWQR6BIpDIaNoeTJev3L52v5JEc6wYZVL+4MUjx5ay8rf+bfr" + "OgCDAXStLW9uALAspoAOU4DAAhDwEzuPdkZtRFKSpJjPd6fTBhAA0IB/+qNU8P1bz0wAAAAA" + "SUVORK5CYII=") + +index.append('MY') +catalog['MY'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABoklE" + "QVQokU2RP2gUYRDF35dvveyZU3NiGj0METGNBCzsBLX0KquAVqK9GywMWFqlsBElIiJYpIg2" + "hojYRFJd4Cr/gMihuTtDvIuaxj8k7jez8yx2o07x+M0UjzczDmhWy68X524BqF+89jOdAAyw" + "Jk4P5wTof+qq5fufOh7AcmP8+PhaqZQ+fX7iQ3v/zM0Dgz4jQDPu1K9eL7p+dRE4v9Lcd3Ji" + "pbJ7C5bVz/anXk5uXr4SI6UoJZgIQ/C1Qx/nH0cAANv4Ujl2ZKAS69a2JzMRDD+Yjb2SRhbi" + "vD+aTLkhf+9r35j1Z+7UL00ugzr76NSrtwcflqbLSE3VRBiEKr5We7+w4IDG3dtD5868ILPk" + "xgVRhmAifPZkpOwDyWIHwHn/vd12TeBwMp121yBiKlRhEAsSyw8XD1LERHLdNTr6bmnJSf+z" + "27OXWQaiCJuHNiu8zUiCdFG0ubrqWsBIkmi3ayJUpYjlmUQYAkUsH2ZZaWzsTaPhtjudqFot" + "bHauDrN/HwBoBhLAt1YrChu93+vrZmaqpvoXVNXMkPeagw0AfwC6FlrZi8M1hwAAAABJRU5E" + "rkJggg==") + +index.append('MZ') +catalog['MZ'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABhUlE" + "QVQokU3GPWuTYRjF8XM3d2JtbenSQoRHLXQUOvsFBMFJ0E27dxMXUayOgopzcXByEHEJiC+f" + "QDrVLtVJF4tpkqIxiel9rutcDvKA8Od3TnqyjlvXgFPAFBBgtf9XajNSAN+e3u2s48eSVOTh" + "JneZhZnMRLqZSBmdne1OBnCuHxsvvz+/4DurTtLldFKkSGdRKV7orOYrZGQAoVhMvvner96/" + "2WyfjYiIUKgeRYSknPP+6f08AT7P+epl+/PC2luP9pifkQc0kiylkDSykGR1pup86MwUoH0x" + "bb1eWdiYizQ5Px5cGfV7o15v1Dv81/CwO+x2h93BeAAgT4FXb/zh9cnRu9nZI//ozW1DIQpB" + "Q2GioViiNWgtAOkYaNzZjN2vmDc9uPdracEkRIQUkkKQFB5So3nyy97bDAB9iwZxiWjdXvxd" + "QiXEWoZKBBGeTqytHXzKAmaWDTfaURFTRxDBFAQMIJIhGeAIx7GrjzRehh5DK9BPyACDBBlU" + "INXfIAAGtPAXn2ZJkhleGGQAAAAASUVORK5CYII=") + +index.append('NG') +catalog['NG'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABHklE" + "QVQokXWRsU5VYRCEv3M54ZpAIrkNoaCh4UGIvQ9AbXwBKxore0tfg5fgIShotKEgkWBy8u/M" + "rMWJSiBOsc3O5pvJTnyFBYCAuPtwd7R/lARI8rA8nH85RxAYMDPzxNW7q3WdzvHueLt5A93d" + "wLa2l+8vRVVUrutv1zMz0N9//XBsOwmb/ivHtz9vR0alTg9PCTMhaceK5epXGh4jo1zlYjAD" + "xmopUvTCnWR4DI9KVdYDobhcshQ1rwgaayRZiBnhSNHIkF8S/kXqZ4TqGh6yyqPT/ax0OsOj" + "uhQ5fwiy1k71n9LqUlsxYmahopODk3JJnqYJWCewt9k7e3vmttuOGUx8gkcQLLBw8/lmt79b" + "Py3p/un+4uMFAUBwyG/TfHYgwNw2twAAAABJRU5ErkJggg==") + +index.append('NI') +catalog['NI'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABO0lE" + "QVQokV1RsWpUARCcvXuB3CkcwYQIKmmMrU0ES/2CNPmItPcFgRT+gpWFpSCIVcBfSJO0NldZ" + "KUEfd5683ZlJ8e7gzmFgYZllmZnA1OiwgoTCmkIJ3TaBBh0uzgBAthwUaJRMRmmYHJRcdMol" + "f76cNyjA+NGaAoWSi0g5udJluaOTfjoJ/FXz6gTH+94bQQbVn5laUpUaF91/KPlgjK9vFCQj" + "AoBtGIY9/5DzVspmPPKDc68REW3bDnq3q10/2V3NXnz/8sdMbwKQ1GyqsbqIt0c3fLZrbOnD" + "lhQv3+f0dfxceO3YJUX+prXUhELRZZM4fBjvPv5qbr/p+slwduckOvaZIDXJcpa7PlO6iOeP" + "gE9oUEoMHk9QdBIlJJGMIlIQkUYxZJcAKHC6wHxdZAn/8H+7feUpANjBPS8xYruwWMQjAAAA" + "AElFTkSuQmCC") + +index.append('NL') +catalog['NL'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAAA/UlE" + "QVQokX2OzSqFYRSF16tPUueMJAnhElyByzKQCzAyNXQN5u7lJDE4RUeU3r1+DD4OCqvd6mm3" + "9mq34Ev+yb9OC4DTcwCxYUEKDTEUyBRDpWr0l+urAQDA3D1AgpwiVCmmmKp0pnp6pXrb2zEw" + "YPMou/tYm0AOCSlUyJAfMB6T2No00BaLxXQ6BZAEQZC/1FqbzWaD7TGdZAm/Cq2RHLwM/d+f" + "tARAw+HFydnx7fy1aMqdKrmo4tJNuqSDrcnN+eWA++en1z5fvBVdcqd7qahOL5007cn6KvA4" + "oLvk7Y31sYlyyaWx1aToSJYtjS/hFOhAB/gN+G1DwJ++8g7ZJGpzVKC8RwAAAABJRU5ErkJg" + "gg==") + +index.append('NO') +catalog['NO'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABWklE" + "QVQokW2RsWpUYRSE57/32lhIltVik6yE2FiIpY3gS9hZiGAXTBGxkzyBgTxAGou0wpKnEAIq" + "QuyMK2iIRbgQ0vjPnDMWdxcsMsVwisOcM3zlJxa6a9989vXq8MG8bRNIAMAw6D/vAIx23wKw" + "vfditZQyfr3jECRLFk1ZTMrk99lRBwBw/voN4E8v25rPLYJM0qRZs1ZXttPp4gLCVgCITABW" + "NeVKs5o1SdeaZEMK6NYjSinDSwrbXvkw83Uqpdy/vCzt08/7L9fOe0ZasjIVZqTCVCqSYcmM" + "XB3dOHh1XCJiiDf85v3Zu+eTa+NtN03T93132ra3t7d0+mNldsSw7Ysnj7MySbBm5aIG1d3b" + "PDn+1CVgyqRtRtrOv8NGNenKZDWZEVAI6BKw1Ewmtu/c6gC069MmmBTEpFopIxCREQDKyRLh" + "Rt+PHn68+PLo23isJeMBMJYO4B/0nlqS8nC3FwAAAABJRU5ErkJggg==") + +index.append('NP') +catalog['NP'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAYAAAB24g05AAAABHNCSVQICAgIfAhkiAAAATdJ" + "REFUKJGVkMErg3Ecxj/vEGvZZi1pxEWtxYk4OCvJRUpOjv6M5eCyUu5ylByclDhZOCAHuSo1" + "jNW7wstqel/vfo+bNjNtn9tzeD49369FFWOpS/PS2Q1IDzejbbTKeOpcDqhgWWZg5KTUTCdQ" + "HSqmhA/E83nrdjcW6k/tl1sSGPNB1HGQ69IeDFq544lgYnjLb1rgV14pZ7MEwmECkQj5RILD" + "u5W2vsFVNRJY1SE5tKajhzQGmJ8+4N1+pOzYSC4yHm+FDeu3oL06fFVsPCCeyZD1rklulnh7" + "Xq8rNWSwb1HFdFru/b3cXE65uTmFe5cbzoe6H9hUHAd8H3yfjliMneI2oejMv5IfemOTugA9" + "LS0pv7CgM9ApaA/U1T31p6TmvlDPrKQvkIdkkAzIIAESkvA+r2o63/P7kcPE9IErAAAAAElF" + "TkSuQmCC") + +index.append('NR') +catalog['NR'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABUUlE" + "QVQokVWRMUtXARTFz9M3SBCiQfgHg3RyCFpahKQv0BC0Bq0ObU0trroJTuFnkD5E5Ozg5BAh" + "JZQRgmLvvXvPOdfh9Zc8nOHC/V24nNNg8hbEP9kIgkQQCoBA/OceYAtia+cNAMsuSJJNWTZp" + "SkmTSimpT9sfWgCoOvt9KZckalw7qVtHMqnlh/NAtDAkUEVZMumQSSc9pZVUUJkCosU9zs5w" + "FlmNClUly5JkSeT4oGTRReBv++3j58W1FzmcU4TVQGWWWU6bZdpZps12bulkUe3Kq2fvdyff" + "f86tP3l0eHx6eT1EKpMhZSookplK6vFk4cs+ZpDM9MbT1Xevn29vvpSbrmeX1Q/uw/2gbnAX" + "7sJBAGiBIHV0cvbrz9XXH+d9P+Q0RJKUSFGSbWs86JOuBnsHhxdX3dKD+5STnqKmxBGXADTA" + "+t06x0Zv58Jd3QAELXK+OQKNnAAAAABJRU5ErkJggg==") + +index.append('NZ') +catalog['NZ'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAAB2ElE" + "QVQokU2RPWtTYRiG75ycJD2HNM1Jk8YQa8FGMQGx/QEWwUmloOhoR3FwEFcdpIO7Y6GI2ILo" + "IipiBRUsOuhQ6JdFB/tB09CYxMamyUne53ne1yEg3W4urmu6A5/vTL7bTz54tHw/tRq2rHvl" + "IzeG9MVzJ688KQEtgAEG2oACFGAHTqdvzT2f4GS6vfYdfguZbCyVeLNQXVzfJ9LM+rxb/tDo" + "b/hMbF4/nrRnrBfB1ULkby20Vzd+O+TF26TP+upVbdQnM+4Uc83fdT8+tZMczMYBtl66I5TL" + "h4dzFEvoo0M9I2cOEpml0LEOG0XyrJb6VO+bKg1MeEUjAiirOnY5EI093XC2At4WuTP1QTuf" + "r4djbSV3vbWkg9nKwDV384SpXYgUARU81fB2ndSX5T++31mv0sdvOyu7kuqRxTKPO9sFU5nb" + "8xYabr9uvjXD2z/e27O/7NvBOMfwlXuZhPr4Z4VWSqLI3NwsKGIiViTTfva4I4CyACbSSjST" + "EIlSukNGke56RFqRXI9uMImwBpQNMLOQEmLpSoqElKhuz/wwveTpZqe3My+j3aBFbDLpKJEw" + "C7MmFmJhEmLNLNOhsUuR4rxOixhAAsBV4ODQlwrgQ/s/YUAD7j/4kDqsVjcKdgAAAABJRU5E" + "rkJggg==") + +index.append('OM') +catalog['OM'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABGklE" + "QVQokXWNPUpDARCE58WngvjTCYIQiB7AVtBjeAA9g6RO52ls08YcwNzAJkUsTRrBndlZixf/" + "UL9iZpadZZsJfnD2sgJsG539ogVwuVy+zmY75+fPo9HBwV4VgKpP+8ZisWgB5Gq1e3Hx9vQU" + "8/mfvY6maST1AGwcHi7H463BwOR/7Q7bzQQ4HQ7f5vOSikqxSJMOFsMRJivC5Ha//zCdtgZK" + "WWRJpiAiAiTIiqhOuyABaAVUqiQzTPXIYiDYRDRca3WZ7K0PSEdYqoibW0YGk/Q6hIOmUif7" + "erxDC8CSSZNFRsZXLxlJOmTKqUx4/UGbR0dFWnm8Q5pMyqIpS6XMTGciITT3gAABBgRcXwEB" + "CHgFDPhj7NbAO7zseXjgtt+GAAAAAElFTkSuQmCC") + +index.append('PA') +catalog['PA'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABTUlE" + "QVQokVWQvUrcURTE517/KQJxm4WFCGJhZcDWVhAsrO3zDLaxTC1i6UMEBXshPoGVbBOIhVoo" + "BnfV4J6PGYvrLjocDlP8zmcZjUZ4JxIASaLfJ9AipoZAB6DX6wH4918k+vOQAMh2dvj8LHdF" + "zPLdyUnXGt+M9es8nyb6vlYXekVS/r3k41jmdJOZ3OviYrQJkr7OY3UBT5PSaEmNo5vMWwHd" + "31ZqWl+uEDTTaEw3msldZnSfcw+g+7bf/dyK20dlKqhIBIWI3Y2N6lYilKmI6l4Gg+7srLv+" + "w5sHXT3IU0F5ylMe5dPuD31UrTW2tyuMQUw5eehlIk/OODs8tIODdirJCpvSKUt5KkWPNzqH" + "Qz899eOjvLiQFBEdgkkNvpRIOBGpYIkspRQAcysrn/f2QGJpqf2mYPMeRrwQFjBOPYe/LSJI" + "kgQZZCt4BT95XQotDMJeAAAAAElFTkSuQmCC") + +index.append('PE') +catalog['PE'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAAAz0lE" + "QVQokYWRzW2CMRBEn+HrAXHgAH3QCZ3QFY1QANdENJBcvLM7OfgLf0LJaA+WPE8e7zRzVwHX" + "K1VA3bTdFtxmAjgeAVe1KlYrG3Czl7CwfTg4wpIjvk6nCQD745NMMj3sj7pc3Lsj2mYzv+Dy" + "7I54ddvufQBEFEwFLRPJEqFX9zPA/AflSPkPIP1GSlly9L+Bdo8U4d6R3Pt7IGJk1gCW0ojo" + "iPeAAiWZDGARYr12BMrRYGvtXuduN3ZIZkH7fmixgPN5LliaD/v9fAXADwI9YYS3Muu2AAAA" + "AElFTkSuQmCC") + +index.append('PH') +catalog['PH'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABcUlE" + "QVQokWWRsWuTARTEL18/tfhZyVAzNUsVhLagiODYf6CLdnXt6tS9Q/8BoUJEBQcRK6QgEdrZ" + "qRShOjlUsIouLomgJua9e+8cvipBj+O44XfTNXZ2OqurQ+AHACCBBAgQSMAAAjZZGtKNfv9R" + "r3fm6GiQqQiRGZGkyKSnM+jpTPfo9bYao9HX6elWhHZ3v3S774dDi4C73NM93MI8zNI92u1q" + "b+9uAZyXhkXxYWXl1Pr6Qrs9Yyb3cKOd0GFGM7onYEVmAp+BjtRZXJja2Li+tDRjFmNLsxiP" + "60GYRT0oge/AJ+mjdBY4rqqrm5vb3a3Fw+cDWtDpFjTS2eKFV3hdApU0J10SWtBFjcZ6Mn9r" + "+/FNM7lrIgufvwOUmZDmpNtAU++Ode+h3hzK/R8apEgCZeZPYFa6jBcv9eC+vg0muZNCBwMR" + "AMrMa+o/w9O3OthHs4lzFdwb7iBRJ4mI2gmUp7tXfq0t55+T/779v2v9BjchVftPWcpLAAAA" + "AElFTkSuQmCC") + +index.append('PK') +catalog['PK'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABjUlE" + "QVQokWVRzUpbYRScm3u9kCYRaRclTQQLmiLd2K0V6gP4COILuK3bZunaTelbKFkpuHNV6M/W" + "RcXUxEqlVGKSJt+cn6+Lq1ToWRzOYmY4M5MMwgAAgBFHjfcNjAAHFFDAAQIK8P7IkAGYzWcB" + "5GneftMey1hh5qqmClUTcVUTMRWXzodOVsjHGN29N+7dhltzi4iNSvP7zdnEJhTSKS7z1XkQ" + "pQJdbHU1Nyq3X26vPX09CZMsyXZWdurlOpXiAkUJ9xMR1ZXK1fpqa6512DsKHpqV5vLj5c0X" + "m1SKCYjM3Qv5GKOYiEhtplZOyz9GlzR+vv6y+2n3YnjBSI0KouR+hy4IjLz+8zNP863WFgMZ" + "wkn/pDvsiouYQJHB/QGBdB52j1aevNp4vjEIgyGHC7WFg28Hx/1jc4Uic/x7iS40MrL98V1/" + "2F9/tl7Nq3tf905/n05tqmZ3HoqUkiSpV+qVtCJRxKRz1tk/3ze3ICGdSRfnFi0aFMnVzZW7" + "u/tUp0tvl/Drv4IfFv8IfwGHykCMTQsl5QAAAABJRU5ErkJggg==") + +index.append('PL') +catalog['PL'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAAAuklE" + "QVQokY2NMW5CQQxEn2FresSduQM3g+b3oKTZ8XpSbPLFD0TKaGzZ8hs5fL/zo1qrqqDeqQEc" + "DoDtAMAGHPYedt7oY1ka35CfB79TRFRm+yc9T1XVCmKljf/kDVAVA+J89rI4k0yvlqx0ypIl" + "Z8bpdL9cGuDbzder1dHkurvcu7Xp8XgUtIKQ3DuZG2hLz+c5A/tMS6zQayxFDsZgBnZKjkdL" + "5CCFFBKZzJ7JGNMF8Qn1ZLbrrxPwBcJ/XXnBf5aSAAAAAElFTkSuQmCC") + +index.append('PR') +catalog['PR'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABi0lE" + "QVQokW2Rz0uUYRSFz/u9r+OPmcGNiZBaSASlYIjmvv6B/oAgglaCUNuGFq0V3LVt0cY2sxI1" + "ClxMiouQoYU0LgqGmoWIheA38333vve4GDUXHp7F3Vx4DscBjQ94MIV2B8gBu4ICCuDiUCAB" + "AnC3WTkaQm0q2e1xmWSRalSxGKnaxURMhKq71aoD+KaC5m/eLh2+qgyUh3ppJEASvEiSkITZ" + "34ODBIAay/1oHA8vvi42/4SYBHgP73kJQADeWwgOiCsr7vEjbH3hXh1Hx3w2Vp04qZXlkJKd" + "y4iYSGF0dGd1NQB4OIt791kcYO0rTPjux5OnsTGTffN5uyNieW55biJ9ZAsIAD5tMEaurzE9" + "RTvni1sfZ0v/vE5TpVfEur1j7BkZGd/edoC8XHCNX1RFXz+Xljg2zuR/3/MAcM619vcDYB31" + "IrxTbr1dLg3eLJKk0VRJ0oxA9905hzR1QHvxeTavG3P4XMBplqp1NS5lVKnRLCLGzXo9AD+H" + "30/eAL4D6ZWB8+v2LgBnnkcsD9MsvJUAAAAASUVORK5CYII=") + +index.append('PT') +catalog['PT'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABXklE" + "QVQokW2RMWuTYRSFz/fmbVIrpUI62CGCgyLipP4Dndw6unTrIJ2cM3YpiL9A3ARnCwr+AUH0" + "D+igS52iaYmJ6XfPufc6JKFLDw+XM9yHO9wGT4Eplgmgwt8t66VUTDDcHwKIiMjwxnHT4Wqk" + "jlTIlHI1p8fHFQVAnpz+2uj4/b7f61O7rhG7b5mnTGPS0izJZjAIoAKISA9//tC3WnXfOOfM" + "x5zuceOFJS2NCwFkAAUBT5d0NuP6e3UPDifPjuzLdtOjYNla2goSQIFB7nRWcD6ztlw5u367" + "LWtRWq5ZkheCFECFwUMKfR3ZrUd+59Vw++qN+e43S+uPLduVQzZkABUCneb28Yf6dzl5cnLt" + "z8/ud3Y+WZldbENKSYsLCtH5W3z5mb1N+/Da1GMZW5JLQYQc7gAq/oHSztYORbkriQErmZsC" + "CQkS3BcE0OABMAbOAQPOgYLR38t/vMh/jtdGdgUCTU8AAAAASUVORK5CYII=") + +index.append('PY') +catalog['PY'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABH0lE" + "QVQokW1SO0oEQRSsnhkHNRHBQITdC3gMUwPxOG5gYGLoLUQQg8UbGIqBV/CDLKz/XdTpelUG" + "4/7A4lFUP+oV3fRLxgxa1P9WMoBeD4AlSIgwA0GTIJ2z53jU71cAAPvhERGIcM4t/1WTnRs3" + "jXNOnY6ACuOx6xoSANvwFJgdJNsoCg2H1ZvqtbJEWf5ZYN3d/VxeKOd6dx/d7iwgpWFTF+1L" + "p6G2Pfo43X45HO3489XzAEgVkhbcNlaW96588H2G5ZUFvw0pPb9/rdZLIbUTmlwfVnjSkGVU" + "ZfHwNEhYvz063rofZFKZIJVpUnkmTCno7mZ9fnJdoRHDG+tFsGCYUZCKSIySFMMRoqAwLQAJ" + "5Q2CgIB5bgXnvpjtBvwCb+9W8SwiBKMAAAAASUVORK5CYII=") + +index.append('QA') +catalog['QA'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABJElE" + "QVQokX2QzyqFURTF1719GdxkYKSQl/AulJSMTJSBKKLIQClMjQwMmHgPI15BZpJ/6Za719rr" + "GJx7P0ZWuzM4+7fXWft0Bv2+MZTh1anJ+y8KCOB7dBK/6gz6/bFeD0AppV5d7W5n0pmpTDIl" + "kSlZ3Lq+bap9S98c7r88PaYkSowkRYohcnJ6BkD3L217Ye9AqkSIJEMMRjDCIoCmDVfHrna2" + "klSEqNZbESKTAtC1Xek6sHR0XNviQBGMIc0IkQAa22WIl+JyublBhuI3ukgFTaYIoOvWvqCU" + "snJypggxFLU4epCZAtBgFKlucrG+pmijU6REk0qlDKDz+f7aG59odyilnC4vpijVv8+UnMpM" + "Z57fPTS2P97ebNuWNT8794z/9ANrfVvlnss+EwAAAABJRU5ErkJggg==") + +index.append('RO') +catalog['RO'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABFElE" + "QVQokV2RMUpdYRCFv3vzP0l6myAW2roAF5LCJgsJ2GVJD1IG7FyEnQgWgRiCqG/mzJwU9waf" + "Dqf95nxwJs6u+SsAZhgPV+cfD6DpppvxQp/RvGbwW5ffT4DuqT1/Ovq22exAtkB+TH+VMy05" + "83G7HQwwd7+y2tVz9y0827lG6ZtwhDOn4+OGAbSptsoq27LDlp12uMORC0Bmw0BdbVVLqGzH" + "63uH/QYABtEqp/wfyD0m7HSsSki9AFUtOWQVe0DYua80rUrRWYRacopV411DJpIlwWAnqTM7" + "5axpD1ga0hFWoqIKGKCUPx9uUlZ9mOcjeJqmBEEyxKmoWtIwwQ8I0LLj/c8vY9CioUX/oS/W" + "jZf7Byetb52vN2aSAAAAAElFTkSuQmCC") + +index.append('RS') +catalog['RS'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABEAAAALCAYAAACZIGYHAAAABHNCSVQICAgIfAhkiAAAASdJ" + "REFUKJGdkb1Kw2AUht8kX0OamDRFq8FBDUVRyeLg4DWIo5OLgjfhZYiDm65ehZPwIUKnBhws" + "VSzaFAtJJTZpk3wuGjSlTfUZ38N5OD/cyeo6M2UF/6X54YOYsgJLK/0qhCTBkESYCcSpRCQb" + "POhAuNIDX+QgNCSsdZJ8yVWk1meH+vt3sLtUNefL3TI4wDX03nmn1QyGUTxO0I0SlVwbOxbm" + "qml4YLrwXj0w8NDMhcpdf6Ny73Pjx3hrgM9mvq6DC0UocREDYzF3FQCjEt5powBAlCTELy0E" + "yYQpviD7m2LdWJbSm/DPtW1fkEmfCVAeabJnHd9OErSfRBWUUvaTgeux2sUluzk9Y4HjsDwo" + "pWzkxYWShq2jw6luka5j2/afGrLYto1PPjGOVBw0sOsAAAAASUVORK5CYII=") + +index.append('RU') +catalog['RU'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAAA8klE" + "QVQokW2PPU7CURDEfw/+FXY0hljQ2Fh4DRNuQss9PIVXoOAuFhYUho6YSEzezNu1eEQ+ZLLZ" + "nd3Zj2w5HJITIqKHzuIKDgZgMgHITChAZs8KjDNHeYbd7nvomzPznOQtlFIioiwWuVzmfk8E" + "rWVrRGRraV94O6fTslp9DZtNPD+X7RY7LeSUUspar/18DjAANlL+DXS591Wl6rFiF4gB6Eer" + "0uLU+u+ClBDDmvGTXn/qZ9hZHXJUhRzSkVQ3Kdzu/LDmbXiBsd9DH7eXd2LhVtrjPQwBI5nZ" + "LCXcsJCK1D/Dxqa1bgHlAHFmXKZXEvALP4llHm/RUYYAAAAASUVORK5CYII=") + +index.append('RW') +catalog['RW'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABRUlE" + "QVQokW2MMYqUURCE673/zYKyKshssJHgZQQzgz2BCAZiZCKCgeCaGegBzPQQIngNMRFBHFc3" + "GXUcu6urDH4GdsGmKaqgvmqXnjqE+SgVAQEEKEgIgULsjDBCeH4DAGyXmwwJJZdbaUp1i0fL" + "F83x+uu945ca8/bJb5chgXIVaLNMeZu+c/hkPz50bY6WcYz7XYDlEkqgzELaWWY5y5TfrO7+" + "Yf8WB29PbyE0MkS3KJQ8MyVXebtjvsTBo4+vKC/3J6zX7edfLKaH1meDdtq0w0447ZRjjjZb" + "v7b6/m5cHAA27r+MgGcm/qu9X728hyGhtbQD4PnSuTZAmyTG4jEe3OSndaYyK7MiFLPJSjqz" + "gpWpun6l3j/DwAlOt7wwDody0Yo99yqzJztzShY5sVRV9WNTWKHhNrAF4szzjAagXRQw8A/4" + "uWjB1/pT4AAAAABJRU5ErkJggg==") + +index.append('SA') +catalog['SA'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABc0lE" + "QVQokX2RvWpUcRTE535ks5vAInGLgDFGQmqtVRRsfYtUfjR5BH2G1IKd0bxAKpsQLFQEhVsI" + "G7tV2BXW5CZ77zkz529horFximGaHwMzGR7grwIgQMDO3YDmQmhQAnj28GkCEoKiQhHBIEVK" + "DHe6Qgq5/PWj3RJAAkY/RxGRIwfSaXtqtLmy47SZNQnppK3r5nhl6SqAEoGUUsvm1vU765c3" + "jpujd1/f31y90ck7RZYfDA/ubtz7PPr04u1zp58BDFJy+tFs2p3rrVy60it6h+PDlu10Nv1R" + "TwoUJrocQA6DQpQPFgdrg7VxPamtrr5X3c589a3qFgvD8XCv2pNI8axBwZSw+/FVljIGTWZu" + "JBu2eZZPTiakp0jngIGiy83bzdubC+ViSunPVv35vsuf7Dxu1TIuAnQGt99sMxQKkzndZFv3" + "t/a/7Lv899AASjTw4HJ/meFOUu5yih5OcefDS1GrS9cYjAgAGdb/ORIt/q9fMHo3F1zEQB0A" + "AAAASUVORK5CYII=") + +index.append('SB') +catalog['SB'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABz0lE" + "QVQokYWNQUiTYRzGn7Xv+0gl3MKCCS0LVgdDSQyExU4WEVGHygiCWF285KkI8VKwaJcOUQQd" + "WjQIpnjx0mjhoGIOYY7ARtGhSQh90Sq3nM73+b/v12F17nd4Ts/ze3x+/1Ii0Ts5+XFq6kAy" + "6WrdAAwAQADzLzuB1fsX4kOjsFKp8NjYTtvGxMTugYHtpVKThNZGxBMxQh/FOhldiA298zqu" + "Jh4/8QFvU6mDw8Nd6fRP11WtltHaIw3pkbYfmzcvPz/UX1xZt2+X7Oz1rAVY9boJBv2iTXf3" + "tmZTlPJEfFTO6dibc8fm0VnLVnF38Ut4VxiAFY0Gxsd74leqT1N9s7NrmUyNdGBaNy5NHx4s" + "fGjItRfulih6pCEAq1Co5fMhKszM/Jqba5AdJ0YWzh9/1XSquVV/ctGlUcooCkUEgAWoXG5N" + "xMtM/x6MfD51tBjZt1Td2LpX2KjU1pWnqKlE0ZCa7YEhoZQTP/Oyv2/ZBMqvv+24U3Rp2Ba3" + "2yKi9d8Ha3SkEowtR/YWv0OevXfyK1/pUem2mxQlhqK1GA3AunjkVihwtnd/5VM99Kj8Qzx7" + "T08XNUULhWJEjOg2RgPwFR/AdfCwjPk0sIn/8gcg3yvgjsU2IAAAAABJRU5ErkJggg==") + +index.append('SD') +catalog['SD'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABOUlE" + "QVQokZWPsUpCcRSHf957CVP0Sks4uPkALY1Ba20+QS9QW0NEUwVBDbX2BC1NQmtQb+ATOOiQ" + "3SI1Rfyf3zmnQTODlj4+vukHh4PqHhxzdUkBpsAEGAOfwAD4AN6BHK5xs3G5/ziIaDCDqlOh" + "dBKki/hSR81mAiKr9E82J+f3WSTqIlB1kblBXIKH4CK5Ws2ABIQF71ayw0Z6tX0Wec7d/Adb" + "BHFs7XYEQF0Z2C31Dp6OOytjL5eXTL2cepp6mnqp9JLPRyBoKioi0kk6Jw9H+uvCEoCZRSDU" + "SGOwsK7rpzunOcffe3czS0DM/iq+FS92L6Jp1J/0zczdZ10Qx/FwOExgoLPQK2R3WeO2EUII" + "IYjIvCISAimk1uv1VquVYIT862rhuVBdq7KspMwgOStJ/QYAsIV/8QVvRk9166paHAAAAABJ" + "RU5ErkJggg==") + +index.append('SE') +catalog['SE'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABTElE" + "QVQokU2RP2rUcRTE57v+NEVEQVEIEgRzAEsrKwuxEe9gERC2Mm4jKBaWNtp6hTSCpUUO4BkC" + "IZ2KqLBs3ps/FmZNhscwxXxeMwMP3mAtvXoNA8aFxQuUUYUiVnUWuiYAL+ePANgZO0tYgeZ7" + "j6lQptwS5bZb+jT/MAEAcvxzKQcnR0kjOvz2p+WmWiqq5G5tX98EaoJtQw5PLv5rxzWN1ZKz" + "kpsqqqi2WgZq6ADjzgJ1lBBh3HEnHXdccZ2G9Ni4/fvLwQQCUdLxGkidYmftihumiQlGwphJ" + "weeBc56Ke7hNDNx9/mzx5PD7L8qfH75LOq57+3tNltyrWdEtk9q5eeXr2/cTVqTVUtPrZ1Vk" + "US0XqiEOc2bONoDjCVVNb13dbAqXbiEN9/a1y5SbpkVHsmTZgAe2nv4f8sfHfRAmbuzeBwjU" + "+gwQMIC/3Ll5iJVVWFoAAAAASUVORK5CYII=") + +index.append('SG') +catalog['SG'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABJ0lE" + "QVQokY2SPUpDURCFz73vQjQknY34k9IiriArcD3au5asIVWKYNIpaRRMHwKCiCikkTgz7xyL" + "p4KK4sd0881p5qQHgEDZ3oZZ1DUBAgD4YwIAUAjszOfV8bHNZry9zf0+1+u4ulKEIuDOCLkr" + "Qu53o1EBUPX7fnkZk0k5OdkMh/nwMO3t+/RC7jKXG83kXh0cBFAAqK6rXm9zf8/xeOvsTBEv" + "5+cyp5vcZC4zudOdQHUKtAaDfHQEKa5vfDZ7HY+bbLo1qszonrvdx9WqFKBeLGw6FRI6HUQr" + "t9uMgHt2b4IRkTzS7m4Bkq/XudORJAiCfifn/LRcFgJJkgT8ZTfbAArJCvjPAQBEFJKfNj5I" + "KeELCVDzzQJy8/zMd4LBb4AMEmTTgDfsYE4MubWOHgAAAABJRU5ErkJggg==") + +index.append('SI') +catalog['SI'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABRklE" + "QVQokW2MsWpUYRCFz3/vD8oWgaBgFjux1EotrXwFCxEfQ7BTLAJ2Kgoi2KhFejtbfQMbG7GJ" + "RViFhM29uvznzBmLu4ZV/Bi+OTMDU8ZxxAa2J2+GE2RXALPZLBOrVaDk7HQPAMjMXLcNhmGo" + "0+PHz/cPFoe9Y/fhZXRd/o9Siu3Sb3988ujC8bFu3zzz6u1iGMIJyaKplEyZtOSdc6dePv1Q" + "43DY/7a6f+1LbF2/d+nzrdc7RE+50WxuNOnWTMbRksCPCliJN3uLs+/2xGCeb5lUUtmUbQqR" + "FBQAXIEa7t73V9uRRTOTdFPXAgy0KGsbdAfUuos7N8YHy+WBpWgy/5iMppDcZCkYW9vzZ3hR" + "7+J7/+uTf34FmWS2lq2tw8lGhKKsLl4BqoGOwnyeJBQQQRYSEiZLiJjKQBkBbxT+Hv85AfgN" + "XohcMYYB/8YAAAAASUVORK5CYII=") + +index.append('SK') +catalog['SK'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABgElE" + "QVQokWWOv0uVcRTGn+97X296jSxEXByi/yIhIlFq6A+owaBwyhwa6kLR1NbUINRUQ6ijRDY0" + "iAVN/djMKMJFb/Tbe015r/ec8zwNr0jWh8PheXg4DyfFdgEQe5AEQJBkabiHk8gBZLUaAEnl" + "SSYASlIF0H62vv3Iy/ri4SNsbkmUB8Duq1P6j5SSu+cjE+tTFwZOr3zs3Wjo5i3Mzca799MP" + "Gm1WwhlBD7nTg/2H8/qVlfzlq+bYSP9Yu63Lk+g7pPHxuH7j53rrQ7Mb7ua0kBs7zqHBKuJL" + "Fg5RDOr+tFqbmpvJMv3uZE5ZwANuMocHggCQA1WJa+cmFxY+N+4V2Dlz7fyJr897LBiUMZky" + "F51pRxWgmpaAY/U7XX214uTo7cdd9bNeW3zaevGa5jSj2e5P5geODi0tzuTDQGXjE9+u4tfq" + "3eOn0rM31SfzA2bqdGSmUrjBIx1sHgFSAeQXL8lNZvDYFWZyV7ndFYEIRHxfXk7bAP8a7Lf/" + "RAD+AEvfR7lINIfTAAAAAElFTkSuQmCC") + +index.append('SN') +catalog['SN'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABK0lE" + "QVQokVWRMWqWYRCEnzf5QUEEGzERYpEulZ12dhZ2HsBCsPQIqXIDbyKk8SJ2goVoJdgYFXdm" + "dyy+X4PDtM/ssLO44FqDXnJwAMNsNnPGcO0dcP7sHJjMZNaDhgavXz4cH9xUXjhS7EhXl5c7" + "APL5x5dO9zQRqxPlhhJllA+Vqkjr5GRgxzCTnnbao9CkEne0UkmltAFIe6Bpx257nOhKevtR" + "v1vPT+vO4X8AG+BptTz2mOj9t3r3Sd+l+7fr6T2l9pWwB3aYHntcU24nenS3Hh/pZ9WTI2Wu" + "L6x9JaOoujxWVxCp12dK1KmM/sXH9ga4rZZGaiVFKlFSK0qUqli46QZ2FBof3zpWy9tb0VoC" + "g1jm1HRvHli8ggJDQfH1DfB35mGKebjfeNMf/ixbkUHqougAAAAASUVORK5CYII=") + +index.append('SV') +catalog['SV'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABPElE" + "QVQokVWRv2qUURDFz3U/99P0EVaICImkSCEBU/peopAHSJM2PolFioAQQhJSbyE2FiGoCC6G" + "yL0zv2Nxs0schuEwzL9zpuj1XFX3BqooUJUqqqGKGhJS9Dio6vDDs14MAiKVOCFTEY50BB0c" + "7c8HQaKbnwHOdKQjnekWjqCFW7gFrTFbfyzVcnJ5u7kxrRVbtukORrbAYAx4nD76fP69ZGYp" + "RZJtWZZt//qyf8v4fOudH1gpZbFYDJ1tT3VwNb87//ojaG/u/uztrK0aVAowrKpX83e3p4v2" + "Nsm9naf/bbCBcnz2+9WL8W8FbLsfHU6QLHBngP1knByfXheNl+8PNr7d1KUm1HCmW6O17CpF" + "EumXs+mnjxeDwNZsfWjhDCId4ZZEEjHJvNcanEiiaHKq1OqRyxhLzAOApH+kWGxNzYWtjQAA" + "AABJRU5ErkJggg==") + +index.append('SY') +catalog['SY'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABDElE" + "QVQokZ1OPUsDQRScDdeG1ClSqIe/R0UQ0Tp/Ij8kpWClhxBIGUF/wxVWOU4iEW0i3BEM2Xn7" + "nsUmp0KwcHgM8z5md5zhG/pb7yxnAAYDAKYKVYRgEhDERCBipP3g5XicAADM5q8IASEYGXlT" + "nkZv3hvpej0Fkrqq2u02Nj6LvBNw7mM2azWhzWyl66y8WxnjhbdwNb2uZdl4VLWlqs2ro+fR" + "4/whm97ENitu718mw6fh9guISKJNBtjJ3lG9rk4PjuPgfP/s/fPt8vAits4MgFssFp1O5+/0" + "ceucK4rCAej3+2VZkiTpvffeR0EyKhGKhDRN8zxPAJDS7XZJigSRaKSIRBaRsAX+gS89B00d" + "BrzDKwAAAABJRU5ErkJggg==") + +index.append('SZ') +catalog['SZ'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABxklE" + "QVQokSXRTUuUYRQG4Pt955nJmqIsQkqaTQ3SoqDAZRCmNgW2axO0rgja6KLCQZAKatN3qwrU" + "RVFEURFE0spdJPRlgUGBo4uQ8GvGd85zzn1adP2FKzl+/UMjUwAASCohpCpVKKqizFRFVJSZ" + "aEtIA8PS6UoZABx0p7u7m9HUCNBhpNGNNLJ/dCKMFnoL+Zu++sdJuDnNqU5zVzC6qTO6RVLT" + "tW094W64dcp6Lswuz8y4WfD4u2lvHFFlbOThyrcvn+/cWPw1a1GyZmwtle6PIyUQzZBouWvd" + "5oPF16QmabMpU9M/Wzp27xwc6jixvXPkwQ+RGOMCkCqQwEqVbaubtg6/mu/rO3rsSHeMsV6v" + "D5yvvhh/X+w8O/3y4uEz+0ORAgQFgHQiOyS51m6ZfPzoiZplWRZCqA4N1iY/fRyofj9wsrGh" + "vHfhsgIhBUxty/N76lgjzUqM70QWYswbk6mvc9eu/K3NtY1d0jSXtLcDSBavotA1zOWaW3SP" + "MCOjWQTNTUAh1U3dGDbuqN1+FnobT8/JHuZdcySpOXc6SZHIHOhudKPT6eLVbFeyr//t/FL2" + "PzITFaGrQhSioEIIIVRBQon14R8k7mFOvA7V6wAAAABJRU5ErkJggg==") + +index.append('TH') +catalog['TH'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABDklE" + "QVQokX1RsUpDURTLtQ+KVB2qi1MXdXJx0N3BjxEcqz/gz3QVuklxL35F9zrUCvaeJMfhQWuh" + "GkIIZwjhpCQ28LbfyZIAnp8BpAUnyHSCkRLIJDMiI5JMcjkalcwE0Ora5C6UUhaLRbm9e3m4" + "v/z4WMlppWzRVIqiLFoyw7SP+/tPj+Pm7fX9+upkNvskzXDQEYpQrY5QrVrrYHAEjBvAbU8y" + "GRnMWtvartU1MqrbCwnADfAtkYwaZmgd/zu7VkU4ogLLpnd4cXbePzjskkkqaNIMkQ6aFGnS" + "kk9Pe9PpTVmtVp1Ox/Zfz2kBoJQyn88bd7t7wyHajhKCEEsEyA1tSJA8mZSv7SH/2bjFD1//" + "ZYwkMQleAAAAAElFTkSuQmCC") + +index.append('TJ') +catalog['TJ'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABO0lE" + "QVQokY2PvU5bURCE51ybIEuWDBXix0kKXPEE9DwLBQUdEpKDkoYiVXgWXiBFmrT0kS1HuKHC" + "hRE6M7tDceGGMp9Wq1lpNNopP/GPBBIAIEBvZ6db0QcwmU4BONOZiLAiQ5YsmUzJpKUkf93e" + "9gEArvdLRyAiyYwwmaTJrExW15rkh/FYQFmv14PBoH3JNoD651szPDJ6sfrd+zx1Gdi2XUpZ" + "LpdNV8A2DC5umq1jx5P12Ds4i783foekJjNbt20ufvTG52X7xJZT3tgrHy9j9vXVDmRmWa1W" + "w+HQbb5hOxbf/TRzypv75dNVF980zXw+73fxnSjjC9jtdHQNCyY4/XI6e5hRZLCqVlWKNSrF" + "KjKqgoo43Dm8u77r4xkM7Y52GVSEggxSVKrdCkVGREQGAGAHGAGb+E9eACEOOiSM812rAAAA" + "AElFTkSuQmCC") + +index.append('TM') +catalog['TM'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABj0lE" + "QVQokYWOP2tTcRiFTy733tyk9s8krThoFwdxKEjFyVFwlI4uIvoFghSku6M6FvoNnARB0MXB" + "qbQOTQsabdJgNFiqJiY1ue857+86JLj6DGc7PE8J1wHgzc+l3sfuELi7Aggzq1dO39ZhwBjT" + "zTEhBrBRe7LczYtQTheqD9PeTLnya9i7XdvabR3sdxpyMZByOl/UNiMAKPD76GuUt4933i9U" + "zuy1P8xXZx+/3FxbvZnLPn0/anTbjW9NugBECAhFSNKi3x2lSdhpHdy7sbZ9uLd+60FUiipJ" + "2USjmZMigBgBCj4e8HSUZ6VCjtf1d0/vbND9+far1knHSBMpUgIQwxCCJ4n/GYbZajCp3mnc" + "33p0aXG5efLluP+DookmTZJiCAo+Glgz41yfFMzNpN32PklzUjKRkpxTg+Rp4pkxqxYmmwSY" + "i5oeKNLlwQFEGINBcyvXrqbILly2aQBNZjKjJj1yyf2fQYPDRnz2ovW655YWJdFJp1zyIJcH" + "d3cFAShhHhjjWY7zwGdgHf/hLwvQQSbnuK52AAAAAElFTkSuQmCC") + +index.append('TN') +catalog['TN'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABN0lE" + "QVQokX2RsUpDYQyFz61dBUEoFFsHp452cfIB1KFbp4Kzo25C36GrTr6C4uAktljhLrp07aq7" + "UF1ukv9z+ItaEEMI4ZCPhJwC/URa7f/MAknDoSRSUkqKwEPhuMsdM37Vj9vbuiQJXt8UoQhO" + "TrS9zcYGwHRKWTIeU1WYFe12kuqSSCynj4+1WHB5yXxOu02vR2uL7i7TJ8xklqRakhSRtysl" + "Hh/pdLi4YHOTsuTgEHOqKi+RVJMkD8wwI4L5nKMjRiMmE7pd1tcxWwLuKQOE445VpESrxc01" + "p6cMBpQl7+/fwM9JWZI57uzv8/xCv894zN4es1n+j9xxd6mepLUsmTEacX7O2RmNBsDdHff3" + "PDzgJg9FKAM1czWbmMlDV1cyK8zkrlx3dvIPFZGk4nPVyH88zvEFZ5M7foT/uzgAAAAASUVO" + "RK5CYII=") + +index.append('TO') +catalog['TO'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAAA9klE" + "QVQokXVSQW4CMRBzlj1Wizhy4MiN5/ANpLZ8gfYX9AEceQ8S7RNWooeOnbiHLGhXaiPLUhTb" + "cTRJfd9jtEoBULrFogB/ogXQdR0uF59O2G6xXtvAbpekmdSQlnzn2/nc1mAfDsgZb+/+OCIl" + "X68mTTpohiNMptWqAA0A29jv0bZ+ea7bQRph/jhiADlUAoDNxscjDMO2HTHOrgwSQPs0n+N1" + "769PS6Cs4fihe8RDGm5wliUzQJk1mOPs4Q21UgES6QhIE9FUDcmSqmEm1YrjAhObCGXkjGpo" + "KCyXJqEMEWQiIaGyhJwrCpC+p4PEPwMu96/wC/4dTt9th7WGAAAAAElFTkSuQmCC") + +index.append('TR') +catalog['TR'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABOUlE" + "QVQokX2RPUpDURCFz4uxUwOxsrAQwcbaLCBoOhcgdi7BBVgHN2CvRSwsJFWqlIFg5xJiVqB5" + "oHdm7mdxH2hAPAzDFPMxP6dCP8rr9Z9RIenmRhHkrJwVgYfCcZc7ZvzKq/G4LUlVxXKpCEUw" + "GChnJhPe3zEjGZZICbNqfz9LbUnk3HRfXurkBDMODxkOSQlLJCuAzLLUypIi5M7Bgfp97u64" + "uuL5mbpmNGJrGzNSM0RSS5I+vzDj9IwIZjNS4uWFx0e6XZ6e6PUawD0XgHDcWb4hcXyMJY6O" + "eH1lOGSxYDotQFmpnaXKjJT08MDODtfX1DXAxQWrFff3zTLuuHsBNtzLTdzecn7O5iajER8f" + "mDUHuMlDESpAC7S3h5k8NJ/LrNrdVacjM7nLvfxQEVmq6nUj//G46BtdQD5PmN5peQAAAABJ" + "RU5ErkJggg==") + +index.append('TT') +catalog['TT'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAAB0UlE" + "QVQokU2Lv0sbYRyHP5fDJZ5kDBmyBFzbimBLoA2lBYO0HToINzgoFRGU0C4Oaav/gf9CV1c3" + "iTg4abgm9jh7JO8ld8lLIiS9q43mh/cm77dTSz48PPAMH0y63UQiAQBAPB6ftFoTXZ8AAngA" + "hkAfuAP+AL8BH1Bofp6urr4dHTHGpJSpVOqDruPsjE5OMB6TEDTl++Nj9SAIlEzmSTb73TSr" + "1SpjzHacF6urdHtL5+fU/UXdLnU61OkomjZyHPUAoGQSvbu0rtu1Wrvd5pxbjL3c2CApqVyi" + "UFAYkhCYmxvV6+pXQEmncXkJoudra9eMcc4552al8npri6Qkw/h/eGg01H1AefqMgoAsC0SZ" + "9XWzUmk2m41Go/TTzu7ukpRULJIQiMVGnKv7AJaWKAhIhPhhgujV5mbZtj3P8zzPsKw3nz6S" + "JLq4UGKxYaulfgGwuEi+j/GYwpDKZSJa3t4u2bbruq7rGtb1u709AKhW729u1M9AZGGBfB9C" + "kBAUhmQYBKzkcoZl1er1muMUTfN9Po9IZFgoqHkg8ugxZqMUjWJWg6ZB0xTHgaK8zeXavj8Y" + "DHq9nsnY8s5O//BQ6QNyCkznzIw8PU1mMvg3Vij8BZ4ENaDZLG35AAAAAElFTkSuQmCC") + +index.append('TW') +catalog['TW'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABEUlE" + "QVQokW2RsU0DURBE585nOUUCCRw4JiGiBWqwKAMKgIwuoApTAhRgUYB1BIYEgUC2bP7Mzif4" + "YHyC1WiSnbcaaSsc3mIzMmQkY63Ht9MDwH/UALi6PAJwvDvo1fX9fBFGROxMz3ta1mSW8o8v" + "JpOmnN5b+mS83+9X7fXi7mUlWbPW/MiJmSmnlMlqNDLQwLDNFEAmvVqJYcnfUaacWACQBhpI" + "EZiueXEzk/30/qkwbW/d3gAAGiTZDsXzqyQzckRIHnDxmy6AZKCJ+RgPZ7ltMxOoTG7X2Paq" + "VDJQkTklSJ1QNw0pSypATyoVtwt0MBEKRKAANYXhMJNQQARZkZBQXEJEkYFq2X0k/vvuZgXg" + "C0lTZuDB8+IlAAAAAElFTkSuQmCC") + +index.append('UA') +catalog['UA'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAAA90lE" + "QVQokW2MQUpDUQxFz/t9CIrDTpQK4mZcjgtw7CLchmsQhO7AmYJzHVjQl+ReB/2tFgzhcBNO" + "0rgz+5JIECSk5h47DgEdcXsNIFtuZVKUnG6pRWqKcsohR/nh/rMzAbxtXKZEyFWEHTVLozzk" + "KF+cNr7UEZJLpEm5imFnOXbeKI9ylKMAdYbKLUXKuf/9x54ph+CDznK9PL4c9V1ySiFSCink" + "KKUUNefVyRHnj61eYXVjv5iB0w57/Mtpunp/WneJ5rAH5KF0YEPamdAlFqQdsJf+OYOCIukS" + "kxPOdtuAaC0gYcucbUqibZ6RUM5kn7eB3xEB/ABIh27o+OhYYQAAAABJRU5ErkJggg==") + +index.append('US') +catalog['US'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABuUlE" + "QVQokVXRvWtTYRgF8PPWBBdRKAgWqcHF1U2koMVN7aCDINVFvGoGJzGLCuLQP0Ckg4jQVoQ6" + "dHBoQUFjLRksfixSDIUiJC3xJiG5uXnz3vfzeRykoZ7pBwfOckRUrGAYIpAHLMCAfVH4wGrQ" + "bv2xAAEEeABRsUIUXq16x3ah7J1zLz96Y/Tz995aY4zROssylWVKKbm0NCeiYmVy+vTiGgVC" + "IFjPgREI2vN3PEInQdqD7EMpce1C9VsFUfHTQtkbYyYf28FgMPHAdLvdk/d1rqTTNE2SpNPp" + "tNvtEK5Lma6vfxbR7bWz0xNzZQrEgeEdDPMGjFdffWMWSkFK1hrWjty8/GXjB6LonXPuzEPT" + "6/VOlUyr1dp3b6der+PSyr/tZrMZx3Gj0fhFYXn5TQ7g+VXhPc7N7CfPR2ZUMNXxOwE1Ojg1" + "xVrDGBjDWh++e7W6VRW12tbY2DHnHBExMxHtxdBZlh3PY/bt69zR+afi/JWRlTLHMZIEUkJK" + "aM1KwVoewjkq3Ti0/Vtsbv4sFE708/kDVjODmfj/eO+0HgW2Q6DRZ0/Ezq2LgfccuQsN9AEF" + "KMABerf9C2iAYqrHmBTIAAAAAElFTkSuQmCC") + +index.append('UY') +catalog['UY'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABYklE" + "QVQokWWRsYqTURSEz725ucZCsovCFrL4CLaWgi+wb2AnqN2CjQ+wvoK2iqA2woqInYWFWGhh" + "iAqyQVlJsfzKjxA3+c98x2KTKDjNmTnVx0xq29bMzAwsZwMzg/X5T8XManVpPyVqf8fTxsO3" + "zfFcWAiT6BQiXCHYu/M6NU1T637Or8wEl6J3LSKFxVqsTM559GlSwOF3zoqQ2SiFvxzPiBAh" + "QkKEA8SZQe/W/YNS66D0duaL74EP6vVF9F98aH8de0d0onM6Red0YnuzTu99SUfNz83hcLHo" + "IqJfCzCbg/EXiSVYyfn96HPZfXJ49XJuZy5CACGFCAUClwlEABuny427B+XBo2/bW8PDH4sV" + "w5qEVVSnkOLC2VPzx+P0cTI9v3VO4t9ClsFYfS0iSklv3o2TXXl2++ZFBwlXdCeVC1c4OEjh" + "MgIRz3efpsnXqePLGU+cOY5j4O6YAYY7ZtnsD8yTlKlFNE/4AAAAAElFTkSuQmCC") + +index.append('UZ') +catalog['UZ'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABZElE" + "QVQokX2RPUtcYRSE54WrYLGIuaYwYAgiQUhtvZWkkhTp0hlIE61SpLAQa39AQAgEf8BC2G6L" + "dCnlpgnpViMrQbJ+sAtx1zNnzpviZoNF4lMMw+EwxUwCNlqttysr83t7nw8OPgLe6ex8+/pz" + "+fH8+vobwAEDDBjXJgEbw+G7TqdbVWcRsbm5OhppejpdXIz29w+dQVetpNrt7QJAv3/dbD46" + "P79eW1s6PR2W92Z6vV9lOdPtXtJklJmTWlycBcbFK3w4ef9k0Du7L3355JIfm8t5ZHpBigyj" + "G0XO8sEh+mkwuGk0pgDknJGRkf9HSqmqvhcRUX/nnP+af4KUIrwIxORwZ37OKWd3pOfAy9e7" + "V8e9cJdRpJuLFCmj02V/mppberhVtYvWM5RPfxxdnlCkaG4mo2gyOi1ImYsuLZcZFQqMQflC" + "Y4GiSx51+KT9cJdLUpZCABKakylvD1prvXLGbX4DOjxvXkiHuOcAAAAASUVORK5CYII=") + +index.append('VA') +catalog['VA'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABa0lE" + "QVQokVWRsU5UYRSE5/z3v7CuuqsbthA0JmoMpQVGSKwsNBY+i8/gE/gOPoGx08YCY2I0MRYS" + "moWGYlciQQKu7J1/xuKyUb56Zs45c2L/6xA6ADIAgWCRoBTdOztIV3QeShk6unr7JQC4wIYL" + "QGMh+kOg4/OMx+OMFk7gYheY9szRSYUO2baLbTsiQlIGBJd/ahSbBh1pOuXx0dbu7uvTWb16" + "91GvtywpSYQ9zy4WrcZq7Pxj/3jz06u6RB87W9+fT39/I5kgw3SrdmM3Nu3GZXp95fKzpy9O" + "9vZGX/r3N952+48lZQFGmXva+JmjciyeHL75NXl3q/dwJa59/vhhsHxjoaqzCLhYjUGLZ0OQ" + "rabubizdXD8YbS9WP9cePGlUJuNJlmDxbA3PD4hszapq4OSLa6uRIuXciZpkJmET1RBmmEhE" + "OUV0IhIiAFwYDGwAbvuP0XvoDwSIEEFChIBL9zaFpf/f3Br+AlOWRi5rOFPDAAAAAElFTkSu" + "QmCC") + +index.append('VE') +catalog['VE'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABZklE" + "QVQokV2RPWtUYRCFz3tzQbZII2lSLCHGTv+AWppAkjLY2ysiCklhk38QtEkjbPIDYpPAFvEi" + "bGFjYSmIXxBTqCEibrbYOWdmLO4mqPDwMF+cZoqf4PAbUGH5MiIAAAEE4gIghAiEEEK9uIf9" + "4Wbnsb94N712/SfgmQ6opKagKpmpPPfZm/26Cjy1u8cPbO3RM+BXJgHP5F9YpmWylG4IdbOM" + "nH8OfG3vAM+089TJaWuAIRRgsLGxcHQ0liAGlb1et9OpMnM00urqJzLMgsy5uUuDwasKCCnI" + "JIPMXq/bNMOVlY9LSx/6/d8HB/Nm0SIFoBqQO6Q05u5Ot2mG29s/zNIstra+j8d++HLh5o33" + "bSKgchu37t97ePrl1OVucrpaU24hyk1Bl3zmysz62yd1H6+ndC34GWSSaZZmk+JiIkJe/Ood" + "oA6gojA7myTkEEEWEhJaS3BvCaCMJp+dgH/b/1YA/gB/9Fp0l9PEswAAAABJRU5ErkJggg==") + +index.append('VN') +catalog['VN'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABC0lE" + "QVQokX2RIU7dYRDE50+fAEETJOJpZB2GAyA4RV2v0CBAwFXaag5R1VM0gCKIhjZhZ/b7IT5S" + "eAlhM9ms2Jmd7Czopcbm/CYWJJ2eSmIMjaFu0uqQKMHmVX+4ulpJkuD6Rt3qxlY3B2aYX6aM" + "iyrsZb0e0koSg43tKo7DrvlZuChPguwhrYa0dCshUYcL8898MpjLYq/4/ELQvKD0dCmHM/Ot" + "wGAOixPz59mSkiFtSaJDggsXd8XXgoJHzovfj1RNvLJkU6VkKnFZfDH35nvxw//lSTIJH5Jp" + "EZtVcVLcmiqOzLZ5KGKl1a1J2HK0v4+ttGLZy9pKZGsn+pj5Q3UPafm7GeQ7Gc96AotzVU2K" + "OmSCAAAAAElFTkSuQmCC") + +index.append('VOLAPUK') +catalog['VOLAPUK'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABhUlE" + "QVQokWVQwYqCUAB8PtRU0kgUs0KSToWUlw59Qqc+OuhalwpSKUvUKJPSyJfK20OHdre5DTPD" + "DENgjMEX4jhOkkTTtG+J/Mcdx1EUxbKsMAwFQUAIKYry20C8G2azmW3blmXVarXhcKhp2na7" + "LcvScZw8zzudjiiKk8nkEwAAhGG4WCwwxrvdjmXZLMuiKBqPx/V6Xdd1URQ/DVEULZfL6/X6" + "eDziODZNc7PZlGWpKArP8zRNR1F0v9+n06ksyzBJkv1+L8tyq9XCGLMsS5KkJEkQQt/3Pc87" + "HA4QQo7jXNdN0xQGQZCmaRiG8/mcIAiGYV6v1+Vy6ff7PM/rug4AOB6PgiDcbjff98lqtQoA" + "aDabNE1TFJXnuW3bhmFkWfbePRgMyrJ0XRchZJomgTF2HGe9XjMMw3FcmqaSJAVBEATBaDTK" + "sowgiKIoEEK9Xk9VVRIA0O12ZVn2PA8A0G63VVVdrVYQQsMwTqfT8/mkKKrRaFQqlT+3/sb5" + "fC6KQlXVb+kHSbvUw5wmwUQAAAAASUVORK5CYII=") + +index.append('VU') +catalog['VU'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABkUlE" + "QVQokU2LTYjMARjGn//M38e6cBNa2nIy2MO4SGoPnOQwLOXjMAdOitO6rDPOo72uIkWKGuXG" + "QbFKe9mJk5ZCLnbXxyz/eZ/nfV8Hs6V+/XoOvwf7duDtQ4wAM4D/B4EB8AdYBX4BP4AVYAko" + "e1+w0J/+/L58MGs9j/3uKYerkOpSjUwp19zvdksA7xZz6dvHU2dqt25UP8lD8iSHGJOWZkkW" + "o6MB1ACE55sFv9etLl0tnrg/pQ1Ts+QgzYaQAZQAzh73rZu1/F13HuvKNC5eY0c0p9xMZk66" + "mXOX89m/Q1X5zfucuqCX86oq3r4uZxGBjCKiFlFk1MN93aZ67xFKAJ++6tiElles2VC7VZy+" + "XJlRNCNpQ5McG+u/eo0SwPM5fli0A3vVbuF8pz+YtMER0mlhTJobk0oNNggvUAIQ1Wyw3SrO" + "dVZtwqqDxooWxqAFmaagwlU4+igB7NmtE0d3Tt39ve3kFjZIJ9dTKQaVUsjTPdzTYSgLoDky" + "u30cOAyMA3NAAAIMiLUtAICAjfgLJMVRmyd9f8QAAAAASUVORK5CYII=") + +index.append('WS') +catalog['WS'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABKUlE" + "QVQokWWRwUpCURRF99OXg0ZCTRxY1LChOAj6AT+gWb/RzB+p33BYgRBhEEg0kEhToiBCGiQU" + "PM/e954GT1PzsNmTuxbnwkmAIywmAjbvbIphBP4lBdBsnu7tb9fru1eXj7Vatdt9G7+Og2JR" + "NwXSJZ/3d6uVAgB8d2fr/OzaLJTLm53Oc4lTMkQO3eg0N3MyqVYjUABijGi3nyaTrNE46PXe" + "zdwos+BmzqmbzULOhBAKo9Hn8cnhV+b9/pgETTS6mU9tWQCQApYqKzG7u3gYDj42FGAyisw3" + "cEFLEUgD7hFuXS8+MFDO/NNc0PNOyAikEUhIN4O0Aq3SkFxSLhQlJ/EHrWsiFBACcqFAoVJx" + "EgoQQSYkJOQtIYQ8EUh+Vg+JtdMuPwH4BW/TXRAimTLYAAAAAElFTkSuQmCC") + +index.append('YE') +catalog['YE'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAAA+ElE" + "QVQokZWPPUpDURCFz5XbPEhArAJik42kCVlONmBtmUcW4QpcRXYRCIh2Fkks7pk5x+KRZwoV" + "/BhmzvzClA98o4u/FqMFIKAC6J4eAdhCCkpHIsMZjnDQEQiK4eD780sFAFhvr8h0poNgOuig" + "SQfdmthM3tw/CKi3m40XC5xOsC1BsuRMp2w500pHWiqTyd1nLefzues6ALZhGP6NUsrhcKiS" + "hmnbo/gRlCKpSroU/rxvF1tS3W775XJ1PB4zU9LoR2wPxel02vd9AbBer/f7PUmSrbWrwEYG" + "GRERMZ/Pd7tdBUDGbDbj0GAwGBGXlCkNC8O3/+YLVjNhourzb1MAAAAASUVORK5CYII=") + +index.append('YU') +catalog['YU'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABEAAAALCAYAAACZIGYHAAAABHNCSVQICAgIfAhkiAAAASdJ" + "REFUKJGdkb1Kw2AUht8kX0OamDRFq8FBDUVRyeLg4DWIo5OLgjfhZYiDm65ehZPwIUKnBhws" + "VSzaFAtJJTZpk3wuGjSlTfUZ38N5OD/cyeo6M2UF/6X54YOYsgJLK/0qhCTBkESYCcSpRCQb" + "POhAuNIDX+QgNCSsdZJ8yVWk1meH+vt3sLtUNefL3TI4wDX03nmn1QyGUTxO0I0SlVwbOxbm" + "qml4YLrwXj0w8NDMhcpdf6Ny73Pjx3hrgM9mvq6DC0UocREDYzF3FQCjEt5powBAlCTELy0E" + "yYQpviD7m2LdWJbSm/DPtW1fkEmfCVAeabJnHd9OErSfRBWUUvaTgeux2sUluzk9Y4HjsDwo" + "pWzkxYWShq2jw6luka5j2/afGrLYto1PPjGOVBw0sOsAAAAASUVORK5CYII=") + +index.append('ZA') +catalog['ZA'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAAB00lE" + "QVQokU3GT0iTcRzH8c+z/ba25xkamg1KkWA1D3UoQoSSpKCgIKGgrnWJIjoJ81JGiFC3rG5B" + "HTtIUEYQRURUginB6lAesnnInpw5ZHv25/f98+sgQfDizRuY6MRE+4k3owvFuWo+L8AGAlpA" + "A4iAKrAOVIA/gHfjHWziHpSzHdtPbR3sev3efJiBkGMGsyNy/7U2PW32fUP/2eq1+drS0u+P" + "4ZfRofM76zUzM+dKi47IWXJknbWOyOvpUcADMPt2bEd+Zfyr/6tpRBojey/mku0di8suipyq" + "U3GiTtVL++tTUx6AQqGwuy8cGsRkKf2TE2u2JQ6peEqUSTYQOe7OZJ/cmTQAmOXp86hciRdO" + "Nm79SJXVb3K9Wg2tkBVLQlaJhDdJCyEMABE+ejh25JDeLyVLrUSTI3bqB51JURIiYVYi5Yy/" + "BRkYAMPHvL493s2F4HPVWBvdPXg9iAWr34N6TUScqlN1opoJTPeuC+bhOPL95sps22rESNDI" + "/nOvHiWePa6E5WUiteTIKrMyay6XLhZfGj2Oq8XAj8UHervObBt+8SA9/6kWtKHXTxMps2NW" + "ESeiIgDUwyWAkR04MLb59uXTK0AIKKCA/TcMMACAgeRf4EIuX9eJDOYAAAAASUVORK5CYII=") + +index.append('ZW') +catalog['ZW'] = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAA3NCSVQICAjb4U/gAAABmklE" + "QVQokU2RP2iTcRRF3y/9mvgn0Ukq0lIDdaiCLk4ODkqREmjAoUIEEUFc1bGCg5OrOlU3h4Ii" + "iF0yaEWFoE5Fl0wdOrlYIzFfk7z73rsONRK4cM98Tmpvt+eX52VJxERCJERsxCpiIjoGRUn5" + "n7y0v7TafDBzGs4IetCDFjQP84DTPOBhRtz5uJ7yPD8wWfICN97eWljoM0HESYxNSSWR0ky3" + "28xEhBMP8xfli8ura/duXIhBISnNAqApTcNAaBiyKbQfSdZ9vFKoH69c+fL76a/F6enXX7dO" + "9HMHAiDUoa4IqAMHe1gTSTutjWxrs9z4TNru+8bk+aXFWg3DIQBV1X+nAKrVaqvVyrKTZ/37" + "B9J2Px0unKu9vF5/cmTgQEADCCgNAQ3Dvim8E8lC4tDN+xHBM53ntxuXJnpZUgZCwKSREEmZ" + "wIIVizYnkjqdTqVSGQ5662+uXq53mCmpjD05I0sCoac0t/PzW+r3+wMMnzXvHj01Uk6LgBMW" + "Fv6/iZO+8mozbf/Ynj02K9dEeqOoOoo9znvty/IXxrxcMJLCp60AAAAASUVORK5CYII=") diff --git a/youtube_dl_gui/logmanager.py b/youtube_dl_gui/logmanager.py index ecf3b453..08d29b52 100644 --- a/youtube_dl_gui/logmanager.py +++ b/youtube_dl_gui/logmanager.py @@ -3,8 +3,9 @@ """Youtubedlg module responsible for handling the log stuff. """ +import logging +from logging.handlers import RotatingFileHandler import os.path -from time import strftime from .utils import ( os_path_exists, @@ -34,7 +35,6 @@ class LogManager(object): """ LOG_FILENAME = "log" - TIME_TEMPLATE = "[{time}] {error_msg}" MAX_LOGSIZE = 524288 # Bytes def __init__(self, config_path, add_time=False): @@ -42,8 +42,23 @@ def __init__(self, config_path, add_time=False): self.add_time = add_time self.log_file = os.path.join(config_path, self.LOG_FILENAME) self._encoding = get_encoding() - self._init_log() - self._auto_clear_log() + self.logger = logging.getLogger(__name__) + self.logger.setLevel(logging.DEBUG) + + check_path(self.config_path) + + self.handler = RotatingFileHandler(filename=self.log_file, + maxBytes=LogManager.MAX_LOGSIZE, + backupCount=5, + encoding=self._encoding) + + fmt = "%(levelname)s-%(threadName)s-%(message)s" + + if self.add_time: + fmt = "%(asctime)s-" + fmt + + self.handler.setFormatter(logging.Formatter(fmt=fmt)) + self.logger.addHandler(self.handler) def log_size(self): """Return log file size in Bytes. """ @@ -54,7 +69,8 @@ def log_size(self): def clear(self): """Clear log file. """ - self._write('', 'w') + with open(self.log_file, "w") as log: + log.write("") def log(self, data): """Log data to the log file. @@ -63,34 +79,4 @@ def log(self, data): data (string): String to write to the log file. """ - self._write(str(data) + '\n', 'a') - - def _write(self, data, mode): - """Write data to the log file. - - That's the main method for writing to the log file. - - Args: - data (string): String to write on the log file. - mode (string): Can be any IO mode supported by python. - - """ - check_path(self.config_path) - - with open(self.log_file, mode) as log: - if mode == 'a' and self.add_time: - msg = self.TIME_TEMPLATE.format(time=strftime('%c'), error_msg=data) - else: - msg = data - - log.write(msg) - - def _init_log(self): - """Initialize the log file if not exist. """ - if not os_path_exists(self.log_file): - self._write('', 'w') - - def _auto_clear_log(self): - """Auto clear the log file. """ - if self.log_size() > self.MAX_LOGSIZE: - self.clear() + self.logger.debug(str(data)) diff --git a/youtube_dl_gui/optionsframe.py b/youtube_dl_gui/optionsframe.py index 3e4bd2a0..95897d20 100644 --- a/youtube_dl_gui/optionsframe.py +++ b/youtube_dl_gui/optionsframe.py @@ -4,13 +4,11 @@ import os -import warnings import wx import wx.adv -from wx.lib.art import flagart -from wx.lib.embeddedimage import PyEmbeddedImage +from .flagart import catalog # noinspection PyPep8Naming from .utils import ( TwoWayOrderedDict as twodict, @@ -227,27 +225,10 @@ def crt_bitmap_combobox(self, choices, size=(-1, -1), event_handler=None): lang_code, lang_name = item _lang, country = lang_code.split('_') - warnings.filterwarnings("ignore") - - if country in flagart.catalog: - flag_bmp = flagart.catalog[country].getBitmap() - elif country == 'CU': - # Cuba Flag .png in base64encode.org - # Me dicen Cuba ;) - flag_bmp = PyEmbeddedImage( - "iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAABGdBTUEAAK/INwWK6QAAABl0R" - "Vh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHFSURBVHjaYrzNwKDquJyBjYfhxz" - "eGfwwMf/4w/PnH8AuI/sBIMPoBRL8Y2NgAAohFhYHhk831pRr+H7iF/v/7/+/f/z8Q8u8/IOP" - "Pn39///37/ef/73//gCLzc/YABBDjZwYG7uqqT8+e8yUl/LawYWD4D9T2/z8DFIOpf2CakZHx" - "/cePAAHEAnTF73//OX///jx9Bs/Xrwyu7v8ZGaAKYYgJTDIyMrAxMQAEEONHBgb29nZmM7Pfs" - "2f//P5jH5/WdvMwoON///kHcgaQ/AslpQQ5lhRuBQggkA0srq4MurrMv/+wTppk9/LMp11f56" - "gF/f4FhH9//fn7+/e/X0ANf/79lOBiYLgHEEAgDX927GD69On31Mk/f//ay6uz2ypa/B8DxFQ" - "Q+gOyAehjCREOBgYZgABifMvAwJWV9f/+/e9//vAmxP0PCfuPDTAwAP3A+ObNG4AAAjvpz58P" - "f/5wZaT/9vL9/+f/f2iogEhg+ILDiwESSt9+/AEIIBYeBoaPf/+3RfT9esvwZ+FNiO3AGPgNY" - "fwFxcPfv////vv/9z/DvuY5AAHEeJqBwVR0JjRSgdH5/w/QUzD0C0z+A5MMYJIJIMAA5qlT7L" - "92ZXAAAAAASUVORK5CYII=").getBitmap() + if country in catalog: + flag_bmp = catalog[country].GetBitmap() else: - flag_bmp = flagart.catalog["BLANK"].getBitmap() + flag_bmp = catalog["BLANK"].GetBitmap() combobox.Append(lang_name, flag_bmp) diff --git a/youtube_dl_gui/version.py b/youtube_dl_gui/version.py index de094a2a..37f669e6 100644 --- a/youtube_dl_gui/version.py +++ b/youtube_dl_gui/version.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- -__version__ = '1.0.1' +__version__ = '1.1.0' From 3d08f90be9de2009020d20ce1dbe3036e23ece3e Mon Sep 17 00:00:00 2001 From: oleksis <“oleksis.fraga@gmail.com”> Date: Sun, 9 Aug 2020 13:36:30 -0400 Subject: [PATCH 15/34] Add workflow release and setup.sh --- .github/ISSUE_TEMPLATE.md | 12 ++-- .github/workflows/release.yml | 105 ++++++++++++++++++++++++++++++++++ requirements.txt | 6 +- setup.py | 4 +- setup.sh | 8 +++ 5 files changed, 124 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 setup.sh diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 5ef4fe42..1f09c549 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -13,18 +13,18 @@ All invalid issues will be rejected!! - If your problem is a bug with **youtube-dl** or a request for new site support please report it [here](https://github.com/rg3/youtube-dl/issues) -- Make sure you are using the *latest* **youtube-dl-gui** version (Click the `Settings` icon and then `About` to view the current version) +- Make sure you are using the *latest* **youtube-dlg** version (Click the `Settings` icon and then `About` to view the current version) - Make sure you are using the *latest* **youtube-dl** version (Click the `Settings` icon and then `Update` to update to the latest **youtube-dl** version) - Make sure you searched the bugtracker for similar issues **including closed ones** -- Make sure to read the [FAQs](https://github.com/MrS0m30n3/youtube-dl-gui/blob/master/docs/faqs.md) file +- Make sure to read the [FAQs](https://github.com/oleksis/youtube-dl-gui/blob/master/docs/faqs.md) file - [ ] **I think** my problem is **NOT** with **youtube-dl** - - [ ] I've **verified** and **i assure** that I'm running youtube-dl-gui **0.4** + - [ ] I've **verified** and **i assure** that I'm running youtube-dlg **1.0.0** - [ ] **I assure** that i am using the latest version of **youtube-dl** - - [ ] [Searched](https://github.com/MrS0m30n3/youtube-dl-gui/issues) bugtracker + - [ ] [Searched](https://github.com/oleksis/youtube-dl-gui/issues) bugtracker - [ ] I've read the FAQs file --- @@ -44,7 +44,7 @@ Please remove any sections between (---) if they are not related to your issue #### If the problem occurs when downloading a URL please provide the full verbose output as follows: -1. Restart **youtube-dl-gui** +1. Restart **youtube-dlg** 1. Go to `Options > Extra` tab 2. Enable **Debug youtube-dl** 3. Go to `Options > Advanced` tab and **Clear** your log content @@ -72,7 +72,7 @@ delete me and insert your log content here ### Feature request (request for a new functionality) -Please make sure that the requested feature is **NOT** already in the [TODO](https://github.com/MrS0m30n3/youtube-dl-gui/blob/master/TODO) list +Please make sure that the requested feature is **NOT** already in the [TODO](https://github.com/oleksis/youtube-dl-gui/blob/master/TODO) list - [ ] I've **verified** and **i assure** that my requested feature is **NOT** in the TODO list diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..245fe7ab --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,105 @@ +name: Upload Release Asset + +on: + push: + tags: + - 'v*' + +jobs: + build_windows_exe: + runs-on: windows-latest + outputs: + youtube-dlg_version: ${{ steps.dump_version.outputs.youtube-dlg_version }} + upload_url: ${{ steps.create_release.outputs.upload_url }} + sha2_windows: ${{ steps.sha2_file.outputs.sha2_windows }} + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.7.7 + uses: actions/setup-python@v2 + with: + python-version: '3.7.7' + architecture: 'x64' + - name: Install Requirements + run: pip install -r requirements.txt + - name: Build PyInstaller + run: python setup.py pyinstaller + - name: Dump version + id: dump_version + shell: python + run: | + import json + import re + + exec(compile(open('youtube_dl_gui/version.py').read(), 'youtube_dl_gui/version.py', 'exec')) + print('::set-output name=youtube-dlg_version::' + __version__) + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: youtube-dlg ${{ steps.dump_version.outputs.youtube-dlg_version }} + draft: false + prerelease: false + - name: Upload Executable youtube-dlg.exe + id: upload-release-asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./dist/youtube-dlg.exe + asset_name: youtube-dlg.exe + asset_content_type: application/octet-stream + - name: Get SHA2-256SUMS for youtube-dlg.exe + id: sha2_file + env: + SHA2: ${{ hashFiles('dist/youtube-dlg.exe') }} + run: echo "::set-output name=sha2_windows::${env:SHA2}" + build_ubuntu_bdist: + needs: build_windows_exe + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.7.7 + uses: actions/setup-python@v2 + with: + python-version: '3.7.7' + architecture: 'x64' + - name: Build Binary using PyInstaller ManyLinux 2014 Docker Action + uses: oleksis/pyinstaller-manylinux@v1 + with: + pyinstaller-params: "['-w', '-F', '--add-data=youtube_dl_gui/data:youtube_dl_gui/data', '--add-data=youtube_dl_gui/locale:youtube_dl_gui/locale', '--exclude-module=tests', '--name=youtube-dlg', 'youtube_dl_gui/__main__.py']" + - name: Upload Binary Distribution to Release + id: upload-release-binary + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ needs.build_windows_exe.outputs.upload_url }} + asset_path: ./dist/youtube-dlg + asset_name: youtube-dlg + asset_content_type: application/octet-stream + - name: Get SHA2-256SUMS for youtube-dlg + id: sha2_file_binary + env: + SHA2: ${{ hashFiles('dist/youtube-dlg') }} + run: echo "::set-output name=sha2_linux::$SHA2" + - name: Make SHA2-256SUMS file + env: + SHA2_WINDOWS: ${{ needs.build_windows_exe.outputs.sha2_windows }} + SHA2_LINUX_BINARY: ${{ steps.sha2_file_binary.outputs.sha2_linux }} + run: | + echo "$SHA2_WINDOWS youtube-dlg.exe" > SHA2-256SUMS + echo "$SHA2_LINUX_BINARY youtube-dlg" >> SHA2-256SUMS + - name: Upload SHA2-256SUMS to Release + id: upload-release-sha2 + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ needs.build_windows_exe.outputs.upload_url }} + asset_path: ./SHA2-256SUMS + asset_name: SHA2-256SUMS + asset_content_type: text/plain diff --git a/requirements.txt b/requirements.txt index 6a515515..9e135a6b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ Pypubsub==4.0.3 -twodict==1.2 -wxPython==4.0.4 +polib==1.1.0 pyinstaller==3.6 -polib==1.1.0 \ No newline at end of file +twodict==1.2 +wxPython==4.1.0 \ No newline at end of file diff --git a/setup.py b/setup.py index 7f49d97d..bcf17ea6 100644 --- a/setup.py +++ b/setup.py @@ -228,14 +228,14 @@ def run(self, version=version_file): "--add-data="+__packagename__+"/data"+path_sep+__packagename__+"/data", "--add-data="+__packagename__+"/locale"+path_sep+__packagename__+"/locale", "--exclude-module=tests", - "--name=youtube-dl-gui", + "--name=youtube-dlg", ""+__packagename__+"/__main__.py", ], dry_run=self.dry_run) if version: time.sleep(3) - SetVersion("./dist/youtube-dl-gui.exe", version) + SetVersion("./dist/youtube-dlg.exe", version) pyinstaller_console = [ diff --git a/setup.sh b/setup.sh new file mode 100644 index 00000000..7daf7e9a --- /dev/null +++ b/setup.sh @@ -0,0 +1,8 @@ +#!/bin/bash -i +# Fail on errors. +set -e + +# Install pre-requirements like wxPython +# on PyInstaller ManyLinux 2014 Docker Action +wget https://extras.wxpython.org/wxPython4/extras/linux/gtk3/centos-7/wxPython-4.1.0-cp36-cp36m-linux_x86_64.whl +pip3 install wxPython-4.1.0-cp36-cp36m-linux_x86_64.whl From 48c5c7680100d08126b18502df5b0a0bd4464604 Mon Sep 17 00:00:00 2001 From: oleksis <“oleksis.fraga@gmail.com”> Date: Sun, 9 Aug 2020 13:52:45 -0400 Subject: [PATCH 16/34] Install wget in Centos 7 for Docker Action --- setup.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setup.sh b/setup.sh index 7daf7e9a..5ae1a822 100644 --- a/setup.sh +++ b/setup.sh @@ -2,6 +2,9 @@ # Fail on errors. set -e +# Install wget in Centos 7 +yum install -y wget + # Install pre-requirements like wxPython # on PyInstaller ManyLinux 2014 Docker Action wget https://extras.wxpython.org/wxPython4/extras/linux/gtk3/centos-7/wxPython-4.1.0-cp36-cp36m-linux_x86_64.whl From 23a4aafec2c8e0be23075b92d46a9d1b1956c189 Mon Sep 17 00:00:00 2001 From: oleksis <“oleksis.fraga@gmail.com”> Date: Mon, 10 Aug 2020 08:42:19 -0400 Subject: [PATCH 17/34] Build Translations --- .github/workflows/release.yml | 2 ++ setup.sh | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 245fe7ab..6136fb7b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,6 +21,8 @@ jobs: architecture: 'x64' - name: Install Requirements run: pip install -r requirements.txt + - name: Build Translations + run: python setup.py build_trans - name: Build PyInstaller run: python setup.py pyinstaller - name: Dump version diff --git a/setup.sh b/setup.sh index 5ae1a822..7ede564d 100644 --- a/setup.sh +++ b/setup.sh @@ -9,3 +9,7 @@ yum install -y wget # on PyInstaller ManyLinux 2014 Docker Action wget https://extras.wxpython.org/wxPython4/extras/linux/gtk3/centos-7/wxPython-4.1.0-cp36-cp36m-linux_x86_64.whl pip3 install wxPython-4.1.0-cp36-cp36m-linux_x86_64.whl +# Install requirements here +pip3 install -r requirements.txt +# Build Translations +python3 setup.py build_trans From f29d4a58e52da170c73267dfc0e13d097188ae04 Mon Sep 17 00:00:00 2001 From: oleksis <“oleksis.fraga@gmail.com”> Date: Wed, 19 Aug 2020 19:37:37 -0400 Subject: [PATCH 18/34] Update requirements --- TODO | 28 +++++++++++++++++++++++----- requirements.txt | 2 +- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/TODO b/TODO index be2da04f..c34197f1 100644 --- a/TODO +++ b/TODO @@ -47,8 +47,12 @@ Extras * Prerequisites gcc-c++, make, gtk3-devel, webkit2gtk3-devel, gstreamer-plugins-base-devel, - libtiff-devel, libjpeg-devel, libpng-devel, SDL2-devel, libSM-devel, - python3-devel, zlib-devel, libexpat-devel, glu-devel + libtiff-devel, libjpeg{62}-devel, libpng{16}-devel, libSDL-devel, libSM-devel, + python3-devel, zlib-devel, libexpat-devel, glu-devel, libnotify-devel, + libsecret-devel, libXtst-devel + +* Recomended + git-core * https://www.wxpython.org/blog/2017-08-17-builds-for-linux-with-pip/index.html * https://github.com/wxWidgets/Phoenix/blob/master/README.rst#prerequisites @@ -67,8 +71,8 @@ Others * https://extras.wxpython.org/wxPython4/extras/4.1.0/ -NOTES: Compile wxPython from GitHub source -========================================= +# Compile wxPython from GitHub source + git clone https://github.com/wxWidgets/Phoenix cd Phoenix @@ -82,8 +86,22 @@ git clone https://github.com/wxWidgets/Catch.git ./ext/wxWidgets/3rdparty/catch python3 -m venv env source env/bin/activate pip install -r requirements.txt -python build.py dox etg --nodoc sip build +python build.py clean + + +python build.py dox etg --nodoc sip build -v OR +python build.py dox etg --nodoc sip build_wx -v + +## Create simbolic link to build/wxbld/gtk3/lib64 + +ln -s lib build/wxbld/gtk3/lib64 + +## Continue with build for Python + python build.py build_py -v + +**Good Luck!** + diff --git a/requirements.txt b/requirements.txt index 9e135a6b..53a7c129 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ Pypubsub==4.0.3 polib==1.1.0 pyinstaller==3.6 twodict==1.2 -wxPython==4.1.0 \ No newline at end of file +wxPython<=4.1.0,>=4.0.7.post2 \ No newline at end of file From 5334c18bbf64d608b3f901e410d086d39f9a1378 Mon Sep 17 00:00:00 2001 From: oleksis <“oleksis.fraga@gmail.com”> Date: Fri, 28 Aug 2020 23:12:12 -0400 Subject: [PATCH 19/34] Set stderr to PIPE setup.py and set wx.Locale for Python 3.8 --- .github/workflows/release.yml | 8 ++++---- ChangeLog | 5 ++++- README.md | 9 ++++++--- requirements.txt | 2 +- setup.py | 1 + youtube_dl_gui/__init__.py | 18 ++++++++++++++++++ youtube_dl_gui/downloaders.py | 3 ++- youtube_dl_gui/version.py | 2 +- 8 files changed, 37 insertions(+), 11 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6136fb7b..d010975d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,10 +14,10 @@ jobs: sha2_windows: ${{ steps.sha2_file.outputs.sha2_windows }} steps: - uses: actions/checkout@v2 - - name: Set up Python 3.7.7 + - name: Set up Python 3.8.3 uses: actions/setup-python@v2 with: - python-version: '3.7.7' + python-version: '3.8.3' architecture: 'x64' - name: Install Requirements run: pip install -r requirements.txt @@ -64,10 +64,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Set up Python 3.7.7 + - name: Set up Python 3.8.3 uses: actions/setup-python@v2 with: - python-version: '3.7.7' + python-version: '3.8.3' architecture: 'x64' - name: Build Binary using PyInstaller ManyLinux 2014 Docker Action uses: oleksis/pyinstaller-manylinux@v1 diff --git a/ChangeLog b/ChangeLog index e275259d..98ca8b39 100644 --- a/ChangeLog +++ b/ChangeLog @@ -6,20 +6,23 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) ## [Unreleased] ### Added -- requirements.txt - ChangeLog - Man page - Add locale (.mo) es_CU - Add flagart module - Add Flag art for Cuba - Add Logging system using the Python 'logging' module +- Release for Python 3.8.3 ### Fixed - Imports for Internationalization with gettext +- Fix bug set wx.Locale for Python 3.8 - Fix bug subprocess for Windows ([WinError 6] Handler no valid) + Set stderr to PIPE ### Changed - Migration to Python 3.* +- Update requirements.txt - README.md - Refactored setup.py - Build the translation files (*.mo) using polib diff --git a/README.md b/README.md index 0edc949c..22def4f7 100644 --- a/README.md +++ b/README.md @@ -19,14 +19,17 @@ A cross platform front-end GUI of the popular [youtube-dl](https://rg3.github.io * [GNU gettext](https://www.gnu.org/software/gettext/) ## Downloads -* [Source (.zip)](https://github.com/oleksis/youtube-dl-gui/archive/v1.1.0-alpha.zip) -* [Source (.tar.gz)](https://github.com/oleksis/youtube-dl-gui/archive/v1.1.0-alpha.tar.gz) +* [SHA2-256SUMS](https://github.com/oleksis/youtube-dl-gui/releases/download/v1.1.1/SHA2-256SUMS) +* [youtube-dlg](https://github.com/oleksis/youtube-dl-gui/releases/download/v1.1.1/youtube-dlg) +* [youtube-dlg.exe](https://github.com/oleksis/youtube-dl-gui/releases/download/v1.1.1/youtube-dlg.exe) +* [Source (.zip)](https://github.com/oleksis/youtube-dl-gui/archive/v1.1.1.zip) +* [Source (.tar.gz)](https://github.com/oleksis/youtube-dl-gui/archive/v1.1.1.tar.gz) ## Installation ### Install From Source * Download & extract the source -* Change directory into *youtube-dl-gui-1.1.0-alpha* +* Change directory into *youtube-dl-gui-1.1.1* * Create virtual environment ``` python3 -m venv env diff --git a/requirements.txt b/requirements.txt index 53a7c129..46b492d3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ Pypubsub==4.0.3 polib==1.1.0 -pyinstaller==3.6 +pyinstaller<=4.0,>=3.6 twodict==1.2 wxPython<=4.1.0,>=4.0.7.post2 \ No newline at end of file diff --git a/setup.py b/setup.py index bcf17ea6..9a8368b4 100644 --- a/setup.py +++ b/setup.py @@ -341,6 +341,7 @@ def setup_windows(): "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", "Programming Language :: Python :: Implementation", "Programming Language :: Python :: Implementation :: CPython", ], diff --git a/youtube_dl_gui/__init__.py b/youtube_dl_gui/__init__.py index be976b41..b465c580 100644 --- a/youtube_dl_gui/__init__.py +++ b/youtube_dl_gui/__init__.py @@ -76,6 +76,20 @@ _ = lang.gettext OUTPUT_FORMATS, DEFAULT_FORMATS, AUDIO_FORMATS, VIDEO_FORMATS, FORMATS = reload_strings(_) +# wx.Locale +locale = { + 'ar_SA': wx.LANGUAGE_ARABIC, + 'cs_CZ': wx.LANGUAGE_CZECH, + 'en_US': wx.LANGUAGE_ENGLISH_US, + 'fr_FR': wx.LANGUAGE_FRENCH, + 'es_CU': wx.LANGUAGE_SPANISH, + 'it_IT': wx.LANGUAGE_ITALIAN, + 'ja_JP': wx.LANGUAGE_JAPANESE, + 'ko_KR': wx.LANGUAGE_KOREAN, + 'pt_BR': wx.LANGUAGE_PORTUGUESE_BRAZILIAN, + 'ru_RU': wx.LANGUAGE_RUSSIAN, + 'es_ES': wx.LANGUAGE_SPANISH +} from .mainframe import MainFrame @@ -85,6 +99,10 @@ def main(): youtubedl_path = os.path.join(opt_manager.options["youtubedl_path"], YOUTUBEDL_BIN) app = wx.App() + # Set wx.Locale + app.locale = wx.Locale( + locale.get(opt_manager.options['locale_name'], wx.LANGUAGE_ENGLISH_US) + ) frame = MainFrame(opt_manager, log_manager) frame.Show() diff --git a/youtube_dl_gui/downloaders.py b/youtube_dl_gui/downloaders.py index 95e7f2f9..de103b10 100644 --- a/youtube_dl_gui/downloaders.py +++ b/youtube_dl_gui/downloaders.py @@ -11,6 +11,7 @@ import os import signal import subprocess +import sys from time import sleep from queue import Queue @@ -335,7 +336,7 @@ def _create_process(self, cmd): self._proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, + stderr=subprocess.PIPE, startupinfo=info, start_new_session=True) except (ValueError, OSError, FileNotFoundError) as error: diff --git a/youtube_dl_gui/version.py b/youtube_dl_gui/version.py index 37f669e6..07892cee 100644 --- a/youtube_dl_gui/version.py +++ b/youtube_dl_gui/version.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- -__version__ = '1.1.0' +__version__ = '1.1.1' From 525d46860e7b94ffd835efc5001e13dda2cad9a6 Mon Sep 17 00:00:00 2001 From: d1t2 Date: Wed, 30 Dec 2020 23:23:25 +0800 Subject: [PATCH 20/34] Fix infinite loop in PipeReader --- youtube_dl_gui/downloaders.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/youtube_dl_gui/downloaders.py b/youtube_dl_gui/downloaders.py index de103b10..cac9b96b 100644 --- a/youtube_dl_gui/downloaders.py +++ b/youtube_dl_gui/downloaders.py @@ -55,8 +55,9 @@ def run(self): while self._running: if self._filedescriptor is not None: - for line in iter(self._filedescriptor.readline, str('')): - line = bytes(line).decode(encoding=get_encoding(), errors='ignore') + pipedata = self._filedescriptor.read() + pipedata = bytes(pipedata).decode(encoding=get_encoding(), errors='ignore') + for line in pipedata.splitlines(): # Ignore ffmpeg stderr if str('ffmpeg version') in line: ignore_line = True From 5b9d43ab875d6d973a1829465cc2cd88f68260b4 Mon Sep 17 00:00:00 2001 From: oleksis <“oleksis.fraga@gmail.com”> Date: Wed, 30 Dec 2020 19:06:26 -0500 Subject: [PATCH 21/34] Update authors --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index f6bfa461..6591a566 100644 --- a/AUTHORS +++ b/AUTHORS @@ -24,5 +24,6 @@ fat115 Mitsuya Tsujikawa pavreh oleksis +d1t2 # Generated by update-authors.sh script From 8523e44a5a69544e5d3450c59334bf4f3c5eb181 Mon Sep 17 00:00:00 2001 From: oleksis <“oleksis.fraga@gmail.com”> Date: Wed, 30 Dec 2020 19:20:33 -0500 Subject: [PATCH 22/34] youtube-dlg v1.1.2 --- youtube_dl_gui/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/youtube_dl_gui/version.py b/youtube_dl_gui/version.py index 07892cee..45da0750 100644 --- a/youtube_dl_gui/version.py +++ b/youtube_dl_gui/version.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- -__version__ = '1.1.1' +__version__ = '1.1.2' From f7db2d11beac87905a6d8eb5792456612676238b Mon Sep 17 00:00:00 2001 From: oleksis Date: Mon, 18 Jan 2021 04:34:25 -0500 Subject: [PATCH 23/34] Use unittest.mock ,fix and pass tests --- .gitignore | 1 + tests/test_ditem.py | 2 +- tests/test_dlist.py | 2 +- tests/test_parsers.py | 4 ++-- tests/test_utils.py | 14 ++++---------- tests/test_widgets.py | 2 +- youtube_dl_gui/utils.py | 6 +++--- 7 files changed, 13 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index 25441494..5c426f98 100644 --- a/.gitignore +++ b/.gitignore @@ -7,5 +7,6 @@ build/ env/ .idea +.vscode *.spec *.egg-info diff --git a/tests/test_ditem.py b/tests/test_ditem.py index fe773592..8d167acd 100644 --- a/tests/test_ditem.py +++ b/tests/test_ditem.py @@ -348,7 +348,7 @@ def test_update_stats_empty_strings(self): ) def test_update_stats_not_string(self): - self.ditem.update_stats({"filename": None, "status": 1234, "eta": False}) + self.ditem.update_stats({"filename": None, "status": None, "eta": False}) self.assertEqual(self.ditem.progress_stats["filename"], "url") self.assertEqual(self.ditem.progress_stats["status"], "Queued") diff --git a/tests/test_dlist.py b/tests/test_dlist.py index e2cb21ac..ace52920 100644 --- a/tests/test_dlist.py +++ b/tests/test_dlist.py @@ -7,12 +7,12 @@ import sys import os.path import unittest +from unittest import mock PATH = os.path.realpath(os.path.abspath(__file__)) sys.path.insert(0, os.path.dirname(os.path.dirname(PATH))) try: - import mock from youtube_dl_gui.downloadmanager import DownloadList, synchronized except ImportError as error: print(error) diff --git a/tests/test_parsers.py b/tests/test_parsers.py index da4ebe2f..41a8d2f4 100644 --- a/tests/test_parsers.py +++ b/tests/test_parsers.py @@ -27,7 +27,7 @@ def setUp(self): self.options_dict = {item.name: item.default_value for item in OptionsParser()._ydl_options} # Add extra options used by the OptionsParser.parse method - self.options_dict["save_path"] = "/home/user/Workplace/test/youtube" + self.options_dict["save_path"] = "/home/user/Workplace/test/youtube/" self.options_dict["cmd_args"] = "" self.options_dict["output_format"] = 1 # Title self.options_dict["second_video_format"] = "0" @@ -37,7 +37,7 @@ def setUp(self): def check_options_parse(self, expected_options): options_parser = OptionsParser() - self.assertItemsEqual(options_parser.parse(self.options_dict), expected_options) + self.assertListEqual(sorted(options_parser.parse(self.options_dict)), sorted(expected_options)) def test_parse_to_audio_requirement_bug(self): """Test case for the 'to_audio' requirement.""" diff --git a/tests/test_utils.py b/tests/test_utils.py index 98439771..b4309773 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -7,13 +7,12 @@ import sys import os.path import unittest +from unittest import mock PATH = os.path.realpath(os.path.abspath(__file__)) sys.path.insert(0, os.path.dirname(os.path.dirname(PATH))) try: - import mock - from youtube_dl_gui import utils except ImportError as error: print(error) @@ -112,14 +111,9 @@ class TestConvertItem(unittest.TestCase): """Test case for the convert_item function.""" def setUp(self): - self.input_list_u = ["v1", "v2", "v3"] - self.input_list_s = [str("v1"), str("v2"), str("v3")] - - self.input_tuple_u = ("v1", "v2", "v3") - self.input_tuple_s = (str("v1"), str("v2"), str("v3")) - - self.input_dict_u = {"k1": "v1", "k2": "v2"} - self.input_dict_s = {str("k1"): str("v1"), str("k2"): str("v2")} + self.input_list_s = self.input_list_u = ["v1", "v2", "v3"] + self.input_tuple_s= self.input_tuple_u = ("v1", "v2", "v3") + self.input_dict_s = self.input_dict_u = {"k1": "v1", "k2": "v2"} def check_iter(self, iterable, iter_type, is_unicode): check_type = str diff --git a/tests/test_widgets.py b/tests/test_widgets.py index 66c57bf0..c3c0466f 100644 --- a/tests/test_widgets.py +++ b/tests/test_widgets.py @@ -7,13 +7,13 @@ import sys import os.path import unittest +from unittest import mock PATH = os.path.realpath(os.path.abspath(__file__)) sys.path.insert(0, os.path.dirname(os.path.dirname(PATH))) try: import wx - import mock from youtube_dl_gui.widgets import ( ListBoxWithHeaders, diff --git a/youtube_dl_gui/utils.py b/youtube_dl_gui/utils.py index 8bbd900b..6cf4e913 100644 --- a/youtube_dl_gui/utils.py +++ b/youtube_dl_gui/utils.py @@ -58,20 +58,20 @@ def convert_item(item, to_unicode=False): """ if to_unicode and isinstance(item, str): # Convert str to unicode - return str(bytes(item), encoding=get_encoding(), errors="ignore") + return str(item) if not to_unicode and isinstance(item, bytes): # Convert bytes to str return bytes(item).decode(encoding=get_encoding(), errors="ignore") - if hasattr(item, '__iter__'): + if isinstance(item, dict) or isinstance(item, list) or isinstance(item, tuple): # Handle iterables temp_list = [] for sub_item in item: if isinstance(item, dict): temp_list.append((convert_item(sub_item, to_unicode), convert_item(item[sub_item], to_unicode))) - else: + elif isinstance(item, list) or isinstance(item, tuple): temp_list.append(convert_item(sub_item, to_unicode)) return type(item)(temp_list) From baad01d3b63b8ebc93f5455db6c1bb1a30dc13b2 Mon Sep 17 00:00:00 2001 From: oleksis Date: Mon, 18 Jan 2021 05:06:54 -0500 Subject: [PATCH 24/34] Add Tests workflow --- .github/workflows/tests.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 00000000..cc76dc06 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,31 @@ +name: Tests +on: + push: + branches: + - master + +jobs: + tests: + name: Tests + runs-on: windows-latest + steps: + - name: Check out repo + uses: actions/checkout@v2 + - name: Set up Python 3.8.3 + uses: actions/setup-python@v2 + with: + python-version: '3.8.3' + architecture: 'x64' + - name: Configure pip caching + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + - name: Install Python dependencies + run: | + python -m pip install -r requirements.txt + - name: Tests + run: |- + python -m unittest discover -s tests -v From 62cd2def7ccf0d42b5ecc8f8a291c46d5e12b5d8 Mon Sep 17 00:00:00 2001 From: oleksis Date: Mon, 18 Jan 2021 05:16:28 -0500 Subject: [PATCH 25/34] Add Build Translations step to Tests workflow --- .github/workflows/tests.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index cc76dc06..9cea4210 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -26,6 +26,8 @@ jobs: - name: Install Python dependencies run: | python -m pip install -r requirements.txt + - name: Build Translations + run: python setup.py build_trans - name: Tests run: |- python -m unittest discover -s tests -v From 7dc46039f497fa58a03fc0eb52103cc54580525e Mon Sep 17 00:00:00 2001 From: oleksis Date: Thu, 21 Jan 2021 04:27:41 -0500 Subject: [PATCH 26/34] Fix some bugs: Logs, Close Frame --- ChangeLog | 5 ++++- youtube_dl_gui/downloaders.py | 32 ++++++++++++------------------- youtube_dl_gui/downloadmanager.py | 17 ++++++++++++---- youtube_dl_gui/version.py | 2 +- 4 files changed, 30 insertions(+), 26 deletions(-) diff --git a/ChangeLog b/ChangeLog index 98ca8b39..19bacbc1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -18,7 +18,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) - Imports for Internationalization with gettext - Fix bug set wx.Locale for Python 3.8 - Fix bug subprocess for Windows ([WinError 6] Handler no valid) - Set stderr to PIPE + - Set stderr to PIPE +- Do not interpret output [debug] as an error in the Logs +- Get all Logs except "ffmpeg version" +- Do not send messages to the GUI if the app does not exist (Destroy) ### Changed - Migration to Python 3.* diff --git a/youtube_dl_gui/downloaders.py b/youtube_dl_gui/downloaders.py index cac9b96b..710cb6ed 100644 --- a/youtube_dl_gui/downloaders.py +++ b/youtube_dl_gui/downloaders.py @@ -46,7 +46,6 @@ def __init__(self, queue): self._filedescriptor = None self._running = True self._queue = queue - self.start() def run(self): @@ -59,15 +58,11 @@ def run(self): pipedata = bytes(pipedata).decode(encoding=get_encoding(), errors='ignore') for line in pipedata.splitlines(): # Ignore ffmpeg stderr - if str('ffmpeg version') in line: + if 'ffmpeg version' in line: ignore_line = True - if not ignore_line and line != "": self._queue.put_nowait(line) - - self._filedescriptor = None ignore_line = False - sleep(self.WAIT_TIME) def attach_filedescriptor(self, filedesc): @@ -181,8 +176,6 @@ def download(self, url, options): if self._is_warning(stderr): self._set_returncode(self.WARNING) - else: - self._set_returncode(self.ERROR) # Set return code to ERROR if we could not start the download process # or the childs return code is greater than zero @@ -232,7 +225,8 @@ def _set_returncode(self, code): @staticmethod def _is_warning(stderr): - return str(stderr).split(':')[0] == 'WARNING' + warning_error = str(stderr).split(':')[0] + return warning_error == 'WARNING' or warning_error == 'ERROR' def _last_data_hook(self): """Set the last data information based on the return code. """ @@ -295,7 +289,6 @@ def _proc_is_alive(self): """Returns True if self._proc is alive else False. """ if self._proc is None: return False - return self._proc.poll() is None def _get_cmd(self, url, options): @@ -322,24 +315,23 @@ def _create_process(self, cmd): """ info = None + kwargs = dict(stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + if os.name == 'nt': # Hide subprocess window info = subprocess.STARTUPINFO() info.dwFlags |= subprocess.STARTF_USESHOWWINDOW info.wShowWindow = subprocess.SW_HIDE - # Encode command for subprocess - # Refer to http://stackoverflow.com/a/9951851/35070 - # if sys.version_info < (3, 0): - # cmd = convert_item(cmd, to_unicode=False) + kwargs['startupinfo'] = info + kwargs['creationflags'] = subprocess.CREATE_NEW_PROCESS_GROUP + else: + kwargs['start_new_session'] = True try: - self._proc = subprocess.Popen(cmd, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - startupinfo=info, - start_new_session=True) + self._proc = subprocess.Popen(cmd, **kwargs) except (ValueError, OSError, FileNotFoundError) as error: self._log('Failed to start process: {}'.format(str(cmd))) self._log(str(error)) diff --git a/youtube_dl_gui/downloadmanager.py b/youtube_dl_gui/downloadmanager.py index d040916e..09c578cd 100644 --- a/youtube_dl_gui/downloadmanager.py +++ b/youtube_dl_gui/downloadmanager.py @@ -27,6 +27,7 @@ RLock ) +import wx from wx import CallAfter # noinspection PyPep8Naming from pubsub import pub as Publisher @@ -501,7 +502,10 @@ def _talk_to_gui(signal, data=None): downloads using the active() method. """ - CallAfter(Publisher.sendMessage, MANAGER_PUB_TOPIC, signal=signal, data=data) + app = wx.GetApp() + + if app is not None: + CallAfter(Publisher.sendMessage, MANAGER_PUB_TOPIC, signal=signal, data=data) def _check_youtubedl(self): """Check if youtube-dl binary exists. If not try to download it. """ @@ -559,17 +563,19 @@ class Worker(Thread): """ WAIT_TIME = 0.1 + worker_count = 0 def __init__(self, opt_manager, youtubedl, log_manager=None, worker=None): super(Worker, self).__init__() # Use Daemon ? # self.setDaemon(True) + Worker.worker_count += 1 self.opt_manager = opt_manager self.log_manager = log_manager if worker: self.worker = worker else: - self.worker = 1 + self.worker = Worker.worker_count self.setName("Worker_" + str(worker)) @@ -638,8 +644,8 @@ def stop_download(self): def close(self): """Kill the worker after stopping the download process. """ + self.stop_download() self._running = False - self._downloader.stop() def available(self): """Return True if the worker has no job else False. """ @@ -752,5 +758,8 @@ def _talk_to_gui(self, signal, data): if signal == 'receive': self._wait_for_reply = True + + app = wx.GetApp() - CallAfter(Publisher.sendMessage, WORKER_PUB_TOPIC, signal=signal, data=data) + if app is not None: + CallAfter(Publisher.sendMessage, WORKER_PUB_TOPIC, signal=signal, data=data) diff --git a/youtube_dl_gui/version.py b/youtube_dl_gui/version.py index 45da0750..5220f471 100644 --- a/youtube_dl_gui/version.py +++ b/youtube_dl_gui/version.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- -__version__ = '1.1.2' +__version__ = '1.1.3' From 4461688ea2409157deb0909453ab6522aafe8e9f Mon Sep 17 00:00:00 2001 From: oleksis Date: Thu, 21 Jan 2021 05:31:28 -0500 Subject: [PATCH 27/34] Update README.md --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 22def4f7..ad6a20c0 100644 --- a/README.md +++ b/README.md @@ -19,17 +19,17 @@ A cross platform front-end GUI of the popular [youtube-dl](https://rg3.github.io * [GNU gettext](https://www.gnu.org/software/gettext/) ## Downloads -* [SHA2-256SUMS](https://github.com/oleksis/youtube-dl-gui/releases/download/v1.1.1/SHA2-256SUMS) -* [youtube-dlg](https://github.com/oleksis/youtube-dl-gui/releases/download/v1.1.1/youtube-dlg) -* [youtube-dlg.exe](https://github.com/oleksis/youtube-dl-gui/releases/download/v1.1.1/youtube-dlg.exe) -* [Source (.zip)](https://github.com/oleksis/youtube-dl-gui/archive/v1.1.1.zip) -* [Source (.tar.gz)](https://github.com/oleksis/youtube-dl-gui/archive/v1.1.1.tar.gz) +* [SHA2-256SUMS](https://github.com/oleksis/youtube-dl-gui/releases/download/v1.1.3/SHA2-256SUMS) +* [youtube-dlg](https://github.com/oleksis/youtube-dl-gui/releases/download/v1.1.3/youtube-dlg) +* [youtube-dlg.exe](https://github.com/oleksis/youtube-dl-gui/releases/download/v1.1.3/youtube-dlg.exe) +* [Source (.zip)](https://github.com/oleksis/youtube-dl-gui/archive/v1.1.3.zip) +* [Source (.tar.gz)](https://github.com/oleksis/youtube-dl-gui/archive/v1.1.3.tar.gz) ## Installation ### Install From Source * Download & extract the source -* Change directory into *youtube-dl-gui-1.1.1* +* Change directory into *youtube-dl-gui-1.1.3* * Create virtual environment ``` python3 -m venv env From f71de2dfd3b04bc800a03ad1cc996771ef542cf9 Mon Sep 17 00:00:00 2001 From: oleksis Date: Sun, 24 Jan 2021 20:01:18 -0500 Subject: [PATCH 28/34] Add Class Diagram UML in .pyns --- docs/youtube-dl-gui_uml.pdf | Bin 0 -> 81337 bytes youtube-dl-gui.pyns | 120 ++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 docs/youtube-dl-gui_uml.pdf create mode 100644 youtube-dl-gui.pyns diff --git a/docs/youtube-dl-gui_uml.pdf b/docs/youtube-dl-gui_uml.pdf new file mode 100644 index 0000000000000000000000000000000000000000..a917c1b3b1a915cb4dcf1c15ab403912ff7abbe2 GIT binary patch literal 81337 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�vDFP8%gaS*P|dS zt*0>$HjEd9NH(|ou)L_LZ-9#KGV^znW8p zmvHWOvY~$5yjVgV8;a&CbW>6Kbm|OE$AL)neUZ{ckYQnAnV_MdpdjmCh*)bFaeV4M z8ZJT_k?!Z+mtTiLtzSJuh=QcbD&jqy^=!9ek{&P@ue=LgM4-v8W#S60FiFn$&YIW25{{ zPZGR}0|UW^EdCW+fy%R!QmtwNIfMPblhZ!7%N}*olTKVs1q}uuX%ys!@zW*Hs9vf5 zK6?^Qinr-`PrI6=4wkldwQ&S*$S|D5zKp-c-C3=EmQM&diO+_c^;yQA%I?HD0UCaS zX5CvGf4T_rS<7Qe=VOK7lYe%tr$hihgQll_!$%AC)xLG0rG_}{)5jNFFP<29BWptk zM|&eZtA8LHeRFt5MgRlgAApAko?bxD!AQi$+7Uo6C@%C5KrO-uPcLNTVDL$;XYKgU zBL{#6;Qvkg$*17v;AmteZf$JyFP*Htp^?3rwF!XbQ;|G8y}Xf$*{2C^0M`G&6`X8s zEsd;~j1( ziJa+^OV3u!$jrpl5x~m+-!?k}KG);7$rf|I`EzjgoF zm;Vrc8u~x<@boGG&VPmQ@d32p>4mKgYz+VDssC;@t=Ols{}S^LLPOYzUi@f=k?s{GnAz^%r*F;J@W8z_jd2I`8**pOZ`Cjrki)- z_VIr4@k0BtxButkK5KL7rL*ns;sX?+75vfP*`4A3;bS9TBEt-NrEj}{BqH+Zgrndx zd&#U0=IJve;a+`tD5DT13K{;mzrT{T`EbI2o!@@y_Id4%Iw$&gzbEQkYCT0n4}aSH zm`C9b3T{WBXx(^}O>;T_$U^wo__%tz=YLn&M7WyteD8c$d3u^8epZSpWP4Kj@yz0_ zGICq|^FyuJ=f3SUV_M>4^1Z^b2rall#Gy=)3d7P!Xv5f(nwyXsRJ zpy8Ovy_~d#*)~h;_Y(iNYoEXNadGmSP8?4HK5zcT{gHx3#6<=(TZWXelnAXBSE1wF zHMN)d5gNE`f=t%&ZLxsQV&%)%(dXhf*RvBIi~{VwY5p;wMr1dk%}|+AL@+m!ppv^&IUE+ zXM2YUg?Q6s!&Zq9Wbh}j!p8^KmJG=f13$@~o_rls%_jj%7OBM)+i@M4UkRbKBQt_~ zBTe@)B!NewF=Hz(l-$l6%i58KZ3cdH#Mh{CDHs{YCePOi0Uhz!Y7XN?V-e9LB2v z24jOOTsnG+%&v33Hc4z;cj+9j>!N@n7xh^eIUE?>&y3D9Lw$)XSy^6bH;_m8YCpet^eE?oiZdABpHU4PXjGc$3=hev*0Mi~+y zld(XNeP1`9tJsD9v8#AoCpXd{XJa4-H;sxiE6=L6b+wXiaP?JUds=S1Yo|j1;dfUq zX{^{$Qf*mJOTZ1(qO~ zoA}PHE}2Wn;(*d={I{rFbLVEPIf}V)3mMg7^D-nI+tFa$TRvw+E$mtXga)R>W4f!&uyl7|jg2J?3YdxL zY@XckPSi~-yjR0jvKZIKdh7SMES4;y%W>#F`$nIeDzYUq&>45B8liMDNU|a5>A5AG zemDsfxL`T2yjWj;Eq;0W{f8TadDk(6FN|eX6_RQX6=|6& zTT|@5z$5%%ypG4?am;GOa9+APS4pt9-h?nWnc0fF5S_uPKXEwQn#GcYD|cY2kiWDK ze&gG1-C#?;XLOyeB|F#fo-qICF80VobU}yMuolHzgP{qp^q-hC)UByjRoS#dL4gPN zKgiaDs86Uq6a_Y?5r{r~P0nu$1Hyw7y{QosDe}}6M1zWP%}&jyz0ia_9VVMpCwFBv z-ko-YiCRQ#?m|RpTsMn_NB*u{q?;i}c(r~G%i_%C$R!NZ%(1S{K0nzEJ@TtfHk=}( zRCuCDc-|nB@gG<|0g{@trj+4nQR3?bZ?})Z!XKqHJ#m>Fq!J3#=zj!@g})S&DXa)T zTfD~Svs2q?O>co=2d(hv``^d}DO92$0CmFE+K*K)T=GJP1O+l_4(XzEqlEU#7Xv?b zXW@nmO*8sue{8o5r6V`Og>q*Ezdzc8NP-@mL)r{lE?z54qvI z43<)i3DWd?QviHTJV0R}E zRC!(8J9LKX=@x8q*3C3pKU?wmc2u@G`}s1p5k$P?~pkBb(j>AQdq!cBA!T zQ)$10J38H6t~)DCfEzDJsR?qQSAvpjFf-(GD;$*GWD5}gv=5N9R0h(gFh$t^T0+qU z5OLPnHSFeYXWV~8(0Zg$4i4-{0~5GKgfUV8(fh%@+`5rChib?1z7l6UUg4|hh-qo>=JEYrpm<#n z+i&(HZM3ka?%xzqxcA3qUfWA>oc^tu2{vQCWPi`8!>J9G5JkPmx%$4aHXQX=2{Zvt zLX;J?1vOdn1mP8Ylmj9_x!sxz>4=+Re<)j7K1SogG9&HoeT6maup&(FTeR}+Va==> zan>BCnQPhI(Y!Fqw(*DRpBG2d$kjg*qrI1Ix=k-Ga!Uys>L+&9+pqGLb ztH$&oxO|@1fzdqmpb?YO{3gPejn9+ZLe88D0i zx43FXACp^riN70{)~2UwOz1B%hF>1^xl&?~LbO`=mpvz0dKT7iEVNzXq#NewxGKy8 zluTyAmTtg3PM2wKJV%Eudk40hiqnVC*hXpDCDJ=V)8o1Xm9N2jxHd#v%d1oyYu6G= zExQA}Lg!V%ljdwZwq`d{PcBW--DMG%p-lFrDqNe}$$o7k4_VekoZPgTshNOQXp9^Z zlrqk&SFl?D{w^V~$@OLDfI$o zz9}wu3eE6w_G##OKhq&1Wd9h9#6qt#IygY;H{J>aQV(7rNBO84tbhVW!-n%oI<>mD zYA>EtZ(6M))}KNZvX(dvt%R|#E>alWLEt^B`IX6dK^F|u)Aa>Gz61GdNwQP57*Et; z>o7>ll>mUxa^3$bbcJt}-67O174fiV^TTgDSld)N-vq2;+=#F)%ShQ;zm-aM75;a` zmtSh%5x|G&U8jn0Ynl0amLeJo#=rhGqJuw<4nRA;ctNpi`*wu!xC~DuU8=w{neE4x z3)IZzc{7DB_es7R|Q#J8wMr}NJQeGZB&6pp?4rJ!Lgi2{81wy1XG%q|cHij-LFkeghk zG<6exxzU$D^)aVu;>-!-XNtG5PV`y!dyPQ; ziv#{%GeQ+i_~^&-ny%hQmBb^#B6x}W;sFA99+ZNbkN0M$IP?C9ljIW+6Fa(>*r5b% zMfKxn@WTlcyX4@dTzG^biqRliiGJ4d_AnGUmwYfy3pNKUhP799vjX#WdYXf5hbS^7 z=Xt~Dz(&Lb^S04N7t^gBrnE_5yj~V&Is0r43T#FZ{cbm=v0)O`V$d%7O&-L8&m;Qh zvipu3-l*9JW5X(-{1lP4K_O?#Daz$-nTcTfPk{BFroN?E$7lO5S_VzpnS_G! z>2&pC>Jm?+44Y9?sJz)PK%%5{(Po_+`0^5GA!_vS@1E9*mC>v#D~qvYXPXJCWqA4bR5+_?A$7r+k`y9SO#J6Fu{^*p z#YYor$TmJ+-sG>3zjp)P(rxL9TD)N!_9#zDgfr~}MTBG69YyPYtIyII@uQgk9gOb% z(|}{qKth^p1jr--eWWnC=r^&LxfawA#(z3rX=$|XV5(?W^I<7S+gt^9Y|>T~A)P9r zIp%U6XReX?thZ3|)n&evJcmz}YZUcq&OV4J31feCVM636V(qWfkIO|0K6iXS%T=%0 z+ML;yW6kpL^XX(HZ_)nxIYsafGd53T>eCTF)NmQU^YJcw*cQzXIf>ncx27nO1+d0- zAKOsm1z6iGMKQ3y>5C#qkJjp0f45nSGxl2*Lu6Na)4gPL5u>H5^EHwiIXi0gU|S5{ zvgY-}Wk% z_rhmDAZC2{kqGX~=dU&#KrzezC|Y!$bC=UTD5{CVd?*@GV>Yg+?k^tFnB>4}26;VF zaWw`&2=%STD4gFUFQb~HkIp;LqRE%TwUs(AOw^|lzfy1I*7VQ&xBihKg7ru zYGYJ1#hhHSY7$lsqEF%Kz7wP@Z zKhz^&TdW`M{3(17Bi(@EP0+AcxSd_`PO?1wnzNGV7;|7^M)9k!GSC!kmTu>3iMYJS|OGOJE_tRyz%_&&?FqZ!G^iiEQe~!`6sg7 zF<}DqCFLlk1SF+qisrY3T)JLR7f2k@R~?4^W9jJ%o=@ogDX__Nh#;?cUgi+~DJ7Gi z-qs(F(Yd5%2Ny8a@zbVJWj-3kv4)3S5|Sh?=u+JrMa%5?gW${zivPXBvDDqmsP^ zd-Ey)k;Y~dXp$X0Kat7KTAR2%aiZXtsQ6P8n2$aQt4Sjrrs$llb*m}Km zul|~1g#;mhxe909Sn?a?kFTV}&cooD>k*-z9JFyIOcF_^7d#enSUKDaP1CPTp`c%} z9M5E@8b3>udhy!iR@1*NTdg$FiFBc<@X^_X$8{kP;J~?;OKFQB?D$ktZA*cI!@z1p z$EVSfo*_J1eIsPidBg==l@rUpx(8w54)emem&_3y{XE3k$r!|Vpaj=J1=G|<#;p2L zH(*$PS=}~ucK%aqm{R8(PrF7UgTxebAHfIdAwglG2V_AmW>t$gBWc}`v3=QWUvEdv z<2NpO+wL+#c(k^iqlxx2%v;MzQ*Qq74GIDfZp_6y(tM4fDtHochleJi(t=o&;`=cC zkH5?TG>V$Jb6vl94~^@*QO&Ts9Q4yRy$%&I(6J*ZjaKBsC`wJ5lqb-mL-%!ULNWKS^7I6@o_UNUs_xzS3rU^fp>(;8iS(a zMM;>UI<#+UR=_{l*%y1~a4@3(#QKs1Jcsk@h24TCavZDgJYaO)dXt&Hq}lk+Xq=RF zk@(q~b+JqjDrxd3H4%kF9+>KqoA^GQ0*CoDK36}(_hGSi53f$NHATRrXV zmr2tl56_$#9S5uT$`ZTgtf|EVbZtc^INjwdW z`bY;vRWzO5`ov2qRf0HcLXBhtO=xBeFW3Zt3Hs=zs71$R<=M$Ar*cYAVdvhk-|_=R zU=QUj^^w4rM1}JyRW(YsntkLTgJz)|4s*BMC*)*IB({TAyOm%?_F%`HXPUB~8cV-? zl^boCw3}QR^(QEJg-UuespU-Ec`Z2zMzXt9l{?@Jr)c<=-uNAm5W^CIYMZaxP6f%b zc7)7CkcAe6>!!Y~0Dr3CFDp&sUCqho(jC=#JA5&M(Q4Es{{H<#P+LB$`6@?~GOBH)KY{Yk zzgr`lbj-h7c(3|r&asikYmI&w)xtF)6XHWYD@0g2n+FB!8jRYMUr6efUp!bi;RwIh zL4o}1^7owxCNW+r=Ip*2vE)b-9Fk#aZ%Ak&Fl5)F}g%Z2GRa_wp}T_pbG z9x$CN1QHZgJxSt_>+0yz+nY(c?R=LR-)=)uam-79tlHrnm`LfS5xu`?2Hm4IvY-C9w@^d+yzS05t1v1YNZl1nPA zUb#wsq2%md-23ap#&XsdXHUj{VWD^;QIPKDmYpo_WrPWreFA}P!%vWGvWh<%kB@TY zzdix;A!^Dir@gDWl`#B1+^QsBWm#91jJ0H|TFzryo!}TtHtLQNXs%+g{9Gnyb59E! z1GIgr^>8iKRbnlvR)zH%B}AkI${O@KPqF#YUlx|j#Oi0LHCd!zGzF~JPH=u(W6|!N zYNWhxdR*+-2zFdc(OOumTrwaED+_VX%j~bL=bC8$vVTWaIh~Q2-9TBiarb)o$xITsw8l79X;4Ul zwmYK1IvV-HS(;*k%#35M(pyc#5wIF$Hj8U}_~?@v3j^mFG|8CqG9$>u{Ldu~>v!@kGt?Z(lQn_;6#-i zkWQ3Ltjw0FRDrdqLNzI6FZ3&HbKIaPPPX_KrTqRClg5$JLHlQzfK9I^I4U@BiNWy>E>7&RC4I);x2rIakaz_g-u7AC@?sjuS4S zZzCtG(E8mwQQ?U`G^HKxBKYnjBg)GK+6i%#m|m8Qow}30vr^V=e(lUePdBeS8|VahvRD3m;8NU55NDV0)0F zlE13L8!A@HIpwXR)>-=>&T^&TvKfX3ZpL%m1V4T%`Vs{K(fg(^&5X z4DA3&qhN-jcDtwX1e5dB$@!KwL>oaYlBQ>P5Ho;-CY4*ZY~y zL&HlwFjzkOt_(H&`t@C@qz^-s&N4zqkO$x|$&z1-#Ggr_g!@Olw8Xp~HXaLeGAxjF z+csV(_kYe^nFQ+)5bd=WL@i&hrWzr|5{Xe38=7l8#NUlsFH3Ct>NI;MF?ci?_Arfp zvvKqV{~X!04i#DS3*Vin{f5dH$}d=F$I;xg60W2HI;p4VXdoMBleOXjWkNgE2SCQmHB0!I^6S&e2x{JJlE1FN;q0fV&Qj@ z1CbAqMgupIDY?5;GJI${Bj+AXM$INT|2p)EcFFuJY1nT2Q6aV|S$=Mz|7t?c49aW` z(})?>ag1STaJw*NQL@ch61s=+eMXgbwj!SFPEh1-s7~mxZkm-jk$*L{F?-{xO3R8W z9qIRPRrp&2I=xBlnHVvzC00WGq#3mRInzqN9Ob2W5PpcgclEWI?Rxwn-|Hv!SmTw@ z2+y6PY;%5kF)vAzhb2BmESeNgU>10!gL1FKBueu9&Qaf|cW02A>`YX1xPO12Za{lP zC!2ZFFSxkdI9xtPrIJoH$|#Lzt9A1d6Mhdy0TP-->fny6#b1j}yIMDgI45 zSqLT=ixwBypOUwqlj>zhSaO+Dav00v9S?7QCnlm?fJ%ygwtrnR$6K5t?dlL0Up3?# zu>)T-F9kM(hMoHQdZDchG<^JEtL$}N%3nh~{jJ+@vgAdpo>i!~s>l&Vs$JvQDq`cT zN*XgX`YZ2tMO3+)$#bzXKTP|X=obue2Z67hhI=n@JCF457$(Un2Q~viGBsb)_b0^} zV9vSEql&VD4;w?jc!PxUR;LN;2koVRCqe!&JJnuNkxO2vH0AROhmS*Jj;_w8W$0-L zfvFW4NAdi2VzCM|{mC*l=B{4G%WMcvXS4tFiz+9v`UT5$xg*yo?_1Z&KSrJYL4&n1 zJDr8&5k!QDL$!YVo3-pYquzcWBRxxpmoHvN)sDs()#@4fOv&1=xYsAT{1Rki3fK^m zaI)i@r?`?9UUa8@_k~tXX!hpq{+pLU;SzKqg|$reDs*iHpGf-+m(MPwaspDR(jL># z_q(2b{Xm;$)kjr2g_5DxNp<%GQ|OK9ds_Md0@TXQv1!k3r?71vc1dhRLn>4-wL5`h zI`v(u*^r9tOEqiN)huqBas4}t=IgITo`g=9b?0;~TJ%_Lk5AE0XuAIC8^Emo7TwFA z_L%)0SH%x~;YDC4$_(XtQ09TsD`JW5e8s6Ud`I$vS&Mv(7dl3VPng*$xss_7UZqee zDZI$Z2U#HyaN^Tn!K|tlyuOnvt8zD*Eim>Tll~0agL_k8C>x6K5K51|_P3=?E9gK0 z`hlFQ_4l%`N46^C0?*x^3B*Z#QI^Yb*eyGn5Dc8H!xwg!?hd$O;{%-j!&*f2`G}Xf zFTGtvU+a)!hm_IX6~AMaXa`!*x$cXz!#{_@N)h2Yqf8>Cq;ke0)89rv@CJNsw1IVL z#iH@MU&NKi8!|E|%yDgT`@%zWxZU@oWeRar?2GH-bl2l@3Zu@5x=HbKnW{~LOYE&u z4`|J)Sda7L0@c@`cuN`rJeO7L&0eF|Z1H>3kq@dL#NR`~6Z;6fO{nRy`)O}!$4hPN z?egUzm;D1Vz8kZ>)bp^s2M^61#_}~C56oFTE+ag~bQK!XUDlPl^h_>S#jN!4SLAkf zH~-XUN{4f)#SNhTG-_;RYAr3IP4S!U*~zK~9Wy=jQ_+#?;kW13rPpP`)i?$I! z6swA)_j}1Ly?oY+Th@r=zhvN^JWe;zeWPeoN zT0a2Q9EokPd3As_sSIql&6S&{&`1UgE*4^%2WPLuVcmo zl-MX$e0;QBx?FNvs}8%qiarO2I;~e^GJD>Ao-NW`>C#JD(Q8F4?GV6s8Ie4qcGrY*{0KJ4vNRK6d}QS>@!_t-|2 zytzK^*sHjf;J6JE6Eo-@B~T?QU`k~^ZQyoNluU@Pv&u5<7F?8K9&Vts|9s_{e>3Li zQCl@QIp*-2>zklX4XyFUx3dT=hhLkUIfZp>#8R<#@{bQ`kDtA{!=4e9r7>JlR~f-I z;#PV;nE(t+Q#Vy zohWyt+kyKCGoz^1hr>=!&VMDAYY;M#57he_{@l3BU@c}PKj<3mg-s39u=-NgynS9; zyAq52A zQXee~SQW*?WFFIF%6>z^2njg#?A`de+!I+dxyMayS1>lHIgW9 zi`X3j($aG7=gI~4gL=0xn*lZRNheEa)}gcG*?~{S$j#1Zlni>FZbvZ zf<2Vx2u1YNx5jyQrDjn%)9HsEnKV|N(shhKh=99tb#(K zepEUWxHG7~6riqrB!*GgqO8+`_F|9elqe3r5i|KyRIKEkQi#7E8pysUO|)pLMtDB) zR3%QXk(wV};!&0$mwVJt{q4)8+-3Q}Q3iI=!Qt`~rFFhW%^%YJ9D;`xcGFK+p@Z~0 zXR|k2vo&^~6@`HVJcrc#W*#gP&511>k2#Vg;cPPdKZD;0)#0Y|@gul_19R zF4)9-J;TqUeC~!}_ur{XEGnN!;Z*5MUOAXkpVGYw@DFon3l)hbFuG1Fdr9z4SFQMs zvx#>Ib+BA5`ri3#YLL$GX4>hJMX^aAYXL-qGa#i3gVHnoM^3M(kiqXmUkmbH!1$Y>z^~kMXAr|IVlyuloO^doT34Byxj-J4tkA#X9CN)X+<^3 z9iyyAMT%&AIoR>_(&A>Dy!YetT5lph+!g)obCX)zQ86IFdHL&e@Kv5l)?{G3c#7i~ z_>4-nErP?gq%zQLEY%?L%ip<%!Ggw5L1vpgSlwQ?Qqlc4SFk-&#&9hzA z;^EfXCej>AHd_!GnDlFmKOC2MrT0pUa*s1My(Mz8WIH{jJ0{tHHf>j}oGfm7y>JDh z?I86o*eB1N(fuLIheq^dp_s5j!|Fcg=P20tP0FY1*pl8=PqT@a^;A;7I^*+m?iGvG zYUpgo-Y+CiU%M|*Z7BWFub(MncD#?vbUywbxe{GeRhnlX7XC-MywTRtvATV+^5yie ztAFlwcJ$aa_-_cksZ#4>wDxNXAF2w@aENBZ4^NEY4JQc`pLt`sq&-NYxKQsxWzqhS zk|$dXymo@&T0#^eM@4wv(W4RQtc>C_Xjm9N(2Mqp^|lKpA?i)VIo21+okkk!H8hF_kA zj$h9O;bMBIPB0x5ME>4Z-z}klU`?89&)nj?vDM7(Cj$bATqvaHyJ{~8x2(>+mn1GxHzl+ne01j))S)luQR9n%=c zpT&t?34^P9-6+5qUxk(`V&EcXPd@O@Fz!I>jq% z`R}m@PCc$_zdxBw5$Pe_pOuj5v17#X{o{I`Xy$udA*ptZS%rY{?#g_*qAFiLVETi^3=0B#O{1rfbew+ml#AXdG4B^Quv8g_#(nYPUY|SnO-%qf!H?3(JMr}E2F_l)d6UQrrk*kh3Y27K3-TwCYR}uO1nesa?RInl|*@X%qN+E%y&--QSXX(3^5ycTJ@) zyT+=dV5p0&Qj~&+1kI@x`>hYj?Jm7dh1cqfTH5VCoq5sbrACcIyR4?s&v7Jz`gsKg zKQ3S~Kqm$sI~EgHiGulla5yGZZt$X1vQ~E`rm}yC!PW19vVV2xup{ac+T|dH3^>}$ z=PiA4va=}p{`%Rllbw9t!qR|2Cw8CuuWDgF^Wk}`M46)T2&jWoVeG+Zbq{fB=X6x5 zQDnAGK9{Nd@~#njAhW06wN*1wyI81?iU(@P`K2ZyF9oIE`WfNQ2}b^dGaQyID@IM~ z81A@}ml57%4D?#iR>HvkY$LXsOc~ZxvW_nGS7q$MA(sJkArIo2`a7md^adVp$>Zw% zzK5?T5PJT`N}4p;6U_xXly>$$R#!H$#a7C^-;<(eaoxj#W#W zk|-n{HusB&r(@GjlG=}DEqzu=M=|XVTI^> z%1WgoGZPs2^`db|=m}(dz-*Meej&9v)N3isfw(U@rVy|V>>#`C zF{#kyL79C;!BN3h(kdK@U5}(|hLJ-r56ky&+dbnX306-q68O?dhN~JFb;kl}D2_5- ze-$sbof)Ubf6w)l%~Ty@+nKE$Lq(0@Yt~$tO<1&5Dmor1FS)8GYFvDZWf0k6`V-h> z*>E)Z67Al(Gr=UYFNsBkTYE)CUkDjxbVw*wWWrI^CU%ZCw@muJ_Ql=~PwrHXR7o@b zK=*?mOq`Q=*f+wGFxu$gd0jjsWF=>$+(Y5H7&rh=TfWE=^_8z^`=w?7u;h^6lQca$ zA<6HGoxCIs=!C#?J1nu}>+2yTx0@kcJuI#fPkfGfb5@Ki2fLsTj_II_*_jHbgy(}U z-<(!A;kpBa%MJ11{HCj$Kh3r5&g{S@)%?_18u_h!{^C)1X?vrcqosE3z05CtFc+Rd z+Z&~WqAXj88fh#=c+4n9Q@2Z_!&B4tlA(J-LA>W=&uFGU|L|_K0Z4_iKS4jEvWV~1 zeP|}YoFO_LQ!?hegp-cb0ylr5s_d@i>#y$eDSVQd_>BjW5q`Hb6m4gLbu}MWG=s4z zD^2l|xmYA}sY_NP8*gUB+{iH#%0AuBSxsl2y~3jsA$> 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 A(f|Me literal 0 HcmV?d00001 diff --git a/youtube-dl-gui.pyns b/youtube-dl-gui.pyns new file mode 100644 index 00000000..46d63fd1 --- /dev/null +++ b/youtube-dl-gui.pyns @@ -0,0 +1,120 @@ +# PynSource Version 1.2 +{'type':'meta', 'info1':'Lorem ipsum dolor sit amet, consectetur adipiscing elit is latin. Comments are saved.'} +{'type':'umlshape', 'id':'PipeReader', 'x':992, 'y':19, 'width':178, 'height':186, 'attrs': 'WAIT_TIME|_filedescriptor|_queue|_running', 'meths': '__init__|attach_filedescriptor|join|run'} +{'type':'umlshape', 'id':'YoutubeDLDownloader', 'x':716, 'y':100, 'width':200, 'height':474, 'attrs': 'ALREADY|ERROR|FILESIZE_ABORT|OK|STOPPED|WARNING|_proc|_return_code|_stderr_queue|_stderr_reader|data_hook|log_data|youtubedl_path', 'meths': '__init__|_create_process|_extract_info|_get_cmd|_hook_data|_is_warning|_last_data_hook|_log|_proc_is_alive|_set_returncode|close|download|stop'} +{'type':'umlshape', 'id':'Thread', 'x':1045, 'y':353, 'width':70, 'height':28, 'attrs': '', 'meths': ''} +{'type':'umlshape', 'id':'object', 'x':416, 'y':313, 'width':70, 'height':28, 'attrs': '', 'meths': ''} +{'type':'umlshape', 'id':'subprocess.Popen', 'x':777, 'y':19, 'width':170, 'height':28, 'attrs': '', 'meths': ''} +{'type':'umlshape', 'id':'Queue', 'x':692, 'y':20, 'width':60, 'height':28, 'attrs': '', 'meths': ''} +{'type':'umlshape', 'id':'DownloadItem', 'x':201, 'y':659, 'width':186, 'height':410, 'attrs': 'ACTIVE_STAGES|COMPLETED_STAGES|ERROR_STAGES|STAGES|_stage|default_values|extensions|filenames|filesizes|object_id|options|path|playlist_index_changed|progress_stats|stage|url', 'meths': '__eq__|__init__|_set_stage|get_files|reset|update_stats'} +{'type':'umlshape', 'id':'DownloadList', 'x':430, 'y':663, 'width':130, 'height':314, 'attrs': '_items_dict|_items_list', 'meths': '__init__|__len__|_swap|change_stage|clear|fetch_next|get_item|get_items|has_item|index|insert|move_down|move_up|remove'} +{'type':'umlshape', 'id':'DownloadManager', 'x':1292, 'y':192, 'width':160, 'height':410, 'attrs': 'WAIT_TIME|_running|_successful|_time_it_took|_workers|download_list|log_manager|opt_manager|parent|successful|time_it_took', 'meths': '__init__|_check_youtubedl|_get_worker|_jobs_done|_talk_to_gui|_youtubedl_path|active|add_url|run|send_to_worker|stop_downloads'} +{'type':'umlshape', 'id':'Worker', 'x':826, 'y':662, 'width':130, 'height':458, 'attrs': 'WAIT_TIME|_data|_downloader|_options|_options_parser|_running|_successful|_wait_for_reply|log_manager|opt_manager|successful|worker|worker_count', 'meths': '__init__|_data_hook|_log_data|_reset|_talk_to_gui|available|close|download|has_index|run|stop_download|update_data'} +{'type':'umlshape', 'id':'time.time', 'x':1504, 'y':384, 'width':100, 'height':28, 'attrs': '', 'meths': ''} +{'type':'umlshape', 'id':'OptionsParser', 'x':604, 'y':666, 'width':154, 'height':154, 'attrs': '_ydl_options', 'meths': '__init__|_build_filesizes|_build_savepath|_build_videoformat|parse'} +{'type':'umlshape', 'id':'UpdateThread', 'x':1021, 'y':664, 'width':178, 'height':218, 'attrs': 'DOWNLOAD_TIMEOUT|GITHUB_API|LATEST_YOUTUBE_DL|LATEST_YOUTUBE_DL_API|download_path|quiet', 'meths': '__init__|_talk_to_gui|get_latest_sourcefile|run'} +{'type':'umlshape', 'id':'LogManager', 'x':40, 'y':161, 'width':110, 'height':250, 'attrs': 'LOG_FILENAME|MAX_LOGSIZE|_encoding|add_time|config_path|handler|log_file|logger', 'meths': '__init__|clear|log|log_size'} +{'type':'umlshape', 'id':'os.path.join', 'x':181, 'y':227, 'width':130, 'height':28, 'attrs': '', 'meths': ''} +{'type':'umlshape', 'id':'RotatingFileHandler', 'x':20, 'y':15, 'width':200, 'height':28, 'attrs': '', 'meths': ''} +{'type':'umlshape', 'id':'logging.getLogger', 'x':178, 'y':74, 'width':180, 'height':28, 'attrs': '', 'meths': ''} +{'type':'umlshape', 'id':'MainFrame', 'x':781, 'y':1781, 'width':234, 'height':1930, 'attrs': 'ABOUT_LABEL|ADD_LABEL|CHOOSE_DIRECTORY|CLOSED_MSG|CLOSING_MSG|DELETE_LABEL|DL_COMPLETED_MSG|DOWNLOAD_ACTIVE|DOWNLOAD_LIST_LABEL|DOWNLOAD_STARTED|DOWN_LABEL|ETA_LABEL|EXTENSION_LABEL|FRAMES_MIN_SIZE|INFO_LABEL|OPEN_DIR_ERR|OPTIONS_LABEL|PAUSE_LABEL|PERCENT_LABEL|PLAY_LABEL|PROVIDE_URL_MSG|RELOAD_LABEL|SHUTDOWN_ERR|SHUTDOWN_MSG|SIZE_LABEL|SPEED_LABEL|START_LABEL|STATUSLIST_COLUMNS|STATUS_LABEL|STOP_LABEL|SUCC_REPORT_MSG|UPDATE_ACTIVE|UPDATE_ERR_MSG|UPDATE_LABEL|UPDATE_SUCC_MSG|UPDATING_MSG|UP_LABEL|URLS_LABEL|URL_REPORT_MSG|VIDEO_LABEL|VIEWLOG_LABEL|WARNING_LABEL|WELCOME_MSG|_app_timer|_bitmaps|_buttons|_download_list|_download_text|_folder_icon|_options_frame|_options_parser|_panel|_path_combobox|_pixmaps_path|_settings_button|_settings_menu|_status_bar|_status_list|_statuslist_menu|_url_list|_url_text|_videoformat_combobox|app_icon|download_manager|log_manager|opt_manager|update_thread', 'meths': '__init__|_after_download|_create_bitmap_button|_create_menu_item|_create_popup|_create_static_bitmap|_create_statictext|_create_textctrl|_download_manager_handler|_download_worker_handler|_get_urls|_on_about|_on_add|_on_arrow_down|_on_arrow_up|_on_close|_on_delete|_on_getcmd|_on_geturl|_on_open_dest|_on_open_path|_on_options|_on_pause|_on_play|_on_reenter|_on_reload|_on_savepath|_on_settings|_on_start|_on_statuslist_right_click|_on_timer|_on_update|_on_urllist_edit|_on_viewlog|_paste_from_clipboard|_print_stats|_reset_widgets|_set_layout|_set_publisher|_start_download|_status_bar_write|_update_handler|_update_pause_button|_update_savepath|_update_videoformat|_update_videoformat_combobox|_update_youtubedl|close|reset|win_ctrla_eventhandler'} +{'type':'umlshape', 'id':'ListCtrl', 'x':207, 'y':3248, 'width':147, 'height':378, 'attrs': '_list_index|_map_id|_url_list|columns', 'meths': 'GetItemData|__init__|_move_item|_set_columns|_update_from_item|bind_item|clear|deselect_all|get_all_selected|get_next_selected|get_selected|has_url|is_empty|move_item_down|move_item_up|remove_row'} +{'type':'umlshape', 'id':'ExtComboBox', 'x':603, 'y':3281, 'width':120, 'height':138, 'attrs': 'max_items', 'meths': 'Append|LoadMultiple|SetValue|__init__'} +{'type':'umlshape', 'id':'DoubleStageButton', 'x':1469, 'y':2097, 'width':180, 'height':186, 'attrs': '_stage|bitmap_pos|bitmaps|labels', 'meths': '__init__|_set_layout|change_stage|set_stage'} +{'type':'umlshape', 'id':'ButtonsChoiceDialog', 'x':1280, 'y':3008, 'width':200, 'height':122, 'attrs': 'BORDER|STYLE', 'meths': '__init__|_on_close'} +{'type':'umlshape', 'id':'ButtonsGroup', 'x':448, 'y':21, 'width':130, 'height':234, 'attrs': 'HEIGHT|WIDTH|_buttons_list|_squared', 'meths': '__init__|add|bind_event|create_sizer|disable_all|enable_all|set_size'} +{'type':'umlshape', 'id':'ShutdownDialog', 'x':1081, 'y':3068, 'width':150, 'height':234, 'attrs': 'BORDER|STYLE|TIMER_INTERVAL|message|msg_text|timeout|timer', 'meths': 'Destroy|__init__|_get_message|_on_timer'} +{'type':'umlshape', 'id':'wx.Dialog', 'x':1198, 'y':2830, 'width':100, 'height':28, 'attrs': '', 'meths': ''} +{'type':'umlshape', 'id':'ListCtrlAutoWidthMixin', 'x':192, 'y':3155, 'width':230, 'height':28, 'attrs': '', 'meths': ''} +{'type':'umlshape', 'id':'wx.Frame', 'x':1210, 'y':1428, 'width':90, 'height':28, 'attrs': '', 'meths': ''} +{'type':'umlshape', 'id':'wx.ComboBox', 'x':603, 'y':3143, 'width':120, 'height':28, 'attrs': '', 'meths': ''} +{'type':'umlshape', 'id':'wx.Button', 'x':1505, 'y':2037, 'width':100, 'height':28, 'attrs': '', 'meths': ''} +{'type':'umlshape', 'id':'wx.ListCtrl', 'x':46, 'y':3152, 'width':120, 'height':28, 'attrs': '', 'meths': ''} +{'type':'umlshape', 'id':'wx.Icon', 'x':1078, 'y':2149, 'width':80, 'height':28, 'attrs': '', 'meths': ''} +{'type':'umlshape', 'id':'wx.Bitmap', 'x':1045, 'y':2743, 'width':100, 'height':28, 'attrs': '', 'meths': ''} +{'type':'umlshape', 'id':'wx.Timer', 'x':1048, 'y':2910, 'width':90, 'height':28, 'attrs': '', 'meths': ''} +{'type':'umlshape', 'id':'wx.Panel', 'x':852, 'y':1484, 'width':90, 'height':28, 'attrs': '', 'meths': ''} +{'type':'umlshape', 'id':'wx.StaticText', 'x':1287, 'y':3194, 'width':140, 'height':28, 'attrs': '', 'meths': ''} +{'type':'umlshape', 'id':'CustomComboBox', 'x':1657, 'y':1355, 'width':170, 'height':618, 'attrs': 'CB_READONLY|NAME|button|listbox|textctrl', 'meths': 'Append|AppendItems|Clear|Delete|Dismiss|FindString|GetCount|GetCurrentSelection|GetInsertionPoint|GetSelection|GetString|GetStringSelection|GetTextSelection|GetValue|IsListEmpty|IsTextEmpty|Popup|SetSelection|SetString|SetStringSelection|SetTextSelection|SetValue|__init__|_calc_popup_position|_calc_popup_size|_on_button|_propagate|add_header|add_item|add_items'} +{'type':'umlshape', 'id':'OptionsFrame', 'x':1222, 'y':1750, 'width':138, 'height':378, 'attrs': 'FRAMES_MIN_SIZE|FRAME_TITLE|_was_shown|app_icon|close_button|log_manager|notebook|opt_manager|panel|reset_button|separator_line|tabs', 'meths': 'Show|__init__|_on_close|_on_reset|_set_layout|load_all_options|reset|save_all_options'} +{'type':'umlshape', 'id':'TabPanel', 'x':466, 'y':1926, 'width':162, 'height':394, 'attrs': 'BUTTONS_SIZE|CHECKBOX_SIZE|CHECKLISTBOX_SIZE|LISTBOX_SIZE|SPINCTRL_SIZE|TEXTCTRL_SIZE|app_icon|log_manager|opt_manager|reset_handler', 'meths': '__init__|crt_bitmap_combobox|crt_button|crt_checkbox|crt_checklistbox|crt_combobox|crt_listbox|crt_spinctrl|crt_staticbox|crt_statictext|crt_textctrl'} +{'type':'umlshape', 'id':'GeneralTab', 'x':26, 'y':2451, 'width':250, 'height':522, 'attrs': 'BUTTONS_SIZE|LOCALE_NAMES|OUTPUT_TEMPLATES|confirm_deletion_checkbox|confirm_exit_checkbox|custom_format_menu|filename_ascii_checkbox|filename_custom_format|filename_custom_format_button|filename_format_combobox|filename_format_label|filename_opts_label|language_combobox|language_label|more_opts_label|opt_manager|show_completion_popup_checkbox|shutdown_checkbox|sudo_textctrl', 'meths': '__init__|_build_custom_format_menu|_on_filename|_on_format|_on_language|_on_shutdown|_on_template|_set_layout|load_options|save_options'} +{'type':'umlshape', 'id':'FormatsTab', 'x':307, 'y':2553, 'width':218, 'height':330, 'attrs': 'AUDIO_QUALITY|add_metadata_checkbox|audio_formats_checklistbox|audio_formats_label|audio_quality_combobox|audio_quality_label|embed_thumbnail_checkbox|extract_audio_checkbox|keep_video_checkbox|opt_manager|post_proc_opts_label|video_formats_checklistbox|video_formats_label', 'meths': '__init__|_set_layout|load_options|save_options'} +{'type':'umlshape', 'id':'DownloadsTab', 'x':34, 'y':1816, 'width':250, 'height':538, 'attrs': 'FILESIZES|SUBS_CHOICES|SUBS_LANG|embed_subs_checkbox|filesize_box|filesize_max_label|filesize_max_sizeunit_combobox|filesize_max_spinctrl|filesize_min_label|filesize_min_sizeunit_combobox|filesize_min_spinctrl|opt_manager|playlist_box|playlist_max_label|playlist_max_spinctrl|playlist_start_label|playlist_start_spinctrl|playlist_stop_label|playlist_stop_spinctrl|subtitles_combobox|subtitles_label|subtitles_lang_listbox|subtitles_opts_label', 'meths': '__init__|_build_filesize_sizer|_build_playlist_sizer|_on_subtitles|_set_layout|load_options|save_options'} +{'type':'umlshape', 'id':'AdvancedTab', 'x':36, 'y':1262, 'width':162, 'height':522, 'attrs': 'TEXTCTRL_SIZE|auth_label|clear_log_button|enable_log_checkbox|logging_label|network_label|opt_manager|password_label|password_textctrl|proxy_label|proxy_textctrl|referer_label|referer_textctrl|retries_label|retries_spinctrl|useragent_label|useragent_textctrl|username_label|username_textctrl|video_pass_label|video_pass_textctrl|view_log_button', 'meths': '__init__|_on_clear|_on_enable_log|_on_view|_set_layout|load_options|save_options'} +{'type':'umlshape', 'id':'ExtraTab', 'x':542, 'y':2553, 'width':210, 'height':266, 'attrs': 'cmdline_args_label|cmdline_args_textctrl|extra_opts_label|ignore_config_checkbox|ignore_errors_checkbox|native_hls_checkbox|no_mtime_checkbox|opt_manager|youtube_dl_debug_checkbox', 'meths': '__init__|_set_layout|load_options|save_options'} +{'type':'umlshape', 'id':'LogGUI', 'x':1236, 'y':1187, 'width':90, 'height':138, 'attrs': 'FRAME_SIZE|TITLE|_text_area', 'meths': '__init__|load'} +{'type':'umlshape', 'id':'wx.TextCtrl', 'x':1421, 'y':1243, 'width':120, 'height':28, 'attrs': '', 'meths': ''} +{'type':'umlshape', 'id':'wx.Notebook', 'x':1421, 'y':1854, 'width':120, 'height':28, 'attrs': '', 'meths': ''} +{'type':'umlshape', 'id':'wx.StaticLine', 'x':1405, 'y':1760, 'width':140, 'height':28, 'attrs': '', 'meths': ''} +{'type':'umlshape', 'id':'OptionsManager', 'x':15, 'y':662, 'width':162, 'height':234, 'attrs': 'SENSITIVE_KEYS|SETTINGS_FILENAME|config_path|options|settings_file', 'meths': '__init__|_get_options|_settings_are_valid|load_default|load_from_file|save_to_file'} +{'type':'umlshape', 'id':'OptionHolder', 'x':14, 'y':440, 'width':154, 'height':170, 'attrs': 'default_value|flag|name|requirements', 'meths': '__init__|check_requirements|is_boolean'} +{'type':'umlshape', 'id':'ListBoxWithHeaders', 'x':1441, 'y':2456, 'width':210, 'height':410, 'attrs': 'EVENTS|NAME|TEXT_PREFIX|__headers', 'meths': 'AppendItems|Clear|Delete|FindString|GetString|GetStringSelection|InsertItems|SetSelection|SetString|SetStringSelection|__init__|_add_prefix|_disable_header_selection|_on_listbox|_remove_prefix|add_header|add_item|add_items'} +{'type':'umlshape', 'id':'ListBoxPopup', 'x':1694, 'y':2453, 'width':130, 'height':282, 'attrs': 'EVENTS_TABLE|__listbox|curitem|value', 'meths': 'Create|GetAdjustedSize|GetControl|GetStringValue|Init|OnDismiss|Popup|__init__|_on_left_down|_on_motion'} +{'type':'umlshape', 'id':'wx.PopupTransientWindow', 'x':1767, 'y':2394, 'width':240, 'height':28, 'attrs': '', 'meths': ''} +{'type':'umlshape', 'id':'wx.ListBox', 'x':1485, 'y':2395, 'width':110, 'height':28, 'attrs': '', 'meths': ''} +{'type':'edge', 'id':'PipeReader_to_Thread', 'source':'PipeReader', 'target':'Thread', 'uml_edge_type': 'generalisation'} +{'type':'edge', 'id':'YoutubeDLDownloader_to_object', 'source':'YoutubeDLDownloader', 'target':'object', 'uml_edge_type': 'generalisation'} +{'type':'edge', 'id':'subprocess.Popen_to_YoutubeDLDownloader', 'source':'subprocess.Popen', 'target':'YoutubeDLDownloader', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'PipeReader_to_YoutubeDLDownloader', 'source':'PipeReader', 'target':'YoutubeDLDownloader', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'Queue_to_YoutubeDLDownloader', 'source':'Queue', 'target':'YoutubeDLDownloader', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'Worker_to_Thread', 'source':'Worker', 'target':'Thread', 'uml_edge_type': 'generalisation'} +{'type':'edge', 'id':'DownloadManager_to_Thread', 'source':'DownloadManager', 'target':'Thread', 'uml_edge_type': 'generalisation'} +{'type':'edge', 'id':'DownloadItem_to_object', 'source':'DownloadItem', 'target':'object', 'uml_edge_type': 'generalisation'} +{'type':'edge', 'id':'DownloadList_to_object', 'source':'DownloadList', 'target':'object', 'uml_edge_type': 'generalisation'} +{'type':'edge', 'id':'time.time_to_DownloadManager', 'source':'time.time', 'target':'DownloadManager', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'Worker_to_Worker', 'source':'Worker', 'target':'Worker', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'OptionsParser_to_Worker', 'source':'OptionsParser', 'target':'Worker', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'UpdateThread_to_DownloadManager', 'source':'UpdateThread', 'target':'DownloadManager', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'YoutubeDLDownloader_to_Worker', 'source':'YoutubeDLDownloader', 'target':'Worker', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'LogManager_to_object', 'source':'LogManager', 'target':'object', 'uml_edge_type': 'generalisation'} +{'type':'edge', 'id':'os.path.join_to_LogManager', 'source':'os.path.join', 'target':'LogManager', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'RotatingFileHandler_to_LogManager', 'source':'RotatingFileHandler', 'target':'LogManager', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'logging.getLogger_to_LogManager', 'source':'logging.getLogger', 'target':'LogManager', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'ShutdownDialog_to_wx.Dialog', 'source':'ShutdownDialog', 'target':'wx.Dialog', 'uml_edge_type': 'generalisation'} +{'type':'edge', 'id':'ListCtrl_to_ListCtrlAutoWidthMixin', 'source':'ListCtrl', 'target':'ListCtrlAutoWidthMixin', 'uml_edge_type': 'generalisation'} +{'type':'edge', 'id':'ButtonsGroup_to_object', 'source':'ButtonsGroup', 'target':'object', 'uml_edge_type': 'generalisation'} +{'type':'edge', 'id':'MainFrame_to_wx.Frame', 'source':'MainFrame', 'target':'wx.Frame', 'uml_edge_type': 'generalisation'} +{'type':'edge', 'id':'ExtComboBox_to_wx.ComboBox', 'source':'ExtComboBox', 'target':'wx.ComboBox', 'uml_edge_type': 'generalisation'} +{'type':'edge', 'id':'ButtonsChoiceDialog_to_wx.Dialog', 'source':'ButtonsChoiceDialog', 'target':'wx.Dialog', 'uml_edge_type': 'generalisation'} +{'type':'edge', 'id':'DoubleStageButton_to_wx.Button', 'source':'DoubleStageButton', 'target':'wx.Button', 'uml_edge_type': 'generalisation'} +{'type':'edge', 'id':'ListCtrl_to_wx.ListCtrl', 'source':'ListCtrl', 'target':'wx.ListCtrl', 'uml_edge_type': 'generalisation'} +{'type':'edge', 'id':'wx.Icon_to_MainFrame', 'source':'wx.Icon', 'target':'MainFrame', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'wx.Bitmap_to_MainFrame', 'source':'wx.Bitmap', 'target':'MainFrame', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'wx.Timer_to_MainFrame', 'source':'wx.Timer', 'target':'MainFrame', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'OptionsParser_to_MainFrame', 'source':'OptionsParser', 'target':'MainFrame', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'ExtComboBox_to_MainFrame', 'source':'ExtComboBox', 'target':'MainFrame', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'wx.Panel_to_MainFrame', 'source':'wx.Panel', 'target':'MainFrame', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'wx.StaticText_to_ShutdownDialog', 'source':'wx.StaticText', 'target':'ShutdownDialog', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'ListCtrl_to_MainFrame', 'source':'ListCtrl', 'target':'MainFrame', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'UpdateThread_to_MainFrame', 'source':'UpdateThread', 'target':'MainFrame', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'wx.Timer_to_ShutdownDialog', 'source':'wx.Timer', 'target':'ShutdownDialog', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'DownloadManager_to_MainFrame', 'source':'DownloadManager', 'target':'MainFrame', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'CustomComboBox_to_MainFrame', 'source':'CustomComboBox', 'target':'MainFrame', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'OptionsFrame_to_MainFrame', 'source':'OptionsFrame', 'target':'MainFrame', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'DownloadList_to_MainFrame', 'source':'DownloadList', 'target':'MainFrame', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'FormatsTab_to_TabPanel', 'source':'FormatsTab', 'target':'TabPanel', 'uml_edge_type': 'generalisation'} +{'type':'edge', 'id':'AdvancedTab_to_TabPanel', 'source':'AdvancedTab', 'target':'TabPanel', 'uml_edge_type': 'generalisation'} +{'type':'edge', 'id':'TabPanel_to_wx.Panel', 'source':'TabPanel', 'target':'wx.Panel', 'uml_edge_type': 'generalisation'} +{'type':'edge', 'id':'OptionsFrame_to_wx.Frame', 'source':'OptionsFrame', 'target':'wx.Frame', 'uml_edge_type': 'generalisation'} +{'type':'edge', 'id':'GeneralTab_to_TabPanel', 'source':'GeneralTab', 'target':'TabPanel', 'uml_edge_type': 'generalisation'} +{'type':'edge', 'id':'DownloadsTab_to_TabPanel', 'source':'DownloadsTab', 'target':'TabPanel', 'uml_edge_type': 'generalisation'} +{'type':'edge', 'id':'LogGUI_to_wx.Frame', 'source':'LogGUI', 'target':'wx.Frame', 'uml_edge_type': 'generalisation'} +{'type':'edge', 'id':'ExtraTab_to_TabPanel', 'source':'ExtraTab', 'target':'TabPanel', 'uml_edge_type': 'generalisation'} +{'type':'edge', 'id':'wx.Button_to_OptionsFrame', 'source':'wx.Button', 'target':'OptionsFrame', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'wx.TextCtrl_to_LogGUI', 'source':'wx.TextCtrl', 'target':'LogGUI', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'wx.Notebook_to_OptionsFrame', 'source':'wx.Notebook', 'target':'OptionsFrame', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'wx.Panel_to_OptionsFrame', 'source':'wx.Panel', 'target':'OptionsFrame', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'wx.Icon_to_OptionsFrame', 'source':'wx.Icon', 'target':'OptionsFrame', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'wx.StaticLine_to_OptionsFrame', 'source':'wx.StaticLine', 'target':'OptionsFrame', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'OptionsManager_to_object', 'source':'OptionsManager', 'target':'object', 'uml_edge_type': 'generalisation'} +{'type':'edge', 'id':'OptionsParser_to_object', 'source':'OptionsParser', 'target':'object', 'uml_edge_type': 'generalisation'} +{'type':'edge', 'id':'OptionHolder_to_object', 'source':'OptionHolder', 'target':'object', 'uml_edge_type': 'generalisation'} +{'type':'edge', 'id':'UpdateThread_to_Thread', 'source':'UpdateThread', 'target':'Thread', 'uml_edge_type': 'generalisation'} +{'type':'edge', 'id':'CustomComboBox_to_wx.Panel', 'source':'CustomComboBox', 'target':'wx.Panel', 'uml_edge_type': 'generalisation'} +{'type':'edge', 'id':'ListBoxPopup_to_wx.PopupTransientWindow', 'source':'ListBoxPopup', 'target':'wx.PopupTransientWindow', 'uml_edge_type': 'generalisation'} +{'type':'edge', 'id':'ListBoxWithHeaders_to_wx.ListBox', 'source':'ListBoxWithHeaders', 'target':'wx.ListBox', 'uml_edge_type': 'generalisation'} +{'type':'edge', 'id':'ListBoxWithHeaders_to_ListBoxPopup', 'source':'ListBoxWithHeaders', 'target':'ListBoxPopup', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'ListBoxPopup_to_CustomComboBox', 'source':'ListBoxPopup', 'target':'CustomComboBox', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'wx.Button_to_CustomComboBox', 'source':'wx.Button', 'target':'CustomComboBox', 'uml_edge_type': 'composition'} +{'type':'edge', 'id':'wx.TextCtrl_to_CustomComboBox', 'source':'wx.TextCtrl', 'target':'CustomComboBox', 'uml_edge_type': 'composition'} From 4f95436a76c433a826b61a59bf3cf675584167ce Mon Sep 17 00:00:00 2001 From: oleksis Date: Tue, 26 Jan 2021 01:47:16 -0500 Subject: [PATCH 29/34] Update Class Diagram UML. Add UML .png --- docs/youtube-dl-gui_uml.pdf | Bin 81337 -> 81901 bytes docs/youtube-dl-gui_uml.png | Bin 0 -> 122014 bytes youtube-dl-gui.pyns | 72 ++++++++++++++++++------------------ 3 files changed, 36 insertions(+), 36 deletions(-) create mode 100644 docs/youtube-dl-gui_uml.png diff --git a/docs/youtube-dl-gui_uml.pdf b/docs/youtube-dl-gui_uml.pdf index a917c1b3b1a915cb4dcf1c15ab403912ff7abbe2..2bf055c943af39b98daecff4f7bf6fc6699b31e7 100644 GIT binary patch delta 19905 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 delta 19336 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 ms$AkH627Kpg`n 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 diff --git a/docs/youtube-dl-gui_uml.png b/docs/youtube-dl-gui_uml.png new file mode 100644 index 0000000000000000000000000000000000000000..b10718e3f879aacc003e1d77899c539e0ddf271f GIT binary patch literal 122014 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 TQMQ@ Date: Sat, 6 Feb 2021 11:36:41 -0500 Subject: [PATCH 30/34] Close properly the file descriptors --- ChangeLog | 1 + README.md | 12 ++++++------ youtube_dl_gui/downloaders.py | 8 ++++++-- youtube_dl_gui/version.py | 2 +- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/ChangeLog b/ChangeLog index 19bacbc1..65e15b61 100644 --- a/ChangeLog +++ b/ChangeLog @@ -22,6 +22,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) - Do not interpret output [debug] as an error in the Logs - Get all Logs except "ffmpeg version" - Do not send messages to the GUI if the app does not exist (Destroy) +- Close properly the file descriptors ### Changed - Migration to Python 3.* diff --git a/README.md b/README.md index ad6a20c0..618c0582 100644 --- a/README.md +++ b/README.md @@ -19,17 +19,17 @@ A cross platform front-end GUI of the popular [youtube-dl](https://rg3.github.io * [GNU gettext](https://www.gnu.org/software/gettext/) ## Downloads -* [SHA2-256SUMS](https://github.com/oleksis/youtube-dl-gui/releases/download/v1.1.3/SHA2-256SUMS) -* [youtube-dlg](https://github.com/oleksis/youtube-dl-gui/releases/download/v1.1.3/youtube-dlg) -* [youtube-dlg.exe](https://github.com/oleksis/youtube-dl-gui/releases/download/v1.1.3/youtube-dlg.exe) -* [Source (.zip)](https://github.com/oleksis/youtube-dl-gui/archive/v1.1.3.zip) -* [Source (.tar.gz)](https://github.com/oleksis/youtube-dl-gui/archive/v1.1.3.tar.gz) +* [SHA2-256SUMS](https://github.com/oleksis/youtube-dl-gui/releases/download/v1.1.4/SHA2-256SUMS) +* [youtube-dlg](https://github.com/oleksis/youtube-dl-gui/releases/download/v1.1.4/youtube-dlg) +* [youtube-dlg.exe](https://github.com/oleksis/youtube-dl-gui/releases/download/v1.1.4/youtube-dlg.exe) +* [Source (.zip)](https://github.com/oleksis/youtube-dl-gui/archive/v1.1.4.zip) +* [Source (.tar.gz)](https://github.com/oleksis/youtube-dl-gui/archive/v1.1.4.tar.gz) ## Installation ### Install From Source * Download & extract the source -* Change directory into *youtube-dl-gui-1.1.3* +* Change directory into *youtube-dl-gui-1.1.4* * Create virtual environment ``` python3 -m venv env diff --git a/youtube_dl_gui/downloaders.py b/youtube_dl_gui/downloaders.py index 710cb6ed..251b3320 100644 --- a/youtube_dl_gui/downloaders.py +++ b/youtube_dl_gui/downloaders.py @@ -53,7 +53,7 @@ def run(self): ignore_line = False while self._running: - if self._filedescriptor is not None: + if self._filedescriptor is not None and not self._filedescriptor.closed: pipedata = self._filedescriptor.read() pipedata = bytes(pipedata).decode(encoding=get_encoding(), errors='ignore') for line in pipedata.splitlines(): @@ -160,7 +160,9 @@ def download(self, url, options): self._stderr_reader.attach_filedescriptor(self._proc.stderr) while self._proc_is_alive(): - stdout = self._proc.stdout.readline().rstrip() + stdout = "" + if not self._proc.stdout.closed: + stdout = self._proc.stdout.readline().rstrip() if stdout: data_dict = extract_data(stdout) @@ -197,6 +199,8 @@ def download(self, url, options): def stop(self): """Stop the download process and set return code to STOPPED. """ if self._proc_is_alive(): + self._proc.stdout.close() + self._proc.stderr.close() if os.name == 'nt': # os.killpg is not available on Windows diff --git a/youtube_dl_gui/version.py b/youtube_dl_gui/version.py index 5220f471..04c06328 100644 --- a/youtube_dl_gui/version.py +++ b/youtube_dl_gui/version.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- -__version__ = '1.1.3' +__version__ = '1.1.4' From 1c22845ec3c473a813d7cf5a2a8654df230702a8 Mon Sep 17 00:00:00 2001 From: oleksis Date: Mon, 1 Mar 2021 11:35:13 -0500 Subject: [PATCH 31/34] Change hashFiles SHA256 function on Github Actions --- .github/workflows/release.yml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d010975d..604d9691 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -56,9 +56,7 @@ jobs: asset_content_type: application/octet-stream - name: Get SHA2-256SUMS for youtube-dlg.exe id: sha2_file - env: - SHA2: ${{ hashFiles('dist/youtube-dlg.exe') }} - run: echo "::set-output name=sha2_windows::${env:SHA2}" + run: echo "::set-output name=sha2_windows::$(Get-FileHash dist\youtube-dlg.exe -Algorithm SHA256).Hash.ToLower()" build_ubuntu_bdist: needs: build_windows_exe runs-on: ubuntu-latest @@ -85,16 +83,14 @@ jobs: asset_content_type: application/octet-stream - name: Get SHA2-256SUMS for youtube-dlg id: sha2_file_binary - env: - SHA2: ${{ hashFiles('dist/youtube-dlg') }} - run: echo "::set-output name=sha2_linux::$SHA2" + run: echo "::set-output name=sha2_linux::$(sha256sum dist/youtube-dlg | awk '{print $1}')" - name: Make SHA2-256SUMS file env: SHA2_WINDOWS: ${{ needs.build_windows_exe.outputs.sha2_windows }} SHA2_LINUX_BINARY: ${{ steps.sha2_file_binary.outputs.sha2_linux }} run: | - echo "$SHA2_WINDOWS youtube-dlg.exe" > SHA2-256SUMS - echo "$SHA2_LINUX_BINARY youtube-dlg" >> SHA2-256SUMS + echo "$SHA2_WINDOWS youtube-dlg.exe" > SHA2-256SUMS + echo "$SHA2_LINUX_BINARY youtube-dlg" >> SHA2-256SUMS - name: Upload SHA2-256SUMS to Release id: upload-release-sha2 uses: actions/upload-release-asset@v1 From ca1838c9bfd098e2e80e4a6fe9c595837b3cae75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oleksis=20Fraga=20Men=C3=A9ndez?= <44526468+oleksis@users.noreply.github.com> Date: Tue, 6 Apr 2021 14:35:09 -0400 Subject: [PATCH 32/34] Create FUNDING.yml --- .github/FUNDING.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..17a0e109 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# These are supported funding model platforms + +github: oleksis +custom: "https://www.buymeacoffee.com/oleksis" From 6eea1bbf4e09479573b9aed66b94f00f937d1a2a Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Wed, 7 Apr 2021 15:14:31 +0300 Subject: [PATCH 33/34] Adding sq translation --- .../sq_SQ/LC_MESSAGES/youtube_dl_gui.po | 602 ++++++++++++++++++ youtube_dl_gui/optionsframe.py | 1 + 2 files changed, 603 insertions(+) create mode 100644 youtube_dl_gui/locale/sq_SQ/LC_MESSAGES/youtube_dl_gui.po diff --git a/youtube_dl_gui/locale/sq_SQ/LC_MESSAGES/youtube_dl_gui.po b/youtube_dl_gui/locale/sq_SQ/LC_MESSAGES/youtube_dl_gui.po new file mode 100644 index 00000000..8958d862 --- /dev/null +++ b/youtube_dl_gui/locale/sq_SQ/LC_MESSAGES/youtube_dl_gui.po @@ -0,0 +1,602 @@ +# Youtube-dlG localization file. +# FIRST AUTHOR: Sotiris Papadopoulos , 2015. +# +msgid "" +msgstr "" +"Project-Id-Version: youtube-dlg 0.4\n" +"POT-Creation-Date: 2018-01-15 16:42+EET\n" +"PO-Revision-Date: 2021-04-07 15:04+0300\n" +"Last-Translator: Besnik Bleta \n" +"Language-Team: sq\n" +"Language: sq\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: pygettext.py 1.5\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 2.4.2\n" + +#: youtube_dl_gui/__init__.py:91 +msgid "Error" +msgstr "Gabim" + +#: youtube_dl_gui/__init__.py:91 +msgid "Failed to locate youtube-dl and updates are disabled" +msgstr "S’u arrit të lokalizohet youtube-dl dhe përditësimet janë çaktivizuar" + +#: youtube_dl_gui/formats.py:9 youtube_dl_gui/formats.py:109 +msgid "ID" +msgstr "ID" + +#: youtube_dl_gui/formats.py:10 youtube_dl_gui/formats.py:110 +#: youtube_dl_gui/mainframe.py:140 +msgid "Title" +msgstr "Titull" + +#: youtube_dl_gui/formats.py:11 youtube_dl_gui/formats.py:111 +msgid "Title + ID" +msgstr "Titull + ID" + +#: youtube_dl_gui/formats.py:12 youtube_dl_gui/formats.py:112 +msgid "Title + Quality" +msgstr "Titull + Cilësi" + +#: youtube_dl_gui/formats.py:13 youtube_dl_gui/formats.py:113 +msgid "Title + ID + Quality" +msgstr "Titull + ID + Cilësi" + +#: youtube_dl_gui/formats.py:14 youtube_dl_gui/formats.py:114 +msgid "Custom" +msgstr "Vetjake" + +#: youtube_dl_gui/formats.py:19 youtube_dl_gui/formats.py:119 +#: youtube_dl_gui/mainframe.py:503 youtube_dl_gui/mainframe.py:506 +msgid "default" +msgstr "parazgjedhje" + +#: youtube_dl_gui/mainframe.py:97 +msgid "Enter URLs below" +msgstr "Jepini URL-të më poshtë" + +#: youtube_dl_gui/mainframe.py:98 +msgid "Update" +msgstr "Përditësoje" + +#: youtube_dl_gui/mainframe.py:99 youtube_dl_gui/optionsframe.py:41 +msgid "Options" +msgstr "Mundësi" + +#: youtube_dl_gui/mainframe.py:100 youtube_dl_gui/optionsframe.py:584 +msgid "Stop" +msgstr "Ndale" + +#: youtube_dl_gui/mainframe.py:101 +msgid "Info" +msgstr "Info" + +#: youtube_dl_gui/mainframe.py:102 +msgid "Welcome" +msgstr "Mirë se vini" + +#: youtube_dl_gui/mainframe.py:103 +msgid "Warning" +msgstr "Sinjalizim" + +#: youtube_dl_gui/mainframe.py:105 +msgid "Add" +msgstr "Shto" + +#: youtube_dl_gui/mainframe.py:106 +msgid "Download list" +msgstr "Listë shkarkimesh" + +#: youtube_dl_gui/mainframe.py:107 youtube_dl_gui/mainframe.py:516 +#: youtube_dl_gui/mainframe.py:534 +msgid "Delete" +msgstr "Fshije" + +#: youtube_dl_gui/mainframe.py:108 +msgid "Play" +msgstr "Luaje" + +#: youtube_dl_gui/mainframe.py:109 +msgid "Up" +msgstr "Sipër" + +#: youtube_dl_gui/mainframe.py:110 +msgid "Down" +msgstr "Poshtë" + +#: youtube_dl_gui/mainframe.py:111 +msgid "Reload" +msgstr "Ringarkoje" + +#: youtube_dl_gui/mainframe.py:112 youtube_dl_gui/mainframe.py:448 +#: youtube_dl_gui/mainframe.py:649 +msgid "Pause" +msgstr "Pauzë" + +#: youtube_dl_gui/mainframe.py:113 youtube_dl_gui/mainframe.py:865 +#: youtube_dl_gui/mainframe.py:866 youtube_dl_gui/optionsframe.py:582 +msgid "Start" +msgstr "Nise" + +#: youtube_dl_gui/mainframe.py:114 +msgid "About" +msgstr "Mbi" + +#: youtube_dl_gui/mainframe.py:115 +msgid "View Log" +msgstr "Shihni Regjistër" + +#: youtube_dl_gui/mainframe.py:117 +msgid "Successfully downloaded {0} URL(s) in {1} day(s) {2} hour(s) {3} minute(s) {4} second(s)" +msgstr "Janë shkarkuar me sukses {0} URL(ra) në {1} ditë {2} orë {3} minutë(a) {4} sekondë(a)" + +#: youtube_dl_gui/mainframe.py:119 +msgid "Downloads completed" +msgstr "Shkarkim i plotësuar" + +#: youtube_dl_gui/mainframe.py:120 +msgid "Total Progress: {0:.1f}% | Queued ({1}) Paused ({2}) Active ({3}) Completed ({4}) Error ({5})" +msgstr "Ecuri Gjithsej: {0:.1f}% | Në radhë ({1}) Të ndalur ({2}) Aktive ({3}) Të plotësuar ({4}) Gabim ({5})" + +#: youtube_dl_gui/mainframe.py:121 +msgid "Stopping downloads" +msgstr "Po ndalen shkarkimet" + +#: youtube_dl_gui/mainframe.py:122 +msgid "Downloads stopped" +msgstr "Shkarkimet u ndalën" + +#: youtube_dl_gui/mainframe.py:123 +msgid "You need to provide at least one URL" +msgstr "Lypset të jepni të paktën një URL" + +#: youtube_dl_gui/mainframe.py:124 +msgid "Downloads started" +msgstr "Shkarkimet nisën" + +#: youtube_dl_gui/mainframe.py:125 +msgid "Choose Directory" +msgstr "Zgjidhni Drejtori" + +#: youtube_dl_gui/mainframe.py:127 +msgid "Download in progress. Please wait for all downloads to complete" +msgstr "Shkarkim në ecuri e sipër. Ju lutemi, pritni që të plotësohen krejt shkarkimet" + +#: youtube_dl_gui/mainframe.py:128 +msgid "Update already in progress" +msgstr "Përditësim tashmë në ecuri e sipër" + +#: youtube_dl_gui/mainframe.py:130 +msgid "Downloading latest youtube-dl. Please wait..." +msgstr "Po shkarkohet youtube-dl më i ri. Ju lutemi, pritni…" + +#: youtube_dl_gui/mainframe.py:131 +msgid "Youtube-dl download failed [{0}]" +msgstr "Shkarkimi i youtube-dl-it dështoi [{0}]" + +#: youtube_dl_gui/mainframe.py:132 +msgid "Successfully downloaded youtube-dl" +msgstr "youtube-dl u shkarkua me sukses" + +#: youtube_dl_gui/mainframe.py:134 +msgid "Unable to open directory: '{dir}'. The specified path does not exist" +msgstr "S’arrihet të hapet drejtoria: '{dir}'. Shtegu i dhënë nuk ekziston" + +#: youtube_dl_gui/mainframe.py:136 +msgid "Error while shutting down. Make sure you typed the correct password" +msgstr "Gabim gjatë fikjes. Sigurohuni se e shtypët saktë fjalëkalimin" + +#: youtube_dl_gui/mainframe.py:138 +msgid "Shutting down system" +msgstr "Po fiket sistemi" + +#: youtube_dl_gui/mainframe.py:141 +msgid "Extension" +msgstr "Zgjerim" + +#: youtube_dl_gui/mainframe.py:142 +msgid "Size" +msgstr "Madhësi" + +#: youtube_dl_gui/mainframe.py:143 +msgid "Percent" +msgstr "Përqind" + +#: youtube_dl_gui/mainframe.py:144 +msgid "ETA" +msgstr "ETA" + +#: youtube_dl_gui/mainframe.py:145 +msgid "Speed" +msgstr "Shpejtësi" + +#: youtube_dl_gui/mainframe.py:146 +msgid "Status" +msgstr "Gjendje" + +#: youtube_dl_gui/mainframe.py:235 +msgid "Get URL" +msgstr "Merrni URL" + +#: youtube_dl_gui/mainframe.py:236 +msgid "Get command" +msgstr "Merrni urdhër" + +#: youtube_dl_gui/mainframe.py:237 +msgid "Open destination" +msgstr "Hap vendmbërritje" + +#: youtube_dl_gui/mainframe.py:238 +msgid "Re-enter" +msgstr "Rijepeni" + +#: youtube_dl_gui/mainframe.py:458 +msgid "Resume" +msgstr "Rimerre" + +#: youtube_dl_gui/mainframe.py:480 +msgid "Video" +msgstr "Video" + +#: youtube_dl_gui/mainframe.py:484 +msgid "Audio" +msgstr "Audio" + +#: youtube_dl_gui/mainframe.py:516 +msgid "No items selected. Please pick an action" +msgstr "S’u përzgjodhën objekte. Ju lutemi, zgjidhni një veprim" + +#: youtube_dl_gui/mainframe.py:516 +msgid "Remove all" +msgstr "Hiqi krejt" + +#: youtube_dl_gui/mainframe.py:516 +msgid "Remove completed" +msgstr "Hiq të përfunduarat" + +#: youtube_dl_gui/mainframe.py:534 +msgid "Are you sure you want to remove selected items?" +msgstr "Jeni i sigurt se doni të hiqen objektet e përzgjedhur?" + +#: youtube_dl_gui/mainframe.py:546 +msgid "Item is active, cannot remove" +msgstr "Objekti është aktiv, s’mund të hiqet" + +#: youtube_dl_gui/mainframe.py:579 +msgid "Item is not completed" +msgstr "Objekti s’është i përfunduar" + +#: youtube_dl_gui/mainframe.py:668 +msgid "Update in progress. Please wait for the update to complete" +msgstr "Përditësim në ecuri e sipër. Ju lutemi, pritni të përfundojë përditësimi" + +#: youtube_dl_gui/mainframe.py:716 +msgid "Logging is disabled" +msgstr "Regjistrimi është i çaktivizuar" + +#: youtube_dl_gui/mainframe.py:891 +msgid "Shutdown" +msgstr "Fike" + +#: youtube_dl_gui/mainframe.py:891 +msgid "Shutting down in {0} second(s)" +msgstr "Do të fiket për {0} sekondë(a)" + +#: youtube_dl_gui/mainframe.py:980 +msgid "No items to download" +msgstr "S’ka objekte për shkarkim" + +#: youtube_dl_gui/mainframe.py:1040 +msgid "Updates are disabled for your system. Please use the system's package manager to update youtube-dl." +msgstr "Për sistemin tuaj janë çaktivizuar përditësimet. Ju lutemi, për të përditësuar youtube-dl-in, përdorni përgjegjës paketash të vetë sistemit." + +#: youtube_dl_gui/mainframe.py:1065 +msgid "Are you sure you want to exit?" +msgstr "Jeni i sigurt se doni të dilet?" + +#: youtube_dl_gui/mainframe.py:1065 +msgid "Exit" +msgstr "Dalje" + +#: youtube_dl_gui/mainframe.py:1306 youtube_dl_gui/mainframe.py:1456 +msgid "Cancel" +msgstr "Anuloje" + +#: youtube_dl_gui/mainframe.py:1455 +msgid "OK" +msgstr "OK" + +#: youtube_dl_gui/optionsframe.py:65 +msgid "Reset" +msgstr "Ktheje te parazgjedhjet" + +#: youtube_dl_gui/optionsframe.py:66 +msgid "Close" +msgstr "Mbylle" + +#: youtube_dl_gui/optionsframe.py:72 +msgid "General" +msgstr "Të përgjithshme" + +#: youtube_dl_gui/optionsframe.py:73 +msgid "Formats" +msgstr "Formate" + +#: youtube_dl_gui/optionsframe.py:74 +msgid "Downloads" +msgstr "Shkarkime" + +#: youtube_dl_gui/optionsframe.py:75 +msgid "Advanced" +msgstr "Të mëtejshme" + +#: youtube_dl_gui/optionsframe.py:76 +msgid "Extra" +msgstr "Ekstra" + +#: youtube_dl_gui/optionsframe.py:310 +msgid "Language" +msgstr "Gjuhë" + +#: youtube_dl_gui/optionsframe.py:313 +msgid "Filename format" +msgstr "Format emrash kartelash" + +#: youtube_dl_gui/optionsframe.py:318 +msgid "Filename options" +msgstr "Mundësi emrash kartelash" + +#: youtube_dl_gui/optionsframe.py:319 +msgid "Restrict filenames to ASCII" +msgstr "Emrat e kartelave kufizoji në ASCII" + +#: youtube_dl_gui/optionsframe.py:321 +msgid "More options" +msgstr "Më tepër mundësi" + +#: youtube_dl_gui/optionsframe.py:322 +msgid "Confirm on exit" +msgstr "Ripohoje gjatë daljes" + +#: youtube_dl_gui/optionsframe.py:323 +msgid "Confirm item deletion" +msgstr "Ripohoni fshirje objekti" + +#: youtube_dl_gui/optionsframe.py:324 +msgid "Inform me on download completion" +msgstr "Njoftomë, kur të plotësohet shkarkimi" + +#: youtube_dl_gui/optionsframe.py:326 +msgid "Shutdown on download completion" +msgstr "Fike, kur të jetë plotësuar shkarkimi" + +#: youtube_dl_gui/optionsframe.py:337 +msgid "SUDO password" +msgstr "Fjalëkalim SUDO" + +#: youtube_dl_gui/optionsframe.py:415 youtube_dl_gui/optionsframe.py:816 +msgid "In order for the changes to take effect please restart {0}" +msgstr "Që të hyjnë në fuqi ndryshimet, ju lutemi, rinisni {0}" + +#: youtube_dl_gui/optionsframe.py:416 youtube_dl_gui/optionsframe.py:817 +msgid "Restart" +msgstr "Rinise" + +#: youtube_dl_gui/optionsframe.py:463 +msgid "high" +msgstr "e lartë" + +#: youtube_dl_gui/optionsframe.py:463 +msgid "low" +msgstr "e ulët" + +#: youtube_dl_gui/optionsframe.py:463 +msgid "mid" +msgstr "e mesme" + +#: youtube_dl_gui/optionsframe.py:468 +msgid "Video formats" +msgstr "Formate video" + +#: youtube_dl_gui/optionsframe.py:471 +msgid "Audio formats" +msgstr "Formate audio" + +#: youtube_dl_gui/optionsframe.py:474 +msgid "Post-Process options" +msgstr "Mundësi për pas kryerjes" + +#: youtube_dl_gui/optionsframe.py:475 +msgid "Keep original files" +msgstr "Mbaj kartelat origjinale" + +#: youtube_dl_gui/optionsframe.py:476 +msgid "Extract audio from video file" +msgstr "Përfto audio prej kartele video" + +#: youtube_dl_gui/optionsframe.py:477 +msgid "Embed thumbnail in audio file" +msgstr "Trupëzo miniaturë në kartelë audio" + +#: youtube_dl_gui/optionsframe.py:478 +msgid "Add metadata to file" +msgstr "Shtoni tejtëdhëna te kartelë" + +#: youtube_dl_gui/optionsframe.py:480 +msgid "Audio quality" +msgstr "Cilësi Audio" + +#: youtube_dl_gui/optionsframe.py:538 +msgid "English" +msgstr "Anglisht" + +#: youtube_dl_gui/optionsframe.py:539 +msgid "French" +msgstr "Frëngjisht" + +#: youtube_dl_gui/optionsframe.py:540 +msgid "German" +msgstr "Gjermanisht" + +#: youtube_dl_gui/optionsframe.py:541 +msgid "Greek" +msgstr "Greqisht" + +#: youtube_dl_gui/optionsframe.py:542 +msgid "Hebrew" +msgstr "Hebraisht" + +#: youtube_dl_gui/optionsframe.py:543 +msgid "Italian" +msgstr "Italisht" + +#: youtube_dl_gui/optionsframe.py:544 +msgid "Portuguese" +msgstr "Portugalisht" + +#: youtube_dl_gui/optionsframe.py:545 +msgid "Russian" +msgstr "Rusisht" + +#: youtube_dl_gui/optionsframe.py:546 +msgid "Spanish" +msgstr "Spanjisht" + +#: youtube_dl_gui/optionsframe.py:547 +msgid "Swedish" +msgstr "Suedisht" + +#: youtube_dl_gui/optionsframe.py:548 +msgid "Turkish" +msgstr "Turqisht" + +#: youtube_dl_gui/optionsframe.py:564 +msgid "None" +msgstr "Asnjë" + +#: youtube_dl_gui/optionsframe.py:565 +msgid "Automatic subtitles (YOUTUBE ONLY)" +msgstr "Titra të automatizuar (VETËM PËR YOUTUBE)" + +#: youtube_dl_gui/optionsframe.py:566 +msgid "All available subtitles" +msgstr "Krejt titrat e gatshëm" + +#: youtube_dl_gui/optionsframe.py:567 +msgid "Subtitles by language" +msgstr "Titra sipas gjuhësh" + +#: youtube_dl_gui/optionsframe.py:573 +msgid "Subtitles" +msgstr "Titra" + +#: youtube_dl_gui/optionsframe.py:577 +msgid "Subtitles options" +msgstr "Mundësi titrash" + +#: youtube_dl_gui/optionsframe.py:578 +msgid "Embed subtitles into video file (mp4 ONLY)" +msgstr "Trupëzo titra në kartelë video file (VETËM për mp4)" + +#: youtube_dl_gui/optionsframe.py:580 +msgid "Playlist" +msgstr "Luajlistë" + +#: youtube_dl_gui/optionsframe.py:586 youtube_dl_gui/optionsframe.py:591 +msgid "Max" +msgstr "Maks." + +#: youtube_dl_gui/optionsframe.py:589 +msgid "Filesize" +msgstr "Madhësi kartele" + +#: youtube_dl_gui/optionsframe.py:594 +msgid "Min" +msgstr "Min." + +#: youtube_dl_gui/optionsframe.py:723 +msgid "Retries" +msgstr "Prova" + +#: youtube_dl_gui/optionsframe.py:726 +msgid "Authentication" +msgstr "Mirëfilltësim" + +#: youtube_dl_gui/optionsframe.py:728 +msgid "Username" +msgstr "Emër përdoruesi" + +#: youtube_dl_gui/optionsframe.py:730 +msgid "Password" +msgstr "Fjalëkalim" + +#: youtube_dl_gui/optionsframe.py:732 +msgid "Video password" +msgstr "Fjalëkalim videoje" + +#: youtube_dl_gui/optionsframe.py:735 +msgid "Network" +msgstr "Rrjet" + +#: youtube_dl_gui/optionsframe.py:737 +msgid "Proxy" +msgstr "Ndërmjetës" + +#: youtube_dl_gui/optionsframe.py:739 +msgid "User agent" +msgstr "Agjent përdoruesi" + +#: youtube_dl_gui/optionsframe.py:741 +msgid "Referer" +msgstr "Referues" + +#: youtube_dl_gui/optionsframe.py:744 +msgid "Logging" +msgstr "Regjistrim" + +#: youtube_dl_gui/optionsframe.py:746 +msgid "Enable log" +msgstr "Aktivizo regjistër" + +#: youtube_dl_gui/optionsframe.py:747 +msgid "View" +msgstr "Parje" + +#: youtube_dl_gui/optionsframe.py:748 +msgid "Clear" +msgstr "Spastroje" + +#: youtube_dl_gui/optionsframe.py:858 +msgid "Youtube-dl command line options (e.g. --help)" +msgstr "Mundësi rreshti urdhrash për youtube-dl (p.sh., --help)" + +#: youtube_dl_gui/optionsframe.py:861 +msgid "Extra options" +msgstr "Mundësi ekstra" + +#: youtube_dl_gui/optionsframe.py:863 +msgid "Debug youtube-dl" +msgstr "Diagnostikoni youtube-dl" + +#: youtube_dl_gui/optionsframe.py:864 +msgid "Ignore errors" +msgstr "Shpërfilli gabimet" + +#: youtube_dl_gui/optionsframe.py:865 +msgid "Ignore youtube-dl config" +msgstr "Shpërfill formësim youtube-dl-je" + +#: youtube_dl_gui/optionsframe.py:866 +msgid "No mtime" +msgstr "S’ka mtime" + +#: youtube_dl_gui/optionsframe.py:867 +msgid "Prefer native HLS" +msgstr "Parapëlqe HLS nga sistemi" + +#: youtube_dl_gui/optionsframe.py:928 +msgid "Log Viewer" +msgstr "Parës Regjistri" diff --git a/youtube_dl_gui/optionsframe.py b/youtube_dl_gui/optionsframe.py index ab278ebe..7e68409b 100644 --- a/youtube_dl_gui/optionsframe.py +++ b/youtube_dl_gui/optionsframe.py @@ -277,6 +277,7 @@ class GeneralTab(TabPanel): ('ko_KR', 'Korean'), ('pt_BR', 'Portuguese'), ('ru_RU', 'Russian'), + ('sq_SQ', 'Albanian'), ('es_ES', 'Spanish') ]) From d915298c77987745eb2c325b258c686500703228 Mon Sep 17 00:00:00 2001 From: oleksis Date: Wed, 7 Apr 2021 21:41:58 -0400 Subject: [PATCH 34/34] [skip ci] Update release workflow --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 604d9691..1da88dc0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -56,7 +56,7 @@ jobs: asset_content_type: application/octet-stream - name: Get SHA2-256SUMS for youtube-dlg.exe id: sha2_file - run: echo "::set-output name=sha2_windows::$(Get-FileHash dist\youtube-dlg.exe -Algorithm SHA256).Hash.ToLower()" + run: echo "::set-output name=sha2_windows::$((Get-FileHash dist\youtube-dlg.exe -Algorithm SHA256).Hash.ToLower())" build_ubuntu_bdist: needs: build_windows_exe runs-on: ubuntu-latest