[
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "content": "## Please follow the guide below\n\n- You will be asked some questions and requested to provide some information, please read them **carefully** and answer **honestly**\n- Put an `x` into all the boxes [ ] relevant to your issue (like that [x])\n- Use *Preview* tab to see how your issue will actually look like\n\n### WARNING\nAll invalid issues will be rejected!!\n\n---\n\n### Before going further\n\n- 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)\n\n- Make sure you are using the *latest* **youtube-dl-gui** version (Click the `Settings` icon and then `About` to view the current version)\n\n- 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)\n\n- Make sure you searched the bugtracker for similar issues **including closed ones**\n\n- Make sure to read the [FAQs](https://github.com/MrS0m30n3/youtube-dl-gui/blob/master/docs/faqs.md) file\n\n  - [ ] **I think** my problem is **NOT** with **youtube-dl**\n  - [ ] I've **verified** and **i assure** that I'm running youtube-dl-gui **0.4**\n  - [ ] **I assure** that i am using the latest version of **youtube-dl**\n  - [ ] [Searched](https://github.com/MrS0m30n3/youtube-dl-gui/issues) bugtracker\n  - [ ] I've read the FAQs file\n\n---\n\n### What is the purpose of your *issue*?\n\n- [ ] Bug report\n- [ ] Feature request (request for a new functionality)\n- [ ] Question\n- [ ] Other\n\nPlease remove any sections between (---) if they are not related to your issue\n\n---\n\n### Bug report\n\n#### If the problem occurs when downloading a URL please provide the full verbose output as follows:\n\n1. Restart **youtube-dl-gui**\n1. Go to `Options > Extra` tab\n2. Enable **Debug youtube-dl**\n3. Go to `Options > Advanced` tab and **Clear** your log content\n4. Try to download the URL\n5. Copy the **whole** log content and insert it between the ``` part below\n\n```\ndelete me and insert your log content here\n```\n\n#### What operating system do you use ?\n\n#### List of actions to perform to reproduce the problem:\n\n  1. ..\n  2. ..\n  3. ..\n  \n#### What is the expected behaviour ?\n\n#### What happens instead ?\n\n\n---\n\n### Feature request (request for a new functionality)\n\nPlease make sure that the requested feature is **NOT** already in the [TODO](https://github.com/MrS0m30n3/youtube-dl-gui/blob/master/TODO) list\n\n- [ ] I've **verified** and **i assure** that my requested feature is **NOT** in the TODO list\n\n#### What operating system do you use ?\n\n\n---\n\n<!--Enter description of your issue, suggested solution and other information below. Please make sure the description is worded well enough to be understood-->\n\n"
  },
  {
    "path": ".gitignore",
    "content": "MANIFEST\n*.pyc\n*.mo\n\ndist/\nbuild/\n"
  },
  {
    "path": ".tx/config",
    "content": "[main]\nhost = https://www.transifex.com\n\n[youtube-dl-gui.resources]\nfile_filter = youtube_dl_gui/locale/<lang>/LC_MESSAGES/youtube_dl_gui.po\nsource_file = youtube_dl_gui/locale/en_US/LC_MESSAGES/youtube_dl_gui.po\nsource_lang = en_US\ntype = PO\n\n"
  },
  {
    "path": "AUTHORS",
    "content": "# Authors ordered by first contribution.\n\nMrS0m30n3 <ytubedlg@gmail.com>\nHenrique Pereira <ikkibr@gmail.com>\nFironet <dolphinssmile@hotmail.com>\nMax Bruckner <max@maxbruckner.de>\nSergey M․ <dstftw@gmail.com>\nMarcin Nowicki <pr0d1r2@gmail.com>\ndnlsrl <dnlsrl.kaiser@gmail.com>\nDavid Wales <daviewales@gmail.com>\nNikita Bystrov <arttsesoft@gmail.com>\nnodiscc <nodiscc@gmail.com>\nLeo Wzukw <leowzukw@users.noreply.github.com>\ntodool <tobie.doolaard@gmail.com>\nYousuf 'Jay' Philips <me@philipz.com>\nmemnoth <yisooan@gmail.com>\nPeter Stevenson <2e0pgs@gmail.com>\nSwyter <swyterzone@gmail.com>\nSebastian Wagner <sebix@sebix.at>\nsaad snosi <fixall.dz@gmail.com>\ncleitonme <cleitonme@gmail.com>\n\n# Generated by update-authors.sh script\n"
  },
  {
    "path": "ChangeLog",
    "content": "# Changelog\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)\n\n## [Unreleased]\n\n### Added\n- ChangeLog\n- 'flac' in available audio formats (#234)\n- French translation (#203)\n- Japanese translation (#226)\n- Italian translation (#231)\n- Czech translation (#233)\n- Automatic locale detection during first run (#235)\n- Man page (#259)\n- Option to disable youtube-dl updates (#21)\n\n### Fixed\n- Bug in utils.convert_item function\n- Bug in downloaders.YoutubeDLDownloader (#244)\n\n### Changed\n- Update timeout from 20 to 10 seconds (#244)\n\n"
  },
  {
    "path": "LICENSE",
    "content": "This is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, compile, sell, or\ndistribute this software, either in source code form or as a compiled\nbinary, for any purpose, commercial or non-commercial, and by any\nmeans.\n\nIn jurisdictions that recognize copyright laws, the author or authors\nof this software dedicate any and all copyright interest in the\nsoftware to the public domain. We make this dedication for the benefit\nof the public at large and to the detriment of our heirs and\nsuccessors. We intend this dedication to be an overt act of\nrelinquishment in perpetuity of all present and future rights to this\nsoftware under copyright law.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n\nFor more information, please refer to <http://unlicense.org/>\n\n"
  },
  {
    "path": "MANIFEST.in",
    "content": "include TODO\ninclude AUTHORS\ninclude LICENSE\ninclude README.md\ninclude MANIFEST.in\ninclude ChangeLog\ninclude youtube-dl-gui.1\n\ninclude tests/*.py\ninclude devscripts/*\ninclude docs/*\n\ninclude youtube_dl_gui/data/pixmaps/icons-license\n\nrecursive-include youtube_dl_gui/data *.png *.ico\nrecursive-include youtube_dl_gui/locale *.po\n\nrecursive-exclude youtube_dl_gui/locale *.mo\n"
  },
  {
    "path": "README.md",
    "content": "[![Donations Badge](https://yourdonation.rocks/images/badge.svg)](https://mrs0m30n3.github.io/youtube-dl-gui/donate.html)\n\n# youtube-dlG\nA 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)\n\n## Screenshots\n![youtube-dl-gui main window](https://raw.githubusercontent.com/MrS0m30n3/youtube-dl-gui/gh-pages/images/ydlg_ui.gif)\n\n## Requirements\n* [Python 2.7.3+](https://www.python.org/downloads)\n* [wxPython 3](https://wxpython.org/download.php)\n* [TwoDict](https://pypi.python.org/pypi/twodict)\n* [GNU gettext](https://www.gnu.org/software/gettext/) (to build the package)\n* [FFmpeg](https://ffmpeg.org/download.html) (optional, to post process video files)\n\n## Downloads\n* [Source (.zip)](https://github.com/MrS0m30n3/youtube-dl-gui/archive/0.4.zip)\n* [Source (.tar.gz)](https://github.com/MrS0m30n3/youtube-dl-gui/archive/0.4.tar.gz)\n* [PyPi](https://pypi.python.org/pypi/youtube-dlg/0.4)\n* [Ubuntu PPA](http://ppa.launchpad.net/nilarimogard/webupd8/ubuntu/pool/main/y/youtube-dlg/)\n* [Arch AUR](https://aur.archlinux.org/packages/youtube-dl-gui-git/)\n* [Slackware SlackBuild](https://slackbuilds.org/repository/14.2/network/youtube-dl-gui/)\n* [openSUSE](https://software.opensuse.org/package/youtube-dl-gui)\n* [Windows Installer](https://github.com/MrS0m30n3/youtube-dl-gui/releases/download/0.4/youtube-dl-gui-0.4-win-setup.zip)\n* [Windows Portable](https://github.com/MrS0m30n3/youtube-dl-gui/releases/download/0.4/youtube-dl-gui-0.4-win-portable.zip)\n\n## Installation\n\n### Install From Source\n1. Download & extract the source\n2. Change directory into *youtube-dl-gui-0.4*\n3. Run `python setup.py install`\n\n### Install PyPi\n1. Run `pip install youtube-dlg`\n\n### Install Windows Installer\n1. Download & extract the Windows installer\n2. Run the `setup.exe` file\n\n## Contributing\n* **Add support for new language:** See [localization howto](docs/localization_howto.md)\n* **Report a bug:** See [issues](https://github.com/MrS0m30n3/youtube-dl-gui/issues)\n\n## Authors\nSee [AUTHORS](AUTHORS) file\n\n## License\nThe [Public Domain License](LICENSE)\n\n## Frequently Asked Questions\nSee [FAQs](docs/faqs.md) file\n\n## Thanks\nThanks to everyone who contributed to this project and to [@philipzae](https://github.com/philipzae) for designing the new UI layout.\n"
  },
  {
    "path": "TODO",
    "content": "Release 0.4.1\n=============\n* Intergrity check youtube-dl bin\n* Non-Windows shutdown using D-Bus instead of 'shutdown'\n* Custom youtube-dl format selection filters (e.g. -f best[height<=360])\n* Remember list of urls after closing & re-opening\n* Context menu add new option \"Go to file\" or change the behaviour of \"Open destination\"\n* Context menu \"Report Failed URL to Github\" (see: #16)\n* Icons theme selection\n\n\nFeatures\n========\n* Improve playlist downloads\n* Mix formats option\n* About dialog show youtube-dl version (probably will have to create new frame)\n* Settings menu add \"Statistics\"\n\n\nLocalization\n============\n* Add support for right to left languages (hebrew, arabic)\n* Fix paths on R2L layouts\n\n\nOther\n=====\n* Re-structure package\n\n* Refactor\n* Review - rewrite threads communications\n* Add support for Python 3.*\n* Logging system using the Python 'logging' module\n* Use youtube-dl directly from python instead of using the subprocess module\n\n\nProbably wont add\n=================\n* ListCtrl double click to \"Rename\"\n* Option to enable-disable items deletion from the filesystem\n* Add '--recode-video' to Formats tab\n* Auto video format detection\n* Change 'Warning' status to 'Finished (*)' or something similar? (see: issue #131)\n* Use proxy during update phase (see: issue #244)\n"
  },
  {
    "path": "devscripts/bump-version.sh",
    "content": "#!/bin/bash\n\n# Author: Sotiris Papadopoulos <ytubedlg@gmail.com>\n# Last-Revision: 2017-04-17\n# Script to bump the version and automatically update related files\n#\n# Usage: ./bump_version.sh <new-version>\n\nPACKAGE=\"youtube_dl_gui\"\n\nFILES=`cat <<EOF\n$PACKAGE/version.py\n.github/ISSUE_TEMPLATE.md\nREADME.md\nyoutube-dl-gui.1\nEOF`\n\n# Update version string on given file\n# $1 = current version\n# $2 = new version\n# $3 = file\nfunction update_version {\n    echo \"Updating file: $3\"\n    sed -i \"s/$1/$2/g\" $3\n}\n\n# Returns 'true' if given version is less or equal to the current version\n# $1 = version to check\n# $2 = current version\nfunction version_le {\n    smallest_version=`echo -e \"$1\\n$2\" | sort -V | head -n1`\n    [ \"$1\" = \"$smallest_version\" ]\n}\n\n\nif [ $# -ne 1 ]; then\n    echo \"Usage ./bump_version.sh <new-version>\"\n    exit 1\nfi\n\ncd ..\n\nnew_version=$1\ncur_version=`grep version \"$PACKAGE/version.py\" | cut -d\"'\" -f2`\n\necho \"Current version = $cur_version\"\necho \"New version     = $new_version\"\necho\n\nif version_le $new_version $cur_version; then\n    echo \"New version must be greater than the current version, exiting...\"\n    exit 1\nfi\n\nfor file in $FILES; do\n    update_version $cur_version $new_version $file\ndone\n\necho \"Done\"\n"
  },
  {
    "path": "devscripts/check-translation.py",
    "content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\"\nAuthor: Sotiris Papadopoulos <ytubedlg@gmail.com>\nLast-Revision: 2017-04-19\n\nScript to automatically check PO files\n\n\"\"\"\n\nfrom __future__ import unicode_literals\n\nimport os\nimport sys\nimport logging\nimport argparse\n\nfrom time import sleep\nfrom datetime import datetime, timedelta, tzinfo\n\ntry:\n    import polib\n    import google_translate\nexcept ImportError as error:\n    print(error)\n    sys.exit(1)\n\n\nWTIME = 2.0  # Time in seconds to wait between requests to avoid ban\n\nPACKAGE = \"youtube_dl_gui\"\n\nPO_FILENAME = \"{}.po\".format(PACKAGE)\n\nLOCALE_PATH_TMPL = os.path.join(PACKAGE, \"locale\", \"{lang}\", \"LC_MESSAGES\", PO_FILENAME)\n\n\nlogging.basicConfig(level=logging.ERROR)\n\n\ndef parse():\n    \"\"\"Parse command line arguments.\"\"\"\n    parser = argparse.ArgumentParser(description=\"Script to automatically check PO files\")\n\n    parser.add_argument(\"language\", help=\"language of the PO file to check\")\n\n    parser.add_argument(\"-w\", \"--werror\", action=\"store_true\", help=\"treat all warning messages as errors\")\n    parser.add_argument(\"-o\", \"--only-headers\", action=\"store_true\", help=\"check only the PO file headers\")\n    parser.add_argument(\"-n\", \"--no-translate\", action=\"store_true\", help=\"do not use the translator to check 'msgstr' fields\")\n    parser.add_argument(\"-t\", \"--tlang\", help=\"force a different language on the translator than the one given\")\n\n    return parser.parse_args()\n\n\nclass UTC_Offset_Timezone(tzinfo):\n\n    \"\"\"Class that represents a UTC offset in the format +/-0000.\"\"\"\n\n    def __init__(self, offset_string):\n        self.offset = timedelta(seconds=UTC_Offset_Timezone.parse_offset(offset_string))\n\n    def utcoffset(self, dt):\n        return self.offset + self.dst(dt)\n\n    def dst(self, dt):\n        return timedelta(0)\n\n    @staticmethod\n    def parse_offset(offset_string):\n        \"\"\"Parse the offset string into seconds.\"\"\"\n\n        if len(offset_string) != 5:\n            raise ValueError(\"Invalid length for offset string ({})\".format(offset_string))\n\n        hours = offset_string[1:3]\n        minutes = offset_string[3:5]\n\n        offset = int(hours) * 3600 + int(minutes) * 60\n\n        if offset_string[0] == \"-\":\n            return -1 * offset\n\n        return offset\n\n\ndef parse_date(date_string):\n    \"\"\"Parse date string into an aware datetime object.\"\"\"\n\n    # Just a small list with the most common timezones\n    offset_list = [\n        (\"JST\", \"0900\"),\n        (\"EEST\", \"0300\"),\n        (\"EET\", \"0200\"),\n        (\"GMT\", \"0000\"),\n        (\"UTC\", \"0000\")\n    ]\n\n    # Replace all the timezones with the offset\n    for item in offset_list:\n        timezone, offset = item\n\n        date_string = date_string.replace(timezone, offset)\n\n    datetime_string = date_string[:16]\n    offset_string = date_string[16:]\n\n    naive_date = datetime.strptime(datetime_string, \"%Y-%m-%d %H:%M\")\n\n    # Create & return an aware datetime object based on the offset\n    return naive_date.replace(tzinfo=UTC_Offset_Timezone(offset_string))\n\n# Print helpers\n\ndef my_print(msg, char=\"*\", value=None, exit=False):\n    \"\"\"Print 'msg', debug 'value' and exit if 'exit' is True.\"\"\"\n    print(\"[{}] {}\".format(char, msg))\n\n    if value is not None:\n        print(\"\\tvalue= \\\"{}\\\"\".format(value))\n\n    if exit:\n        sys.exit(1)\n\ndef perror(msg, value=None):\n    my_print(msg, \"-\", value, True)\n\ndef pwarn(msg, value=None, exit=False):\n    my_print(msg, \"!\", value, exit)\n\ndef pinfo(msg):\n    my_print(msg)\n\n#############################\n\n\ndef main(args):\n    os.chdir(\"..\")\n\n    # setup\n    pot_file_path = LOCALE_PATH_TMPL.format(lang=\"en_US\")\n    po_file_path = LOCALE_PATH_TMPL.format(lang=args.language)\n\n    if not os.path.exists(pot_file_path):\n        perror(\"Failed to locate POT file, exiting...\", pot_file_path)\n\n    if not os.path.exists(po_file_path):\n        perror(\"Failed to locate PO file, exiting...\", po_file_path)\n\n    pot_file = polib.pofile(pot_file_path)\n    po_file = polib.pofile(po_file_path)\n\n    # check headers\n    pinfo(\"Checking PO headers\")\n\n    pot_headers = pot_file.metadata\n    po_headers = po_file.metadata\n\n    if pot_headers[\"Project-Id-Version\"] != po_headers[\"Project-Id-Version\"]:\n        pwarn(\"'Project-Id-Version' headers do not match\", exit=args.werror)\n\n    if pot_headers[\"POT-Creation-Date\"] != po_headers[\"POT-Creation-Date\"]:\n        pwarn(\"'POT-Creation-Date' headers do not match\", exit=args.werror)\n\n    po_creation_date = parse_date(po_headers[\"POT-Creation-Date\"])\n    po_revision_date = parse_date(po_headers[\"PO-Revision-Date\"])\n\n    # Aware datetimes convert to UTC automatically when comparing\n    if po_revision_date <= po_creation_date:\n        pwarn(\"PO file seems outdated\", exit=args.werror)\n\n    if \"Language\" in po_headers and po_headers[\"Language\"] != args.language:\n        pwarn(\"'Language' header does not match with the given language\", po_headers[\"Language\"], args.werror)\n\n    pinfo(\"Last-Translator: {}\".format(po_headers[\"Last-Translator\"]))\n\n    # check translations\n    if args.only_headers:\n        sys.exit(0)\n\n    pinfo(\"Checking translations, this might take a while...\")\n\n    pot_msgid = [entry.msgid for entry in pot_file]\n    po_msgid = [entry.msgid for entry in po_file]\n\n    # lists to hold reports\n    missing_msgid = []\n    not_translated = []\n    same_msgstr = []\n    with_typo = []\n    verify_trans = []\n    fuzzy_trans = po_file.fuzzy_entries()\n\n    for msgid in pot_msgid:\n        if msgid not in po_msgid:\n            missing_msgid.append(msgid)\n\n    # Init translator only if the '--no-translate' flag is NOT set\n    translator = None\n    if not args.no_translate:\n        translator = google_translate.GoogleTranslator(timeout=5.0, retries=2, wait_time=WTIME)\n\n        # Set source language for GoogleTranslator\n        if args.tlang is not None:\n            src_lang = args.tlang\n            pinfo(\"Forcing '{}' as the translator's source language\".format(src_lang))\n        else:\n            # Get a valid source language for Google\n            # for example convert 'ar_SA' to 'ar' or 'zh_CN' to 'zh-CN'\n            src_lang = args.language\n\n            if src_lang not in translator._lang_dict:\n                src_lang = src_lang.replace(\"_\", \"-\")\n\n                if src_lang not in translator._lang_dict:\n                    src_lang = src_lang.split(\"-\")[0]\n\n    # Keep entries that need further analysis using the translator\n    further_analysis = []\n\n    for entry in po_file:\n        if not entry.translated():\n            not_translated.append(entry)\n\n        elif entry.msgid == entry.msgstr:\n            same_msgstr.append(entry)\n\n        else:\n            further_analysis.append(entry)\n\n    if translator is not None and further_analysis:\n        # eta = (items_to_analyze * (WTIME + avg_ms)) - WTIME\n        # We subtract WTIME at the end because there is no wait for the last item on the list\n        # avg_msg = 200ms\n        eta_seconds = (len(further_analysis) * (WTIME + 0.2)) - WTIME\n        eta_seconds = int(round(eta_seconds))\n\n        eta = timedelta(seconds=eta_seconds)\n        pinfo(\"Approximate time to check translations online: {}\".format(eta))\n\n        # Pass translations as a list since GoogleTranslator can handle them\n        words_dict = translator.get_info_dict([entry.msgstr for entry in further_analysis], \"en\", src_lang)\n\n        for index, word_dict in enumerate(words_dict):\n            # Get the corresponding POEntry since the words_dict does not contain those\n            entry = further_analysis[index]\n\n            if word_dict is not None:\n                if word_dict[\"has_typo\"]:\n                    with_typo.append(entry)\n\n                if word_dict[\"translation\"].lower() != entry.msgid.lower():\n\n                    found = False\n\n                    # Check verbs, nouns, adverbs, etc..\n                    for key in word_dict[\"extra\"]:\n                        if entry.msgid.lower() in word_dict[\"extra\"][key].keys():\n                            found = True\n                            break\n\n                    if not found:\n                        verify_trans.append((entry, word_dict[\"translation\"]))\n\n    # time to report\n    print(\"=\" * 25 + \"Report\" + \"=\" * 25)\n\n    if missing_msgid:\n        print(\"Missing msgids\")\n\n        for msgid in missing_msgid:\n            print(\"  \\\"{}\\\"\".format(msgid))\n\n    if not_translated:\n        print(\"Not translated\")\n\n        for entry in not_translated:\n            print(\"  line: {} msgid: \\\"{}\\\"\".format(entry.linenum, entry.msgid))\n\n    if same_msgstr:\n        print(\"Same msgstr\")\n\n        for entry in same_msgstr:\n            print(\"  line: {} msgid: \\\"{}\\\"\".format(entry.linenum, entry.msgid))\n\n    if with_typo:\n        print(\"With typo\")\n\n        for entry in with_typo:\n            print(\"  line: {} msgid: \\\"{}\\\" msgstr: \\\"{}\\\"\".format(entry.linenum, entry.msgid, entry.msgstr))\n\n    if verify_trans:\n        print(\"Verify translation\")\n\n        for item in verify_trans:\n            entry, translation = item\n            print(\"  line: {} msgid: \\\"{}\\\" trans: \\\"{}\\\"\".format(entry.linenum, entry.msgid, translation))\n\n    if fuzzy_trans:\n        print(\"Fuzzy translations\")\n\n        for entry in fuzzy_trans:\n            print(\"  line: {} msgid: \\\"{}\\\"\".format(entry.linenum, entry.msgid))\n\n    total = len(missing_msgid) + len(not_translated) + len(same_msgstr) + len(with_typo) + len(verify_trans) + len(fuzzy_trans)\n\n    print(\"\")\n    print(\"Missing msgids\\t\\t: {}\".format(len(missing_msgid)))\n    print(\"Not translated\\t\\t: {}\".format(len(not_translated)))\n    print(\"Same msgstr\\t\\t: {}\".format(len(same_msgstr)))\n    print(\"With typo\\t\\t: {}\".format(len(with_typo)))\n    print(\"Verify translation\\t: {}\".format(len(verify_trans)))\n    print(\"Fuzzy translations\\t: {}\".format(len(fuzzy_trans)))\n    print(\"Total\\t\\t\\t: {}\".format(total))\n    print(\"\")\n    print(\"Total entries\\t\\t: {}\".format(len(po_file)))\n\n\nif __name__ == \"__main__\":\n    try:\n        main(parse())\n    except KeyboardInterrupt:\n        print(\"KeyboardInterrupt\")\n"
  },
  {
    "path": "devscripts/new-locale.py",
    "content": "#!/usr/bin/env python\n\n\"\"\"\nAuthor: Sotiris Papadopoulos <ytubedlg@gmail.com>\nLast-Revision: 2017-01-30\n\nScript to add support for a new language\n\nUsage   : ./new-locale.py <language_code>\nExample : ./new-locale.py en_US\n\n\"\"\"\n\nimport os\nimport sys\nimport shutil\n\n\nPACKAGE = \"youtube_dl_gui\"\n\nLOCALE_PATH_TMPL = os.path.join(PACKAGE, \"locale\", \"{lang}\", \"LC_MESSAGES\")\n\nPO_FILE_TMPL = os.path.join(\"{parent_dir}\", \"youtube_dl_gui.po\")\n\n\ndef error(msg):\n    print(\"[-]{0}\".format(msg))\n    sys.exit(1)\n\n\ndef output(msg):\n    print(\"[*]{0}\".format(msg))\n\n\ndef manage_directory():\n    \"\"\"Allow script calls from the 'devscripts' dir and the package dir.\"\"\"\n    if os.path.basename(os.getcwd()) == \"devscripts\":\n        os.chdir(\"..\")\n\n\ndef main(lang_code):\n    manage_directory()\n\n    target_dir = LOCALE_PATH_TMPL.format(lang=lang_code)\n    default_dir = LOCALE_PATH_TMPL.format(lang=\"en_US\")\n\n    target_po = PO_FILE_TMPL.format(parent_dir=target_dir)\n    source_po = PO_FILE_TMPL.format(parent_dir=default_dir)\n\n    if os.path.exists(target_dir):\n        error(\"Locale '{0}' already exists, exiting...\".format(lang_code))\n\n    output(\"Creating directory: '{0}'\".format(target_dir))\n    os.makedirs(target_dir)\n\n    output(\"Creating PO file: '{0}'\".format(target_po))\n    shutil.copy(source_po, target_po)\n\n    output(\"Done\")\n\n\nif __name__ == \"__main__\":\n    if len(sys.argv) == 2:\n        main(sys.argv[1])\n    else:\n        print(\"Usage   : {0} <language_code>\".format(sys.argv[0]))\n        print(\"Example : {0} en_US\".format(sys.argv[0]))\n"
  },
  {
    "path": "devscripts/update-authors.sh",
    "content": "#!/bin/bash\n\n# Author: Sotiris Papadopoulos <ytubedlg@gmail.com>\n# Last-Revision: 2017-04-17\n# Script to update the AUTHORS file\n#\n# Usage: ./update-authors.sh\n\ncd ..\n\ngit log --reverse --format=\"%aN <%aE>\" | python -c \"\n\nimport sys\n\nauthors = set()\n\nsys.stdout.write('# Authors ordered by first contribution.\\n\\n')\n\nfor line in sys.stdin:\n    username, _ = line.split('<')\n\n    if username not in authors:\n        authors.add(username)\n        sys.stdout.write(line)\n\nsys.stdout.write('\\n# Generated by update-authors.sh script\\n')\n\" > AUTHORS\n"
  },
  {
    "path": "devscripts/update-locales.sh",
    "content": "#!/bin/bash\n\n# Author: Sotiris Papadopoulos <ytubedlg@gmail.com>\n# Last-Revision: 2017-01-30\n# Script to update all locale files and rebuild the MO files\n#\n# Usage: ./update_locales.sh\n\nPACKAGE=\"youtube_dl_gui\"\n\nPO_FILE=\"$PACKAGE.po\"\n\nMO_FILE=\"$PACKAGE.mo\"\n\n\ncd ..\n\nVERSION=$(grep version \"$PACKAGE/version.py\" | cut -d\"'\" -f2)\n\nDIRS=$(find \"$PACKAGE/locale\" -mindepth 2 -maxdepth 2)\n\n\necho \"[*]Creating new .PO file\"\n\npygettext.py -v -o new.po \"$PACKAGE/*.py\"\n\n#vim new.po\n\necho \"[*]Updating old .PO files\"\n\nfor dir in $DIRS; do\n    msgmerge --update --no-wrap -v \"$dir/$PO_FILE\" new.po\n\n    # Strip empty headers\n    sed -i \"/: \\\\n/d\" \"$dir/$PO_FILE\"\n\n    # Upate version\n    sed -i \"s/Project-Id-Version:.*\\\\\\n/Project-Id-Version: youtube-dlg $VERSION\\\\\\n/g\" \"$dir/$PO_FILE\"\ndone\n\necho\nread -p \"Open files for revision?(y/n) \" ch\n\nif [ $ch = 'y' ]; then\n    for dir in $DIRS; do\n        vim \"$dir/$PO_FILE\"\n    done\nfi\n\necho \"[*]Building .MO files\"\n\nfor dir in $DIRS; do\n    msgfmt --use-fuzzy --output-file \"$dir/$MO_FILE\" \"$dir/$PO_FILE\"\ndone\n\necho \"[*]Done\"\n"
  },
  {
    "path": "docs/faqs.md",
    "content": "# Frequently Asked Questions\n\n* **How can I make sure I'm getting the best quality possible?**:\nDon't force any output format, leave the **default** format selected in the main window (*default = highest quality*).\nRead https://github.com/rg3/youtube-dl#format-selection for more details.\n\n* **Post processing takes too long**:\nThere should be no post-processing if you leave the video format to default (which defaults to the best format) and did not check convert to audio or embed subtitles, otherwise the file will be re-encoded to the format you selected (which takes time/CPU resources).\n\n* **The website I'm trying to download from is not supported**:\nYoutube-dl-gui uses [youtube-dl](https://github.com/rg3/youtube-dl) in the backend to download files. Youtube-dl provides a list of [extractors](https://github.com/rg3/youtube-dl/tree/master/youtube_dl/extractor) to work with each particular site. If you'd like to request support for a new website, please submit it to youtube-dl's [issue tracker](https://github.com/rg3/youtube-dl/issues).\n\n* **How do I change the naming pattern for downloaded files?**:\nYou can change the naming pattern by picking a different filename format under the Options>General tab. You can also use a custom pattern by setting the option to \"Custom\" and editing the output template field. For more infomations on the output template see [youtube-dl's output template section](https://github.com/rg3/youtube-dl/blob/master/README.md#output-template).\n\n* **When is the next release coming?**:\nYoutube-dl-gui does not have a release schedule, next release will come when it's ready.\n\n* **How can i log the youtube-dl's debug output?**:\nJust go to Options>Extra tab and enable the \"Debug youtube-dl\" option.\n\n* **I don't see my language in the available subtitles languages**:\nYou can't see it because it's not there, feel free to open a new pull-request and add support for your language (it's literally one line of code you don't need years of experience).\n\n* **I'm on OS-X and i get 'No module named wx' error**:\nYou need to install wxPython. If you have [homebrew](https://brew.sh/) installed you can just run: `brew install wxpython`\n\n* **Is there an option to change the maximum parallel downloads?**:\nYou can change the number of max parallel downloads by editing the \"workers_number\" option in your settings.json file. Note that you need to restart youtube-dl-gui for the changes to take place.\n\n  settings.json file location:\n  * Windows: `%appdata%\\youtube-dlg\\settings.json`\n  * Linux: `~/.config/youtube-dlg/settings.json`\n\n* **Not all formats reported by youtube-dl '-F' are available in youtube-dl-gui**:\nUnfortunately it is not possible to support all the video formats that youtube-dl provides. If you want to use a \"custom\"\nformat you can follow this steps:\n\n  1. Set the download format to \"default\"\n  2. Go to Options>Extra tab\n  3. Add `-f your_custom_format` in the commands box\n  4. Download your video as you would normally do\n\n* **How can i use a youtube-dl option that's not available in youtube-dl-gui?**:\nYou can add extra youtube-dl command line options in the commands box under the Options>Extra tab.\n\n* **Can i download only the subtitles file?**:\nIf the video file is presented youtube-dl-gui will go ahead and download only the subtitles file. If the video file is NOT presented you can add the `--skip-download` option, which will skip the video download phase. If you are not happy with the above options you should use a different tool for the job since youtube-dl-gui is not a subtitles downloader.\n\n* **I'm using the HLS downloader and i don't see any progress report**:\nThat's a known issue you should use the native HLS implementation by enabling the \"Prefer Native HLS\" option under the Options>Extra tab. NOTE that the native HLS implementation **might not work on every site**. For more info you can read issue [#49](https://github.com/MrS0m30n3/youtube-dl-gui/issues/49).\n\n* **I'm using the HLS downloader and the 'stop' button won't work**:\nThat's also a known issue with the HLS downloader on Windows. You should use the native HLS implementation or wait for the download to complete normally. For more info you can read issue [#49](https://github.com/MrS0m30n3/youtube-dl-gui/issues/49).\n\n* **Is it possible to use a youtube-dl version other than the official one?**:\nYou can use your own version of youtube-dl by editing the \"youtubedl_path\" option in your settings.json file and make it point to your own binary (e.g. /usr/local/bin). Note that if youtube-dl-gui does not have write permissions to this new directory the \"update\" option in the GUI will fail. Also, note that changing the \"youtubedl_path\" won't change the update source which is hardcoded for now to \"https://yt-dl.org/latest/\".\n\n* **The program crashes frequently or pretends to succeed downloading the files (Windows)**:\nFollow [youtube-dl instructions](https://github.com/rg3/youtube-dl#the-exe-throws-an-error-due-to-missing-msvcr100dll) on updating your Visual C++ Redistributable.\n\n* **How come 1080p YouTube downloads are not working?**:\nTry disabling native HLS. You can disable the \"Prefer Native HLS\" option under the Options>Extra tab.\n"
  },
  {
    "path": "docs/localization_howto.md",
    "content": "# Localization Guide - [Transifex](https://www.transifex.com/youtube-dl-gui/public/)\r\n\r\n## &#x1F534; DISCLAIMER\r\n**By sending a translation you agree to publish your work under the [UNLICENSE](https://unlicense.org/) license!**\r\n\r\n## Contents\r\n  * [Translators](localization_howto.md#translators)\r\n  * [Testers](localization_howto.md#testers)\r\n  * [Devs](localization_howto.md#devs)\r\n  * [FAQs](localization_howto.md#faqs)\r\n\r\n## Translators\r\n\r\n### Requirements\r\n  * A modern browser\r\n  * A Transifex account, [sign-up](https://www.transifex.com/signup/)\r\n\r\n### Notes\r\n  * If your language is currently not supported you can make a request for new language support.\r\n  * When you request support for a new language, the language code should be in the format **en_US** and NOT just **en**.\r\n  * Variables such as **{0}**, **{1}**, **{dir}**, **{0:.1f}**, etc should be copied exactly as they appear in the translation box.\r\n  * Variables represent a word that will be replaced by real data (name, number, etc).\r\n  * Variables can be moved around the string in order to make the most logical translation.\r\n  * When new strings for translation are available you will get inbox notifications.\r\n  * For help you can leave a comment with @username or send a direct message to one of the maintainers.\r\n  * Maintainer usernames are: `MrS0m30n3`, `nodiscc`\r\n\r\n### Gettings Started\r\n  1. [Sign-in](https://www.transifex.com/signin/) to Transifex\r\n  2. [Join a translation team](https://docs.transifex.com/getting-started/translators#joining-a-translation-team)\r\n  3. [Start translating using the web editor](https://docs.transifex.com/translation/translating-with-the-web-editor)\r\n\r\n### Help\r\n  * [Translators getting started](https://docs.transifex.com/getting-started/translators)\r\n  * [Translating offline](https://docs.transifex.com/translation/offline)\r\n  * [Using the glossary](https://docs.transifex.com/translation/using-the-glossary)\r\n  * For help you can [leave a comment or open an issue](https://docs.transifex.com/translation/tools-in-the-editor#comments-and-issues)\r\n\r\n## Testers\r\n\r\n### Requirements\r\n  * Check [project requirements](../README.md#requirements)\r\n  * [Git](https://git-scm.com/downloads)\r\n  * [Transifex CLI client](https://docs.transifex.com/client/installing-the-client)\r\n  * Some kind of text editor to edit some code (notepad++, nano, etc are sufficient)\r\n  * A Transifex account, [sign-up](https://www.transifex.com/signup/)\r\n\r\n### Notes\r\n  * The instructions below assume basic knowledge of the command line (OS independent).\r\n  * The **language code** being used should be in the format `<ISO 639-1>_<ISO 3166-1 alpha-2>` (e.g. en_US).\r\n  * You can locally edit the translation file (PO) by opening it using a simple editor and editing the **msgstr** fields.\r\n  * You can find the translation file (PO) after downloading it under the\r\n    `youtube_dl_gui/locale/<LANG_CODE>/LC_MESSAGES/` directory.\r\n  * In order to get the translations from Transifex **your account needs permissions to access the project**.\r\n\r\n### Getting Started\r\n  1. Open a terminal\r\n  2. Test that Git works: `git --version`\r\n  3. Test that Transifex CLI client works: `tx --version`\r\n  4. Clone upstream using Git: `git clone https://github.com/MrS0m30n3/youtube-dl-gui`\r\n  5. Change to project directory: `cd youtube-dl-gui`\r\n  6. Pull the translation you want to test from Transifex (**Auth needed**): `tx pull --force -l <LANGUAGE_CODE_HERE>`\r\n  7. Make the language appear under **Options>General** tab (only for new languages):\r\n      1. Open the **optionsframe.py** under the **youtube_dl_gui** directory\r\n      2. Search for the **LOCALE_NAMES** attribute\r\n      3. Add the new language to it (in our example `('el_GR', 'Greek'),`)\r\n      4. Don't forget to save your changes\r\n\r\n  ```python\r\n  LOCALE_NAMES = twodict([\r\n    + ('el_GR', 'Greek'),   # language_code, language_name\r\n      ('ar_SA', 'Arabic'),\r\n      ('cs_CZ', 'Czech'),\r\n        ...\r\n  ```\r\n  8. Build the binary translation files (MO): `python setup.py build_trans`\r\n  9. Test the translations by running youtube-dl-gui and selecting your language: `python -m youtube_dl_gui`\r\n  10. Make changes locally in your translation file (PO) and go back to step 8 to test them\r\n\r\n### Help\r\n  * [ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)\r\n  * [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)\r\n  * [Command line basics Linux](https://lifehacker.com/5633909/who-needs-a-mouse-learn-to-use-the-command-line-for-almost-anything)\r\n\r\n## Devs\r\n\r\n### Requirements\r\n  * See [Testers](localization_howto.md#testers) requirements\r\n\r\n### Notes\r\n  * Read [Testers](localization_howto.md#testers) notes first.\r\n  * Binary translation files (MO) are ignored and you should not push them upstream.\r\n  * Don't forget to update the [ChangeLog](../ChangeLog) after adding a new language.\r\n  * You can gather all extra requirements below using **pip**.\r\n\r\n### Getting Started\r\n\r\n#### Add a new language under Options>General tab\r\n  1. Open the **optionsframe.py** file\r\n  2. Search for the **LOCALE_NAMES** attribute\r\n  3. Add the new language to it and make sure to **sort alphabetically** based on the language name\r\n\r\n  ```python\r\n  LOCALE_NAMES = twodict([\r\n    ('en_US', 'English'),\r\n    ('fr_FR', 'French'),\r\n  + ('el_GR', 'Greek'),\r\n    ('it_IT', 'Italian'),\r\n        ...\r\n  ```\r\n\r\n#### Build the binary translation files (MO)\r\n  1. Just run the setup script: `python setup.py build_trans`\r\n\r\n#### Automatically check translations using Google translate (Requires: polib & doodle_translate)\r\n  1. Change directory to `devscripts`\r\n  2. Run the check script: `python check-translation.py <LANGUAGE_CODE_HERE>`\r\n\r\n#### Get translations from Transifex (Requires: Permissions to access project)\r\n  * Pull everything: `tx pull -a`\r\n  * Pull reviewed: `tx pull --mode reviewed -a`\r\n  * Pull everything (force): `tx pull -a -f`\r\n  * Pull specific language: `tx pull -l <LANGUAGE_CODE_HERE>`\r\n  * Pull only completed translations (100%): `tx pull -a --minimum-perc=100`\r\n\r\n#### Update source strings (Only Maintainers, Requires: python-gettext)\r\n  1. Change directory to `devscripts`\r\n  2. Run the `update-locales.sh` script (also builds MO files)\r\n  3. Push changes to Transifex: `tx push --source --translations`\r\n\r\n#### Add support for new language locally (DEPRECATED, ONLY TESTING)\r\n  1. Change directory to `devscripts`\r\n  2. Run the new locale script: `python new-locale.py <LANGUAGE_CODE_HERE>`\r\n\r\n### Help\r\n  * [ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)\r\n  * [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)\r\n  * [PO file headers](https://www.gnu.org/software/gettext/manual/html_node/Header-Entry.html)\r\n  * [GNU gettext manual](https://www.gnu.org/software/gettext/manual/html_node/index.html#SEC_Contents)\r\n  * Transifex [user roles](https://docs.transifex.com/teams/understanding-user-roles)\r\n  * Transifex [CLI client introduction](https://docs.transifex.com/client/introduction)\r\n\r\n## FAQs\r\n\r\n* **Translations unnecessarily having country codes?**:\r\nSome languages have dialects in different countries. For example, `de_AT` is used for Austria, and `pt_BR` for Brazil. The country code serves to distinguish the dialects. Also, using a single format (*ll_CC*) instead of multiple for the locale name simplifies some implementation specific things.\r\n"
  },
  {
    "path": "setup.py",
    "content": "#!/usr/bin/env python2\n# -*- coding: utf-8 -*-\n\n\"\"\"Youtube-dlg setup file.\n\nExamples:\n    Windows::\n\n        python setup.py py2exe\n\n    Linux::\n\n        python setup.py install\n\n    Build source distribution::\n\n        python setup.py sdist\n\n    Build platform distribution::\n\n        python setup.py bdist\n\n    Build the translations::\n\n        python setup.py build_trans\n\n    Build with updates disabled::\n\n        python setup.py build --no-updates\n\nRequirements:\n\n    * GNU gettext utilities\n\nNotes:\n    If you get 'TypeError: decoding Unicode is not supported' when you run\n    py2exe then apply the following patch::\n\n        http://sourceforge.net/p/py2exe/patches/28/\n\n    Basic steps of the setup::\n\n        * Run pre-build tasks\n        * Call setup handler based on OS & options\n            * Set up hicolor icons (if supported by platform)\n            * Set up fallback pixmaps icon (if supported by platform)\n            * Set up package level pixmaps icons (*.png)\n            * Set up package level i18n files (*.mo)\n            * Set up scripts (executables) (if supported by platform)\n        * Run setup\n\n\"\"\"\n\nfrom distutils import cmd, log\nfrom distutils.core import setup\nfrom distutils.command.build import build\n\nimport os\nimport sys\nimport glob\nfrom shutil import copyfile\nfrom subprocess import call\n\nPY2EXE = len(sys.argv) >= 2 and sys.argv[1] == \"py2exe\"\n\nif PY2EXE:\n    try:\n        import py2exe\n    except ImportError as error:\n        print(error)\n        sys.exit(1)\n\nfrom youtube_dl_gui import (\n    __author__,\n    __appname__,\n    __contact__,\n    __version__,\n    __license__,\n    __projecturl__,\n    __description__,\n    __packagename__,\n    __descriptionfull__\n)\n\n# Setup can not handle unicode\n__packagename__ = str(__packagename__)\n\n\ndef on_windows():\n    \"\"\"Returns True if OS is Windows.\"\"\"\n    return os.name == \"nt\"\n\n\nclass BuildBin(cmd.Command):\n\n    description = \"build the youtube-dl-gui binary file\"\n    user_options = []\n\n    def initialize_options(self):\n        self.scripts_dir = None\n\n    def finalize_options(self):\n        self.scripts_dir = os.path.join(\"build\", \"_scripts\")\n\n    def run(self):\n        if not os.path.exists(self.scripts_dir):\n            os.makedirs(self.scripts_dir)\n\n        copyfile(os.path.join(__packagename__, \"__main__.py\"),\n                 os.path.join(self.scripts_dir, \"youtube-dl-gui\"))\n\n\nclass BuildTranslations(cmd.Command):\n\n    description = \"build the translation files\"\n    user_options = []\n\n    def initialize_options(self):\n        self.exec_name = None\n        self.search_pattern = None\n\n    def finalize_options(self):\n        if on_windows():\n            self.exec_name = \"msgfmt.exe\"\n        else:\n            self.exec_name = \"msgfmt\"\n\n        self.search_pattern = os.path.join(__packagename__, \"locale\", \"*\", \"LC_MESSAGES\", \"youtube_dl_gui.po\")\n\n    def run(self):\n        for po_file in glob.glob(self.search_pattern):\n            mo_file = po_file.replace(\".po\", \".mo\")\n\n            try:\n                log.info(\"building MO file for '{}'\".format(po_file))\n                call([self.exec_name, \"-o\", mo_file, po_file])\n            except OSError:\n                log.error(\"could not locate file '{}', exiting...\".format(self.exec_name))\n                sys.exit(1)\n\n\nclass Build(build):\n\n    \"\"\"Overwrite the default 'build' behaviour.\"\"\"\n\n    sub_commands = [\n        (\"build_bin\", None),\n        (\"build_trans\", None)\n    ] + build.sub_commands\n\n    build.user_options.append((\"no-updates\", None, \"build with updates disabled\"))\n\n    def initialize_options(self):\n        build.initialize_options(self)\n        self.no_updates = None\n\n    def run(self):\n        build.run(self)\n\n        if self.no_updates:\n            self.__disable_updates()\n\n    def __disable_updates(self):\n        lib_dir = os.path.join(self.build_lib, __packagename__)\n        target_file = \"optionsmanager.py\"\n\n        # Options file should be available from previous build commands\n        optionsfile = os.path.join(lib_dir, target_file)\n        data = None\n\n        with open(optionsfile, \"r\") as input_file:\n            data = input_file.readlines()\n\n        if data is None:\n            log.error(\"building with updates disabled failed!\")\n            sys.exit(1)\n\n        for index, line in enumerate(data):\n            if \"'disable_update': False\" in line:\n                log.info(\"disabling updates\")\n                data[index] = line.replace(\"False\", \"True\")\n                break\n\n        with open(optionsfile, \"w\") as output_file:\n            output_file.writelines(data)\n\n\n# Overwrite cmds\ncmdclass = {\n    \"build\": Build,\n    \"build_bin\": BuildBin,\n    \"build_trans\": BuildTranslations\n}\n\n\ndef linux_setup():\n    scripts = []\n    data_files = []\n    package_data = {}\n\n    # Add hicolor icons\n    for path in glob.glob(\"youtube_dl_gui/data/icons/hicolor/*x*\"):\n        size = os.path.basename(path)\n\n        dst = \"share/icons/hicolor/{size}/apps\".format(size=size)\n        src = \"{icon_path}/apps/youtube-dl-gui.png\".format(icon_path=path)\n\n        data_files.append((dst, [src]))\n\n    # Add fallback icon, see issue #14\n    data_files.append(\n        (\"share/pixmaps\", [\"youtube_dl_gui/data/pixmaps/youtube-dl-gui.png\"])\n    )\n\n    # Add man page\n    data_files.append(\n        (\"share/man/man1\", [\"youtube-dl-gui.1\"])\n    )\n\n    # Add pixmaps icons (*.png) & i18n files\n    package_data[__packagename__] = [\n        \"data/pixmaps/*.png\",\n        \"locale/*/LC_MESSAGES/*.mo\"\n    ]\n\n    # Add scripts\n    scripts.append(\"build/_scripts/youtube-dl-gui\")\n\n    setup_params = {\n        \"scripts\": scripts,\n        \"data_files\": data_files,\n        \"package_data\": package_data\n    }\n\n    return setup_params\n\n\ndef windows_setup():\n    def normal_setup():\n        package_data = {}\n\n        # Add pixmaps icons (*.png) & i18n files\n        package_data[__packagename__] = [\n            \"data\\\\pixmaps\\\\*.png\",\n            \"locale\\\\*\\\\LC_MESSAGES\\\\*.mo\"\n        ]\n\n        setup_params = {\n            \"package_data\": package_data\n        }\n\n        return setup_params\n\n    def py2exe_setup():\n        windows = []\n        data_files = []\n\n        # py2exe dependencies & options\n        # TODO change directory for ffmpeg.exe & ffprobe.exe\n        dependencies = [\n            \"C:\\\\Windows\\\\System32\\\\ffmpeg.exe\",\n            \"C:\\\\Windows\\\\System32\\\\ffprobe.exe\",\n            \"C:\\\\python27\\\\DLLs\\\\MSVCP90.dll\"\n        ]\n\n        options = {\n            \"includes\": [\"wx.lib.pubsub.*\",\n                         \"wx.lib.pubsub.core.*\",\n                         \"wx.lib.pubsub.core.arg1.*\"]\n        }\n        #############################################\n\n        # Add py2exe deps & pixmaps icons (*.png)\n        data_files.extend([\n            (\"\", dependencies),\n            (\"data\\\\pixmaps\", glob.glob(\"youtube_dl_gui\\\\data\\\\pixmaps\\\\*.png\")),\n        ])\n\n        # We have to manually add the translation files since py2exe cant do it\n        for lang in os.listdir(\"youtube_dl_gui\\\\locale\"):\n            dst = os.path.join(\"locale\", lang, \"LC_MESSAGES\")\n            src = os.path.join(\"youtube_dl_gui\", dst, \"youtube_dl_gui.mo\")\n\n            data_files.append((dst, [src]))\n\n        # Add GUI executable details\n        windows.append({\n            \"script\": \"build\\\\_scripts\\\\youtube-dl-gui\",\n            \"icon_resources\": [(0, \"youtube_dl_gui\\\\data\\\\pixmaps\\\\youtube-dl-gui.ico\")]\n        })\n\n        setup_params = {\n            \"windows\": windows,\n            \"data_files\": data_files,\n            \"options\": {\"py2exe\": options}\n        }\n\n        return setup_params\n\n    if PY2EXE:\n        return py2exe_setup()\n\n    return normal_setup()\n\n\nif on_windows():\n    params = windows_setup()\nelse:\n    params = linux_setup()\n\nsetup(\n    author              = __author__,\n    name                = __appname__,\n    version             = __version__,\n    license             = __license__,\n    author_email        = __contact__,\n    url                 = __projecturl__,\n    description         = __description__,\n    long_description    = __descriptionfull__,\n    packages            = [__packagename__],\n    cmdclass            = cmdclass,\n\n    **params\n)\n"
  },
  {
    "path": "tests/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_ditem.py",
    "content": "#!/usr/bin/env python\n# -*- coding: UTF-8 -*-\n\n\"\"\"Contains test cases for the DownloadItem object.\"\"\"\n\nfrom __future__ import unicode_literals\n\nimport sys\nimport os.path\nimport unittest\n\nPATH = os.path.realpath(os.path.abspath(__file__))\nsys.path.insert(0, os.path.dirname(os.path.dirname(PATH)))\n\ntry:\n    from youtube_dl_gui.downloadmanager import DownloadItem\nexcept ImportError as error:\n    print error\n    sys.exit(1)\n\n\nclass TestItemInit(unittest.TestCase):\n\n    \"\"\"Test case for DownloadItem init.\"\"\"\n\n    def test_init(self):\n        url = \"url\"\n        options = [\"-f\", \"flv\"]\n\n        ditem = DownloadItem(url, options)\n\n        self.assertEqual(ditem.stage, \"Queued\")\n        self.assertEqual(ditem.url, url)\n        self.assertEqual(ditem.options, options)\n        self.assertEqual(ditem.object_id, hash(url + unicode(options)))\n\n        self.assertEqual(ditem.path, \"\")\n        self.assertEqual(ditem.filenames, [])\n        self.assertEqual(ditem.extensions, [])\n        self.assertEqual(ditem.filesizes, [])\n\n        self.assertEqual(\n            ditem.progress_stats,\n            {\"filename\": url,\n             \"extension\": \"-\",\n             \"filesize\": \"-\",\n             \"percent\": \"0%\",\n             \"speed\": \"-\",\n             \"eta\": \"-\",\n             \"status\": \"Queued\",\n             \"playlist_size\": \"\",\n             \"playlist_index\": \"\"}\n        )\n\n\nclass TestGetFiles(unittest.TestCase):\n\n    \"\"\"Test case for DownloadItem get_files method.\"\"\"\n\n    def setUp(self):\n        self.ditem = DownloadItem(\"url\", [\"-f\", \"flv\"])\n\n    def test_get_files(self):\n        path = os.path.join(\"/home\", \"user\", \"downloads\")\n\n        self.ditem.path = path\n        self.ditem.filenames = [\"file1\", \"file2\"]\n        self.ditem.extensions = [\".mp4\", \".m4a\"]\n\n        self.assertEqual(self.ditem.get_files(), [os.path.join(path, \"file1\" + \".mp4\"), os.path.join(path, \"file2\" + \".m4a\")])\n\n    def test_get_files_no_data(self):\n        self.assertEqual(self.ditem.get_files(), [])\n\n\nclass TestItemComparison(unittest.TestCase):\n\n    \"\"\"Test case for DownloadItem __eq__ method.\"\"\"\n\n    def test_equal_true(self):\n        ditem1 = DownloadItem(\"url\", [\"-f\", \"flv\"])\n        ditem2 = DownloadItem(\"url\", [\"-f\", \"flv\"])\n\n        self.assertTrue(ditem1 == ditem2)\n\n    def test_equal_false(self):\n        ditem1 = DownloadItem(\"url\", [\"-f\", \"flv\"])\n        ditem2 = DownloadItem(\"url2\", [\"-f\", \"flv\"])\n\n        self.assertFalse(ditem1 == ditem2)\n\n        ditem1 = DownloadItem(\"url\", [\"-f\", \"flv\"])\n        ditem2 = DownloadItem(\"url\", [\"-f\", \"mp4\"])\n\n        self.assertFalse(ditem1 == ditem2)\n\n\nclass TestSetItemStage(unittest.TestCase):\n\n    \"\"\"Test case for DownloadItem stage setter.\"\"\"\n\n    def setUp(self):\n        self.ditem = DownloadItem(\"url\", [\"-f\", \"flv\"])\n\n    def test_set_stage_valid(self):\n        self.ditem.stage = \"Queued\"\n        self.assertEqual(self.ditem.stage, \"Queued\")\n        self.assertEqual(self.ditem.progress_stats[\"status\"], \"Queued\")\n\n        self.ditem.stage = \"Active\"\n        self.assertEqual(self.ditem.stage, \"Active\")\n        self.assertEqual(self.ditem.progress_stats[\"status\"], \"Pre Processing\")\n\n        self.ditem.stage = \"Completed\"\n        self.assertEqual(self.ditem.stage, \"Completed\")\n        self.assertEqual(self.ditem.progress_stats[\"status\"], \"Finished\")\n\n        self.ditem.stage = \"Paused\"\n        self.assertEqual(self.ditem.stage, \"Paused\")\n        self.assertEqual(self.ditem.progress_stats[\"status\"], \"Paused\")\n\n        self.ditem.stage = \"Error\"\n        self.assertEqual(self.ditem.stage, \"Error\")\n        self.assertEqual(self.ditem.progress_stats[\"status\"], \"Error\")\n\n    def test_set_stage_invalid(self):\n        raised = False\n\n        try:\n            self.ditem.stage = \"some other status\"\n        except ValueError:\n            raised = True\n\n        self.assertTrue(raised)\n\n\nclass TestUpdateStats(unittest.TestCase):\n\n    \"\"\"Test case for DownloadItem update_stats method.\"\"\"\n\n    def setUp(self):\n        self.ditem = DownloadItem(\"url\", [\"-f\", \"flv\"])\n\n    def test_update_stats(self):\n        path = os.path.join(\"/home\", \"user\")\n\n        # 1st playlist item\n\n        self.ditem.update_stats({\"filename\": \"somefilename.f1\",\n                                 \"extension\": \".mp4\",\n                                 \"filesize\": \"9.45MiB\",\n                                 \"percent\": \"2.0%\",\n                                 \"speed\": \"200.00KiB/s\",\n                                 \"eta\": \"00:38\",\n                                 \"status\": \"Downloading\",\n                                 \"path\": path,\n                                 \"playlist_size\": \"10\",\n                                 \"playlist_index\": \"1\"})\n\n        self.assertEqual(self.ditem.path, path)\n        self.assertEqual(self.ditem.filenames, [\"somefilename.f1\"])\n        self.assertEqual(self.ditem.extensions, [\".mp4\"])\n\n        # Do not update filesizes unless percentage is 100%\n        # See https://github.com/MrS0m30n3/youtube-dl-gui/issues/162\n        self.assertEqual(self.ditem.filesizes, [])\n\n        self.assertEqual(\n            self.ditem.progress_stats,\n            {\"filename\": \"somefilename.f1\",\n             \"extension\": \".mp4\",\n             \"filesize\": \"9.45MiB\",\n             \"percent\": \"2.0%\",\n             \"speed\": \"200.00KiB/s\",\n             \"eta\": \"00:38\",\n             \"status\": \"Downloading\",\n             \"playlist_size\": \"10\",\n             \"playlist_index\": \"1\"}\n        )\n\n        # Since the percentage is 100% this should update the filesizes list\n        self.ditem.update_stats({\"filesize\": \"9.45MiB\",\n                                 \"percent\": \"100%\",\n                                 \"speed\": \"\",\n                                 \"eta\": \"\",\n                                 \"status\": \"Downloading\"})\n\n        self.assertEqual(self.ditem.filesizes, [9909043.20])\n\n\n        self.ditem.update_stats({\"filename\": \"somefilename.f2\",\n                                 \"extension\": \".m4a\",\n                                 \"filesize\": \"2.22MiB\",\n                                 \"percent\": \"33.0%\",\n                                 \"speed\": \"200.00KiB/s\",\n                                 \"eta\": \"00:20\",\n                                 \"status\": \"Downloading\",\n                                 \"path\": path})\n\n        self.assertEqual(self.ditem.path, path)\n        self.assertEqual(self.ditem.filenames, [\"somefilename.f1\", \"somefilename.f2\"])\n        self.assertEqual(self.ditem.extensions, [\".mp4\", \".m4a\"])\n        self.assertEqual(self.ditem.filesizes, [9909043.20])\n\n        self.assertEqual(\n            self.ditem.progress_stats,\n            {\"filename\": \"somefilename.f2\",\n             \"extension\": \".m4a\",\n             \"filesize\": \"2.22MiB\",\n             \"percent\": \"33.0%\",\n             \"speed\": \"200.00KiB/s\",\n             \"eta\": \"00:20\",\n             \"status\": \"Downloading\",\n             \"playlist_size\": \"10\",\n             \"playlist_index\": \"1\"}\n        )\n\n        # Since the percentage is 100% this should update the filesizes list\n        self.ditem.update_stats({\"filesize\": \"2.22MiB\",\n                                 \"percent\": \"100%\",\n                                 \"speed\": \"\",\n                                 \"eta\": \"\",\n                                 \"status\": \"Downloading\"})\n\n        self.assertEqual(self.ditem.filesizes, [9909043.20, 2327838.72])\n\n        # Moving to the 2nd playlist item\n\n        self.ditem.update_stats({\"filename\": \"someotherfilename.f1\",\n                                 \"extension\": \".mp4\",\n                                 \"filesize\": \"10.25MiB\",\n                                 \"percent\": \"50.0%\",\n                                 \"speed\": \"200.00KiB/s\",\n                                 \"eta\": \"00:38\",\n                                 \"status\": \"Downloading\",\n                                 \"path\": path,\n                                 \"playlist_size\": \"10\",\n                                 \"playlist_index\": \"2\"})\n\n        # We must reset filenames, extensions & filesizes lists when changing playlist index\n        # else the filesizes for the post processed files will be wrong\n        self.assertEqual(self.ditem.filenames, [\"someotherfilename.f1\"])\n        self.assertEqual(self.ditem.extensions, [\".mp4\"])\n        self.assertEqual(self.ditem.filesizes, [])\n\n        self.assertEqual(\n            self.ditem.progress_stats,\n            {\"filename\": \"someotherfilename.f1\",\n             \"extension\": \".mp4\",\n             \"filesize\": \"10.25MiB\",\n             \"percent\": \"50.0%\",\n             \"speed\": \"200.00KiB/s\",\n             \"eta\": \"00:38\",\n             \"status\": \"Downloading\",\n             \"playlist_size\": \"10\",\n             \"playlist_index\": \"2\"}\n        )\n\n        # Since the percentage is 100% this should update the filesizes list\n        self.ditem.update_stats({\"filesize\": \"10.25MiB\",\n                                 \"percent\": \"100%\",\n                                 \"speed\": \"\",\n                                 \"eta\": \"\",\n                                 \"status\": \"Downloading\"})\n\n        self.assertEqual(self.ditem.filesizes, [10747904.0])\n\n\n        self.ditem.update_stats({\"filename\": \"someotherfilename.f2\",\n                                 \"extension\": \".m4a\",\n                                 \"filesize\": \"3.33MiB\",\n                                 \"percent\": \"33.0%\",\n                                 \"speed\": \"200.00KiB/s\",\n                                 \"eta\": \"00:30\",\n                                 \"status\": \"Downloading\",\n                                 \"path\": path})\n\n        self.assertEqual(self.ditem.path, path)\n        self.assertEqual(self.ditem.filenames, [\"someotherfilename.f1\", \"someotherfilename.f2\"])\n        self.assertEqual(self.ditem.extensions, [\".mp4\", \".m4a\"])\n        self.assertEqual(self.ditem.filesizes, [10747904.0])\n\n        self.assertEqual(\n            self.ditem.progress_stats,\n            {\"filename\": \"someotherfilename.f2\",\n             \"extension\": \".m4a\",\n             \"filesize\": \"3.33MiB\",\n             \"percent\": \"33.0%\",\n             \"speed\": \"200.00KiB/s\",\n             \"eta\": \"00:30\",\n             \"status\": \"Downloading\",\n             \"playlist_size\": \"10\",\n             \"playlist_index\": \"2\"}\n        )\n\n        # Since the percentage is 100% this should update the filesizes list\n        self.ditem.update_stats({\"filesize\": \"3.33MiB\",\n                                 \"percent\": \"100%\",\n                                 \"speed\": \"\",\n                                 \"eta\": \"\",\n                                 \"status\": \"Downloading\"})\n\n        self.assertEqual(self.ditem.filesizes, [10747904.0, 3491758.08])\n\n        # Let's move to the 3rd playlist item\n\n        # Here we mimic youtube-dl's \"max downloads limit reached\"\n        # this line should not reset the filenames, extensions & filesizes lists\n        # since we will lose the ability to play the last playlist item\n        self.ditem.update_stats({\"status\": \"Downloading\",\n                                 \"playlist_size\": \"10\",\n                                 \"playlist_index\": \"3\"})\n\n        self.assertEqual(self.ditem.filenames, [\"someotherfilename.f1\", \"someotherfilename.f2\"])\n        self.assertEqual(self.ditem.extensions, [\".mp4\", \".m4a\"])\n        self.assertEqual(self.ditem.filesizes, [10747904.0, 3491758.08])\n        self.assertEqual(\n            self.ditem.progress_stats,\n            {\"filename\": \"someotherfilename.f2\",\n             \"extension\": \".m4a\",\n             \"filesize\": \"3.33MiB\",\n             \"percent\": \"100%\",\n             \"speed\": \"-\",\n             \"eta\": \"-\",\n             \"status\": \"Downloading\",\n             \"playlist_size\": \"10\",\n             \"playlist_index\": \"3\"}\n        )\n\n    def test_update_stats_invalid_input(self):\n        self.assertRaises(AssertionError, self.ditem.update_stats, [])\n\n    def test_update_stats_empty_strings(self):\n        self.ditem.update_stats({\"filename\": \"\",\n                                 \"extension\": \"\",\n                                 \"filesize\": \"\",\n                                 \"percent\": \"\",\n                                 \"speed\": \"\",\n                                 \"eta\": \"\",\n                                 \"status\": \"\",\n                                 \"playlist_size\": \"\",\n                                 \"playlist_index\": \"\"})\n\n        self.assertEqual(\n            self.ditem.progress_stats,\n            {\"filename\": \"url\",\n             \"extension\": \"-\",\n             \"filesize\": \"-\",\n             \"percent\": \"0%\",\n             \"speed\": \"-\",\n             \"eta\": \"-\",\n             \"status\": \"Queued\",\n             \"playlist_size\": \"\",\n             \"playlist_index\": \"\"}\n        )\n\n    def test_update_stats_not_string(self):\n        self.ditem.update_stats({\"filename\": None, \"status\": 1234, \"eta\": False})\n\n        self.assertEqual(self.ditem.progress_stats[\"filename\"], \"url\")\n        self.assertEqual(self.ditem.progress_stats[\"status\"], \"Queued\")\n        self.assertEqual(self.ditem.progress_stats[\"eta\"], \"-\")\n\n\nclass TestDownloadItemPrivate(unittest.TestCase):\n\n    \"\"\"Test case for private method of the DownloadItem.\"\"\"\n\n    def test_set_stage(self):\n        ditem = DownloadItem(\"url\", [\"-f\", \"flv\"])\n\n        active_status = [\"Pre Processing\", \"Downloading\", \"Post Processing\"]\n        complete_status = [\"Finished\", \"Warning\", \"Already Downloaded\"]\n        error_status = [\"Error\", \"Stopped\", \"Filesize Abort\"]\n\n        for status in active_status:\n            ditem._set_stage(status)\n            self.assertEqual(ditem.stage, \"Active\")\n\n        for status in complete_status:\n            ditem._set_stage(status)\n            self.assertEqual(ditem.stage, \"Completed\")\n\n        for status in error_status:\n            ditem._set_stage(status)\n            self.assertEqual(ditem.stage, \"Error\")\n\n    def test_calc_post_proc_size(self):\n        # REFACTOR Not an actual method\n        # should transfer to TestUpdateStats\n        ditem = DownloadItem(\"url\", [\"-f\", \"flv\"])\n\n        ditem.update_stats({\"filename\": \"file.f123\",\n                            \"extension\": \".webm\",\n                            \"filesize\": \"10.00MiB\",\n                            \"percent\": \"100%\",\n                            \"speed\": \"\",\n                            \"eta\": \"\",\n                            \"status\": \"Downloading\",\n                            \"path\": \"/home/user\"})\n\n        ditem.update_stats({\"filename\": \"file.f456\",\n                            \"extension\": \".m4a\",\n                            \"filesize\": \"3.45MiB\",\n                            \"percent\": \"100%\",\n                            \"speed\": \"\",\n                            \"eta\": \"\",\n                            \"status\": \"Downloading\",\n                            \"path\": \"/home/user\"})\n\n        # Mimic youtube-dl post process behaviour\n        ditem.update_stats({\"filename\": \"file\",\n                            \"extension\": \".webm\",\n                            \"percent\": \"100%\",\n                            \"speed\": \"\",\n                            \"eta\": \"\",\n                            \"status\": \"Post Processing\"})\n\n        self.assertEqual(ditem.filesizes, [10485760.00, 3617587.20, 14103347.20])\n\n        self.assertEqual(\n            ditem.progress_stats,\n            {\"filename\": \"file\",\n             \"extension\": \".webm\",\n             \"filesize\": \"13.45MiB\",\n             \"percent\": \"100%\",\n             \"speed\": \"-\",\n             \"eta\": \"-\",\n             \"status\": \"Post Processing\",\n             \"playlist_size\": \"\",\n             \"playlist_index\": \"\"}\n        )\n\n\nclass TestReset(unittest.TestCase):\n\n    \"\"\"Test case for the DownloadItem reset method.\"\"\"\n\n    def setUp(self):\n        self.ditem = DownloadItem(\"url\", [\"-f\", \"flv\"])\n\n    def test_reset_completed_stage(self):\n        self.ditem._stage = \"Completed\"\n        self.ditem.path = os.path.join(\"/home\", \"user\")\n        self.ditem.filenames = [\"file\"]\n        self.ditem.extensions = [\".mp4\"]\n        self.ditem.filesizes = [123456.00]\n        self.ditem.progress_stats = {\n            \"filename\": \"file\",\n            \"extension\": \".mp4\",\n            \"filsize\": \"6.66MiB\",\n            \"percent\": \"100%\",\n            \"speed\": \"-\",\n            \"eta\": \"-\",\n            \"status\": \"Finished\",\n            \"playlist_size\": \"\",\n            \"playlist_index\": \"\"\n        }\n\n        self.ditem.reset()\n\n        self.assertEqual(self.ditem._stage, \"Queued\")\n        self.assertEqual(self.ditem.path, \"\")\n        self.assertEqual(self.ditem.filenames, [])\n        self.assertEqual(self.ditem.extensions, [])\n        self.assertEqual(self.ditem.filesizes, [])\n        self.assertEqual(\n            self.ditem.progress_stats,\n            {\"filename\": \"url\",\n             \"extension\": \"-\",\n             \"filesize\": \"-\",\n             \"percent\": \"0%\",\n             \"speed\": \"-\",\n             \"eta\": \"-\",\n             \"status\": \"Queued\",\n             \"playlist_size\": \"\",\n             \"playlist_index\": \"\"}\n        )\n\n    def test_reset_error_stage(self):\n        self.ditem._stage = \"Error\"\n        self.ditem.path = os.path.join(\"/home\", \"user\")\n        self.ditem.filenames = [\"file1\", \"file2\", \"file\"]\n        self.ditem.extensions = [\".mp4\", \".m4a\", \".mp4\"]\n        self.ditem.filesizes = [1234.00, 3421.00, 4655.00]\n        self.ditem.progress_stats = {\n            \"filename\": \"file\",\n            \"extension\": \".mp4\",\n            \"filsize\": \"9.45MiB\",\n            \"percent\": \"100%\",\n            \"speed\": \"-\",\n            \"eta\": \"-\",\n            \"status\": \"Error\",\n            \"playlist_size\": \"10\",\n            \"playlist_index\": \"8\"\n        }\n\n        self.ditem.reset()\n\n        self.assertEqual(self.ditem._stage, \"Queued\")\n        self.assertEqual(self.ditem.path, \"\")\n        self.assertEqual(self.ditem.filenames, [])\n        self.assertEqual(self.ditem.extensions, [])\n        self.assertEqual(self.ditem.filesizes, [])\n        self.assertEqual(\n            self.ditem.progress_stats,\n            {\"filename\": \"url\",\n             \"extension\": \"-\",\n             \"filesize\": \"-\",\n             \"percent\": \"0%\",\n             \"speed\": \"-\",\n             \"eta\": \"-\",\n             \"status\": \"Queued\",\n             \"playlist_size\": \"\",\n             \"playlist_index\": \"\"}\n        )\n\n    def test_reset_paused_stage(self):\n        self.ditem._stage = \"Paused\"\n        # No need to change filanames, extension, etc\n        # since everything in pause state has the default value\n\n        self.ditem.reset()\n        self.assertEqual(self.ditem._stage, \"Queued\")\n\n    def test_reset_active_stage(self):\n        self.ditem._stage = \"Active\"\n        self.ditem.path = os.path.join(\"/home\", \"user\")\n        self.ditem.filenames = [\"file1\"]\n        self.ditem.extensions = [\".mp4\"]\n        self.ditem.filesizes = []\n        self.ditem.progress_stats = {\n            \"filename\": \"file1\",\n            \"extension\": \".mp4\",\n            \"filsize\": \"9.45MiB\",\n            \"percent\": \"75.5%\",\n            \"speed\": \"200.00KiB/s\",\n            \"eta\": \"00:10\",\n            \"status\": \"Downloading\"\n        }\n\n        self.assertRaises(RuntimeError, self.ditem.reset)\n\n\ndef main():\n    unittest.main()\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tests/test_dlist.py",
    "content": "#!/usr/bin/env python\n# -*- coding: UTF-8 -*-\n\n\"\"\"Contains test cases for the DownloadList object.\"\"\"\n\nfrom __future__ import unicode_literals\n\nimport sys\nimport os.path\nimport unittest\n\nPATH = os.path.realpath(os.path.abspath(__file__))\nsys.path.insert(0, os.path.dirname(os.path.dirname(PATH)))\n\ntry:\n    import mock\n    from youtube_dl_gui.downloadmanager import DownloadList, synchronized\nexcept ImportError as error:\n    print error\n    sys.exit(1)\n\n\nclass TestInit(unittest.TestCase):\n\n    \"\"\"Test case for the DownloadList init.\"\"\"\n\n    def test_init(self):\n        mocks = [mock.Mock(object_id=0), mock.Mock(object_id=1)]\n\n        dlist = DownloadList(mocks)\n        self.assertEqual(dlist._items_list, [0, 1])\n        self.assertEqual(dlist._items_dict, {0: mocks[0], 1: mocks[1]})\n\n    def test_init_empty(self):\n        dlist = DownloadList()\n        self.assertEqual(dlist._items_list, [])\n        self.assertEqual(dlist._items_dict, {})\n\n    def test_init_invalid_args(self):\n        self.assertRaises(AssertionError, DownloadList, {})\n        self.assertRaises(AssertionError, DownloadList, ())\n        self.assertRaises(AssertionError, DownloadList, False)\n\n\nclass TestInsert(unittest.TestCase):\n\n    \"\"\"Test case for the DownloadList insert method.\"\"\"\n\n    def test_insert(self):\n        mock_ditem = mock.Mock(object_id=0)\n\n        dlist = DownloadList()\n        dlist.insert(mock_ditem)\n\n        self.assertEqual(dlist._items_list, [0])\n        self.assertEqual(dlist._items_dict, {0: mock_ditem})\n\n\nclass TestRemove(unittest.TestCase):\n\n    \"\"\"Test case for the DownloadList remove method.\"\"\"\n\n    def setUp(self):\n        self.mocks = [mock.Mock(object_id=0), mock.Mock(object_id=1), mock.Mock(object_id=2)]\n        self.dlist = DownloadList(self.mocks)\n\n    def test_remove(self):\n        self.assertTrue(self.dlist.remove(1))\n\n        self.assertEqual(self.dlist._items_list, [0, 2])\n        self.assertEqual(self.dlist._items_dict, {0: self.mocks[0], 2: self.mocks[2]})\n\n    def test_remove_not_exist(self):\n        self.assertRaises(KeyError, self.dlist.remove, 3)\n\n    def test_remove_active(self):\n        self.mocks[1].stage = \"Active\"\n\n        self.assertFalse(self.dlist.remove(1))\n        self.assertEqual(self.dlist._items_list, [0, 1, 2])\n        self.assertEqual(self.dlist._items_dict, {0: self.mocks[0], 1: self.mocks[1], 2: self.mocks[2]})\n\n\nclass TestFetchNext(unittest.TestCase):\n\n    \"\"\"Test case for the DownloadList fetch_next method.\"\"\"\n\n    def test_fetch_next(self):\n        items_count = 3\n\n        mocks = [mock.Mock(object_id=i, stage=\"Queued\") for i in range(items_count)]\n\n        dlist = DownloadList(mocks)\n\n        for i in range(items_count):\n            self.assertEqual(dlist.fetch_next(), mocks[i])\n            mocks[i].stage = \"Active\"\n\n        self.assertIsNone(dlist.fetch_next())\n\n        for i in range(items_count):\n            mocks[i].stage = \"Completed\"\n\n        self.assertIsNone(dlist.fetch_next())\n\n        mocks[1].stage = \"Queued\"  # Re-queue item\n        self.assertEqual(dlist.fetch_next(), mocks[1])\n\n    def test_fetch_next_empty_list(self):\n        dlist = DownloadList()\n        self.assertIsNone(dlist.fetch_next())\n\n\nclass TestMoveUp(unittest.TestCase):\n\n    \"\"\"Test case for the DownloadList move_up method.\"\"\"\n\n    def setUp(self):\n        mocks = [mock.Mock(object_id=i, stage=\"Queued\") for i in range(3)]\n        self.dlist = DownloadList(mocks)\n\n    def test_move_up(self):\n        self.assertTrue(self.dlist.move_up(1))\n        self.assertEqual(self.dlist._items_list, [1, 0, 2])\n\n    def test_move_up_already_on_top(self):\n        self.assertFalse(self.dlist.move_up(0))\n        self.assertEqual(self.dlist._items_list, [0, 1, 2])\n\n    def test_move_up_not_exist(self):\n        self.assertRaises(ValueError, self.dlist.move_up, 666)\n\n\nclass TestMoveDown(unittest.TestCase):\n\n    \"\"\"Test case for the DownloadList move_down method.\"\"\"\n\n    def setUp(self):\n        mocks = [mock.Mock(object_id=i, stage=\"Queued\") for i in range(3)]\n        self.dlist = DownloadList(mocks)\n\n    def test_move_down(self):\n        self.assertTrue(self.dlist.move_down(1))\n        self.assertEqual(self.dlist._items_list, [0, 2, 1])\n\n    def test_move_down_already_on_bottom(self):\n        self.assertFalse(self.dlist.move_down(2))\n        self.assertEqual(self.dlist._items_list, [0, 1, 2])\n\n    def test_move_down_not_exist(self):\n        self.assertRaises(ValueError, self.dlist.move_down, 666)\n\n\nclass TestGetItem(unittest.TestCase):\n\n    \"\"\"Test case for the DownloadList get_item method.\"\"\"\n\n    def test_get_item(self):\n        mocks = [mock.Mock(object_id=i) for i in range(3)]\n        dlist = DownloadList(mocks)\n\n        self.assertEqual(dlist.get_item(0), mocks[0])\n        self.assertEqual(dlist.get_item(2), mocks[2])\n\n    def test_get_item_not_exist(self):\n        dlist = DownloadList()\n        self.assertRaises(KeyError, dlist.get_item, 0)\n\n\nclass TestGetLength(unittest.TestCase):\n\n    \"\"\"Test case for the DownloadList __len__ method.\"\"\"\n\n    def test_get_length(self):\n        dlist = DownloadList([mock.Mock(), mock.Mock()])\n        self.assertEqual(len(dlist), 2)\n\n    def test_get_length_empty_list(self):\n        dlist = DownloadList()\n        self.assertEqual(len(dlist), 0)\n\n\nclass TestHasItem(unittest.TestCase):\n\n    \"\"\"Test case for the DownloadList has_item method.\"\"\"\n\n    def setUp(self):\n        mock_ditem = mock.Mock(object_id=1337)\n        self.dlist = DownloadList([mock_ditem])\n\n    def test_has_item_true(self):\n        self.assertTrue(self.dlist.has_item(1337))\n\n    def test_has_item_false(self):\n        self.assertFalse(self.dlist.has_item(1000))\n\n\nclass TestGetItems(unittest.TestCase):\n\n    \"\"\"Test case for the DownloadList get_items method.\"\"\"\n\n    def test_get_items(self):\n        mocks = [mock.Mock() for _ in range(3)]\n        dlist = DownloadList(mocks)\n\n        self.assertEqual(dlist.get_items(), mocks)\n\n    def test_get_items_empty_list(self):\n        dlist = DownloadList()\n        self.assertEqual(dlist.get_items(), [])\n\n\nclass TestClear(unittest.TestCase):\n\n    \"\"\"Test case for the DownloadList clear method.\"\"\"\n\n    def test_clear(self):\n        dlist = DownloadList([mock.Mock() for _ in range(3)])\n\n        self.assertEqual(len(dlist), 3)\n        dlist.clear()\n        self.assertEqual(len(dlist), 0)\n\n\nclass TestChangeStage(unittest.TestCase):\n\n    \"\"\"Test case for the DownloadList change_stage method.\"\"\"\n\n    def setUp(self):\n        self.mocks = [mock.Mock(object_id=i, stage=\"Queued\") for i in range(3)]\n        self.dlist = DownloadList(self.mocks)\n\n    def test_change_stage(self):\n        self.dlist.change_stage(0, \"Active\")\n        self.assertEqual(self.mocks[0].stage, \"Active\")\n\n    def test_change_stage_id_not_exist(self):\n        self.assertRaises(KeyError, self.dlist.change_stage, 3, \"Active\")\n\n\nclass TestIndex(unittest.TestCase):\n\n    \"\"\"Test case for the DownloadList index method.\"\"\"\n\n    def setUp(self):\n        self.mocks = [mock.Mock(object_id=i) for i in range(3)]\n        self.dlist = DownloadList(self.mocks)\n\n    def test_index(self):\n        self.assertEqual(self.dlist.index(2), 2)\n\n    def test_index_not_exist(self):\n        self.assertEqual(self.dlist.index(3), -1)\n\n\nclass TestSynchronizeDecorator(unittest.TestCase):\n\n    def test_synchronize(self):\n        mock_func = mock.Mock()\n        mock_lock = mock.Mock()\n\n        decorated_func = synchronized(mock_lock)(mock_func)\n\n        self.assertEqual(decorated_func(1, a=2), mock_func.return_value)\n\n        mock_func.assert_called_once_with(1, a=2)\n        mock_lock.acquire.assert_called_once()\n        mock_lock.release.assert_called_once()\n\n\ndef main():\n    unittest.main()\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tests/test_parsers.py",
    "content": "#!/usr/bin/env python\n# -*- coding: UTF-8 -*-\n\n\"\"\"Contains test cases for the parsers module.\"\"\"\n\nfrom __future__ import unicode_literals\n\nimport sys\nimport os.path\nimport unittest\n\nPATH = os.path.realpath(os.path.abspath(__file__))\nsys.path.insert(0, os.path.dirname(os.path.dirname(PATH)))\n\ntry:\n    from youtube_dl_gui.parsers import OptionsParser\nexcept ImportError as error:\n    print error\n    sys.exit(1)\n\n\nclass TestParse(unittest.TestCase):\n\n    \"\"\"Test case for OptionsParser parse method.\"\"\"\n\n    def setUp(self):\n        # Create the options_dict based on the OptionHolder\n        # items inside the OptionsParser object\n        self.options_dict = {item.name:item.default_value for item in OptionsParser()._ydl_options}\n\n        # Add extra options used by the OptionsParser.parse method\n        self.options_dict[\"save_path\"] = \"/home/user/Workplace/test/youtube\"\n        self.options_dict[\"cmd_args\"] = \"\"\n        self.options_dict[\"output_format\"] = 1  # Title\n        self.options_dict[\"second_video_format\"] = \"0\"\n        self.options_dict[\"min_filesize_unit\"] = \"\"\n        self.options_dict[\"max_filesize_unit\"] = \"\"\n\n    def check_options_parse(self, expected_options):\n        options_parser = OptionsParser()\n\n        self.assertItemsEqual(options_parser.parse(self.options_dict), expected_options)\n\n    def test_parse_to_audio_requirement_bug(self):\n        \"\"\"Test case for the 'to_audio' requirement.\"\"\"\n\n        self.options_dict[\"audio_quality\"] = \"9\"\n        self.options_dict[\"audio_format\"] = \"mp3\"\n        self.options_dict[\"embed_thumbnail\"] = True\n\n        expected_cmd_list = [\"--newline\",\n                             \"-x\",\n                             \"--audio-format\",\n                             \"mp3\",\n                             \"--embed-thumbnail\",\n                             \"--audio-quality\",\n                             \"9\",\n                             \"-o\",\n                             \"/home/user/Workplace/test/youtube/%(title)s.%(ext)s\"]\n\n        self.check_options_parse(expected_cmd_list)\n\n        # Setting 'to_audio' to True should return the same results\n        # since the '-x' flag is already set on audio extraction\n        self.options_dict[\"to_audio\"] = True\n\n        self.check_options_parse(expected_cmd_list)\n\n    def test_parse_cmd_args_with_quotes(self):\n        \"\"\"Test the youtube-dl cmd line args parsing when quotes are presented.\n\n        See: https://github.com/MrS0m30n3/youtube-dl-gui/issues/54\n\n        \"\"\"\n\n        self.options_dict[\"video_format\"] = \"mp4\"\n\n        # Test with three quoted 'cmd_args'\n        self.options_dict[\"cmd_args\"] = \"--recode-video mkv --postprocessor-args \\\"-codec copy -report\\\"\"\n\n        expected_cmd_list = [\"--newline\",\n                             \"-f\",\n                             \"mp4\",\n                             \"-o\",\n                             \"/home/user/Workplace/test/youtube/%(title)s.%(ext)s\",\n                             \"--recode-video\",\n                             \"mkv\",\n                             \"--postprocessor-args\",\n                             \"-codec copy -report\"]\n\n        self.check_options_parse(expected_cmd_list)\n\n\n        # Test with two quoted 'cmd_args'\n        self.options_dict[\"cmd_args\"] = \"--postprocessor-args \\\"-y -report\\\"\"\n\n        expected_cmd_list = [\"--newline\",\n                             \"-f\",\n                             \"mp4\",\n                             \"-o\",\n                             \"/home/user/Workplace/test/youtube/%(title)s.%(ext)s\",\n                             \"--postprocessor-args\",\n                             \"-y -report\"]\n\n        self.check_options_parse(expected_cmd_list)\n\n\n        # Test with one quoted 'cmd_arg' followed by other cmd line args\n        self.options_dict[\"cmd_args\"] = \"--postprocessor-args \\\"-y\\\" -v\"\n\n        expected_cmd_list = [\"--newline\",\n                             \"-f\",\n                             \"mp4\",\n                             \"-o\",\n                             \"/home/user/Workplace/test/youtube/%(title)s.%(ext)s\",\n                             \"--postprocessor-args\",\n                             \"-y\",\n                             \"-v\"]\n\n        self.check_options_parse(expected_cmd_list)\n\n\n        # Test the example presented in issue #54\n        self.options_dict[\"cmd_args\"] = \"-f \\\"(mp4)[width<1300]\\\"\"\n        self.options_dict[\"video_format\"] = \"0\"  # Set video format to 'default'\n\n        expected_cmd_list = [\"--newline\",\n                             \"-o\",\n                             \"/home/user/Workplace/test/youtube/%(title)s.%(ext)s\",\n                             \"-f\",\n                             \"(mp4)[width<1300]\"]\n\n        self.check_options_parse(expected_cmd_list)\n\n\ndef main():\n    unittest.main()\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tests/test_utils.py",
    "content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\"Contains test cases for the utils.py module.\"\"\"\n\nfrom __future__ import unicode_literals\n\nimport sys\nimport os.path\nimport unittest\n\nPATH = os.path.realpath(os.path.abspath(__file__))\nsys.path.insert(0, os.path.dirname(os.path.dirname(PATH)))\n\ntry:\n    import mock\n\n    from youtube_dl_gui import utils\nexcept ImportError as error:\n    print error\n    sys.exit(1)\n\n\nclass TestToBytes(unittest.TestCase):\n\n    \"\"\"Test case for the to_bytes method.\"\"\"\n\n    def test_to_bytes_bytes(self):\n        self.assertEqual(utils.to_bytes(\"596.00B\"), 596.00)\n        self.assertEqual(utils.to_bytes(\"133.55B\"), 133.55)\n\n    def test_to_bytes_kilobytes(self):\n        self.assertEqual(utils.to_bytes(\"1.00KiB\"), 1024.00)\n        self.assertEqual(utils.to_bytes(\"5.55KiB\"), 5683.20)\n\n    def test_to_bytes_megabytes(self):\n        self.assertEqual(utils.to_bytes(\"13.64MiB\"), 14302576.64)\n        self.assertEqual(utils.to_bytes(\"1.00MiB\"), 1048576.00)\n\n    def test_to_bytes_gigabytes(self):\n        self.assertEqual(utils.to_bytes(\"1.00GiB\"), 1073741824.00)\n        self.assertEqual(utils.to_bytes(\"1.55GiB\"), 1664299827.20)\n\n    def test_to_bytes_terabytes(self):\n        self.assertEqual(utils.to_bytes(\"1.00TiB\"), 1099511627776.00)\n\n\nclass TestFormatBytes(unittest.TestCase):\n\n    \"\"\"Test case for the format_bytes method.\"\"\"\n\n    def test_format_bytes_bytes(self):\n        self.assertEqual(utils.format_bytes(518.00), \"518.00B\")\n\n    def test_format_bytes_kilobytes(self):\n        self.assertEqual(utils.format_bytes(1024.00), \"1.00KiB\")\n\n    def test_format_bytes_megabytes(self):\n        self.assertEqual(utils.format_bytes(1048576.00), \"1.00MiB\")\n\n    def test_format_bytes_gigabytes(self):\n        self.assertEqual(utils.format_bytes(1073741824.00), \"1.00GiB\")\n\n    def test_format_bytes_terabytes(self):\n        self.assertEqual(utils.format_bytes(1099511627776.00), \"1.00TiB\")\n\n\nclass TestBuildCommand(unittest.TestCase):\n\n    \"\"\"Test case for the build_command method.\"\"\"\n\n    def setUp(self):\n        self.url = \"https://www.youtube.com/watch?v=aaaaaaaaaaa&list=AAAAAAAAAAA\"\n\n        self.options = [\"-o\", None, \"-f\", \"mp4\", \"--ignore-config\"]\n\n        self.result = \"{{ydl_bin}} -o \\\"{{tmpl}}\\\" -f mp4 --ignore-config \\\"{url}\\\"\".format(url=self.url)\n\n    def run_tests(self, ydl_bin, tmpl):\n        \"\"\"Run the main test.\n\n        Args:\n            ydl_bin (str): Name of the youtube-dl binary\n            tmpl (str): Youtube-dl output template\n\n        \"\"\"\n        utils.YOUTUBEDL_BIN = ydl_bin\n        self.options[1] = tmpl  # Plug the template in our options\n\n        result = self.result.format(ydl_bin=ydl_bin, tmpl=tmpl)\n\n        self.assertEqual(utils.build_command(self.options, self.url), result)\n\n    def test_build_command_with_spaces_linux(self):\n        tmpl = \"/home/user/downloads/%(upload_date)s/%(id)s_%(playlist_id)s - %(format)s.%(ext)s\"\n\n        self.run_tests(\"youtube-dl\", tmpl)\n\n    def test_build_command_without_spaces_linux(self):\n        tmpl = \"/home/user/downloads/%(id)s.%(ext)s\"\n\n        self.run_tests(\"youtube-dl\", tmpl)\n\n    def test_build_command_with_spaces_windows(self):\n        tmpl = \"C:\\\\downloads\\\\%(upload_date)s\\\\%(id)s_%(playlist_id)s - %(format)s.%(ext)s\"\n\n        self.run_tests(\"youtube-dl.exe\", tmpl)\n\n    def test_build_command_without_spaces_windows(self):\n        tmpl = \"C:\\\\downloads\\\\%(id)s.%(ext)s\"\n\n        self.run_tests(\"youtube-dl.exe\", tmpl)\n\n\nclass TestConvertItem(unittest.TestCase):\n\n    \"\"\"Test case for the convert_item function.\"\"\"\n\n    def setUp(self):\n        self.input_list_u = [\"v1\", \"v2\", \"v3\"]\n        self.input_list_s = [str(\"v1\"), str(\"v2\"), str(\"v3\")]\n\n        self.input_tuple_u = (\"v1\", \"v2\", \"v3\")\n        self.input_tuple_s = (str(\"v1\"), str(\"v2\"), str(\"v3\"))\n\n        self.input_dict_u = {\"k1\": \"v1\", \"k2\": \"v2\"}\n        self.input_dict_s = {str(\"k1\"): str(\"v1\"), str(\"k2\"): str(\"v2\")}\n\n    def check_iter(self, iterable, iter_type, is_unicode):\n        check_type = unicode if is_unicode else str\n\n        iterable = utils.convert_item(iterable, is_unicode)\n\n        self.assertIsInstance(iterable, iter_type)\n\n        for item in iterable:\n            if iter_type == dict:\n                self.assertIsInstance(iterable[item], check_type)\n\n            self.assertIsInstance(item, check_type)\n\n    def test_convert_item_unicode_str(self):\n        self.assertIsInstance(utils.convert_item(\"test\"), str)\n\n    def test_convert_item_unicode_unicode(self):\n        self.assertIsInstance(utils.convert_item(\"test\", True), unicode)\n\n    def test_convert_item_str_unicode(self):\n        self.assertIsInstance(utils.convert_item(str(\"test\"), True), unicode)\n\n    def test_convert_item_str_str(self):\n        self.assertIsInstance(utils.convert_item(str(\"test\")), str)\n\n    def test_convert_item_list_empty(self):\n        self.assertEqual(len(utils.convert_item([])), 0)\n\n    def test_convert_item_dict_empty(self):\n        self.assertEqual(len(utils.convert_item({})), 0)\n\n    def test_convert_item_list_unicode_str(self):\n        self.check_iter(self.input_list_u, list, False)\n\n    def test_convert_item_list_str_unicode(self):\n        self.check_iter(self.input_list_s, list, True)\n\n    def test_convert_item_tuple_unicode_str(self):\n        self.check_iter(self.input_tuple_u, tuple, False)\n\n    def test_convert_item_tuple_str_unicode(self):\n        self.check_iter(self.input_tuple_s, tuple, True)\n\n    def test_convert_item_dict_unicode_str(self):\n        self.check_iter(self.input_dict_u, dict, False)\n\n    def test_convert_item_dict_str_unicode(self):\n        self.check_iter(self.input_dict_s, dict, True)\n\n\nclass TestGetDefaultLang(unittest.TestCase):\n\n    \"\"\"Test case for the get_default_lang function.\"\"\"\n\n    @mock.patch(\"youtube_dl_gui.utils.locale_getdefaultlocale\")\n    def run_tests(self, ret_value, result, mock_getdefaultlocale):\n        \"\"\"Run the main test.\n\n        Args:\n            ret_value (tuple): Return tuple of the locale.getdefaultlocale module\n            result (unicode): Result we want to see\n            mock_getdefaultlocale (MagicMock): Mock object\n        \"\"\"\n        mock_getdefaultlocale.return_value = ret_value\n        lang = utils.get_default_lang()\n\n        mock_getdefaultlocale.assert_called_once()\n        self.assertEqual(lang, result)\n\n    def test_get_default_lang(self):\n        self.run_tests((\"it_IT\", \"UTF-8\"), \"it_IT\")\n\n    def test_get_default_lang_none(self):\n        self.run_tests((None, None), \"en_US\")\n\n    def test_get_default_lang_empty(self):\n        self.run_tests((\"\", \"\"), \"en_US\")\n\n\ndef main():\n    unittest.main()\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "tests/test_widgets.py",
    "content": "#!/usr/bin/env python\n# -*- coding: UTF-8 -*-\n\n\"\"\"Contains test cases for the widgets.py module.\"\"\"\n\nfrom __future__ import unicode_literals\n\nimport sys\nimport os.path\nimport unittest\n\nPATH = os.path.realpath(os.path.abspath(__file__))\nsys.path.insert(0, os.path.dirname(os.path.dirname(PATH)))\n\n\ntry:\n    import wx\n    import mock\n\n    from youtube_dl_gui.widgets import (\n        ListBoxWithHeaders,\n        CustomComboBox,\n        ListBoxPopup\n    )\nexcept ImportError as error:\n    print error\n    sys.exit(1)\n\n\nclass TestListBoxWithHeaders(unittest.TestCase):\n\n    \"\"\"Test cases for the ListBoxWithHeaders widget.\"\"\"\n\n    def setUp(self):\n        self.app = wx.App()\n\n        self.frame = wx.Frame(None)\n        self.listbox = ListBoxWithHeaders(self.frame)\n\n        self.listbox.add_header(\"Header\")\n        self.listbox.add_items([\"item%s\" % i for i in xrange(10)])\n\n    def tearDown(self):\n        self.frame.Destroy()\n\n    def test_find_string_header_found(self):\n        self.assertEqual(self.listbox.FindString(\"Header\"), 0)\n\n    def test_find_string_header_not_found(self):\n        self.assertEqual(self.listbox.FindString(\"Header2\"), wx.NOT_FOUND)\n\n    def test_find_string_item_found(self):\n        self.assertEqual(self.listbox.FindString(\"item1\"), 2)\n\n    def test_find_string_item_not_found(self):\n        self.assertEqual(self.listbox.FindString(\"item\"), wx.NOT_FOUND)\n\n    def test_get_string_header(self):\n        self.assertEqual(self.listbox.GetString(0), \"Header\")\n\n    def test_get_string_item(self):\n        self.assertEqual(self.listbox.GetString(10), \"item9\")\n\n    def test_get_string_item_not_found(self):\n        self.assertEqual(self.listbox.GetString(11), \"\")\n\n    def test_get_string_item_negative_index(self):\n        self.assertEqual(self.listbox.GetString(-1), \"\")\n\n    def test_insert_items(self):\n        self.listbox.SetSelection(1)\n\n        self.listbox.InsertItems([\"new_item1\", \"new_item2\"], 1)\n        self.assertEqual(self.listbox.GetString(1), \"new_item1\")\n        self.assertEqual(self.listbox.GetString(2), \"new_item2\")\n        self.assertEqual(self.listbox.GetString(3), \"item0\")\n\n        self.assertTrue(self.listbox.IsSelected(3))  # Old selection + 2\n\n    def test_set_selection_header(self):\n        self.listbox.SetSelection(0)\n        self.assertFalse(self.listbox.IsSelected(0))\n\n    def test_set_selection_item_valid_index(self):\n        self.listbox.SetSelection(1)\n        self.assertEqual(self.listbox.GetSelection(), 1)\n\n    def test_set_selection_item_invalid_index(self):\n        self.listbox.SetSelection(1)\n        self.assertEqual(self.listbox.GetSelection(), 1)\n\n        self.listbox.SetSelection(wx.NOT_FOUND)\n        self.assertEqual(self.listbox.GetSelection(), wx.NOT_FOUND)\n\n    def test_set_string_item(self):\n        self.listbox.SetString(1, \"item_mod0\")\n        self.assertEqual(self.listbox.GetString(1), \"item_mod0\")\n\n    def test_set_string_header(self):\n        self.listbox.SetString(0, \"New header\")\n        self.assertEqual(self.listbox.GetString(0), \"New header\")\n\n        # Make sure that the header is not selectable\n        self.listbox.SetSelection(0)\n        self.assertFalse(self.listbox.IsSelected(0))\n\n    def test_set_string_selection_header(self):\n        self.assertFalse(self.listbox.SetStringSelection(\"Header\"))\n        self.assertFalse(self.listbox.IsSelected(0))\n\n    def test_set_string_selection_item(self):\n        self.assertTrue(self.listbox.SetStringSelection(\"item1\"))\n        self.assertTrue(self.listbox.IsSelected(2))\n\n    def test_get_string_selection(self):\n        self.listbox.SetSelection(1)\n        self.assertEqual(self.listbox.GetStringSelection(), \"item0\")\n\n    def test_get_string_selection_empty(self):\n        self.assertEqual(self.listbox.GetStringSelection(), \"\")\n\n    # wx.ItemContainer methods\n\n    def test_append(self):\n        self.listbox.Append(\"item666\")\n        self.assertEqual(self.listbox.GetString(11), \"item666\")\n\n    def test_append_items(self):\n        self.listbox.AppendItems([\"new_item1\", \"new_item2\"])\n        self.assertEqual(self.listbox.GetString(11), \"new_item1\")\n        self.assertEqual(self.listbox.GetString(12), \"new_item2\")\n\n    def test_clear(self):\n        self.listbox.Clear()\n        self.assertEqual(self.listbox.GetItems(), [])\n\n    def test_delete(self):\n        self.listbox.Delete(0)\n        self.assertEqual(self.listbox.GetString(0), \"item0\")\n\n        # Test item selection\n        self.listbox.SetSelection(0)\n        self.assertTrue(self.listbox.IsSelected(0))\n\n    # Test object extra methods\n\n    def test_add_header(self):\n        self.listbox.add_header(\"Header2\")\n        self.listbox.SetSelection(11)\n        self.assertFalse(self.listbox.IsSelected(11))\n\n    @mock.patch(\"wx.ListBox.Append\")\n    def test_add_item_with_prefix(self, mock_append):\n        self.listbox.add_item(\"new_item\")\n        mock_append.assert_called_once_with(ListBoxWithHeaders.TEXT_PREFIX + \"new_item\")\n\n    @mock.patch(\"wx.ListBox.Append\")\n    def test_add_item_without_prefix(self, mock_append):\n        self.listbox.add_item(\"new_item\", with_prefix=False)\n        mock_append.assert_called_once_with(\"new_item\")\n\n    @mock.patch(\"wx.ListBox.AppendItems\")\n    def test_add_items_with_prefix(self, mock_append):\n        self.listbox.add_items([\"new_item1\", \"new_item2\"])\n        mock_append.assert_called_once_with([ListBoxWithHeaders.TEXT_PREFIX + \"new_item1\",\n                                             ListBoxWithHeaders.TEXT_PREFIX + \"new_item2\"])\n    @mock.patch(\"wx.ListBox.AppendItems\")\n    def test_add_items_without_prefix(self, mock_append):\n        self.listbox.add_items([\"new_item1\", \"new_item2\"], with_prefix=False)\n        mock_append.assert_called_once_with([\"new_item1\", \"new_item2\"])\n\n\nclass TestCustomComboBox(unittest.TestCase):\n\n    \"\"\"Test cases for the CustomComboBox widget.\"\"\"\n\n    def setUp(self):\n        self.app = wx.App()\n\n        self.frame = wx.Frame(None)\n        self.combobox = CustomComboBox(self.frame)\n\n        # Call directly the ListBoxWithHeaders methods\n        self.combobox.listbox.GetControl().add_header(\"Header\")\n        self.combobox.listbox.GetControl().add_items([\"item%s\" % i for i in xrange(10)])\n\n    def tearDown(self):\n        self.frame.Destroy()\n\n    def test_init(self):\n        combobox = CustomComboBox(self.frame, -1, \"item1\", choices=[\"item0\", \"item1\", \"item2\"])\n\n        self.assertEqual(combobox.GetValue(), \"item1\")\n        self.assertEqual(combobox.GetCount(), 3)\n        self.assertEqual(combobox.GetSelection(), 1)\n\n    # wx.ComboBox methods\n    # Not all of them since most of them are calls to ListBoxWithHeaders\n    # methods and we already have tests for those\n\n    def test_is_list_empty_false(self):\n        self.assertFalse(self.combobox.IsListEmpty())\n\n    def test_is_list_empty_true(self):\n        self.combobox.Clear()\n        self.assertTrue(self.combobox.IsListEmpty())\n\n    def test_is_text_empty_false(self):\n        self.combobox.SetValue(\"somevalue\")\n        self.assertFalse(self.combobox.IsTextEmpty())\n\n    def test_is_text_empty_true(self):\n        self.assertTrue(self.combobox.IsTextEmpty())\n\n    def test_set_selection_item(self):\n        self.combobox.SetSelection(1)\n        self.assertEqual(self.combobox.GetSelection(), 1)\n        self.assertEqual(self.combobox.GetValue(), \"item0\")\n\n    def test_set_selection_header(self):\n        self.combobox.SetSelection(0)\n        self.assertEqual(self.combobox.GetSelection(), wx.NOT_FOUND)\n        self.assertEqual(self.combobox.GetValue(), \"\")\n\n    def test_set_string_selection_item(self):\n        self.combobox.SetStringSelection(\"item0\")\n        self.assertEqual(self.combobox.GetStringSelection(), \"item0\")\n        self.assertEqual(self.combobox.GetValue(), \"item0\")\n\n    def test_set_string_selection_header(self):\n        self.combobox.SetStringSelection(\"Header\")\n        self.assertEqual(self.combobox.GetStringSelection(), \"\")\n        self.assertEqual(self.combobox.GetValue(), \"\")\n\n    def test_set_string_selection_invalid_string(self):\n        self.combobox.SetStringSelection(\"abcde\")\n        self.assertEqual(self.combobox.GetStringSelection(), \"\")\n        self.assertEqual(self.combobox.GetValue(), \"\")\n\n    # wx.ItemContainer methods\n\n    def test_clear(self):\n        self.combobox.SetValue(\"value\")\n\n        self.combobox.Clear()\n        self.assertEqual(self.combobox.GetCount(), 0)\n        self.assertTrue(self.combobox.IsTextEmpty())\n\n    def test_append(self):\n        self.combobox.Append(\"item10\")\n        self.assertEqual(self.combobox.GetCount(), 12)\n\n    def test_append_items(self):\n        self.combobox.AppendItems([\"item10\", \"item11\"])\n        self.assertEqual(self.combobox.GetCount(), 13)\n\n    def test_delete(self):\n        self.combobox.Delete(1)\n        self.assertEqual(self.combobox.GetString(1), \"item1\")\n\n    # wx.TextEntry methods\n\n    def test_get_value(self):\n        self.combobox.SetValue(\"value\")\n        self.assertEqual(self.combobox.GetValue(), \"value\")\n\n\ndef main():\n    unittest.main()\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "youtube-dl-gui.1",
    "content": ".\\\" [program name] [section] [date YYYY-MM-DD] [version] [empty]\n.TH YOUTUBE\\-DL\\-GUI 1 \"2018-01-13\" \"Version 0.4\" \"\"\n\n.SH NAME\nyoutube\\-dl\\-gui \\- cross platform graphical user interface for youtube\\-dl.\n\n.SH SYNOPSIS\n.B youtube\\-dl\\-gui\n\n.SH DESCRIPTION\nYoutube\\-dl\\-gui is a graphical frontend of the popular youtube\\-dl\ncommand\\-line program. As such, it does not take any command\\-line parameters.\nYou may configure the program through the graphical settings dialog, or the\nconfiguration file.\n\n.SH FILES\n.\\\" .IP text indent_size (.IP = Indented Paragraph)\n.IP \"\\fB$HOME/.config/youtube\\-dlg/settings.json\\fR\" 4\nConfiguration file.\n.\\\" .IP text indent_size (.IP = Indented Paragraph)\n.IP \"\\fB$HOME/.config/youtube\\-dlg/log\\fR\" 4\nLog file.\n\n.SH NOTES\nFAQS: https://github.com/MrS0m30n3/youtube-dl-gui/blob/master/docs/faqs.md\n\n.SH SEE ALSO\n.BR youtube-dl (1)\n"
  },
  {
    "path": "youtube_dl_gui/__init__.py",
    "content": "#!/usr/bin/env python2\n# -*- coding: utf-8 -*-\n\n\"\"\"Youtubedlg __init__ file.\n\nResponsible on how the package looks from the outside.\n\nExample:\n    In order to load the GUI from a python script.\n\n        import youtube_dl_gui\n\n        youtube_dl_gui.main()\n\n\"\"\"\n\nfrom __future__ import unicode_literals\n\nimport sys\nimport gettext\nimport os.path\n\ntry:\n    import wx\nexcept ImportError as error:\n    print error\n    sys.exit(1)\n\n__packagename__ = \"youtube_dl_gui\"\n\n# For package use\nfrom .version import __version__\nfrom .info import (\n    __author__,\n    __appname__,\n    __contact__,\n    __license__,\n    __projecturl__,\n    __licensefull__,\n    __description__,\n    __descriptionfull__,\n)\n\ngettext.install(__packagename__)\nfrom .formats import reload_strings\n\nfrom .logmanager import LogManager\nfrom .optionsmanager import OptionsManager\n\nfrom .utils import (\n    get_config_path,\n    get_locale_file,\n    os_path_exists,\n    YOUTUBEDL_BIN\n)\n\n\n# Set config path and create options and log managers\nconfig_path = get_config_path()\n\nopt_manager = OptionsManager(config_path)\nlog_manager = None\n\nif opt_manager.options['enable_log']:\n    log_manager = LogManager(config_path, opt_manager.options['log_time'])\n\n# Set gettext before MainFrame import\n# because the GUI strings are class level attributes\nlocale_dir = get_locale_file()\n\ntry:\n    gettext.translation(__packagename__, locale_dir, [opt_manager.options['locale_name']]).install(unicode=True)\nexcept IOError:\n    opt_manager.options['locale_name'] = 'en_US'\n    gettext.install(__packagename__)\n\nreload_strings()\n\nfrom .mainframe import MainFrame\n\n\ndef main():\n    \"\"\"The real main. Creates and calls the main app windows. \"\"\"\n    youtubedl_path = os.path.join(opt_manager.options[\"youtubedl_path\"], YOUTUBEDL_BIN)\n\n    app = wx.App()\n    frame = MainFrame(opt_manager, log_manager)\n    frame.Show()\n\n    if opt_manager.options[\"disable_update\"] and not os_path_exists(youtubedl_path):\n        wx.MessageBox(_(\"Failed to locate youtube-dl and updates are disabled\"), _(\"Error\"), wx.OK | wx.ICON_ERROR)\n        frame.close()\n\n    app.MainLoop()\n"
  },
  {
    "path": "youtube_dl_gui/__main__.py",
    "content": "#!/usr/bin/env python2\n# -*- coding: utf-8 -*-\n\n\"\"\"Youtubedlg __main__ file.\n\n__main__ file is a python 'executable' file which calls the youtubedlg\nmain() function in order to start the app. It can be used to start\nthe app from the package directory OR it can be used to start the app\nfrom a different directory after you have installed the youtube_dl_gui\npackage.\n\nExample:\n    In order to run the app from the package directory.\n\n        $ cd <package director>\n        $ python __main__.py\n\n    In order to run the app from /usr/local/bin etc.. AFTER\n    you have installed the package using setup.py.\n\n        $ youtube-dl-gui\n\n\"\"\"\n\nfrom __future__ import unicode_literals\n\nimport sys\n\nif __package__ is None and not hasattr(sys, \"frozen\"):\n    # direct call of __main__.py\n    import os.path\n    PATH = os.path.realpath(os.path.abspath(__file__))\n    sys.path.append(os.path.dirname(os.path.dirname(PATH)))\n\nimport youtube_dl_gui\n\n\nif __name__ == '__main__':\n    youtube_dl_gui.main()\n"
  },
  {
    "path": "youtube_dl_gui/data/pixmaps/icons-license",
    "content": "icons from: https://www.iconfinder.com/iconsets/google-material-design-icons\nlicense: https://creativecommons.org/licenses/by-sa/3.0\n"
  },
  {
    "path": "youtube_dl_gui/downloaders.py",
    "content": "#!/usr/bin/env python2\n# -*- coding: utf-8 -*-\n\n\"\"\"Python module to download videos.\n\nThis module contains the actual downloaders responsible\nfor downloading the video files.\n\n\"\"\"\n\nfrom __future__ import unicode_literals\n\nimport re\nimport os\nimport sys\nimport locale\nimport signal\nimport subprocess\n\nfrom time import sleep\nfrom Queue import Queue\nfrom threading import Thread\n\nfrom .utils import convert_item\n\n\nclass PipeReader(Thread):\n    \"\"\"Helper class to avoid deadlocks when reading from subprocess pipes.\n\n    This class uses python threads and queues in order to read from subprocess\n    pipes in an asynchronous way.\n\n    Attributes:\n        WAIT_TIME (float): Time in seconds to sleep.\n\n    Args:\n        queue (Queue.Queue): Python queue to store the output of the subprocess.\n\n    Warnings:\n        All the operations are based on 'str' types. The caller has to convert\n        the queued items back to 'unicode' if he needs to.\n\n    \"\"\"\n\n    WAIT_TIME = 0.1\n\n    def __init__(self, queue):\n        super(PipeReader, self).__init__()\n        self._filedescriptor = None\n        self._running = True\n        self._queue = queue\n\n        self.start()\n\n    def run(self):\n        # Flag to ignore specific lines\n        ignore_line = False\n\n        while self._running:\n            if self._filedescriptor is not None:\n                for line in iter(self._filedescriptor.readline, str('')):\n                    # Ignore ffmpeg stderr\n                    if str('ffmpeg version') in line:\n                        ignore_line = True\n\n                    if not ignore_line:\n                        self._queue.put_nowait(line)\n\n                self._filedescriptor = None\n                ignore_line = False\n\n            sleep(self.WAIT_TIME)\n\n    def attach_filedescriptor(self, filedesc):\n        \"\"\"Attach a filedescriptor to the PipeReader. \"\"\"\n        self._filedescriptor = filedesc\n\n    def join(self, timeout=None):\n        self._running = False\n        super(PipeReader, self).join(timeout)\n\n\nclass YoutubeDLDownloader(object):\n\n    \"\"\"Python class for downloading videos using youtube-dl & subprocess.\n\n    Attributes:\n        OK, ERROR, STOPPED, ALREADY, FILESIZE_ABORT, WARNING (int): Integers\n            that describe the return code from the download() method. The\n            larger the number the higher is the hierarchy of the code.\n            Codes with smaller hierachy cannot overwrite codes with higher\n            hierarchy.\n\n    Args:\n        youtubedl_path (string): Absolute path to youtube-dl binary.\n\n        data_hook (function): Optional callback function to retrieve download\n            process data.\n\n        log_data (function): Optional callback function to write data to\n            the log file.\n\n    Warnings:\n        The caller is responsible for calling the close() method after he has\n        finished with the object in order for the object to be able to properly\n        close down itself.\n\n    Example:\n        How to use YoutubeDLDownloader from a python script.\n\n            from downloaders import YoutubeDLDownloader\n\n            def data_hook(data):\n                print data\n\n            downloader = YoutubeDLDownloader('/usr/bin/youtube-dl', data_hook)\n\n            downloader.download(<URL STRING>, ['-f', 'flv'])\n\n    \"\"\"\n\n    OK = 0\n    WARNING = 1\n    ERROR = 2\n    FILESIZE_ABORT = 3\n    ALREADY = 4\n    STOPPED = 5\n\n    def __init__(self, youtubedl_path, data_hook=None, log_data=None):\n        self.youtubedl_path = youtubedl_path\n        self.data_hook = data_hook\n        self.log_data = log_data\n\n        self._return_code = self.OK\n        self._proc = None\n\n        self._stderr_queue = Queue()\n        self._stderr_reader = PipeReader(self._stderr_queue)\n\n    def download(self, url, options):\n        \"\"\"Download url using given options.\n\n        Args:\n            url (string): URL string to download.\n            options (list): Python list that contains youtube-dl options.\n\n        Returns:\n            An integer that shows the status of the download process.\n            There are 6 different return codes.\n\n            OK (0): The download process completed successfully.\n            WARNING (1): A warning occured during the download process.\n            ERROR (2): An error occured during the download process.\n            FILESIZE_ABORT (3): The corresponding url video file was larger or\n                smaller from the given filesize limit.\n            ALREADY (4): The given url is already downloaded.\n            STOPPED (5): The download process was stopped by the user.\n\n        \"\"\"\n        self._return_code = self.OK\n\n        cmd = self._get_cmd(url, options)\n        self._create_process(cmd)\n\n        if self._proc is not None:\n            self._stderr_reader.attach_filedescriptor(self._proc.stderr)\n\n        while self._proc_is_alive():\n            stdout = self._proc.stdout.readline().rstrip()\n            stdout = convert_item(stdout, to_unicode=True)\n\n            if stdout:\n                data_dict = extract_data(stdout)\n                self._extract_info(data_dict)\n                self._hook_data(data_dict)\n\n        # Read stderr after download process has been completed\n        # We don't need to read stderr in real time\n        while not self._stderr_queue.empty():\n            stderr = self._stderr_queue.get_nowait().rstrip()\n            stderr = convert_item(stderr, to_unicode=True)\n\n            self._log(stderr)\n\n            if self._is_warning(stderr):\n                self._set_returncode(self.WARNING)\n            else:\n                self._set_returncode(self.ERROR)\n\n        # Set return code to ERROR if we could not start the download process\n        # or the childs return code is greater than zero\n        # NOTE: In Linux if the called script is just empty Python exits\n        # normally (ret=0), so we cant detect this or similar cases\n        # using the code below\n        # NOTE: In Unix a negative return code (-N) indicates that the child\n        # was terminated by signal N (e.g. -9 = SIGKILL)\n        if self._proc is None or self._proc.returncode > 0:\n            self._return_code = self.ERROR\n\n        if self._proc is not None and self._proc.returncode > 0:\n            self._log('Child process exited with non-zero code: {}'.format(self._proc.returncode))\n\n        self._last_data_hook()\n\n        return self._return_code\n\n    def stop(self):\n        \"\"\"Stop the download process and set return code to STOPPED. \"\"\"\n        if self._proc_is_alive():\n\n            if os.name == 'nt':\n                # os.killpg is not available on Windows\n                # See: https://bugs.python.org/issue5115\n                self._proc.kill()\n\n                # When we kill the child process on Windows the return code\n                # gets set to 1, so we want to reset the return code back to 0\n                # in order to avoid creating logging output in the download(...)\n                # method\n                self._proc.returncode = 0\n            else:\n                os.killpg(self._proc.pid, signal.SIGKILL)\n\n            self._set_returncode(self.STOPPED)\n\n    def close(self):\n        \"\"\"Destructor like function for the object. \"\"\"\n        self._stderr_reader.join()\n\n    def _set_returncode(self, code):\n        \"\"\"Set self._return_code only if the hierarchy of the given code is\n        higher than the current self._return_code. \"\"\"\n        if code >= self._return_code:\n            self._return_code = code\n\n    def _is_warning(self, stderr):\n        return stderr.split(':')[0] == 'WARNING'\n\n    def _last_data_hook(self):\n        \"\"\"Set the last data information based on the return code. \"\"\"\n        data_dictionary = {}\n\n        if self._return_code == self.OK:\n            data_dictionary['status'] = 'Finished'\n        elif self._return_code == self.ERROR:\n            data_dictionary['status'] = 'Error'\n            data_dictionary['speed'] = ''\n            data_dictionary['eta'] = ''\n        elif self._return_code == self.WARNING:\n            data_dictionary['status'] = 'Warning'\n            data_dictionary['speed'] = ''\n            data_dictionary['eta'] = ''\n        elif self._return_code == self.STOPPED:\n            data_dictionary['status'] = 'Stopped'\n            data_dictionary['speed'] = ''\n            data_dictionary['eta'] = ''\n        elif self._return_code == self.ALREADY:\n            data_dictionary['status'] = 'Already Downloaded'\n        else:\n            data_dictionary['status'] = 'Filesize Abort'\n\n        self._hook_data(data_dictionary)\n\n    def _extract_info(self, data):\n        \"\"\"Extract informations about the download process from the given data.\n\n        Args:\n            data (dict): Python dictionary that contains different\n                keys. The keys are not standar the dictionary can also be\n                empty when there are no data to extract. See extract_data().\n\n        \"\"\"\n        if 'status' in data:\n            if data['status'] == 'Already Downloaded':\n                # Set self._return_code to already downloaded\n                # and trash that key\n                self._set_returncode(self.ALREADY)\n                data['status'] = None\n\n            if data['status'] == 'Filesize Abort':\n                # Set self._return_code to filesize abort\n                # and trash that key\n                self._set_returncode(self.FILESIZE_ABORT)\n                data['status'] = None\n\n    def _log(self, data):\n        \"\"\"Log data using the callback function. \"\"\"\n        if self.log_data is not None:\n            self.log_data(data)\n\n    def _hook_data(self, data):\n        \"\"\"Pass data back to the caller. \"\"\"\n        if self.data_hook is not None:\n            self.data_hook(data)\n\n    def _proc_is_alive(self):\n        \"\"\"Returns True if self._proc is alive else False. \"\"\"\n        if self._proc is None:\n            return False\n\n        return self._proc.poll() is None\n\n    def _get_cmd(self, url, options):\n        \"\"\"Build the subprocess command.\n\n        Args:\n            url (string): URL string to download.\n            options (list): Python list that contains youtube-dl options.\n\n        Returns:\n            Python list that contains the command to execute.\n\n        \"\"\"\n        if os.name == 'nt':\n            cmd = [self.youtubedl_path] + options + [url]\n        else:\n            cmd = ['python', self.youtubedl_path] + options + [url]\n\n        return cmd\n\n    def _create_process(self, cmd):\n        \"\"\"Create new subprocess.\n\n        Args:\n            cmd (list): Python list that contains the command to execute.\n\n        \"\"\"\n        info = preexec = None\n\n        # Keep a unicode copy of cmd for the log\n        ucmd = cmd\n\n        if os.name == 'nt':\n            # Hide subprocess window\n            info = subprocess.STARTUPINFO()\n            info.dwFlags |= subprocess.STARTF_USESHOWWINDOW\n        else:\n            # Make subprocess the process group leader\n            # in order to kill the whole process group with os.killpg\n            preexec = os.setsid\n\n        # Encode command for subprocess\n        # Refer to http://stackoverflow.com/a/9951851/35070\n        if sys.version_info < (3, 0):\n            cmd = convert_item(cmd, to_unicode=False)\n\n        try:\n            self._proc = subprocess.Popen(cmd,\n                                          stdout=subprocess.PIPE,\n                                          stderr=subprocess.PIPE,\n                                          preexec_fn=preexec,\n                                          startupinfo=info)\n        except (ValueError, OSError) as error:\n            self._log('Failed to start process: {}'.format(ucmd))\n            self._log(convert_item(str(error), to_unicode=True))\n\n\ndef extract_data(stdout):\n    \"\"\"Extract data from youtube-dl stdout.\n\n    Args:\n        stdout (string): String that contains the youtube-dl stdout.\n\n    Returns:\n        Python dictionary. The returned dictionary can be empty if there are\n        no data to extract else it may contain one or more of the\n        following keys:\n\n        'status'         : Contains the status of the download process.\n        'path'           : Destination path.\n        'extension'      : The file extension.\n        'filename'       : The filename without the extension.\n        'percent'        : The percentage of the video being downloaded.\n        'eta'            : Estimated time for the completion of the download process.\n        'speed'          : Download speed.\n        'filesize'       : The size of the video file being downloaded.\n        'playlist_index' : The playlist index of the current video file being downloaded.\n        'playlist_size'  : The number of videos in the playlist.\n\n    \"\"\"\n    # REFACTOR\n    def extract_filename(input_data):\n        path, fullname = os.path.split(input_data.strip(\"\\\"\"))\n        filename, extension = os.path.splitext(fullname)\n\n        return path, filename, extension\n\n    data_dictionary = {}\n\n    if not stdout:\n        return data_dictionary\n\n    # We want to keep the spaces in order to extract filenames with\n    # multiple whitespaces correctly. We also keep a copy of the old\n    # 'stdout' for backward compatibility with the old code\n    stdout_with_spaces = stdout.split(' ')\n    stdout = stdout.split()\n\n    stdout[0] = stdout[0].lstrip('\\r')\n\n    if stdout[0] == '[download]':\n        data_dictionary['status'] = 'Downloading'\n\n        # Get path, filename & extension\n        if stdout[1] == 'Destination:':\n            path, filename, extension = extract_filename(' '.join(stdout_with_spaces[2:]))\n\n            data_dictionary['path'] = path\n            data_dictionary['filename'] = filename\n            data_dictionary['extension'] = extension\n\n        # Get progress info\n        if '%' in stdout[1]:\n            if stdout[1] == '100%':\n                data_dictionary['speed'] = ''\n                data_dictionary['eta'] = ''\n                data_dictionary['percent'] = '100%'\n                data_dictionary['filesize'] = stdout[3]\n            else:\n                data_dictionary['percent'] = stdout[1]\n                data_dictionary['filesize'] = stdout[3]\n                data_dictionary['speed'] = stdout[5]\n                data_dictionary['eta'] = stdout[7]\n\n        # Get playlist info\n        if stdout[1] == 'Downloading' and stdout[2] == 'video':\n            data_dictionary['playlist_index'] = stdout[3]\n            data_dictionary['playlist_size'] = stdout[5]\n\n        # Remove the 'and merged' part from stdout when using ffmpeg to merge the formats\n        if stdout[-3] == 'downloaded' and stdout [-1] == 'merged':\n            stdout = stdout[:-2]\n            stdout_with_spaces = stdout_with_spaces[:-2]\n\n            data_dictionary['percent'] = '100%'\n\n        # Get file already downloaded status\n        if stdout[-1] == 'downloaded':\n            data_dictionary['status'] = 'Already Downloaded'\n            path, filename, extension = extract_filename(' '.join(stdout_with_spaces[1:-4]))\n\n            data_dictionary['path'] = path\n            data_dictionary['filename'] = filename\n            data_dictionary['extension'] = extension\n\n        # Get filesize abort status\n        if stdout[-1] == 'Aborting.':\n            data_dictionary['status'] = 'Filesize Abort'\n\n    elif stdout[0] == '[hlsnative]':\n        # native hls extractor\n        # see: https://github.com/rg3/youtube-dl/blob/master/youtube_dl/downloader/hls.py#L54\n        data_dictionary['status'] = 'Downloading'\n\n        if len(stdout) == 7:\n            segment_no = float(stdout[6])\n            current_segment = float(stdout[4])\n\n            # Get the percentage\n            percent = '{0:.1f}%'.format(current_segment / segment_no * 100)\n            data_dictionary['percent'] = percent\n\n    elif stdout[0] == '[ffmpeg]':\n        data_dictionary['status'] = 'Post Processing'\n\n        # Get final extension after merging process\n        if stdout[1] == 'Merging':\n            path, filename, extension = extract_filename(' '.join(stdout_with_spaces[4:]))\n\n            data_dictionary['path'] = path\n            data_dictionary['filename'] = filename\n            data_dictionary['extension'] = extension\n\n        # Get final extension ffmpeg post process simple (not file merge)\n        if stdout[1] == 'Destination:':\n            path, filename, extension = extract_filename(' '.join(stdout_with_spaces[2:]))\n\n            data_dictionary['path'] = path\n            data_dictionary['filename'] = filename\n            data_dictionary['extension'] = extension\n\n        # Get final extension after recoding process\n        if stdout[1] == 'Converting':\n            path, filename, extension = extract_filename(' '.join(stdout_with_spaces[8:]))\n\n            data_dictionary['path'] = path\n            data_dictionary['filename'] = filename\n            data_dictionary['extension'] = extension\n\n    elif stdout[0][0] != '[' or stdout[0] == '[debug]':\n        pass  # Just ignore this output\n\n    else:\n        data_dictionary['status'] = 'Pre Processing'\n\n    return data_dictionary\n"
  },
  {
    "path": "youtube_dl_gui/downloadmanager.py",
    "content": "#!/usr/bin/env python2\n# -*- coding: utf-8 -*-\n\n\"\"\"Youtubedlg module for managing the download process.\n\nThis module is responsible for managing the download process\nand update the GUI interface.\n\nAttributes:\n    MANAGER_PUB_TOPIC (string): wxPublisher subscription topic of the\n        DownloadManager thread.\n\n    WORKER_PUB_TOPIC (string): wxPublisher subscription topic of the\n        Worker thread.\n\nNote:\n    It's not the actual module that downloads the urls\n    thats the job of the 'downloaders' module.\n\n\"\"\"\n\nfrom __future__ import unicode_literals\n\nimport time\nimport os.path\n\nfrom threading import (\n    Thread,\n    RLock,\n    Lock\n)\n\nfrom wx import CallAfter\nfrom wx.lib.pubsub import setuparg1\nfrom wx.lib.pubsub import pub as Publisher\n\nfrom .parsers import OptionsParser\nfrom .updatemanager import UpdateThread\nfrom .downloaders import YoutubeDLDownloader\n\nfrom .utils import (\n    YOUTUBEDL_BIN,\n    os_path_exists,\n    format_bytes,\n    to_string,\n    to_bytes\n)\n\n\nMANAGER_PUB_TOPIC = 'dlmanager'\nWORKER_PUB_TOPIC = 'dlworker'\n\n_SYNC_LOCK = RLock()\n\n# Decorator that adds thread synchronization to a function\ndef synchronized(lock):\n    def _decorator(func):\n        def _wrapper(*args, **kwargs):\n            lock.acquire()\n            ret_value = func(*args, **kwargs)\n            lock.release()\n            return ret_value\n        return _wrapper\n    return _decorator\n\n\nclass DownloadItem(object):\n\n    \"\"\"Object that represents a download.\n\n    Attributes:\n        STAGES (tuple): Main stages of the download item.\n\n        ACTIVE_STAGES (tuple): Sub stages of the 'Active' stage.\n\n        COMPLETED_STAGES (tuple): Sub stages of the 'Completed' stage.\n\n        ERROR_STAGES (tuple): Sub stages of the 'Error' stage.\n\n    Args:\n        url (string): URL that corresponds to the download item.\n\n        options (list): Options list to use during the download phase.\n\n    \"\"\"\n\n    STAGES = (\"Queued\", \"Active\", \"Paused\", \"Completed\", \"Error\")\n\n    ACTIVE_STAGES = (\"Pre Processing\", \"Downloading\", \"Post Processing\")\n\n    COMPLETED_STAGES = (\"Finished\", \"Warning\", \"Already Downloaded\")\n\n    ERROR_STAGES = (\"Error\", \"Stopped\", \"Filesize Abort\")\n\n    def __init__(self, url, options):\n        self.url = url\n        self.options = options\n        self.object_id = hash(url + to_string(options))\n\n        self.reset()\n\n    @property\n    def stage(self):\n        return self._stage\n\n    @stage.setter\n    def stage(self, value):\n        if value not in self.STAGES:\n            raise ValueError(value)\n\n        if value == \"Queued\":\n            self.progress_stats[\"status\"] = value\n        if value == \"Active\":\n            self.progress_stats[\"status\"] = self.ACTIVE_STAGES[0]\n        if value == \"Completed\":\n            self.progress_stats[\"status\"] = self.COMPLETED_STAGES[0]\n        if value == \"Paused\":\n            self.progress_stats[\"status\"] = value\n        if value == \"Error\":\n            self.progress_stats[\"status\"] = self.ERROR_STAGES[0]\n\n        self._stage = value\n\n    def reset(self):\n        if hasattr(self, \"_stage\") and self._stage == self.STAGES[1]:\n            raise RuntimeError(\"Cannot reset an 'Active' item\")\n\n        self._stage = self.STAGES[0]\n        self.path = \"\"\n        self.filenames = []\n        self.extensions = []\n        self.filesizes = []\n\n        self.default_values = {\n            \"filename\": self.url,\n            \"extension\": \"-\",\n            \"filesize\": \"-\",\n            \"percent\": \"0%\",\n            \"speed\": \"-\",\n            \"eta\": \"-\",\n            \"status\": self.stage,\n            \"playlist_size\": \"\",\n            \"playlist_index\": \"\"\n        }\n\n        self.progress_stats = dict(self.default_values)\n\n        # Keep track when the 'playlist_index' changes\n        self.playlist_index_changed = False\n\n    def get_files(self):\n        \"\"\"Returns a list that contains all the system files bind to this object.\"\"\"\n        files = []\n\n        for index, item in enumerate(self.filenames):\n            filename = item + self.extensions[index]\n            files.append(os.path.join(self.path, filename))\n\n        return files\n\n    def update_stats(self, stats_dict):\n        \"\"\"Updates the progress_stats dict from the given dictionary.\"\"\"\n        assert isinstance(stats_dict, dict)\n\n        for key in stats_dict:\n            if key in self.progress_stats:\n                value = stats_dict[key]\n\n                if not isinstance(value, basestring) or not value:\n                    self.progress_stats[key] = self.default_values[key]\n                else:\n                    self.progress_stats[key] = value\n\n        # Extract extra stuff\n        if \"playlist_index\" in stats_dict:\n            self.playlist_index_changed = True\n\n        if \"filename\" in stats_dict:\n\n            # Reset filenames, extensions & filesizes lists when changing playlist item\n            if self.playlist_index_changed:\n                self.filenames = []\n                self.extensions = []\n                self.filesizes = []\n\n                self.playlist_index_changed = False\n\n            self.filenames.append(stats_dict[\"filename\"])\n\n        if \"extension\" in stats_dict:\n            self.extensions.append(stats_dict[\"extension\"])\n\n        if \"path\" in stats_dict:\n            self.path = stats_dict[\"path\"]\n\n        if \"filesize\" in stats_dict:\n            if stats_dict[\"percent\"] == \"100%\" and len(self.filesizes) < len(self.filenames):\n                filesize = stats_dict[\"filesize\"].lstrip(\"~\")  # HLS downloader etc\n                self.filesizes.append(to_bytes(filesize))\n\n        if \"status\" in stats_dict:\n            # If we are post processing try to calculate the size of\n            # the output file since youtube-dl does not\n            if stats_dict[\"status\"] == self.ACTIVE_STAGES[2] and len(self.filesizes) == 2:\n                post_proc_filesize = self.filesizes[0] + self.filesizes[1]\n\n                self.filesizes.append(post_proc_filesize)\n                self.progress_stats[\"filesize\"] = format_bytes(post_proc_filesize)\n\n            self._set_stage(stats_dict[\"status\"])\n\n    def _set_stage(self, status):\n        if status in self.ACTIVE_STAGES:\n            self._stage = self.STAGES[1]\n\n        if status in self.COMPLETED_STAGES:\n            self._stage = self.STAGES[3]\n\n        if status in self.ERROR_STAGES:\n            self._stage = self.STAGES[4]\n\n    def __eq__(self, other):\n        return self.object_id == other.object_id\n\n\n\nclass DownloadList(object):\n\n    \"\"\"List like data structure that contains DownloadItems.\n\n    Args:\n        items (list): List that contains DownloadItems.\n\n    \"\"\"\n\n    def __init__(self, items=None):\n        assert isinstance(items, list) or items is None\n\n        if items is None:\n            self._items_dict = {}  # Speed up lookup\n            self._items_list = []  # Keep the sequence\n        else:\n            self._items_list = [item.object_id for item in items]\n            self._items_dict = {item.object_id: item for item in items}\n\n    @synchronized(_SYNC_LOCK)\n    def clear(self):\n        \"\"\"Removes all the items from the list even the 'Active' ones.\"\"\"\n        self._items_list = []\n        self._items_dict = {}\n\n    @synchronized(_SYNC_LOCK)\n    def insert(self, item):\n        \"\"\"Inserts the given item to the list. Does not check for duplicates. \"\"\"\n        self._items_list.append(item.object_id)\n        self._items_dict[item.object_id] = item\n\n    @synchronized(_SYNC_LOCK)\n    def remove(self, object_id):\n        \"\"\"Removes an item from the list.\n\n        Removes the item with the corresponding object_id from\n        the list if the item is not in 'Active' state.\n\n        Returns:\n            True on success else False.\n\n        \"\"\"\n        if self._items_dict[object_id].stage != \"Active\":\n            self._items_list.remove(object_id)\n            del self._items_dict[object_id]\n\n            return True\n        return False\n\n    @synchronized(_SYNC_LOCK)\n    def fetch_next(self):\n        \"\"\"Returns the next queued item on the list.\n\n        Returns:\n            Next queued item or None if no other item exist.\n\n        \"\"\"\n        for object_id in self._items_list:\n            cur_item = self._items_dict[object_id]\n\n            if cur_item.stage == \"Queued\":\n                return cur_item\n\n        return None\n\n    @synchronized(_SYNC_LOCK)\n    def move_up(self, object_id):\n        \"\"\"Moves the item with the corresponding object_id up to the list.\"\"\"\n        index = self._items_list.index(object_id)\n\n        if index > 0:\n            self._swap(index, index - 1)\n            return True\n\n        return False\n\n    @synchronized(_SYNC_LOCK)\n    def move_down(self, object_id):\n        \"\"\"Moves the item with the corresponding object_id down to the list.\"\"\"\n        index = self._items_list.index(object_id)\n\n        if index < (len(self._items_list) - 1):\n            self._swap(index, index + 1)\n            return True\n\n        return False\n\n    @synchronized(_SYNC_LOCK)\n    def get_item(self, object_id):\n        \"\"\"Returns the DownloadItem with the given object_id.\"\"\"\n        return self._items_dict[object_id]\n\n    @synchronized(_SYNC_LOCK)\n    def has_item(self, object_id):\n        \"\"\"Returns True if the given object_id is in the list else False.\"\"\"\n        return object_id in self._items_list\n\n    @synchronized(_SYNC_LOCK)\n    def get_items(self):\n        \"\"\"Returns a list with all the items.\"\"\"\n        return [self._items_dict[object_id] for object_id in self._items_list]\n\n    @synchronized(_SYNC_LOCK)\n    def change_stage(self, object_id, new_stage):\n        \"\"\"Change the stage of the item with the given object_id.\"\"\"\n        self._items_dict[object_id].stage = new_stage\n\n    @synchronized(_SYNC_LOCK)\n    def index(self, object_id):\n        \"\"\"Get the zero based index of the item with the given object_id.\"\"\"\n        if object_id in self._items_list:\n            return self._items_list.index(object_id)\n        return -1\n\n    @synchronized(_SYNC_LOCK)\n    def __len__(self):\n        return len(self._items_list)\n\n    def _swap(self, index1, index2):\n        self._items_list[index1], self._items_list[index2] = self._items_list[index2], self._items_list[index1]\n\n\nclass DownloadManager(Thread):\n\n    \"\"\"Manages the download process.\n\n    Attributes:\n        WAIT_TIME (float): Time in seconds to sleep.\n\n    Args:\n        download_list (DownloadList): List that contains items to download.\n\n        opt_manager (optionsmanager.OptionsManager): Object responsible for\n            managing the youtubedlg options.\n\n        log_manager (logmanager.LogManager): Object responsible for writing\n            errors to the log.\n\n    \"\"\"\n\n    WAIT_TIME = 0.1\n\n    def __init__(self, parent, download_list, opt_manager, log_manager=None):\n        super(DownloadManager, self).__init__()\n        self.parent = parent\n        self.opt_manager = opt_manager\n        self.log_manager = log_manager\n        self.download_list = download_list\n\n        self._time_it_took = 0\n        self._successful = 0\n        self._running = True\n\n        # Init the custom workers thread pool\n        log_lock = None if log_manager is None else Lock()\n        wparams = (opt_manager, self._youtubedl_path(), log_manager, log_lock)\n        self._workers = [Worker(*wparams) for _ in xrange(opt_manager.options[\"workers_number\"])]\n\n        self.start()\n\n    @property\n    def successful(self):\n        \"\"\"Returns number of successful downloads. \"\"\"\n        return self._successful\n\n    @property\n    def time_it_took(self):\n        \"\"\"Returns time(seconds) it took for the download process\n        to complete. \"\"\"\n        return self._time_it_took\n\n    def run(self):\n        if not self.opt_manager.options[\"disable_update\"]:\n            self._check_youtubedl()\n        self._time_it_took = time.time()\n\n        while self._running:\n            item = self.download_list.fetch_next()\n\n            if item is not None:\n                worker = self._get_worker()\n\n                if worker is not None:\n                    worker.download(item.url, item.options, item.object_id)\n                    self.download_list.change_stage(item.object_id, \"Active\")\n\n            if item is None and self._jobs_done():\n                break\n\n            time.sleep(self.WAIT_TIME)\n\n        # Close all the workers\n        for worker in self._workers:\n            worker.close()\n\n        # Join and collect\n        for worker in self._workers:\n            worker.join()\n            self._successful += worker.successful\n\n        self._time_it_took = time.time() - self._time_it_took\n\n        if not self._running:\n            self._talk_to_gui('closed')\n        else:\n            self._talk_to_gui('finished')\n\n    def active(self):\n        \"\"\"Returns number of active items.\n\n        Note:\n            active_items = (workers that work) + (items waiting in the url_list).\n\n        \"\"\"\n        #counter = 0\n        #for worker in self._workers:\n            #if not worker.available():\n                #counter += 1\n\n        #counter += len(self.download_list)\n\n        return len(self.download_list)\n\n    def stop_downloads(self):\n        \"\"\"Stop the download process. Also send 'closing'\n        signal back to the GUI.\n\n        Note:\n            It does NOT kill the workers thats the job of the\n            clean up task in the run() method.\n\n        \"\"\"\n        self._talk_to_gui('closing')\n        self._running = False\n\n    def add_url(self, url):\n        \"\"\"Add given url to the download_list.\n\n        Args:\n            url (dict): Python dictionary that contains two keys.\n                The url and the index of the corresponding row in which\n                the worker should send back the information about the\n                download process.\n\n        \"\"\"\n        self.download_list.append(url)\n\n    def send_to_worker(self, data):\n        \"\"\"Send data to the Workers.\n\n        Args:\n            data (dict): Python dictionary that holds the 'index'\n            which is used to identify the Worker thread and the data which\n            can be any of the Worker's class valid data. For a list of valid\n            data keys see __init__() under the Worker class.\n\n        \"\"\"\n        if 'index' in data:\n            for worker in self._workers:\n                if worker.has_index(data['index']):\n                    worker.update_data(data)\n\n    def _talk_to_gui(self, data):\n        \"\"\"Send data back to the GUI using wxCallAfter and wxPublisher.\n\n        Args:\n            data (string): Unique signal string that informs the GUI for the\n                download process.\n\n        Note:\n            DownloadManager supports 4 signals.\n                1) closing: The download process is closing.\n                2) closed: The download process has closed.\n                3) finished: The download process was completed normally.\n                4) report_active: Signal the gui to read the number of active\n                    downloads using the active() method.\n\n        \"\"\"\n        CallAfter(Publisher.sendMessage, MANAGER_PUB_TOPIC, data)\n\n    def _check_youtubedl(self):\n        \"\"\"Check if youtube-dl binary exists. If not try to download it. \"\"\"\n        if not os_path_exists(self._youtubedl_path()) and self.parent.update_thread is None:\n            self.parent.update_thread = UpdateThread(self.opt_manager.options['youtubedl_path'], True)\n            self.parent.update_thread.join()\n            self.parent.update_thread = None\n\n    def _get_worker(self):\n        for worker in self._workers:\n            if worker.available():\n                return worker\n\n        return None\n\n    def _jobs_done(self):\n        \"\"\"Returns True if the workers have finished their jobs else False. \"\"\"\n        for worker in self._workers:\n            if not worker.available():\n                return False\n\n        return True\n\n    def _youtubedl_path(self):\n        \"\"\"Returns the path to youtube-dl binary. \"\"\"\n        path = self.opt_manager.options['youtubedl_path']\n        path = os.path.join(path, YOUTUBEDL_BIN)\n        return path\n\n\nclass Worker(Thread):\n\n    \"\"\"Simple worker which downloads the given url using a downloader\n    from the downloaders.py module.\n\n    Attributes:\n        WAIT_TIME (float): Time in seconds to sleep.\n\n    Args:\n        opt_manager (optionsmanager.OptionsManager): Check DownloadManager\n            description.\n\n        youtubedl (string): Absolute path to youtube-dl binary.\n\n        log_manager (logmanager.LogManager): Check DownloadManager\n            description.\n\n        log_lock (threading.Lock): Synchronization lock for the log_manager.\n            If the log_manager is set (not None) then the caller has to make\n            sure that the log_lock is also set.\n\n    Note:\n        For available data keys see self._data under the __init__() method.\n\n    \"\"\"\n\n    WAIT_TIME = 0.1\n\n    def __init__(self, opt_manager, youtubedl, log_manager=None, log_lock=None):\n        super(Worker, self).__init__()\n        self.opt_manager = opt_manager\n        self.log_manager = log_manager\n        self.log_lock = log_lock\n\n        self._downloader = YoutubeDLDownloader(youtubedl, self._data_hook, self._log_data)\n        self._options_parser = OptionsParser()\n        self._successful = 0\n        self._running = True\n        self._options = None\n\n        self._wait_for_reply = False\n\n        self._data = {\n            'playlist_index': None,\n            'playlist_size': None,\n            'new_filename': None,\n            'extension': None,\n            'filesize': None,\n            'filename': None,\n            'percent': None,\n            'status': None,\n            'index': None,\n            'speed': None,\n            'path': None,\n            'eta': None,\n            'url': None\n        }\n\n        self.start()\n\n    def run(self):\n        while self._running:\n            if self._data['url'] is not None:\n                #options = self._options_parser.parse(self.opt_manager.options)\n                ret_code = self._downloader.download(self._data['url'], self._options)\n\n                if (ret_code == YoutubeDLDownloader.OK or\n                        ret_code == YoutubeDLDownloader.ALREADY or\n                        ret_code == YoutubeDLDownloader.WARNING):\n                    self._successful += 1\n\n                # Ask GUI for name updates\n                #self._talk_to_gui('receive', {'source': 'filename', 'dest': 'new_filename'})\n\n                # Wait until you get a reply\n                #while self._wait_for_reply:\n                    #time.sleep(self.WAIT_TIME)\n\n                self._reset()\n\n            time.sleep(self.WAIT_TIME)\n\n        # Call the destructor function of YoutubeDLDownloader object\n        self._downloader.close()\n\n    def download(self, url, options, object_id):\n        \"\"\"Download given item.\n\n        Args:\n            item (dict): Python dictionary that contains two keys.\n                The url and the index of the corresponding row in which\n                the worker should send back the information about the\n                download process.\n\n        \"\"\"\n        self._data['url'] = url\n        self._options = options\n        self._data['index'] = object_id\n\n    def stop_download(self):\n        \"\"\"Stop the download process of the worker. \"\"\"\n        self._downloader.stop()\n\n    def close(self):\n        \"\"\"Kill the worker after stopping the download process. \"\"\"\n        self._running = False\n        self._downloader.stop()\n\n    def available(self):\n        \"\"\"Return True if the worker has no job else False. \"\"\"\n        return self._data['url'] is None\n\n    def has_index(self, index):\n        \"\"\"Return True if index is equal to self._data['index'] else False. \"\"\"\n        return self._data['index'] == index\n\n    def update_data(self, data):\n        \"\"\"Update self._data from the given data. \"\"\"\n        if self._wait_for_reply:\n            # Update data only if a receive request has been issued\n            for key in data:\n                self._data[key] = data[key]\n\n            self._wait_for_reply = False\n\n    @property\n    def successful(self):\n        \"\"\"Return the number of successful downloads for current worker. \"\"\"\n        return self._successful\n\n    def _reset(self):\n        \"\"\"Reset self._data back to the original state. \"\"\"\n        for key in self._data:\n            self._data[key] = None\n\n    def _log_data(self, data):\n        \"\"\"Callback method for self._downloader.\n\n        This method is used to write the given data in a synchronized way\n        to the log file using the self.log_manager and the self.log_lock.\n\n        Args:\n            data (string): String to write to the log file.\n\n        \"\"\"\n        if self.log_manager is not None:\n            self.log_lock.acquire()\n            self.log_manager.log(data)\n            self.log_lock.release()\n\n    def _data_hook(self, data):\n        \"\"\"Callback method for self._downloader.\n\n        This method updates self._data and sends the updates back to the\n        GUI using the self._talk_to_gui() method.\n\n        Args:\n            data (dict): Python dictionary which contains information\n                about the download process. For more info see the\n                extract_data() function under the downloaders.py module.\n\n        \"\"\"\n        ## Temp dictionary which holds the updates\n        #temp_dict = {}\n\n        ## Update each key\n        #for key in data:\n            #if self._data[key] != data[key]:\n                #self._data[key] = data[key]\n                #temp_dict[key] = data[key]\n\n        ## Build the playlist status if there is an update\n        ## REFACTOR re-implement this on DownloadItem or ListCtrl level?\n        ##if self._data['playlist_index'] is not None:\n            ##if 'status' in temp_dict or 'playlist_index' in temp_dict:\n                ##temp_dict['status'] = '{status} {index}/{size}'.format(\n                        ##status=self._data['status'],\n                        ##index=self._data['playlist_index'],\n                        ##size=self._data['playlist_size']\n                    ##)\n\n        #if len(temp_dict):\n            #self._talk_to_gui('send', temp_dict)\n        self._talk_to_gui('send', data)\n\n    def _talk_to_gui(self, signal, data):\n        \"\"\"Communicate with the GUI using wxCallAfter and wxPublisher.\n\n        Send/Ask data to/from the GUI. Note that if the signal is 'receive'\n        then the Worker will wait until it receives a reply from the GUI.\n\n        Args:\n            signal (string): Unique string that informs the GUI about the\n                communication procedure.\n\n            data (dict): Python dictionary which holds the data to be sent\n                back to the GUI. If the signal is 'send' then the dictionary\n                contains the updates for the GUI (e.g. percentage, eta). If\n                the signal is 'receive' then the dictionary contains exactly\n                three keys. The 'index' (row) from which we want to retrieve\n                the data, the 'source' which identifies a column in the\n                wxListCtrl widget and the 'dest' which tells the wxListCtrl\n                under which key to store the retrieved data.\n\n        Note:\n            Worker class supports 2 signals.\n                1) send: The Worker sends data back to the GUI\n                         (e.g. Send status updates).\n                2) receive: The Worker asks data from the GUI\n                            (e.g. Receive the name of a file).\n\n        Structure:\n            ('send', {'index': <item_row>, data_to_send*})\n\n            ('receive', {'index': <item_row>, 'source': 'source_key', 'dest': 'destination_key'})\n\n        \"\"\"\n        data['index'] = self._data['index']\n\n        if signal == 'receive':\n            self._wait_for_reply = True\n\n        CallAfter(Publisher.sendMessage, WORKER_PUB_TOPIC, (signal, data))\n\n"
  },
  {
    "path": "youtube_dl_gui/formats.py",
    "content": "# -*- coding: UTF-8 -*-\n\nimport gettext\n\nfrom .utils import TwoWayOrderedDict as tdict\n\n\nOUTPUT_FORMATS = tdict([\n    (0, _(\"ID\")),\n    (1, _(\"Title\")),\n    (2, _(\"Title + ID\")),\n    (4, _(\"Title + Quality\")),\n    (5, _(\"Title + ID + Quality\")),\n    (3, _(\"Custom\"))\n])\n\n\nDEFAULT_FORMATS = tdict([\n    (\"0\", _(\"default\"))\n])\n\n\nVIDEO_FORMATS = tdict([\n    (\"3gp\", \"3gp\"),\n    (\"17\", \"3gp [144p]\"),\n    (\"36\", \"3gp [240p]\"),\n    (\"flv\", \"flv\"),\n    (\"5\", \"flv [240p]\"),\n    (\"34\", \"flv [360p]\"),\n    (\"35\", \"flv [480p]\"),\n    (\"webm\", \"webm\"),\n    (\"43\", \"webm [360p]\"),\n    (\"44\", \"webm [480p]\"),\n    (\"45\", \"webm [720p]\"),\n    (\"46\", \"webm [1080p]\"),\n    (\"mp4\", \"mp4\"),\n    (\"18\", \"mp4 [360p]\"),\n    (\"22\", \"mp4 [720p]\"),\n    (\"37\", \"mp4 [1080p]\"),\n    (\"38\", \"mp4 [4K]\"),\n    (\"160\", \"mp4 [144p] (DASH Video)\"),\n    (\"133\", \"mp4 [240p] (DASH Video)\"),\n    (\"134\", \"mp4 [360p] (DASH Video)\"),\n    (\"135\", \"mp4 [480p] (DASH Video)\"),\n    (\"136\", \"mp4 [720p] (DASH Video)\"),\n    (\"137\", \"mp4 [1080p] (DASH Video)\"),\n    (\"264\", \"mp4 [1440p] (DASH Video)\"),\n    (\"138\", \"mp4 [2160p] (DASH Video)\"),\n    (\"242\", \"webm [240p] (DASH Video)\"),\n    (\"243\", \"webm [360p] (DASH Video)\"),\n    (\"244\", \"webm [480p] (DASH Video)\"),\n    (\"247\", \"webm [720p] (DASH Video)\"),\n    (\"248\", \"webm [1080p] (DASH Video)\"),\n    (\"271\", \"webm [1440p] (DASH Video)\"),\n    (\"272\", \"webm [2160p] (DASH Video)\"),\n    (\"82\", \"mp4 [360p] (3D)\"),\n    (\"83\", \"mp4 [480p] (3D)\"),\n    (\"84\", \"mp4 [720p] (3D)\"),\n    (\"85\", \"mp4 [1080p] (3D)\"),\n    (\"100\", \"webm [360p] (3D)\"),\n    (\"101\", \"webm [480p] (3D)\"),\n    (\"102\", \"webm [720p] (3D)\"),\n    (\"139\", \"m4a 48k (DASH Audio)\"),\n    (\"140\", \"m4a 128k (DASH Audio)\"),\n    (\"141\", \"m4a 256k (DASH Audio)\"),\n    (\"171\", \"webm 48k (DASH Audio)\"),\n    (\"172\", \"webm 256k (DASH Audio)\")\n])\n\n\nAUDIO_FORMATS = tdict([\n    (\"mp3\", \"mp3\"),\n    (\"wav\", \"wav\"),\n    (\"aac\", \"aac\"),\n    (\"m4a\", \"m4a\"),\n    (\"vorbis\", \"vorbis\"),\n    (\"opus\", \"opus\"),\n    (\"flac\", \"flac\")\n])\n\n\nFORMATS = DEFAULT_FORMATS.copy()\nFORMATS.update(VIDEO_FORMATS)\nFORMATS.update(AUDIO_FORMATS)\n\n\ndef reload_strings():\n    # IF YOU DONT WANT YOUR EYES TO BLEED STOP HERE\n    # YOU HAVE BEEN WARNED\n    # DO NOT LOOK THE CODE BELOW\n    #\n    #\n    #\n    #\n    #\n    #\n    #\n    #\n    #NOTE Remove\n    # Code is so messed up that i need to reload strings else\n    # the translations wont work on the about gettext tags\n    global OUTPUT_FORMATS\n    global DEFAULT_FORMATS\n    global VIDEO_FORMATS\n    global AUDIO_FORMATS\n    global FORMATS\n\n    OUTPUT_FORMATS = tdict([\n        (0, _(\"ID\")),\n        (1, _(\"Title\")),\n        (2, _(\"Title + ID\")),\n        (4, _(\"Title + Quality\")),\n        (5, _(\"Title + ID + Quality\")),\n        (3, _(\"Custom\"))\n    ])\n\n\n    DEFAULT_FORMATS = tdict([\n        (\"0\", _(\"default\"))\n    ])\n\n\n    VIDEO_FORMATS = tdict([\n        (\"3gp\", \"3gp\"),\n        (\"17\", \"3gp [144p]\"),\n        (\"36\", \"3gp [240p]\"),\n        (\"flv\", \"flv\"),\n        (\"5\", \"flv [240p]\"),\n        (\"34\", \"flv [360p]\"),\n        (\"35\", \"flv [480p]\"),\n        (\"webm\", \"webm\"),\n        (\"43\", \"webm [360p]\"),\n        (\"44\", \"webm [480p]\"),\n        (\"45\", \"webm [720p]\"),\n        (\"46\", \"webm [1080p]\"),\n        (\"mp4\", \"mp4\"),\n        (\"18\", \"mp4 [360p]\"),\n        (\"22\", \"mp4 [720p]\"),\n        (\"37\", \"mp4 [1080p]\"),\n        (\"38\", \"mp4 [4K]\"),\n        (\"160\", \"mp4 [144p] (DASH Video)\"),\n        (\"133\", \"mp4 [240p] (DASH Video)\"),\n        (\"134\", \"mp4 [360p] (DASH Video)\"),\n        (\"135\", \"mp4 [480p] (DASH Video)\"),\n        (\"136\", \"mp4 [720p] (DASH Video)\"),\n        (\"137\", \"mp4 [1080p] (DASH Video)\"),\n        (\"264\", \"mp4 [1440p] (DASH Video)\"),\n        (\"138\", \"mp4 [2160p] (DASH Video)\"),\n        (\"242\", \"webm [240p] (DASH Video)\"),\n        (\"243\", \"webm [360p] (DASH Video)\"),\n        (\"244\", \"webm [480p] (DASH Video)\"),\n        (\"247\", \"webm [720p] (DASH Video)\"),\n        (\"248\", \"webm [1080p] (DASH Video)\"),\n        (\"271\", \"webm [1440p] (DASH Video)\"),\n        (\"272\", \"webm [2160p] (DASH Video)\"),\n        (\"82\", \"mp4 [360p] (3D)\"),\n        (\"83\", \"mp4 [480p] (3D)\"),\n        (\"84\", \"mp4 [720p] (3D)\"),\n        (\"85\", \"mp4 [1080p] (3D)\"),\n        (\"100\", \"webm [360p] (3D)\"),\n        (\"101\", \"webm [480p] (3D)\"),\n        (\"102\", \"webm [720p] (3D)\"),\n        (\"139\", \"m4a 48k (DASH Audio)\"),\n        (\"140\", \"m4a 128k (DASH Audio)\"),\n        (\"141\", \"m4a 256k (DASH Audio)\"),\n        (\"171\", \"webm 48k (DASH Audio)\"),\n        (\"172\", \"webm 256k (DASH Audio)\")\n    ])\n\n\n    AUDIO_FORMATS = tdict([\n        (\"mp3\", \"mp3\"),\n        (\"wav\", \"wav\"),\n        (\"aac\", \"aac\"),\n        (\"m4a\", \"m4a\"),\n        (\"vorbis\", \"vorbis\"),\n        (\"opus\", \"opus\"),\n        (\"flac\", \"flac\")\n    ])\n\n\n    FORMATS = DEFAULT_FORMATS.copy()\n    FORMATS.update(VIDEO_FORMATS)\n    FORMATS.update(AUDIO_FORMATS)\n"
  },
  {
    "path": "youtube_dl_gui/info.py",
    "content": "# -*- coding: utf-8 -*-\n\n\"\"\"Youtubedlg module that holds package information.\n\nNote:\n    All those info could be stored in the __init__ file\n    but we keep them here to keep the code clean.\n\n\"\"\"\n\nfrom __future__ import unicode_literals\n\n__author__ = 'Sotiris Papadopoulos'\n__contact__ = 'ytubedlg@gmail.com'\n__projecturl__ = 'https://mrs0m30n3.github.io/youtube-dl-gui/'\n\n__appname__ = 'Youtube-DLG'\n__license__ = 'UNLICENSE'\n\n__description__ = 'Youtube-dl GUI'\n\n__descriptionfull__ = '''A cross platform front-end GUI of the popular\nyoutube-dl written in wxPython'''\n\n__licensefull__ = '''\nThis is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, compile, sell, or\ndistribute this software, either in source code form or as a compiled\nbinary, for any purpose, commercial or non-commercial, and by any\nmeans.\n\nIn jurisdictions that recognize copyright laws, the author or authors\nof this software dedicate any and all copyright interest in the\nsoftware to the public domain. We make this dedication for the benefit\nof the public at large and to the detriment of our heirs and\nsuccessors. We intend this dedication to be an overt act of\nrelinquishment in perpetuity of all present and future rights to this\nsoftware under copyright law.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n\nFor more information, please refer to <http://unlicense.org/>'''\n"
  },
  {
    "path": "youtube_dl_gui/locale/ar_SA/LC_MESSAGES/youtube_dl_gui.po",
    "content": "# Youtube-dlG localization file.\n# FIRST AUTHOR: Sotiris Papadopoulos <ytubedlg@gmail.com>, 2015.\n# snosi <fixall.dz@gmail.com>, 2017.\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: youtube-dlg 0.4\\n\"\n\"POT-Creation-Date: 2018-01-15 16:42+EET\\n\"\n\"PO-Revision-Date: 2017-06-22 17:52+0100\\n\"\n\"Last-Translator: snosi <fixall.dz@gmail.com>\\n\"\n\"Language-Team: snosi\\n\"\n\"Language: ar\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\\n\"\n\"X-Generator: Virtaal 0.7.1\\n\"\n\"Generated-By: pygettext.py 1.5\\n\"\n\n#: youtube_dl_gui/__init__.py:91\nmsgid \"Error\"\nmsgstr \"\"\n\n#: youtube_dl_gui/__init__.py:91\nmsgid \"Failed to locate youtube-dl and updates are disabled\"\nmsgstr \"\"\n\n#: youtube_dl_gui/formats.py:9 youtube_dl_gui/formats.py:109\nmsgid \"ID\"\nmsgstr \"ID\"\n\n#: youtube_dl_gui/formats.py:10 youtube_dl_gui/formats.py:110\n#: youtube_dl_gui/mainframe.py:140\nmsgid \"Title\"\nmsgstr \"العنوان\"\n\n#: youtube_dl_gui/formats.py:11 youtube_dl_gui/formats.py:111\nmsgid \"Title + ID\"\nmsgstr \"العنوان + ID\"\n\n#: youtube_dl_gui/formats.py:12 youtube_dl_gui/formats.py:112\nmsgid \"Title + Quality\"\nmsgstr \"العنوان + الجودة\"\n\n#: youtube_dl_gui/formats.py:13 youtube_dl_gui/formats.py:113\nmsgid \"Title + ID + Quality\"\nmsgstr \"العنوان + ID + الجودة\"\n\n#: youtube_dl_gui/formats.py:14 youtube_dl_gui/formats.py:114\nmsgid \"Custom\"\nmsgstr \"مخصص\"\n\n#: youtube_dl_gui/formats.py:19 youtube_dl_gui/formats.py:119\n#: youtube_dl_gui/mainframe.py:503 youtube_dl_gui/mainframe.py:506\nmsgid \"default\"\nmsgstr \"افتراضي (دون تعديل)\"\n\n#: youtube_dl_gui/mainframe.py:97\nmsgid \"Enter URLs below\"\nmsgstr \"ضع الرابط هنا\"\n\n#: youtube_dl_gui/mainframe.py:98\nmsgid \"Update\"\nmsgstr \"تحديث\"\n\n#: youtube_dl_gui/mainframe.py:99 youtube_dl_gui/optionsframe.py:41\nmsgid \"Options\"\nmsgstr \"الخيارات\"\n\n#: youtube_dl_gui/mainframe.py:100 youtube_dl_gui/optionsframe.py:584\nmsgid \"Stop\"\nmsgstr \"إيقاف\"\n\n#: youtube_dl_gui/mainframe.py:101\nmsgid \"Info\"\nmsgstr \"معلومات\"\n\n#: youtube_dl_gui/mainframe.py:102\nmsgid \"Welcome\"\nmsgstr \"مرحباً\"\n\n#: youtube_dl_gui/mainframe.py:103\nmsgid \"Warning\"\nmsgstr \"تحذير\"\n\n#: youtube_dl_gui/mainframe.py:105\nmsgid \"Add\"\nmsgstr \"إضافة\"\n\n#: youtube_dl_gui/mainframe.py:106\nmsgid \"Download list\"\nmsgstr \"قائمة التنزيلات\"\n\n#: youtube_dl_gui/mainframe.py:107 youtube_dl_gui/mainframe.py:516\n#: youtube_dl_gui/mainframe.py:534\nmsgid \"Delete\"\nmsgstr \"حذف\"\n\n#: youtube_dl_gui/mainframe.py:108\nmsgid \"Play\"\nmsgstr \"تشغيل\"\n\n#: youtube_dl_gui/mainframe.py:109\nmsgid \"Up\"\nmsgstr \"فوق\"\n\n#: youtube_dl_gui/mainframe.py:110\nmsgid \"Down\"\nmsgstr \"اسفل\"\n\n#: youtube_dl_gui/mainframe.py:111\nmsgid \"Reload\"\nmsgstr \"أعد التحميل\"\n\n#: youtube_dl_gui/mainframe.py:112 youtube_dl_gui/mainframe.py:448\n#: youtube_dl_gui/mainframe.py:649\nmsgid \"Pause\"\nmsgstr \"توقيف مؤقت\"\n\n#: youtube_dl_gui/mainframe.py:113 youtube_dl_gui/mainframe.py:865\n#: youtube_dl_gui/mainframe.py:866 youtube_dl_gui/optionsframe.py:582\nmsgid \"Start\"\nmsgstr \"إبدأ\"\n\n#: youtube_dl_gui/mainframe.py:114\nmsgid \"About\"\nmsgstr \"عن\"\n\n#: youtube_dl_gui/mainframe.py:115\nmsgid \"View Log\"\nmsgstr \"شاهد السِجِل\"\n\n#: youtube_dl_gui/mainframe.py:117\nmsgid \"Successfully downloaded {0} URL(s) in {1} day(s) {2} hour(s) {3} minute(s) {4} second(s)\"\nmsgstr \"انتهاء التنزيلات {0} URL(s) في {1} يوم {2} ساعة {3} دقيقة {4} ثانية\"\n\n#: youtube_dl_gui/mainframe.py:119\nmsgid \"Downloads completed\"\nmsgstr \"اكتمال التنزيلات\"\n\n#: youtube_dl_gui/mainframe.py:120\nmsgid \"Total Progress: {0:.1f}% | Queued ({1}) Paused ({2}) Active ({3}) Completed ({4}) Error ({5})\"\nmsgstr \"مجموع العمليات : {0:.1f}% | في الانتظار ({1}) موقف مؤقتا ({2}) نشط ({3}) اكتملت ({4}) أخطاء ({5})\"\n\n#: youtube_dl_gui/mainframe.py:121\nmsgid \"Stopping downloads\"\nmsgstr \"إيقاف التنزيل\"\n\n#: youtube_dl_gui/mainframe.py:122\nmsgid \"Downloads stopped\"\nmsgstr \"تم إيقاف التنزيل\"\n\n#: youtube_dl_gui/mainframe.py:123\nmsgid \"You need to provide at least one URL\"\nmsgstr \"يجب ان تضع رابطا واحدا على الاقل\"\n\n#: youtube_dl_gui/mainframe.py:124\nmsgid \"Downloads started\"\nmsgstr \"بدأ التنزيل\"\n\n#: youtube_dl_gui/mainframe.py:125\nmsgid \"Choose Directory\"\nmsgstr \"اختر مكان الحفظ\"\n\n#: youtube_dl_gui/mainframe.py:127\nmsgid \"Download in progress. Please wait for all downloads to complete\"\nmsgstr \"جاري التنزيل . انتظر حتى تكتمل التنزيلات\"\n\n#: youtube_dl_gui/mainframe.py:128\nmsgid \"Update already in progress\"\nmsgstr \"جاري التحديث\"\n\n#: youtube_dl_gui/mainframe.py:130\nmsgid \"Downloading latest youtube-dl. Please wait...\"\nmsgstr \"...يرجى الإنتظار youtube-dl تحديث . \"\n\n#: youtube_dl_gui/mainframe.py:131\nmsgid \"Youtube-dl download failed [{0}]\"\nmsgstr \"فشل تحديث youtube-dl [{0}]\"\n\n#: youtube_dl_gui/mainframe.py:132\nmsgid \"Successfully downloaded youtube-dl\"\nmsgstr \"youtube-dl اكتمل تحديث\"\n\n#: youtube_dl_gui/mainframe.py:134\nmsgid \"Unable to open directory: '{dir}'. The specified path does not exist\"\nmsgstr \"فعل لفتح الدليل : '{dir}'. المسار غير موجود\"\n\n#: youtube_dl_gui/mainframe.py:136\nmsgid \"Error while shutting down. Make sure you typed the correct password\"\nmsgstr \"تعذر إيقاف الجهاز . تأكد من كلمة المرور\"\n\n#: youtube_dl_gui/mainframe.py:138\nmsgid \"Shutting down system\"\nmsgstr \"إيقاف تشغيل النظام\"\n\n#: youtube_dl_gui/mainframe.py:141\nmsgid \"Extension\"\nmsgstr \"نوع الملف (الصيغة)\"\n\n#: youtube_dl_gui/mainframe.py:142\nmsgid \"Size\"\nmsgstr \"الحجم\"\n\n#: youtube_dl_gui/mainframe.py:143\nmsgid \"Percent\"\nmsgstr \"النِسبة المئوية\"\n\n#: youtube_dl_gui/mainframe.py:144\nmsgid \"ETA\"\nmsgstr \"الوقت المتبقي\"\n\n#: youtube_dl_gui/mainframe.py:145\nmsgid \"Speed\"\nmsgstr \"سرعة التنزيل\"\n\n#: youtube_dl_gui/mainframe.py:146\nmsgid \"Status\"\nmsgstr \"الحالة\"\n\n#: youtube_dl_gui/mainframe.py:235\nmsgid \"Get URL\"\nmsgstr \"جد الرابط\"\n\n#: youtube_dl_gui/mainframe.py:236\nmsgid \"Get command\"\nmsgstr \"الحصول على أمر\"\n\n#: youtube_dl_gui/mainframe.py:237\nmsgid \"Open destination\"\nmsgstr \"افتح وجهة\"\n\n#: youtube_dl_gui/mainframe.py:238\nmsgid \"Re-enter\"\nmsgstr \"اعادة الادخال\"\n\n#: youtube_dl_gui/mainframe.py:458\nmsgid \"Resume\"\nmsgstr \"استئتاف\"\n\n#: youtube_dl_gui/mainframe.py:480\nmsgid \"Video\"\nmsgstr \"الفيديو\"\n\n#: youtube_dl_gui/mainframe.py:484\nmsgid \"Audio\"\nmsgstr \"الصوتي\"\n\n#: youtube_dl_gui/mainframe.py:516\nmsgid \"No items selected. Please pick an action\"\nmsgstr \"لم يتم تحديد أي عنصر. يرجى اختيار إجراء\"\n\n#: youtube_dl_gui/mainframe.py:516\nmsgid \"Remove all\"\nmsgstr \"حذف الكل \"\n\n#: youtube_dl_gui/mainframe.py:516\nmsgid \"Remove completed\"\nmsgstr \"حذف المكتمل\"\n\n#: youtube_dl_gui/mainframe.py:534\nmsgid \"Are you sure you want to remove selected items?\"\nmsgstr \"هل انت متأكد من حذف هذا العنصر ؟\"\n\n#: youtube_dl_gui/mainframe.py:546\nmsgid \"Item is active, cannot remove\"\nmsgstr \"لا يمكن الحذف الآن\"\n\n#: youtube_dl_gui/mainframe.py:579\nmsgid \"Item is not completed\"\nmsgstr \"لم يكتمل العنصر بعد\"\n\n#: youtube_dl_gui/mainframe.py:668\nmsgid \"Update in progress. Please wait for the update to complete\"\nmsgstr \"قيد التحديث .يرجى الانتظار\"\n\n#: youtube_dl_gui/mainframe.py:716\nmsgid \"Logging is disabled\"\nmsgstr \"تم تعطيل التسجيل\"\n\n#: youtube_dl_gui/mainframe.py:891\nmsgid \"Shutdown\"\nmsgstr \"إيقاف التشغيل\"\n\n#: youtube_dl_gui/mainframe.py:891\nmsgid \"Shutting down in {0} second(s)\"\nmsgstr \"إيقاف التشغيل في {0} ثانية(s)\"\n\n#: youtube_dl_gui/mainframe.py:980\nmsgid \"No items to download\"\nmsgstr \"لا توجد عناصر للتنزيل\"\n\n#: youtube_dl_gui/mainframe.py:1040\nmsgid \"Updates are disabled for your system. Please use the system's package manager to update youtube-dl.\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:1065\nmsgid \"Are you sure you want to exit?\"\nmsgstr \"هل تريد الخروج من البرنامج ؟\"\n\n#: youtube_dl_gui/mainframe.py:1065\nmsgid \"Exit\"\nmsgstr \"خروج\"\n\n#: youtube_dl_gui/mainframe.py:1306 youtube_dl_gui/mainframe.py:1456\nmsgid \"Cancel\"\nmsgstr \"إلغاء\"\n\n#: youtube_dl_gui/mainframe.py:1455\nmsgid \"OK\"\nmsgstr \"موافق\"\n\n#: youtube_dl_gui/optionsframe.py:65\nmsgid \"Reset\"\nmsgstr \"إسترجاع الإعدادات الافتراضية\"\n\n#: youtube_dl_gui/optionsframe.py:66\nmsgid \"Close\"\nmsgstr \"إنهاء\"\n\n#: youtube_dl_gui/optionsframe.py:72\nmsgid \"General\"\nmsgstr \"عام\"\n\n#: youtube_dl_gui/optionsframe.py:73\nmsgid \"Formats\"\nmsgstr \"الصيغ\"\n\n#: youtube_dl_gui/optionsframe.py:74\nmsgid \"Downloads\"\nmsgstr \"التنزيلات\"\n\n#: youtube_dl_gui/optionsframe.py:75\nmsgid \"Advanced\"\nmsgstr \"متقدم\"\n\n#: youtube_dl_gui/optionsframe.py:76\nmsgid \"Extra\"\nmsgstr \"إضافي\"\n\n#: youtube_dl_gui/optionsframe.py:310\nmsgid \"Language\"\nmsgstr \"اللغة\"\n\n#: youtube_dl_gui/optionsframe.py:313\nmsgid \"Filename format\"\nmsgstr \"اسم الملف الصيغة\"\n\n#: youtube_dl_gui/optionsframe.py:318\nmsgid \"Filename options\"\nmsgstr \"اسم الملف خيارات\"\n\n#: youtube_dl_gui/optionsframe.py:319\nmsgid \"Restrict filenames to ASCII\"\nmsgstr \"قيّد اسم الملف الى ASCII\"\n\n#: youtube_dl_gui/optionsframe.py:321\nmsgid \"More options\"\nmsgstr \"مزيد من الخيارات\"\n\n#: youtube_dl_gui/optionsframe.py:322\nmsgid \"Confirm on exit\"\nmsgstr \"التأكيد عند الإنهاء\"\n\n#: youtube_dl_gui/optionsframe.py:323\nmsgid \"Confirm item deletion\"\nmsgstr \"التأكيد عند حذف عنصر\"\n\n#: youtube_dl_gui/optionsframe.py:324\nmsgid \"Inform me on download completion\"\nmsgstr \"نبهني عند انتهاء التنزيلات\"\n\n#: youtube_dl_gui/optionsframe.py:326\nmsgid \"Shutdown on download completion\"\nmsgstr \"اوقف التشغيل عند انتهاء التنزيلات\"\n\n#: youtube_dl_gui/optionsframe.py:337\nmsgid \"SUDO password\"\nmsgstr \"كلمة مرور المسؤول\"\n\n#: youtube_dl_gui/optionsframe.py:415 youtube_dl_gui/optionsframe.py:816\nmsgid \"In order for the changes to take effect please restart {0}\"\nmsgstr \"تطبق التغييرات بعد اعادة التشغيل {0}\"\n\n#: youtube_dl_gui/optionsframe.py:416 youtube_dl_gui/optionsframe.py:817\nmsgid \"Restart\"\nmsgstr \"إعادة التشغيل\"\n\n#: youtube_dl_gui/optionsframe.py:463\nmsgid \"high\"\nmsgstr \"عالي\"\n\n#: youtube_dl_gui/optionsframe.py:463\nmsgid \"low\"\nmsgstr \"منخفض\"\n\n#: youtube_dl_gui/optionsframe.py:463\nmsgid \"mid\"\nmsgstr \"متوسط\"\n\n#: youtube_dl_gui/optionsframe.py:468\nmsgid \"Video formats\"\nmsgstr \"صيغ الفيديو\"\n\n#: youtube_dl_gui/optionsframe.py:471\nmsgid \"Audio formats\"\nmsgstr \"صيغ الصوت\"\n\n#: youtube_dl_gui/optionsframe.py:474\nmsgid \"Post-Process options\"\nmsgstr \"خيارات مابعد العملية\"\n\n#: youtube_dl_gui/optionsframe.py:475\nmsgid \"Keep original files\"\nmsgstr \"احتفظ بالملفات الأصلية\"\n\n#: youtube_dl_gui/optionsframe.py:476\nmsgid \"Extract audio from video file\"\nmsgstr \"استخرج ملف صوتي من ملف الفيديو\"\n\n#: youtube_dl_gui/optionsframe.py:477\nmsgid \"Embed thumbnail in audio file\"\nmsgstr \"تضمين الصورة المصغرة في الملف الصوتي\"\n\n#: youtube_dl_gui/optionsframe.py:478\nmsgid \"Add metadata to file\"\nmsgstr \"أضف بيانات وصفية للملف\"\n\n#: youtube_dl_gui/optionsframe.py:480\nmsgid \"Audio quality\"\nmsgstr \"جودة الصوت\"\n\n#: youtube_dl_gui/optionsframe.py:538\nmsgid \"English\"\nmsgstr \"الانجليزية\"\n\n#: youtube_dl_gui/optionsframe.py:539\nmsgid \"French\"\nmsgstr \"الفرنسية\"\n\n#: youtube_dl_gui/optionsframe.py:540\nmsgid \"German\"\nmsgstr \"الالمانية\"\n\n#: youtube_dl_gui/optionsframe.py:541\nmsgid \"Greek\"\nmsgstr \"اليونانية\"\n\n#: youtube_dl_gui/optionsframe.py:542\nmsgid \"Hebrew\"\nmsgstr \"العبرية\"\n\n#: youtube_dl_gui/optionsframe.py:543\nmsgid \"Italian\"\nmsgstr \"الايطالية\"\n\n#: youtube_dl_gui/optionsframe.py:544\nmsgid \"Portuguese\"\nmsgstr \"البرتغالية\"\n\n#: youtube_dl_gui/optionsframe.py:545\nmsgid \"Russian\"\nmsgstr \"الروسية\"\n\n#: youtube_dl_gui/optionsframe.py:546\nmsgid \"Spanish\"\nmsgstr \"الاسبانية\"\n\n#: youtube_dl_gui/optionsframe.py:547\nmsgid \"Swedish\"\nmsgstr \"السويدية\"\n\n#: youtube_dl_gui/optionsframe.py:548\nmsgid \"Turkish\"\nmsgstr \"التركية\"\n\n#: youtube_dl_gui/optionsframe.py:564\nmsgid \"None\"\nmsgstr \"لاشيء\"\n\n#: youtube_dl_gui/optionsframe.py:565\nmsgid \"Automatic subtitles (YOUTUBE ONLY)\"\nmsgstr \"الترجمة التلقائية (YOUTUBE ONLY)\"\n\n#: youtube_dl_gui/optionsframe.py:566\nmsgid \"All available subtitles\"\nmsgstr \"كل الترجمات المتوفرة\"\n\n#: youtube_dl_gui/optionsframe.py:567\nmsgid \"Subtitles by language\"\nmsgstr \"الترجمات حسب اللغة\"\n\n#: youtube_dl_gui/optionsframe.py:573\nmsgid \"Subtitles\"\nmsgstr \"الترجمات\"\n\n#: youtube_dl_gui/optionsframe.py:577\nmsgid \"Subtitles options\"\nmsgstr \"خيارات الترجمة\"\n\n#: youtube_dl_gui/optionsframe.py:578\nmsgid \"Embed subtitles into video file (mp4 ONLY)\"\nmsgstr \"تضمين ترجمات مصاحبة مع ملف الفيديو (mp4 فقط)\"\n\n#: youtube_dl_gui/optionsframe.py:580\nmsgid \"Playlist\"\nmsgstr \"قائمة التشغيل\"\n\n#: youtube_dl_gui/optionsframe.py:586 youtube_dl_gui/optionsframe.py:591\nmsgid \"Max\"\nmsgstr \"اقصى\"\n\n#: youtube_dl_gui/optionsframe.py:589\nmsgid \"Filesize\"\nmsgstr \"حجم الملف\"\n\n#: youtube_dl_gui/optionsframe.py:594\nmsgid \"Min\"\nmsgstr \"اقل\"\n\n#: youtube_dl_gui/optionsframe.py:723\nmsgid \"Retries\"\nmsgstr \"فحص\"\n\n#: youtube_dl_gui/optionsframe.py:726\nmsgid \"Authentication\"\nmsgstr \"إستيثاق\"\n\n#: youtube_dl_gui/optionsframe.py:728\nmsgid \"Username\"\nmsgstr \"اسم المستخدم\"\n\n#: youtube_dl_gui/optionsframe.py:730\nmsgid \"Password\"\nmsgstr \"كلمة المرور\"\n\n#: youtube_dl_gui/optionsframe.py:732\nmsgid \"Video password\"\nmsgstr \"كلمة المرور الخاصة بالفيديو\"\n\n#: youtube_dl_gui/optionsframe.py:735\nmsgid \"Network\"\nmsgstr \"الشبكة\"\n\n#: youtube_dl_gui/optionsframe.py:737\nmsgid \"Proxy\"\nmsgstr \"الخادم الوكيل (البروكسي)\"\n\n#: youtube_dl_gui/optionsframe.py:739\nmsgid \"User agent\"\nmsgstr \"مستخدم وكيل\"\n\n#: youtube_dl_gui/optionsframe.py:741\nmsgid \"Referer\"\nmsgstr \"يدل\"\n\n#: youtube_dl_gui/optionsframe.py:744\nmsgid \"Logging\"\nmsgstr \"تسجيل\"\n\n#: youtube_dl_gui/optionsframe.py:746\nmsgid \"Enable log\"\nmsgstr \"تفعيل السجّل\"\n\n#: youtube_dl_gui/optionsframe.py:747\nmsgid \"View\"\nmsgstr \"عرض\"\n\n#: youtube_dl_gui/optionsframe.py:748\nmsgid \"Clear\"\nmsgstr \"مسح\"\n\n#: youtube_dl_gui/optionsframe.py:858\nmsgid \"Youtube-dl command line options (e.g. --help)\"\nmsgstr \"Youtube-dl خيارات موجه الاوامر (e.g. --help)\"\n\n#: youtube_dl_gui/optionsframe.py:861\nmsgid \"Extra options\"\nmsgstr \"خيارات إضافية\"\n\n#: youtube_dl_gui/optionsframe.py:863\nmsgid \"Debug youtube-dl\"\nmsgstr \"youtube-dl تصحيح أخطاء\"\n\n#: youtube_dl_gui/optionsframe.py:864\nmsgid \"Ignore errors\"\nmsgstr \"تجاهل الأخطاء\"\n\n#: youtube_dl_gui/optionsframe.py:865\nmsgid \"Ignore youtube-dl config\"\nmsgstr \"youtube-dl تجاهل إعدادات\"\n\n#: youtube_dl_gui/optionsframe.py:866\nmsgid \"No mtime\"\nmsgstr \"No mtime\"\n\n#: youtube_dl_gui/optionsframe.py:867\nmsgid \"Prefer native HLS\"\nmsgstr \"الأصلي HLS تفضيل\"\n\n#: youtube_dl_gui/optionsframe.py:928\nmsgid \"Log Viewer\"\nmsgstr \"عارض السجل\"\n"
  },
  {
    "path": "youtube_dl_gui/locale/cs_CZ/LC_MESSAGES/youtube_dl_gui.po",
    "content": "# Youtube-dlG localization file.\n# FIRST AUTHOR: Sotiris Papadopoulos <ytubedlg@gmail.com>, 2015.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: youtube-dlg 0.4\\n\"\n\"POT-Creation-Date: 2018-01-15 16:42+EET\\n\"\n\"PO-Revision-Date: 2017-10-08 19:27+0200\\n\"\n\"Last-Translator: Pavel Řehák <pavel-rehak@email.cz>\\n\"\n\"Language-Team: \\n\"\n\"Language: cs_CZ\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: pygettext.py 1.5\\n\"\n\"X-Generator: Poedit 1.8.7.1\\n\"\n\n#: youtube_dl_gui/__init__.py:91\nmsgid \"Error\"\nmsgstr \"\"\n\n#: youtube_dl_gui/__init__.py:91\nmsgid \"Failed to locate youtube-dl and updates are disabled\"\nmsgstr \"\"\n\n#: youtube_dl_gui/formats.py:9 youtube_dl_gui/formats.py:109\nmsgid \"ID\"\nmsgstr \"ID\"\n\n#: youtube_dl_gui/formats.py:10 youtube_dl_gui/formats.py:110\n#: youtube_dl_gui/mainframe.py:140\nmsgid \"Title\"\nmsgstr \"Název\"\n\n#: youtube_dl_gui/formats.py:11 youtube_dl_gui/formats.py:111\nmsgid \"Title + ID\"\nmsgstr \"Název + ID\"\n\n#: youtube_dl_gui/formats.py:12 youtube_dl_gui/formats.py:112\nmsgid \"Title + Quality\"\nmsgstr \"Název + Kvalita\"\n\n#: youtube_dl_gui/formats.py:13 youtube_dl_gui/formats.py:113\nmsgid \"Title + ID + Quality\"\nmsgstr \"Název + ID + Kvalita\"\n\n#: youtube_dl_gui/formats.py:14 youtube_dl_gui/formats.py:114\nmsgid \"Custom\"\nmsgstr \"Vlastní\"\n\n#: youtube_dl_gui/formats.py:19 youtube_dl_gui/formats.py:119\n#: youtube_dl_gui/mainframe.py:503 youtube_dl_gui/mainframe.py:506\nmsgid \"default\"\nmsgstr \"výchozí\"\n\n#: youtube_dl_gui/mainframe.py:97\nmsgid \"Enter URLs below\"\nmsgstr \"Níže vložte URL adresy\"\n\n#: youtube_dl_gui/mainframe.py:98\nmsgid \"Update\"\nmsgstr \"Aktualizovat\"\n\n#: youtube_dl_gui/mainframe.py:99 youtube_dl_gui/optionsframe.py:41\nmsgid \"Options\"\nmsgstr \"Volby\"\n\n#: youtube_dl_gui/mainframe.py:100 youtube_dl_gui/optionsframe.py:584\nmsgid \"Stop\"\nmsgstr \"Zastavit\"\n\n#: youtube_dl_gui/mainframe.py:101\nmsgid \"Info\"\nmsgstr \"Informace\"\n\n#: youtube_dl_gui/mainframe.py:102\nmsgid \"Welcome\"\nmsgstr \"Vítejte\"\n\n#: youtube_dl_gui/mainframe.py:103\nmsgid \"Warning\"\nmsgstr \"Upozornění\"\n\n#: youtube_dl_gui/mainframe.py:105\nmsgid \"Add\"\nmsgstr \"Přidat\"\n\n#: youtube_dl_gui/mainframe.py:106\nmsgid \"Download list\"\nmsgstr \"Seznam stahování\"\n\n#: youtube_dl_gui/mainframe.py:107 youtube_dl_gui/mainframe.py:516\n#: youtube_dl_gui/mainframe.py:534\nmsgid \"Delete\"\nmsgstr \"Smazat\"\n\n#: youtube_dl_gui/mainframe.py:108\nmsgid \"Play\"\nmsgstr \"Přehrát\"\n\n#: youtube_dl_gui/mainframe.py:109\nmsgid \"Up\"\nmsgstr \"Nahoru\"\n\n#: youtube_dl_gui/mainframe.py:110\nmsgid \"Down\"\nmsgstr \"Dolů\"\n\n#: youtube_dl_gui/mainframe.py:111\nmsgid \"Reload\"\nmsgstr \"Obnovit\"\n\n#: youtube_dl_gui/mainframe.py:112 youtube_dl_gui/mainframe.py:448\n#: youtube_dl_gui/mainframe.py:649\nmsgid \"Pause\"\nmsgstr \"Pozastavit\"\n\n#: youtube_dl_gui/mainframe.py:113 youtube_dl_gui/mainframe.py:865\n#: youtube_dl_gui/mainframe.py:866 youtube_dl_gui/optionsframe.py:582\nmsgid \"Start\"\nmsgstr \"Spustit\"\n\n#: youtube_dl_gui/mainframe.py:114\nmsgid \"About\"\nmsgstr \"O aplikaci\"\n\n#: youtube_dl_gui/mainframe.py:115\nmsgid \"View Log\"\nmsgstr \"Zobrazit záznam\"\n\n#: youtube_dl_gui/mainframe.py:117\nmsgid \"Successfully downloaded {0} URL(s) in {1} day(s) {2} hour(s) {3} minute(s) {4} second(s)\"\nmsgstr \"Úspěšně staženo {0} URL za {1} dní {2} hodin {3} minut {4} sekund\"\n\n#: youtube_dl_gui/mainframe.py:119\nmsgid \"Downloads completed\"\nmsgstr \"Stahování dokončeno\"\n\n#: youtube_dl_gui/mainframe.py:120\nmsgid \"Total Progress: {0:.1f}% | Queued ({1}) Paused ({2}) Active ({3}) Completed ({4}) Error ({5})\"\nmsgstr \"Celkový postup: {0:.1f}% | Ve frontě ({1}) Pozastaveno ({2}) Aktivní ({3}) Dokončené ({4}) Chyby ({5})\"\n\n#: youtube_dl_gui/mainframe.py:121\nmsgid \"Stopping downloads\"\nmsgstr \"Stahování se zastavuje\"\n\n#: youtube_dl_gui/mainframe.py:122\nmsgid \"Downloads stopped\"\nmsgstr \"Stahování zastaveno\"\n\n#: youtube_dl_gui/mainframe.py:123\nmsgid \"You need to provide at least one URL\"\nmsgstr \"Je třeba zadat alespoň jednu URL adresu\"\n\n#: youtube_dl_gui/mainframe.py:124\nmsgid \"Downloads started\"\nmsgstr \"Stahování zahájeno\"\n\n#: youtube_dl_gui/mainframe.py:125\nmsgid \"Choose Directory\"\nmsgstr \"Vyberte adresář\"\n\n#: youtube_dl_gui/mainframe.py:127\nmsgid \"Download in progress. Please wait for all downloads to complete\"\nmsgstr \"Stahuje se. Počkejte prosím, až se dokončí všechna stahování\"\n\n#: youtube_dl_gui/mainframe.py:128\nmsgid \"Update already in progress\"\nmsgstr \"Aktualizace již probíhá\"\n\n#: youtube_dl_gui/mainframe.py:130\nmsgid \"Downloading latest youtube-dl. Please wait...\"\nmsgstr \"Stahuje se nejnovější youtube-dl. Počkejte prosím...\"\n\n#: youtube_dl_gui/mainframe.py:131\nmsgid \"Youtube-dl download failed [{0}]\"\nmsgstr \"Stahování youtube-dl selhalo [{0}]\"\n\n#: youtube_dl_gui/mainframe.py:132\nmsgid \"Successfully downloaded youtube-dl\"\nmsgstr \"Youtube-dl byl úspěšně stažen\"\n\n#: youtube_dl_gui/mainframe.py:134\nmsgid \"Unable to open directory: '{dir}'. The specified path does not exist\"\nmsgstr \"Nelze otevřít adresář: '{dir}'. Uvedená cesta neexistuje\"\n\n#: youtube_dl_gui/mainframe.py:136\nmsgid \"Error while shutting down. Make sure you typed the correct password\"\nmsgstr \"Při vypínání nastala chyba. Ujistěte se, že jste zadali správné heslo\"\n\n#: youtube_dl_gui/mainframe.py:138\nmsgid \"Shutting down system\"\nmsgstr \"Systém se vypíná\"\n\n#: youtube_dl_gui/mainframe.py:141\nmsgid \"Extension\"\nmsgstr \"Přípona\"\n\n#: youtube_dl_gui/mainframe.py:142\nmsgid \"Size\"\nmsgstr \"Velikost\"\n\n#: youtube_dl_gui/mainframe.py:143\nmsgid \"Percent\"\nmsgstr \"Procenta\"\n\n#: youtube_dl_gui/mainframe.py:144\nmsgid \"ETA\"\nmsgstr \"ETA\"\n\n#: youtube_dl_gui/mainframe.py:145\nmsgid \"Speed\"\nmsgstr \"Rychlost\"\n\n#: youtube_dl_gui/mainframe.py:146\nmsgid \"Status\"\nmsgstr \"Stav\"\n\n#: youtube_dl_gui/mainframe.py:235\nmsgid \"Get URL\"\nmsgstr \"Kopírovat URL\"\n\n#: youtube_dl_gui/mainframe.py:236\nmsgid \"Get command\"\nmsgstr \"Kopírovat příkaz\"\n\n#: youtube_dl_gui/mainframe.py:237\nmsgid \"Open destination\"\nmsgstr \"Otevřít cíl\"\n\n#: youtube_dl_gui/mainframe.py:238\nmsgid \"Re-enter\"\nmsgstr \"Znovu vložit\"\n\n#: youtube_dl_gui/mainframe.py:458\nmsgid \"Resume\"\nmsgstr \"Pokračovat\"\n\n#: youtube_dl_gui/mainframe.py:480\nmsgid \"Video\"\nmsgstr \"Video\"\n\n#: youtube_dl_gui/mainframe.py:484\nmsgid \"Audio\"\nmsgstr \"Zvuk\"\n\n#: youtube_dl_gui/mainframe.py:516\nmsgid \"No items selected. Please pick an action\"\nmsgstr \"Není vybrána žádná položka. Prosím vyberte akci\"\n\n#: youtube_dl_gui/mainframe.py:516\nmsgid \"Remove all\"\nmsgstr \"Odstranit vše\"\n\n#: youtube_dl_gui/mainframe.py:516\nmsgid \"Remove completed\"\nmsgstr \"Odstranit dokončené\"\n\n#: youtube_dl_gui/mainframe.py:534\nmsgid \"Are you sure you want to remove selected items?\"\nmsgstr \"Jste si jistí, že chcete odstranit vybranou položku?\"\n\n#: youtube_dl_gui/mainframe.py:546\nmsgid \"Item is active, cannot remove\"\nmsgstr \"Položka je aktivní, nelze ji odstranit\"\n\n#: youtube_dl_gui/mainframe.py:579\nmsgid \"Item is not completed\"\nmsgstr \"Položka není dokončena\"\n\n#: youtube_dl_gui/mainframe.py:668\nmsgid \"Update in progress. Please wait for the update to complete\"\nmsgstr \"Probíhá aktualizace. Počkejte prosím, až se aktualizace dokončí\"\n\n#: youtube_dl_gui/mainframe.py:716\nmsgid \"Logging is disabled\"\nmsgstr \"Záznamy jsou zakázány\"\n\n#: youtube_dl_gui/mainframe.py:891\nmsgid \"Shutdown\"\nmsgstr \"Vypnout\"\n\n#: youtube_dl_gui/mainframe.py:891\nmsgid \"Shutting down in {0} second(s)\"\nmsgstr \"Vypnutí za {0} sekund\"\n\n#: youtube_dl_gui/mainframe.py:980\nmsgid \"No items to download\"\nmsgstr \"Nejsou žádné položky ke stažení\"\n\n#: youtube_dl_gui/mainframe.py:1040\nmsgid \"Updates are disabled for your system. Please use the system's package manager to update youtube-dl.\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:1065\nmsgid \"Are you sure you want to exit?\"\nmsgstr \"Jste si jistí, že chcete aplikaci ukončit?\"\n\n#: youtube_dl_gui/mainframe.py:1065\nmsgid \"Exit\"\nmsgstr \"Ukončit\"\n\n#: youtube_dl_gui/mainframe.py:1306 youtube_dl_gui/mainframe.py:1456\nmsgid \"Cancel\"\nmsgstr \"Zrušit\"\n\n#: youtube_dl_gui/mainframe.py:1455\nmsgid \"OK\"\nmsgstr \"OK\"\n\n#: youtube_dl_gui/optionsframe.py:65\nmsgid \"Reset\"\nmsgstr \"Resetovat\"\n\n#: youtube_dl_gui/optionsframe.py:66\nmsgid \"Close\"\nmsgstr \"Zavřít\"\n\n#: youtube_dl_gui/optionsframe.py:72\nmsgid \"General\"\nmsgstr \"Obecné\"\n\n#: youtube_dl_gui/optionsframe.py:73\nmsgid \"Formats\"\nmsgstr \"Formáty\"\n\n#: youtube_dl_gui/optionsframe.py:74\nmsgid \"Downloads\"\nmsgstr \"Stahování\"\n\n#: youtube_dl_gui/optionsframe.py:75\nmsgid \"Advanced\"\nmsgstr \"Pokročilé\"\n\n#: youtube_dl_gui/optionsframe.py:76\nmsgid \"Extra\"\nmsgstr \"Extra\"\n\n#: youtube_dl_gui/optionsframe.py:310\nmsgid \"Language\"\nmsgstr \"Jazyk\"\n\n#: youtube_dl_gui/optionsframe.py:313\nmsgid \"Filename format\"\nmsgstr \"Formát názvu souboru\"\n\n#: youtube_dl_gui/optionsframe.py:318\nmsgid \"Filename options\"\nmsgstr \"Volby pro název souboru\"\n\n#: youtube_dl_gui/optionsframe.py:319\nmsgid \"Restrict filenames to ASCII\"\nmsgstr \"Omezit názvy souborů na ASCII\"\n\n#: youtube_dl_gui/optionsframe.py:321\nmsgid \"More options\"\nmsgstr \"Další volby\"\n\n#: youtube_dl_gui/optionsframe.py:322\nmsgid \"Confirm on exit\"\nmsgstr \"Potvrdit při ukončení\"\n\n#: youtube_dl_gui/optionsframe.py:323\nmsgid \"Confirm item deletion\"\nmsgstr \"Potvrdit mazání položky\"\n\n#: youtube_dl_gui/optionsframe.py:324\nmsgid \"Inform me on download completion\"\nmsgstr \"Informovat mě, když je stahování dokončeno\"\n\n#: youtube_dl_gui/optionsframe.py:326\nmsgid \"Shutdown on download completion\"\nmsgstr \"Vypnout, když je stahování dokončeno\"\n\n#: youtube_dl_gui/optionsframe.py:337\nmsgid \"SUDO password\"\nmsgstr \"Heslo SUDO\"\n\n#: youtube_dl_gui/optionsframe.py:415 youtube_dl_gui/optionsframe.py:816\nmsgid \"In order for the changes to take effect please restart {0}\"\nmsgstr \"Aby se projevily změny, prosím restartujte {0}\"\n\n#: youtube_dl_gui/optionsframe.py:416 youtube_dl_gui/optionsframe.py:817\nmsgid \"Restart\"\nmsgstr \"Restartovat\"\n\n#: youtube_dl_gui/optionsframe.py:463\nmsgid \"high\"\nmsgstr \"vysoká\"\n\n#: youtube_dl_gui/optionsframe.py:463\nmsgid \"low\"\nmsgstr \"nízká\"\n\n#: youtube_dl_gui/optionsframe.py:463\nmsgid \"mid\"\nmsgstr \"střední\"\n\n#: youtube_dl_gui/optionsframe.py:468\nmsgid \"Video formats\"\nmsgstr \"Video formáty\"\n\n#: youtube_dl_gui/optionsframe.py:471\nmsgid \"Audio formats\"\nmsgstr \"Zvukové formáty\"\n\n#: youtube_dl_gui/optionsframe.py:474\nmsgid \"Post-Process options\"\nmsgstr \"Volby po stažení\"\n\n#: youtube_dl_gui/optionsframe.py:475\nmsgid \"Keep original files\"\nmsgstr \"Zachovat původní soubory\"\n\n#: youtube_dl_gui/optionsframe.py:476\nmsgid \"Extract audio from video file\"\nmsgstr \"Extrahovat zvuk z video souboru\"\n\n#: youtube_dl_gui/optionsframe.py:477\nmsgid \"Embed thumbnail in audio file\"\nmsgstr \"Vložit náhled do zvukového souboru\"\n\n#: youtube_dl_gui/optionsframe.py:478\nmsgid \"Add metadata to file\"\nmsgstr \"Přidat do souboru metadata\"\n\n#: youtube_dl_gui/optionsframe.py:480\nmsgid \"Audio quality\"\nmsgstr \"Kvalita zvuku\"\n\n#: youtube_dl_gui/optionsframe.py:538\nmsgid \"English\"\nmsgstr \"Angličtina\"\n\n#: youtube_dl_gui/optionsframe.py:539\nmsgid \"French\"\nmsgstr \"Francouzština\"\n\n#: youtube_dl_gui/optionsframe.py:540\nmsgid \"German\"\nmsgstr \"Němčina\"\n\n#: youtube_dl_gui/optionsframe.py:541\nmsgid \"Greek\"\nmsgstr \"Řečtina\"\n\n#: youtube_dl_gui/optionsframe.py:542\nmsgid \"Hebrew\"\nmsgstr \"Hebrejština\"\n\n#: youtube_dl_gui/optionsframe.py:543\nmsgid \"Italian\"\nmsgstr \"Italština\"\n\n#: youtube_dl_gui/optionsframe.py:544\nmsgid \"Portuguese\"\nmsgstr \"Portugalština\"\n\n#: youtube_dl_gui/optionsframe.py:545\nmsgid \"Russian\"\nmsgstr \"Ruština\"\n\n#: youtube_dl_gui/optionsframe.py:546\nmsgid \"Spanish\"\nmsgstr \"Španělština\"\n\n#: youtube_dl_gui/optionsframe.py:547\nmsgid \"Swedish\"\nmsgstr \"Švédština\"\n\n#: youtube_dl_gui/optionsframe.py:548\nmsgid \"Turkish\"\nmsgstr \"Turečtina\"\n\n#: youtube_dl_gui/optionsframe.py:564\nmsgid \"None\"\nmsgstr \"Žádné\"\n\n#: youtube_dl_gui/optionsframe.py:565\nmsgid \"Automatic subtitles (YOUTUBE ONLY)\"\nmsgstr \"Automatické titulky (POUZE YOUTUBE)\"\n\n#: youtube_dl_gui/optionsframe.py:566\nmsgid \"All available subtitles\"\nmsgstr \"Všechny dostupné titulky\"\n\n#: youtube_dl_gui/optionsframe.py:567\nmsgid \"Subtitles by language\"\nmsgstr \"Titulky dle jazyka\"\n\n#: youtube_dl_gui/optionsframe.py:573\nmsgid \"Subtitles\"\nmsgstr \"Titulky\"\n\n#: youtube_dl_gui/optionsframe.py:577\nmsgid \"Subtitles options\"\nmsgstr \"Volby pro titulky\"\n\n#: youtube_dl_gui/optionsframe.py:578\nmsgid \"Embed subtitles into video file (mp4 ONLY)\"\nmsgstr \"Vložit titulky do video souboru (POUZE mp4)\"\n\n#: youtube_dl_gui/optionsframe.py:580\nmsgid \"Playlist\"\nmsgstr \"Seznam k přehrání\"\n\n#: youtube_dl_gui/optionsframe.py:586 youtube_dl_gui/optionsframe.py:591\nmsgid \"Max\"\nmsgstr \"Max\"\n\n#: youtube_dl_gui/optionsframe.py:589\nmsgid \"Filesize\"\nmsgstr \"Velikost souboru\"\n\n#: youtube_dl_gui/optionsframe.py:594\nmsgid \"Min\"\nmsgstr \"Min\"\n\n#: youtube_dl_gui/optionsframe.py:723\nmsgid \"Retries\"\nmsgstr \"Opakování\"\n\n#: youtube_dl_gui/optionsframe.py:726\nmsgid \"Authentication\"\nmsgstr \"Ověření\"\n\n#: youtube_dl_gui/optionsframe.py:728\nmsgid \"Username\"\nmsgstr \"Uživatelské jméno\"\n\n#: youtube_dl_gui/optionsframe.py:730\nmsgid \"Password\"\nmsgstr \"Heslo\"\n\n#: youtube_dl_gui/optionsframe.py:732\nmsgid \"Video password\"\nmsgstr \"Heslo videa\"\n\n#: youtube_dl_gui/optionsframe.py:735\nmsgid \"Network\"\nmsgstr \"Síť\"\n\n#: youtube_dl_gui/optionsframe.py:737\nmsgid \"Proxy\"\nmsgstr \"Proxy\"\n\n#: youtube_dl_gui/optionsframe.py:739\nmsgid \"User agent\"\nmsgstr \"Uživatelský agent\"\n\n#: youtube_dl_gui/optionsframe.py:741\nmsgid \"Referer\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:744\nmsgid \"Logging\"\nmsgstr \"Záznamy\"\n\n#: youtube_dl_gui/optionsframe.py:746\nmsgid \"Enable log\"\nmsgstr \"Povolit záznam\"\n\n#: youtube_dl_gui/optionsframe.py:747\nmsgid \"View\"\nmsgstr \"Zobrazit\"\n\n#: youtube_dl_gui/optionsframe.py:748\nmsgid \"Clear\"\nmsgstr \"Vymazat\"\n\n#: youtube_dl_gui/optionsframe.py:858\nmsgid \"Youtube-dl command line options (e.g. --help)\"\nmsgstr \"Volby příkazového řádku pro youtube-dl (např. --help)\"\n\n#: youtube_dl_gui/optionsframe.py:861\nmsgid \"Extra options\"\nmsgstr \"Další volby\"\n\n#: youtube_dl_gui/optionsframe.py:863\nmsgid \"Debug youtube-dl\"\nmsgstr \"Debug youtube-dl\"\n\n#: youtube_dl_gui/optionsframe.py:864\nmsgid \"Ignore errors\"\nmsgstr \"Ignorovat chyby\"\n\n#: youtube_dl_gui/optionsframe.py:865\nmsgid \"Ignore youtube-dl config\"\nmsgstr \"Ignorovat nastavení youtube-dl\"\n\n#: youtube_dl_gui/optionsframe.py:866\nmsgid \"No mtime\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:867\nmsgid \"Prefer native HLS\"\nmsgstr \"Preferovat nativní HLS\"\n\n#: youtube_dl_gui/optionsframe.py:928\nmsgid \"Log Viewer\"\nmsgstr \"Prohlížeč záznamu událostí\"\n"
  },
  {
    "path": "youtube_dl_gui/locale/en_US/LC_MESSAGES/youtube_dl_gui.po",
    "content": "# Youtube-dlG localization file.\n# FIRST AUTHOR: Sotiris Papadopoulos <ytubedlg@gmail.com>, 2015.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: youtube-dlg 0.4\\n\"\n\"POT-Creation-Date: 2018-01-15 16:42+EET\\n\"\n\"PO-Revision-Date: 2017-06-15 17:14+EEST\\n\"\n\"Last-Translator: Sotiris Papadopoulos <ytubedlg@gmail.com>\\n\"\n\"Language-Team: en\\n\"\n\"Language: \\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: pygettext.py 1.5\\n\"\n\n#: youtube_dl_gui/__init__.py:91\nmsgid \"Error\"\nmsgstr \"\"\n\n#: youtube_dl_gui/__init__.py:91\nmsgid \"Failed to locate youtube-dl and updates are disabled\"\nmsgstr \"\"\n\n#: youtube_dl_gui/formats.py:9 youtube_dl_gui/formats.py:109\nmsgid \"ID\"\nmsgstr \"\"\n\n#: youtube_dl_gui/formats.py:10 youtube_dl_gui/formats.py:110\n#: youtube_dl_gui/mainframe.py:140\nmsgid \"Title\"\nmsgstr \"\"\n\n#: youtube_dl_gui/formats.py:11 youtube_dl_gui/formats.py:111\nmsgid \"Title + ID\"\nmsgstr \"\"\n\n#: youtube_dl_gui/formats.py:12 youtube_dl_gui/formats.py:112\nmsgid \"Title + Quality\"\nmsgstr \"\"\n\n#: youtube_dl_gui/formats.py:13 youtube_dl_gui/formats.py:113\nmsgid \"Title + ID + Quality\"\nmsgstr \"\"\n\n#: youtube_dl_gui/formats.py:14 youtube_dl_gui/formats.py:114\nmsgid \"Custom\"\nmsgstr \"\"\n\n#: youtube_dl_gui/formats.py:19 youtube_dl_gui/formats.py:119\n#: youtube_dl_gui/mainframe.py:503 youtube_dl_gui/mainframe.py:506\nmsgid \"default\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:97\nmsgid \"Enter URLs below\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:98\nmsgid \"Update\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:99 youtube_dl_gui/optionsframe.py:41\nmsgid \"Options\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:100 youtube_dl_gui/optionsframe.py:584\nmsgid \"Stop\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:101\nmsgid \"Info\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:102\nmsgid \"Welcome\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:103\nmsgid \"Warning\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:105\nmsgid \"Add\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:106\nmsgid \"Download list\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:107 youtube_dl_gui/mainframe.py:516\n#: youtube_dl_gui/mainframe.py:534\nmsgid \"Delete\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:108\nmsgid \"Play\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:109\nmsgid \"Up\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:110\nmsgid \"Down\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:111\nmsgid \"Reload\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:112 youtube_dl_gui/mainframe.py:448\n#: youtube_dl_gui/mainframe.py:649\nmsgid \"Pause\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:113 youtube_dl_gui/mainframe.py:865\n#: youtube_dl_gui/mainframe.py:866 youtube_dl_gui/optionsframe.py:582\nmsgid \"Start\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:114\nmsgid \"About\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:115\nmsgid \"View Log\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:117\nmsgid \"Successfully downloaded {0} URL(s) in {1} day(s) {2} hour(s) {3} minute(s) {4} second(s)\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:119\nmsgid \"Downloads completed\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:120\nmsgid \"Total Progress: {0:.1f}% | Queued ({1}) Paused ({2}) Active ({3}) Completed ({4}) Error ({5})\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:121\nmsgid \"Stopping downloads\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:122\nmsgid \"Downloads stopped\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:123\nmsgid \"You need to provide at least one URL\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:124\nmsgid \"Downloads started\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:125\nmsgid \"Choose Directory\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:127\nmsgid \"Download in progress. Please wait for all downloads to complete\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:128\nmsgid \"Update already in progress\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:130\nmsgid \"Downloading latest youtube-dl. Please wait...\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:131\nmsgid \"Youtube-dl download failed [{0}]\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:132\nmsgid \"Successfully downloaded youtube-dl\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:134\nmsgid \"Unable to open directory: '{dir}'. The specified path does not exist\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:136\nmsgid \"Error while shutting down. Make sure you typed the correct password\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:138\nmsgid \"Shutting down system\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:141\nmsgid \"Extension\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:142\nmsgid \"Size\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:143\nmsgid \"Percent\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:144\nmsgid \"ETA\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:145\nmsgid \"Speed\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:146\nmsgid \"Status\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:235\nmsgid \"Get URL\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:236\nmsgid \"Get command\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:237\nmsgid \"Open destination\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:238\nmsgid \"Re-enter\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:458\nmsgid \"Resume\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:480\nmsgid \"Video\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:484\nmsgid \"Audio\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:516\nmsgid \"No items selected. Please pick an action\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:516\nmsgid \"Remove all\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:516\nmsgid \"Remove completed\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:534\nmsgid \"Are you sure you want to remove selected items?\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:546\nmsgid \"Item is active, cannot remove\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:579\nmsgid \"Item is not completed\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:668\nmsgid \"Update in progress. Please wait for the update to complete\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:716\nmsgid \"Logging is disabled\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:891\nmsgid \"Shutdown\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:891\nmsgid \"Shutting down in {0} second(s)\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:980\nmsgid \"No items to download\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:1040\nmsgid \"Updates are disabled for your system. Please use the system's package manager to update youtube-dl.\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:1065\nmsgid \"Are you sure you want to exit?\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:1065\nmsgid \"Exit\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:1306 youtube_dl_gui/mainframe.py:1456\nmsgid \"Cancel\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:1455\nmsgid \"OK\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:65\nmsgid \"Reset\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:66\nmsgid \"Close\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:72\nmsgid \"General\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:73\nmsgid \"Formats\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:74\nmsgid \"Downloads\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:75\nmsgid \"Advanced\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:76\nmsgid \"Extra\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:310\nmsgid \"Language\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:313\nmsgid \"Filename format\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:318\nmsgid \"Filename options\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:319\nmsgid \"Restrict filenames to ASCII\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:321\nmsgid \"More options\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:322\nmsgid \"Confirm on exit\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:323\nmsgid \"Confirm item deletion\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:324\nmsgid \"Inform me on download completion\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:326\nmsgid \"Shutdown on download completion\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:337\nmsgid \"SUDO password\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:415 youtube_dl_gui/optionsframe.py:816\nmsgid \"In order for the changes to take effect please restart {0}\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:416 youtube_dl_gui/optionsframe.py:817\nmsgid \"Restart\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:463\nmsgid \"high\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:463\nmsgid \"low\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:463\nmsgid \"mid\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:468\nmsgid \"Video formats\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:471\nmsgid \"Audio formats\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:474\nmsgid \"Post-Process options\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:475\nmsgid \"Keep original files\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:476\nmsgid \"Extract audio from video file\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:477\nmsgid \"Embed thumbnail in audio file\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:478\nmsgid \"Add metadata to file\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:480\nmsgid \"Audio quality\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:538\nmsgid \"English\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:539\nmsgid \"French\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:540\nmsgid \"German\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:541\nmsgid \"Greek\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:542\nmsgid \"Hebrew\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:543\nmsgid \"Italian\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:544\nmsgid \"Portuguese\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:545\nmsgid \"Russian\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:546\nmsgid \"Spanish\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:547\nmsgid \"Swedish\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:548\nmsgid \"Turkish\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:564\nmsgid \"None\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:565\nmsgid \"Automatic subtitles (YOUTUBE ONLY)\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:566\nmsgid \"All available subtitles\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:567\nmsgid \"Subtitles by language\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:573\nmsgid \"Subtitles\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:577\nmsgid \"Subtitles options\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:578\nmsgid \"Embed subtitles into video file (mp4 ONLY)\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:580\nmsgid \"Playlist\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:586 youtube_dl_gui/optionsframe.py:591\nmsgid \"Max\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:589\nmsgid \"Filesize\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:594\nmsgid \"Min\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:723\nmsgid \"Retries\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:726\nmsgid \"Authentication\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:728\nmsgid \"Username\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:730\nmsgid \"Password\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:732\nmsgid \"Video password\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:735\nmsgid \"Network\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:737\nmsgid \"Proxy\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:739\nmsgid \"User agent\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:741\nmsgid \"Referer\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:744\nmsgid \"Logging\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:746\nmsgid \"Enable log\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:747\nmsgid \"View\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:748\nmsgid \"Clear\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:858\nmsgid \"Youtube-dl command line options (e.g. --help)\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:861\nmsgid \"Extra options\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:863\nmsgid \"Debug youtube-dl\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:864\nmsgid \"Ignore errors\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:865\nmsgid \"Ignore youtube-dl config\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:866\nmsgid \"No mtime\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:867\nmsgid \"Prefer native HLS\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:928\nmsgid \"Log Viewer\"\nmsgstr \"\"\n"
  },
  {
    "path": "youtube_dl_gui/locale/es_ES/LC_MESSAGES/youtube_dl_gui.po",
    "content": "# Youtube-dlG localization file.\n# FIRST AUTHOR: Sotiris Papadopoulos <ytubedlg@gmail.com>, 2015.\n# Ismael Ferreras Morezuelas <swyterzone+youtubedlgui@gmail.com>, 2016-2017.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: youtube-dlg 0.4\\n\"\n\"POT-Creation-Date: 2018-01-15 16:42+EET\\n\"\n\"PO-Revision-Date: 2017-06-15 17:55+EEST\\n\"\n\"Last-Translator: Ismael Ferreras Morezuelas <swyterzone+youtubedlgui@gmail.com>\\n\"\n\"Language-Team: Spanish <>\\n\"\n\"Language: es_ES\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: pygettext.py 1.5\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n\"X-Generator: Gtranslator 2.91.7\\n\"\n\n#: youtube_dl_gui/__init__.py:91\nmsgid \"Error\"\nmsgstr \"\"\n\n#: youtube_dl_gui/__init__.py:91\nmsgid \"Failed to locate youtube-dl and updates are disabled\"\nmsgstr \"\"\n\n#: youtube_dl_gui/formats.py:9 youtube_dl_gui/formats.py:109\nmsgid \"ID\"\nmsgstr \"Identificador\"\n\n#: youtube_dl_gui/formats.py:10 youtube_dl_gui/formats.py:110\n#: youtube_dl_gui/mainframe.py:140\nmsgid \"Title\"\nmsgstr \"Título\"\n\n#: youtube_dl_gui/formats.py:11 youtube_dl_gui/formats.py:111\nmsgid \"Title + ID\"\nmsgstr \"Título + identificador\"\n\n#: youtube_dl_gui/formats.py:12 youtube_dl_gui/formats.py:112\nmsgid \"Title + Quality\"\nmsgstr \"Título + calidad\"\n\n#: youtube_dl_gui/formats.py:13 youtube_dl_gui/formats.py:113\nmsgid \"Title + ID + Quality\"\nmsgstr \"Título + identificador + calidad\"\n\n#: youtube_dl_gui/formats.py:14 youtube_dl_gui/formats.py:114\nmsgid \"Custom\"\nmsgstr \"Personalizado\"\n\n#: youtube_dl_gui/formats.py:19 youtube_dl_gui/formats.py:119\n#: youtube_dl_gui/mainframe.py:503 youtube_dl_gui/mainframe.py:506\nmsgid \"default\"\nmsgstr \"predeterminada\"\n\n#: youtube_dl_gui/mainframe.py:97\nmsgid \"Enter URLs below\"\nmsgstr \"Pega direcciones URL en la parte inferior\"\n\n#: youtube_dl_gui/mainframe.py:98\nmsgid \"Update\"\nmsgstr \"Actualizar\"\n\n#: youtube_dl_gui/mainframe.py:99 youtube_dl_gui/optionsframe.py:41\nmsgid \"Options\"\nmsgstr \"Ajustes\"\n\n#: youtube_dl_gui/mainframe.py:100 youtube_dl_gui/optionsframe.py:584\nmsgid \"Stop\"\nmsgstr \"Detener\"\n\n#: youtube_dl_gui/mainframe.py:101\nmsgid \"Info\"\nmsgstr \"Info\"\n\n#: youtube_dl_gui/mainframe.py:102\nmsgid \"Welcome\"\nmsgstr \"Bienvenid@\"\n\n#: youtube_dl_gui/mainframe.py:103\nmsgid \"Warning\"\nmsgstr \"Advertencia\"\n\n#: youtube_dl_gui/mainframe.py:105\nmsgid \"Add\"\nmsgstr \"Añadir\"\n\n#: youtube_dl_gui/mainframe.py:106\nmsgid \"Download list\"\nmsgstr \"Descargar lista\"\n\n#: youtube_dl_gui/mainframe.py:107 youtube_dl_gui/mainframe.py:516\n#: youtube_dl_gui/mainframe.py:534\nmsgid \"Delete\"\nmsgstr \"Borrar\"\n\n#: youtube_dl_gui/mainframe.py:108\nmsgid \"Play\"\nmsgstr \"Reproducir\"\n\n#: youtube_dl_gui/mainframe.py:109\nmsgid \"Up\"\nmsgstr \"Subir\"\n\n#: youtube_dl_gui/mainframe.py:110\nmsgid \"Down\"\nmsgstr \"Bajar\"\n\n#: youtube_dl_gui/mainframe.py:111\nmsgid \"Reload\"\nmsgstr \"Recargar\"\n\n#: youtube_dl_gui/mainframe.py:112 youtube_dl_gui/mainframe.py:448\n#: youtube_dl_gui/mainframe.py:649\nmsgid \"Pause\"\nmsgstr \"Pausar\"\n\n#: youtube_dl_gui/mainframe.py:113 youtube_dl_gui/mainframe.py:865\n#: youtube_dl_gui/mainframe.py:866 youtube_dl_gui/optionsframe.py:582\nmsgid \"Start\"\nmsgstr \"Iniciar\"\n\n#: youtube_dl_gui/mainframe.py:114\nmsgid \"About\"\nmsgstr \"Acerca de\"\n\n#: youtube_dl_gui/mainframe.py:115\nmsgid \"View Log\"\nmsgstr \"Ver registro\"\n\n#: youtube_dl_gui/mainframe.py:117\nmsgid \"Successfully downloaded {0} URL(s) in {1} day(s) {2} hour(s) {3} minute(s) {4} second(s)\"\nmsgstr \"Se han terminado de descargar {0} elementos en {1} días, {2} horas, {3} minutos y {4} segundos.\"\n\n#: youtube_dl_gui/mainframe.py:119\nmsgid \"Downloads completed\"\nmsgstr \"Se han completado todas las descargas.\"\n\n#: youtube_dl_gui/mainframe.py:120\nmsgid \"Total Progress: {0:.1f}% | Queued ({1}) Paused ({2}) Active ({3}) Completed ({4}) Error ({5})\"\nmsgstr \"Progreso total: {0:.1f}% | Restantes ({1}) En pausa ({2}) Activas ({3}) Terminadas ({4}) Fallidas ({5})\"\n\n#: youtube_dl_gui/mainframe.py:121\nmsgid \"Stopping downloads\"\nmsgstr \"Parando descargas en curso.\"\n\n#: youtube_dl_gui/mainframe.py:122\nmsgid \"Downloads stopped\"\nmsgstr \"Se han detenido todas las descargas en curso.\"\n\n#: youtube_dl_gui/mainframe.py:123\nmsgid \"You need to provide at least one URL\"\nmsgstr \"Es necesario proporcionar al menos una dirección de descarga para poder empezar.\"\n\n#: youtube_dl_gui/mainframe.py:124\nmsgid \"Downloads started\"\nmsgstr \"Comenzando descargas.\"\n\n#: youtube_dl_gui/mainframe.py:125\nmsgid \"Choose Directory\"\nmsgstr \"Elegir carpeta\"\n\n#: youtube_dl_gui/mainframe.py:127\nmsgid \"Download in progress. Please wait for all downloads to complete\"\nmsgstr \"Ha empezado la descarga, un momento.\"\n\n#: youtube_dl_gui/mainframe.py:128\nmsgid \"Update already in progress\"\nmsgstr \"Ya se está actualizando.\"\n\n#: youtube_dl_gui/mainframe.py:130\nmsgid \"Downloading latest youtube-dl. Please wait...\"\nmsgstr \"Descargando última versión de youtube-dl. Un momento...\"\n\n#: youtube_dl_gui/mainframe.py:131\nmsgid \"Youtube-dl download failed [{0}]\"\nmsgstr \"No se ha podido descargar youtube-dl [{0}].\"\n\n#: youtube_dl_gui/mainframe.py:132\nmsgid \"Successfully downloaded youtube-dl\"\nmsgstr \"El nuevo youtube-dl se ha descargado sin problemas.\"\n\n#: youtube_dl_gui/mainframe.py:134\nmsgid \"Unable to open directory: '{dir}'. The specified path does not exist\"\nmsgstr \"No se ha podido abrir la carpeta: «{dir}». La ruta no existe.\"\n\n#: youtube_dl_gui/mainframe.py:136\nmsgid \"Error while shutting down. Make sure you typed the correct password\"\nmsgstr \"No se ha podido apagar el equipo. Asegúrate de que la contraseña esté bien escrita.\"\n\n#: youtube_dl_gui/mainframe.py:138\nmsgid \"Shutting down system\"\nmsgstr \"Apagando el equipo\"\n\n#: youtube_dl_gui/mainframe.py:141\nmsgid \"Extension\"\nmsgstr \"Extensión\"\n\n#: youtube_dl_gui/mainframe.py:142\nmsgid \"Size\"\nmsgstr \"Tamaño\"\n\n#: youtube_dl_gui/mainframe.py:143\nmsgid \"Percent\"\nmsgstr \"Porcentaje\"\n\n#: youtube_dl_gui/mainframe.py:144\nmsgid \"ETA\"\nmsgstr \"Tiempo estimado\"\n\n#: youtube_dl_gui/mainframe.py:145\nmsgid \"Speed\"\nmsgstr \"Velocidad\"\n\n#: youtube_dl_gui/mainframe.py:146\nmsgid \"Status\"\nmsgstr \"Estado\"\n\n#: youtube_dl_gui/mainframe.py:235\nmsgid \"Get URL\"\nmsgstr \"Copiar dirección URL al portapapeles\"\n\n#: youtube_dl_gui/mainframe.py:236\nmsgid \"Get command\"\nmsgstr \"Copiar los argumentos de descarga al portapapeles\"\n\n#: youtube_dl_gui/mainframe.py:237\nmsgid \"Open destination\"\nmsgstr \"Abrir carpeta de descarga\"\n\n#: youtube_dl_gui/mainframe.py:238\nmsgid \"Re-enter\"\nmsgstr \"Reintroducir\"\n\n#: youtube_dl_gui/mainframe.py:458\nmsgid \"Resume\"\nmsgstr \"Reanudar\"\n\n#: youtube_dl_gui/mainframe.py:480\nmsgid \"Video\"\nmsgstr \"Vídeo\"\n\n#: youtube_dl_gui/mainframe.py:484\nmsgid \"Audio\"\nmsgstr \"Audio\"\n\n#: youtube_dl_gui/mainframe.py:516\nmsgid \"No items selected. Please pick an action\"\nmsgstr \"No has seleccionado nada, elige una acción.\"\n\n#: youtube_dl_gui/mainframe.py:516\nmsgid \"Remove all\"\nmsgstr \"Borrar todo\"\n\n#: youtube_dl_gui/mainframe.py:516\nmsgid \"Remove completed\"\nmsgstr \"Borrado completado.\"\n\n#: youtube_dl_gui/mainframe.py:534\nmsgid \"Are you sure you want to remove selected items?\"\nmsgstr \"¿Seguro que quieres borrar los elementos seleccionados?\"\n\n#: youtube_dl_gui/mainframe.py:546\nmsgid \"Item is active, cannot remove\"\nmsgstr \"El elemento se está utilizando y no se puede borrar.\"\n\n#: youtube_dl_gui/mainframe.py:579\nmsgid \"Item is not completed\"\nmsgstr \"El elemento todavía no se ha descargado.\"\n\n#: youtube_dl_gui/mainframe.py:668\nmsgid \"Update in progress. Please wait for the update to complete\"\nmsgstr \"Hay una descarga en curso, espera a que termine.\"\n\n#: youtube_dl_gui/mainframe.py:716\nmsgid \"Logging is disabled\"\nmsgstr \"El registro de errores está desactivado.\"\n\n#: youtube_dl_gui/mainframe.py:891\nmsgid \"Shutdown\"\nmsgstr \"Apagar\"\n\n#: youtube_dl_gui/mainframe.py:891\nmsgid \"Shutting down in {0} second(s)\"\nmsgstr \"Apagando el equipo en {0} seg.\"\n\n#: youtube_dl_gui/mainframe.py:980\nmsgid \"No items to download\"\nmsgstr \"No hay nada para descargar.\"\n\n#: youtube_dl_gui/mainframe.py:1040\nmsgid \"Updates are disabled for your system. Please use the system's package manager to update youtube-dl.\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:1065\nmsgid \"Are you sure you want to exit?\"\nmsgstr \"¿Seguro que quieres salir?\"\n\n#: youtube_dl_gui/mainframe.py:1065\nmsgid \"Exit\"\nmsgstr \"Salir\"\n\n#: youtube_dl_gui/mainframe.py:1306 youtube_dl_gui/mainframe.py:1456\nmsgid \"Cancel\"\nmsgstr \"Cancelar\"\n\n#: youtube_dl_gui/mainframe.py:1455\nmsgid \"OK\"\nmsgstr \"Aceptar\"\n\n#: youtube_dl_gui/optionsframe.py:65\nmsgid \"Reset\"\nmsgstr \"Reiniciar\"\n\n#: youtube_dl_gui/optionsframe.py:66\nmsgid \"Close\"\nmsgstr \"Cerrar\"\n\n#: youtube_dl_gui/optionsframe.py:72\nmsgid \"General\"\nmsgstr \"General\"\n\n#: youtube_dl_gui/optionsframe.py:73\nmsgid \"Formats\"\nmsgstr \"Formatos\"\n\n#: youtube_dl_gui/optionsframe.py:74\nmsgid \"Downloads\"\nmsgstr \"Descargas\"\n\n#: youtube_dl_gui/optionsframe.py:75\nmsgid \"Advanced\"\nmsgstr \"Avanzado\"\n\n#: youtube_dl_gui/optionsframe.py:76\nmsgid \"Extra\"\nmsgstr \"Extra\"\n\n#: youtube_dl_gui/optionsframe.py:310\nmsgid \"Language\"\nmsgstr \"Idioma\"\n\n#: youtube_dl_gui/optionsframe.py:313\nmsgid \"Filename format\"\nmsgstr \"Nomenclatura de archivos\"\n\n#: youtube_dl_gui/optionsframe.py:318\nmsgid \"Filename options\"\nmsgstr \"Opciones de nomenclatura\"\n\n#: youtube_dl_gui/optionsframe.py:319\nmsgid \"Restrict filenames to ASCII\"\nmsgstr \"Limitar nombres de archivo a ASCII\"\n\n#: youtube_dl_gui/optionsframe.py:321\nmsgid \"More options\"\nmsgstr \"Más opciones\"\n\n#: youtube_dl_gui/optionsframe.py:322\nmsgid \"Confirm on exit\"\nmsgstr \"Confirmar salida\"\n\n#: youtube_dl_gui/optionsframe.py:323\nmsgid \"Confirm item deletion\"\nmsgstr \"Confirmar al borrar archivos\"\n\n#: youtube_dl_gui/optionsframe.py:324\nmsgid \"Inform me on download completion\"\nmsgstr \"Avisarme cuando terminen las descargas\"\n\n#: youtube_dl_gui/optionsframe.py:326\nmsgid \"Shutdown on download completion\"\nmsgstr \"Apagar al terminar las descargas\"\n\n#: youtube_dl_gui/optionsframe.py:337\nmsgid \"SUDO password\"\nmsgstr \"Contraseña de superusuario\"\n\n#: youtube_dl_gui/optionsframe.py:415 youtube_dl_gui/optionsframe.py:816\nmsgid \"In order for the changes to take effect please restart {0}\"\nmsgstr \"Reinicia {0} para que los cambios surtan efecto.\"\n\n#: youtube_dl_gui/optionsframe.py:416 youtube_dl_gui/optionsframe.py:817\nmsgid \"Restart\"\nmsgstr \"Reiniciar\"\n\n#: youtube_dl_gui/optionsframe.py:463\nmsgid \"high\"\nmsgstr \"Alta\"\n\n#: youtube_dl_gui/optionsframe.py:463\nmsgid \"low\"\nmsgstr \"Baja\"\n\n#: youtube_dl_gui/optionsframe.py:463\nmsgid \"mid\"\nmsgstr \"Intermedia\"\n\n#: youtube_dl_gui/optionsframe.py:468\nmsgid \"Video formats\"\nmsgstr \"Formatos de vídeo\"\n\n#: youtube_dl_gui/optionsframe.py:471\nmsgid \"Audio formats\"\nmsgstr \"Formatos de audio\"\n\n#: youtube_dl_gui/optionsframe.py:474\nmsgid \"Post-Process options\"\nmsgstr \"Opciones de posproceso\"\n\n#: youtube_dl_gui/optionsframe.py:475\nmsgid \"Keep original files\"\nmsgstr \"Conservar los archivos originales\"\n\n#: youtube_dl_gui/optionsframe.py:476\nmsgid \"Extract audio from video file\"\nmsgstr \"Extraer el audio del vídeo\"\n\n#: youtube_dl_gui/optionsframe.py:477\nmsgid \"Embed thumbnail in audio file\"\nmsgstr \"Incluir una miniatura en el archivo de audio\"\n\n#: youtube_dl_gui/optionsframe.py:478\nmsgid \"Add metadata to file\"\nmsgstr \"Incluir metadatos en el archivo\"\n\n#: youtube_dl_gui/optionsframe.py:480\nmsgid \"Audio quality\"\nmsgstr \"Calidad del audio\"\n\n#: youtube_dl_gui/optionsframe.py:538\nmsgid \"English\"\nmsgstr \"Inglés\"\n\n#: youtube_dl_gui/optionsframe.py:539\nmsgid \"French\"\nmsgstr \"Francés\"\n\n#: youtube_dl_gui/optionsframe.py:540\nmsgid \"German\"\nmsgstr \"Alemán\"\n\n#: youtube_dl_gui/optionsframe.py:541\nmsgid \"Greek\"\nmsgstr \"Griego\"\n\n#: youtube_dl_gui/optionsframe.py:542\nmsgid \"Hebrew\"\nmsgstr \"Hebreo\"\n\n#: youtube_dl_gui/optionsframe.py:543\nmsgid \"Italian\"\nmsgstr \"Italiano\"\n\n#: youtube_dl_gui/optionsframe.py:544\nmsgid \"Portuguese\"\nmsgstr \"Portugués\"\n\n#: youtube_dl_gui/optionsframe.py:545\nmsgid \"Russian\"\nmsgstr \"Ruso\"\n\n#: youtube_dl_gui/optionsframe.py:546\nmsgid \"Spanish\"\nmsgstr \"Español\"\n\n#: youtube_dl_gui/optionsframe.py:547\nmsgid \"Swedish\"\nmsgstr \"Sueco\"\n\n#: youtube_dl_gui/optionsframe.py:548\nmsgid \"Turkish\"\nmsgstr \"Turco\"\n\n#: youtube_dl_gui/optionsframe.py:564\nmsgid \"None\"\nmsgstr \"Ninguno\"\n\n#: youtube_dl_gui/optionsframe.py:565\nmsgid \"Automatic subtitles (YOUTUBE ONLY)\"\nmsgstr \"Descargar archivo con subtítulos automáticos (sólo en YouTube)\"\n\n#: youtube_dl_gui/optionsframe.py:566\nmsgid \"All available subtitles\"\nmsgstr \"Descargar todos los subtítulos disponibles\"\n\n#: youtube_dl_gui/optionsframe.py:567\nmsgid \"Subtitles by language\"\nmsgstr \"Idioma de subtítulos\"\n\n#: youtube_dl_gui/optionsframe.py:573\nmsgid \"Subtitles\"\nmsgstr \"Subtítulos\"\n\n#: youtube_dl_gui/optionsframe.py:577\nmsgid \"Subtitles options\"\nmsgstr \"Opciones de subtítulos\"\n\n#: youtube_dl_gui/optionsframe.py:578\nmsgid \"Embed subtitles into video file (mp4 ONLY)\"\nmsgstr \"Insertar los subtítulos en el vídeo (sólo para vídeos en mp4)\"\n\n#: youtube_dl_gui/optionsframe.py:580\nmsgid \"Playlist\"\nmsgstr \"Lista de reproducción\"\n\n#: youtube_dl_gui/optionsframe.py:586 youtube_dl_gui/optionsframe.py:591\nmsgid \"Max\"\nmsgstr \"Máximo\"\n\n#: youtube_dl_gui/optionsframe.py:589\nmsgid \"Filesize\"\nmsgstr \"Tamaño de archivo\"\n\n#: youtube_dl_gui/optionsframe.py:594\nmsgid \"Min\"\nmsgstr \"Mínimo\"\n\n#: youtube_dl_gui/optionsframe.py:723\nmsgid \"Retries\"\nmsgstr \"Reintentos\"\n\n#: youtube_dl_gui/optionsframe.py:726\nmsgid \"Authentication\"\nmsgstr \"Autenticación\"\n\n#: youtube_dl_gui/optionsframe.py:728\nmsgid \"Username\"\nmsgstr \"Nombre de usuario\"\n\n#: youtube_dl_gui/optionsframe.py:730\nmsgid \"Password\"\nmsgstr \"Contraseña\"\n\n#: youtube_dl_gui/optionsframe.py:732\nmsgid \"Video password\"\nmsgstr \"Contraseña del vídeo\"\n\n#: youtube_dl_gui/optionsframe.py:735\nmsgid \"Network\"\nmsgstr \"Red\"\n\n#: youtube_dl_gui/optionsframe.py:737\nmsgid \"Proxy\"\nmsgstr \"Proxy\"\n\n#: youtube_dl_gui/optionsframe.py:739\nmsgid \"User agent\"\nmsgstr \"Agente de usuario\"\n\n#: youtube_dl_gui/optionsframe.py:741\nmsgid \"Referer\"\nmsgstr \"Página de origen\"\n\n#: youtube_dl_gui/optionsframe.py:744\nmsgid \"Logging\"\nmsgstr \"Registro de errores\"\n\n#: youtube_dl_gui/optionsframe.py:746\nmsgid \"Enable log\"\nmsgstr \"Registrar los errores del programa\"\n\n#: youtube_dl_gui/optionsframe.py:747\nmsgid \"View\"\nmsgstr \"Ver registro\"\n\n#: youtube_dl_gui/optionsframe.py:748\nmsgid \"Clear\"\nmsgstr \"Borrar registro\"\n\n#: youtube_dl_gui/optionsframe.py:858\nmsgid \"Youtube-dl command line options (e.g. --help)\"\nmsgstr \"Argumentos adicionales para Youtube-dl (p. ej. --help)\"\n\n#: youtube_dl_gui/optionsframe.py:861\nmsgid \"Extra options\"\nmsgstr \"Otras opciones\"\n\n#: youtube_dl_gui/optionsframe.py:863\nmsgid \"Debug youtube-dl\"\nmsgstr \"Depurar youtube-dl\"\n\n#: youtube_dl_gui/optionsframe.py:864\nmsgid \"Ignore errors\"\nmsgstr \"Ignorar errores\"\n\n#: youtube_dl_gui/optionsframe.py:865\nmsgid \"Ignore youtube-dl config\"\nmsgstr \"Ignorar los ajustes de youtube-dl\"\n\n#: youtube_dl_gui/optionsframe.py:866\nmsgid \"No mtime\"\nmsgstr \"Sin mtime\"\n\n#: youtube_dl_gui/optionsframe.py:867\nmsgid \"Prefer native HLS\"\nmsgstr \"Utilizar HLS nativo cuando sea posible\"\n\n#: youtube_dl_gui/optionsframe.py:928\nmsgid \"Log Viewer\"\nmsgstr \"Visor de errores\"\n"
  },
  {
    "path": "youtube_dl_gui/locale/fr_FR/LC_MESSAGES/youtube_dl_gui.po",
    "content": "# Youtube-dlG localization file.\n# FIRST AUTHOR: Sotiris Papadopoulos <ytubedlg@gmail.com>, 2015.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: youtube-dlg 0.4\\n\"\n\"POT-Creation-Date: 2018-01-15 16:42+EET\\n\"\n\"PO-Revision-Date: 2017-08-22 08:44+0200\\n\"\n\"Last-Translator: fat115 <fat115@framasoft.org>\\n\"\n\"Language-Team: fr\\n\"\n\"Language: fr\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: pygettext.py 1.5\\n\"\n\n#: youtube_dl_gui/__init__.py:91\nmsgid \"Error\"\nmsgstr \"\"\n\n#: youtube_dl_gui/__init__.py:91\nmsgid \"Failed to locate youtube-dl and updates are disabled\"\nmsgstr \"\"\n\n#: youtube_dl_gui/formats.py:9 youtube_dl_gui/formats.py:109\nmsgid \"ID\"\nmsgstr \"ID\"\n\n#: youtube_dl_gui/formats.py:10 youtube_dl_gui/formats.py:110\n#: youtube_dl_gui/mainframe.py:140\nmsgid \"Title\"\nmsgstr \"Titre\"\n\n#: youtube_dl_gui/formats.py:11 youtube_dl_gui/formats.py:111\nmsgid \"Title + ID\"\nmsgstr \"Titre + ID\"\n\n#: youtube_dl_gui/formats.py:12 youtube_dl_gui/formats.py:112\nmsgid \"Title + Quality\"\nmsgstr \"Titre + Qualité\"\n\n#: youtube_dl_gui/formats.py:13 youtube_dl_gui/formats.py:113\nmsgid \"Title + ID + Quality\"\nmsgstr \"Titre + ID + Qualité\"\n\n#: youtube_dl_gui/formats.py:14 youtube_dl_gui/formats.py:114\nmsgid \"Custom\"\nmsgstr \"Personnalisé\"\n\n#: youtube_dl_gui/formats.py:19 youtube_dl_gui/formats.py:119\n#: youtube_dl_gui/mainframe.py:503 youtube_dl_gui/mainframe.py:506\nmsgid \"default\"\nmsgstr \"par défaut\"\n\n#: youtube_dl_gui/mainframe.py:97\nmsgid \"Enter URLs below\"\nmsgstr \"Entrer les URLs ci-dessous\"\n\n#: youtube_dl_gui/mainframe.py:98\nmsgid \"Update\"\nmsgstr \"Mise à jour\"\n\n#: youtube_dl_gui/mainframe.py:99 youtube_dl_gui/optionsframe.py:41\nmsgid \"Options\"\nmsgstr \"Options\"\n\n#: youtube_dl_gui/mainframe.py:100 youtube_dl_gui/optionsframe.py:584\nmsgid \"Stop\"\nmsgstr \"Arrêter\"\n\n#: youtube_dl_gui/mainframe.py:101\nmsgid \"Info\"\nmsgstr \"Info\"\n\n#: youtube_dl_gui/mainframe.py:102\nmsgid \"Welcome\"\nmsgstr \"Bienvenue\"\n\n#: youtube_dl_gui/mainframe.py:103\nmsgid \"Warning\"\nmsgstr \"Avertissement\"\n\n#: youtube_dl_gui/mainframe.py:105\nmsgid \"Add\"\nmsgstr \"Ajouter\"\n\n#: youtube_dl_gui/mainframe.py:106\nmsgid \"Download list\"\nmsgstr \"Liste de téléchargements\"\n\n#: youtube_dl_gui/mainframe.py:107 youtube_dl_gui/mainframe.py:516\n#: youtube_dl_gui/mainframe.py:534\nmsgid \"Delete\"\nmsgstr \"Effacer\"\n\n#: youtube_dl_gui/mainframe.py:108\nmsgid \"Play\"\nmsgstr \"Lire\"\n\n#: youtube_dl_gui/mainframe.py:109\nmsgid \"Up\"\nmsgstr \"Monter\"\n\n#: youtube_dl_gui/mainframe.py:110\nmsgid \"Down\"\nmsgstr \"Descendre\"\n\n#: youtube_dl_gui/mainframe.py:111\nmsgid \"Reload\"\nmsgstr \"Recharger\"\n\n#: youtube_dl_gui/mainframe.py:112 youtube_dl_gui/mainframe.py:448\n#: youtube_dl_gui/mainframe.py:649\nmsgid \"Pause\"\nmsgstr \"Pause\"\n\n#: youtube_dl_gui/mainframe.py:113 youtube_dl_gui/mainframe.py:865\n#: youtube_dl_gui/mainframe.py:866 youtube_dl_gui/optionsframe.py:582\nmsgid \"Start\"\nmsgstr \"Démarrer\"\n\n#: youtube_dl_gui/mainframe.py:114\nmsgid \"About\"\nmsgstr \"À propos\"\n\n#: youtube_dl_gui/mainframe.py:115\nmsgid \"View Log\"\nmsgstr \"Afficher le journal\"\n\n#: youtube_dl_gui/mainframe.py:117\nmsgid \"Successfully downloaded {0} URL(s) in {1} day(s) {2} hour(s) {3} minute(s) {4} second(s)\"\nmsgstr \"Téléchargements réussis {0} URL(s) en {1} jour(s) {2} heure(s) {3} minute(s) {4} seconde(s)\"\n\n#: youtube_dl_gui/mainframe.py:119\nmsgid \"Downloads completed\"\nmsgstr \"Téléchargements terminés\"\n\n#: youtube_dl_gui/mainframe.py:120\nmsgid \"Total Progress: {0:.1f}% | Queued ({1}) Paused ({2}) Active ({3}) Completed ({4}) Error ({5})\"\nmsgstr \"Progression totale : {0:.1f}% | En file ({1}) En pause ({2}) Actif ({3}) Complété ({4}) Erreur ({5})\"\n\n#: youtube_dl_gui/mainframe.py:121\nmsgid \"Stopping downloads\"\nmsgstr \"Arrêt des téléchargements\"\n\n#: youtube_dl_gui/mainframe.py:122\nmsgid \"Downloads stopped\"\nmsgstr \"Téléchargements arrêtés\"\n\n#: youtube_dl_gui/mainframe.py:123\nmsgid \"You need to provide at least one URL\"\nmsgstr \"Vous devez fournir au moins une URL\"\n\n#: youtube_dl_gui/mainframe.py:124\nmsgid \"Downloads started\"\nmsgstr \"Téléchargements démarrés\"\n\n#: youtube_dl_gui/mainframe.py:125\nmsgid \"Choose Directory\"\nmsgstr \"Sélectionner le répertoire\"\n\n#: youtube_dl_gui/mainframe.py:127\nmsgid \"Download in progress. Please wait for all downloads to complete\"\nmsgstr \"Téléchargement en cours. Merci d'attendre qu'ils soient tous terminés\"\n\n#: youtube_dl_gui/mainframe.py:128\nmsgid \"Update already in progress\"\nmsgstr \"Mise à jour déjà en cours\"\n\n#: youtube_dl_gui/mainframe.py:130\nmsgid \"Downloading latest youtube-dl. Please wait...\"\nmsgstr \"Mise à jour de youtube-dl. Merci de patienter...\"\n\n#: youtube_dl_gui/mainframe.py:131\nmsgid \"Youtube-dl download failed [{0}]\"\nmsgstr \"Echec de la mise à jour de youtube-dl [{0}]\"\n\n#: youtube_dl_gui/mainframe.py:132\nmsgid \"Successfully downloaded youtube-dl\"\nmsgstr \"Mise à jour de youtube-dl réussie.\"\n\n#: youtube_dl_gui/mainframe.py:134\nmsgid \"Unable to open directory: '{dir}'. The specified path does not exist\"\nmsgstr \"Impossible d'ouvrir le répertoire: '{dir}'. Le chemin n'existe pas\"\n\n#: youtube_dl_gui/mainframe.py:136\nmsgid \"Error while shutting down. Make sure you typed the correct password\"\nmsgstr \"Erreur d'arrêt du système. Vérifiez le mot de passe saisi\"\n\n#: youtube_dl_gui/mainframe.py:138\nmsgid \"Shutting down system\"\nmsgstr \"Arrêt du système\"\n\n#: youtube_dl_gui/mainframe.py:141\nmsgid \"Extension\"\nmsgstr \"Extension\"\n\n#: youtube_dl_gui/mainframe.py:142\nmsgid \"Size\"\nmsgstr \"Taille\"\n\n#: youtube_dl_gui/mainframe.py:143\nmsgid \"Percent\"\nmsgstr \"Pourcents\"\n\n#: youtube_dl_gui/mainframe.py:144\nmsgid \"ETA\"\nmsgstr \"ETA\"\n\n#: youtube_dl_gui/mainframe.py:145\nmsgid \"Speed\"\nmsgstr \"Vitesse\"\n\n#: youtube_dl_gui/mainframe.py:146\nmsgid \"Status\"\nmsgstr \"État\"\n\n#: youtube_dl_gui/mainframe.py:235\nmsgid \"Get URL\"\nmsgstr \"Récupérer l'URL\"\n\n#: youtube_dl_gui/mainframe.py:236\nmsgid \"Get command\"\nmsgstr \"Récupérer la commande\"\n\n#: youtube_dl_gui/mainframe.py:237\nmsgid \"Open destination\"\nmsgstr \"Ouvrir le répertoire de destination\"\n\n#: youtube_dl_gui/mainframe.py:238\nmsgid \"Re-enter\"\nmsgstr \"Re-entrer\"\n\n#: youtube_dl_gui/mainframe.py:458\nmsgid \"Resume\"\nmsgstr \"Reprendre\"\n\n#: youtube_dl_gui/mainframe.py:480\nmsgid \"Video\"\nmsgstr \"Vidéo\"\n\n#: youtube_dl_gui/mainframe.py:484\nmsgid \"Audio\"\nmsgstr \"Audio\"\n\n#: youtube_dl_gui/mainframe.py:516\nmsgid \"No items selected. Please pick an action\"\nmsgstr \"Pas d'élément sélectionné. Choisissez une action\"\n\n#: youtube_dl_gui/mainframe.py:516\nmsgid \"Remove all\"\nmsgstr \"Tout enlever\"\n\n#: youtube_dl_gui/mainframe.py:516\nmsgid \"Remove completed\"\nmsgstr \"Enlèvement terminé\"\n\n#: youtube_dl_gui/mainframe.py:534\nmsgid \"Are you sure you want to remove selected items?\"\nmsgstr \"Êtes vous sûr de vouloir enlever les éléments sélectionnées ?\"\n\n#: youtube_dl_gui/mainframe.py:546\nmsgid \"Item is active, cannot remove\"\nmsgstr \"Élément actif, ne peut être enlevé\"\n\n#: youtube_dl_gui/mainframe.py:579\nmsgid \"Item is not completed\"\nmsgstr \"Élément incomplet\"\n\n#: youtube_dl_gui/mainframe.py:668\nmsgid \"Update in progress. Please wait for the update to complete\"\nmsgstr \"Mise à jour en cours. Veuillez attendre qu'elle soit terminée\"\n\n#: youtube_dl_gui/mainframe.py:716\nmsgid \"Logging is disabled\"\nmsgstr \"La journalisation est désactivée\"\n\n#: youtube_dl_gui/mainframe.py:891\nmsgid \"Shutdown\"\nmsgstr \"Arrêt\"\n\n#: youtube_dl_gui/mainframe.py:891\nmsgid \"Shutting down in {0} second(s)\"\nmsgstr \"Arrêt dans {0} seconde(s)\"\n\n#: youtube_dl_gui/mainframe.py:980\nmsgid \"No items to download\"\nmsgstr \"Pas d'élément à télécharger\"\n\n#: youtube_dl_gui/mainframe.py:1040\nmsgid \"Updates are disabled for your system. Please use the system's package manager to update youtube-dl.\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:1065\nmsgid \"Are you sure you want to exit?\"\nmsgstr \"Êtes vous sûr de vouloir quitter ?\"\n\n#: youtube_dl_gui/mainframe.py:1065\nmsgid \"Exit\"\nmsgstr \"Quitter\"\n\n#: youtube_dl_gui/mainframe.py:1306 youtube_dl_gui/mainframe.py:1456\nmsgid \"Cancel\"\nmsgstr \"Annuler\"\n\n#: youtube_dl_gui/mainframe.py:1455\nmsgid \"OK\"\nmsgstr \"OK\"\n\n#: youtube_dl_gui/optionsframe.py:65\nmsgid \"Reset\"\nmsgstr \"Remise à zéro\"\n\n#: youtube_dl_gui/optionsframe.py:66\nmsgid \"Close\"\nmsgstr \"Fermer\"\n\n#: youtube_dl_gui/optionsframe.py:72\nmsgid \"General\"\nmsgstr \"Général\"\n\n#: youtube_dl_gui/optionsframe.py:73\nmsgid \"Formats\"\nmsgstr \"Formats\"\n\n#: youtube_dl_gui/optionsframe.py:74\nmsgid \"Downloads\"\nmsgstr \"Téléchargements\"\n\n#: youtube_dl_gui/optionsframe.py:75\nmsgid \"Advanced\"\nmsgstr \"Avancé\"\n\n#: youtube_dl_gui/optionsframe.py:76\nmsgid \"Extra\"\nmsgstr \"Extra\"\n\n#: youtube_dl_gui/optionsframe.py:310\nmsgid \"Language\"\nmsgstr \"Langue\"\n\n#: youtube_dl_gui/optionsframe.py:313\nmsgid \"Filename format\"\nmsgstr \"Format du nom de fichier\"\n\n#: youtube_dl_gui/optionsframe.py:318\nmsgid \"Filename options\"\nmsgstr \"Options des noms de fichiers\"\n\n#: youtube_dl_gui/optionsframe.py:319\nmsgid \"Restrict filenames to ASCII\"\nmsgstr \"Convertir les noms en ASCII\"\n\n#: youtube_dl_gui/optionsframe.py:321\nmsgid \"More options\"\nmsgstr \"Plus d'options\"\n\n#: youtube_dl_gui/optionsframe.py:322\nmsgid \"Confirm on exit\"\nmsgstr \"Confirmation de sortie\"\n\n#: youtube_dl_gui/optionsframe.py:323\nmsgid \"Confirm item deletion\"\nmsgstr \"Confirmation d'effacement\"\n\n#: youtube_dl_gui/optionsframe.py:324\nmsgid \"Inform me on download completion\"\nmsgstr \"M'informer de la fin des téléchargements\"\n\n#: youtube_dl_gui/optionsframe.py:326\nmsgid \"Shutdown on download completion\"\nmsgstr \"Arrêter à la fin des téléchargements\"\n\n#: youtube_dl_gui/optionsframe.py:337\nmsgid \"SUDO password\"\nmsgstr \"Mot de passe SUDO\"\n\n#: youtube_dl_gui/optionsframe.py:415 youtube_dl_gui/optionsframe.py:816\nmsgid \"In order for the changes to take effect please restart {0}\"\nmsgstr \"Pour que les changements prennent effet, veuillez redémarrer {0}\"\n\n#: youtube_dl_gui/optionsframe.py:416 youtube_dl_gui/optionsframe.py:817\nmsgid \"Restart\"\nmsgstr \"Redémarrer\"\n\n#: youtube_dl_gui/optionsframe.py:463\nmsgid \"high\"\nmsgstr \"haute\"\n\n#: youtube_dl_gui/optionsframe.py:463\nmsgid \"low\"\nmsgstr \"basse\"\n\n#: youtube_dl_gui/optionsframe.py:463\nmsgid \"mid\"\nmsgstr \"moyenne\"\n\n#: youtube_dl_gui/optionsframe.py:468\nmsgid \"Video formats\"\nmsgstr \"Formats Vidéo\"\n\n#: youtube_dl_gui/optionsframe.py:471\nmsgid \"Audio formats\"\nmsgstr \"Formats Audio\"\n\n#: youtube_dl_gui/optionsframe.py:474\nmsgid \"Post-Process options\"\nmsgstr \"Options post-traitement\"\n\n#: youtube_dl_gui/optionsframe.py:475\nmsgid \"Keep original files\"\nmsgstr \"Conserver les fichiers originaux\"\n\n#: youtube_dl_gui/optionsframe.py:476\nmsgid \"Extract audio from video file\"\nmsgstr \"Extraire l'audio du fichier vidéo\"\n\n#: youtube_dl_gui/optionsframe.py:477\nmsgid \"Embed thumbnail in audio file\"\nmsgstr \"Inclure une vignette dans le fichier audio\"\n\n#: youtube_dl_gui/optionsframe.py:478\nmsgid \"Add metadata to file\"\nmsgstr \"Ajouter les métadonnées au fichier\"\n\n#: youtube_dl_gui/optionsframe.py:480\nmsgid \"Audio quality\"\nmsgstr \"Qualité Audio\"\n\n#: youtube_dl_gui/optionsframe.py:538\nmsgid \"English\"\nmsgstr \"Anglais\"\n\n#: youtube_dl_gui/optionsframe.py:539\nmsgid \"French\"\nmsgstr \"Français\"\n\n#: youtube_dl_gui/optionsframe.py:540\nmsgid \"German\"\nmsgstr \"Allemand\"\n\n#: youtube_dl_gui/optionsframe.py:541\nmsgid \"Greek\"\nmsgstr \"Grec\"\n\n#: youtube_dl_gui/optionsframe.py:542\nmsgid \"Hebrew\"\nmsgstr \"Hébreu\"\n\n#: youtube_dl_gui/optionsframe.py:543\nmsgid \"Italian\"\nmsgstr \"Italien\"\n\n#: youtube_dl_gui/optionsframe.py:544\nmsgid \"Portuguese\"\nmsgstr \"Portugais\"\n\n#: youtube_dl_gui/optionsframe.py:545\nmsgid \"Russian\"\nmsgstr \"Russe\"\n\n#: youtube_dl_gui/optionsframe.py:546\nmsgid \"Spanish\"\nmsgstr \"Espagnol\"\n\n#: youtube_dl_gui/optionsframe.py:547\nmsgid \"Swedish\"\nmsgstr \"Suédois\"\n\n#: youtube_dl_gui/optionsframe.py:548\nmsgid \"Turkish\"\nmsgstr \"Turc\"\n\n#: youtube_dl_gui/optionsframe.py:564\nmsgid \"None\"\nmsgstr \"Aucun\"\n\n#: youtube_dl_gui/optionsframe.py:565\nmsgid \"Automatic subtitles (YOUTUBE ONLY)\"\nmsgstr \"Sous-titre automatique (YOUTUBE uniquement)\"\n\n#: youtube_dl_gui/optionsframe.py:566\nmsgid \"All available subtitles\"\nmsgstr \"Tous les sous-titres disponibles\"\n\n#: youtube_dl_gui/optionsframe.py:567\nmsgid \"Subtitles by language\"\nmsgstr \"Sous-titres par langue\"\n\n#: youtube_dl_gui/optionsframe.py:573\nmsgid \"Subtitles\"\nmsgstr \"Sous-titres\"\n\n#: youtube_dl_gui/optionsframe.py:577\nmsgid \"Subtitles options\"\nmsgstr \"Options de sous-titres\"\n\n#: youtube_dl_gui/optionsframe.py:578\nmsgid \"Embed subtitles into video file (mp4 ONLY)\"\nmsgstr \"Inclure les sous-titres dans la vidéo (mp4 uniquement)\"\n\n#: youtube_dl_gui/optionsframe.py:580\nmsgid \"Playlist\"\nmsgstr \"Liste de lecture\"\n\n#: youtube_dl_gui/optionsframe.py:586 youtube_dl_gui/optionsframe.py:591\nmsgid \"Max\"\nmsgstr \"Max\"\n\n#: youtube_dl_gui/optionsframe.py:589\nmsgid \"Filesize\"\nmsgstr \"Taille de fichier\"\n\n#: youtube_dl_gui/optionsframe.py:594\nmsgid \"Min\"\nmsgstr \"Min\"\n\n#: youtube_dl_gui/optionsframe.py:723\nmsgid \"Retries\"\nmsgstr \"Essais\"\n\n#: youtube_dl_gui/optionsframe.py:726\nmsgid \"Authentication\"\nmsgstr \"Authentification\"\n\n#: youtube_dl_gui/optionsframe.py:728\nmsgid \"Username\"\nmsgstr \"Utilisateur\"\n\n#: youtube_dl_gui/optionsframe.py:730\nmsgid \"Password\"\nmsgstr \"Mot de passe\"\n\n#: youtube_dl_gui/optionsframe.py:732\nmsgid \"Video password\"\nmsgstr \"Mot de passe vidéo\"\n\n#: youtube_dl_gui/optionsframe.py:735\nmsgid \"Network\"\nmsgstr \"Réseau\"\n\n#: youtube_dl_gui/optionsframe.py:737\nmsgid \"Proxy\"\nmsgstr \"Proxy\"\n\n#: youtube_dl_gui/optionsframe.py:739\nmsgid \"User agent\"\nmsgstr \"User agent\"\n\n#: youtube_dl_gui/optionsframe.py:741\nmsgid \"Referer\"\nmsgstr \"Referer\"\n\n#: youtube_dl_gui/optionsframe.py:744\nmsgid \"Logging\"\nmsgstr \"Journalisation\"\n\n#: youtube_dl_gui/optionsframe.py:746\nmsgid \"Enable log\"\nmsgstr \"Activer le journal\"\n\n#: youtube_dl_gui/optionsframe.py:747\nmsgid \"View\"\nmsgstr \"Afficher le journal\"\n\n#: youtube_dl_gui/optionsframe.py:748\nmsgid \"Clear\"\nmsgstr \"Effacer le journal\"\n\n#: youtube_dl_gui/optionsframe.py:858\nmsgid \"Youtube-dl command line options (e.g. --help)\"\nmsgstr \"Options de ligne de commande youtube-dl (ex : --help)\"\n\n#: youtube_dl_gui/optionsframe.py:861\nmsgid \"Extra options\"\nmsgstr \"Options supplémentaires\"\n\n#: youtube_dl_gui/optionsframe.py:863\nmsgid \"Debug youtube-dl\"\nmsgstr \"Déboguer youtube-dl\"\n\n#: youtube_dl_gui/optionsframe.py:864\nmsgid \"Ignore errors\"\nmsgstr \"Ignorer les erreurs\"\n\n#: youtube_dl_gui/optionsframe.py:865\nmsgid \"Ignore youtube-dl config\"\nmsgstr \"Ignorer config youtube-dl\"\n\n#: youtube_dl_gui/optionsframe.py:866\nmsgid \"No mtime\"\nmsgstr \"Pas de mtime\"\n\n#: youtube_dl_gui/optionsframe.py:867\nmsgid \"Prefer native HLS\"\nmsgstr \"Préférer HLS natif\"\n\n#: youtube_dl_gui/optionsframe.py:928\nmsgid \"Log Viewer\"\nmsgstr \"Journal\"\n"
  },
  {
    "path": "youtube_dl_gui/locale/it_IT/LC_MESSAGES/youtube_dl_gui.po",
    "content": "# Youtube-dlG localization file.\n# FIRST AUTHOR: Sotiris Papadopoulos <ytubedlg@gmail.com>, 2015.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: youtube-dlg 0.4\\n\"\n\"POT-Creation-Date: 2018-01-15 16:42+EET\\n\"\n\"PO-Revision-Date: 2017-07-21 09:00+0200\\n\"\n\"Last-Translator: Luigi Baldoni <aloisio@gmx.com>\\n\"\n\"Language-Team: en\\n\"\n\"Language: it_IT\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: pygettext.py 1.5\\n\"\n\"X-Generator: Poedit 1.8.9\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n\"X-Language: it_IT\\n\"\n\"X-Source-Language: C\\n\"\n\n#: youtube_dl_gui/__init__.py:91\nmsgid \"Error\"\nmsgstr \"\"\n\n#: youtube_dl_gui/__init__.py:91\nmsgid \"Failed to locate youtube-dl and updates are disabled\"\nmsgstr \"\"\n\n#: youtube_dl_gui/formats.py:9 youtube_dl_gui/formats.py:109\nmsgid \"ID\"\nmsgstr \"ID\"\n\n#: youtube_dl_gui/formats.py:10 youtube_dl_gui/formats.py:110\n#: youtube_dl_gui/mainframe.py:140\nmsgid \"Title\"\nmsgstr \"Titolo\"\n\n#: youtube_dl_gui/formats.py:11 youtube_dl_gui/formats.py:111\nmsgid \"Title + ID\"\nmsgstr \"Titolo + ID\"\n\n#: youtube_dl_gui/formats.py:12 youtube_dl_gui/formats.py:112\nmsgid \"Title + Quality\"\nmsgstr \"Titolo + qualità\"\n\n#: youtube_dl_gui/formats.py:13 youtube_dl_gui/formats.py:113\nmsgid \"Title + ID + Quality\"\nmsgstr \"Titolo + ID + qualità\"\n\n#: youtube_dl_gui/formats.py:14 youtube_dl_gui/formats.py:114\nmsgid \"Custom\"\nmsgstr \"Personalizzato\"\n\n#: youtube_dl_gui/formats.py:19 youtube_dl_gui/formats.py:119\n#: youtube_dl_gui/mainframe.py:503 youtube_dl_gui/mainframe.py:506\nmsgid \"default\"\nmsgstr \"predefinito\"\n\n#: youtube_dl_gui/mainframe.py:97\nmsgid \"Enter URLs below\"\nmsgstr \"Immetti URL qui sotto\"\n\n#: youtube_dl_gui/mainframe.py:98\nmsgid \"Update\"\nmsgstr \"Aggiorna\"\n\n#: youtube_dl_gui/mainframe.py:99 youtube_dl_gui/optionsframe.py:41\nmsgid \"Options\"\nmsgstr \"Opzioni\"\n\n#: youtube_dl_gui/mainframe.py:100 youtube_dl_gui/optionsframe.py:584\nmsgid \"Stop\"\nmsgstr \"Fine\"\n\n#: youtube_dl_gui/mainframe.py:101\nmsgid \"Info\"\nmsgstr \"Informazioni\"\n\n#: youtube_dl_gui/mainframe.py:102\nmsgid \"Welcome\"\nmsgstr \"Benvenuto\"\n\n#: youtube_dl_gui/mainframe.py:103\nmsgid \"Warning\"\nmsgstr \"Avviso\"\n\n#: youtube_dl_gui/mainframe.py:105\nmsgid \"Add\"\nmsgstr \"Aggiungi\"\n\n#: youtube_dl_gui/mainframe.py:106\nmsgid \"Download list\"\nmsgstr \"Elenco scaricamenti\"\n\n#: youtube_dl_gui/mainframe.py:107 youtube_dl_gui/mainframe.py:516\n#: youtube_dl_gui/mainframe.py:534\nmsgid \"Delete\"\nmsgstr \"Elimina\"\n\n#: youtube_dl_gui/mainframe.py:108\nmsgid \"Play\"\nmsgstr \"Riproduci\"\n\n#: youtube_dl_gui/mainframe.py:109\nmsgid \"Up\"\nmsgstr \"Su\"\n\n#: youtube_dl_gui/mainframe.py:110\nmsgid \"Down\"\nmsgstr \"Giù\"\n\n#: youtube_dl_gui/mainframe.py:111\nmsgid \"Reload\"\nmsgstr \"Ricarica\"\n\n#: youtube_dl_gui/mainframe.py:112 youtube_dl_gui/mainframe.py:448\n#: youtube_dl_gui/mainframe.py:649\nmsgid \"Pause\"\nmsgstr \"Metti in pausa\"\n\n#: youtube_dl_gui/mainframe.py:113 youtube_dl_gui/mainframe.py:865\n#: youtube_dl_gui/mainframe.py:866 youtube_dl_gui/optionsframe.py:582\nmsgid \"Start\"\nmsgstr \"Inizio\"\n\n#: youtube_dl_gui/mainframe.py:114\nmsgid \"About\"\nmsgstr \"Informazioni\"\n\n#: youtube_dl_gui/mainframe.py:115\nmsgid \"View Log\"\nmsgstr \"Mostra registro\"\n\n#: youtube_dl_gui/mainframe.py:117\nmsgid \"Successfully downloaded {0} URL(s) in {1} day(s) {2} hour(s) {3} minute(s) {4} second(s)\"\nmsgstr \"Scaricate correttamente {0} URL in {1} giorni {2} ore {3} minuti {4} secondi\"\n\n#: youtube_dl_gui/mainframe.py:119\nmsgid \"Downloads completed\"\nmsgstr \"Scaricamenti completati\"\n\n#: youtube_dl_gui/mainframe.py:120\nmsgid \"Total Progress: {0:.1f}% | Queued ({1}) Paused ({2}) Active ({3}) Completed ({4}) Error ({5})\"\nmsgstr \"Progresso complessivo: {0:.1f}% | Accodati ({1}) In pausa ({2}) Attivi ({3}) Completati ({4}) Con errori ({5})\"\n\n#: youtube_dl_gui/mainframe.py:121\nmsgid \"Stopping downloads\"\nmsgstr \"Arresto scaricamenti\"\n\n#: youtube_dl_gui/mainframe.py:122\nmsgid \"Downloads stopped\"\nmsgstr \"Scaricamenti arrestati\"\n\n#: youtube_dl_gui/mainframe.py:123\nmsgid \"You need to provide at least one URL\"\nmsgstr \"Devi fornire almeno una URL\"\n\n#: youtube_dl_gui/mainframe.py:124\nmsgid \"Downloads started\"\nmsgstr \"Scaricamenti avviati\"\n\n#: youtube_dl_gui/mainframe.py:125\nmsgid \"Choose Directory\"\nmsgstr \"Scegli cartella\"\n\n#: youtube_dl_gui/mainframe.py:127\nmsgid \"Download in progress. Please wait for all downloads to complete\"\nmsgstr \"Scaricamento in corso. Attendi che tutti gli scaricamenti vengano completati\"\n\n#: youtube_dl_gui/mainframe.py:128\nmsgid \"Update already in progress\"\nmsgstr \"Aggiornamento già in corso\"\n\n#: youtube_dl_gui/mainframe.py:130\nmsgid \"Downloading latest youtube-dl. Please wait...\"\nmsgstr \"Scaricamento youtube-dl più recente. Attendere...\"\n\n#: youtube_dl_gui/mainframe.py:131\nmsgid \"Youtube-dl download failed [{0}]\"\nmsgstr \"Scaricamento youtube-dl non riuscito [{0}]\"\n\n#: youtube_dl_gui/mainframe.py:132\nmsgid \"Successfully downloaded youtube-dl\"\nmsgstr \"Youtube-dl scaricato con successo\"\n\n#: youtube_dl_gui/mainframe.py:134\nmsgid \"Unable to open directory: '{dir}'. The specified path does not exist\"\nmsgstr \"Impossibile aprire la cartella: '{dir}'. Il percorso indicato non esiste\"\n\n#: youtube_dl_gui/mainframe.py:136\nmsgid \"Error while shutting down. Make sure you typed the correct password\"\nmsgstr \"Errore durante l'arresto. Assicurati di aver inserito la password esatta\"\n\n#: youtube_dl_gui/mainframe.py:138\nmsgid \"Shutting down system\"\nmsgstr \"Arresto del sistema in corso\"\n\n#: youtube_dl_gui/mainframe.py:141\nmsgid \"Extension\"\nmsgstr \"Estensione\"\n\n#: youtube_dl_gui/mainframe.py:142\nmsgid \"Size\"\nmsgstr \"Size\"\n\n#: youtube_dl_gui/mainframe.py:143\nmsgid \"Percent\"\nmsgstr \"Percentuale\"\n\n#: youtube_dl_gui/mainframe.py:144\nmsgid \"ETA\"\nmsgstr \"ETA\"\n\n#: youtube_dl_gui/mainframe.py:145\nmsgid \"Speed\"\nmsgstr \"Velocità\"\n\n#: youtube_dl_gui/mainframe.py:146\nmsgid \"Status\"\nmsgstr \"Stato\"\n\n#: youtube_dl_gui/mainframe.py:235\nmsgid \"Get URL\"\nmsgstr \"Copia URL\"\n\n#: youtube_dl_gui/mainframe.py:236\nmsgid \"Get command\"\nmsgstr \"Copia comando\"\n\n#: youtube_dl_gui/mainframe.py:237\nmsgid \"Open destination\"\nmsgstr \"Apri destinazione\"\n\n#: youtube_dl_gui/mainframe.py:238\nmsgid \"Re-enter\"\nmsgstr \"Reinserisci\"\n\n#: youtube_dl_gui/mainframe.py:458\nmsgid \"Resume\"\nmsgstr \"Riprendi\"\n\n#: youtube_dl_gui/mainframe.py:480\nmsgid \"Video\"\nmsgstr \"Video\"\n\n#: youtube_dl_gui/mainframe.py:484\nmsgid \"Audio\"\nmsgstr \"Audio\"\n\n#: youtube_dl_gui/mainframe.py:516\nmsgid \"No items selected. Please pick an action\"\nmsgstr \"Nessun elemento selezionato. Decidere un'azione\"\n\n#: youtube_dl_gui/mainframe.py:516\nmsgid \"Remove all\"\nmsgstr \"Rimuovi tutti\"\n\n#: youtube_dl_gui/mainframe.py:516\nmsgid \"Remove completed\"\nmsgstr \"Rimuovi completati\"\n\n#: youtube_dl_gui/mainframe.py:534\nmsgid \"Are you sure you want to remove selected items?\"\nmsgstr \"Sei sicuro di voler rimuovere gli elementi selezionati?\"\n\n#: youtube_dl_gui/mainframe.py:546\nmsgid \"Item is active, cannot remove\"\nmsgstr \"L'elemento è attivo, impossibile rimuoverlo\"\n\n#: youtube_dl_gui/mainframe.py:579\nmsgid \"Item is not completed\"\nmsgstr \"L'elemento non è stato completato\"\n\n#: youtube_dl_gui/mainframe.py:668\nmsgid \"Update in progress. Please wait for the update to complete\"\nmsgstr \"Aggiornamento in corso. Attendi che l'aggiornamento sia completo\"\n\n#: youtube_dl_gui/mainframe.py:716\nmsgid \"Logging is disabled\"\nmsgstr \"Il registro è disabilitato\"\n\n#: youtube_dl_gui/mainframe.py:891\nmsgid \"Shutdown\"\nmsgstr \"Arresto\"\n\n#: youtube_dl_gui/mainframe.py:891\nmsgid \"Shutting down in {0} second(s)\"\nmsgstr \"Arresto in {0} secondi\"\n\n#: youtube_dl_gui/mainframe.py:980\nmsgid \"No items to download\"\nmsgstr \"Nessun elemento da scaricare\"\n\n#: youtube_dl_gui/mainframe.py:1040\nmsgid \"Updates are disabled for your system. Please use the system's package manager to update youtube-dl.\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:1065\nmsgid \"Are you sure you want to exit?\"\nmsgstr \"Sei sicuro di voler uscire?\"\n\n#: youtube_dl_gui/mainframe.py:1065\nmsgid \"Exit\"\nmsgstr \"Uscita\"\n\n#: youtube_dl_gui/mainframe.py:1306 youtube_dl_gui/mainframe.py:1456\nmsgid \"Cancel\"\nmsgstr \"Annulla\"\n\n#: youtube_dl_gui/mainframe.py:1455\nmsgid \"OK\"\nmsgstr \"OK\"\n\n#: youtube_dl_gui/optionsframe.py:65\nmsgid \"Reset\"\nmsgstr \"Azzera\"\n\n#: youtube_dl_gui/optionsframe.py:66\nmsgid \"Close\"\nmsgstr \"Chiudi\"\n\n#: youtube_dl_gui/optionsframe.py:72\nmsgid \"General\"\nmsgstr \"Generale\"\n\n#: youtube_dl_gui/optionsframe.py:73\nmsgid \"Formats\"\nmsgstr \"Formati\"\n\n#: youtube_dl_gui/optionsframe.py:74\nmsgid \"Downloads\"\nmsgstr \"Scaricamenti\"\n\n#: youtube_dl_gui/optionsframe.py:75\nmsgid \"Advanced\"\nmsgstr \"Avanzate\"\n\n#: youtube_dl_gui/optionsframe.py:76\nmsgid \"Extra\"\nmsgstr \"Extra\"\n\n#: youtube_dl_gui/optionsframe.py:310\nmsgid \"Language\"\nmsgstr \"Lingua\"\n\n#: youtube_dl_gui/optionsframe.py:313\nmsgid \"Filename format\"\nmsgstr \"Formato nome file\"\n\n#: youtube_dl_gui/optionsframe.py:318\nmsgid \"Filename options\"\nmsgstr \"Opzioni nome file\"\n\n#: youtube_dl_gui/optionsframe.py:319\nmsgid \"Restrict filenames to ASCII\"\nmsgstr \"Limita nomi file ad ASCII\"\n\n#: youtube_dl_gui/optionsframe.py:321\nmsgid \"More options\"\nmsgstr \"Più opzioni\"\n\n#: youtube_dl_gui/optionsframe.py:322\nmsgid \"Confirm on exit\"\nmsgstr \"Chiedi conferma all'uscita\"\n\n#: youtube_dl_gui/optionsframe.py:323\nmsgid \"Confirm item deletion\"\nmsgstr \"Chiedi conferma per eliminare elementi\"\n\n#: youtube_dl_gui/optionsframe.py:324\nmsgid \"Inform me on download completion\"\nmsgstr \"Informami al termine degli scaricamenti\"\n\n#: youtube_dl_gui/optionsframe.py:326\nmsgid \"Shutdown on download completion\"\nmsgstr \"Arresto al termine degli scaricamenti\"\n\n#: youtube_dl_gui/optionsframe.py:337\nmsgid \"SUDO password\"\nmsgstr \"password SUDO\"\n\n#: youtube_dl_gui/optionsframe.py:415 youtube_dl_gui/optionsframe.py:816\nmsgid \"In order for the changes to take effect please restart {0}\"\nmsgstr \"Per attivare i cambiamenti è necessario riavviare {0}\"\n\n#: youtube_dl_gui/optionsframe.py:416 youtube_dl_gui/optionsframe.py:817\nmsgid \"Restart\"\nmsgstr \"Riavvia\"\n\n#: youtube_dl_gui/optionsframe.py:463\nmsgid \"high\"\nmsgstr \"alta\"\n\n#: youtube_dl_gui/optionsframe.py:463\nmsgid \"low\"\nmsgstr \"bassa\"\n\n#: youtube_dl_gui/optionsframe.py:463\nmsgid \"mid\"\nmsgstr \"media\"\n\n#: youtube_dl_gui/optionsframe.py:468\nmsgid \"Video formats\"\nmsgstr \"Formati video\"\n\n#: youtube_dl_gui/optionsframe.py:471\nmsgid \"Audio formats\"\nmsgstr \"Formati audio\"\n\n#: youtube_dl_gui/optionsframe.py:474\nmsgid \"Post-Process options\"\nmsgstr \"Opzioni post-elaborazione\"\n\n#: youtube_dl_gui/optionsframe.py:475\nmsgid \"Keep original files\"\nmsgstr \"Conserva file originali\"\n\n#: youtube_dl_gui/optionsframe.py:476\nmsgid \"Extract audio from video file\"\nmsgstr \"Estrai audio dal file video\"\n\n#: youtube_dl_gui/optionsframe.py:477\nmsgid \"Embed thumbnail in audio file\"\nmsgstr \"Incorpora miniatura nel file audio\"\n\n#: youtube_dl_gui/optionsframe.py:478\nmsgid \"Add metadata to file\"\nmsgstr \"Aggiungi metadati al file\"\n\n#: youtube_dl_gui/optionsframe.py:480\nmsgid \"Audio quality\"\nmsgstr \"Qualità audio\"\n\n#: youtube_dl_gui/optionsframe.py:538\nmsgid \"English\"\nmsgstr \"Inglese\"\n\n#: youtube_dl_gui/optionsframe.py:539\nmsgid \"French\"\nmsgstr \"Francese\"\n\n#: youtube_dl_gui/optionsframe.py:540\nmsgid \"German\"\nmsgstr \"Tedesco\"\n\n#: youtube_dl_gui/optionsframe.py:541\nmsgid \"Greek\"\nmsgstr \"Greco\"\n\n#: youtube_dl_gui/optionsframe.py:542\nmsgid \"Hebrew\"\nmsgstr \"Ebraico\"\n\n#: youtube_dl_gui/optionsframe.py:543\nmsgid \"Italian\"\nmsgstr \"Italiano\"\n\n#: youtube_dl_gui/optionsframe.py:544\nmsgid \"Portuguese\"\nmsgstr \"Portoghese\"\n\n#: youtube_dl_gui/optionsframe.py:545\nmsgid \"Russian\"\nmsgstr \"Russo\"\n\n#: youtube_dl_gui/optionsframe.py:546\nmsgid \"Spanish\"\nmsgstr \"Spagnolo\"\n\n#: youtube_dl_gui/optionsframe.py:547\nmsgid \"Swedish\"\nmsgstr \"Svedese\"\n\n#: youtube_dl_gui/optionsframe.py:548\nmsgid \"Turkish\"\nmsgstr \"Turco\"\n\n#: youtube_dl_gui/optionsframe.py:564\nmsgid \"None\"\nmsgstr \"Nessuno\"\n\n#: youtube_dl_gui/optionsframe.py:565\nmsgid \"Automatic subtitles (YOUTUBE ONLY)\"\nmsgstr \"Sottotitoli automatici (SOLO YOUTUBE)\"\n\n#: youtube_dl_gui/optionsframe.py:566\nmsgid \"All available subtitles\"\nmsgstr \"Tutti i sottotitoli disponibili\"\n\n#: youtube_dl_gui/optionsframe.py:567\nmsgid \"Subtitles by language\"\nmsgstr \"Sottotitoli per lingua\"\n\n#: youtube_dl_gui/optionsframe.py:573\nmsgid \"Subtitles\"\nmsgstr \"Sottotitoli\"\n\n#: youtube_dl_gui/optionsframe.py:577\nmsgid \"Subtitles options\"\nmsgstr \"Opzioni sottotitoli\"\n\n#: youtube_dl_gui/optionsframe.py:578\nmsgid \"Embed subtitles into video file (mp4 ONLY)\"\nmsgstr \"Incorpora sottotitoli nel file video (SOLO mp4)\"\n\n#: youtube_dl_gui/optionsframe.py:580\nmsgid \"Playlist\"\nmsgstr \"Scaletta\"\n\n#: youtube_dl_gui/optionsframe.py:586 youtube_dl_gui/optionsframe.py:591\nmsgid \"Max\"\nmsgstr \"Massimo\"\n\n#: youtube_dl_gui/optionsframe.py:589\nmsgid \"Filesize\"\nmsgstr \"Dimensione file\"\n\n#: youtube_dl_gui/optionsframe.py:594\nmsgid \"Min\"\nmsgstr \"Minimo\"\n\n#: youtube_dl_gui/optionsframe.py:723\nmsgid \"Retries\"\nmsgstr \"Tentativi\"\n\n#: youtube_dl_gui/optionsframe.py:726\nmsgid \"Authentication\"\nmsgstr \"Autenticazione\"\n\n#: youtube_dl_gui/optionsframe.py:728\nmsgid \"Username\"\nmsgstr \"Nome utente\"\n\n#: youtube_dl_gui/optionsframe.py:730\nmsgid \"Password\"\nmsgstr \"Password\"\n\n#: youtube_dl_gui/optionsframe.py:732\nmsgid \"Video password\"\nmsgstr \"Password video\"\n\n#: youtube_dl_gui/optionsframe.py:735\nmsgid \"Network\"\nmsgstr \"Rete\"\n\n#: youtube_dl_gui/optionsframe.py:737\nmsgid \"Proxy\"\nmsgstr \"Proxy\"\n\n#: youtube_dl_gui/optionsframe.py:739\nmsgid \"User agent\"\nmsgstr \"User agent\"\n\n#: youtube_dl_gui/optionsframe.py:741\nmsgid \"Referer\"\nmsgstr \"Referer\"\n\n#: youtube_dl_gui/optionsframe.py:744\nmsgid \"Logging\"\nmsgstr \"Registro\"\n\n#: youtube_dl_gui/optionsframe.py:746\nmsgid \"Enable log\"\nmsgstr \"Abilita registro\"\n\n#: youtube_dl_gui/optionsframe.py:747\nmsgid \"View\"\nmsgstr \"Visualizza\"\n\n#: youtube_dl_gui/optionsframe.py:748\nmsgid \"Clear\"\nmsgstr \"Pulisci\"\n\n#: youtube_dl_gui/optionsframe.py:858\nmsgid \"Youtube-dl command line options (e.g. --help)\"\nmsgstr \"Opzioni a linea di comando di youtube-dl (ad es. --help)\"\n\n#: youtube_dl_gui/optionsframe.py:861\nmsgid \"Extra options\"\nmsgstr \"Opzioni extra\"\n\n#: youtube_dl_gui/optionsframe.py:863\nmsgid \"Debug youtube-dl\"\nmsgstr \"Debug di youtube-dl\"\n\n#: youtube_dl_gui/optionsframe.py:864\nmsgid \"Ignore errors\"\nmsgstr \"Ignora errori\"\n\n#: youtube_dl_gui/optionsframe.py:865\nmsgid \"Ignore youtube-dl config\"\nmsgstr \"Ignora configurazione youtube-dl\"\n\n#: youtube_dl_gui/optionsframe.py:866\nmsgid \"No mtime\"\nmsgstr \"No mtime\"\n\n#: youtube_dl_gui/optionsframe.py:867\nmsgid \"Prefer native HLS\"\nmsgstr \"HLS nativo preferito\"\n\n#: youtube_dl_gui/optionsframe.py:928\nmsgid \"Log Viewer\"\nmsgstr \"Visualizzatore registro\"\n"
  },
  {
    "path": "youtube_dl_gui/locale/ja_JP/LC_MESSAGES/youtube_dl_gui.po",
    "content": "# Youtube-dlG localization file.\n# FIRST AUTHOR: Sotiris Papadopoulos <ytubedlg@gmail.com>, 2015.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: youtube-dlg 0.4\\n\"\n\"POT-Creation-Date: 2018-01-15 16:42+EET\\n\"\n\"PO-Revision-Date: 2017-09-26 00:44+JST\\n\"\n\"Last-Translator: Mitsuya Tsujikawa <mtujikawa@gmail.com>\\n\"\n\"Language-Team: ja\\n\"\n\"Language: JP\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: pygettext.py 1.5\\n\"\n\n#: youtube_dl_gui/__init__.py:91\nmsgid \"Error\"\nmsgstr \"\"\n\n#: youtube_dl_gui/__init__.py:91\nmsgid \"Failed to locate youtube-dl and updates are disabled\"\nmsgstr \"\"\n\n#: youtube_dl_gui/formats.py:9 youtube_dl_gui/formats.py:109\nmsgid \"ID\"\nmsgstr \"ID\"\n\n#: youtube_dl_gui/formats.py:10 youtube_dl_gui/formats.py:110\n#: youtube_dl_gui/mainframe.py:140\nmsgid \"Title\"\nmsgstr \"タイトル\"\n\n#: youtube_dl_gui/formats.py:11 youtube_dl_gui/formats.py:111\nmsgid \"Title + ID\"\nmsgstr \"タイトル + ID\"\n\n#: youtube_dl_gui/formats.py:12 youtube_dl_gui/formats.py:112\nmsgid \"Title + Quality\"\nmsgstr \"タイトル + 品質\"\n\n#: youtube_dl_gui/formats.py:13 youtube_dl_gui/formats.py:113\nmsgid \"Title + ID + Quality\"\nmsgstr \"タイトル + ID + 品質\"\n\n#: youtube_dl_gui/formats.py:14 youtube_dl_gui/formats.py:114\nmsgid \"Custom\"\nmsgstr \"カスタム\"\n\n#: youtube_dl_gui/formats.py:19 youtube_dl_gui/formats.py:119\n#: youtube_dl_gui/mainframe.py:503 youtube_dl_gui/mainframe.py:506\nmsgid \"default\"\nmsgstr \"既定値\"\n\n#: youtube_dl_gui/mainframe.py:97\nmsgid \"Enter URLs below\"\nmsgstr \"下の欄にURLを入力してください\"\n\n#: youtube_dl_gui/mainframe.py:98\nmsgid \"Update\"\nmsgstr \"更新\"\n\n#: youtube_dl_gui/mainframe.py:99 youtube_dl_gui/optionsframe.py:41\nmsgid \"Options\"\nmsgstr \"設定\"\n\n#: youtube_dl_gui/mainframe.py:100 youtube_dl_gui/optionsframe.py:584\nmsgid \"Stop\"\nmsgstr \"停止\"\n\n#: youtube_dl_gui/mainframe.py:101\nmsgid \"Info\"\nmsgstr \"情報\"\n\n#: youtube_dl_gui/mainframe.py:102\nmsgid \"Welcome\"\nmsgstr \"ようこそ\"\n\n#: youtube_dl_gui/mainframe.py:103\nmsgid \"Warning\"\nmsgstr \"警告\"\n\n#: youtube_dl_gui/mainframe.py:105\nmsgid \"Add\"\nmsgstr \"追加\"\n\n#: youtube_dl_gui/mainframe.py:106\nmsgid \"Download list\"\nmsgstr \"ダウンロードリスト\"\n\n#: youtube_dl_gui/mainframe.py:107 youtube_dl_gui/mainframe.py:516\n#: youtube_dl_gui/mainframe.py:534\nmsgid \"Delete\"\nmsgstr \"削除\"\n\n#: youtube_dl_gui/mainframe.py:108\nmsgid \"Play\"\nmsgstr \"再生\"\n\n#: youtube_dl_gui/mainframe.py:109\nmsgid \"Up\"\nmsgstr \"上へ\"\n\n#: youtube_dl_gui/mainframe.py:110\nmsgid \"Down\"\nmsgstr \"下へ\"\n\n#: youtube_dl_gui/mainframe.py:111\nmsgid \"Reload\"\nmsgstr \"再読込み\"\n\n#: youtube_dl_gui/mainframe.py:112 youtube_dl_gui/mainframe.py:448\n#: youtube_dl_gui/mainframe.py:649\nmsgid \"Pause\"\nmsgstr \"一時停止\"\n\n#: youtube_dl_gui/mainframe.py:113 youtube_dl_gui/mainframe.py:865\n#: youtube_dl_gui/mainframe.py:866 youtube_dl_gui/optionsframe.py:582\nmsgid \"Start\"\nmsgstr \"ダウンロード開始\"\n\n#: youtube_dl_gui/mainframe.py:114\nmsgid \"About\"\nmsgstr \"製品情報\"\n\n#: youtube_dl_gui/mainframe.py:115\nmsgid \"View Log\"\nmsgstr \"ログの表示\"\n\n#: youtube_dl_gui/mainframe.py:117\nmsgid \"Successfully downloaded {0} URL(s) in {1} day(s) {2} hour(s) {3} minute(s) {4} second(s)\"\nmsgstr \"正常にダウンロードしました: {0} URL(s) in {1} 日 {2} 時 {3} 分 {4} 秒\"\n\n#: youtube_dl_gui/mainframe.py:119\nmsgid \"Downloads completed\"\nmsgstr \"ダウンロード完了\"\n\n#: youtube_dl_gui/mainframe.py:120\nmsgid \"Total Progress: {0:.1f}% | Queued ({1}) Paused ({2}) Active ({3}) Completed ({4}) Error ({5})\"\nmsgstr \"進捗状況: {0:.1f}% | 待機 ({1}) 停止 ({2}) 進行中 ({3}) 完了 ({4}) エラー ({5})\"\n\n#: youtube_dl_gui/mainframe.py:121\nmsgid \"Stopping downloads\"\nmsgstr \"ダウンロードを停止中\"\n\n#: youtube_dl_gui/mainframe.py:122\nmsgid \"Downloads stopped\"\nmsgstr \"ダウンロードを停止しました\"\n\n#: youtube_dl_gui/mainframe.py:123\nmsgid \"You need to provide at least one URL\"\nmsgstr \"最低でも1つはURLを入力する必要があります\"\n\n#: youtube_dl_gui/mainframe.py:124\nmsgid \"Downloads started\"\nmsgstr \"ダウンロードを開始しました\"\n\n#: youtube_dl_gui/mainframe.py:125\nmsgid \"Choose Directory\"\nmsgstr \"フォルダを選択してください\"\n\n#: youtube_dl_gui/mainframe.py:127\nmsgid \"Download in progress. Please wait for all downloads to complete\"\nmsgstr \"ダウンロード中です。すべて完了するまでお待ちください。\"\n\n#: youtube_dl_gui/mainframe.py:128\nmsgid \"Update already in progress\"\nmsgstr \"更新中です\"\n\n#: youtube_dl_gui/mainframe.py:130\nmsgid \"Downloading latest youtube-dl. Please wait...\"\nmsgstr \"youtube-dlの最新版をダウンロードしています。お待ちください...\"\n\n#: youtube_dl_gui/mainframe.py:131\nmsgid \"Youtube-dl download failed [{0}]\"\nmsgstr \"Youtube-dlのダウンロードに失敗しました [{0}]\"\n\n#: youtube_dl_gui/mainframe.py:132\nmsgid \"Successfully downloaded youtube-dl\"\nmsgstr \"Youtube-dlのダウンロードに成功しました\"\n\n#: youtube_dl_gui/mainframe.py:134\nmsgid \"Unable to open directory: '{dir}'. The specified path does not exist\"\nmsgstr \"フォルダを開くことができません: '{dir}'。指定されたパスが存在しません。\"\n\n#: youtube_dl_gui/mainframe.py:136\nmsgid \"Error while shutting down. Make sure you typed the correct password\"\nmsgstr \"シャットダウンの際にエラーが発生しました。必ず正確なパスワードを入力したことを確認してください\"\n\n#: youtube_dl_gui/mainframe.py:138\nmsgid \"Shutting down system\"\nmsgstr \"システムをシャットダウンします\"\n\n#: youtube_dl_gui/mainframe.py:141\nmsgid \"Extension\"\nmsgstr \"拡張子\"\n\n#: youtube_dl_gui/mainframe.py:142\nmsgid \"Size\"\nmsgstr \"サイズ\"\n\n#: youtube_dl_gui/mainframe.py:143\nmsgid \"Percent\"\nmsgstr \"パーセント\"\n\n#: youtube_dl_gui/mainframe.py:144\nmsgid \"ETA\"\nmsgstr \"ETA\"\n\n#: youtube_dl_gui/mainframe.py:145\nmsgid \"Speed\"\nmsgstr \"速度\"\n\n#: youtube_dl_gui/mainframe.py:146\nmsgid \"Status\"\nmsgstr \"状態\"\n\n#: youtube_dl_gui/mainframe.py:235\nmsgid \"Get URL\"\nmsgstr \"URLをコピー\"\n\n#: youtube_dl_gui/mainframe.py:236\nmsgid \"Get command\"\nmsgstr \"コマンドをコピー\"\n\n#: youtube_dl_gui/mainframe.py:237\nmsgid \"Open destination\"\nmsgstr \"保存先フォルダを開く\"\n\n#: youtube_dl_gui/mainframe.py:238\nmsgid \"Re-enter\"\nmsgstr \"再登録\"\n\n#: youtube_dl_gui/mainframe.py:458\nmsgid \"Resume\"\nmsgstr \"再開\"\n\n#: youtube_dl_gui/mainframe.py:480\nmsgid \"Video\"\nmsgstr \"動画\"\n\n#: youtube_dl_gui/mainframe.py:484\nmsgid \"Audio\"\nmsgstr \"音声\"\n\n#: youtube_dl_gui/mainframe.py:516\nmsgid \"No items selected. Please pick an action\"\nmsgstr \"項目が選択されていません。操作項目を選択してください\"\n\n#: youtube_dl_gui/mainframe.py:516\nmsgid \"Remove all\"\nmsgstr \"全消去\"\n\n#: youtube_dl_gui/mainframe.py:516\nmsgid \"Remove completed\"\nmsgstr \"消去完了\"\n\n#: youtube_dl_gui/mainframe.py:534\nmsgid \"Are you sure you want to remove selected items?\"\nmsgstr \"選択アイテムを消去してよろしいですか?\"\n\n#: youtube_dl_gui/mainframe.py:546\nmsgid \"Item is active, cannot remove\"\nmsgstr \"使用中のため削除できません\"\n\n#: youtube_dl_gui/mainframe.py:579\nmsgid \"Item is not completed\"\nmsgstr \"未完了のアイテムです\"\n\n#: youtube_dl_gui/mainframe.py:668\nmsgid \"Update in progress. Please wait for the update to complete\"\nmsgstr \"アップデート中です。完了までお待ちください\"\n\n#: youtube_dl_gui/mainframe.py:716\nmsgid \"Logging is disabled\"\nmsgstr \"ロギング無効\"\n\n#: youtube_dl_gui/mainframe.py:891\nmsgid \"Shutdown\"\nmsgstr \"シャットダウン\"\n\n#: youtube_dl_gui/mainframe.py:891\nmsgid \"Shutting down in {0} second(s)\"\nmsgstr \"{0} 秒後にシャットダウンします\"\n\n#: youtube_dl_gui/mainframe.py:980\nmsgid \"No items to download\"\nmsgstr \"ダウンロードする項目がありません\"\n\n#: youtube_dl_gui/mainframe.py:1040\nmsgid \"Updates are disabled for your system. Please use the system's package manager to update youtube-dl.\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:1065\nmsgid \"Are you sure you want to exit?\"\nmsgstr \"終了してよろしいですか?\"\n\n#: youtube_dl_gui/mainframe.py:1065\nmsgid \"Exit\"\nmsgstr \"終了\"\n\n#: youtube_dl_gui/mainframe.py:1306 youtube_dl_gui/mainframe.py:1456\nmsgid \"Cancel\"\nmsgstr \"キャンセル\"\n\n#: youtube_dl_gui/mainframe.py:1455\nmsgid \"OK\"\nmsgstr \"OK\"\n\n#: youtube_dl_gui/optionsframe.py:65\nmsgid \"Reset\"\nmsgstr \"リセット\"\n\n#: youtube_dl_gui/optionsframe.py:66\nmsgid \"Close\"\nmsgstr \"閉じる\"\n\n#: youtube_dl_gui/optionsframe.py:72\nmsgid \"General\"\nmsgstr \"一般\"\n\n#: youtube_dl_gui/optionsframe.py:73\nmsgid \"Formats\"\nmsgstr \"形式\"\n\n#: youtube_dl_gui/optionsframe.py:74\nmsgid \"Downloads\"\nmsgstr \"ダウンロード\"\n\n#: youtube_dl_gui/optionsframe.py:75\nmsgid \"Advanced\"\nmsgstr \"高度\"\n\n#: youtube_dl_gui/optionsframe.py:76\nmsgid \"Extra\"\nmsgstr \"その他\"\n\n#: youtube_dl_gui/optionsframe.py:310\nmsgid \"Language\"\nmsgstr \"言語\"\n\n#: youtube_dl_gui/optionsframe.py:313\nmsgid \"Filename format\"\nmsgstr \"ファイル名の形式\"\n\n#: youtube_dl_gui/optionsframe.py:318\nmsgid \"Filename options\"\nmsgstr \"ファイル名のオプション\"\n\n#: youtube_dl_gui/optionsframe.py:319\nmsgid \"Restrict filenames to ASCII\"\nmsgstr \"ファイル名をASCIIに制限する\"\n\n#: youtube_dl_gui/optionsframe.py:321\nmsgid \"More options\"\nmsgstr \"詳細設定\"\n\n#: youtube_dl_gui/optionsframe.py:322\nmsgid \"Confirm on exit\"\nmsgstr \"終了時の確認\"\n\n#: youtube_dl_gui/optionsframe.py:323\nmsgid \"Confirm item deletion\"\nmsgstr \"アイテム削除時の確認\"\n\n#: youtube_dl_gui/optionsframe.py:324\nmsgid \"Inform me on download completion\"\nmsgstr \"ダウンロード完了時に通知\"\n\n#: youtube_dl_gui/optionsframe.py:326\nmsgid \"Shutdown on download completion\"\nmsgstr \"ダウンロード完了後シャットダウン\"\n\n#: youtube_dl_gui/optionsframe.py:337\nmsgid \"SUDO password\"\nmsgstr \"SUDO パスワード\"\n\n#: youtube_dl_gui/optionsframe.py:415 youtube_dl_gui/optionsframe.py:816\nmsgid \"In order for the changes to take effect please restart {0}\"\nmsgstr \"変更を反映するに再起動してください {0}\"\n\n#: youtube_dl_gui/optionsframe.py:416 youtube_dl_gui/optionsframe.py:817\nmsgid \"Restart\"\nmsgstr \"再起動\"\n\n#: youtube_dl_gui/optionsframe.py:463\nmsgid \"high\"\nmsgstr \"高\"\n\n#: youtube_dl_gui/optionsframe.py:463\nmsgid \"low\"\nmsgstr \"低\"\n\n#: youtube_dl_gui/optionsframe.py:463\nmsgid \"mid\"\nmsgstr \"中\"\n\n#: youtube_dl_gui/optionsframe.py:468\nmsgid \"Video formats\"\nmsgstr \"動画形式\"\n\n#: youtube_dl_gui/optionsframe.py:471\nmsgid \"Audio formats\"\nmsgstr \"音声形式\"\n\n#: youtube_dl_gui/optionsframe.py:474\nmsgid \"Post-Process options\"\nmsgstr \"後処理の設定\"\n\n#: youtube_dl_gui/optionsframe.py:475\nmsgid \"Keep original files\"\nmsgstr \"オリジナルファイルを残す\"\n\n#: youtube_dl_gui/optionsframe.py:476\nmsgid \"Extract audio from video file\"\nmsgstr \"動画から音声を抽出\"\n\n#: youtube_dl_gui/optionsframe.py:477\nmsgid \"Embed thumbnail in audio file\"\nmsgstr \"音声ファイルにサムネイル埋め込み\"\n\n#: youtube_dl_gui/optionsframe.py:478\nmsgid \"Add metadata to file\"\nmsgstr \"メタデータの追加\"\n\n#: youtube_dl_gui/optionsframe.py:480\nmsgid \"Audio quality\"\nmsgstr \"音声品質\"\n\n#: youtube_dl_gui/optionsframe.py:538\nmsgid \"English\"\nmsgstr \"英語\"\n\n#: youtube_dl_gui/optionsframe.py:539\nmsgid \"French\"\nmsgstr \"フランス語\"\n\n#: youtube_dl_gui/optionsframe.py:540\nmsgid \"German\"\nmsgstr \"ドイツ語\"\n\n#: youtube_dl_gui/optionsframe.py:541\nmsgid \"Greek\"\nmsgstr \"ギリシャ語\"\n\n#: youtube_dl_gui/optionsframe.py:542\nmsgid \"Hebrew\"\nmsgstr \"ヘブライ語\"\n\n#: youtube_dl_gui/optionsframe.py:543\nmsgid \"Italian\"\nmsgstr \"イタリア語\"\n\n#: youtube_dl_gui/optionsframe.py:544\nmsgid \"Portuguese\"\nmsgstr \"ポルトガル語\"\n\n#: youtube_dl_gui/optionsframe.py:545\nmsgid \"Russian\"\nmsgstr \"ロシア語\"\n\n#: youtube_dl_gui/optionsframe.py:546\nmsgid \"Spanish\"\nmsgstr \"スペイン語\"\n\n#: youtube_dl_gui/optionsframe.py:547\nmsgid \"Swedish\"\nmsgstr \"スウェーデン語\"\n\n#: youtube_dl_gui/optionsframe.py:548\nmsgid \"Turkish\"\nmsgstr \"トルコ語\"\n\n#: youtube_dl_gui/optionsframe.py:564\nmsgid \"None\"\nmsgstr \"なし\"\n\n#: youtube_dl_gui/optionsframe.py:565\nmsgid \"Automatic subtitles (YOUTUBE ONLY)\"\nmsgstr \"自動字幕 (Youtubeのみ)\"\n\n#: youtube_dl_gui/optionsframe.py:566\nmsgid \"All available subtitles\"\nmsgstr \"すべての字幕\"\n\n#: youtube_dl_gui/optionsframe.py:567\nmsgid \"Subtitles by language\"\nmsgstr \"字幕言語\"\n\n#: youtube_dl_gui/optionsframe.py:573\nmsgid \"Subtitles\"\nmsgstr \"字幕\"\n\n#: youtube_dl_gui/optionsframe.py:577\nmsgid \"Subtitles options\"\nmsgstr \"字幕設定\"\n\n#: youtube_dl_gui/optionsframe.py:578\nmsgid \"Embed subtitles into video file (mp4 ONLY)\"\nmsgstr \"動画に字幕埋め込み (mp4のみ)\"\n\n#: youtube_dl_gui/optionsframe.py:580\nmsgid \"Playlist\"\nmsgstr \"プレイリスト\"\n\n#: youtube_dl_gui/optionsframe.py:586 youtube_dl_gui/optionsframe.py:591\nmsgid \"Max\"\nmsgstr \"最大\"\n\n#: youtube_dl_gui/optionsframe.py:589\nmsgid \"Filesize\"\nmsgstr \"ファイルサイズ\"\n\n#: youtube_dl_gui/optionsframe.py:594\nmsgid \"Min\"\nmsgstr \"最小\"\n\n#: youtube_dl_gui/optionsframe.py:723\nmsgid \"Retries\"\nmsgstr \"リトライ\"\n\n#: youtube_dl_gui/optionsframe.py:726\nmsgid \"Authentication\"\nmsgstr \"認証情報\"\n\n#: youtube_dl_gui/optionsframe.py:728\nmsgid \"Username\"\nmsgstr \"アカウント\"\n\n#: youtube_dl_gui/optionsframe.py:730\nmsgid \"Password\"\nmsgstr \"パスワード\"\n\n#: youtube_dl_gui/optionsframe.py:732\nmsgid \"Video password\"\nmsgstr \"ビデオパスワード\"\n\n#: youtube_dl_gui/optionsframe.py:735\nmsgid \"Network\"\nmsgstr \"ネットワーク\"\n\n#: youtube_dl_gui/optionsframe.py:737\nmsgid \"Proxy\"\nmsgstr \"プロキシ\"\n\n#: youtube_dl_gui/optionsframe.py:739\nmsgid \"User agent\"\nmsgstr \"ユーザーエージェント(UA)\"\n\n#: youtube_dl_gui/optionsframe.py:741\nmsgid \"Referer\"\nmsgstr \"リファラー(Referer)\"\n\n#: youtube_dl_gui/optionsframe.py:744\nmsgid \"Logging\"\nmsgstr \"ロギング\"\n\n#: youtube_dl_gui/optionsframe.py:746\nmsgid \"Enable log\"\nmsgstr \"ログを有効化\"\n\n#: youtube_dl_gui/optionsframe.py:747\nmsgid \"View\"\nmsgstr \"表示\"\n\n#: youtube_dl_gui/optionsframe.py:748\nmsgid \"Clear\"\nmsgstr \"消去\"\n\n#: youtube_dl_gui/optionsframe.py:858\nmsgid \"Youtube-dl command line options (e.g. --help)\"\nmsgstr \"Youtube-dl コマンドラインオプション (例 --help)\"\n\n#: youtube_dl_gui/optionsframe.py:861\nmsgid \"Extra options\"\nmsgstr \"追加設定\"\n\n#: youtube_dl_gui/optionsframe.py:863\nmsgid \"Debug youtube-dl\"\nmsgstr \"youtube-dlのデバッグ\"\n\n#: youtube_dl_gui/optionsframe.py:864\nmsgid \"Ignore errors\"\nmsgstr \"エラーを無視\"\n\n#: youtube_dl_gui/optionsframe.py:865\nmsgid \"Ignore youtube-dl config\"\nmsgstr \"youtube-dlのconfig無視\"\n\n#: youtube_dl_gui/optionsframe.py:866\nmsgid \"No mtime\"\nmsgstr \"mtime無し\"\n\n#: youtube_dl_gui/optionsframe.py:867\nmsgid \"Prefer native HLS\"\nmsgstr \"ネイティブHLS優先\"\n\n#: youtube_dl_gui/optionsframe.py:928\nmsgid \"Log Viewer\"\nmsgstr \"ログビュワー\"\n"
  },
  {
    "path": "youtube_dl_gui/locale/ko_KR/LC_MESSAGES/youtube_dl_gui.po",
    "content": "# Youtube-dlG localization file.\n# FIRST AUTHOR: Sotiris Papadopoulos <ytubedlg@gmail.com>, 2015.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: youtube-dlg 0.4\\n\"\n\"POT-Creation-Date: 2018-01-15 16:42+EET\\n\"\n\"PO-Revision-Date: 2017-02-22 02:49+GMT\\n\"\n\"Last-Translator: Yi Soo, An <yisooan@gmail.com>\\n\"\n\"Language-Team: kr\\n\"\n\"Language: \\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: vim\\n\"\n\n#: youtube_dl_gui/__init__.py:91\nmsgid \"Error\"\nmsgstr \"\"\n\n#: youtube_dl_gui/__init__.py:91\nmsgid \"Failed to locate youtube-dl and updates are disabled\"\nmsgstr \"\"\n\n#: youtube_dl_gui/formats.py:9 youtube_dl_gui/formats.py:109\nmsgid \"ID\"\nmsgstr \"ID\"\n\n#: youtube_dl_gui/formats.py:10 youtube_dl_gui/formats.py:110\n#: youtube_dl_gui/mainframe.py:140\nmsgid \"Title\"\nmsgstr \"제목\"\n\n#: youtube_dl_gui/formats.py:11 youtube_dl_gui/formats.py:111\nmsgid \"Title + ID\"\nmsgstr \"제목 + ID\"\n\n#: youtube_dl_gui/formats.py:12 youtube_dl_gui/formats.py:112\nmsgid \"Title + Quality\"\nmsgstr \"제목 + 화질\"\n\n#: youtube_dl_gui/formats.py:13 youtube_dl_gui/formats.py:113\nmsgid \"Title + ID + Quality\"\nmsgstr \"제목 + ID + 화질\"\n\n#: youtube_dl_gui/formats.py:14 youtube_dl_gui/formats.py:114\nmsgid \"Custom\"\nmsgstr \"사용자 설정\"\n\n#: youtube_dl_gui/formats.py:19 youtube_dl_gui/formats.py:119\n#: youtube_dl_gui/mainframe.py:503 youtube_dl_gui/mainframe.py:506\nmsgid \"default\"\nmsgstr \"기본값\"\n\n#: youtube_dl_gui/mainframe.py:97\nmsgid \"Enter URLs below\"\nmsgstr \"URL을 입력하세요.\"\n\n#: youtube_dl_gui/mainframe.py:98\nmsgid \"Update\"\nmsgstr \"업데이트\"\n\n#: youtube_dl_gui/mainframe.py:99 youtube_dl_gui/optionsframe.py:41\nmsgid \"Options\"\nmsgstr \"옵션\"\n\n#: youtube_dl_gui/mainframe.py:100 youtube_dl_gui/optionsframe.py:584\nmsgid \"Stop\"\nmsgstr \"정지\"\n\n#: youtube_dl_gui/mainframe.py:101\nmsgid \"Info\"\nmsgstr \"정보\"\n\n#: youtube_dl_gui/mainframe.py:102\nmsgid \"Welcome\"\nmsgstr \"환영합니다.\"\n\n#: youtube_dl_gui/mainframe.py:103\nmsgid \"Warning\"\nmsgstr \"주의\"\n\n#: youtube_dl_gui/mainframe.py:105\nmsgid \"Add\"\nmsgstr \"추가\"\n\n#: youtube_dl_gui/mainframe.py:106\nmsgid \"Download list\"\nmsgstr \"다운로드 목록\"\n\n#: youtube_dl_gui/mainframe.py:107 youtube_dl_gui/mainframe.py:516\n#: youtube_dl_gui/mainframe.py:534\nmsgid \"Delete\"\nmsgstr \"삭제\"\n\n#: youtube_dl_gui/mainframe.py:108\nmsgid \"Play\"\nmsgstr \"재생\"\n\n#: youtube_dl_gui/mainframe.py:109\nmsgid \"Up\"\nmsgstr \"위로\"\n\n#: youtube_dl_gui/mainframe.py:110\nmsgid \"Down\"\nmsgstr \"아래로\"\n\n#: youtube_dl_gui/mainframe.py:111\nmsgid \"Reload\"\nmsgstr \"새로고침\"\n\n#: youtube_dl_gui/mainframe.py:112 youtube_dl_gui/mainframe.py:448\n#: youtube_dl_gui/mainframe.py:649\nmsgid \"Pause\"\nmsgstr \"일시정지\"\n\n#: youtube_dl_gui/mainframe.py:113 youtube_dl_gui/mainframe.py:865\n#: youtube_dl_gui/mainframe.py:866 youtube_dl_gui/optionsframe.py:582\nmsgid \"Start\"\nmsgstr \"시작\"\n\n#: youtube_dl_gui/mainframe.py:114\nmsgid \"About\"\nmsgstr \"정보\"\n\n#: youtube_dl_gui/mainframe.py:115\nmsgid \"View Log\"\nmsgstr \"로그 보기\"\n\n#: youtube_dl_gui/mainframe.py:117\nmsgid \"Successfully downloaded {0} URL(s) in {1} day(s) {2} hour(s) {3} minute(s) {4} second(s)\"\nmsgstr \"{0}개의 URL을 받는데 총 {1}일 {2}시간 {3}분 {4}초가 걸렸습니다.\"\n\n#: youtube_dl_gui/mainframe.py:119\nmsgid \"Downloads completed\"\nmsgstr \"다운로드 완료\"\n\n#: youtube_dl_gui/mainframe.py:120\nmsgid \"Total Progress: {0:.1f}% | Queued ({1}) Paused ({2}) Active ({3}) Completed ({4}) Error ({5})\"\nmsgstr \"진행 상태: {0:.1f}% | 대기 ({1}) 일시정지 ({2}) 진행중 ({3}) 완료 ({4}) 오류 ({5})\"\n\n#: youtube_dl_gui/mainframe.py:121\nmsgid \"Stopping downloads\"\nmsgstr \"다운로드 정지중\"\n\n#: youtube_dl_gui/mainframe.py:122\nmsgid \"Downloads stopped\"\nmsgstr \"다운로드 정지\"\n\n#: youtube_dl_gui/mainframe.py:123\nmsgid \"You need to provide at least one URL\"\nmsgstr \"최소 한 개 이상의 URL을 입력해야 합니다.\"\n\n#: youtube_dl_gui/mainframe.py:124\nmsgid \"Downloads started\"\nmsgstr \"다운로드 시작됨\"\n\n#: youtube_dl_gui/mainframe.py:125\nmsgid \"Choose Directory\"\nmsgstr \"저장 경로\"\n\n#: youtube_dl_gui/mainframe.py:127\nmsgid \"Download in progress. Please wait for all downloads to complete\"\nmsgstr \"다운로드가 완료될 때까지 기다려주세요.\"\n\n#: youtube_dl_gui/mainframe.py:128\nmsgid \"Update already in progress\"\nmsgstr \"업데이트 진행중입니다.\"\n\n#: youtube_dl_gui/mainframe.py:130\nmsgid \"Downloading latest youtube-dl. Please wait...\"\nmsgstr \"최신 youtube-dl을 다운로드하고 있습니다. 잠시만 기다려주세요.\"\n\n#: youtube_dl_gui/mainframe.py:131\nmsgid \"Youtube-dl download failed [{0}]\"\nmsgstr \"Youtube-dl 다운로드 실패 [{0}]\"\n\n#: youtube_dl_gui/mainframe.py:132\nmsgid \"Successfully downloaded youtube-dl\"\nmsgstr \"Youtube-dl 다운로드가 완료되었습니다.\"\n\n#: youtube_dl_gui/mainframe.py:134\nmsgid \"Unable to open directory: '{dir}'. The specified path does not exist\"\nmsgstr \"'{dir}'을 열 수 없습니다. 올바른 경로가 아닙니다.\"\n\n#: youtube_dl_gui/mainframe.py:136\nmsgid \"Error while shutting down. Make sure you typed the correct password\"\nmsgstr \"시스템을 종료할 수 없습니다. 패스워드가 올바른지 확인하세요.\"\n\n#: youtube_dl_gui/mainframe.py:138\nmsgid \"Shutting down system\"\nmsgstr \"시스템 종료 중..\"\n\n#: youtube_dl_gui/mainframe.py:141\nmsgid \"Extension\"\nmsgstr \"확장자\"\n\n#: youtube_dl_gui/mainframe.py:142\nmsgid \"Size\"\nmsgstr \"크기\"\n\n#: youtube_dl_gui/mainframe.py:143\nmsgid \"Percent\"\nmsgstr \"퍼센트\"\n\n#: youtube_dl_gui/mainframe.py:144\nmsgid \"ETA\"\nmsgstr \"기타\"\n\n#: youtube_dl_gui/mainframe.py:145\nmsgid \"Speed\"\nmsgstr \"속도\"\n\n#: youtube_dl_gui/mainframe.py:146\nmsgid \"Status\"\nmsgstr \"상태\"\n\n#: youtube_dl_gui/mainframe.py:235\nmsgid \"Get URL\"\nmsgstr \"클립보드로 URL 복사\"\n\n#: youtube_dl_gui/mainframe.py:236\nmsgid \"Get command\"\nmsgstr \"커맨드라인 명령어 복사\"\n\n#: youtube_dl_gui/mainframe.py:237\nmsgid \"Open destination\"\nmsgstr \"폴더 열기\"\n\n#: youtube_dl_gui/mainframe.py:238\nmsgid \"Re-enter\"\nmsgstr \"대기열에 넣기\"\n\n#: youtube_dl_gui/mainframe.py:458\nmsgid \"Resume\"\nmsgstr \"이어받기\"\n\n#: youtube_dl_gui/mainframe.py:480\nmsgid \"Video\"\nmsgstr \"비디오\"\n\n#: youtube_dl_gui/mainframe.py:484\nmsgid \"Audio\"\nmsgstr \"오디오\"\n\n#: youtube_dl_gui/mainframe.py:516\nmsgid \"No items selected. Please pick an action\"\nmsgstr \"선택된 아이템이 없습니다. 다음 중 하나를 실행하시겠습니까?\"\n\n#: youtube_dl_gui/mainframe.py:516\nmsgid \"Remove all\"\nmsgstr \"전체삭제\"\n\n#: youtube_dl_gui/mainframe.py:516\nmsgid \"Remove completed\"\nmsgstr \"완료된 것만 삭제\"\n\n#: youtube_dl_gui/mainframe.py:534\nmsgid \"Are you sure you want to remove selected items?\"\nmsgstr \"해당 아이템을 삭제하시겠습니까?\"\n\n#: youtube_dl_gui/mainframe.py:546\nmsgid \"Item is active, cannot remove\"\nmsgstr \"삭제할 수 없습니다.\"\n\n#: youtube_dl_gui/mainframe.py:579\nmsgid \"Item is not completed\"\nmsgstr \"다운로드가 완료되지 않았습니다.\"\n\n#: youtube_dl_gui/mainframe.py:668\nmsgid \"Update in progress. Please wait for the update to complete\"\nmsgstr \"업데이트가 완료될 때까지 기다려주세요.\"\n\n#: youtube_dl_gui/mainframe.py:716\nmsgid \"Logging is disabled\"\nmsgstr \"로깅이 비활성화되었습니다.\"\n\n#: youtube_dl_gui/mainframe.py:891\nmsgid \"Shutdown\"\nmsgstr \"시스템 종료\"\n\n#: youtube_dl_gui/mainframe.py:891\nmsgid \"Shutting down in {0} second(s)\"\nmsgstr \"{0}초 후에 시스템이 종료됩니다.\"\n\n#: youtube_dl_gui/mainframe.py:980\nmsgid \"No items to download\"\nmsgstr \"다운로드할 아이템이 없습니다.\"\n\n#: youtube_dl_gui/mainframe.py:1040\nmsgid \"Updates are disabled for your system. Please use the system's package manager to update youtube-dl.\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:1065\nmsgid \"Are you sure you want to exit?\"\nmsgstr \"종료하시겠습니까?\"\n\n#: youtube_dl_gui/mainframe.py:1065\nmsgid \"Exit\"\nmsgstr \"종료\"\n\n#: youtube_dl_gui/mainframe.py:1306 youtube_dl_gui/mainframe.py:1456\nmsgid \"Cancel\"\nmsgstr \"취소\"\n\n#: youtube_dl_gui/mainframe.py:1455\nmsgid \"OK\"\nmsgstr \"확인\"\n\n#: youtube_dl_gui/optionsframe.py:65\nmsgid \"Reset\"\nmsgstr \"초기화\"\n\n#: youtube_dl_gui/optionsframe.py:66\nmsgid \"Close\"\nmsgstr \"닫기\"\n\n#: youtube_dl_gui/optionsframe.py:72\nmsgid \"General\"\nmsgstr \"일반\"\n\n#: youtube_dl_gui/optionsframe.py:73\nmsgid \"Formats\"\nmsgstr \"포맷\"\n\n#: youtube_dl_gui/optionsframe.py:74\nmsgid \"Downloads\"\nmsgstr \"다운로드\"\n\n#: youtube_dl_gui/optionsframe.py:75\nmsgid \"Advanced\"\nmsgstr \"고급설정\"\n\n#: youtube_dl_gui/optionsframe.py:76\nmsgid \"Extra\"\nmsgstr \"기타\"\n\n#: youtube_dl_gui/optionsframe.py:310\nmsgid \"Language\"\nmsgstr \"언어\"\n\n#: youtube_dl_gui/optionsframe.py:313\nmsgid \"Filename format\"\nmsgstr \"파일이름 형식\"\n\n#: youtube_dl_gui/optionsframe.py:318\nmsgid \"Filename options\"\nmsgstr \"파일이름 옵션\"\n\n#: youtube_dl_gui/optionsframe.py:319\nmsgid \"Restrict filenames to ASCII\"\nmsgstr \"ASCII 문자로 제한\"\n\n#: youtube_dl_gui/optionsframe.py:321\nmsgid \"More options\"\nmsgstr \"추가옵션\"\n\n#: youtube_dl_gui/optionsframe.py:322\nmsgid \"Confirm on exit\"\nmsgstr \"종료시 확인\"\n\n#: youtube_dl_gui/optionsframe.py:323\nmsgid \"Confirm item deletion\"\nmsgstr \"아이템 삭제시 확인\"\n\n#: youtube_dl_gui/optionsframe.py:324\nmsgid \"Inform me on download completion\"\nmsgstr \"완료시 알림\"\n\n#: youtube_dl_gui/optionsframe.py:326\nmsgid \"Shutdown on download completion\"\nmsgstr \"완료시 시스템종료\"\n\n#: youtube_dl_gui/optionsframe.py:337\nmsgid \"SUDO password\"\nmsgstr \"SUDO 암호\"\n\n#: youtube_dl_gui/optionsframe.py:415 youtube_dl_gui/optionsframe.py:816\nmsgid \"In order for the changes to take effect please restart {0}\"\nmsgstr \"설정값을 적용하기 위해 {0}을 재시작하세요.\"\n\n#: youtube_dl_gui/optionsframe.py:416 youtube_dl_gui/optionsframe.py:817\nmsgid \"Restart\"\nmsgstr \"재시작\"\n\n#: youtube_dl_gui/optionsframe.py:463\nmsgid \"high\"\nmsgstr \"높음\"\n\n#: youtube_dl_gui/optionsframe.py:463\nmsgid \"low\"\nmsgstr \"낮음\"\n\n#: youtube_dl_gui/optionsframe.py:463\nmsgid \"mid\"\nmsgstr \"중간\"\n\n#: youtube_dl_gui/optionsframe.py:468\nmsgid \"Video formats\"\nmsgstr \"비디오 형식\"\n\n#: youtube_dl_gui/optionsframe.py:471\nmsgid \"Audio formats\"\nmsgstr \"오디오 형식\"\n\n#: youtube_dl_gui/optionsframe.py:474\nmsgid \"Post-Process options\"\nmsgstr \"후처리 옵션\"\n\n#: youtube_dl_gui/optionsframe.py:475\nmsgid \"Keep original files\"\nmsgstr \"원본파일 유지\"\n\n#: youtube_dl_gui/optionsframe.py:476\nmsgid \"Extract audio from video file\"\nmsgstr \"오디오 추출\"\n\n#: youtube_dl_gui/optionsframe.py:477\nmsgid \"Embed thumbnail in audio file\"\nmsgstr \"오디오에 섬네일 추가\"\n\n#: youtube_dl_gui/optionsframe.py:478\nmsgid \"Add metadata to file\"\nmsgstr \"메타데이터 추가\"\n\n#: youtube_dl_gui/optionsframe.py:480\nmsgid \"Audio quality\"\nmsgstr \"오디오 음질\"\n\n#: youtube_dl_gui/optionsframe.py:538\nmsgid \"English\"\nmsgstr \"English\"\n\n#: youtube_dl_gui/optionsframe.py:539\nmsgid \"French\"\nmsgstr \"French\"\n\n#: youtube_dl_gui/optionsframe.py:540\nmsgid \"German\"\nmsgstr \"German\"\n\n#: youtube_dl_gui/optionsframe.py:541\nmsgid \"Greek\"\nmsgstr \"Greek\"\n\n#: youtube_dl_gui/optionsframe.py:542\nmsgid \"Hebrew\"\nmsgstr \"Hebrew\"\n\n#: youtube_dl_gui/optionsframe.py:543\nmsgid \"Italian\"\nmsgstr \"Italian\"\n\n#: youtube_dl_gui/optionsframe.py:544\nmsgid \"Portuguese\"\nmsgstr \"Portuguese\"\n\n#: youtube_dl_gui/optionsframe.py:545\nmsgid \"Russian\"\nmsgstr \"Russian\"\n\n#: youtube_dl_gui/optionsframe.py:546\nmsgid \"Spanish\"\nmsgstr \"Spanish\"\n\n#: youtube_dl_gui/optionsframe.py:547\nmsgid \"Swedish\"\nmsgstr \"Swedish\"\n\n#: youtube_dl_gui/optionsframe.py:548\nmsgid \"Turkish\"\nmsgstr \"Turkish\"\n\n#: youtube_dl_gui/optionsframe.py:564\nmsgid \"None\"\nmsgstr \"없음\"\n\n#: youtube_dl_gui/optionsframe.py:565\nmsgid \"Automatic subtitles (YOUTUBE ONLY)\"\nmsgstr \"오토매틱 자막 (유튜브 전용)\"\n\n#: youtube_dl_gui/optionsframe.py:566\nmsgid \"All available subtitles\"\nmsgstr \"사용가능한 모든 자막\"\n\n#: youtube_dl_gui/optionsframe.py:567\nmsgid \"Subtitles by language\"\nmsgstr \"언어에 따른 자막\"\n\n#: youtube_dl_gui/optionsframe.py:573\nmsgid \"Subtitles\"\nmsgstr \"자막\"\n\n#: youtube_dl_gui/optionsframe.py:577\nmsgid \"Subtitles options\"\nmsgstr \"자막 옵션\"\n\n#: youtube_dl_gui/optionsframe.py:578\nmsgid \"Embed subtitles into video file (mp4 ONLY)\"\nmsgstr \"비디오에 자막 삽입 (mp4 전용)\"\n\n#: youtube_dl_gui/optionsframe.py:580\nmsgid \"Playlist\"\nmsgstr \"재생목록\"\n\n#: youtube_dl_gui/optionsframe.py:586 youtube_dl_gui/optionsframe.py:591\nmsgid \"Max\"\nmsgstr \"최대\"\n\n#: youtube_dl_gui/optionsframe.py:589\nmsgid \"Filesize\"\nmsgstr \"파일크기\"\n\n#: youtube_dl_gui/optionsframe.py:594\nmsgid \"Min\"\nmsgstr \"최소\"\n\n#: youtube_dl_gui/optionsframe.py:723\nmsgid \"Retries\"\nmsgstr \"재시도 횟수\"\n\n#: youtube_dl_gui/optionsframe.py:726\nmsgid \"Authentication\"\nmsgstr \"인증\"\n\n#: youtube_dl_gui/optionsframe.py:728\nmsgid \"Username\"\nmsgstr \"사용자 이름\"\n\n#: youtube_dl_gui/optionsframe.py:730\nmsgid \"Password\"\nmsgstr \"비밀번호\"\n\n#: youtube_dl_gui/optionsframe.py:732\nmsgid \"Video password\"\nmsgstr \"비디오 비밀번호\"\n\n#: youtube_dl_gui/optionsframe.py:735\nmsgid \"Network\"\nmsgstr \"네트워크\"\n\n#: youtube_dl_gui/optionsframe.py:737\nmsgid \"Proxy\"\nmsgstr \"프록시\"\n\n#: youtube_dl_gui/optionsframe.py:739\nmsgid \"User agent\"\nmsgstr \"User agent\"\n\n#: youtube_dl_gui/optionsframe.py:741\nmsgid \"Referer\"\nmsgstr \"Referer\"\n\n#: youtube_dl_gui/optionsframe.py:744\nmsgid \"Logging\"\nmsgstr \"로그\"\n\n#: youtube_dl_gui/optionsframe.py:746\nmsgid \"Enable log\"\nmsgstr \"로그 사용\"\n\n#: youtube_dl_gui/optionsframe.py:747\nmsgid \"View\"\nmsgstr \"보기\"\n\n#: youtube_dl_gui/optionsframe.py:748\nmsgid \"Clear\"\nmsgstr \"초기화\"\n\n#: youtube_dl_gui/optionsframe.py:858\nmsgid \"Youtube-dl command line options (e.g. --help)\"\nmsgstr \"Youtube-dl 커맨드라인 옵션 (예: --help)\"\n\n#: youtube_dl_gui/optionsframe.py:861\nmsgid \"Extra options\"\nmsgstr \"기타 옵션\"\n\n#: youtube_dl_gui/optionsframe.py:863\nmsgid \"Debug youtube-dl\"\nmsgstr \"Youtube-dl 디버그\"\n\n#: youtube_dl_gui/optionsframe.py:864\nmsgid \"Ignore errors\"\nmsgstr \"오류 무시\"\n\n#: youtube_dl_gui/optionsframe.py:865\nmsgid \"Ignore youtube-dl config\"\nmsgstr \"Youtube-dl 설정 무시\"\n\n#: youtube_dl_gui/optionsframe.py:866\nmsgid \"No mtime\"\nmsgstr \"mtime 사용안함\"\n\n#: youtube_dl_gui/optionsframe.py:867\nmsgid \"Prefer native HLS\"\nmsgstr \"네이티브 HLS 사용\"\n\n#: youtube_dl_gui/optionsframe.py:928\nmsgid \"Log Viewer\"\nmsgstr \"로그 뷰어\"\n"
  },
  {
    "path": "youtube_dl_gui/locale/pt_BR/LC_MESSAGES/youtube_dl_gui.po",
    "content": "# Youtube-dlG localization file.\n# FIRST AUTHOR: Sotiris Papadopoulos <ytubedlg@gmail.com>, 2015.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: youtube-dlg 0.4\\n\"\n\"POT-Creation-Date: 2018-01-15 16:42+EET\\n\"\n\"PO-Revision-Date: 2017-06-16 11:27-0300\\n\"\n\"Last-Translator: Cleiton Meurer <cleitonme@gmail.com>\\n\"\n\"Language-Team: pt_BR\\n\"\n\"Language: pt_BR\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: pygettext.py 1.5\\n\"\n\"X-Generator: Poedit 2.0.2\\n\"\n\"Plural-Forms: nplurals=2; plural=(n > 1);\\n\"\n\n#: youtube_dl_gui/__init__.py:91\nmsgid \"Error\"\nmsgstr \"\"\n\n#: youtube_dl_gui/__init__.py:91\nmsgid \"Failed to locate youtube-dl and updates are disabled\"\nmsgstr \"\"\n\n#: youtube_dl_gui/formats.py:9 youtube_dl_gui/formats.py:109\nmsgid \"ID\"\nmsgstr \"ID\"\n\n#: youtube_dl_gui/formats.py:10 youtube_dl_gui/formats.py:110\n#: youtube_dl_gui/mainframe.py:140\nmsgid \"Title\"\nmsgstr \"Título\"\n\n#: youtube_dl_gui/formats.py:11 youtube_dl_gui/formats.py:111\nmsgid \"Title + ID\"\nmsgstr \"Título + ID\"\n\n#: youtube_dl_gui/formats.py:12 youtube_dl_gui/formats.py:112\nmsgid \"Title + Quality\"\nmsgstr \"Título + Qualidade\"\n\n#: youtube_dl_gui/formats.py:13 youtube_dl_gui/formats.py:113\nmsgid \"Title + ID + Quality\"\nmsgstr \"Título + ID + Qualidade\"\n\n#: youtube_dl_gui/formats.py:14 youtube_dl_gui/formats.py:114\nmsgid \"Custom\"\nmsgstr \"Personalizar\"\n\n#: youtube_dl_gui/formats.py:19 youtube_dl_gui/formats.py:119\n#: youtube_dl_gui/mainframe.py:503 youtube_dl_gui/mainframe.py:506\nmsgid \"default\"\nmsgstr \"Padrão\"\n\n#: youtube_dl_gui/mainframe.py:97\nmsgid \"Enter URLs below\"\nmsgstr \"Insira as URLs abaixo\"\n\n#: youtube_dl_gui/mainframe.py:98\nmsgid \"Update\"\nmsgstr \"Atualizar\"\n\n#: youtube_dl_gui/mainframe.py:99 youtube_dl_gui/optionsframe.py:41\nmsgid \"Options\"\nmsgstr \"Opções\"\n\n#: youtube_dl_gui/mainframe.py:100 youtube_dl_gui/optionsframe.py:584\nmsgid \"Stop\"\nmsgstr \"Parar\"\n\n#: youtube_dl_gui/mainframe.py:101\nmsgid \"Info\"\nmsgstr \"Informações\"\n\n#: youtube_dl_gui/mainframe.py:102\nmsgid \"Welcome\"\nmsgstr \"Bem Vindo\"\n\n#: youtube_dl_gui/mainframe.py:103\nmsgid \"Warning\"\nmsgstr \"Atenção\"\n\n#: youtube_dl_gui/mainframe.py:105\nmsgid \"Add\"\nmsgstr \"Adicionar\"\n\n#: youtube_dl_gui/mainframe.py:106\nmsgid \"Download list\"\nmsgstr \"Lista de Download\"\n\n#: youtube_dl_gui/mainframe.py:107 youtube_dl_gui/mainframe.py:516\n#: youtube_dl_gui/mainframe.py:534\nmsgid \"Delete\"\nmsgstr \"Apagar\"\n\n#: youtube_dl_gui/mainframe.py:108\nmsgid \"Play\"\nmsgstr \"Tocar\"\n\n#: youtube_dl_gui/mainframe.py:109\nmsgid \"Up\"\nmsgstr \"Subir\"\n\n#: youtube_dl_gui/mainframe.py:110\nmsgid \"Down\"\nmsgstr \"Descer\"\n\n#: youtube_dl_gui/mainframe.py:111\nmsgid \"Reload\"\nmsgstr \"Recarregar\"\n\n#: youtube_dl_gui/mainframe.py:112 youtube_dl_gui/mainframe.py:448\n#: youtube_dl_gui/mainframe.py:649\nmsgid \"Pause\"\nmsgstr \"Pausar\"\n\n#: youtube_dl_gui/mainframe.py:113 youtube_dl_gui/mainframe.py:865\n#: youtube_dl_gui/mainframe.py:866 youtube_dl_gui/optionsframe.py:582\nmsgid \"Start\"\nmsgstr \"Iniciar\"\n\n#: youtube_dl_gui/mainframe.py:114\nmsgid \"About\"\nmsgstr \"Sobre\"\n\n#: youtube_dl_gui/mainframe.py:115\nmsgid \"View Log\"\nmsgstr \"Ver Log\"\n\n#: youtube_dl_gui/mainframe.py:117\nmsgid \"Successfully downloaded {0} URL(s) in {1} day(s) {2} hour(s) {3} minute(s) {4} second(s)\"\nmsgstr \"Baixado com sucesso {0} url(s) em {1} dia(s) {2} hora(s) {3} minuto(s) {4} segundo(s)\"\n\n#: youtube_dl_gui/mainframe.py:119\nmsgid \"Downloads completed\"\nmsgstr \"Downloads completo\"\n\n#: youtube_dl_gui/mainframe.py:120\nmsgid \"Total Progress: {0:.1f}% | Queued ({1}) Paused ({2}) Active ({3}) Completed ({4}) Error ({5})\"\nmsgstr \"Progresso total: {0:.1f}% | Fila ({1}) Pausado ({2}) Ativo ({3}) Completo ({4}) Com erros ({5})\"\n\n#: youtube_dl_gui/mainframe.py:121\nmsgid \"Stopping downloads\"\nmsgstr \"Parar downloads\"\n\n#: youtube_dl_gui/mainframe.py:122\nmsgid \"Downloads stopped\"\nmsgstr \"Downloads parados\"\n\n#: youtube_dl_gui/mainframe.py:123\nmsgid \"You need to provide at least one URL\"\nmsgstr \"Você precisa fornecer pelo menos um endereço\"\n\n#: youtube_dl_gui/mainframe.py:124\nmsgid \"Downloads started\"\nmsgstr \"Downloads iniciados\"\n\n#: youtube_dl_gui/mainframe.py:125\nmsgid \"Choose Directory\"\nmsgstr \"Escolher pasta\"\n\n#: youtube_dl_gui/mainframe.py:127\nmsgid \"Download in progress. Please wait for all downloads to complete\"\nmsgstr \"Download em progresso. Aguarde todos os downloads para concluir\"\n\n#: youtube_dl_gui/mainframe.py:128\nmsgid \"Update already in progress\"\nmsgstr \"Atualização já em andamento\"\n\n#: youtube_dl_gui/mainframe.py:130\nmsgid \"Downloading latest youtube-dl. Please wait...\"\nmsgstr \"Atualizando para ultima versão youtube-dl. Aguarde...\"\n\n#: youtube_dl_gui/mainframe.py:131\nmsgid \"Youtube-dl download failed [{0}]\"\nmsgstr \"Youtube-dl download falhou [{0}]\"\n\n#: youtube_dl_gui/mainframe.py:132\nmsgid \"Successfully downloaded youtube-dl\"\nmsgstr \"Baixado com sucesso do youtube-dl\"\n\n#: youtube_dl_gui/mainframe.py:134\nmsgid \"Unable to open directory: '{dir}'. The specified path does not exist\"\nmsgstr \"Não é possível abrir diretório: '{dir}'. O caminho especificado não existe\"\n\n#: youtube_dl_gui/mainframe.py:136\nmsgid \"Error while shutting down. Make sure you typed the correct password\"\nmsgstr \"Erro ao desligar. Certifique-se de que você digitou a senha correta\"\n\n#: youtube_dl_gui/mainframe.py:138\nmsgid \"Shutting down system\"\nmsgstr \"Desligar sistema\"\n\n#: youtube_dl_gui/mainframe.py:141\nmsgid \"Extension\"\nmsgstr \"Formato\"\n\n#: youtube_dl_gui/mainframe.py:142\nmsgid \"Size\"\nmsgstr \"Tamanho\"\n\n#: youtube_dl_gui/mainframe.py:143\nmsgid \"Percent\"\nmsgstr \"Porcentagem\"\n\n#: youtube_dl_gui/mainframe.py:144\nmsgid \"ETA\"\nmsgstr \"ETA\"\n\n#: youtube_dl_gui/mainframe.py:145\nmsgid \"Speed\"\nmsgstr \"Velocidade\"\n\n#: youtube_dl_gui/mainframe.py:146\nmsgid \"Status\"\nmsgstr \"Status\"\n\n#: youtube_dl_gui/mainframe.py:235\nmsgid \"Get URL\"\nmsgstr \"Obter URL\"\n\n#: youtube_dl_gui/mainframe.py:236\nmsgid \"Get command\"\nmsgstr \"Comandos\"\n\n#: youtube_dl_gui/mainframe.py:237\nmsgid \"Open destination\"\nmsgstr \"Abrir pasta de destino\"\n\n#: youtube_dl_gui/mainframe.py:238\nmsgid \"Re-enter\"\nmsgstr \"Re-abrir\"\n\n#: youtube_dl_gui/mainframe.py:458\nmsgid \"Resume\"\nmsgstr \"Resumir\"\n\n#: youtube_dl_gui/mainframe.py:480\nmsgid \"Video\"\nmsgstr \"Video\"\n\n#: youtube_dl_gui/mainframe.py:484\nmsgid \"Audio\"\nmsgstr \"Audio\"\n\n#: youtube_dl_gui/mainframe.py:516\nmsgid \"No items selected. Please pick an action\"\nmsgstr \"Nenhum item selecionado. Escolha uma ação\"\n\n#: youtube_dl_gui/mainframe.py:516\nmsgid \"Remove all\"\nmsgstr \"Apagar tudo\"\n\n#: youtube_dl_gui/mainframe.py:516\nmsgid \"Remove completed\"\nmsgstr \"Remover completo\"\n\n#: youtube_dl_gui/mainframe.py:534\nmsgid \"Are you sure you want to remove selected items?\"\nmsgstr \"Tem certeza de que deseja remover itens selecionados?\"\n\n#: youtube_dl_gui/mainframe.py:546\nmsgid \"Item is active, cannot remove\"\nmsgstr \"O item está ativo, não pode remover\"\n\n#: youtube_dl_gui/mainframe.py:579\nmsgid \"Item is not completed\"\nmsgstr \"O item não está completo\"\n\n#: youtube_dl_gui/mainframe.py:668\nmsgid \"Update in progress. Please wait for the update to complete\"\nmsgstr \"Atualização em andamento. Aguarde até a atualização ser concluída\"\n\n#: youtube_dl_gui/mainframe.py:716\nmsgid \"Logging is disabled\"\nmsgstr \"O log está desativado\"\n\n#: youtube_dl_gui/mainframe.py:891\nmsgid \"Shutdown\"\nmsgstr \"Desligar\"\n\n#: youtube_dl_gui/mainframe.py:891\nmsgid \"Shutting down in {0} second(s)\"\nmsgstr \"Desligando em {0} segundo(s)\"\n\n#: youtube_dl_gui/mainframe.py:980\nmsgid \"No items to download\"\nmsgstr \"Nenhum item para download\"\n\n#: youtube_dl_gui/mainframe.py:1040\nmsgid \"Updates are disabled for your system. Please use the system's package manager to update youtube-dl.\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:1065\nmsgid \"Are you sure you want to exit?\"\nmsgstr \"Você tem certeza que quer sair?\"\n\n#: youtube_dl_gui/mainframe.py:1065\nmsgid \"Exit\"\nmsgstr \"Sair\"\n\n#: youtube_dl_gui/mainframe.py:1306 youtube_dl_gui/mainframe.py:1456\nmsgid \"Cancel\"\nmsgstr \"Cancelar\"\n\n#: youtube_dl_gui/mainframe.py:1455\nmsgid \"OK\"\nmsgstr \"OK\"\n\n#: youtube_dl_gui/optionsframe.py:65\nmsgid \"Reset\"\nmsgstr \"Reset\"\n\n#: youtube_dl_gui/optionsframe.py:66\nmsgid \"Close\"\nmsgstr \"Fechar\"\n\n#: youtube_dl_gui/optionsframe.py:72\nmsgid \"General\"\nmsgstr \"Geral\"\n\n#: youtube_dl_gui/optionsframe.py:73\nmsgid \"Formats\"\nmsgstr \"Formatos\"\n\n#: youtube_dl_gui/optionsframe.py:74\nmsgid \"Downloads\"\nmsgstr \"Downloads\"\n\n#: youtube_dl_gui/optionsframe.py:75\nmsgid \"Advanced\"\nmsgstr \"Avançado\"\n\n#: youtube_dl_gui/optionsframe.py:76\nmsgid \"Extra\"\nmsgstr \"Extra\"\n\n#: youtube_dl_gui/optionsframe.py:310\nmsgid \"Language\"\nmsgstr \"Idiomas\"\n\n#: youtube_dl_gui/optionsframe.py:313\nmsgid \"Filename format\"\nmsgstr \"Nome do Arquivo\"\n\n#: youtube_dl_gui/optionsframe.py:318\nmsgid \"Filename options\"\nmsgstr \"Opções de nome de arquivo\"\n\n#: youtube_dl_gui/optionsframe.py:319\nmsgid \"Restrict filenames to ASCII\"\nmsgstr \"Restringem nomes de arquivos (ASCII)\"\n\n#: youtube_dl_gui/optionsframe.py:321\nmsgid \"More options\"\nmsgstr \"Mais opções\"\n\n#: youtube_dl_gui/optionsframe.py:322\nmsgid \"Confirm on exit\"\nmsgstr \"Confirme antes de sair\"\n\n#: youtube_dl_gui/optionsframe.py:323\nmsgid \"Confirm item deletion\"\nmsgstr \"Confirmar apagar de itens\"\n\n#: youtube_dl_gui/optionsframe.py:324\nmsgid \"Inform me on download completion\"\nmsgstr \"Avisar após a conclusão do download\"\n\n#: youtube_dl_gui/optionsframe.py:326\nmsgid \"Shutdown on download completion\"\nmsgstr \"Desligamento após conclusão do download\"\n\n#: youtube_dl_gui/optionsframe.py:337\nmsgid \"SUDO password\"\nmsgstr \"SUDO senha\"\n\n#: youtube_dl_gui/optionsframe.py:415 youtube_dl_gui/optionsframe.py:816\nmsgid \"In order for the changes to take effect please restart {0}\"\nmsgstr \"Para que as alterações tenham efeito, reinicie {0}\"\n\n#: youtube_dl_gui/optionsframe.py:416 youtube_dl_gui/optionsframe.py:817\nmsgid \"Restart\"\nmsgstr \"Reiniciar\"\n\n#: youtube_dl_gui/optionsframe.py:463\nmsgid \"high\"\nmsgstr \"Alto\"\n\n#: youtube_dl_gui/optionsframe.py:463\nmsgid \"low\"\nmsgstr \"Baixo\"\n\n#: youtube_dl_gui/optionsframe.py:463\nmsgid \"mid\"\nmsgstr \"Medio\"\n\n#: youtube_dl_gui/optionsframe.py:468\nmsgid \"Video formats\"\nmsgstr \"Formato do Video\"\n\n#: youtube_dl_gui/optionsframe.py:471\nmsgid \"Audio formats\"\nmsgstr \"Formato do Audio\"\n\n#: youtube_dl_gui/optionsframe.py:474\nmsgid \"Post-Process options\"\nmsgstr \"Opções pós-processo\"\n\n#: youtube_dl_gui/optionsframe.py:475\nmsgid \"Keep original files\"\nmsgstr \"Manter arquivos originais\"\n\n#: youtube_dl_gui/optionsframe.py:476\nmsgid \"Extract audio from video file\"\nmsgstr \"Extraia áudio do arquivo de vídeo\"\n\n#: youtube_dl_gui/optionsframe.py:477\nmsgid \"Embed thumbnail in audio file\"\nmsgstr \"Incorporar miniatura no arquivo de áudio\"\n\n#: youtube_dl_gui/optionsframe.py:478\nmsgid \"Add metadata to file\"\nmsgstr \"Adicionar metadados ao arquivo\"\n\n#: youtube_dl_gui/optionsframe.py:480\nmsgid \"Audio quality\"\nmsgstr \"Qualidade do Audio\"\n\n#: youtube_dl_gui/optionsframe.py:538\nmsgid \"English\"\nmsgstr \"Inglês\"\n\n#: youtube_dl_gui/optionsframe.py:539\nmsgid \"French\"\nmsgstr \"Francês\"\n\n#: youtube_dl_gui/optionsframe.py:540\nmsgid \"German\"\nmsgstr \"Alemão\"\n\n#: youtube_dl_gui/optionsframe.py:541\nmsgid \"Greek\"\nmsgstr \"Grego\"\n\n#: youtube_dl_gui/optionsframe.py:542\nmsgid \"Hebrew\"\nmsgstr \"Hebraico\"\n\n#: youtube_dl_gui/optionsframe.py:543\nmsgid \"Italian\"\nmsgstr \"Italiano\"\n\n#: youtube_dl_gui/optionsframe.py:544\nmsgid \"Portuguese\"\nmsgstr \"Português\"\n\n#: youtube_dl_gui/optionsframe.py:545\nmsgid \"Russian\"\nmsgstr \"Russo\"\n\n#: youtube_dl_gui/optionsframe.py:546\nmsgid \"Spanish\"\nmsgstr \"Espanhol\"\n\n#: youtube_dl_gui/optionsframe.py:547\nmsgid \"Swedish\"\nmsgstr \"Sueco\"\n\n#: youtube_dl_gui/optionsframe.py:548\nmsgid \"Turkish\"\nmsgstr \"Turco\"\n\n#: youtube_dl_gui/optionsframe.py:564\nmsgid \"None\"\nmsgstr \"Nenhum\"\n\n#: youtube_dl_gui/optionsframe.py:565\nmsgid \"Automatic subtitles (YOUTUBE ONLY)\"\nmsgstr \"Baixar arquivos de legendas automáticamente (YOUTUBE SOMENTE)\"\n\n#: youtube_dl_gui/optionsframe.py:566\nmsgid \"All available subtitles\"\nmsgstr \"Todas as legendas disponíveis\"\n\n#: youtube_dl_gui/optionsframe.py:567\nmsgid \"Subtitles by language\"\nmsgstr \"Legendas por idioma\"\n\n#: youtube_dl_gui/optionsframe.py:573\nmsgid \"Subtitles\"\nmsgstr \"Legenda\"\n\n#: youtube_dl_gui/optionsframe.py:577\nmsgid \"Subtitles options\"\nmsgstr \"Opções de legenda\"\n\n#: youtube_dl_gui/optionsframe.py:578\nmsgid \"Embed subtitles into video file (mp4 ONLY)\"\nmsgstr \"Incorporar as legendas do vídeo (apenas para vídeos mp4)\"\n\n#: youtube_dl_gui/optionsframe.py:580\nmsgid \"Playlist\"\nmsgstr \"Playlist\"\n\n#: youtube_dl_gui/optionsframe.py:586 youtube_dl_gui/optionsframe.py:591\nmsgid \"Max\"\nmsgstr \"Max\"\n\n#: youtube_dl_gui/optionsframe.py:589\nmsgid \"Filesize\"\nmsgstr \"Tamanho\"\n\n#: youtube_dl_gui/optionsframe.py:594\nmsgid \"Min\"\nmsgstr \"Min\"\n\n#: youtube_dl_gui/optionsframe.py:723\nmsgid \"Retries\"\nmsgstr \"Tentativas\"\n\n#: youtube_dl_gui/optionsframe.py:726\nmsgid \"Authentication\"\nmsgstr \"Autenticação\"\n\n#: youtube_dl_gui/optionsframe.py:728\nmsgid \"Username\"\nmsgstr \"Usuario\"\n\n#: youtube_dl_gui/optionsframe.py:730\nmsgid \"Password\"\nmsgstr \"Senha\"\n\n#: youtube_dl_gui/optionsframe.py:732\nmsgid \"Video password\"\nmsgstr \"Senha do Video\"\n\n#: youtube_dl_gui/optionsframe.py:735\nmsgid \"Network\"\nmsgstr \"Rede\"\n\n#: youtube_dl_gui/optionsframe.py:737\nmsgid \"Proxy\"\nmsgstr \"Proxy\"\n\n#: youtube_dl_gui/optionsframe.py:739\nmsgid \"User agent\"\nmsgstr \"User Agent\"\n\n#: youtube_dl_gui/optionsframe.py:741\nmsgid \"Referer\"\nmsgstr \"Referente\"\n\n#: youtube_dl_gui/optionsframe.py:744\nmsgid \"Logging\"\nmsgstr \"Log\"\n\n#: youtube_dl_gui/optionsframe.py:746\nmsgid \"Enable log\"\nmsgstr \"Habilitar Log\"\n\n#: youtube_dl_gui/optionsframe.py:747\nmsgid \"View\"\nmsgstr \"Ver Log\"\n\n#: youtube_dl_gui/optionsframe.py:748\nmsgid \"Clear\"\nmsgstr \"Limpar Log\"\n\n#: youtube_dl_gui/optionsframe.py:858\nmsgid \"Youtube-dl command line options (e.g. --help)\"\nmsgstr \"Argumentos da linha de comando (e.x. --help)\"\n\n#: youtube_dl_gui/optionsframe.py:861\nmsgid \"Extra options\"\nmsgstr \"Opções extras\"\n\n#: youtube_dl_gui/optionsframe.py:863\nmsgid \"Debug youtube-dl\"\nmsgstr \"Depurar youtube-dl\"\n\n#: youtube_dl_gui/optionsframe.py:864\nmsgid \"Ignore errors\"\nmsgstr \"Ignorar erros\"\n\n#: youtube_dl_gui/optionsframe.py:865\nmsgid \"Ignore youtube-dl config\"\nmsgstr \"Ignorar configurações do youtube-dl\"\n\n#: youtube_dl_gui/optionsframe.py:866\nmsgid \"No mtime\"\nmsgstr \"Sem mtime\"\n\n#: youtube_dl_gui/optionsframe.py:867\nmsgid \"Prefer native HLS\"\nmsgstr \"Preferir HLS nativo\"\n\n#: youtube_dl_gui/optionsframe.py:928\nmsgid \"Log Viewer\"\nmsgstr \"Ver log\"\n"
  },
  {
    "path": "youtube_dl_gui/locale/ru_RU/LC_MESSAGES/youtube_dl_gui.po",
    "content": "# Youtube-dlG localization file.\n# FIRST AUTHOR: Sotiris Papadopoulos <ytubedlg@gmail.com>, 2015.\n# Nikita «Arttse» Bystrov <arttsesoft@gmail.com>, 2015.\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: youtube-dlg 0.4\\n\"\n\"POT-Creation-Date: 2018-01-15 16:42+EET\\n\"\n\"PO-Revision-Date: 2017-06-18 18:20+EEST\\n\"\n\"Last-Translator: Nikita «Arttse» Bystrov <arttsesoft@gmail.com>\\n\"\n\"Language-Team: ru\\n\"\n\"Language: ru\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: pygettext.py 1.5\\n\"\n\n#: youtube_dl_gui/__init__.py:91\nmsgid \"Error\"\nmsgstr \"\"\n\n#: youtube_dl_gui/__init__.py:91\nmsgid \"Failed to locate youtube-dl and updates are disabled\"\nmsgstr \"\"\n\n#: youtube_dl_gui/formats.py:9 youtube_dl_gui/formats.py:109\nmsgid \"ID\"\nmsgstr \"\"\n\n#: youtube_dl_gui/formats.py:10 youtube_dl_gui/formats.py:110\n#: youtube_dl_gui/mainframe.py:140\nmsgid \"Title\"\nmsgstr \"Заголовок\"\n\n#: youtube_dl_gui/formats.py:11 youtube_dl_gui/formats.py:111\nmsgid \"Title + ID\"\nmsgstr \"Заголовок + ID\"\n\n#: youtube_dl_gui/formats.py:12 youtube_dl_gui/formats.py:112\nmsgid \"Title + Quality\"\nmsgstr \"Заголовок + Качество\"\n\n#: youtube_dl_gui/formats.py:13 youtube_dl_gui/formats.py:113\nmsgid \"Title + ID + Quality\"\nmsgstr \"Заголовок + ID + Качество\"\n\n#: youtube_dl_gui/formats.py:14 youtube_dl_gui/formats.py:114\nmsgid \"Custom\"\nmsgstr \"Пользовательский\"\n\n#: youtube_dl_gui/formats.py:19 youtube_dl_gui/formats.py:119\n#: youtube_dl_gui/mainframe.py:503 youtube_dl_gui/mainframe.py:506\nmsgid \"default\"\nmsgstr \"по умолчанию\"\n\n#: youtube_dl_gui/mainframe.py:97\nmsgid \"Enter URLs below\"\nmsgstr \"Введите URL-адреса ниже\"\n\n#: youtube_dl_gui/mainframe.py:98\nmsgid \"Update\"\nmsgstr \"Обновить\"\n\n#: youtube_dl_gui/mainframe.py:99 youtube_dl_gui/optionsframe.py:41\nmsgid \"Options\"\nmsgstr \"Настройки\"\n\n#: youtube_dl_gui/mainframe.py:100 youtube_dl_gui/optionsframe.py:584\nmsgid \"Stop\"\nmsgstr \"Остановить\"\n\n#: youtube_dl_gui/mainframe.py:101\nmsgid \"Info\"\nmsgstr \"Информация\"\n\n#: youtube_dl_gui/mainframe.py:102\nmsgid \"Welcome\"\nmsgstr \"Добро пожаловать\"\n\n#: youtube_dl_gui/mainframe.py:103\nmsgid \"Warning\"\nmsgstr \"Внимание\"\n\n#: youtube_dl_gui/mainframe.py:105\nmsgid \"Add\"\nmsgstr \"Добавить\"\n\n#: youtube_dl_gui/mainframe.py:106\nmsgid \"Download list\"\nmsgstr \"Список для скачивания\"\n\n#: youtube_dl_gui/mainframe.py:107 youtube_dl_gui/mainframe.py:516\n#: youtube_dl_gui/mainframe.py:534\nmsgid \"Delete\"\nmsgstr \"Удалить\"\n\n#: youtube_dl_gui/mainframe.py:108\nmsgid \"Play\"\nmsgstr \"Воспроизвести\"\n\n#: youtube_dl_gui/mainframe.py:109\nmsgid \"Up\"\nmsgstr \"Вверх\"\n\n#: youtube_dl_gui/mainframe.py:110\nmsgid \"Down\"\nmsgstr \"Вниз\"\n\n#: youtube_dl_gui/mainframe.py:111\nmsgid \"Reload\"\nmsgstr \"Перезапустить\"\n\n#: youtube_dl_gui/mainframe.py:112 youtube_dl_gui/mainframe.py:448\n#: youtube_dl_gui/mainframe.py:649\nmsgid \"Pause\"\nmsgstr \"Пауза\"\n\n#: youtube_dl_gui/mainframe.py:113 youtube_dl_gui/mainframe.py:865\n#: youtube_dl_gui/mainframe.py:866 youtube_dl_gui/optionsframe.py:582\nmsgid \"Start\"\nmsgstr \"Запустить\"\n\n#: youtube_dl_gui/mainframe.py:114\nmsgid \"About\"\nmsgstr \"О программе\"\n\n#: youtube_dl_gui/mainframe.py:115\nmsgid \"View Log\"\nmsgstr \"Просмотреть журнал\"\n\n#: youtube_dl_gui/mainframe.py:117\nmsgid \"Successfully downloaded {0} URL(s) in {1} day(s) {2} hour(s) {3} minute(s) {4} second(s)\"\nmsgstr \"Успешно скачано {0} по указанным URL-адресам за {1} дней {2} часов {3} минут {4} секунд\"\n\n#: youtube_dl_gui/mainframe.py:119\nmsgid \"Downloads completed\"\nmsgstr \"Скачивание завершено\"\n\n#: youtube_dl_gui/mainframe.py:120\nmsgid \"Total Progress: {0:.1f}% | Queued ({1}) Paused ({2}) Active ({3}) Completed ({4}) Error ({5})\"\nmsgstr \"Общий прогресс: {0:.1f}% | В очереди ({1}) На паузе ({2}) Активных ({3}) Завершенных ({4}) С ошибками ({5})\"\n\n#: youtube_dl_gui/mainframe.py:121\nmsgid \"Stopping downloads\"\nmsgstr \"Производится остановка скачивания\"\n\n#: youtube_dl_gui/mainframe.py:122\nmsgid \"Downloads stopped\"\nmsgstr \"Скачивание остановлено\"\n\n#: youtube_dl_gui/mainframe.py:123\nmsgid \"You need to provide at least one URL\"\nmsgstr \"Вы должны указать хотя бы один URL-адрес\"\n\n#: youtube_dl_gui/mainframe.py:124\nmsgid \"Downloads started\"\nmsgstr \"Началось скачивание\"\n\n#: youtube_dl_gui/mainframe.py:125\nmsgid \"Choose Directory\"\nmsgstr \"Выбрать директорию\"\n\n#: youtube_dl_gui/mainframe.py:127\nmsgid \"Download in progress. Please wait for all downloads to complete\"\nmsgstr \"Скачивание в процессе. Пожалуйста, подождите, пока все загрузки будут завершены\"\n\n#: youtube_dl_gui/mainframe.py:128\nmsgid \"Update already in progress\"\nmsgstr \"Обновление уже в процессе\"\n\n#: youtube_dl_gui/mainframe.py:130\nmsgid \"Downloading latest youtube-dl. Please wait...\"\nmsgstr \"Загрузка последней версии youtube-dl. Пожалуйста, подождите...\"\n\n#: youtube_dl_gui/mainframe.py:131\nmsgid \"Youtube-dl download failed [{0}]\"\nmsgstr \"Не удалось скачать youtube-dl [{0}]\"\n\n#: youtube_dl_gui/mainframe.py:132\nmsgid \"Successfully downloaded youtube-dl\"\nmsgstr \"Youtube-dl успешно скачан\"\n\n#: youtube_dl_gui/mainframe.py:134\nmsgid \"Unable to open directory: '{dir}'. The specified path does not exist\"\nmsgstr \"Невозможно открыть каталог: '{dir}'. Указанный путь не существует\"\n\n#: youtube_dl_gui/mainframe.py:136\nmsgid \"Error while shutting down. Make sure you typed the correct password\"\nmsgstr \"Ошибка при выключении. Убедитесь, что вы ввели корректный пароль вашей учетной записи администратора\"\n\n#: youtube_dl_gui/mainframe.py:138\nmsgid \"Shutting down system\"\nmsgstr \"Выключение системы\"\n\n#: youtube_dl_gui/mainframe.py:141\nmsgid \"Extension\"\nmsgstr \"Расширение\"\n\n#: youtube_dl_gui/mainframe.py:142\nmsgid \"Size\"\nmsgstr \"Размер\"\n\n#: youtube_dl_gui/mainframe.py:143\nmsgid \"Percent\"\nmsgstr \"Процент\"\n\n#: youtube_dl_gui/mainframe.py:144\nmsgid \"ETA\"\nmsgstr \"Осталось\"\n\n#: youtube_dl_gui/mainframe.py:145\nmsgid \"Speed\"\nmsgstr \"Скорость скачивания\"\n\n#: youtube_dl_gui/mainframe.py:146\nmsgid \"Status\"\nmsgstr \"Статус\"\n\n#: youtube_dl_gui/mainframe.py:235\nmsgid \"Get URL\"\nmsgstr \"Скопировать URL-адрес\"\n\n#: youtube_dl_gui/mainframe.py:236\nmsgid \"Get command\"\nmsgstr \"Скопировать команду для CLI\"\n\n#: youtube_dl_gui/mainframe.py:237\nmsgid \"Open destination\"\nmsgstr \"Открыть папку назначения\"\n\n#: youtube_dl_gui/mainframe.py:238\nmsgid \"Re-enter\"\nmsgstr \"Повторить\"\n\n#: youtube_dl_gui/mainframe.py:458\nmsgid \"Resume\"\nmsgstr \"Возобновить\"\n\n#: youtube_dl_gui/mainframe.py:480\nmsgid \"Video\"\nmsgstr \"Видео\"\n\n#: youtube_dl_gui/mainframe.py:484\nmsgid \"Audio\"\nmsgstr \"Аудио\"\n\n#: youtube_dl_gui/mainframe.py:516\nmsgid \"No items selected. Please pick an action\"\nmsgstr \"Ни одного элемента не выбрано. Пожалуйста, выберите действие\"\n\n#: youtube_dl_gui/mainframe.py:516\nmsgid \"Remove all\"\nmsgstr \"Удалить все\"\n\n#: youtube_dl_gui/mainframe.py:516\nmsgid \"Remove completed\"\nmsgstr \"Удалить завершенные\"\n\n#: youtube_dl_gui/mainframe.py:534\nmsgid \"Are you sure you want to remove selected items?\"\nmsgstr \"Вы действительно хотите удалить выбранные элементы?\"\n\n#: youtube_dl_gui/mainframe.py:546\nmsgid \"Item is active, cannot remove\"\nmsgstr \"Элемент активен, его нельзя удалить\"\n\n#: youtube_dl_gui/mainframe.py:579\nmsgid \"Item is not completed\"\nmsgstr \"Элемент не завершен\"\n\n#: youtube_dl_gui/mainframe.py:668\nmsgid \"Update in progress. Please wait for the update to complete\"\nmsgstr \"Обновление в процессе. Пожалуйста, дождитесь завершения обновления\"\n\n#: youtube_dl_gui/mainframe.py:716\nmsgid \"Logging is disabled\"\nmsgstr \"Журнал отключен\"\n\n#: youtube_dl_gui/mainframe.py:891\nmsgid \"Shutdown\"\nmsgstr \"Выключение\"\n\n#: youtube_dl_gui/mainframe.py:891\nmsgid \"Shutting down in {0} second(s)\"\nmsgstr \"Выключение через {0} секунд\"\n\n#: youtube_dl_gui/mainframe.py:980\nmsgid \"No items to download\"\nmsgstr \"Нет ни одного элемента для скачивания\"\n\n#: youtube_dl_gui/mainframe.py:1040\nmsgid \"Updates are disabled for your system. Please use the system's package manager to update youtube-dl.\"\nmsgstr \"\"\n\n#: youtube_dl_gui/mainframe.py:1065\nmsgid \"Are you sure you want to exit?\"\nmsgstr \"Вы уверены, что хотите выйти?\"\n\n#: youtube_dl_gui/mainframe.py:1065\nmsgid \"Exit\"\nmsgstr \"Выход\"\n\n#: youtube_dl_gui/mainframe.py:1306 youtube_dl_gui/mainframe.py:1456\nmsgid \"Cancel\"\nmsgstr \"Отмена\"\n\n#: youtube_dl_gui/mainframe.py:1455\nmsgid \"OK\"\nmsgstr \"\"\n\n#: youtube_dl_gui/optionsframe.py:65\nmsgid \"Reset\"\nmsgstr \"Сбросить\"\n\n#: youtube_dl_gui/optionsframe.py:66\nmsgid \"Close\"\nmsgstr \"Закрыть\"\n\n#: youtube_dl_gui/optionsframe.py:72\nmsgid \"General\"\nmsgstr \"Общие\"\n\n#: youtube_dl_gui/optionsframe.py:73\nmsgid \"Formats\"\nmsgstr \"Форматы\"\n\n#: youtube_dl_gui/optionsframe.py:74\nmsgid \"Downloads\"\nmsgstr \"Загрузки\"\n\n#: youtube_dl_gui/optionsframe.py:75\nmsgid \"Advanced\"\nmsgstr \"Дополнительные\"\n\n#: youtube_dl_gui/optionsframe.py:76\nmsgid \"Extra\"\nmsgstr \"Особые\"\n\n#: youtube_dl_gui/optionsframe.py:310\nmsgid \"Language\"\nmsgstr \"Язык\"\n\n#: youtube_dl_gui/optionsframe.py:313\nmsgid \"Filename format\"\nmsgstr \"Формат имени файла\"\n\n#: youtube_dl_gui/optionsframe.py:318\nmsgid \"Filename options\"\nmsgstr \"Параметры имени файла\"\n\n#: youtube_dl_gui/optionsframe.py:319\nmsgid \"Restrict filenames to ASCII\"\nmsgstr \"Ограничить имена файлов в ASCII\"\n\n#: youtube_dl_gui/optionsframe.py:321\nmsgid \"More options\"\nmsgstr \"Больше параметров\"\n\n#: youtube_dl_gui/optionsframe.py:322\nmsgid \"Confirm on exit\"\nmsgstr \"Подтверждение при выходе\"\n\n#: youtube_dl_gui/optionsframe.py:323\nmsgid \"Confirm item deletion\"\nmsgstr \"Подтверждение при удалении элемента\"\n\n#: youtube_dl_gui/optionsframe.py:324\nmsgid \"Inform me on download completion\"\nmsgstr \"Сообщите мне о завершении загрузки\"\n\n#: youtube_dl_gui/optionsframe.py:326\nmsgid \"Shutdown on download completion\"\nmsgstr \"Выключение по завершении загрузки\"\n\n#: youtube_dl_gui/optionsframe.py:337\nmsgid \"SUDO password\"\nmsgstr \"Пароль от вашей учетной записи (администратора)\"\n\n#: youtube_dl_gui/optionsframe.py:415 youtube_dl_gui/optionsframe.py:816\nmsgid \"In order for the changes to take effect please restart {0}\"\nmsgstr \"Для того, чтобы изменения вступили в силу, пожалуйста, перезапустите {0}\"\n\n#: youtube_dl_gui/optionsframe.py:416 youtube_dl_gui/optionsframe.py:817\nmsgid \"Restart\"\nmsgstr \"Перезагрузка\"\n\n#: youtube_dl_gui/optionsframe.py:463\nmsgid \"high\"\nmsgstr \"высокое\"\n\n#: youtube_dl_gui/optionsframe.py:463\nmsgid \"low\"\nmsgstr \"низкое\"\n\n#: youtube_dl_gui/optionsframe.py:463\nmsgid \"mid\"\nmsgstr \"среднее\"\n\n#: youtube_dl_gui/optionsframe.py:468\nmsgid \"Video formats\"\nmsgstr \"Формат видео\"\n\n#: youtube_dl_gui/optionsframe.py:471\nmsgid \"Audio formats\"\nmsgstr \"Формат аудио\"\n\n#: youtube_dl_gui/optionsframe.py:474\nmsgid \"Post-Process options\"\nmsgstr \"Параметры пост-обработки\"\n\n#: youtube_dl_gui/optionsframe.py:475\nmsgid \"Keep original files\"\nmsgstr \"Сохранять исходные файлы\"\n\n#: youtube_dl_gui/optionsframe.py:476\nmsgid \"Extract audio from video file\"\nmsgstr \"Извлечь аудио из видео файла\"\n\n#: youtube_dl_gui/optionsframe.py:477\nmsgid \"Embed thumbnail in audio file\"\nmsgstr \"Добавить эскиз в аудиофайл\"\n\n#: youtube_dl_gui/optionsframe.py:478\nmsgid \"Add metadata to file\"\nmsgstr \"Добавить метаданные в файл\"\n\n#: youtube_dl_gui/optionsframe.py:480\nmsgid \"Audio quality\"\nmsgstr \"Качество аудио\"\n\n#: youtube_dl_gui/optionsframe.py:538\nmsgid \"English\"\nmsgstr \"Английский\"\n\n#: youtube_dl_gui/optionsframe.py:539\nmsgid \"French\"\nmsgstr \"Французский\"\n\n#: youtube_dl_gui/optionsframe.py:540\nmsgid \"German\"\nmsgstr \"Немецкий\"\n\n#: youtube_dl_gui/optionsframe.py:541\nmsgid \"Greek\"\nmsgstr \"Греческий\"\n\n#: youtube_dl_gui/optionsframe.py:542\nmsgid \"Hebrew\"\nmsgstr \"Иврит\"\n\n#: youtube_dl_gui/optionsframe.py:543\nmsgid \"Italian\"\nmsgstr \"Итальянский\"\n\n#: youtube_dl_gui/optionsframe.py:544\nmsgid \"Portuguese\"\nmsgstr \"Португальский\"\n\n#: youtube_dl_gui/optionsframe.py:545\nmsgid \"Russian\"\nmsgstr \"Русский\"\n\n#: youtube_dl_gui/optionsframe.py:546\nmsgid \"Spanish\"\nmsgstr \"Испанский\"\n\n#: youtube_dl_gui/optionsframe.py:547\nmsgid \"Swedish\"\nmsgstr \"Шведский\"\n\n#: youtube_dl_gui/optionsframe.py:548\nmsgid \"Turkish\"\nmsgstr \"Турецкий\"\n\n#: youtube_dl_gui/optionsframe.py:564\nmsgid \"None\"\nmsgstr \"Никакие\"\n\n#: youtube_dl_gui/optionsframe.py:565\nmsgid \"Automatic subtitles (YOUTUBE ONLY)\"\nmsgstr \"Скачать файл авто-субтитров (ТОЛЬКО YOUTUBE)\"\n\n#: youtube_dl_gui/optionsframe.py:566\nmsgid \"All available subtitles\"\nmsgstr \"Скачать все имеющиеся субтитры\"\n\n#: youtube_dl_gui/optionsframe.py:567\nmsgid \"Subtitles by language\"\nmsgstr \"Язык субтитров\"\n\n#: youtube_dl_gui/optionsframe.py:573\nmsgid \"Subtitles\"\nmsgstr \"Субтитры\"\n\n#: youtube_dl_gui/optionsframe.py:577\nmsgid \"Subtitles options\"\nmsgstr \"Параметры субтитров\"\n\n#: youtube_dl_gui/optionsframe.py:578\nmsgid \"Embed subtitles into video file (mp4 ONLY)\"\nmsgstr \"Встроить субтитры в видео (только mp4)\"\n\n#: youtube_dl_gui/optionsframe.py:580\nmsgid \"Playlist\"\nmsgstr \"Плейлист\"\n\n#: youtube_dl_gui/optionsframe.py:586 youtube_dl_gui/optionsframe.py:591\nmsgid \"Max\"\nmsgstr \"Максимум\"\n\n#: youtube_dl_gui/optionsframe.py:589\nmsgid \"Filesize\"\nmsgstr \"Размер файла\"\n\n#: youtube_dl_gui/optionsframe.py:594\nmsgid \"Min\"\nmsgstr \"Минимум\"\n\n#: youtube_dl_gui/optionsframe.py:723\nmsgid \"Retries\"\nmsgstr \"Количество попыток\"\n\n#: youtube_dl_gui/optionsframe.py:726\nmsgid \"Authentication\"\nmsgstr \"Аутентификация\"\n\n#: youtube_dl_gui/optionsframe.py:728\nmsgid \"Username\"\nmsgstr \"Имя пользователя\"\n\n#: youtube_dl_gui/optionsframe.py:730\nmsgid \"Password\"\nmsgstr \"Пароль\"\n\n#: youtube_dl_gui/optionsframe.py:732\nmsgid \"Video password\"\nmsgstr \"Пароль на видео\"\n\n#: youtube_dl_gui/optionsframe.py:735\nmsgid \"Network\"\nmsgstr \"Сеть\"\n\n#: youtube_dl_gui/optionsframe.py:737\nmsgid \"Proxy\"\nmsgstr \"Прокси\"\n\n#: youtube_dl_gui/optionsframe.py:739\nmsgid \"User agent\"\nmsgstr \"Строка агента пользователя\"\n\n#: youtube_dl_gui/optionsframe.py:741\nmsgid \"Referer\"\nmsgstr \"Реферер\"\n\n#: youtube_dl_gui/optionsframe.py:744\nmsgid \"Logging\"\nmsgstr \"Логирование\"\n\n#: youtube_dl_gui/optionsframe.py:746\nmsgid \"Enable log\"\nmsgstr \"Вести журнал\"\n\n#: youtube_dl_gui/optionsframe.py:747\nmsgid \"View\"\nmsgstr \"Просмотреть\"\n\n#: youtube_dl_gui/optionsframe.py:748\nmsgid \"Clear\"\nmsgstr \"Очистить\"\n\n#: youtube_dl_gui/optionsframe.py:858\nmsgid \"Youtube-dl command line options (e.g. --help)\"\nmsgstr \"Параметры командной строки youtube-dl (например --help)\"\n\n#: youtube_dl_gui/optionsframe.py:861\nmsgid \"Extra options\"\nmsgstr \"Особые параметры\"\n\n#: youtube_dl_gui/optionsframe.py:863\nmsgid \"Debug youtube-dl\"\nmsgstr \"Отладка youtube-dl\"\n\n#: youtube_dl_gui/optionsframe.py:864\nmsgid \"Ignore errors\"\nmsgstr \"Игнорировать ошибки\"\n\n#: youtube_dl_gui/optionsframe.py:865\nmsgid \"Ignore youtube-dl config\"\nmsgstr \"Игнорировать конфигурацию youtube-dl\"\n\n#: youtube_dl_gui/optionsframe.py:866\nmsgid \"No mtime\"\nmsgstr \"Не использовать mtime\"\n\n#: youtube_dl_gui/optionsframe.py:867\nmsgid \"Prefer native HLS\"\nmsgstr \"Использовать нативный HLS\"\n\n#: youtube_dl_gui/optionsframe.py:928\nmsgid \"Log Viewer\"\nmsgstr \"Просмотрщик журнала\"\n"
  },
  {
    "path": "youtube_dl_gui/logmanager.py",
    "content": "#!/usr/bin/env python2\n# -*- coding: utf-8 -*-\n\n\"\"\"Youtubedlg module responsible for handling the log stuff. \"\"\"\n\nfrom __future__ import unicode_literals\n\nimport os.path\nfrom time import strftime\n\nfrom .utils import (\n    os_path_exists,\n    get_encoding,\n    check_path\n)\n\n\nclass LogManager(object):\n\n    \"\"\"Simple log manager for youtube-dl.\n\n    This class is mainly used to log the youtube-dl STDERR.\n\n    Attributes:\n        LOG_FILENAME (string): Filename of the log file.\n        TIME_TEMPLATE (string): Custom template to log the time.\n        MAX_LOGSIZE (int): Maximum size(Bytes) of the log file.\n\n    Args:\n        config_path (string): Absolute path where LogManager should\n            store the log file.\n\n        add_time (boolean): If True LogManager will also log the time.\n\n    \"\"\"\n\n    LOG_FILENAME = \"log\"\n    TIME_TEMPLATE = \"[{time}] {error_msg}\"\n    MAX_LOGSIZE = 524288  # Bytes\n\n    def __init__(self, config_path, add_time=False):\n        self.config_path = config_path\n        self.add_time = add_time\n        self.log_file = os.path.join(config_path, self.LOG_FILENAME)\n        self._encoding = get_encoding()\n        self._init_log()\n        self._auto_clear_log()\n\n    def log_size(self):\n        \"\"\"Return log file size in Bytes. \"\"\"\n        if not os_path_exists(self.log_file):\n            return 0\n\n        return os.path.getsize(self.log_file)\n\n    def clear(self):\n        \"\"\"Clear log file. \"\"\"\n        self._write('', 'w')\n\n    def log(self, data):\n        \"\"\"Log data to the log file.\n\n        Args:\n            data (string): String to write to the log file.\n\n        \"\"\"\n        if isinstance(data, basestring):\n            self._write(data + '\\n', 'a')\n\n    def _write(self, data, mode):\n        \"\"\"Write data to the log file.\n\n        That's the main method for writing to the log file.\n\n        Args:\n            data (string): String to write on the log file.\n            mode (string): Can be any IO mode supported by python.\n\n        \"\"\"\n        check_path(self.config_path)\n\n        with open(self.log_file, mode) as log:\n            if mode == 'a' and self.add_time:\n                msg = self.TIME_TEMPLATE.format(time=strftime('%c'), error_msg=data)\n            else:\n                msg = data\n\n            log.write(msg.encode(self._encoding, 'ignore'))\n\n    def _init_log(self):\n        \"\"\"Initialize the log file if not exist. \"\"\"\n        if not os_path_exists(self.log_file):\n            self._write('', 'w')\n\n    def _auto_clear_log(self):\n        \"\"\"Auto clear the log file. \"\"\"\n        if self.log_size() > self.MAX_LOGSIZE:\n            self.clear()\n"
  },
  {
    "path": "youtube_dl_gui/mainframe.py",
    "content": "#!/usr/bin/env python2\n# -*- coding: utf-8 -*-\n\n\"\"\"Youtubedlg module responsible for the main app window. \"\"\"\n\nfrom __future__ import unicode_literals\n\nimport os\nimport gettext\n\nimport wx\nfrom wx.lib.pubsub import setuparg1 #NOTE Should remove deprecated\nfrom wx.lib.pubsub import pub as Publisher\n\nfrom wx.lib.mixins.listctrl import ListCtrlAutoWidthMixin\n\nfrom .parsers import OptionsParser\n\nfrom .optionsframe import (\n    OptionsFrame,\n    LogGUI\n)\n\nfrom .updatemanager import (\n    UPDATE_PUB_TOPIC,\n    UpdateThread\n)\n\nfrom .downloadmanager import (\n    MANAGER_PUB_TOPIC,\n    WORKER_PUB_TOPIC,\n    DownloadManager,\n    DownloadList,\n    DownloadItem\n)\n\nfrom .utils import (\n    get_pixmaps_dir,\n    build_command,\n    get_icon_file,\n    shutdown_sys,\n    remove_file,\n    open_file,\n    get_time\n)\n\nfrom .widgets import CustomComboBox\n\nfrom .formats import (\n    DEFAULT_FORMATS,\n    VIDEO_FORMATS,\n    AUDIO_FORMATS,\n    FORMATS\n)\n\nfrom .info import (\n    __descriptionfull__,\n    __licensefull__,\n    __projecturl__,\n    __appname__,\n    __author__\n)\n\nfrom .version import __version__\n\n\nclass MainFrame(wx.Frame):\n\n    \"\"\"Main window class.\n\n    This class is responsible for creating the main app window\n    and binding the events.\n\n    Attributes:\n        FRAMES_MIN_SIZE (tuple): Tuple that contains the minumum width, height of the frame.\n\n        Labels area (strings): Strings for the widgets labels.\n\n        STATUSLIST_COLUMNS (dict): Python dictionary which holds informations\n            about the wxListCtrl columns. For more informations read the\n            comments above the STATUSLIST_COLUMNS declaration.\n\n    Args:\n        opt_manager (optionsmanager.OptionsManager): Object responsible for\n            handling the settings.\n\n        log_manager (logmanager.LogManager): Object responsible for handling\n            the log stuff.\n\n        parent (wx.Window): Frame parent.\n\n    \"\"\"\n\n    FRAMES_MIN_SIZE = (560, 360)\n\n    # Labels area\n    URLS_LABEL = _(\"Enter URLs below\")\n    UPDATE_LABEL = _(\"Update\")\n    OPTIONS_LABEL = _(\"Options\")\n    STOP_LABEL = _(\"Stop\")\n    INFO_LABEL = _(\"Info\")\n    WELCOME_MSG = _(\"Welcome\")\n    WARNING_LABEL = _(\"Warning\")\n\n    ADD_LABEL = _(\"Add\")\n    DOWNLOAD_LIST_LABEL = _(\"Download list\")\n    DELETE_LABEL = _(\"Delete\")\n    PLAY_LABEL = _(\"Play\")\n    UP_LABEL = _(\"Up\")\n    DOWN_LABEL = _(\"Down\")\n    RELOAD_LABEL = _(\"Reload\")\n    PAUSE_LABEL = _(\"Pause\")\n    START_LABEL = _(\"Start\")\n    ABOUT_LABEL = _(\"About\")\n    VIEWLOG_LABEL = _(\"View Log\")\n\n    SUCC_REPORT_MSG = _(\"Successfully downloaded {0} URL(s) in {1} \"\n                       \"day(s) {2} hour(s) {3} minute(s) {4} second(s)\")\n    DL_COMPLETED_MSG = _(\"Downloads completed\")\n    URL_REPORT_MSG = _(\"Total Progress: {0:.1f}% | Queued ({1}) Paused ({2}) Active ({3}) Completed ({4}) Error ({5})\")\n    CLOSING_MSG = _(\"Stopping downloads\")\n    CLOSED_MSG = _(\"Downloads stopped\")\n    PROVIDE_URL_MSG = _(\"You need to provide at least one URL\")\n    DOWNLOAD_STARTED = _(\"Downloads started\")\n    CHOOSE_DIRECTORY = _(\"Choose Directory\")\n\n    DOWNLOAD_ACTIVE = _(\"Download in progress. Please wait for all downloads to complete\")\n    UPDATE_ACTIVE = _(\"Update already in progress\")\n\n    UPDATING_MSG = _(\"Downloading latest youtube-dl. Please wait...\")\n    UPDATE_ERR_MSG = _(\"Youtube-dl download failed [{0}]\")\n    UPDATE_SUCC_MSG = _(\"Successfully downloaded youtube-dl\")\n\n    OPEN_DIR_ERR = _(\"Unable to open directory: '{dir}'. \"\n                    \"The specified path does not exist\")\n    SHUTDOWN_ERR = _(\"Error while shutting down. \"\n                    \"Make sure you typed the correct password\")\n    SHUTDOWN_MSG = _(\"Shutting down system\")\n\n    VIDEO_LABEL = _(\"Title\")\n    EXTENSION_LABEL = _(\"Extension\")\n    SIZE_LABEL = _(\"Size\")\n    PERCENT_LABEL = _(\"Percent\")\n    ETA_LABEL = _(\"ETA\")\n    SPEED_LABEL = _(\"Speed\")\n    STATUS_LABEL = _(\"Status\")\n    #################################\n\n    # STATUSLIST_COLUMNS\n    #\n    # Dictionary which contains the columns for the wxListCtrl widget.\n    # Each key represents a column and holds informations about itself.\n    # Structure informations:\n    #  column_key: (column_number, column_label, minimum_width, is_resizable)\n    #\n    STATUSLIST_COLUMNS = {\n        'filename': (0, VIDEO_LABEL, 150, True),\n        'extension': (1, EXTENSION_LABEL, 60, False),\n        'filesize': (2, SIZE_LABEL, 80, False),\n        'percent': (3, PERCENT_LABEL, 65, False),\n        'eta': (4, ETA_LABEL, 45, False),\n        'speed': (5, SPEED_LABEL, 90, False),\n        'status': (6, STATUS_LABEL, 160, False)\n    }\n\n    def __init__(self, opt_manager, log_manager, parent=None):\n        super(MainFrame, self).__init__(parent, wx.ID_ANY, __appname__, size=opt_manager.options[\"main_win_size\"])\n        self.opt_manager = opt_manager\n        self.log_manager = log_manager\n        self.download_manager = None\n        self.update_thread = None\n        self.app_icon = None  #REFACTOR Get and set on __init__.py\n\n        self._download_list = DownloadList()\n\n        # Set up youtube-dl options parser\n        self._options_parser = OptionsParser()\n\n        # Get the pixmaps directory\n        self._pixmaps_path = get_pixmaps_dir()\n\n        # Set the Timer\n        self._app_timer = wx.Timer(self)\n\n        # Set the app icon\n        app_icon_path = get_icon_file()\n        if app_icon_path is not None:\n            self.app_icon = wx.Icon(app_icon_path, wx.BITMAP_TYPE_PNG)\n            self.SetIcon(self.app_icon)\n\n        bitmap_data = (\n           (\"down\", \"arrow_down_32px.png\"),\n           (\"up\", \"arrow_up_32px.png\"),\n           (\"play\", \"camera_32px.png\"),\n           (\"start\", \"cloud_download_32px.png\"),\n           (\"delete\", \"delete_32px.png\"),\n           (\"folder\", \"folder_32px.png\"),\n           (\"pause\", \"pause_32px.png\"),\n           (\"resume\", \"play_arrow_32px.png\"),\n           (\"reload\", \"reload_32px.png\"),\n           (\"settings\", \"settings_20px.png\"),\n           (\"stop\", \"stop_32px.png\")\n        )\n\n        self._bitmaps = {}\n\n        for item in bitmap_data:\n            target, name = item\n            self._bitmaps[target] = wx.Bitmap(os.path.join(self._pixmaps_path, name))\n\n        # Set the data for all the wx.Button items\n        # name, label, size, event_handler\n        buttons_data = (\n            (\"delete\", self.DELETE_LABEL, (-1, -1), self._on_delete, wx.BitmapButton),\n            (\"play\", self.PLAY_LABEL, (-1, -1), self._on_play, wx.BitmapButton),\n            (\"up\", self.UP_LABEL, (-1, -1), self._on_arrow_up, wx.BitmapButton),\n            (\"down\", self.DOWN_LABEL, (-1, -1), self._on_arrow_down, wx.BitmapButton),\n            (\"reload\", self.RELOAD_LABEL, (-1, -1), self._on_reload, wx.BitmapButton),\n            (\"pause\", self.PAUSE_LABEL, (-1, -1), self._on_pause, wx.BitmapButton),\n            (\"start\", self.START_LABEL, (-1, -1), self._on_start, wx.BitmapButton),\n            (\"savepath\", \"...\", (35, -1), self._on_savepath, wx.Button),\n            (\"add\", self.ADD_LABEL, (-1, -1), self._on_add, wx.Button)\n        )\n\n        # Set the data for the settings menu item\n        # label, event_handler\n        settings_menu_data = (\n            (self.OPTIONS_LABEL, self._on_options),\n            (self.UPDATE_LABEL, self._on_update),\n            (self.VIEWLOG_LABEL, self._on_viewlog),\n            (self.ABOUT_LABEL, self._on_about)\n        )\n\n        statuslist_menu_data = (\n            (_(\"Get URL\"), self._on_geturl),\n            (_(\"Get command\"), self._on_getcmd),\n            (_(\"Open destination\"), self._on_open_dest),\n            (_(\"Re-enter\"), self._on_reenter)\n        )\n\n        # Create options frame\n        self._options_frame = OptionsFrame(self)\n\n        # Create frame components\n        self._panel = wx.Panel(self)\n\n        self._url_text = self._create_statictext(self.URLS_LABEL)\n\n        #REFACTOR Move to buttons_data\n        self._settings_button = self._create_bitmap_button(self._bitmaps[\"settings\"], (30, 30), self._on_settings)\n\n        self._url_list = self._create_textctrl(wx.TE_MULTILINE | wx.TE_DONTWRAP, self._on_urllist_edit)\n\n        self._folder_icon = self._create_static_bitmap(self._bitmaps[\"folder\"], self._on_open_path)\n\n        self._path_combobox = ExtComboBox(self._panel, 5, style=wx.CB_READONLY)\n        self._videoformat_combobox = CustomComboBox(self._panel, style=wx.CB_READONLY)\n\n        self._download_text = self._create_statictext(self.DOWNLOAD_LIST_LABEL)\n        self._status_list = ListCtrl(self.STATUSLIST_COLUMNS,\n                                     parent=self._panel,\n                                     style=wx.LC_REPORT | wx.LC_HRULES | wx.LC_VRULES)\n\n        # Dictionary to store all the buttons\n        self._buttons = {}\n\n        for item in buttons_data:\n            name, label, size, evt_handler, parent = item\n\n            button = parent(self._panel, size=size)\n\n            if parent == wx.Button:\n                button.SetLabel(label)\n            elif parent == wx.BitmapButton:\n                button.SetToolTip(wx.ToolTip(label))\n\n            if name in self._bitmaps:\n                button.SetBitmap(self._bitmaps[name], wx.TOP)\n\n            if evt_handler is not None:\n                button.Bind(wx.EVT_BUTTON, evt_handler)\n\n            self._buttons[name] = button\n\n        self._status_bar = self.CreateStatusBar()\n\n        # Create extra components\n        self._settings_menu = self._create_menu_item(settings_menu_data)\n        self._statuslist_menu = self._create_menu_item(statuslist_menu_data)\n\n        # Overwrite the menu hover event to avoid changing the statusbar\n        self.Bind(wx.EVT_MENU_HIGHLIGHT, lambda event: None)\n\n        # Bind extra events\n        self.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self._on_statuslist_right_click, self._status_list)\n        self.Bind(wx.EVT_TEXT, self._update_savepath, self._path_combobox)\n        self.Bind(wx.EVT_LIST_ITEM_SELECTED, self._update_pause_button, self._status_list)\n        self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self._update_pause_button, self._status_list)\n        self.Bind(wx.EVT_CLOSE, self._on_close)\n        self.Bind(wx.EVT_TIMER, self._on_timer, self._app_timer)\n\n        self._videoformat_combobox.Bind(wx.EVT_COMBOBOX, self._update_videoformat)\n\n        # Set threads wxCallAfter handlers\n        self._set_publisher(self._update_handler, UPDATE_PUB_TOPIC)\n        self._set_publisher(self._download_worker_handler, WORKER_PUB_TOPIC)\n        self._set_publisher(self._download_manager_handler, MANAGER_PUB_TOPIC)\n\n        # Set up extra stuff\n        self.Center()\n        self.SetMinSize(self.FRAMES_MIN_SIZE)\n\n        self._status_bar_write(self.WELCOME_MSG)\n\n        self._update_videoformat_combobox()\n        self._path_combobox.LoadMultiple(self.opt_manager.options[\"save_path_dirs\"])\n        self._path_combobox.SetValue(self.opt_manager.options[\"save_path\"])\n\n        self._set_layout()\n\n        self._url_list.SetFocus()\n\n    def _create_menu_item(self, items):\n        menu = wx.Menu()\n\n        for item in items:\n            label, evt_handler = item\n            menu_item = menu.Append(-1, label)\n\n            menu.Bind(wx.EVT_MENU, evt_handler, menu_item)\n\n        return menu\n\n    def _on_statuslist_right_click(self, event):\n        selected = event.GetIndex()\n\n        if selected != -1:\n            self._status_list.deselect_all()\n            self._status_list.Select(selected, on=1)\n\n            self.PopupMenu(self._statuslist_menu)\n\n    def _on_reenter(self, event):\n        selected = self._status_list.get_selected()\n\n        if selected != -1:\n            object_id = self._status_list.GetItemData(selected)\n            download_item = self._download_list.get_item(object_id)\n\n            if download_item.stage != \"Active\":\n                self._status_list.remove_row(selected)\n                self._download_list.remove(object_id)\n\n                options = self._options_parser.parse(self.opt_manager.options)\n\n                download_item = DownloadItem(download_item.url, options)\n                download_item.path = self.opt_manager.options[\"save_path\"]\n\n                if not self._download_list.has_item(download_item.object_id):\n                    self._status_list.bind_item(download_item)\n                    self._download_list.insert(download_item)\n\n    def reset(self):\n        self._update_videoformat_combobox()\n        self._path_combobox.LoadMultiple(self.opt_manager.options[\"save_path_dirs\"])\n        self._path_combobox.SetValue(self.opt_manager.options[\"save_path\"])\n\n    def _on_open_dest(self, event):\n        selected = self._status_list.get_selected()\n\n        if selected != -1:\n            object_id = self._status_list.GetItemData(selected)\n            download_item = self._download_list.get_item(object_id)\n\n            if download_item.path:\n                open_file(download_item.path)\n\n    def _on_open_path(self, event):\n        open_file(self._path_combobox.GetValue())\n\n    def _on_geturl(self, event):\n        selected = self._status_list.get_selected()\n\n        if selected != -1:\n            object_id = self._status_list.GetItemData(selected)\n            download_item = self._download_list.get_item(object_id)\n\n            url = download_item.url\n\n            if not wx.TheClipboard.IsOpened():\n                clipdata = wx.TextDataObject()\n                clipdata.SetText(url)\n                wx.TheClipboard.Open()\n                wx.TheClipboard.SetData(clipdata)\n                wx.TheClipboard.Close()\n\n    def _on_getcmd(self, event):\n        selected = self._status_list.get_selected()\n\n        if selected != -1:\n            object_id = self._status_list.GetItemData(selected)\n            download_item = self._download_list.get_item(object_id)\n\n            cmd = build_command(download_item.options, download_item.url)\n\n            if not wx.TheClipboard.IsOpened():\n                clipdata = wx.TextDataObject()\n                clipdata.SetText(cmd)\n                wx.TheClipboard.Open()\n                wx.TheClipboard.SetData(clipdata)\n                wx.TheClipboard.Close()\n\n    def _on_timer(self, event):\n        total_percentage = 0.0\n        queued = paused = active = completed = error = 0\n\n        for item in self._download_list.get_items():\n            if item.stage == \"Queued\":\n                queued += 1\n            if item.stage == \"Paused\":\n                paused += 1\n            if item.stage == \"Active\":\n                active += 1\n                total_percentage += float(item.progress_stats[\"percent\"].split('%')[0])\n            if item.stage == \"Completed\":\n                completed += 1\n            if item.stage == \"Error\":\n                error += 1\n\n        # REFACTOR Store percentage as float in the DownloadItem?\n        # REFACTOR DownloadList keep track for each item stage?\n\n        items_count = active + completed + error + queued\n        total_percentage += completed * 100.0 + error * 100.0\n\n        if items_count:\n            total_percentage /= items_count\n\n        msg = self.URL_REPORT_MSG.format(total_percentage, queued, paused, active, completed, error)\n\n        if self.update_thread is None:\n            # Dont overwrite the update messages\n            self._status_bar_write(msg)\n\n    def _update_pause_button(self, event):\n        selected_rows = self._status_list.get_all_selected()\n\n        label = _(\"Pause\")\n        bitmap = self._bitmaps[\"pause\"]\n\n        for row in selected_rows:\n            object_id = self._status_list.GetItemData(row)\n            download_item = self._download_list.get_item(object_id)\n\n            if download_item.stage == \"Paused\":\n                # If we find one or more items in Paused\n                # state set the button functionality to resume\n                label = _(\"Resume\")\n                bitmap = self._bitmaps[\"resume\"]\n                break\n\n        self._buttons[\"pause\"].SetLabel(label)\n        self._buttons[\"pause\"].SetToolTip(wx.ToolTip(label))\n        self._buttons[\"pause\"].SetBitmap(bitmap, wx.TOP)\n\n    def _update_videoformat_combobox(self):\n        self._videoformat_combobox.Clear()\n\n        self._videoformat_combobox.add_items(list(DEFAULT_FORMATS.values()), False)\n\n        vformats = []\n        for vformat in self.opt_manager.options[\"selected_video_formats\"]:\n            vformats.append(FORMATS[vformat])\n\n        aformats = []\n        for aformat in self.opt_manager.options[\"selected_audio_formats\"]:\n            aformats.append(FORMATS[aformat])\n\n        if vformats:\n            self._videoformat_combobox.add_header(_(\"Video\"))\n            self._videoformat_combobox.add_items(vformats)\n\n        if aformats:\n            self._videoformat_combobox.add_header(_(\"Audio\"))\n            self._videoformat_combobox.add_items(aformats)\n\n        current_index = self._videoformat_combobox.FindString(FORMATS[self.opt_manager.options[\"selected_format\"]])\n\n        if current_index == wx.NOT_FOUND:\n            self._videoformat_combobox.SetSelection(0)\n        else:\n            self._videoformat_combobox.SetSelection(current_index)\n\n        self._update_videoformat(None)\n\n    def _update_videoformat(self, event):\n        self.opt_manager.options[\"selected_format\"] = selected_format = FORMATS[self._videoformat_combobox.GetValue()]\n\n        if selected_format in VIDEO_FORMATS:\n            self.opt_manager.options[\"video_format\"] = selected_format\n            self.opt_manager.options[\"audio_format\"] = \"\"  #NOTE Set to default value, check parsers.py\n        elif selected_format in AUDIO_FORMATS:\n            self.opt_manager.options[\"video_format\"] = DEFAULT_FORMATS[_(\"default\")]\n            self.opt_manager.options[\"audio_format\"] = selected_format\n        else:\n            self.opt_manager.options[\"video_format\"] = DEFAULT_FORMATS[_(\"default\")]\n            self.opt_manager.options[\"audio_format\"] = \"\"\n\n    def _update_savepath(self, event):\n        self.opt_manager.options[\"save_path\"] = self._path_combobox.GetValue()\n\n    def _on_delete(self, event):\n        index = self._status_list.get_next_selected()\n\n        if index == -1:\n            dlg = ButtonsChoiceDialog(self, [_(\"Remove all\"), _(\"Remove completed\")], _(\"No items selected. Please pick an action\"), _(\"Delete\"))\n            ret_code = dlg.ShowModal()\n            dlg.Destroy()\n\n            #REFACTOR Maybe add this functionality directly to DownloadList?\n            if ret_code == 1:\n                for ditem in self._download_list.get_items():\n                    if ditem.stage != \"Active\":\n                        self._status_list.remove_row(self._download_list.index(ditem.object_id))\n                        self._download_list.remove(ditem.object_id)\n\n            if ret_code == 2:\n                for ditem in self._download_list.get_items():\n                    if ditem.stage == \"Completed\":\n                        self._status_list.remove_row(self._download_list.index(ditem.object_id))\n                        self._download_list.remove(ditem.object_id)\n        else:\n            if self.opt_manager.options[\"confirm_deletion\"]:\n                dlg = wx.MessageDialog(self, _(\"Are you sure you want to remove selected items?\"), _(\"Delete\"), wx.YES_NO | wx.ICON_QUESTION)\n                result = dlg.ShowModal() == wx.ID_YES\n                dlg.Destroy()\n            else:\n                result = True\n\n            if result:\n                while index >= 0:\n                    object_id = self._status_list.GetItemData(index)\n                    selected_download_item = self._download_list.get_item(object_id)\n\n                    if selected_download_item.stage == \"Active\":\n                        self._create_popup(_(\"Item is active, cannot remove\"), self.WARNING_LABEL, wx.OK | wx.ICON_EXCLAMATION)\n                    else:\n                        #if selected_download_item.stage == \"Completed\":\n                            #dlg = wx.MessageDialog(self, \"Do you want to remove the files associated with this item?\", \"Remove files\", wx.YES_NO | wx.ICON_QUESTION)\n\n                            #result = dlg.ShowModal() == wx.ID_YES\n                            #dlg.Destroy()\n\n                            #if result:\n                                #for cur_file in selected_download_item.get_files():\n                                    #remove_file(cur_file)\n\n                        self._status_list.remove_row(index)\n                        self._download_list.remove(object_id)\n                        index -= 1\n\n                    index = self._status_list.get_next_selected(index)\n\n        self._update_pause_button(None)\n\n    def _on_play(self, event):\n        selected_rows = self._status_list.get_all_selected()\n\n        if selected_rows:\n            for selected_row in selected_rows:\n                object_id = self._status_list.GetItemData(selected_row)\n                selected_download_item = self._download_list.get_item(object_id)\n\n                if selected_download_item.stage == \"Completed\":\n                    if selected_download_item.filenames:\n                        filename = selected_download_item.get_files()[-1]\n                        open_file(filename)\n                else:\n                    self._create_popup(_(\"Item is not completed\"), self.INFO_LABEL, wx.OK | wx.ICON_INFORMATION)\n\n    def _on_arrow_up(self, event):\n        index = self._status_list.get_next_selected()\n\n        if index != -1:\n            while index >= 0:\n                object_id = self._status_list.GetItemData(index)\n                download_item = self._download_list.get_item(object_id)\n\n                new_index = index - 1\n                if new_index < 0:\n                    new_index = 0\n\n                if not self._status_list.IsSelected(new_index):\n                    self._download_list.move_up(object_id)\n                    self._status_list.move_item_up(index)\n                    self._status_list._update_from_item(new_index, download_item)\n\n                index = self._status_list.get_next_selected(index)\n\n    def _on_arrow_down(self, event):\n        index = self._status_list.get_next_selected(reverse=True)\n\n        if index != -1:\n            while index >= 0:\n                object_id = self._status_list.GetItemData(index)\n                download_item = self._download_list.get_item(object_id)\n\n                new_index = index + 1\n                if new_index >= self._status_list.GetItemCount():\n                    new_index = self._status_list.GetItemCount() - 1\n\n                if not self._status_list.IsSelected(new_index):\n                    self._download_list.move_down(object_id)\n                    self._status_list.move_item_down(index)\n                    self._status_list._update_from_item(new_index, download_item)\n\n                index = self._status_list.get_next_selected(index, True)\n\n    def _on_reload(self, event):\n        selected_rows = self._status_list.get_all_selected()\n\n        if not selected_rows:\n            for index, item in enumerate(self._download_list.get_items()):\n                if item.stage in (\"Paused\", \"Completed\", \"Error\"):\n                    # Store the old savepath because reset is going to remove it\n                    savepath = item.path\n                    item.reset()\n                    item.path = savepath\n                    self._status_list._update_from_item(index, item)\n        else:\n            for selected_row in selected_rows:\n                object_id = self._status_list.GetItemData(selected_row)\n                item = self._download_list.get_item(object_id)\n\n                if item.stage in (\"Paused\", \"Completed\", \"Error\"):\n                    # Store the old savepath because reset is going to remove it\n                    savepath = item.path\n                    item.reset()\n                    item.path = savepath\n                    self._status_list._update_from_item(selected_row, item)\n\n            self._update_pause_button(None)\n\n    def _on_pause(self, event):\n        selected_rows = self._status_list.get_all_selected()\n\n        if selected_rows:\n            #REFACTOR Use DoubleStageButton for this and check stage\n            if self._buttons[\"pause\"].GetLabel() == _(\"Pause\"):\n                new_state = \"Paused\"\n            else:\n                new_state = \"Queued\"\n\n            for selected_row in selected_rows:\n                object_id = self._status_list.GetItemData(selected_row)\n                download_item = self._download_list.get_item(object_id)\n\n                if download_item.stage == \"Queued\" or download_item.stage == \"Paused\":\n                    self._download_list.change_stage(object_id, new_state)\n\n                self._status_list._update_from_item(selected_row, download_item)\n\n            self._update_pause_button(None)\n\n    def _on_start(self, event):\n        if self.download_manager is None:\n            if self.update_thread is not None and self.update_thread.is_alive():\n                self._create_popup(_(\"Update in progress. Please wait for the update to complete\"),\n                                   self.WARNING_LABEL,\n                                   wx.OK | wx.ICON_EXCLAMATION)\n            else:\n                self._start_download()\n        else:\n            self.download_manager.stop_downloads()\n\n    def _on_savepath(self, event):\n        dlg = wx.DirDialog(self, self.CHOOSE_DIRECTORY, self._path_combobox.GetStringSelection())\n\n        if dlg.ShowModal() == wx.ID_OK:\n            path = dlg.GetPath()\n\n            self._path_combobox.Append(path)\n            self._path_combobox.SetValue(path)\n            self._update_savepath(None)\n\n        dlg.Destroy()\n\n    def _on_add(self, event):\n        urls = self._get_urls()\n\n        if not urls:\n            self._create_popup(self.PROVIDE_URL_MSG,\n                               self.WARNING_LABEL,\n                               wx.OK | wx.ICON_EXCLAMATION)\n        else:\n            self._url_list.Clear()\n            options = self._options_parser.parse(self.opt_manager.options)\n\n            for url in urls:\n                download_item = DownloadItem(url, options)\n                download_item.path = self.opt_manager.options[\"save_path\"]\n\n                if not self._download_list.has_item(download_item.object_id):\n                    self._status_list.bind_item(download_item)\n                    self._download_list.insert(download_item)\n\n\n    def _on_settings(self, event):\n        event_object_pos = event.EventObject.GetPosition()\n        event_object_height = event.EventObject.GetSize()[1]\n        event_object_pos = (event_object_pos[0], event_object_pos[1] + event_object_height)\n        self.PopupMenu(self._settings_menu, event_object_pos)\n\n    def _on_viewlog(self, event):\n        if self.log_manager is None:\n            self._create_popup(_(\"Logging is disabled\"),\n                               self.WARNING_LABEL,\n                               wx.OK | wx.ICON_EXCLAMATION)\n        else:\n            log_window = LogGUI(self)\n            log_window.load(self.log_manager.log_file)\n            log_window.Show()\n\n    def _on_about(self, event):\n        info = wx.AboutDialogInfo()\n\n        if self.app_icon is not None:\n            info.SetIcon(self.app_icon)\n\n        info.SetName(__appname__)\n        info.SetVersion(__version__)\n        info.SetDescription(__descriptionfull__)\n        info.SetWebSite(__projecturl__)\n        info.SetLicense(__licensefull__)\n        info.AddDeveloper(__author__)\n\n        wx.AboutBox(info)\n\n    def _set_publisher(self, handler, topic):\n        \"\"\"Sets a handler for the given topic.\n\n        Args:\n            handler (function): Can be any function with one parameter\n                the message that the caller sends.\n\n            topic (string): Can be any string that identifies the caller.\n                You can bind multiple handlers on the same topic or\n                multiple topics on the same handler.\n\n        \"\"\"\n        Publisher.subscribe(handler, topic)\n\n    def _create_statictext(self, label):\n        return wx.StaticText(self._panel, label=label)\n\n    def _create_bitmap_button(self, icon, size=(-1, -1), handler=None):\n        button = wx.BitmapButton(self._panel, bitmap=icon, size=size, style=wx.NO_BORDER)\n\n        if handler is not None:\n            button.Bind(wx.EVT_BUTTON, handler)\n\n        return button\n\n    def _create_static_bitmap(self, icon, event_handler=None):\n        static_bitmap = wx.StaticBitmap(self._panel, bitmap=icon)\n\n        if event_handler is not None:\n            static_bitmap.Bind(wx.EVT_LEFT_DCLICK, event_handler)\n\n        return static_bitmap\n\n    def _create_textctrl(self, style=None, event_handler=None):\n        if style is None:\n            textctrl = wx.TextCtrl(self._panel)\n        else:\n            textctrl = wx.TextCtrl(self._panel, style=style)\n\n        if event_handler is not None:\n            textctrl.Bind(wx.EVT_TEXT_PASTE, event_handler)\n            textctrl.Bind(wx.EVT_MIDDLE_DOWN, event_handler)\n\n        if os.name == 'nt':\n            # Enable CTRL+A on Windows\n            def win_ctrla_eventhandler(event):\n                if event.GetKeyCode() == wx.WXK_CONTROL_A:\n                    event.GetEventObject().SelectAll()\n\n                event.Skip()\n\n            textctrl.Bind(wx.EVT_CHAR, win_ctrla_eventhandler)\n\n        return textctrl\n\n    def _create_popup(self, text, title, style):\n        wx.MessageBox(text, title, style)\n\n    def _set_layout(self):\n        \"\"\"Sets the layout of the main window. \"\"\"\n        main_sizer = wx.BoxSizer()\n        panel_sizer = wx.BoxSizer(wx.VERTICAL)\n\n        top_sizer = wx.BoxSizer(wx.HORIZONTAL)\n        top_sizer.Add(self._url_text, 0, wx.ALIGN_BOTTOM | wx.BOTTOM, 5)\n        top_sizer.AddSpacer((-1, -1), 1)\n        top_sizer.Add(self._settings_button)\n        panel_sizer.Add(top_sizer, 0, wx.EXPAND)\n\n        panel_sizer.Add(self._url_list, 1, wx.EXPAND)\n\n        mid_sizer = wx.BoxSizer(wx.HORIZONTAL)\n        mid_sizer.Add(self._folder_icon)\n        mid_sizer.AddSpacer((3, -1))\n        mid_sizer.Add(self._path_combobox, 2, wx.ALIGN_CENTER_VERTICAL)\n        mid_sizer.AddSpacer((5, -1))\n        mid_sizer.Add(self._buttons[\"savepath\"], flag=wx.ALIGN_CENTER_VERTICAL)\n        mid_sizer.AddSpacer((10, -1), 1)\n        mid_sizer.Add(self._videoformat_combobox, 1, wx.ALIGN_CENTER_VERTICAL)\n        mid_sizer.AddSpacer((5, -1))\n        mid_sizer.Add(self._buttons[\"add\"], flag=wx.ALIGN_CENTER_VERTICAL)\n        panel_sizer.Add(mid_sizer, 0, wx.EXPAND | wx.ALL, 10)\n\n        panel_sizer.Add(self._download_text, 0, wx.BOTTOM, 5)\n        panel_sizer.Add(self._status_list, 2, wx.EXPAND)\n\n        bottom_sizer = wx.BoxSizer(wx.HORIZONTAL)\n        bottom_sizer.Add(self._buttons[\"delete\"])\n        bottom_sizer.AddSpacer((5, -1))\n        bottom_sizer.Add(self._buttons[\"play\"])\n        bottom_sizer.AddSpacer((5, -1))\n        bottom_sizer.Add(self._buttons[\"up\"])\n        bottom_sizer.AddSpacer((5, -1))\n        bottom_sizer.Add(self._buttons[\"down\"])\n        bottom_sizer.AddSpacer((5, -1))\n        bottom_sizer.Add(self._buttons[\"reload\"])\n        bottom_sizer.AddSpacer((5, -1))\n        bottom_sizer.Add(self._buttons[\"pause\"])\n        bottom_sizer.AddSpacer((10, -1), 1)\n        bottom_sizer.Add(self._buttons[\"start\"])\n        panel_sizer.Add(bottom_sizer, 0, wx.EXPAND | wx.TOP, 5)\n\n        main_sizer.Add(panel_sizer, 1, wx.ALL | wx.EXPAND, 10)\n        self._panel.SetSizer(main_sizer)\n\n        self._panel.Layout()\n\n    def _update_youtubedl(self):\n        \"\"\"Update youtube-dl binary to the latest version. \"\"\"\n        if self.download_manager is not None and self.download_manager.is_alive():\n            self._create_popup(self.DOWNLOAD_ACTIVE,\n                               self.WARNING_LABEL,\n                               wx.OK | wx.ICON_EXCLAMATION)\n        elif self.update_thread is not None and self.update_thread.is_alive():\n            self._create_popup(self.UPDATE_ACTIVE,\n                               self.INFO_LABEL,\n                               wx.OK | wx.ICON_INFORMATION)\n        else:\n            self.update_thread = UpdateThread(self.opt_manager.options['youtubedl_path'])\n\n    def _status_bar_write(self, msg):\n        \"\"\"Display msg in the status bar. \"\"\"\n        self._status_bar.SetStatusText(msg)\n\n    def _reset_widgets(self):\n        \"\"\"Resets GUI widgets after update or download process. \"\"\"\n        self._buttons[\"start\"].SetLabel(_(\"Start\"))\n        self._buttons[\"start\"].SetToolTip(wx.ToolTip(_(\"Start\")))\n        self._buttons[\"start\"].SetBitmap(self._bitmaps[\"start\"], wx.TOP)\n\n    def _print_stats(self):\n        \"\"\"Display download stats in the status bar. \"\"\"\n        suc_downloads = self.download_manager.successful\n        dtime = get_time(self.download_manager.time_it_took)\n\n        msg = self.SUCC_REPORT_MSG.format(suc_downloads,\n                                          dtime['days'],\n                                          dtime['hours'],\n                                          dtime['minutes'],\n                                          dtime['seconds'])\n\n        self._status_bar_write(msg)\n\n    def _after_download(self):\n        \"\"\"Run tasks after download process has been completed.\n\n        Note:\n            Here you can add any tasks you want to run after the\n            download process has been completed.\n\n        \"\"\"\n        if self.opt_manager.options['shutdown']:\n            dlg = ShutdownDialog(self, 60, _(\"Shutting down in {0} second(s)\"), _(\"Shutdown\"))\n            result = dlg.ShowModal() == wx.ID_OK\n            dlg.Destroy()\n\n            if result:\n                self.opt_manager.save_to_file()\n                success = shutdown_sys(self.opt_manager.options['sudo_password'])\n\n                if success:\n                    self._status_bar_write(self.SHUTDOWN_MSG)\n                else:\n                    self._status_bar_write(self.SHUTDOWN_ERR)\n        else:\n            if self.opt_manager.options[\"show_completion_popup\"]:\n                self._create_popup(self.DL_COMPLETED_MSG, self.INFO_LABEL, wx.OK | wx.ICON_INFORMATION)\n\n    def _download_worker_handler(self, msg):\n        \"\"\"downloadmanager.Worker thread handler.\n\n        Handles messages from the Worker thread.\n\n        Args:\n            See downloadmanager.Worker _talk_to_gui() method.\n\n        \"\"\"\n        signal, data = msg.data\n\n        download_item = self._download_list.get_item(data[\"index\"])\n        download_item.update_stats(data)\n        row = self._download_list.index(data[\"index\"])\n\n        self._status_list._update_from_item(row, download_item)\n\n    def _download_manager_handler(self, msg):\n        \"\"\"downloadmanager.DownloadManager thread handler.\n\n        Handles messages from the DownloadManager thread.\n\n        Args:\n            See downloadmanager.DownloadManager _talk_to_gui() method.\n\n        \"\"\"\n        data = msg.data\n\n        if data == 'finished':\n            self._print_stats()\n            self._reset_widgets()\n            self.download_manager = None\n            self._app_timer.Stop()\n            self._after_download()\n        elif data == 'closed':\n            self._status_bar_write(self.CLOSED_MSG)\n            self._reset_widgets()\n            self.download_manager = None\n            self._app_timer.Stop()\n        elif data == 'closing':\n            self._status_bar_write(self.CLOSING_MSG)\n        elif data == 'report_active':\n            pass\n            #NOTE Remove from here and downloadmanager\n            #since now we have the wx.Timer to check progress\n\n    def _update_handler(self, msg):\n        \"\"\"updatemanager.UpdateThread thread handler.\n\n        Handles messages from the UpdateThread thread.\n\n        Args:\n            See updatemanager.UpdateThread _talk_to_gui() method.\n\n        \"\"\"\n        data = msg.data\n\n        if data[0] == 'download':\n            self._status_bar_write(self.UPDATING_MSG)\n        elif data[0] == 'error':\n            self._status_bar_write(self.UPDATE_ERR_MSG.format(data[1]))\n        elif data[0] == 'correct':\n            self._status_bar_write(self.UPDATE_SUCC_MSG)\n        else:\n            self._reset_widgets()\n            self.update_thread = None\n\n    def _get_urls(self):\n        \"\"\"Returns urls list. \"\"\"\n        return [line for line in self._url_list.GetValue().split('\\n') if line]\n\n    def _start_download(self):\n        if self._status_list.is_empty():\n            self._create_popup(_(\"No items to download\"),\n                               self.WARNING_LABEL,\n                               wx.OK | wx.ICON_EXCLAMATION)\n        else:\n            self._app_timer.Start(100)\n            self.download_manager = DownloadManager(self, self._download_list, self.opt_manager, self.log_manager)\n\n            self._status_bar_write(self.DOWNLOAD_STARTED)\n            self._buttons[\"start\"].SetLabel(self.STOP_LABEL)\n            self._buttons[\"start\"].SetToolTip(wx.ToolTip(self.STOP_LABEL))\n            self._buttons[\"start\"].SetBitmap(self._bitmaps[\"stop\"], wx.TOP)\n\n    def _paste_from_clipboard(self):\n        \"\"\"Paste the content of the clipboard to the self._url_list widget.\n        It also adds a new line at the end of the data if not exist.\n\n        \"\"\"\n        if not wx.TheClipboard.IsOpened():\n\n            if wx.TheClipboard.Open():\n                if wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_TEXT)):\n\n                    data = wx.TextDataObject()\n                    wx.TheClipboard.GetData(data)\n\n                    data = data.GetText()\n\n                    if data[-1] != '\\n':\n                        data += '\\n'\n\n                    self._url_list.WriteText(data)\n\n                wx.TheClipboard.Close()\n\n    def _on_urllist_edit(self, event):\n        \"\"\"Event handler of the self._url_list widget.\n\n        This method is triggered when the users pastes text into\n        the URLs list either by using CTRL+V or by using the middle\n        click of the mouse.\n\n        \"\"\"\n        if event.GetEventType() == wx.EVT_TEXT_PASTE.typeId:\n            self._paste_from_clipboard()\n        else:\n            wx.TheClipboard.UsePrimarySelection(True)\n            self._paste_from_clipboard()\n            wx.TheClipboard.UsePrimarySelection(False)\n\n    def _on_update(self, event):\n        \"\"\"Event handler of the self._update_btn widget.\n\n        This method is used when the update button is pressed to start\n        the update process.\n\n        Note:\n            Currently there is not way to stop the update process.\n\n        \"\"\"\n        if self.opt_manager.options[\"disable_update\"]:\n            self._create_popup(_(\"Updates are disabled for your system. Please use the system's package manager to update youtube-dl.\"),\n                               self.INFO_LABEL,\n                               wx.OK | wx.ICON_INFORMATION)\n        else:\n            self._update_youtubedl()\n\n    def _on_options(self, event):\n        \"\"\"Event handler of the self._options_btn widget.\n\n        This method is used when the options button is pressed to show\n        the options window.\n\n        \"\"\"\n        self._options_frame.load_all_options()\n        self._options_frame.Show()\n\n    def _on_close(self, event):\n        \"\"\"Event handler for the wx.EVT_CLOSE event.\n\n        This method is used when the user tries to close the program\n        to save the options and make sure that the download & update\n        processes are not running.\n\n        \"\"\"\n        if self.opt_manager.options[\"confirm_exit\"]:\n            dlg = wx.MessageDialog(self, _(\"Are you sure you want to exit?\"), _(\"Exit\"), wx.YES_NO | wx.ICON_QUESTION)\n\n            result = dlg.ShowModal() == wx.ID_YES\n            dlg.Destroy()\n        else:\n            result = True\n\n        if result:\n            self.close()\n\n    def close(self):\n        if self.download_manager is not None:\n            self.download_manager.stop_downloads()\n            self.download_manager.join()\n\n        if self.update_thread is not None:\n            self.update_thread.join()\n\n        # Store main-options frame size\n        self.opt_manager.options['main_win_size'] = self.GetSize()\n        self.opt_manager.options['opts_win_size'] = self._options_frame.GetSize()\n\n        self.opt_manager.options[\"save_path_dirs\"] = self._path_combobox.GetStrings()\n\n        self._options_frame.save_all_options()\n        self.opt_manager.save_to_file()\n\n        self.Destroy()\n\n\nclass ListCtrl(wx.ListCtrl, ListCtrlAutoWidthMixin):\n\n    \"\"\"Custom ListCtrl widget.\n\n    Args:\n        columns (dict): See MainFrame class STATUSLIST_COLUMNS attribute.\n\n    \"\"\"\n\n    def __init__(self, columns, *args, **kwargs):\n        super(ListCtrl, self).__init__(*args, **kwargs)\n        ListCtrlAutoWidthMixin.__init__(self)\n        self.columns = columns\n        self._list_index = 0\n        self._url_list = set()\n        self._set_columns()\n\n    def remove_row(self, row_number):\n        self.DeleteItem(row_number)\n        self._list_index -= 1\n\n    def move_item_up(self, row_number):\n        self._move_item(row_number, row_number - 1)\n\n    def move_item_down(self, row_number):\n        self._move_item(row_number, row_number + 1)\n\n    def _move_item(self, cur_row, new_row):\n        self.Freeze()\n        item = self.GetItem(cur_row)\n        self.DeleteItem(cur_row)\n\n        item.SetId(new_row)\n        self.InsertItem(item)\n\n        self.Select(new_row)\n        self.Thaw()\n\n    def has_url(self, url):\n        \"\"\"Returns True if the url is aleady in the ListCtrl else False.\n\n        Args:\n            url (string): URL string.\n\n        \"\"\"\n        return url in self._url_list\n\n    def bind_item(self, download_item):\n        self.InsertStringItem(self._list_index, download_item.url)\n\n        self.SetItemData(self._list_index, download_item.object_id)\n\n        self._update_from_item(self._list_index, download_item)\n\n        self._list_index += 1\n\n    def _update_from_item(self, row, download_item):\n        progress_stats = download_item.progress_stats\n\n        for key in self.columns:\n            column = self.columns[key][0]\n\n            if key == \"status\" and progress_stats[\"playlist_index\"]:\n                # Not the best place but we build the playlist status here\n                status = \"{0} {1}/{2}\".format(progress_stats[\"status\"],\n                                              progress_stats[\"playlist_index\"],\n                                              progress_stats[\"playlist_size\"])\n\n                self.SetStringItem(row, column, status)\n            else:\n                self.SetStringItem(row, column, progress_stats[key])\n\n    def clear(self):\n        \"\"\"Clear the ListCtrl widget & reset self._list_index and\n        self._url_list. \"\"\"\n        self.DeleteAllItems()\n        self._list_index = 0\n        self._url_list = set()\n\n    def is_empty(self):\n        \"\"\"Returns True if the list is empty else False. \"\"\"\n        return self._list_index == 0\n\n    def get_selected(self):\n        return self.GetNextItem(-1, wx.LIST_NEXT_ALL, wx.LIST_STATE_SELECTED)\n\n    def get_all_selected(self):\n        return [index for index in xrange(self._list_index) if self.IsSelected(index)]\n\n    def deselect_all(self):\n        for index in xrange(self._list_index):\n            self.Select(index, on=0)\n\n    def get_next_selected(self, start=-1, reverse=False):\n        if start == -1:\n            start = self._list_index - 1 if reverse else 0\n        else:\n            # start from next item\n            if reverse:\n                start -= 1\n            else:\n                start += 1\n\n        end = -1 if reverse else self._list_index\n        step = -1 if reverse else 1\n\n        for index in xrange(start, end, step):\n            if self.IsSelected(index):\n                return index\n\n        return -1\n\n    def _set_columns(self):\n        \"\"\"Initializes ListCtrl columns.\n        See MainFrame STATUSLIST_COLUMNS attribute for more info. \"\"\"\n        for column_item in sorted(self.columns.values()):\n            self.InsertColumn(column_item[0], column_item[1], width=wx.LIST_AUTOSIZE_USEHEADER)\n\n            # If the column width obtained from wxLIST_AUTOSIZE_USEHEADER\n            # is smaller than the minimum allowed column width\n            # then set the column width to the minumum allowed size\n            if self.GetColumnWidth(column_item[0]) < column_item[2]:\n                self.SetColumnWidth(column_item[0], column_item[2])\n\n            # Set auto-resize if enabled\n            if column_item[3]:\n                self.setResizeColumn(column_item[0])\n\n# REFACTOR Extra widgets below should move to other module with widgets\n\nclass ExtComboBox(wx.ComboBox):\n\n    def __init__(self, parent, max_items=-1, *args, **kwargs):\n        super(ExtComboBox, self).__init__(parent, *args, **kwargs)\n\n        assert max_items > 0 or max_items == -1\n        self.max_items = max_items\n\n    def Append(self, new_value):\n        if self.FindString(new_value) == wx.NOT_FOUND:\n            super(ExtComboBox, self).Append(new_value)\n\n            if self.max_items != -1 and self.GetCount() > self.max_items:\n                self.SetItems(self.GetStrings()[1:])\n\n    def SetValue(self, new_value):\n        if self.FindString(new_value) == wx.NOT_FOUND:\n            self.Append(new_value)\n\n        self.SetSelection(self.FindString(new_value))\n\n    def LoadMultiple(self, items_list):\n        for item in items_list:\n            self.Append(item)\n\n\nclass DoubleStageButton(wx.Button):\n\n    def __init__(self, parent, labels, bitmaps, bitmap_pos=wx.TOP, *args, **kwargs):\n        super(DoubleStageButton, self).__init__(parent, *args, **kwargs)\n\n        assert isinstance(labels, tuple) and isinstance(bitmaps, tuple)\n        assert len(labels) == 2\n        assert len(bitmaps) == 0 or len(bitmaps) == 2\n\n        self.labels = labels\n        self.bitmaps = bitmaps\n        self.bitmap_pos = bitmap_pos\n\n        self._stage = 0\n        self._set_layout()\n\n    def _set_layout(self):\n        self.SetLabel(self.labels[self._stage])\n\n        if len(self.bitmaps):\n            self.SetBitmap(self.bitmaps[self._stage], self.bitmap_pos)\n\n    def change_stage(self):\n        self._stage = 0 if self._stage else 1\n        self._set_layout()\n\n    def set_stage(self, new_stage):\n        assert new_stage == 0 or new_stage == 1\n\n        self._stage = new_stage\n        self._set_layout()\n\n\nclass ButtonsChoiceDialog(wx.Dialog):\n\n    if os.name == \"nt\":\n        STYLE = wx.DEFAULT_DIALOG_STYLE\n    else:\n        STYLE = wx.DEFAULT_DIALOG_STYLE | wx.MAXIMIZE_BOX\n\n    BORDER = 10\n\n    def __init__(self, parent, choices, message, *args, **kwargs):\n        super(ButtonsChoiceDialog, self).__init__(parent, wx.ID_ANY, *args, style=self.STYLE, **kwargs)\n\n        buttons = []\n\n        # Create components\n        panel = wx.Panel(self)\n\n        info_bmp = wx.ArtProvider.GetBitmap(wx.ART_INFORMATION, wx.ART_MESSAGE_BOX)\n\n        info_icon = wx.StaticBitmap(panel, wx.ID_ANY, info_bmp)\n        msg_text = wx.StaticText(panel, wx.ID_ANY, message)\n\n        buttons.append(wx.Button(panel, wx.ID_CANCEL, _(\"Cancel\")))\n\n        for index, label in enumerate(choices):\n            buttons.append(wx.Button(panel, index + 1, label))\n\n        # Get the maximum button width & height\n        max_width = max_height = -1\n\n        for button in buttons:\n            button_width, button_height = button.GetSize()\n\n            if button_width > max_width:\n                max_width = button_width\n\n            if button_height > max_height:\n                max_height = button_height\n\n        max_width += 10\n\n        # Set buttons width & bind events\n        for button in buttons:\n            if button != buttons[0]:\n                button.SetMinSize((max_width, max_height))\n            else:\n                # On Close button change only the height\n                button.SetMinSize((-1, max_height))\n\n            button.Bind(wx.EVT_BUTTON, self._on_close)\n\n        # Set sizers\n        vertical_sizer = wx.BoxSizer(wx.VERTICAL)\n\n        message_sizer = wx.BoxSizer(wx.HORIZONTAL)\n        message_sizer.Add(info_icon)\n        message_sizer.AddSpacer((10, 10))\n        message_sizer.Add(msg_text, flag=wx.EXPAND)\n\n        vertical_sizer.Add(message_sizer, 1, wx.ALL, border=self.BORDER)\n\n        buttons_sizer = wx.BoxSizer(wx.HORIZONTAL)\n        for button in buttons[1:]:\n            buttons_sizer.Add(button)\n            buttons_sizer.AddSpacer((5, -1))\n\n        buttons_sizer.AddSpacer((-1, -1), 1)\n        buttons_sizer.Add(buttons[0], flag=wx.ALIGN_RIGHT)\n        vertical_sizer.Add(buttons_sizer, flag=wx.EXPAND | wx.ALL, border=self.BORDER)\n\n        panel.SetSizer(vertical_sizer)\n\n        width, height = panel.GetBestSize()\n        self.SetSize((width, height * 1.3))\n\n        self.Center()\n\n    def _on_close(self, event):\n        self.EndModal(event.GetEventObject().GetId())\n\n\nclass ButtonsGroup(object):\n\n    WIDTH = 0\n    HEIGHT = 1\n\n    def __init__(self, buttons_list=None, squared=False):\n        if buttons_list is None:\n            self._buttons_list = []\n        else:\n            self._buttons_list = buttons_list\n\n        self._squared = squared\n\n    def set_size(self, size):\n        assert len(size) == 2\n\n        width, height = size\n\n        if width == -1:\n            for button in self._buttons_list:\n                cur_width = button.GetSize()[self.WIDTH]\n\n                if cur_width > width:\n                    width = cur_width\n\n        if height == -1:\n            for button in self._buttons_list:\n                cur_height = button.GetSize()[self.HEIGHT]\n\n                if cur_height > height:\n                    height = cur_height\n\n        if self._squared:\n            width = height = (width if width > height else height)\n\n        for button in self._buttons_list:\n            button.SetMinSize((width, height))\n\n    def create_sizer(self, orient=wx.HORIZONTAL, space=-1):\n        box_sizer = wx.BoxSizer(orient)\n\n        for button in self._buttons_list:\n            box_sizer.Add(button)\n\n            if space != -1:\n                box_sizer.AddSpacer((space, space))\n\n        return box_sizer\n\n    def bind_event(self, event, event_handler):\n        for button in self._buttons_list:\n            button.Bind(event, event_handler)\n\n    def disable_all(self):\n        for button in self._buttons_list:\n            button.Enable(False)\n\n    def enable_all(self):\n        for button in self._buttons_list:\n            button.Enable(True)\n\n    def add(self, button):\n        self._buttons_list.append(button)\n\n\nclass ShutdownDialog(wx.Dialog):\n\n    if os.name == \"nt\":\n        STYLE = wx.DEFAULT_DIALOG_STYLE\n    else:\n        STYLE = wx.DEFAULT_DIALOG_STYLE | wx.MAXIMIZE_BOX\n\n    TIMER_INTERVAL = 1000  # milliseconds\n\n    BORDER = 10\n\n    def __init__(self, parent, timeout, message, *args, **kwargs):\n        super(ShutdownDialog, self).__init__(parent, wx.ID_ANY, *args, style=self.STYLE, **kwargs)\n        assert timeout > 0\n\n        self.timeout = timeout\n        self.message = message\n\n        # Create components\n        panel = wx.Panel(self)\n\n        info_bmp = wx.ArtProvider.GetBitmap(wx.ART_INFORMATION, wx.ART_MESSAGE_BOX)\n        info_icon = wx.StaticBitmap(panel, wx.ID_ANY, info_bmp)\n\n        self.msg_text = msg_text = wx.StaticText(panel, wx.ID_ANY, self._get_message())\n        ok_button = wx.Button(panel, wx.ID_OK, _(\"OK\"))\n        cancel_button = wx.Button(panel, wx.ID_CANCEL, _(\"Cancel\"))\n\n        # Set layout\n        vertical_sizer = wx.BoxSizer(wx.VERTICAL)\n\n        message_sizer = wx.BoxSizer(wx.HORIZONTAL)\n        message_sizer.Add(info_icon)\n        message_sizer.AddSpacer((10, 10))\n        message_sizer.Add(msg_text, flag=wx.EXPAND)\n\n        vertical_sizer.Add(message_sizer, 1, wx.ALL, border=self.BORDER)\n\n        buttons_sizer = wx.BoxSizer(wx.HORIZONTAL)\n        buttons_sizer.Add(ok_button)\n        buttons_sizer.AddSpacer((5, -1))\n        buttons_sizer.Add(cancel_button)\n\n        vertical_sizer.Add(buttons_sizer, flag=wx.ALIGN_RIGHT | wx.ALL, border=self.BORDER)\n\n        panel.SetSizer(vertical_sizer)\n\n        width, height = panel.GetBestSize()\n        self.SetSize((width * 1.3, height * 1.3))\n\n        self.Center()\n\n        # Set up timer\n        self.timer = wx.Timer(self)\n        self.Bind(wx.EVT_TIMER, self._on_timer, self.timer)\n        self.timer.Start(self.TIMER_INTERVAL)\n\n    def _get_message(self):\n        return self.message.format(self.timeout)\n\n    def _on_timer(self, event):\n        self.timeout -= 1\n        self.msg_text.SetLabel(self._get_message())\n\n        if self.timeout <= 0:\n            self.EndModal(wx.ID_OK)\n\n    def Destroy(self):\n        self.timer.Stop()\n        return super(ShutdownDialog, self).Destroy()\n"
  },
  {
    "path": "youtube_dl_gui/optionsframe.py",
    "content": "#!/usr/bin/env python2\n# -*- coding: utf-8 -*-\n\n\"\"\"Youtubedlg module responsible for the options window. \"\"\"\n\nfrom __future__ import unicode_literals\n\nimport os\nimport gettext\n\nimport wx\nimport wx.combo\nfrom wx.lib.art import flagart\n\nfrom .utils import (\n    TwoWayOrderedDict as twodict,\n    os_path_exists,\n    get_icon_file,\n    os_sep\n)\n\nfrom .info import __appname__\n\nfrom .formats import (\n    OUTPUT_FORMATS,\n    VIDEO_FORMATS,\n    AUDIO_FORMATS\n)\n#REFACTOR Move all formats, etc to formats.py\n\n\nclass OptionsFrame(wx.Frame):\n\n    \"\"\"Youtubedlg options frame class.\n\n    Args:\n        parent (mainframe.MainFrame): Parent class.\n\n    \"\"\"\n\n    FRAME_TITLE = _(\"Options\")\n\n    FRAMES_MIN_SIZE = (500, 470)\n\n    def __init__(self, parent):\n        wx.Frame.__init__(self, parent, title=self.FRAME_TITLE, size=parent.opt_manager.options[\"opts_win_size\"])\n        self.opt_manager = parent.opt_manager\n        self.log_manager = parent.log_manager\n        self.app_icon = None\n\n        # Set the app icon\n        #REFACTOR Get icon from parent\n        app_icon_path = get_icon_file()\n        if app_icon_path is not None:\n            self.app_icon = wx.Icon(app_icon_path, wx.BITMAP_TYPE_PNG)\n            self.SetIcon(self.app_icon)\n\n        self._was_shown = False\n\n        # Create options frame basic components\n        self.panel = wx.Panel(self)\n\n        self.notebook = wx.Notebook(self.panel)\n        self.separator_line = wx.StaticLine(self.panel)\n        self.reset_button = wx.Button(self.panel, label=_(\"Reset\"))\n        self.close_button = wx.Button(self.panel, label=_(\"Close\"))\n\n        # Create tabs\n        tab_args = (self, self.notebook)\n\n        self.tabs = (\n            (GeneralTab(*tab_args), _(\"General\")),\n            (FormatsTab(*tab_args), _(\"Formats\")),\n            (DownloadsTab(*tab_args), _(\"Downloads\")),\n            (AdvancedTab(*tab_args), _(\"Advanced\")),\n            (ExtraTab(*tab_args), _(\"Extra\"))\n        )\n\n        # Add tabs on notebook\n        for tab, label in self.tabs:\n            self.notebook.AddPage(tab, label)\n\n        # Bind events\n        self.Bind(wx.EVT_BUTTON, self._on_reset, self.reset_button)\n        self.Bind(wx.EVT_BUTTON, self._on_close, self.close_button)\n        self.Bind(wx.EVT_CLOSE, self._on_close)\n\n        self.SetMinSize(self.FRAMES_MIN_SIZE)\n\n        self._set_layout()\n        self.load_all_options()\n\n    def _set_layout(self):\n        main_sizer = wx.BoxSizer(wx.VERTICAL)\n\n        main_sizer.Add(self.notebook, 1, wx.EXPAND | wx.ALL, border=5)\n        main_sizer.Add(self.separator_line, 0, wx.EXPAND)\n\n        buttons_sizer = wx.BoxSizer(wx.HORIZONTAL)\n        buttons_sizer.Add(self.reset_button)\n        buttons_sizer.AddSpacer((5, -1))\n        buttons_sizer.Add(self.close_button)\n\n        main_sizer.Add(buttons_sizer, flag=wx.ALIGN_RIGHT | wx.ALL, border=5)\n\n        self.panel.SetSizer(main_sizer)\n\n        self.panel.Layout()\n\n    def _on_close(self, event):\n        \"\"\"Event handler for wx.EVT_CLOSE event.\"\"\"\n        self.save_all_options()\n        #REFACTOR Parent create specific callback\n        self.GetParent()._update_videoformat_combobox()\n        self.Hide()\n\n    def _on_reset(self, event):\n        \"\"\"Event handler for the reset button wx.EVT_BUTTON event.\"\"\"\n        self.reset()\n        self.GetParent().reset()\n\n    def reset(self):\n        \"\"\"Reset the default options.\"\"\"\n        self.opt_manager.load_default()\n        self.load_all_options()\n\n    def load_all_options(self):\n        \"\"\"Load all the options on each tab.\"\"\"\n        for tab, _ in self.tabs:\n            tab.load_options()\n\n    def save_all_options(self):\n        \"\"\"Save all the options from all the tabs back to the OptionsManager.\"\"\"\n        for tab, _ in self.tabs:\n            tab.save_options()\n\n    def Show(self, *args, **kwargs):\n        # CenterOnParent can't go to main frame's __init__ as main frame may change\n        # own position and options frame won't be centered on main frame anymore.\n        if not self._was_shown:\n            self._was_shown = True\n            self.CenterOnParent()\n        return wx.Frame.Show(self, *args, **kwargs)\n\n\nclass TabPanel(wx.Panel):\n\n    \"\"\"Main tab class from which each tab inherits.\n\n    Args:\n        parent (OptionsFrame): The parent of all tabs.\n\n        notebook (wx.Notebook): The container for each tab.\n\n    Notes:\n        In order to use a different size you must overwrite the below *_SIZE\n        attributes on the corresponding child object.\n\n    \"\"\"\n\n    CHECKBOX_SIZE = (-1, -1)\n    if os.name == \"nt\":\n        # Make checkboxes look the same on Windows\n        CHECKBOX_SIZE = (-1, 25)\n\n    BUTTONS_SIZE = (-1, -1)\n    TEXTCTRL_SIZE = (-1, -1)\n    SPINCTRL_SIZE = (70, -1)\n\n    CHECKLISTBOX_SIZE = (-1, 80)\n    LISTBOX_SIZE = (-1, 80)\n\n    def __init__(self, parent, notebook):\n        super(TabPanel, self).__init__(notebook)\n        #REFACTOR Maybe add methods to access those\n        #save_options(key, value)\n        #load_options(key)\n        self.opt_manager = parent.opt_manager\n        self.log_manager = parent.log_manager\n        self.app_icon = parent.app_icon\n\n        self.reset_handler = parent.reset\n\n    # Shortcut methods below\n\n    def crt_button(self, label, event_handler=None):\n        button = wx.Button(self, label=label, size=self.BUTTONS_SIZE)\n\n        if event_handler is not None:\n            button.Bind(wx.EVT_BUTTON, event_handler)\n\n        return button\n\n    def crt_checkbox(self, label, event_handler=None):\n        checkbox = wx.CheckBox(self, label=label, size=self.CHECKBOX_SIZE)\n\n        if event_handler is not None:\n            checkbox.Bind(wx.EVT_CHECKBOX, event_handler)\n\n        return checkbox\n\n    def crt_textctrl(self, style=None):\n        if style is None:\n            textctrl = wx.TextCtrl(self, size=self.TEXTCTRL_SIZE)\n        else:\n            textctrl = wx.TextCtrl(self, size=self.TEXTCTRL_SIZE, style=style)\n\n        return textctrl\n\n    def crt_combobox(self, choices, size=(-1, -1), event_handler=None):\n        combobox = wx.ComboBox(self, choices=choices, size=size, style=wx.CB_READONLY)\n\n        if event_handler is not None:\n            combobox.Bind(wx.EVT_COMBOBOX, event_handler)\n\n        return combobox\n\n    def crt_bitmap_combobox(self, choices, size=(-1, -1), event_handler=None):\n        combobox = wx.combo.BitmapComboBox(self, size=size, style=wx.CB_READONLY)\n\n        for item in choices:\n            lang_code, lang_name = item\n\n            _, country = lang_code.split('_')\n\n            if country in flagart.catalog:\n                flag_bmp = flagart.catalog[country].getBitmap()\n            else:\n                flag_bmp = flagart.catalog[\"BLANK\"].getBitmap()\n\n            combobox.Append(lang_name, flag_bmp)\n\n        if event_handler is not None:\n            combobox.Bind(wx.EVT_COMBOBOX, event_handler)\n\n        return combobox\n\n    def crt_spinctrl(self, spin_range=(0, 9999)):\n        spinctrl = wx.SpinCtrl(self, size=self.SPINCTRL_SIZE)\n        spinctrl.SetRange(*spin_range)\n\n        return spinctrl\n\n    def crt_statictext(self, label):\n        return wx.StaticText(self, wx.ID_ANY, label)\n\n    def crt_staticbox(self, label):\n        return wx.StaticBox(self, wx.ID_ANY, label)\n\n    def crt_checklistbox(self, choices, style=None):\n        if style is None:\n            checklistbox = wx.CheckListBox(self, choices=choices, size=self.CHECKLISTBOX_SIZE)\n        else:\n            checklistbox = wx.CheckListBox(self, choices=choices, style=style, size=self.CHECKLISTBOX_SIZE)\n\n        return checklistbox\n\n    def crt_listbox(self, choices, style=None):\n        if style is None:\n            listbox = wx.ListBox(self, choices=choices, size=self.LISTBOX_SIZE)\n        else:\n            listbox = wx.ListBox(self, choices=choices, style=style, size=self.LISTBOX_SIZE)\n\n        return listbox\n\n\nclass GeneralTab(TabPanel):\n\n    # Lang code = <ISO 639-1>_<ISO 3166-1 alpha-2>\n    LOCALE_NAMES = twodict([\n        ('ar_SA', 'Arabic'),\n        ('cs_CZ', 'Czech'),\n        ('en_US', 'English'),\n        ('fr_FR', 'French'),\n        ('it_IT', 'Italian'),\n        ('ja_JP', 'Japanese'),\n        ('ko_KR', 'Korean'),\n        ('pt_BR', 'Portuguese'),\n        ('ru_RU', 'Russian'),\n        ('es_ES', 'Spanish')\n    ])\n\n    OUTPUT_TEMPLATES = [\n        \"Id\",\n        \"Title\",\n        \"Ext\",\n        \"Uploader\",\n        \"Resolution\",\n        \"Autonumber\",\n        \"\",\n        \"View Count\",\n        \"Like Count\",\n        \"Dislike Count\",\n        \"Comment Count\",\n        \"Average Rating\",\n        \"Age Limit\",\n        \"Width\",\n        \"Height\",\n        \"Extractor\",\n        \"\",\n        \"Playlist\",\n        \"Playlist Index\",\n    ]\n\n    BUTTONS_SIZE = (30, -1)\n\n    def __init__(self, *args, **kwargs):\n        super(GeneralTab, self).__init__(*args, **kwargs)\n\n        self.language_label = self.crt_statictext(_(\"Language\"))\n        self.language_combobox = self.crt_bitmap_combobox(list(self.LOCALE_NAMES.items()), event_handler=self._on_language)\n\n        self.filename_format_label = self.crt_statictext(_(\"Filename format\"))\n        self.filename_format_combobox = self.crt_combobox(list(OUTPUT_FORMATS.values()), event_handler=self._on_filename)\n        self.filename_custom_format = self.crt_textctrl()\n        self.filename_custom_format_button = self.crt_button(\"...\", self._on_format)\n\n        self.filename_opts_label = self.crt_statictext(_(\"Filename options\"))\n        self.filename_ascii_checkbox = self.crt_checkbox(_(\"Restrict filenames to ASCII\"))\n\n        self.more_opts_label = self.crt_statictext(_(\"More options\"))\n        self.confirm_exit_checkbox = self.crt_checkbox(_(\"Confirm on exit\"))\n        self.confirm_deletion_checkbox = self.crt_checkbox(_(\"Confirm item deletion\"))\n        self.show_completion_popup_checkbox = self.crt_checkbox(_(\"Inform me on download completion\"))\n\n        self.shutdown_checkbox = self.crt_checkbox(_(\"Shutdown on download completion\"), event_handler=self._on_shutdown)\n        self.sudo_textctrl = self.crt_textctrl(wx.TE_PASSWORD)\n\n        # Build the menu for the custom format button\n        self.custom_format_menu = self._build_custom_format_menu()\n\n        self._set_layout()\n\n        if os.name == \"nt\":\n            self.sudo_textctrl.Hide()\n\n        self.sudo_textctrl.SetToolTip(wx.ToolTip(_(\"SUDO password\")))\n\n    def _set_layout(self):\n        main_sizer = wx.BoxSizer(wx.HORIZONTAL)\n        vertical_sizer = wx.BoxSizer(wx.VERTICAL)\n\n        vertical_sizer.Add(self.language_label)\n        vertical_sizer.Add(self.language_combobox, flag=wx.EXPAND | wx.ALL, border=5)\n\n        vertical_sizer.Add(self.filename_format_label, flag=wx.TOP, border=5)\n        vertical_sizer.Add(self.filename_format_combobox, flag=wx.EXPAND | wx.ALL, border=5)\n\n        custom_format_sizer = wx.BoxSizer(wx.HORIZONTAL)\n        custom_format_sizer.Add(self.filename_custom_format, 1, wx.ALIGN_CENTER_VERTICAL)\n        custom_format_sizer.AddSpacer((5, -1))\n        custom_format_sizer.Add(self.filename_custom_format_button)\n\n        vertical_sizer.Add(custom_format_sizer, flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)\n\n        vertical_sizer.Add(self.filename_opts_label, flag=wx.TOP, border=5)\n        vertical_sizer.Add(self.filename_ascii_checkbox, flag=wx.ALL, border=5)\n\n        vertical_sizer.Add(self.more_opts_label, flag=wx.TOP, border=5)\n        vertical_sizer.Add(self.confirm_exit_checkbox, flag=wx.ALL, border=5)\n        vertical_sizer.Add(self.confirm_deletion_checkbox, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)\n        vertical_sizer.Add(self.show_completion_popup_checkbox, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)\n\n        shutdown_sizer = wx.BoxSizer(wx.HORIZONTAL)\n        shutdown_sizer.Add(self.shutdown_checkbox)\n        shutdown_sizer.AddSpacer((-1, -1), 1)\n        shutdown_sizer.Add(self.sudo_textctrl, 1)\n\n        vertical_sizer.Add(shutdown_sizer, flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)\n\n        main_sizer.Add(vertical_sizer, 1, wx.EXPAND | wx.ALL, border=5)\n        self.SetSizer(main_sizer)\n\n    def _build_custom_format_menu(self):\n        menu = wx.Menu()\n\n        for template in self.OUTPUT_TEMPLATES:\n            if template:\n                menu_item = menu.Append(wx.ID_ANY, template)\n                menu.Bind(wx.EVT_MENU, self._on_template, menu_item)\n            else:\n                menu.AppendSeparator()\n\n        return menu\n\n    def _on_template(self, event):\n        \"\"\"Event handler for the wx.EVT_MENU of the custom_format_menu menu items.\"\"\"\n        label = self.custom_format_menu.GetLabelText(event.GetId())\n        label = label.lower().replace(' ', '_')\n\n        custom_format = self.filename_custom_format.GetValue()\n\n        if label == \"ext\":\n            prefix = '.'\n        else:\n            prefix = '-'\n\n        if not custom_format or custom_format[-1] == os_sep:\n            # If the custom format is empty or ends with path separator\n            # remove the prefix\n            prefix = ''\n\n        template = \"{0}%({1})s\".format(prefix, label)\n        self.filename_custom_format.SetValue(custom_format + template)\n\n    def _on_format(self, event):\n        \"\"\"Event handler for the wx.EVT_BUTTON of the filename_custom_format_button.\"\"\"\n        event_object_pos = event.EventObject.GetPosition()\n        event_object_height = event.EventObject.GetSize()[1]\n        event_object_pos = (event_object_pos[0], event_object_pos[1] + event_object_height)\n        self.PopupMenu(self.custom_format_menu, event_object_pos)\n\n    def _on_language(self, event):\n        \"\"\"Event handler for the wx.EVT_COMBOBOX of the language_combobox.\"\"\"\n        wx.MessageBox(_(\"In order for the changes to take effect please restart {0}\").format(__appname__),\n                      _(\"Restart\"),\n                      wx.OK | wx.ICON_INFORMATION,\n                      self)\n\n    def _on_filename(self, event):\n        \"\"\"Event handler for the wx.EVT_COMBOBOX of the filename_format_combobox.\"\"\"\n        custom_selected = self.filename_format_combobox.GetValue() == OUTPUT_FORMATS[3]\n\n        self.filename_custom_format.Enable(custom_selected)\n        self.filename_custom_format_button.Enable(custom_selected)\n\n    def _on_shutdown(self, event):\n        \"\"\"Event handler for the wx.EVT_CHECKBOX of the shutdown_checkbox.\"\"\"\n        self.sudo_textctrl.Enable(self.shutdown_checkbox.GetValue())\n\n    def load_options(self):\n        self.language_combobox.SetValue(self.LOCALE_NAMES.get(self.opt_manager.options[\"locale_name\"], \"English\"))\n        self.filename_format_combobox.SetValue(OUTPUT_FORMATS[self.opt_manager.options[\"output_format\"]])\n        self.filename_custom_format.SetValue(self.opt_manager.options[\"output_template\"])\n        self.filename_ascii_checkbox.SetValue(self.opt_manager.options[\"restrict_filenames\"])\n        self.shutdown_checkbox.SetValue(self.opt_manager.options[\"shutdown\"])\n        self.sudo_textctrl.SetValue(self.opt_manager.options[\"sudo_password\"])\n        self.confirm_exit_checkbox.SetValue(self.opt_manager.options[\"confirm_exit\"])\n        self.show_completion_popup_checkbox.SetValue(self.opt_manager.options[\"show_completion_popup\"])\n        self.confirm_deletion_checkbox.SetValue(self.opt_manager.options[\"confirm_deletion\"])\n\n        #REFACTOR Automatically call on the new methods\n        #save_options\n        #load_options\n        #NOTE Maybe on init add callback?\n        self._on_filename(None)\n        self._on_shutdown(None)\n\n    def save_options(self):\n        self.opt_manager.options[\"locale_name\"] = self.LOCALE_NAMES[self.language_combobox.GetValue()]\n        self.opt_manager.options[\"output_format\"] = OUTPUT_FORMATS[self.filename_format_combobox.GetValue()]\n        self.opt_manager.options[\"output_template\"] = self.filename_custom_format.GetValue()\n        self.opt_manager.options[\"restrict_filenames\"] = self.filename_ascii_checkbox.GetValue()\n        self.opt_manager.options[\"shutdown\"] = self.shutdown_checkbox.GetValue()\n        self.opt_manager.options[\"sudo_password\"] = self.sudo_textctrl.GetValue()\n        self.opt_manager.options[\"confirm_exit\"] = self.confirm_exit_checkbox.GetValue()\n        self.opt_manager.options[\"show_completion_popup\"] = self.show_completion_popup_checkbox.GetValue()\n        self.opt_manager.options[\"confirm_deletion\"] = self.confirm_deletion_checkbox.GetValue()\n\n\nclass FormatsTab(TabPanel):\n\n    AUDIO_QUALITY = twodict([(\"0\", _(\"high\")), (\"5\", _(\"mid\")), (\"9\", _(\"low\"))])\n\n    def __init__(self, *args, **kwargs):\n        super(FormatsTab, self).__init__(*args, **kwargs)\n\n        self.video_formats_label = self.crt_statictext(_(\"Video formats\"))\n        self.video_formats_checklistbox = self.crt_checklistbox(list(VIDEO_FORMATS.values()))\n\n        self.audio_formats_label = self.crt_statictext(_(\"Audio formats\"))\n        self.audio_formats_checklistbox = self.crt_checklistbox(list(AUDIO_FORMATS.values()))\n\n        self.post_proc_opts_label = self.crt_statictext(_(\"Post-Process options\"))\n        self.keep_video_checkbox = self.crt_checkbox(_(\"Keep original files\"))\n        self.extract_audio_checkbox = self.crt_checkbox(_(\"Extract audio from video file\"))\n        self.embed_thumbnail_checkbox = self.crt_checkbox(_(\"Embed thumbnail in audio file\"))\n        self.add_metadata_checkbox = self.crt_checkbox(_(\"Add metadata to file\"))\n\n        self.audio_quality_label = self.crt_statictext(_(\"Audio quality\"))\n        self.audio_quality_combobox = self.crt_combobox(list(self.AUDIO_QUALITY.values()))\n\n        self._set_layout()\n\n    def _set_layout(self):\n        main_sizer = wx.BoxSizer(wx.HORIZONTAL)\n        vertical_sizer = wx.BoxSizer(wx.VERTICAL)\n\n        vertical_sizer.Add(self.video_formats_label)\n        vertical_sizer.Add(self.video_formats_checklistbox, 1, wx.EXPAND | wx.ALL, border=5)\n\n        vertical_sizer.Add(self.audio_formats_label, flag=wx.TOP, border=5)\n        vertical_sizer.Add(self.audio_formats_checklistbox, 1, wx.EXPAND | wx.ALL, border=5)\n\n        vertical_sizer.Add(self.post_proc_opts_label, flag=wx.TOP, border=5)\n        vertical_sizer.Add(self.keep_video_checkbox, flag=wx.ALL, border=5)\n        vertical_sizer.Add(self.extract_audio_checkbox, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)\n        vertical_sizer.Add(self.embed_thumbnail_checkbox, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)\n        vertical_sizer.Add(self.add_metadata_checkbox, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)\n\n        audio_quality_sizer = wx.BoxSizer(wx.HORIZONTAL)\n        audio_quality_sizer.Add(self.audio_quality_label, flag=wx.ALIGN_CENTER_VERTICAL)\n        audio_quality_sizer.AddSpacer((20, -1))\n        audio_quality_sizer.Add(self.audio_quality_combobox)\n\n        vertical_sizer.Add(audio_quality_sizer, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)\n\n        main_sizer.Add(vertical_sizer, 1, wx.EXPAND | wx.ALL, border=5)\n        self.SetSizer(main_sizer)\n\n    def load_options(self):\n        checked_video_formats = [VIDEO_FORMATS[vformat] for vformat in self.opt_manager.options[\"selected_video_formats\"]]\n        self.video_formats_checklistbox.SetCheckedStrings(checked_video_formats)\n        checked_audio_formats = [AUDIO_FORMATS[aformat] for aformat in self.opt_manager.options[\"selected_audio_formats\"]]\n        self.audio_formats_checklistbox.SetCheckedStrings(checked_audio_formats)\n        self.keep_video_checkbox.SetValue(self.opt_manager.options[\"keep_video\"])\n        self.audio_quality_combobox.SetValue(self.AUDIO_QUALITY[self.opt_manager.options[\"audio_quality\"]])\n        self.extract_audio_checkbox.SetValue(self.opt_manager.options[\"to_audio\"])\n        self.embed_thumbnail_checkbox.SetValue(self.opt_manager.options[\"embed_thumbnail\"])\n        self.add_metadata_checkbox.SetValue(self.opt_manager.options[\"add_metadata\"])\n\n    def save_options(self):\n        checked_video_formats = [VIDEO_FORMATS[vformat] for vformat in self.video_formats_checklistbox.GetCheckedStrings()]\n        self.opt_manager.options[\"selected_video_formats\"] = checked_video_formats\n        checked_audio_formats = [AUDIO_FORMATS[aformat] for aformat in self.audio_formats_checklistbox.GetCheckedStrings()]\n        self.opt_manager.options[\"selected_audio_formats\"] = checked_audio_formats\n        self.opt_manager.options[\"keep_video\"] = self.keep_video_checkbox.GetValue()\n        self.opt_manager.options[\"audio_quality\"] = self.AUDIO_QUALITY[self.audio_quality_combobox.GetValue()]\n        self.opt_manager.options[\"to_audio\"] = self.extract_audio_checkbox.GetValue()\n        self.opt_manager.options[\"embed_thumbnail\"] = self.embed_thumbnail_checkbox.GetValue()\n        self.opt_manager.options[\"add_metadata\"] = self.add_metadata_checkbox.GetValue()\n\n\nclass DownloadsTab(TabPanel):\n\n    # Lang code = ISO 639-1\n    SUBS_LANG = twodict([\n        (\"en\", _(\"English\")),\n        (\"fr\", _(\"French\")),\n        (\"de\", _(\"German\")),\n        (\"el\", _(\"Greek\")),\n        (\"he\", _(\"Hebrew\")),\n        (\"it\", _(\"Italian\")),\n        (\"pt\", _(\"Portuguese\")),\n        (\"ru\", _(\"Russian\")),\n        (\"es\", _(\"Spanish\")),\n        (\"sv\", _(\"Swedish\")),\n        (\"tr\", _(\"Turkish\"))\n    ])\n\n    FILESIZES = twodict([\n        (\"\", \"Bytes\"),\n        (\"k\", \"Kilobytes\"),\n        (\"m\", \"Megabytes\"),\n        (\"g\", \"Gigabytes\"),\n        (\"t\", \"Terabytes\"),\n        (\"p\", \"Petabytes\"),\n        (\"e\", \"Exabytes\"),\n        (\"z\", \"Zettabytes\"),\n        (\"y\", \"Yottabytes\")\n    ])\n\n    SUBS_CHOICES = [\n        _(\"None\"),\n        _(\"Automatic subtitles (YOUTUBE ONLY)\"),\n        _(\"All available subtitles\"),\n        _(\"Subtitles by language\")\n    ]\n\n    def __init__(self, *args, **kwargs):\n        super(DownloadsTab, self).__init__(*args, **kwargs)\n\n        self.subtitles_label = self.crt_statictext(_(\"Subtitles\"))\n        self.subtitles_combobox = self.crt_combobox(self.SUBS_CHOICES, event_handler=self._on_subtitles)\n        self.subtitles_lang_listbox = self.crt_listbox(list(self.SUBS_LANG.values()))\n\n        self.subtitles_opts_label = self.crt_statictext(_(\"Subtitles options\"))\n        self.embed_subs_checkbox = self.crt_checkbox(_(\"Embed subtitles into video file (mp4 ONLY)\"))\n\n        self.playlist_box = self.crt_staticbox(_(\"Playlist\"))\n\n        self.playlist_start_label = self.crt_statictext(_(\"Start\"))\n        self.playlist_start_spinctrl = self.crt_spinctrl((1, 9999))\n        self.playlist_stop_label = self.crt_statictext(_(\"Stop\"))\n        self.playlist_stop_spinctrl = self.crt_spinctrl()\n        self.playlist_max_label = self.crt_statictext(_(\"Max\"))\n        self.playlist_max_spinctrl = self.crt_spinctrl()\n\n        self.filesize_box = self.crt_staticbox(_(\"Filesize\"))\n\n        self.filesize_max_label = self.crt_statictext(_(\"Max\"))\n        self.filesize_max_spinctrl = self.crt_spinctrl((0, 1024))\n        self.filesize_max_sizeunit_combobox = self.crt_combobox(list(self.FILESIZES.values()))\n        self.filesize_min_label = self.crt_statictext(_(\"Min\"))\n        self.filesize_min_spinctrl = self.crt_spinctrl((0, 1024))\n        self.filesize_min_sizeunit_combobox = self.crt_combobox(list(self.FILESIZES.values()))\n\n        self._set_layout()\n\n    def _set_layout(self):\n        main_sizer = wx.BoxSizer(wx.HORIZONTAL)\n        vertical_sizer = wx.BoxSizer(wx.VERTICAL)\n\n        vertical_sizer.Add(self.subtitles_label)\n        vertical_sizer.Add(self.subtitles_combobox, flag=wx.EXPAND | wx.ALL, border=5)\n        vertical_sizer.Add(self.subtitles_lang_listbox, 1, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)\n\n        vertical_sizer.Add(self.subtitles_opts_label, flag=wx.TOP, border=5)\n        vertical_sizer.Add(self.embed_subs_checkbox, flag=wx.ALL, border=5)\n\n        plist_and_fsize_sizer = wx.BoxSizer(wx.HORIZONTAL)\n        plist_and_fsize_sizer.Add(self._build_playlist_sizer(), 1, wx.EXPAND)\n        plist_and_fsize_sizer.AddSpacer((5, -1))\n        plist_and_fsize_sizer.Add(self._build_filesize_sizer(), 1, wx.EXPAND)\n\n        vertical_sizer.Add(plist_and_fsize_sizer, 1, wx.EXPAND | wx.TOP, border=5)\n\n        main_sizer.Add(vertical_sizer, 1, wx.EXPAND | wx.ALL, border=5)\n        self.SetSizer(main_sizer)\n\n    def _build_playlist_sizer(self):\n        playlist_box_sizer = wx.StaticBoxSizer(self.playlist_box, wx.VERTICAL)\n        playlist_box_sizer.AddSpacer((-1, 10))\n\n        border = wx.GridBagSizer(5, 40)\n\n        border.Add(self.playlist_start_label, (0, 0), flag=wx.ALIGN_CENTER_VERTICAL)\n        border.Add(self.playlist_start_spinctrl, (0, 1))\n\n        border.Add(self.playlist_stop_label, (1, 0), flag=wx.ALIGN_CENTER_VERTICAL)\n        border.Add(self.playlist_stop_spinctrl, (1, 1))\n\n        border.Add(self.playlist_max_label, (2, 0), flag=wx.ALIGN_CENTER_VERTICAL)\n        border.Add(self.playlist_max_spinctrl, (2, 1))\n\n        playlist_box_sizer.Add(border, flag=wx.ALIGN_CENTER)\n\n        return playlist_box_sizer\n\n    def _build_filesize_sizer(self):\n        filesize_box_sizer = wx.StaticBoxSizer(self.filesize_box, wx.VERTICAL)\n\n        border = wx.GridBagSizer(5, 20)\n\n        border.Add(self.filesize_max_label, (0, 0), (1, 2), wx.ALIGN_CENTER_HORIZONTAL)\n\n        border.Add(self.filesize_max_spinctrl, (1, 0))\n        border.Add(self.filesize_max_sizeunit_combobox, (1, 1))\n\n        border.Add(self.filesize_min_label, (2, 0), (1, 2), wx.ALIGN_CENTER_HORIZONTAL)\n\n        border.Add(self.filesize_min_spinctrl, (3, 0))\n        border.Add(self.filesize_min_sizeunit_combobox, (3, 1))\n\n        filesize_box_sizer.Add(border, flag=wx.ALIGN_CENTER)\n\n        return filesize_box_sizer\n\n    def _on_subtitles(self, event):\n        \"\"\"Event handler for the wx.EVT_COMBOBOX of the subtitles_combobox.\"\"\"\n        self.subtitles_lang_listbox.Enable(self.subtitles_combobox.GetValue() == self.SUBS_CHOICES[-1])\n\n    def load_options(self):\n        #NOTE Find a better way to do this\n        if self.opt_manager.options[\"write_subs\"]:\n            self.subtitles_combobox.SetValue(self.SUBS_CHOICES[3])\n        elif self.opt_manager.options[\"write_all_subs\"]:\n            self.subtitles_combobox.SetValue(self.SUBS_CHOICES[2])\n        elif self.opt_manager.options[\"write_auto_subs\"]:\n            self.subtitles_combobox.SetValue(self.SUBS_CHOICES[1])\n        else:\n            self.subtitles_combobox.SetValue(self.SUBS_CHOICES[0])\n\n        self.subtitles_lang_listbox.SetStringSelection(self.SUBS_LANG[self.opt_manager.options[\"subs_lang\"]])\n        self.embed_subs_checkbox.SetValue(self.opt_manager.options[\"embed_subs\"])\n        self.playlist_start_spinctrl.SetValue(self.opt_manager.options[\"playlist_start\"])\n        self.playlist_stop_spinctrl.SetValue(self.opt_manager.options[\"playlist_end\"])\n        self.playlist_max_spinctrl.SetValue(self.opt_manager.options[\"max_downloads\"])\n        self.filesize_min_spinctrl.SetValue(self.opt_manager.options[\"min_filesize\"])\n        self.filesize_max_spinctrl.SetValue(self.opt_manager.options[\"max_filesize\"])\n        self.filesize_min_sizeunit_combobox.SetValue(self.FILESIZES[self.opt_manager.options[\"min_filesize_unit\"]])\n        self.filesize_max_sizeunit_combobox.SetValue(self.FILESIZES[self.opt_manager.options[\"max_filesize_unit\"]])\n\n        self._on_subtitles(None)\n\n    def save_options(self):\n        subs_choice = self.SUBS_CHOICES.index(self.subtitles_combobox.GetValue())\n        if subs_choice == 1:\n            self.opt_manager.options[\"write_subs\"] = False\n            self.opt_manager.options[\"write_all_subs\"] = False\n            self.opt_manager.options[\"write_auto_subs\"] = True\n        elif subs_choice == 2:\n            self.opt_manager.options[\"write_subs\"] = False\n            self.opt_manager.options[\"write_all_subs\"] = True\n            self.opt_manager.options[\"write_auto_subs\"] = False\n        elif subs_choice == 3:\n            self.opt_manager.options[\"write_subs\"] = True\n            self.opt_manager.options[\"write_all_subs\"] = False\n            self.opt_manager.options[\"write_auto_subs\"] = False\n        else:\n            self.opt_manager.options[\"write_subs\"] = False\n            self.opt_manager.options[\"write_all_subs\"] = False\n            self.opt_manager.options[\"write_auto_subs\"] = False\n\n        self.opt_manager.options[\"subs_lang\"] = self.SUBS_LANG[self.subtitles_lang_listbox.GetStringSelection()]\n        self.opt_manager.options[\"embed_subs\"] = self.embed_subs_checkbox.GetValue()\n        self.opt_manager.options[\"playlist_start\"] = self.playlist_start_spinctrl.GetValue()\n        self.opt_manager.options[\"playlist_end\"] = self.playlist_stop_spinctrl.GetValue()\n        self.opt_manager.options[\"max_downloads\"] = self.playlist_max_spinctrl.GetValue()\n        self.opt_manager.options[\"min_filesize\"] = self.filesize_min_spinctrl.GetValue()\n        self.opt_manager.options[\"max_filesize\"] = self.filesize_max_spinctrl.GetValue()\n        self.opt_manager.options[\"min_filesize_unit\"] = self.FILESIZES[self.filesize_min_sizeunit_combobox.GetValue()]\n        self.opt_manager.options[\"max_filesize_unit\"] = self.FILESIZES[self.filesize_max_sizeunit_combobox.GetValue()]\n\n\nclass AdvancedTab(TabPanel):\n\n    TEXTCTRL_SIZE = (300, -1)\n\n    def __init__(self, *args, **kwargs):\n        super(AdvancedTab, self).__init__(*args, **kwargs)\n\n        self.retries_label = self.crt_statictext(_(\"Retries\"))\n        self.retries_spinctrl = self.crt_spinctrl((1, 999))\n\n        self.auth_label = self.crt_statictext(_(\"Authentication\"))\n\n        self.username_label = self.crt_statictext(_(\"Username\"))\n        self.username_textctrl = self.crt_textctrl()\n        self.password_label = self.crt_statictext(_(\"Password\"))\n        self.password_textctrl = self.crt_textctrl(wx.TE_PASSWORD)\n        self.video_pass_label = self.crt_statictext(_(\"Video password\"))\n        self.video_pass_textctrl = self.crt_textctrl(wx.TE_PASSWORD)\n\n        self.network_label = self.crt_statictext(_(\"Network\"))\n\n        self.proxy_label = self.crt_statictext(_(\"Proxy\"))\n        self.proxy_textctrl = self.crt_textctrl()\n        self.useragent_label = self.crt_statictext(_(\"User agent\"))\n        self.useragent_textctrl = self.crt_textctrl()\n        self.referer_label = self.crt_statictext(_(\"Referer\"))\n        self.referer_textctrl = self.crt_textctrl()\n\n        self.logging_label = self.crt_statictext(_(\"Logging\"))\n\n        self.enable_log_checkbox = self.crt_checkbox(_(\"Enable log\"), self._on_enable_log)\n        self.view_log_button = self.crt_button(_(\"View\"), self._on_view)\n        self.clear_log_button = self.crt_button(_(\"Clear\"), self._on_clear)\n\n        self._set_layout()\n\n        if self.log_manager is None:\n            self.view_log_button.Disable()\n            self.clear_log_button.Disable()\n\n    def _set_layout(self):\n        main_sizer = wx.BoxSizer(wx.HORIZONTAL)\n        vertical_sizer = wx.BoxSizer(wx.VERTICAL)\n\n        # Set up retries box\n        retries_sizer = wx.BoxSizer(wx.HORIZONTAL)\n        retries_sizer.Add(self.retries_label, flag=wx.ALIGN_CENTER_VERTICAL)\n        retries_sizer.AddSpacer((20, -1))\n        retries_sizer.Add(self.retries_spinctrl)\n        vertical_sizer.Add(retries_sizer, flag=wx.ALIGN_RIGHT | wx.TOP | wx.RIGHT, border=5)\n\n        # Set up authentication box\n        vertical_sizer.Add(self.auth_label, flag=wx.TOP, border=10)\n        auth_sizer = wx.GridBagSizer(5, -1)\n\n        auth_sizer.Add(self.username_label, (0, 0), flag=wx.ALIGN_CENTER_VERTICAL)\n        auth_sizer.Add(self.username_textctrl, (0, 2))\n\n        auth_sizer.Add(self.password_label, (1, 0), flag=wx.ALIGN_CENTER_VERTICAL)\n        auth_sizer.Add(self.password_textctrl, (1, 2))\n\n        auth_sizer.Add(self.video_pass_label, (2, 0), flag=wx.ALIGN_CENTER_VERTICAL)\n        auth_sizer.Add(self.video_pass_textctrl, (2, 2))\n\n        auth_sizer.AddGrowableCol(1)\n        vertical_sizer.Add(auth_sizer, flag=wx.EXPAND | wx.ALL, border=5)\n\n        # Set up network box\n        vertical_sizer.Add(self.network_label, flag=wx.TOP, border=10)\n        network_sizer = wx.GridBagSizer(5, -1)\n\n        network_sizer.Add(self.proxy_label, (0, 0), flag=wx.ALIGN_CENTER_VERTICAL)\n        network_sizer.Add(self.proxy_textctrl, (0, 2))\n\n        network_sizer.Add(self.useragent_label, (1, 0), flag=wx.ALIGN_CENTER_VERTICAL)\n        network_sizer.Add(self.useragent_textctrl, (1, 2))\n\n        network_sizer.Add(self.referer_label, (2, 0), flag=wx.ALIGN_CENTER_VERTICAL)\n        network_sizer.Add(self.referer_textctrl, (2, 2))\n\n        network_sizer.AddGrowableCol(1)\n        vertical_sizer.Add(network_sizer, flag=wx.EXPAND | wx.ALL, border=5)\n\n        # Set up logging box\n        vertical_sizer.Add(self.logging_label, flag=wx.TOP, border=10)\n\n        logging_sizer = wx.BoxSizer(wx.HORIZONTAL)\n        logging_sizer.Add(self.enable_log_checkbox)\n        logging_sizer.AddSpacer((-1, -1), 1)\n        logging_sizer.Add(self.view_log_button)\n        logging_sizer.AddSpacer((5, -1))\n        logging_sizer.Add(self.clear_log_button)\n\n        vertical_sizer.Add(logging_sizer, flag=wx.EXPAND | wx.ALL, border=5)\n\n        main_sizer.Add(vertical_sizer, 1, wx.EXPAND | wx.ALL, border=5)\n        self.SetSizer(main_sizer)\n\n    def _on_enable_log(self, event):\n        \"\"\"Event handler for the wx.EVT_CHECKBOX of the enable_log_checkbox.\"\"\"\n        wx.MessageBox(_(\"In order for the changes to take effect please restart {0}\").format(__appname__),\n                      _(\"Restart\"),\n                      wx.OK | wx.ICON_INFORMATION,\n                      self)\n\n    def _on_view(self, event):\n        \"\"\"Event handler for the wx.EVT_BUTTON of the view_log_button.\"\"\"\n        log_window = LogGUI(self)\n        log_window.load(self.log_manager.log_file)\n        log_window.Show()\n\n    def _on_clear(self, event):\n        \"\"\"Event handler for the wx.EVT_BUTTON of the clear_log_button.\"\"\"\n        if self.log_manager is not None:\n            self.log_manager.clear()\n\n    def load_options(self):\n        self.retries_spinctrl.SetValue(self.opt_manager.options[\"retries\"])\n        self.username_textctrl.SetValue(self.opt_manager.options[\"username\"])\n        self.password_textctrl.SetValue(self.opt_manager.options[\"password\"])\n        self.video_pass_textctrl.SetValue(self.opt_manager.options[\"video_password\"])\n        self.proxy_textctrl.SetValue(self.opt_manager.options[\"proxy\"])\n        self.useragent_textctrl.SetValue(self.opt_manager.options[\"user_agent\"])\n        self.referer_textctrl.SetValue(self.opt_manager.options[\"referer\"])\n        self.enable_log_checkbox.SetValue(self.opt_manager.options[\"enable_log\"])\n\n    def save_options(self):\n        self.opt_manager.options[\"retries\"] = self.retries_spinctrl.GetValue()\n        self.opt_manager.options[\"username\"] = self.username_textctrl.GetValue()\n        self.opt_manager.options[\"password\"] = self.password_textctrl.GetValue()\n        self.opt_manager.options[\"video_password\"] = self.video_pass_textctrl.GetValue()\n        self.opt_manager.options[\"proxy\"] = self.proxy_textctrl.GetValue()\n        self.opt_manager.options[\"user_agent\"] = self.useragent_textctrl.GetValue()\n        self.opt_manager.options[\"referer\"] = self.referer_textctrl.GetValue()\n        self.opt_manager.options[\"enable_log\"] = self.enable_log_checkbox.GetValue()\n\n\nclass ExtraTab(TabPanel):\n\n    def __init__(self, *args, **kwargs):\n        super(ExtraTab, self).__init__(*args, **kwargs)\n\n        self.cmdline_args_label = self.crt_statictext(_(\"Youtube-dl command line options (e.g. --help)\"))\n        self.cmdline_args_textctrl = self.crt_textctrl(wx.TE_MULTILINE | wx.TE_LINEWRAP)\n\n        self.extra_opts_label = self.crt_statictext(_(\"Extra options\"))\n\n        self.youtube_dl_debug_checkbox = self.crt_checkbox(_(\"Debug youtube-dl\"))\n        self.ignore_errors_checkbox = self.crt_checkbox(_(\"Ignore errors\"))\n        self.ignore_config_checkbox = self.crt_checkbox(_(\"Ignore youtube-dl config\"))\n        self.no_mtime_checkbox = self.crt_checkbox(_(\"No mtime\"))\n        self.native_hls_checkbox = self.crt_checkbox(_(\"Prefer native HLS\"))\n\n        self._set_layout()\n\n    def _set_layout(self):\n        main_sizer = wx.BoxSizer(wx.HORIZONTAL)\n        vertical_sizer = wx.BoxSizer(wx.VERTICAL)\n\n        vertical_sizer.Add(self.cmdline_args_label)\n        vertical_sizer.Add(self.cmdline_args_textctrl, 1, wx.EXPAND | wx.ALL, border=5)\n\n        vertical_sizer.Add(self.extra_opts_label, flag=wx.TOP, border=5)\n\n        extra_opts_sizer = wx.WrapSizer()\n        extra_opts_sizer.Add(self.youtube_dl_debug_checkbox)\n        extra_opts_sizer.AddSpacer((5, -1))\n        extra_opts_sizer.Add(self.ignore_errors_checkbox)\n        extra_opts_sizer.AddSpacer((5, -1))\n        extra_opts_sizer.Add(self.ignore_config_checkbox)\n        extra_opts_sizer.AddSpacer((5, -1))\n        extra_opts_sizer.Add(self.no_mtime_checkbox)\n        extra_opts_sizer.AddSpacer((5, -1))\n        extra_opts_sizer.Add(self.native_hls_checkbox)\n\n        vertical_sizer.Add(extra_opts_sizer, flag=wx.ALL, border=5)\n\n        main_sizer.Add(vertical_sizer, 1, wx.EXPAND | wx.ALL, border=5)\n        self.SetSizer(main_sizer)\n\n    def load_options(self):\n        self.cmdline_args_textctrl.SetValue(self.opt_manager.options[\"cmd_args\"])\n        self.ignore_errors_checkbox.SetValue(self.opt_manager.options[\"ignore_errors\"])\n        self.youtube_dl_debug_checkbox.SetValue(self.opt_manager.options[\"youtube_dl_debug\"])\n        self.ignore_config_checkbox.SetValue(self.opt_manager.options[\"ignore_config\"])\n        self.native_hls_checkbox.SetValue(self.opt_manager.options[\"native_hls\"])\n        self.no_mtime_checkbox.SetValue(self.opt_manager.options[\"nomtime\"])\n\n    def save_options(self):\n        self.opt_manager.options[\"cmd_args\"] = self.cmdline_args_textctrl.GetValue()\n        self.opt_manager.options[\"ignore_errors\"] = self.ignore_errors_checkbox.GetValue()\n        self.opt_manager.options[\"youtube_dl_debug\"] = self.youtube_dl_debug_checkbox.GetValue()\n        self.opt_manager.options[\"ignore_config\"] = self.ignore_config_checkbox.GetValue()\n        self.opt_manager.options[\"native_hls\"] = self.native_hls_checkbox.GetValue()\n        self.opt_manager.options[\"nomtime\"] = self.no_mtime_checkbox.GetValue()\n\n\nclass LogGUI(wx.Frame):\n\n    \"\"\"Simple window for reading the STDERR.\n\n    Attributes:\n        TITLE (string): Frame title.\n        FRAME_SIZE (tuple): Tuple that holds the frame size (width, height).\n\n    Args:\n        parent (wx.Window): Frame parent.\n\n    \"\"\"\n\n    # REFACTOR move it on widgets module\n\n    TITLE = _(\"Log Viewer\")\n    FRAME_SIZE = (750, 200)\n\n    def __init__(self, parent=None):\n        wx.Frame.__init__(self, parent, title=self.TITLE, size=self.FRAME_SIZE)\n\n        panel = wx.Panel(self)\n\n        self._text_area = wx.TextCtrl(\n            panel,\n            style=wx.TE_MULTILINE | wx.TE_READONLY | wx.HSCROLL\n        )\n\n        sizer = wx.BoxSizer()\n        sizer.Add(self._text_area, 1, wx.EXPAND)\n        panel.SetSizerAndFit(sizer)\n\n    def load(self, filename):\n        \"\"\"Load file content on the text area. \"\"\"\n        if os_path_exists(filename):\n            self._text_area.LoadFile(filename)\n"
  },
  {
    "path": "youtube_dl_gui/optionsmanager.py",
    "content": "#!/usr/bin/env python2\n# -*- coding: utf-8 -*-\n\n\"\"\"Youtubedlg module to handle settings. \"\"\"\n\nfrom __future__ import unicode_literals\n\nimport os\nimport json\n\nfrom .utils import (\n    os_path_expanduser,\n    os_path_exists,\n    encode_tuple,\n    decode_tuple,\n    check_path,\n    get_default_lang\n)\n\nfrom .formats import (\n    OUTPUT_FORMATS,\n    FORMATS\n)\n\n\nclass OptionsManager(object):\n\n    \"\"\"Handles youtubedlg options.\n\n    This class is responsible for storing and retrieving the options.\n\n    Attributes:\n        SETTINGS_FILENAME (string): Filename of the settings file.\n        SENSITIVE_KEYS (tuple): Contains the keys that we don't want\n            to store on the settings file. (SECURITY ISSUES).\n\n    Args:\n        config_path (string): Absolute path where OptionsManager\n            should store the settings file.\n\n    Note:\n        See load_default() method for available options.\n\n    Example:\n        Access the options using the 'options' variable.\n\n        opt_manager = OptionsManager('.')\n        opt_manager.options['save_path'] = '~/Downloads'\n\n    \"\"\"\n\n    SETTINGS_FILENAME = 'settings.json'\n    SENSITIVE_KEYS = ('sudo_password', 'password', 'video_password')\n\n    def __init__(self, config_path):\n        self.config_path = config_path\n        self.settings_file = os.path.join(config_path, self.SETTINGS_FILENAME)\n        self.options = dict()\n        self.load_default()\n        self.load_from_file()\n\n    def load_default(self):\n        \"\"\"Load the default options.\n\n        Note:\n            This method is automatically called by the constructor.\n\n        Options Description:\n\n            save_path (string): Path where youtube-dl should store the\n                downloaded file. Default is $HOME.\n\n            video_format (string): Video format to download.\n                When this options is set to '0' youtube-dl will choose\n                the best video format available for the given URL.\n\n            second_video_format (string): Video format to mix with the first\n                one (-f 18+17).\n\n            to_audio (boolean): If True youtube-dl will post process the\n                video file.\n\n            keep_video (boolen): If True youtube-dl will keep the video file\n                after post processing it.\n\n            audio_format (string): Audio format of the post processed file.\n                Available values are \"mp3\", \"wav\", \"aac\", \"m4a\", \"vorbis\",\n                \"opus\" & \"flac\".\n\n            audio_quality (string): Audio quality of the post processed file.\n                Available values are \"9\", \"5\", \"0\". The lowest the value the\n                better the quality.\n\n            restrict_filenames (boolean): If True youtube-dl will restrict\n                the downloaded file filename to ASCII characters only.\n\n            output_format (int): This option sets the downloaded file\n                output template. See formats.OUTPUT_FORMATS for more info.\n\n            output_template (string): Can be any output template supported\n                by youtube-dl.\n\n            playlist_start (int): Playlist index to start downloading.\n\n            playlist_end (int): Playlist index to stop downloading.\n\n            max_downloads (int): Maximum number of video files to download\n                from the given playlist.\n\n            min_filesize (float): Minimum file size of the video file.\n                If the video file is smaller than the given size then\n                youtube-dl will abort the download process.\n\n            max_filesize (float): Maximum file size of the video file.\n                If the video file is larger than the given size then\n                youtube-dl will abort the download process.\n\n            min_filesize_unit (string): Minimum file size unit.\n                Available values: '', 'k', 'm', 'g', 'y', 'p', 'e', 'z', 'y'.\n\n            max_filesize_unit (string): Maximum file size unit.\n                See 'min_filesize_unit' option for available values.\n\n            write_subs (boolean): If True youtube-dl will try to download\n                the subtitles file for the given URL.\n\n            write_all_subs (boolean): If True youtube-dl will try to download\n                all the available subtitles files for the given URL.\n\n            write_auto_subs (boolean): If True youtube-dl will try to download\n                the automatic subtitles file for the given URL.\n\n            embed_subs (boolean): If True youtube-dl will merge the subtitles\n                file with the video. (ONLY mp4 files).\n\n            subs_lang (string): Language of the subtitles file to download.\n                Needs 'write_subs' option.\n\n            ignore_errors (boolean): If True youtube-dl will ignore the errors\n                and continue the download process.\n\n            open_dl_dir (boolean): If True youtube-dlg will open the\n                destination folder after download process has been completed.\n\n            write_description (boolean): If True youtube-dl will write video\n                description to a .description file.\n\n            write_info (boolean): If True youtube-dl will write video\n                metadata to a .info.json file.\n\n            write_thumbnail (boolean): If True youtube-dl will write\n                thumbnail image to disk.\n\n            retries (int): Number of youtube-dl retries.\n\n            user_agent (string): Specify a custom user agent for youtube-dl.\n\n            referer (string): Specify a custom referer to use if the video\n                access is restricted to one domain.\n\n            proxy (string): Use the specified HTTP/HTTPS proxy.\n\n            shutdown (boolean): If True youtube-dlg will turn the computer\n                off after the download process has been completed.\n\n            sudo_password (string): SUDO password for the shutdown process if\n                the user does not have elevated privileges.\n\n            username (string): Username to login with.\n\n            password (string): Password to login with.\n\n            video_password (string): Video password for the given URL.\n\n            youtubedl_path (string): Absolute path to the youtube-dl binary.\n                Default is the self.config_path. You can change this option\n                to point on /usr/local/bin etc.. if you want to use the\n                youtube-dl binary on your system. This is also the directory\n                where youtube-dlg will auto download the youtube-dl if not\n                exists so you should make sure you have write access if you\n                want to update the youtube-dl binary from within youtube-dlg.\n\n            cmd_args (string): String that contains extra youtube-dl options\n                seperated by spaces.\n\n            enable_log (boolean): If True youtube-dlg will enable\n                the LogManager. See main() function under __init__().\n\n            log_time (boolean): See logmanager.LogManager add_time attribute.\n\n            workers_number (int): Number of download workers that download manager\n                will spawn. Must be greater than zero.\n\n            locale_name (string): Locale name (e.g. ru_RU).\n\n            main_win_size (tuple): Main window size (width, height).\n                If window becomes to small the program will reset its size.\n                See _settings_are_valid method MIN_FRAME_SIZE.\n\n            opts_win_size (tuple): Options window size (width, height).\n                If window becomes to small the program will reset its size.\n                See _settings_are_valid method MIN_FRAME_SIZE.\n\n            save_path_dirs (list): List that contains temporary save paths.\n\n            selected_video_formats (list): List that contains the selected\n                video formats to display on the main window.\n\n            selected_audio_formats (list): List that contains the selected\n                audio formats to display on the main window.\n\n            selected_format (string): Current format selected on the main window.\n\n            youtube_dl_debug (boolean): When True will pass '-v' flag to youtube-dl.\n\n            ignore_config (boolean): When True will ignore youtube-dl config file options.\n\n            confirm_exit (boolean): When True create popup to confirm exiting youtube-dl-gui.\n\n            native_hls (boolean): When True youtube-dl will use the native HLS implementation.\n\n            show_completion_popup (boolean): When True youtube-dl-gui will create a popup\n                to inform the user for the download completion.\n\n            confirm_deletion (boolean): When True ask user before item removal.\n\n            nomtime (boolean): When True will not use the Last-modified header to\n                set the file modification time.\n\n            embed_thumbnail (boolean): When True will embed the thumbnail in\n                the audio file as cover art.\n\n            add_metadata (boolean): When True will write metadata to file.\n\n            disable_update (boolean): When True the update process will be disabled.\n\n        \"\"\"\n        #REFACTOR Remove old options & check options validation\n        self.options = {\n            'save_path': os_path_expanduser('~'),\n            'save_path_dirs': [\n                os_path_expanduser('~'),\n                os.path.join(os_path_expanduser('~'), \"Downloads\"),\n                os.path.join(os_path_expanduser('~'), \"Desktop\"),\n                os.path.join(os_path_expanduser('~'), \"Videos\"),\n                os.path.join(os_path_expanduser('~'), \"Music\"),\n            ],\n            'video_format': '0',\n            'second_video_format': '0',\n            'to_audio': False,\n            'keep_video': False,\n            'audio_format': '',\n            'audio_quality': '5',\n            'restrict_filenames': False,\n            'output_format': 1,\n            'output_template': os.path.join('%(uploader)s', '%(title)s.%(ext)s'),\n            'playlist_start': 1,\n            'playlist_end': 0,\n            'max_downloads': 0,\n            'min_filesize': 0,\n            'max_filesize': 0,\n            'min_filesize_unit': '',\n            'max_filesize_unit': '',\n            'write_subs': False,\n            'write_all_subs': False,\n            'write_auto_subs': False,\n            'embed_subs': False,\n            'subs_lang': 'en',\n            'ignore_errors': True,\n            'open_dl_dir': False,\n            'write_description': False,\n            'write_info': False,\n            'write_thumbnail': False,\n            'retries': 10,\n            'user_agent': '',\n            'referer': '',\n            'proxy': '',\n            'shutdown': False,\n            'sudo_password': '',\n            'username': '',\n            'password': '',\n            'video_password': '',\n            'youtubedl_path': self.config_path,\n            'cmd_args': '',\n            'enable_log': True,\n            'log_time': True,\n            'workers_number': 3,\n            'locale_name': get_default_lang(),\n            'main_win_size': (740, 490),\n            'opts_win_size': (640, 490),\n            'selected_video_formats': ['webm', 'mp4'],\n            'selected_audio_formats': ['mp3', 'm4a', 'vorbis'],\n            'selected_format': '0',\n            'youtube_dl_debug': False,\n            'ignore_config': True,\n            'confirm_exit': True,\n            'native_hls': True,\n            'show_completion_popup': True,\n            'confirm_deletion': True,\n            'nomtime': False,\n            'embed_thumbnail': False,\n            'add_metadata': False,\n            'disable_update': False\n        }\n\n        # Set the youtubedl_path again if the disable_update option is set\n        new_path = '/usr/bin'\n\n        if self.options['disable_update'] and os.name != 'nt' and os_path_exists(new_path):\n            self.options['youtubedl_path'] = new_path\n\n    def load_from_file(self):\n        \"\"\"Load options from settings file. \"\"\"\n        if not os_path_exists(self.settings_file):\n            return\n\n        with open(self.settings_file, 'rb') as settings_file:\n            try:\n                options = json.load(settings_file)\n\n                if self._settings_are_valid(options):\n                    self.options = options\n            except:\n                self.load_default()\n\n    def save_to_file(self):\n        \"\"\"Save options to settings file. \"\"\"\n        check_path(self.config_path)\n\n        with open(self.settings_file, 'wb') as settings_file:\n            options = self._get_options()\n            json.dump(options,\n                      settings_file,\n                      indent=4,\n                      separators=(',', ': '))\n\n    def _settings_are_valid(self, settings_dictionary):\n        \"\"\"Check settings.json dictionary.\n\n        Args:\n            settings_dictionary (dict): Options dictionary loaded\n                from the settings file. See load_from_file() method.\n\n        Returns:\n            True if settings.json dictionary is valid, else False.\n\n        \"\"\"\n        VALID_VIDEO_FORMAT = ('0', '17', '36', '5', '34', '35', '43', '44', '45',\n            '46', '18', '22', '37', '38', '160', '133', '134', '135', '136','137',\n            '264', '138', '242', '243', '244', '247', '248', '271', '272', '82',\n            '83', '84', '85', '100', '101', '102', '139', '140', '141', '171', '172')\n\n        VALID_AUDIO_FORMAT = ('mp3', 'wav', 'aac', 'm4a', 'vorbis', 'opus', 'flac', '')\n\n        VALID_AUDIO_QUALITY = ('0', '5', '9')\n\n        VALID_FILESIZE_UNIT = ('', 'k', 'm', 'g', 't', 'p', 'e', 'z', 'y')\n\n        VALID_SUB_LANGUAGE = ('en', 'el', 'pt', 'fr', 'it', 'ru', 'es', 'de', 'he', 'sv', 'tr')\n\n        MIN_FRAME_SIZE = 100\n\n        # Decode string formatted tuples back to normal tuples\n        settings_dictionary['main_win_size'] = decode_tuple(settings_dictionary['main_win_size'])\n        settings_dictionary['opts_win_size'] = decode_tuple(settings_dictionary['opts_win_size'])\n\n        for key in self.options:\n            if key not in settings_dictionary:\n                return False\n\n            if type(self.options[key]) != type(settings_dictionary[key]):\n                return False\n\n        # Check if each key has a valid value\n        rules_dict = {\n            'video_format': FORMATS.keys(),\n            'second_video_format': VALID_VIDEO_FORMAT,\n            'audio_format': VALID_AUDIO_FORMAT,\n            'audio_quality': VALID_AUDIO_QUALITY,\n            'output_format': OUTPUT_FORMATS.keys(),\n            'min_filesize_unit': VALID_FILESIZE_UNIT,\n            'max_filesize_unit': VALID_FILESIZE_UNIT,\n            'subs_lang': VALID_SUB_LANGUAGE\n        }\n\n        for key, valid_list in rules_dict.items():\n            if settings_dictionary[key] not in valid_list:\n                return False\n\n        # Check workers number value\n        if settings_dictionary['workers_number'] < 1:\n            return False\n\n        # Check main-options frame size\n        for size in settings_dictionary['main_win_size']:\n            if size < MIN_FRAME_SIZE:\n                return False\n\n        for size in settings_dictionary['opts_win_size']:\n            if size < MIN_FRAME_SIZE:\n                return False\n\n        return True\n\n    def _get_options(self):\n        \"\"\"Return options dictionary without SENSITIVE_KEYS. \"\"\"\n        temp_options = self.options.copy()\n\n        for key in self.SENSITIVE_KEYS:\n            temp_options[key] = ''\n\n        # Encode normal tuples to string formatted tuples\n        temp_options['main_win_size'] = encode_tuple(temp_options['main_win_size'])\n        temp_options['opts_win_size'] = encode_tuple(temp_options['opts_win_size'])\n\n        return temp_options\n\n"
  },
  {
    "path": "youtube_dl_gui/parsers.py",
    "content": "#!/usr/bin/env python2\n# -*- coding: utf-8 -*-\n\n\"\"\"Youtubedlg module responsible for parsing the options. \"\"\"\n\nfrom __future__ import unicode_literals\n\nimport os.path\n\nfrom .utils import (\n    remove_shortcuts,\n    to_string\n)\n\n\nclass OptionHolder(object):\n\n    \"\"\"Simple data structure that holds informations for the given option.\n\n    Args:\n        name (string): Option name. Must be a valid option name\n            from the optionsmanager.OptionsManager class.\n            See optionsmanager.OptionsManager load_default() method.\n\n        flag (string): The option command line switch.\n            See https://github.com/rg3/youtube-dl/#options\n\n        default_value (any): The option default value. Must be the same type\n            with the corresponding option from the optionsmanager.OptionsManager\n            class.\n\n        requirements (list): The requirements for the given option. This\n            argument is a list of strings with the name of all the options\n            that this specific option needs. For example 'subs_lang' needs the\n            'write_subs' option to be enabled.\n\n    \"\"\"\n\n    def __init__(self, name, flag, default_value, requirements=None):\n        self.name = name\n        self.flag = flag\n        self.requirements = requirements\n        self.default_value = default_value\n\n    def is_boolean(self):\n        \"\"\"Returns True if the option is a boolean switch else False. \"\"\"\n        return type(self.default_value) is bool\n\n    def check_requirements(self, options_dict):\n        \"\"\"Check if the required options are enabled.\n\n        Args:\n            options_dict (dict): Dictionary with all the options.\n\n        Returns:\n            True if any of the required options is enabled else False.\n\n        \"\"\"\n        if self.requirements is None:\n            return True\n\n        return any([options_dict[req] for req in self.requirements])\n\n\nclass OptionsParser(object):\n\n    \"\"\"Parse optionsmanager.OptionsManager options.\n\n    This class is responsible for turning some of the youtube-dlg options\n    to youtube-dl command line options.\n\n    \"\"\"\n\n    def __init__(self):\n        self._ydl_options = [\n            OptionHolder('playlist_start', '--playlist-start', 1),\n            OptionHolder('playlist_end', '--playlist-end', 0),\n            OptionHolder('max_downloads', '--max-downloads', 0),\n            OptionHolder('username', '-u', ''),\n            OptionHolder('password', '-p', ''),\n            OptionHolder('video_password', '--video-password', ''),\n            OptionHolder('retries', '-R', 10),\n            OptionHolder('proxy', '--proxy', ''),\n            OptionHolder('user_agent', '--user-agent', ''),\n            OptionHolder('referer', '--referer', ''),\n            OptionHolder('ignore_errors', '-i', False),\n            OptionHolder('write_description', '--write-description', False),\n            OptionHolder('write_info', '--write-info-json', False),\n            OptionHolder('write_thumbnail', '--write-thumbnail', False),\n            OptionHolder('min_filesize', '--min-filesize', 0),\n            OptionHolder('max_filesize', '--max-filesize', 0),\n            OptionHolder('write_all_subs', '--all-subs', False),\n            OptionHolder('write_auto_subs', '--write-auto-sub', False),\n            OptionHolder('write_subs', '--write-sub', False),\n            OptionHolder('keep_video', '-k', False),\n            OptionHolder('restrict_filenames', '--restrict-filenames', False),\n            OptionHolder('save_path', '-o', ''),\n            OptionHolder('embed_subs', '--embed-subs', False, ['write_auto_subs', 'write_subs']),\n            OptionHolder('to_audio', '-x', False),\n            OptionHolder('audio_format', '--audio-format', ''),\n            OptionHolder('video_format', '-f', '0'),\n            OptionHolder('subs_lang', '--sub-lang', '', ['write_subs']),\n            OptionHolder('audio_quality', '--audio-quality', '5', ['to_audio']),\n            OptionHolder('youtube_dl_debug', '-v', False),\n            OptionHolder('ignore_config', '--ignore-config', False),\n            OptionHolder('native_hls', '--hls-prefer-native', False),\n            OptionHolder('nomtime', '--no-mtime', False),\n            OptionHolder('embed_thumbnail', '--embed-thumbnail', False),\n            OptionHolder('add_metadata', '--add-metadata', False)\n        ]\n\n    def parse(self, options_dictionary):\n        \"\"\"Parse optionsmanager.OptionsManager options.\n\n        Parses the given options to youtube-dl command line arguments.\n\n        Args:\n            options_dictionary (dict): Dictionary with all the options.\n\n        Returns:\n            List of strings with all the youtube-dl command line options.\n\n        \"\"\"\n        # REFACTOR\n        options_list = ['--newline']\n\n        # Create a copy of options_dictionary\n        # We don't want to edit the original options dictionary\n        # and change some of the options values like 'save_path' etc..\n        options_dict = options_dictionary.copy()\n\n        self._build_savepath(options_dict)\n        self._build_videoformat(options_dict)\n        self._build_filesizes(options_dict)\n\n        # Parse basic youtube-dl command line options\n        for option in self._ydl_options:\n            #NOTE Special case should be removed\n            if option.name == \"to_audio\":\n                if options_dict[\"audio_format\"] == \"\":\n                    value = options_dict[option.name]\n\n                    if value != option.default_value:\n                        options_list.append(option.flag)\n            elif option.name == \"audio_format\":\n                value = options_dict[option.name]\n\n                if value != option.default_value:\n                    options_list.append(\"-x\")\n                    options_list.append(option.flag)\n                    options_list.append(to_string(value))\n\n                    #NOTE Temp fix\n                    # If current 'audio_quality' is not the default one ('5')\n                    # then append the audio quality flag and value to the\n                    # options list\n                    if options_dict[\"audio_quality\"] != \"5\":\n                        options_list.append(\"--audio-quality\")\n                        options_list.append(to_string(options_dict[\"audio_quality\"]))\n\n            elif option.name == \"audio_quality\":\n                # If the '--audio-quality' is not already in the options list\n                # from the above branch then follow the standard procedure.\n                # We don't have to worry for the sequence in which the code\n                # will be executed since the 'audio_quality' option is placed\n                # after the 'audio_format' option in the self._ydl_options list\n                if option.flag not in options_list:\n                    if option.check_requirements(options_dict):\n                        value = options_dict[option.name]\n\n                        if value != option.default_value:\n                            options_list.append(option.flag)\n                            options_list.append(to_string(value))\n\n            elif option.check_requirements(options_dict):\n                value = options_dict[option.name]\n\n                if value != option.default_value:\n                    options_list.append(option.flag)\n\n                    if not option.is_boolean():\n                        options_list.append(to_string(value))\n\n        # Parse cmd_args\n\n        # Indicates whether an item needs special handling\n        special_case = False\n\n        # Temp list to hold special items\n        special_items = []\n\n        for item in options_dict[\"cmd_args\"].split():\n\n            # Its a special case if its already a special case\n            # or an item starts with double quotes\n            special_case = (special_case or item[0] == \"\\\"\")\n\n            if special_case:\n                special_items.append(item)\n            else:\n                options_list.append(item)\n\n            # If its a special case and we meet a double quote\n            # at the end of the item, special case is over and\n            # we need to join, filter and append our special items\n            # to the options list\n            if special_case and item[-1] == \"\\\"\":\n                options_list.append(\" \".join(special_items)[1:-1])\n\n                special_case = False\n                special_items = []\n\n        return options_list\n\n    def _build_savepath(self, options_dict):\n        \"\"\"Build the save path.\n\n        We use this method to build the value of the 'save_path' option and\n        store it back to the options dictionary.\n\n        Args:\n            options_dict (dict): Copy of the original options dictionary.\n\n        \"\"\"\n        save_path = remove_shortcuts(options_dict['save_path'])\n\n        if options_dict[\"output_format\"] == 0:\n            template = \"%(id)s.%(ext)s\"\n        elif options_dict[\"output_format\"] == 1:\n            template = \"%(title)s.%(ext)s\"\n        elif options_dict[\"output_format\"] == 2:\n            template = \"%(title)s-%(id)s.%(ext)s\"\n        elif options_dict[\"output_format\"] == 4:\n            template = \"%(title)s-%(height)sp.%(ext)s\"\n        elif options_dict[\"output_format\"] == 5:\n            template = \"%(title)s-%(id)s-%(height)sp.%(ext)s\"\n        else:\n            template = options_dict[\"output_template\"]\n\n        options_dict[\"save_path\"] = os.path.join(save_path, template)\n\n    def _build_videoformat(self, options_dict):\n        \"\"\"Build the video format.\n\n        We use this method to build the value of the 'video_format' option and\n        store it back to the options dictionary.\n\n        Args:\n            options_dict (dict): Copy of the original options dictionary.\n\n        \"\"\"\n        if options_dict['video_format'] != '0' and options_dict['second_video_format'] != '0':\n            options_dict['video_format'] = options_dict['video_format'] + '+' + options_dict['second_video_format']\n\n    def _build_filesizes(self, options_dict):\n        \"\"\"Build the filesize options values.\n\n        We use this method to build the values of 'min_filesize' and\n        'max_filesize' options and store them back to options dictionary.\n\n        Args:\n            options_dict (dict): Copy of the original options dictionary.\n\n        \"\"\"\n        if options_dict['min_filesize']:\n            options_dict['min_filesize'] = to_string(options_dict['min_filesize']) + options_dict['min_filesize_unit']\n\n        if options_dict['max_filesize']:\n            options_dict['max_filesize'] = to_string(options_dict['max_filesize']) + options_dict['max_filesize_unit']\n"
  },
  {
    "path": "youtube_dl_gui/updatemanager.py",
    "content": "#!/usr/bin/env python2\n# -*- coding: utf-8 -*-\n\n\"\"\"Youtubedlg module to update youtube-dl binary.\n\nAttributes:\n    UPDATE_PUB_TOPIC (string): wxPublisher subscription topic of the\n        UpdateThread thread.\n\n\"\"\"\n\nfrom __future__ import unicode_literals\n\nimport os.path\nfrom threading import Thread\nfrom urllib2 import urlopen, URLError, HTTPError\n\nfrom wx import CallAfter\nfrom wx.lib.pubsub import setuparg1\nfrom wx.lib.pubsub import pub as Publisher\n\nfrom .utils import (\n    YOUTUBEDL_BIN,\n    check_path\n)\n\nUPDATE_PUB_TOPIC = 'update'\n\n\nclass UpdateThread(Thread):\n\n    \"\"\"Python Thread that downloads youtube-dl binary.\n\n    Attributes:\n        LATEST_YOUTUBE_DL (string): URL with the latest youtube-dl binary.\n        DOWNLOAD_TIMEOUT (int): Download timeout in seconds.\n\n    Args:\n        download_path (string): Absolute path where UpdateThread will download\n            the latest youtube-dl.\n\n        quiet (boolean): If True UpdateThread won't send the finish signal\n            back to the caller. Finish signal can be used to make sure that\n            the UpdateThread has been completed in an asynchronous way.\n\n    \"\"\"\n\n    LATEST_YOUTUBE_DL = 'https://yt-dl.org/latest/'\n    DOWNLOAD_TIMEOUT = 10\n\n    def __init__(self, download_path, quiet=False):\n        super(UpdateThread, self).__init__()\n        self.download_path = download_path\n        self.quiet = quiet\n        self.start()\n\n    def run(self):\n        self._talk_to_gui('download')\n\n        source_file = self.LATEST_YOUTUBE_DL + YOUTUBEDL_BIN\n        destination_file = os.path.join(self.download_path, YOUTUBEDL_BIN)\n\n        check_path(self.download_path)\n\n        try:\n            stream = urlopen(source_file, timeout=self.DOWNLOAD_TIMEOUT)\n\n            with open(destination_file, 'wb') as dest_file:\n                dest_file.write(stream.read())\n\n            self._talk_to_gui('correct')\n        except (HTTPError, URLError, IOError) as error:\n            self._talk_to_gui('error', unicode(error))\n\n        if not self.quiet:\n            self._talk_to_gui('finish')\n\n    def _talk_to_gui(self, signal, data=None):\n        \"\"\"Communicate with the GUI using wxCallAfter and wxPublisher.\n\n        Args:\n            signal (string): Unique signal string that informs the GUI for the\n                update process.\n\n            data (string): Can be any string data to pass along with the\n                given signal. Default is None.\n\n        Note:\n            UpdateThread supports 4 signals.\n                1) download: The update process started\n                2) correct: The update process completed successfully\n                3) error: An error occured while downloading youtube-dl binary\n                4) finish: The update thread is ready to join\n\n        \"\"\"\n        CallAfter(Publisher.sendMessage, UPDATE_PUB_TOPIC, (signal, data))\n"
  },
  {
    "path": "youtube_dl_gui/utils.py",
    "content": "#!/usr/bin/env python2\n# -*- coding: utf-8 -*-\n\n\"\"\"Youtubedlg module that contains util functions.\n\nAttributes:\n    _RANDOM_OBJECT (object): Object that it's used as a default parameter.\n\n    YOUTUBEDL_BIN (string): Youtube-dl binary filename.\n\n\"\"\"\n\nfrom __future__ import unicode_literals\n\nimport os\nimport sys\nimport json\nimport math\nimport locale\nimport subprocess\n\ntry:\n    from twodict import TwoWayOrderedDict\nexcept ImportError as error:\n    print error\n    sys.exit(1)\n\nfrom .info import __appname__\nfrom .version import __version__\n\n\n_RANDOM_OBJECT = object()\n\n\nYOUTUBEDL_BIN = 'youtube-dl'\nif os.name == 'nt':\n    YOUTUBEDL_BIN += '.exe'\n\n\nFILESIZE_METRICS = [\"B\", \"KiB\", \"MiB\", \"GiB\", \"TiB\", \"PiB\", \"EiB\", \"ZiB\", \"YiB\"]\n\nKILO_SIZE = 1024.0\n\n\ndef get_encoding():\n    \"\"\"Return system encoding. \"\"\"\n    try:\n        encoding = locale.getpreferredencoding()\n        'TEST'.encode(encoding)\n    except:\n        encoding = 'UTF-8'\n\n    return encoding\n\n\ndef convert_item(item, to_unicode=False):\n    \"\"\"Convert item between 'unicode' and 'str'.\n\n    Args:\n        item (-): Can be any python item.\n\n        to_unicode (boolean): When True it will convert all the 'str' types\n            to 'unicode'. When False it will convert all the 'unicode'\n            types back to 'str'.\n\n    \"\"\"\n    if to_unicode and isinstance(item, str):\n        # Convert str to unicode\n        return item.decode(get_encoding(), 'ignore')\n\n    if not to_unicode and isinstance(item, unicode):\n        # Convert unicode to str\n        return item.encode(get_encoding(), 'ignore')\n\n    if hasattr(item, '__iter__'):\n        # Handle iterables\n        temp_list = []\n\n        for sub_item in item:\n            if isinstance(item, dict):\n                temp_list.append((convert_item(sub_item, to_unicode), convert_item(item[sub_item], to_unicode)))\n            else:\n                temp_list.append(convert_item(sub_item, to_unicode))\n\n        return type(item)(temp_list)\n\n    return item\n\n\ndef convert_on_bounds(func):\n    \"\"\"Decorator to convert string inputs & outputs.\n\n    Covert string inputs & outputs between 'str' and 'unicode' at the\n    application bounds using the preferred system encoding. It will convert\n    all the string params (args, kwargs) to 'str' type and all the\n    returned strings values back to 'unicode'.\n\n    \"\"\"\n    def wrapper(*args, **kwargs):\n        returned_value = func(*convert_item(args), **convert_item(kwargs))\n\n        return convert_item(returned_value, True)\n\n    return wrapper\n\n\n# See: https://github.com/MrS0m30n3/youtube-dl-gui/issues/57\n# Patch os functions to convert between 'str' and 'unicode' on app bounds\nos_sep = unicode(os.sep)\nos_getenv = convert_on_bounds(os.getenv)\nos_makedirs = convert_on_bounds(os.makedirs)\nos_path_isdir = convert_on_bounds(os.path.isdir)\nos_path_exists = convert_on_bounds(os.path.exists)\nos_path_dirname = convert_on_bounds(os.path.dirname)\nos_path_abspath = convert_on_bounds(os.path.abspath)\nos_path_realpath = convert_on_bounds(os.path.realpath)\nos_path_expanduser = convert_on_bounds(os.path.expanduser)\n\n# Patch locale functions\nlocale_getdefaultlocale = convert_on_bounds(locale.getdefaultlocale)\n\n# Patch Windows specific functions\nif os.name == 'nt':\n    os_startfile = convert_on_bounds(os.startfile)\n\ndef remove_file(filename):\n    if os_path_exists(filename):\n        os.remove(filename)\n        return True\n\n    return False\n\ndef remove_shortcuts(path):\n    \"\"\"Return given path after removing the shortcuts. \"\"\"\n    return path.replace('~', os_path_expanduser('~'))\n\n\ndef absolute_path(filename):\n    \"\"\"Return absolute path to the given file. \"\"\"\n    return os_path_dirname(os_path_realpath(os_path_abspath(filename)))\n\n\ndef open_file(file_path):\n    \"\"\"Open file in file_path using the default OS application.\n\n    Returns:\n        True on success else False.\n\n    \"\"\"\n    file_path = remove_shortcuts(file_path)\n\n    if not os_path_exists(file_path):\n        return False\n\n    if os.name == \"nt\":\n        os_startfile(file_path)\n    else:\n        subprocess.call((\"xdg-open\", file_path))\n\n    return True\n\n\ndef encode_tuple(tuple_to_encode):\n    \"\"\"Turn size tuple into string. \"\"\"\n    return '%s/%s' % (tuple_to_encode[0], tuple_to_encode[1])\n\n\ndef decode_tuple(encoded_tuple):\n    \"\"\"Turn tuple string back to tuple. \"\"\"\n    s = encoded_tuple.split('/')\n    return int(s[0]), int(s[1])\n\n\ndef check_path(path):\n    \"\"\"Create path if not exist. \"\"\"\n    if not os_path_exists(path):\n        os_makedirs(path)\n\n\ndef get_config_path():\n    \"\"\"Return user config path.\n\n    Note:\n        Windows = %AppData% + app_name\n        Linux   = ~/.config + app_name\n\n    \"\"\"\n    if os.name == 'nt':\n        path = os_getenv('APPDATA')\n    else:\n        path = os.path.join(os_path_expanduser('~'), '.config')\n\n    return os.path.join(path, __appname__.lower())\n\n\ndef shutdown_sys(password=None):\n    \"\"\"Shuts down the system.\n    Returns True if no errors occur else False.\n\n    Args:\n        password (string): SUDO password for linux.\n\n    Note:\n        On Linux you need to provide sudo password if you don't\n        have elevated privileges.\n\n    \"\"\"\n    _stderr = subprocess.PIPE\n    _stdin = None\n    info = None\n    encoding = get_encoding()\n\n    if os.name == 'nt':\n        cmd = ['shutdown', '/s', '/t', '1']\n\n        # Hide subprocess window\n        info = subprocess.STARTUPINFO()\n        info.dwFlags |= subprocess.STARTF_USESHOWWINDOW\n    else:\n        if password:\n            _stdin = subprocess.PIPE\n            password = ('%s\\n' % password).encode(encoding)\n            cmd = ['sudo', '-S', '/sbin/shutdown', '-h', 'now']\n        else:\n            cmd = ['/sbin/shutdown', '-h', 'now']\n\n    cmd = [item.encode(encoding, 'ignore') for item in cmd]\n\n    shutdown_proc = subprocess.Popen(cmd,\n                                     stderr=_stderr,\n                                     stdin=_stdin,\n                                     startupinfo=info)\n\n    output = shutdown_proc.communicate(password)[1]\n\n    return not output or output == \"Password:\"\n\n\ndef to_string(data):\n    \"\"\"Convert data to string.\n    Works for both Python2 & Python3. \"\"\"\n    return '%s' % data\n\n\ndef get_time(seconds):\n    \"\"\"Convert given seconds to days, hours, minutes and seconds.\n\n    Args:\n        seconds (float): Time in seconds.\n\n    Returns:\n        Dictionary that contains the corresponding days, hours, minutes\n        and seconds of the given seconds.\n\n    \"\"\"\n    dtime = dict(seconds=0, minutes=0, hours=0, days=0)\n\n    dtime['days'] = int(seconds / 86400)\n    dtime['hours'] = int(seconds % 86400 / 3600)\n    dtime['minutes'] = int(seconds % 86400 % 3600 / 60)\n    dtime['seconds'] = int(seconds % 86400 % 3600 % 60)\n\n    return dtime\n\n\ndef get_locale_file():\n    \"\"\"Search for youtube-dlg locale file.\n\n    Returns:\n        The path to youtube-dlg locale file if exists else None.\n\n    Note:\n        Paths that get_locale_file() func searches.\n\n        __main__ dir, library dir\n\n    \"\"\"\n    DIR_NAME = \"locale\"\n\n    SEARCH_DIRS = [\n        os.path.join(absolute_path(sys.argv[0]), DIR_NAME),\n        os.path.join(os_path_dirname(__file__), DIR_NAME),\n    ]\n\n    for directory in SEARCH_DIRS:\n        if os_path_isdir(directory):\n            return directory\n\n    return None\n\n\ndef get_icon_file():\n    \"\"\"Search for youtube-dlg app icon.\n\n    Returns:\n        The path to youtube-dlg icon file if exists, else returns None.\n\n    \"\"\"\n    ICON_NAME = \"youtube-dl-gui.png\"\n\n    pixmaps_dir = get_pixmaps_dir()\n\n    if pixmaps_dir is not None:\n        icon_file = os.path.join(pixmaps_dir, ICON_NAME)\n\n        if os_path_exists(icon_file):\n            return icon_file\n\n    return None\n\n\ndef get_pixmaps_dir():\n    \"\"\"Return absolute path to the pixmaps icons folder.\n\n    Note:\n        Paths we search: __main__ dir, library dir\n\n    \"\"\"\n    search_dirs = [\n        os.path.join(absolute_path(sys.argv[0]), \"data\"),\n        os.path.join(os_path_dirname(__file__), \"data\")\n    ]\n\n    for directory in search_dirs:\n        pixmaps_dir = os.path.join(directory, \"pixmaps\")\n\n        if os_path_exists(pixmaps_dir):\n            return pixmaps_dir\n\n    return None\n\n\ndef to_bytes(string):\n    \"\"\"Convert given youtube-dl size string to bytes.\"\"\"\n    value = 0.0\n\n    for index, metric in enumerate(reversed(FILESIZE_METRICS)):\n        if metric in string:\n            value = float(string.split(metric)[0])\n            break\n\n    exponent = index * (-1) + (len(FILESIZE_METRICS) - 1)\n\n    return round(value * (KILO_SIZE ** exponent), 2)\n\n\ndef format_bytes(bytes):\n    \"\"\"Format bytes to youtube-dl size output strings.\"\"\"\n    if bytes == 0.0:\n        exponent = 0\n    else:\n        exponent = int(math.log(bytes, KILO_SIZE))\n\n    suffix = FILESIZE_METRICS[exponent]\n    output_value = bytes / (KILO_SIZE ** exponent)\n\n    return \"%.2f%s\" % (output_value, suffix)\n\n\ndef build_command(options_list, url):\n    \"\"\"Build the youtube-dl command line string.\"\"\"\n\n    def escape(option):\n        \"\"\"Wrap option with double quotes if it contains special symbols.\"\"\"\n        special_symbols = [\" \", \"(\", \")\"]\n\n        for symbol in special_symbols:\n            if symbol in option:\n                return \"\\\"{}\\\"\".format(option)\n\n        return option\n\n    # If option has special symbols wrap it with double quotes\n    # Probably not the best solution since if the option already contains\n    # double quotes it will be a mess, see issue #173\n    options = [escape(option) for option in options_list]\n\n    # Always wrap the url with double quotes\n    url = \"\\\"{}\\\"\".format(url)\n\n    return \" \".join([YOUTUBEDL_BIN] + options + [url])\n\n\ndef get_default_lang():\n    \"\"\"Get default language using the 'locale' module.\"\"\"\n    default_lang, _ = locale_getdefaultlocale()\n\n    if not default_lang:\n        default_lang = \"en_US\"\n\n    return default_lang\n"
  },
  {
    "path": "youtube_dl_gui/version.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom __future__ import unicode_literals\n\n__version__ = '0.4'\n"
  },
  {
    "path": "youtube_dl_gui/widgets.py",
    "content": "#!/usr/bin/env python\n# -*- coding: UTF-8 -*-\n\nfrom __future__ import unicode_literals\n\nimport sys\n\ntry:\n    import wx\nexcept ImportError as error:\n    print error\n    sys.exit(1)\n\n\ndef crt_command_event(event_type, event_id=0):\n    \"\"\"Shortcut to create command events.\"\"\"\n    return wx.CommandEvent(event_type.typeId, event_id)\n\n\nclass ListBoxWithHeaders(wx.ListBox):\n\n    \"\"\"Custom ListBox object that supports 'headers'.\n\n    Attributes:\n        NAME (string): Default name for the name argument of the __init__.\n\n        TEXT_PREFIX (string): Text to add before normal items in order to\n            distinguish them (normal items) from headers.\n\n        EVENTS (list): List with events to overwrite to avoid header selection.\n\n    \"\"\"\n\n    NAME = \"listBoxWithHeaders\"\n\n    TEXT_PREFIX = \"    \"\n\n    EVENTS = [\n        wx.EVT_LEFT_DOWN,\n        wx.EVT_LEFT_DCLICK,\n        wx.EVT_RIGHT_DOWN,\n        wx.EVT_RIGHT_DCLICK,\n        wx.EVT_MIDDLE_DOWN,\n        wx.EVT_MIDDLE_DCLICK\n    ]\n\n    def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,\n            size=wx.DefaultSize, choices=[], style=0, validator=wx.DefaultValidator, name=NAME):\n        super(ListBoxWithHeaders, self).__init__(parent, id, pos, size, [], style, validator, name)\n        self.__headers = set()\n\n        # Ignore all key events i'm bored to handle the header selection\n        self.Bind(wx.EVT_KEY_DOWN, lambda event: None)\n\n        # Make sure that a header is never selected\n        self.Bind(wx.EVT_LISTBOX, self._on_listbox)\n        for event in self.EVENTS:\n            self.Bind(event, self._disable_header_selection)\n\n        # Append the items in our own way in order to add the TEXT_PREFIX\n        self.AppendItems(choices)\n\n    def _disable_header_selection(self, event):\n        \"\"\"Stop event propagation if the selected item is a header.\"\"\"\n        row = self.HitTest(event.GetPosition())\n        event_skip = True\n\n        if row != wx.NOT_FOUND and self.GetString(row) in self.__headers:\n            event_skip = False\n\n        event.Skip(event_skip)\n\n    def _on_listbox(self, event):\n        \"\"\"Make sure no header is selected.\"\"\"\n        if event.GetString() in self.__headers:\n            self.Deselect(event.GetSelection())\n        event.Skip()\n\n    def _add_prefix(self, string):\n        return self.TEXT_PREFIX + string\n\n    def _remove_prefix(self, string):\n        if string[:len(self.TEXT_PREFIX)] == self.TEXT_PREFIX:\n            return string[len(self.TEXT_PREFIX):]\n        return string\n\n    # wx.ListBox methods\n\n    def FindString(self, string):\n        index = super(ListBoxWithHeaders, self).FindString(string)\n\n        if index == wx.NOT_FOUND:\n            # This time try with prefix\n            index = super(ListBoxWithHeaders, self).FindString(self._add_prefix(string))\n\n        return index\n\n    def GetStringSelection(self):\n        return self._remove_prefix(super(ListBoxWithHeaders, self).GetStringSelection())\n\n    def GetString(self, index):\n        if index < 0 or index >= self.GetCount():\n            # Return empty string based on the wx.ListBox docs\n            # for some reason parent GetString does not handle\n            # invalid indices\n            return \"\"\n\n        return self._remove_prefix(super(ListBoxWithHeaders, self).GetString(index))\n\n    def InsertItems(self, items, pos):\n        items = [self._add_prefix(item) for item in items]\n        super(ListBoxWithHeaders, self).InsertItems(items, pos)\n\n    def SetSelection(self, index):\n        if index == wx.NOT_FOUND:\n            self.Deselect(self.GetSelection())\n        elif self.GetString(index) not in self.__headers:\n            super(ListBoxWithHeaders, self).SetSelection(index)\n\n    def SetString(self, index, string):\n        old_string = self.GetString(index)\n\n        if old_string in self.__headers and string != old_string:\n            self.__headers.remove(old_string)\n            self.__headers.add(string)\n\n        super(ListBoxWithHeaders, self).SetString(index, string)\n\n    def SetStringSelection(self, string):\n        if string in self.__headers:\n            return False\n\n        self.SetSelection(self.FindString(string))\n        return True\n\n    # wx.ItemContainer methods\n\n    def Append(self, string):\n        super(ListBoxWithHeaders, self).Append(self._add_prefix(string))\n\n    def AppendItems(self, strings):\n        strings = [self._add_prefix(string) for string in strings]\n        super(ListBoxWithHeaders, self).AppendItems(strings)\n\n    def Clear(self):\n        self.__headers.clear()\n        super(ListBoxWithHeaders, self).Clear()\n\n    def Delete(self, index):\n        string = self.GetString(index)\n\n        if string in self.__headers:\n            self.__headers.remove(string)\n\n        super(ListBoxWithHeaders, self).Delete(index)\n\n    # Extra methods\n\n    def add_header(self, header_string):\n        self.__headers.add(header_string)\n        super(ListBoxWithHeaders, self).Append(header_string)\n\n    def add_item(self, item, with_prefix=True):\n        if with_prefix:\n            item = self._add_prefix(item)\n\n        super(ListBoxWithHeaders, self).Append(item)\n\n    def add_items(self, items, with_prefix=True):\n        if with_prefix:\n            items = [self._add_prefix(item) for item in items]\n\n        super(ListBoxWithHeaders, self).AppendItems(items)\n\n\nclass ListBoxPopup(wx.PopupTransientWindow):\n\n    \"\"\"ListBoxWithHeaders as a popup.\n\n    This class uses the wx.PopupTransientWindow to create the popup and the\n    API is based on the wx.combo.ComboPopup class.\n\n    Attributes:\n        EVENTS_TABLE (dict): Dictionary that contains all the events\n            that this class emits.\n\n    \"\"\"\n\n    EVENTS_TABLE = {\n        \"EVT_COMBOBOX\": crt_command_event(wx.EVT_COMBOBOX),\n        \"EVT_COMBOBOX_DROPDOWN\" : crt_command_event(wx.EVT_COMBOBOX_DROPDOWN),\n        \"EVT_COMBOBOX_CLOSEUP\": crt_command_event(wx.EVT_COMBOBOX_CLOSEUP)\n    }\n\n    def __init__(self, parent=None, flags=wx.BORDER_NONE):\n        super(ListBoxPopup, self).__init__(parent, flags)\n        self.__listbox = None\n\n    def _on_motion(self, event):\n        row = self.__listbox.HitTest(event.GetPosition())\n\n        if row != wx.NOT_FOUND:\n            self.__listbox.SetSelection(row)\n\n            if self.__listbox.IsSelected(row):\n                self.curitem = row\n\n    def _on_left_down(self, event):\n        self.value = self.curitem\n        self.Dismiss()\n\n        # Send EVT_COMBOBOX to inform the parent for changes\n        wx.PostEvent(self, self.EVENTS_TABLE[\"EVT_COMBOBOX\"])\n\n    def Popup(self):\n        super(ListBoxPopup, self).Popup()\n        wx.PostEvent(self, self.EVENTS_TABLE[\"EVT_COMBOBOX_DROPDOWN\"])\n\n    def OnDismiss(self):\n        wx.PostEvent(self, self.EVENTS_TABLE[\"EVT_COMBOBOX_CLOSEUP\"])\n\n    # wx.combo.ComboPopup methods\n\n    def Init(self):\n        self.value = self.curitem = -1\n\n    def Create(self, parent):\n        self.__listbox = ListBoxWithHeaders(parent, style=wx.LB_SINGLE)\n\n        self.__listbox.Bind(wx.EVT_MOTION, self._on_motion)\n        self.__listbox.Bind(wx.EVT_LEFT_DOWN, self._on_left_down)\n\n        sizer = wx.BoxSizer()\n        sizer.Add(self.__listbox, 1, wx.EXPAND)\n        self.SetSizer(sizer)\n        return True\n\n    def GetAdjustedSize(self, min_width, pref_height, max_height):\n        width, height = self.GetBestSize()\n\n        if width < min_width:\n            width = min_width\n\n        if pref_height != -1:\n            height = pref_height * self.__listbox.GetCount() + 5\n\n        if height > max_height:\n            height = max_height\n\n        return wx.Size(width, height)\n\n    def GetControl(self):\n        return self.__listbox\n\n    def GetStringValue(self):\n        return self.__listbox.GetString(self.value)\n\n    #def SetStringValue(self, string):\n        #self.__listbox.SetStringSelection(string)\n\n\nclass CustomComboBox(wx.Panel):\n\n    \"\"\"Custom combobox.\n\n    Attributes:\n        CB_READONLY (long): Read-only style. The only one supported from the\n            wx.ComboBox styles.\n\n        NAME (string): Default name for the name argument of the __init__.\n\n    \"\"\"\n    #NOTE wx.ComboBox does not support EVT_MOTION inside the popup\n    #NOTE Tried with ComboCtrl but i was not able to draw the button\n\n    CB_READONLY = wx.TE_READONLY\n\n    NAME = \"customComboBox\"\n\n    def __init__(self, parent, id=wx.ID_ANY, value=\"\", pos=wx.DefaultPosition,\n            size=wx.DefaultSize, choices=[], style=0, validator=wx.DefaultValidator, name=NAME):\n        super(CustomComboBox, self).__init__(parent, id, pos, size, 0, name)\n\n        assert style == self.CB_READONLY or style == 0\n\n        # Create components\n        self.textctrl = wx.TextCtrl(self, wx.ID_ANY, style=style, validator=validator)\n        tc_height = self.textctrl.GetSize()[1]\n\n        self.button = wx.Button(self, wx.ID_ANY, \"▾\", size=(tc_height, tc_height))\n\n        # Create the ListBoxPopup in two steps\n        self.listbox = ListBoxPopup(self)\n        self.listbox.Init()\n        self.listbox.Create(self.listbox)\n\n        # Set layout\n        sizer = wx.BoxSizer()\n        sizer.Add(self.textctrl, 1, wx.ALIGN_CENTER_VERTICAL)\n        sizer.Add(self.button)\n        self.SetSizer(sizer)\n\n        # Bind events\n        self.button.Bind(wx.EVT_BUTTON, self._on_button)\n\n        for event in ListBoxPopup.EVENTS_TABLE.values():\n            self.listbox.Bind(wx.PyEventBinder(event.GetEventType()), self._propagate)\n\n        # Append items since the ListBoxPopup does not have the 'choices' arg\n        self.listbox.GetControl().AppendItems(choices)\n        self.SetStringSelection(value)\n\n    def _propagate(self, event):\n        if event.GetEventType() == wx.EVT_COMBOBOX.typeId:\n            self.textctrl.SetValue(self.listbox.GetStringValue())\n\n        wx.PostEvent(self, event)\n\n    def _on_button(self, event):\n        self.Popup()\n\n    def _calc_popup_position(self):\n        tc_x_axis, tc_y_axis = self.textctrl.ClientToScreen((0, 0))\n        _, tc_height = self.textctrl.GetSize()\n\n        return tc_x_axis, tc_y_axis + tc_height\n\n    def _calc_popup_size(self):\n        me_width, _ = self.GetSize()\n        _, tc_height = self.textctrl.GetSize()\n        _, screen_height = wx.DisplaySize()\n\n        _, me_y_axis = self.GetScreenPosition()\n\n        available_height = screen_height - (me_y_axis + tc_height)\n        sug_width, sug_height = self.listbox.GetAdjustedSize(me_width, tc_height, available_height)\n\n        return me_width, sug_height\n\n    # wx.ComboBox methods\n\n    def Dismiss(self):\n        self.listbox.Dismiss()\n\n    def FindString(self, string, caseSensitive=False):\n        #TODO handle caseSensitive\n        return self.listbox.GetControl().FindString(string)\n\n    def GetCount(self):\n        return self.listbox.GetControl().GetCount()\n\n    def GetCurrentSelection(self):\n        return self.GetSelection()\n\n    def GetInsertionPoint(self):\n        return self.textctrl.GetInsertionPoint()\n\n    def GetSelection(self):\n        return self.listbox.value\n\n    def GetTextSelection(self):\n        return self.textctrl.GetSelection()\n\n    def GetString(self, index):\n        return self.listbox.GetControl().GetString(index)\n\n    def GetStringSelection(self):\n        return self.listbox.GetStringValue()\n\n    def IsListEmpty(self):\n        return self.listbox.GetControl().GetCount() == 0\n\n    def IsTextEmpty(self):\n        return not self.textctrl.GetValue()\n\n    def Popup(self):\n        self.listbox.SetPosition(self._calc_popup_position())\n        self.listbox.SetSize(self._calc_popup_size())\n\n        self.listbox.Popup()\n\n    def SetSelection(self, index):\n        self.listbox.GetControl().SetSelection(index)\n        if self.listbox.GetControl().IsSelected(index):\n            self.listbox.value = index\n            self.textctrl.SetValue(self.listbox.GetStringValue())\n\n    def SetString(self, index, string):\n        self.listbox.GetControl().SetString(index, string)\n\n    def SetTextSelection(self, from_, to_):\n        self.textctrl.SetSelection(from_, to_)\n\n    def SetStringSelection(self, string):\n        index = self.listbox.GetControl().FindString(string)\n        self.listbox.GetControl().SetSelection(index)\n\n        if index != wx.NOT_FOUND and self.listbox.GetControl().GetSelection() == index:\n            self.listbox.value = index\n            self.textctrl.SetValue(string)\n\n    def SetValue(self, value):\n        self.textctrl.SetValue(value)\n\n    # wx.ItemContainer methods\n\n    def Clear(self):\n        self.textctrl.Clear()\n        self.listbox.GetControl().Clear()\n\n    def Append(self, item):\n        self.listbox.GetControl().Append(item)\n\n    def AppendItems(self, items):\n        self.listbox.GetControl().AppendItems(items)\n\n    def Delete(self, index):\n        self.listbox.GetControl().Delete(index)\n\n    # wx.TextEntry methods\n\n    def GetValue(self):\n        return self.textctrl.GetValue()\n\n    # ListBoxWithHeaders methods\n\n    def add_header(self, header):\n        self.listbox.GetControl().add_header(header)\n\n    def add_item(self, item, with_prefix=True):\n        self.listbox.GetControl().add_item(item, with_prefix)\n\n    def add_items(self, items, with_prefix=True):\n        self.listbox.GetControl().add_items(items, with_prefix)\n"
  }
]