├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── .tx └── config ├── AUTHORS ├── ChangeLog ├── LICENSE ├── MANIFEST.in ├── README.md ├── TODO ├── devscripts ├── bump-version.sh ├── check-translation.py ├── new-locale.py ├── update-authors.sh └── update-locales.sh ├── docs ├── faqs.md └── localization_howto.md ├── setup.py ├── tests ├── __init__.py ├── test_ditem.py ├── test_dlist.py ├── test_parsers.py ├── test_utils.py └── test_widgets.py ├── youtube-dl-gui.1 └── youtube_dl_gui ├── __init__.py ├── __main__.py ├── data ├── icons │ └── hicolor │ │ ├── 128x128 │ │ └── apps │ │ │ └── youtube-dl-gui.png │ │ ├── 16x16 │ │ └── apps │ │ │ └── youtube-dl-gui.png │ │ ├── 256x256 │ │ └── apps │ │ │ └── youtube-dl-gui.png │ │ ├── 32x32 │ │ └── apps │ │ │ └── youtube-dl-gui.png │ │ ├── 48x48 │ │ └── apps │ │ │ └── youtube-dl-gui.png │ │ └── 64x64 │ │ └── apps │ │ └── youtube-dl-gui.png └── pixmaps │ ├── arrow_down_32px.png │ ├── arrow_up_32px.png │ ├── camera_32px.png │ ├── cloud_download_32px.png │ ├── delete_32px.png │ ├── folder_32px.png │ ├── icons-license │ ├── pause_32px.png │ ├── play_arrow_32px.png │ ├── reload_32px.png │ ├── settings_20px.png │ ├── stop_32px.png │ ├── youtube-dl-gui.ico │ └── youtube-dl-gui.png ├── downloaders.py ├── downloadmanager.py ├── formats.py ├── info.py ├── locale ├── ar_SA │ └── LC_MESSAGES │ │ └── youtube_dl_gui.po ├── cs_CZ │ └── LC_MESSAGES │ │ └── youtube_dl_gui.po ├── en_US │ └── LC_MESSAGES │ │ └── youtube_dl_gui.po ├── es_ES │ └── LC_MESSAGES │ │ └── youtube_dl_gui.po ├── fr_FR │ └── LC_MESSAGES │ │ └── youtube_dl_gui.po ├── it_IT │ └── LC_MESSAGES │ │ └── youtube_dl_gui.po ├── ja_JP │ └── LC_MESSAGES │ │ └── youtube_dl_gui.po ├── ko_KR │ └── LC_MESSAGES │ │ └── youtube_dl_gui.po ├── pt_BR │ └── LC_MESSAGES │ │ └── youtube_dl_gui.po └── ru_RU │ └── LC_MESSAGES │ └── youtube_dl_gui.po ├── logmanager.py ├── mainframe.py ├── optionsframe.py ├── optionsmanager.py ├── parsers.py ├── updatemanager.py ├── utils.py ├── version.py └── widgets.py /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Please follow the guide below 2 | 3 | - You will be asked some questions and requested to provide some information, please read them **carefully** and answer **honestly** 4 | - Put an `x` into all the boxes [ ] relevant to your issue (like that [x]) 5 | - Use *Preview* tab to see how your issue will actually look like 6 | 7 | ### WARNING 8 | All invalid issues will be rejected!! 9 | 10 | --- 11 | 12 | ### Before going further 13 | 14 | - 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) 15 | 16 | - Make sure you are using the *latest* **youtube-dl-gui** version (Click the `Settings` icon and then `About` to view the current version) 17 | 18 | - 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) 19 | 20 | - Make sure you searched the bugtracker for similar issues **including closed ones** 21 | 22 | - Make sure to read the [FAQs](https://github.com/MrS0m30n3/youtube-dl-gui/blob/master/docs/faqs.md) file 23 | 24 | - [ ] **I think** my problem is **NOT** with **youtube-dl** 25 | - [ ] I've **verified** and **i assure** that I'm running youtube-dl-gui **0.4** 26 | - [ ] **I assure** that i am using the latest version of **youtube-dl** 27 | - [ ] [Searched](https://github.com/MrS0m30n3/youtube-dl-gui/issues) bugtracker 28 | - [ ] I've read the FAQs file 29 | 30 | --- 31 | 32 | ### What is the purpose of your *issue*? 33 | 34 | - [ ] Bug report 35 | - [ ] Feature request (request for a new functionality) 36 | - [ ] Question 37 | - [ ] Other 38 | 39 | Please remove any sections between (---) if they are not related to your issue 40 | 41 | --- 42 | 43 | ### Bug report 44 | 45 | #### If the problem occurs when downloading a URL please provide the full verbose output as follows: 46 | 47 | 1. Restart **youtube-dl-gui** 48 | 1. Go to `Options > Extra` tab 49 | 2. Enable **Debug youtube-dl** 50 | 3. Go to `Options > Advanced` tab and **Clear** your log content 51 | 4. Try to download the URL 52 | 5. Copy the **whole** log content and insert it between the ``` part below 53 | 54 | ``` 55 | delete me and insert your log content here 56 | ``` 57 | 58 | #### What operating system do you use ? 59 | 60 | #### List of actions to perform to reproduce the problem: 61 | 62 | 1. .. 63 | 2. .. 64 | 3. .. 65 | 66 | #### What is the expected behaviour ? 67 | 68 | #### What happens instead ? 69 | 70 | 71 | --- 72 | 73 | ### Feature request (request for a new functionality) 74 | 75 | Please make sure that the requested feature is **NOT** already in the [TODO](https://github.com/MrS0m30n3/youtube-dl-gui/blob/master/TODO) list 76 | 77 | - [ ] I've **verified** and **i assure** that my requested feature is **NOT** in the TODO list 78 | 79 | #### What operating system do you use ? 80 | 81 | 82 | --- 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | MANIFEST 2 | *.pyc 3 | *.mo 4 | 5 | dist/ 6 | build/ 7 | -------------------------------------------------------------------------------- /.tx/config: -------------------------------------------------------------------------------- 1 | [main] 2 | host = https://www.transifex.com 3 | 4 | [youtube-dl-gui.resources] 5 | file_filter = youtube_dl_gui/locale//LC_MESSAGES/youtube_dl_gui.po 6 | source_file = youtube_dl_gui/locale/en_US/LC_MESSAGES/youtube_dl_gui.po 7 | source_lang = en_US 8 | type = PO 9 | 10 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # Authors ordered by first contribution. 2 | 3 | MrS0m30n3 4 | Henrique Pereira 5 | Fironet 6 | Max Bruckner 7 | Sergey M․ 8 | Marcin Nowicki 9 | dnlsrl 10 | David Wales 11 | Nikita Bystrov 12 | nodiscc 13 | Leo Wzukw 14 | todool 15 | Yousuf 'Jay' Philips 16 | memnoth 17 | Peter Stevenson <2e0pgs@gmail.com> 18 | Swyter 19 | Sebastian Wagner 20 | saad snosi 21 | cleitonme 22 | 23 | # Generated by update-authors.sh script 24 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 5 | 6 | ## [Unreleased] 7 | 8 | ### Added 9 | - ChangeLog 10 | - 'flac' in available audio formats (#234) 11 | - French translation (#203) 12 | - Japanese translation (#226) 13 | - Italian translation (#231) 14 | - Czech translation (#233) 15 | - Automatic locale detection during first run (#235) 16 | - Man page (#259) 17 | - Option to disable youtube-dl updates (#21) 18 | 19 | ### Fixed 20 | - Bug in utils.convert_item function 21 | - Bug in downloaders.YoutubeDLDownloader (#244) 22 | 23 | ### Changed 24 | - Update timeout from 20 to 10 seconds (#244) 25 | 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | 26 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include TODO 2 | include AUTHORS 3 | include LICENSE 4 | include README.md 5 | include MANIFEST.in 6 | include ChangeLog 7 | include youtube-dl-gui.1 8 | 9 | include tests/*.py 10 | include devscripts/* 11 | include docs/* 12 | 13 | include youtube_dl_gui/data/pixmaps/icons-license 14 | 15 | recursive-include youtube_dl_gui/data *.png *.ico 16 | recursive-include youtube_dl_gui/locale *.po 17 | 18 | recursive-exclude youtube_dl_gui/locale *.mo 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Donations Badge](https://yourdonation.rocks/images/badge.svg)](https://mrs0m30n3.github.io/youtube-dl-gui/donate.html) 2 | 3 | # youtube-dlG 4 | A cross platform front-end GUI of the popular [youtube-dl](https://rg3.github.io/youtube-dl/) media downloader written in wxPython. [Supported sites](https://rg3.github.io/youtube-dl/supportedsites.html) 5 | 6 | ## Screenshots 7 | ![youtube-dl-gui main window](https://raw.githubusercontent.com/MrS0m30n3/youtube-dl-gui/gh-pages/images/ydlg_ui.gif) 8 | 9 | ## Requirements 10 | * [Python 2.7.3+](https://www.python.org/downloads) 11 | * [wxPython 3](https://wxpython.org/download.php) 12 | * [TwoDict](https://pypi.python.org/pypi/twodict) 13 | * [GNU gettext](https://www.gnu.org/software/gettext/) (to build the package) 14 | * [FFmpeg](https://ffmpeg.org/download.html) (optional, to post process video files) 15 | 16 | ## Downloads 17 | * [Source (.zip)](https://github.com/MrS0m30n3/youtube-dl-gui/archive/0.4.zip) 18 | * [Source (.tar.gz)](https://github.com/MrS0m30n3/youtube-dl-gui/archive/0.4.tar.gz) 19 | * [PyPi](https://pypi.python.org/pypi/youtube-dlg/0.4) 20 | * [Ubuntu PPA](http://ppa.launchpad.net/nilarimogard/webupd8/ubuntu/pool/main/y/youtube-dlg/) 21 | * [Arch AUR](https://aur.archlinux.org/packages/youtube-dl-gui-git/) 22 | * [Slackware SlackBuild](https://slackbuilds.org/repository/14.2/network/youtube-dl-gui/) 23 | * [openSUSE](https://software.opensuse.org/package/youtube-dl-gui) 24 | * [Windows Installer](https://github.com/MrS0m30n3/youtube-dl-gui/releases/download/0.4/youtube-dl-gui-0.4-win-setup.zip) 25 | * [Windows Portable](https://github.com/MrS0m30n3/youtube-dl-gui/releases/download/0.4/youtube-dl-gui-0.4-win-portable.zip) 26 | 27 | ## Installation 28 | 29 | ### Install From Source 30 | 1. Download & extract the source 31 | 2. Change directory into *youtube-dl-gui-0.4* 32 | 3. Run `python setup.py install` 33 | 34 | ### Install PyPi 35 | 1. Run `pip install youtube-dlg` 36 | 37 | ### Install Windows Installer 38 | 1. Download & extract the Windows installer 39 | 2. Run the `setup.exe` file 40 | 41 | ## Contributing 42 | * **Add support for new language:** See [localization howto](docs/localization_howto.md) 43 | * **Report a bug:** See [issues](https://github.com/MrS0m30n3/youtube-dl-gui/issues) 44 | 45 | ## Authors 46 | See [AUTHORS](AUTHORS) file 47 | 48 | ## License 49 | The [Public Domain License](LICENSE) 50 | 51 | ## Frequently Asked Questions 52 | See [FAQs](docs/faqs.md) file 53 | 54 | ## Thanks 55 | Thanks to everyone who contributed to this project and to [@philipzae](https://github.com/philipzae) for designing the new UI layout. 56 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | Release 0.4.1 2 | ============= 3 | * Intergrity check youtube-dl bin 4 | * Non-Windows shutdown using D-Bus instead of 'shutdown' 5 | * Custom youtube-dl format selection filters (e.g. -f best[height<=360]) 6 | * Remember list of urls after closing & re-opening 7 | * Context menu add new option "Go to file" or change the behaviour of "Open destination" 8 | * Context menu "Report Failed URL to Github" (see: #16) 9 | * Icons theme selection 10 | 11 | 12 | Features 13 | ======== 14 | * Improve playlist downloads 15 | * Mix formats option 16 | * About dialog show youtube-dl version (probably will have to create new frame) 17 | * Settings menu add "Statistics" 18 | 19 | 20 | Localization 21 | ============ 22 | * Add support for right to left languages (hebrew, arabic) 23 | * Fix paths on R2L layouts 24 | 25 | 26 | Other 27 | ===== 28 | * Re-structure package 29 | 30 | * Refactor 31 | * Review - rewrite threads communications 32 | * Add support for Python 3.* 33 | * Logging system using the Python 'logging' module 34 | * Use youtube-dl directly from python instead of using the subprocess module 35 | 36 | 37 | Probably wont add 38 | ================= 39 | * ListCtrl double click to "Rename" 40 | * Option to enable-disable items deletion from the filesystem 41 | * Add '--recode-video' to Formats tab 42 | * Auto video format detection 43 | * Change 'Warning' status to 'Finished (*)' or something similar? (see: issue #131) 44 | * Use proxy during update phase (see: issue #244) 45 | -------------------------------------------------------------------------------- /devscripts/bump-version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Author: Sotiris Papadopoulos 4 | # Last-Revision: 2017-04-17 5 | # Script to bump the version and automatically update related files 6 | # 7 | # Usage: ./bump_version.sh 8 | 9 | PACKAGE="youtube_dl_gui" 10 | 11 | FILES=`cat <" 38 | exit 1 39 | fi 40 | 41 | cd .. 42 | 43 | new_version=$1 44 | cur_version=`grep version "$PACKAGE/version.py" | cut -d"'" -f2` 45 | 46 | echo "Current version = $cur_version" 47 | echo "New version = $new_version" 48 | echo 49 | 50 | if version_le $new_version $cur_version; then 51 | echo "New version must be greater than the current version, exiting..." 52 | exit 1 53 | fi 54 | 55 | for file in $FILES; do 56 | update_version $cur_version $new_version $file 57 | done 58 | 59 | echo "Done" 60 | -------------------------------------------------------------------------------- /devscripts/check-translation.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Author: Sotiris Papadopoulos 6 | Last-Revision: 2017-04-19 7 | 8 | Script to automatically check PO files 9 | 10 | """ 11 | 12 | from __future__ import unicode_literals 13 | 14 | import os 15 | import sys 16 | import logging 17 | import argparse 18 | 19 | from time import sleep 20 | from datetime import datetime, timedelta, tzinfo 21 | 22 | try: 23 | import polib 24 | import google_translate 25 | except ImportError as error: 26 | print(error) 27 | sys.exit(1) 28 | 29 | 30 | WTIME = 2.0 # Time in seconds to wait between requests to avoid ban 31 | 32 | PACKAGE = "youtube_dl_gui" 33 | 34 | PO_FILENAME = "{}.po".format(PACKAGE) 35 | 36 | LOCALE_PATH_TMPL = os.path.join(PACKAGE, "locale", "{lang}", "LC_MESSAGES", PO_FILENAME) 37 | 38 | 39 | logging.basicConfig(level=logging.ERROR) 40 | 41 | 42 | def parse(): 43 | """Parse command line arguments.""" 44 | parser = argparse.ArgumentParser(description="Script to automatically check PO files") 45 | 46 | parser.add_argument("language", help="language of the PO file to check") 47 | 48 | parser.add_argument("-w", "--werror", action="store_true", help="treat all warning messages as errors") 49 | parser.add_argument("-o", "--only-headers", action="store_true", help="check only the PO file headers") 50 | parser.add_argument("-n", "--no-translate", action="store_true", help="do not use the translator to check 'msgstr' fields") 51 | parser.add_argument("-t", "--tlang", help="force a different language on the translator than the one given") 52 | 53 | return parser.parse_args() 54 | 55 | 56 | class UTC_Offset_Timezone(tzinfo): 57 | 58 | """Class that represents a UTC offset in the format +/-0000.""" 59 | 60 | def __init__(self, offset_string): 61 | self.offset = timedelta(seconds=UTC_Offset_Timezone.parse_offset(offset_string)) 62 | 63 | def utcoffset(self, dt): 64 | return self.offset + self.dst(dt) 65 | 66 | def dst(self, dt): 67 | return timedelta(0) 68 | 69 | @staticmethod 70 | def parse_offset(offset_string): 71 | """Parse the offset string into seconds.""" 72 | 73 | if len(offset_string) != 5: 74 | raise ValueError("Invalid length for offset string ({})".format(offset_string)) 75 | 76 | hours = offset_string[1:3] 77 | minutes = offset_string[3:5] 78 | 79 | offset = int(hours) * 3600 + int(minutes) * 60 80 | 81 | if offset_string[0] == "-": 82 | return -1 * offset 83 | 84 | return offset 85 | 86 | 87 | def parse_date(date_string): 88 | """Parse date string into an aware datetime object.""" 89 | 90 | # Just a small list with the most common timezones 91 | offset_list = [ 92 | ("JST", "0900"), 93 | ("EEST", "0300"), 94 | ("EET", "0200"), 95 | ("GMT", "0000"), 96 | ("UTC", "0000") 97 | ] 98 | 99 | # Replace all the timezones with the offset 100 | for item in offset_list: 101 | timezone, offset = item 102 | 103 | date_string = date_string.replace(timezone, offset) 104 | 105 | datetime_string = date_string[:16] 106 | offset_string = date_string[16:] 107 | 108 | naive_date = datetime.strptime(datetime_string, "%Y-%m-%d %H:%M") 109 | 110 | # Create & return an aware datetime object based on the offset 111 | return naive_date.replace(tzinfo=UTC_Offset_Timezone(offset_string)) 112 | 113 | # Print helpers 114 | 115 | def my_print(msg, char="*", value=None, exit=False): 116 | """Print 'msg', debug 'value' and exit if 'exit' is True.""" 117 | print("[{}] {}".format(char, msg)) 118 | 119 | if value is not None: 120 | print("\tvalue= \"{}\"".format(value)) 121 | 122 | if exit: 123 | sys.exit(1) 124 | 125 | def perror(msg, value=None): 126 | my_print(msg, "-", value, True) 127 | 128 | def pwarn(msg, value=None, exit=False): 129 | my_print(msg, "!", value, exit) 130 | 131 | def pinfo(msg): 132 | my_print(msg) 133 | 134 | ############################# 135 | 136 | 137 | def main(args): 138 | os.chdir("..") 139 | 140 | # setup 141 | pot_file_path = LOCALE_PATH_TMPL.format(lang="en_US") 142 | po_file_path = LOCALE_PATH_TMPL.format(lang=args.language) 143 | 144 | if not os.path.exists(pot_file_path): 145 | perror("Failed to locate POT file, exiting...", pot_file_path) 146 | 147 | if not os.path.exists(po_file_path): 148 | perror("Failed to locate PO file, exiting...", po_file_path) 149 | 150 | pot_file = polib.pofile(pot_file_path) 151 | po_file = polib.pofile(po_file_path) 152 | 153 | # check headers 154 | pinfo("Checking PO headers") 155 | 156 | pot_headers = pot_file.metadata 157 | po_headers = po_file.metadata 158 | 159 | if pot_headers["Project-Id-Version"] != po_headers["Project-Id-Version"]: 160 | pwarn("'Project-Id-Version' headers do not match", exit=args.werror) 161 | 162 | if pot_headers["POT-Creation-Date"] != po_headers["POT-Creation-Date"]: 163 | pwarn("'POT-Creation-Date' headers do not match", exit=args.werror) 164 | 165 | po_creation_date = parse_date(po_headers["POT-Creation-Date"]) 166 | po_revision_date = parse_date(po_headers["PO-Revision-Date"]) 167 | 168 | # Aware datetimes convert to UTC automatically when comparing 169 | if po_revision_date <= po_creation_date: 170 | pwarn("PO file seems outdated", exit=args.werror) 171 | 172 | if "Language" in po_headers and po_headers["Language"] != args.language: 173 | pwarn("'Language' header does not match with the given language", po_headers["Language"], args.werror) 174 | 175 | pinfo("Last-Translator: {}".format(po_headers["Last-Translator"])) 176 | 177 | # check translations 178 | if args.only_headers: 179 | sys.exit(0) 180 | 181 | pinfo("Checking translations, this might take a while...") 182 | 183 | pot_msgid = [entry.msgid for entry in pot_file] 184 | po_msgid = [entry.msgid for entry in po_file] 185 | 186 | # lists to hold reports 187 | missing_msgid = [] 188 | not_translated = [] 189 | same_msgstr = [] 190 | with_typo = [] 191 | verify_trans = [] 192 | fuzzy_trans = po_file.fuzzy_entries() 193 | 194 | for msgid in pot_msgid: 195 | if msgid not in po_msgid: 196 | missing_msgid.append(msgid) 197 | 198 | # Init translator only if the '--no-translate' flag is NOT set 199 | translator = None 200 | if not args.no_translate: 201 | translator = google_translate.GoogleTranslator(timeout=5.0, retries=2, wait_time=WTIME) 202 | 203 | # Set source language for GoogleTranslator 204 | if args.tlang is not None: 205 | src_lang = args.tlang 206 | pinfo("Forcing '{}' as the translator's source language".format(src_lang)) 207 | else: 208 | # Get a valid source language for Google 209 | # for example convert 'ar_SA' to 'ar' or 'zh_CN' to 'zh-CN' 210 | src_lang = args.language 211 | 212 | if src_lang not in translator._lang_dict: 213 | src_lang = src_lang.replace("_", "-") 214 | 215 | if src_lang not in translator._lang_dict: 216 | src_lang = src_lang.split("-")[0] 217 | 218 | # Keep entries that need further analysis using the translator 219 | further_analysis = [] 220 | 221 | for entry in po_file: 222 | if not entry.translated(): 223 | not_translated.append(entry) 224 | 225 | elif entry.msgid == entry.msgstr: 226 | same_msgstr.append(entry) 227 | 228 | else: 229 | further_analysis.append(entry) 230 | 231 | if translator is not None and further_analysis: 232 | # eta = (items_to_analyze * (WTIME + avg_ms)) - WTIME 233 | # We subtract WTIME at the end because there is no wait for the last item on the list 234 | # avg_msg = 200ms 235 | eta_seconds = (len(further_analysis) * (WTIME + 0.2)) - WTIME 236 | eta_seconds = int(round(eta_seconds)) 237 | 238 | eta = timedelta(seconds=eta_seconds) 239 | pinfo("Approximate time to check translations online: {}".format(eta)) 240 | 241 | # Pass translations as a list since GoogleTranslator can handle them 242 | words_dict = translator.get_info_dict([entry.msgstr for entry in further_analysis], "en", src_lang) 243 | 244 | for index, word_dict in enumerate(words_dict): 245 | # Get the corresponding POEntry since the words_dict does not contain those 246 | entry = further_analysis[index] 247 | 248 | if word_dict is not None: 249 | if word_dict["has_typo"]: 250 | with_typo.append(entry) 251 | 252 | if word_dict["translation"].lower() != entry.msgid.lower(): 253 | 254 | found = False 255 | 256 | # Check verbs, nouns, adverbs, etc.. 257 | for key in word_dict["extra"]: 258 | if entry.msgid.lower() in word_dict["extra"][key].keys(): 259 | found = True 260 | break 261 | 262 | if not found: 263 | verify_trans.append((entry, word_dict["translation"])) 264 | 265 | # time to report 266 | print("=" * 25 + "Report" + "=" * 25) 267 | 268 | if missing_msgid: 269 | print("Missing msgids") 270 | 271 | for msgid in missing_msgid: 272 | print(" \"{}\"".format(msgid)) 273 | 274 | if not_translated: 275 | print("Not translated") 276 | 277 | for entry in not_translated: 278 | print(" line: {} msgid: \"{}\"".format(entry.linenum, entry.msgid)) 279 | 280 | if same_msgstr: 281 | print("Same msgstr") 282 | 283 | for entry in same_msgstr: 284 | print(" line: {} msgid: \"{}\"".format(entry.linenum, entry.msgid)) 285 | 286 | if with_typo: 287 | print("With typo") 288 | 289 | for entry in with_typo: 290 | print(" line: {} msgid: \"{}\" msgstr: \"{}\"".format(entry.linenum, entry.msgid, entry.msgstr)) 291 | 292 | if verify_trans: 293 | print("Verify translation") 294 | 295 | for item in verify_trans: 296 | entry, translation = item 297 | print(" line: {} msgid: \"{}\" trans: \"{}\"".format(entry.linenum, entry.msgid, translation)) 298 | 299 | if fuzzy_trans: 300 | print("Fuzzy translations") 301 | 302 | for entry in fuzzy_trans: 303 | print(" line: {} msgid: \"{}\"".format(entry.linenum, entry.msgid)) 304 | 305 | total = len(missing_msgid) + len(not_translated) + len(same_msgstr) + len(with_typo) + len(verify_trans) + len(fuzzy_trans) 306 | 307 | print("") 308 | print("Missing msgids\t\t: {}".format(len(missing_msgid))) 309 | print("Not translated\t\t: {}".format(len(not_translated))) 310 | print("Same msgstr\t\t: {}".format(len(same_msgstr))) 311 | print("With typo\t\t: {}".format(len(with_typo))) 312 | print("Verify translation\t: {}".format(len(verify_trans))) 313 | print("Fuzzy translations\t: {}".format(len(fuzzy_trans))) 314 | print("Total\t\t\t: {}".format(total)) 315 | print("") 316 | print("Total entries\t\t: {}".format(len(po_file))) 317 | 318 | 319 | if __name__ == "__main__": 320 | try: 321 | main(parse()) 322 | except KeyboardInterrupt: 323 | print("KeyboardInterrupt") 324 | -------------------------------------------------------------------------------- /devscripts/new-locale.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | Author: Sotiris Papadopoulos 5 | Last-Revision: 2017-01-30 6 | 7 | Script to add support for a new language 8 | 9 | Usage : ./new-locale.py 10 | Example : ./new-locale.py en_US 11 | 12 | """ 13 | 14 | import os 15 | import sys 16 | import shutil 17 | 18 | 19 | PACKAGE = "youtube_dl_gui" 20 | 21 | LOCALE_PATH_TMPL = os.path.join(PACKAGE, "locale", "{lang}", "LC_MESSAGES") 22 | 23 | PO_FILE_TMPL = os.path.join("{parent_dir}", "youtube_dl_gui.po") 24 | 25 | 26 | def error(msg): 27 | print("[-]{0}".format(msg)) 28 | sys.exit(1) 29 | 30 | 31 | def output(msg): 32 | print("[*]{0}".format(msg)) 33 | 34 | 35 | def manage_directory(): 36 | """Allow script calls from the 'devscripts' dir and the package dir.""" 37 | if os.path.basename(os.getcwd()) == "devscripts": 38 | os.chdir("..") 39 | 40 | 41 | def main(lang_code): 42 | manage_directory() 43 | 44 | target_dir = LOCALE_PATH_TMPL.format(lang=lang_code) 45 | default_dir = LOCALE_PATH_TMPL.format(lang="en_US") 46 | 47 | target_po = PO_FILE_TMPL.format(parent_dir=target_dir) 48 | source_po = PO_FILE_TMPL.format(parent_dir=default_dir) 49 | 50 | if os.path.exists(target_dir): 51 | error("Locale '{0}' already exists, exiting...".format(lang_code)) 52 | 53 | output("Creating directory: '{0}'".format(target_dir)) 54 | os.makedirs(target_dir) 55 | 56 | output("Creating PO file: '{0}'".format(target_po)) 57 | shutil.copy(source_po, target_po) 58 | 59 | output("Done") 60 | 61 | 62 | if __name__ == "__main__": 63 | if len(sys.argv) == 2: 64 | main(sys.argv[1]) 65 | else: 66 | print("Usage : {0} ".format(sys.argv[0])) 67 | print("Example : {0} en_US".format(sys.argv[0])) 68 | -------------------------------------------------------------------------------- /devscripts/update-authors.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Author: Sotiris Papadopoulos 4 | # Last-Revision: 2017-04-17 5 | # Script to update the AUTHORS file 6 | # 7 | # Usage: ./update-authors.sh 8 | 9 | cd .. 10 | 11 | git log --reverse --format="%aN <%aE>" | python -c " 12 | 13 | import sys 14 | 15 | authors = set() 16 | 17 | sys.stdout.write('# Authors ordered by first contribution.\n\n') 18 | 19 | for line in sys.stdin: 20 | username, _ = line.split('<') 21 | 22 | if username not in authors: 23 | authors.add(username) 24 | sys.stdout.write(line) 25 | 26 | sys.stdout.write('\n# Generated by update-authors.sh script\n') 27 | " > AUTHORS 28 | -------------------------------------------------------------------------------- /devscripts/update-locales.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Author: Sotiris Papadopoulos 4 | # Last-Revision: 2017-01-30 5 | # Script to update all locale files and rebuild the MO files 6 | # 7 | # Usage: ./update_locales.sh 8 | 9 | PACKAGE="youtube_dl_gui" 10 | 11 | PO_FILE="$PACKAGE.po" 12 | 13 | MO_FILE="$PACKAGE.mo" 14 | 15 | 16 | cd .. 17 | 18 | VERSION=$(grep version "$PACKAGE/version.py" | cut -d"'" -f2) 19 | 20 | DIRS=$(find "$PACKAGE/locale" -mindepth 2 -maxdepth 2) 21 | 22 | 23 | echo "[*]Creating new .PO file" 24 | 25 | pygettext.py -v -o new.po "$PACKAGE/*.py" 26 | 27 | #vim new.po 28 | 29 | echo "[*]Updating old .PO files" 30 | 31 | for dir in $DIRS; do 32 | msgmerge --update --no-wrap -v "$dir/$PO_FILE" new.po 33 | 34 | # Strip empty headers 35 | sed -i "/: \\n/d" "$dir/$PO_FILE" 36 | 37 | # Upate version 38 | sed -i "s/Project-Id-Version:.*\\\n/Project-Id-Version: youtube-dlg $VERSION\\\n/g" "$dir/$PO_FILE" 39 | done 40 | 41 | echo 42 | read -p "Open files for revision?(y/n) " ch 43 | 44 | if [ $ch = 'y' ]; then 45 | for dir in $DIRS; do 46 | vim "$dir/$PO_FILE" 47 | done 48 | fi 49 | 50 | echo "[*]Building .MO files" 51 | 52 | for dir in $DIRS; do 53 | msgfmt --use-fuzzy --output-file "$dir/$MO_FILE" "$dir/$PO_FILE" 54 | done 55 | 56 | echo "[*]Done" 57 | -------------------------------------------------------------------------------- /docs/faqs.md: -------------------------------------------------------------------------------- 1 | # Frequently Asked Questions 2 | 3 | * **How can I make sure I'm getting the best quality possible?**: 4 | Don't force any output format, leave the **default** format selected in the main window (*default = highest quality*). 5 | Read https://github.com/rg3/youtube-dl#format-selection for more details. 6 | 7 | * **Post processing takes too long**: 8 | There 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). 9 | 10 | * **The website I'm trying to download from is not supported**: 11 | Youtube-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). 12 | 13 | * **How do I change the naming pattern for downloaded files?**: 14 | You 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). 15 | 16 | * **When is the next release coming?**: 17 | Youtube-dl-gui does not have a release schedule, next release will come when it's ready. 18 | 19 | * **How can i log the youtube-dl's debug output?**: 20 | Just go to Options>Extra tab and enable the "Debug youtube-dl" option. 21 | 22 | * **I don't see my language in the available subtitles languages**: 23 | You 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). 24 | 25 | * **I'm on OS-X and i get 'No module named wx' error**: 26 | You need to install wxPython. If you have [homebrew](https://brew.sh/) installed you can just run: `brew install wxpython` 27 | 28 | * **Is there an option to change the maximum parallel downloads?**: 29 | You 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. 30 | 31 | settings.json file location: 32 | * Windows: `%appdata%\youtube-dlg\settings.json` 33 | * Linux: `~/.config/youtube-dlg/settings.json` 34 | 35 | * **Not all formats reported by youtube-dl '-F' are available in youtube-dl-gui**: 36 | Unfortunately it is not possible to support all the video formats that youtube-dl provides. If you want to use a "custom" 37 | format you can follow this steps: 38 | 39 | 1. Set the download format to "default" 40 | 2. Go to Options>Extra tab 41 | 3. Add `-f your_custom_format` in the commands box 42 | 4. Download your video as you would normally do 43 | 44 | * **How can i use a youtube-dl option that's not available in youtube-dl-gui?**: 45 | You can add extra youtube-dl command line options in the commands box under the Options>Extra tab. 46 | 47 | * **Can i download only the subtitles file?**: 48 | If 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. 49 | 50 | * **I'm using the HLS downloader and i don't see any progress report**: 51 | That'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). 52 | 53 | * **I'm using the HLS downloader and the 'stop' button won't work**: 54 | That'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). 55 | 56 | * **Is it possible to use a youtube-dl version other than the official one?**: 57 | You 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/". 58 | 59 | * **The program crashes frequently or pretends to succeed downloading the files (Windows)**: 60 | Follow [youtube-dl instructions](https://github.com/rg3/youtube-dl#the-exe-throws-an-error-due-to-missing-msvcr100dll) on updating your Visual C++ Redistributable. 61 | 62 | * **How come 1080p YouTube downloads are not working?**: 63 | Try disabling native HLS. You can disable the "Prefer Native HLS" option under the Options>Extra tab. 64 | -------------------------------------------------------------------------------- /docs/localization_howto.md: -------------------------------------------------------------------------------- 1 | # Localization Guide - [Transifex](https://www.transifex.com/youtube-dl-gui/public/) 2 | 3 | ## 🔴 DISCLAIMER 4 | **By sending a translation you agree to publish your work under the [UNLICENSE](https://unlicense.org/) license!** 5 | 6 | ## Contents 7 | * [Translators](localization_howto.md#translators) 8 | * [Testers](localization_howto.md#testers) 9 | * [Devs](localization_howto.md#devs) 10 | * [FAQs](localization_howto.md#faqs) 11 | 12 | ## Translators 13 | 14 | ### Requirements 15 | * A modern browser 16 | * A Transifex account, [sign-up](https://www.transifex.com/signup/) 17 | 18 | ### Notes 19 | * If your language is currently not supported you can make a request for new language support. 20 | * When you request support for a new language, the language code should be in the format **en_US** and NOT just **en**. 21 | * Variables such as **{0}**, **{1}**, **{dir}**, **{0:.1f}**, etc should be copied exactly as they appear in the translation box. 22 | * Variables represent a word that will be replaced by real data (name, number, etc). 23 | * Variables can be moved around the string in order to make the most logical translation. 24 | * When new strings for translation are available you will get inbox notifications. 25 | * For help you can leave a comment with @username or send a direct message to one of the maintainers. 26 | * Maintainer usernames are: `MrS0m30n3`, `nodiscc` 27 | 28 | ### Gettings Started 29 | 1. [Sign-in](https://www.transifex.com/signin/) to Transifex 30 | 2. [Join a translation team](https://docs.transifex.com/getting-started/translators#joining-a-translation-team) 31 | 3. [Start translating using the web editor](https://docs.transifex.com/translation/translating-with-the-web-editor) 32 | 33 | ### Help 34 | * [Translators getting started](https://docs.transifex.com/getting-started/translators) 35 | * [Translating offline](https://docs.transifex.com/translation/offline) 36 | * [Using the glossary](https://docs.transifex.com/translation/using-the-glossary) 37 | * For help you can [leave a comment or open an issue](https://docs.transifex.com/translation/tools-in-the-editor#comments-and-issues) 38 | 39 | ## Testers 40 | 41 | ### Requirements 42 | * Check [project requirements](../README.md#requirements) 43 | * [Git](https://git-scm.com/downloads) 44 | * [Transifex CLI client](https://docs.transifex.com/client/installing-the-client) 45 | * Some kind of text editor to edit some code (notepad++, nano, etc are sufficient) 46 | * A Transifex account, [sign-up](https://www.transifex.com/signup/) 47 | 48 | ### Notes 49 | * The instructions below assume basic knowledge of the command line (OS independent). 50 | * The **language code** being used should be in the format `_` (e.g. en_US). 51 | * You can locally edit the translation file (PO) by opening it using a simple editor and editing the **msgstr** fields. 52 | * You can find the translation file (PO) after downloading it under the 53 | `youtube_dl_gui/locale//LC_MESSAGES/` directory. 54 | * In order to get the translations from Transifex **your account needs permissions to access the project**. 55 | 56 | ### Getting Started 57 | 1. Open a terminal 58 | 2. Test that Git works: `git --version` 59 | 3. Test that Transifex CLI client works: `tx --version` 60 | 4. Clone upstream using Git: `git clone https://github.com/MrS0m30n3/youtube-dl-gui` 61 | 5. Change to project directory: `cd youtube-dl-gui` 62 | 6. Pull the translation you want to test from Transifex (**Auth needed**): `tx pull --force -l ` 63 | 7. Make the language appear under **Options>General** tab (only for new languages): 64 | 1. Open the **optionsframe.py** under the **youtube_dl_gui** directory 65 | 2. Search for the **LOCALE_NAMES** attribute 66 | 3. Add the new language to it (in our example `('el_GR', 'Greek'),`) 67 | 4. Don't forget to save your changes 68 | 69 | ```python 70 | LOCALE_NAMES = twodict([ 71 | + ('el_GR', 'Greek'), # language_code, language_name 72 | ('ar_SA', 'Arabic'), 73 | ('cs_CZ', 'Czech'), 74 | ... 75 | ``` 76 | 8. Build the binary translation files (MO): `python setup.py build_trans` 77 | 9. Test the translations by running youtube-dl-gui and selecting your language: `python -m youtube_dl_gui` 78 | 10. Make changes locally in your translation file (PO) and go back to step 8 to test them 79 | 80 | ### Help 81 | * [ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) 82 | * [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) 83 | * [Command line basics Linux](https://lifehacker.com/5633909/who-needs-a-mouse-learn-to-use-the-command-line-for-almost-anything) 84 | 85 | ## Devs 86 | 87 | ### Requirements 88 | * See [Testers](localization_howto.md#testers) requirements 89 | 90 | ### Notes 91 | * Read [Testers](localization_howto.md#testers) notes first. 92 | * Binary translation files (MO) are ignored and you should not push them upstream. 93 | * Don't forget to update the [ChangeLog](../ChangeLog) after adding a new language. 94 | * You can gather all extra requirements below using **pip**. 95 | 96 | ### Getting Started 97 | 98 | #### Add a new language under Options>General tab 99 | 1. Open the **optionsframe.py** file 100 | 2. Search for the **LOCALE_NAMES** attribute 101 | 3. Add the new language to it and make sure to **sort alphabetically** based on the language name 102 | 103 | ```python 104 | LOCALE_NAMES = twodict([ 105 | ('en_US', 'English'), 106 | ('fr_FR', 'French'), 107 | + ('el_GR', 'Greek'), 108 | ('it_IT', 'Italian'), 109 | ... 110 | ``` 111 | 112 | #### Build the binary translation files (MO) 113 | 1. Just run the setup script: `python setup.py build_trans` 114 | 115 | #### Automatically check translations using Google translate (Requires: polib & doodle_translate) 116 | 1. Change directory to `devscripts` 117 | 2. Run the check script: `python check-translation.py ` 118 | 119 | #### Get translations from Transifex (Requires: Permissions to access project) 120 | * Pull everything: `tx pull -a` 121 | * Pull reviewed: `tx pull --mode reviewed -a` 122 | * Pull everything (force): `tx pull -a -f` 123 | * Pull specific language: `tx pull -l ` 124 | * Pull only completed translations (100%): `tx pull -a --minimum-perc=100` 125 | 126 | #### Update source strings (Only Maintainers, Requires: python-gettext) 127 | 1. Change directory to `devscripts` 128 | 2. Run the `update-locales.sh` script (also builds MO files) 129 | 3. Push changes to Transifex: `tx push --source --translations` 130 | 131 | #### Add support for new language locally (DEPRECATED, ONLY TESTING) 132 | 1. Change directory to `devscripts` 133 | 2. Run the new locale script: `python new-locale.py ` 134 | 135 | ### Help 136 | * [ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) 137 | * [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) 138 | * [PO file headers](https://www.gnu.org/software/gettext/manual/html_node/Header-Entry.html) 139 | * [GNU gettext manual](https://www.gnu.org/software/gettext/manual/html_node/index.html#SEC_Contents) 140 | * Transifex [user roles](https://docs.transifex.com/teams/understanding-user-roles) 141 | * Transifex [CLI client introduction](https://docs.transifex.com/client/introduction) 142 | 143 | ## FAQs 144 | 145 | * **Translations unnecessarily having country codes?**: 146 | Some 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. 147 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | # -*- coding: utf-8 -*- 3 | 4 | """Youtube-dlg setup file. 5 | 6 | Examples: 7 | Windows:: 8 | 9 | python setup.py py2exe 10 | 11 | Linux:: 12 | 13 | python setup.py install 14 | 15 | Build source distribution:: 16 | 17 | python setup.py sdist 18 | 19 | Build platform distribution:: 20 | 21 | python setup.py bdist 22 | 23 | Build the translations:: 24 | 25 | python setup.py build_trans 26 | 27 | Build with updates disabled:: 28 | 29 | python setup.py build --no-updates 30 | 31 | Requirements: 32 | 33 | * GNU gettext utilities 34 | 35 | Notes: 36 | If you get 'TypeError: decoding Unicode is not supported' when you run 37 | py2exe then apply the following patch:: 38 | 39 | http://sourceforge.net/p/py2exe/patches/28/ 40 | 41 | Basic steps of the setup:: 42 | 43 | * Run pre-build tasks 44 | * Call setup handler based on OS & options 45 | * Set up hicolor icons (if supported by platform) 46 | * Set up fallback pixmaps icon (if supported by platform) 47 | * Set up package level pixmaps icons (*.png) 48 | * Set up package level i18n files (*.mo) 49 | * Set up scripts (executables) (if supported by platform) 50 | * Run setup 51 | 52 | """ 53 | 54 | from distutils import cmd, log 55 | from distutils.core import setup 56 | from distutils.command.build import build 57 | 58 | import os 59 | import sys 60 | import glob 61 | from shutil import copyfile 62 | from subprocess import call 63 | 64 | PY2EXE = len(sys.argv) >= 2 and sys.argv[1] == "py2exe" 65 | 66 | if PY2EXE: 67 | try: 68 | import py2exe 69 | except ImportError as error: 70 | print(error) 71 | sys.exit(1) 72 | 73 | from youtube_dl_gui import ( 74 | __author__, 75 | __appname__, 76 | __contact__, 77 | __version__, 78 | __license__, 79 | __projecturl__, 80 | __description__, 81 | __packagename__, 82 | __descriptionfull__ 83 | ) 84 | 85 | # Setup can not handle unicode 86 | __packagename__ = str(__packagename__) 87 | 88 | 89 | def on_windows(): 90 | """Returns True if OS is Windows.""" 91 | return os.name == "nt" 92 | 93 | 94 | class BuildBin(cmd.Command): 95 | 96 | description = "build the youtube-dl-gui binary file" 97 | user_options = [] 98 | 99 | def initialize_options(self): 100 | self.scripts_dir = None 101 | 102 | def finalize_options(self): 103 | self.scripts_dir = os.path.join("build", "_scripts") 104 | 105 | def run(self): 106 | if not os.path.exists(self.scripts_dir): 107 | os.makedirs(self.scripts_dir) 108 | 109 | copyfile(os.path.join(__packagename__, "__main__.py"), 110 | os.path.join(self.scripts_dir, "youtube-dl-gui")) 111 | 112 | 113 | class BuildTranslations(cmd.Command): 114 | 115 | description = "build the translation files" 116 | user_options = [] 117 | 118 | def initialize_options(self): 119 | self.exec_name = None 120 | self.search_pattern = None 121 | 122 | def finalize_options(self): 123 | if on_windows(): 124 | self.exec_name = "msgfmt.exe" 125 | else: 126 | self.exec_name = "msgfmt" 127 | 128 | self.search_pattern = os.path.join(__packagename__, "locale", "*", "LC_MESSAGES", "youtube_dl_gui.po") 129 | 130 | def run(self): 131 | for po_file in glob.glob(self.search_pattern): 132 | mo_file = po_file.replace(".po", ".mo") 133 | 134 | try: 135 | log.info("building MO file for '{}'".format(po_file)) 136 | call([self.exec_name, "-o", mo_file, po_file]) 137 | except OSError: 138 | log.error("could not locate file '{}', exiting...".format(self.exec_name)) 139 | sys.exit(1) 140 | 141 | 142 | class Build(build): 143 | 144 | """Overwrite the default 'build' behaviour.""" 145 | 146 | sub_commands = [ 147 | ("build_bin", None), 148 | ("build_trans", None) 149 | ] + build.sub_commands 150 | 151 | build.user_options.append(("no-updates", None, "build with updates disabled")) 152 | 153 | def initialize_options(self): 154 | build.initialize_options(self) 155 | self.no_updates = None 156 | 157 | def run(self): 158 | build.run(self) 159 | 160 | if self.no_updates: 161 | self.__disable_updates() 162 | 163 | def __disable_updates(self): 164 | lib_dir = os.path.join(self.build_lib, __packagename__) 165 | target_file = "optionsmanager.py" 166 | 167 | # Options file should be available from previous build commands 168 | optionsfile = os.path.join(lib_dir, target_file) 169 | data = None 170 | 171 | with open(optionsfile, "r") as input_file: 172 | data = input_file.readlines() 173 | 174 | if data is None: 175 | log.error("building with updates disabled failed!") 176 | sys.exit(1) 177 | 178 | for index, line in enumerate(data): 179 | if "'disable_update': False" in line: 180 | log.info("disabling updates") 181 | data[index] = line.replace("False", "True") 182 | break 183 | 184 | with open(optionsfile, "w") as output_file: 185 | output_file.writelines(data) 186 | 187 | 188 | # Overwrite cmds 189 | cmdclass = { 190 | "build": Build, 191 | "build_bin": BuildBin, 192 | "build_trans": BuildTranslations 193 | } 194 | 195 | 196 | def linux_setup(): 197 | scripts = [] 198 | data_files = [] 199 | package_data = {} 200 | 201 | # Add hicolor icons 202 | for path in glob.glob("youtube_dl_gui/data/icons/hicolor/*x*"): 203 | size = os.path.basename(path) 204 | 205 | dst = "share/icons/hicolor/{size}/apps".format(size=size) 206 | src = "{icon_path}/apps/youtube-dl-gui.png".format(icon_path=path) 207 | 208 | data_files.append((dst, [src])) 209 | 210 | # Add fallback icon, see issue #14 211 | data_files.append( 212 | ("share/pixmaps", ["youtube_dl_gui/data/pixmaps/youtube-dl-gui.png"]) 213 | ) 214 | 215 | # Add man page 216 | data_files.append( 217 | ("share/man/man1", ["youtube-dl-gui.1"]) 218 | ) 219 | 220 | # Add pixmaps icons (*.png) & i18n files 221 | package_data[__packagename__] = [ 222 | "data/pixmaps/*.png", 223 | "locale/*/LC_MESSAGES/*.mo" 224 | ] 225 | 226 | # Add scripts 227 | scripts.append("build/_scripts/youtube-dl-gui") 228 | 229 | setup_params = { 230 | "scripts": scripts, 231 | "data_files": data_files, 232 | "package_data": package_data 233 | } 234 | 235 | return setup_params 236 | 237 | 238 | def windows_setup(): 239 | def normal_setup(): 240 | package_data = {} 241 | 242 | # Add pixmaps icons (*.png) & i18n files 243 | package_data[__packagename__] = [ 244 | "data\\pixmaps\\*.png", 245 | "locale\\*\\LC_MESSAGES\\*.mo" 246 | ] 247 | 248 | setup_params = { 249 | "package_data": package_data 250 | } 251 | 252 | return setup_params 253 | 254 | def py2exe_setup(): 255 | windows = [] 256 | data_files = [] 257 | 258 | # py2exe dependencies & options 259 | # TODO change directory for ffmpeg.exe & ffprobe.exe 260 | dependencies = [ 261 | "C:\\Windows\\System32\\ffmpeg.exe", 262 | "C:\\Windows\\System32\\ffprobe.exe", 263 | "C:\\python27\\DLLs\\MSVCP90.dll" 264 | ] 265 | 266 | options = { 267 | "includes": ["wx.lib.pubsub.*", 268 | "wx.lib.pubsub.core.*", 269 | "wx.lib.pubsub.core.arg1.*"] 270 | } 271 | ############################################# 272 | 273 | # Add py2exe deps & pixmaps icons (*.png) 274 | data_files.extend([ 275 | ("", dependencies), 276 | ("data\\pixmaps", glob.glob("youtube_dl_gui\\data\\pixmaps\\*.png")), 277 | ]) 278 | 279 | # We have to manually add the translation files since py2exe cant do it 280 | for lang in os.listdir("youtube_dl_gui\\locale"): 281 | dst = os.path.join("locale", lang, "LC_MESSAGES") 282 | src = os.path.join("youtube_dl_gui", dst, "youtube_dl_gui.mo") 283 | 284 | data_files.append((dst, [src])) 285 | 286 | # Add GUI executable details 287 | windows.append({ 288 | "script": "build\\_scripts\\youtube-dl-gui", 289 | "icon_resources": [(0, "youtube_dl_gui\\data\\pixmaps\\youtube-dl-gui.ico")] 290 | }) 291 | 292 | setup_params = { 293 | "windows": windows, 294 | "data_files": data_files, 295 | "options": {"py2exe": options} 296 | } 297 | 298 | return setup_params 299 | 300 | if PY2EXE: 301 | return py2exe_setup() 302 | 303 | return normal_setup() 304 | 305 | 306 | if on_windows(): 307 | params = windows_setup() 308 | else: 309 | params = linux_setup() 310 | 311 | setup( 312 | author = __author__, 313 | name = __appname__, 314 | version = __version__, 315 | license = __license__, 316 | author_email = __contact__, 317 | url = __projecturl__, 318 | description = __description__, 319 | long_description = __descriptionfull__, 320 | packages = [__packagename__], 321 | cmdclass = cmdclass, 322 | 323 | **params 324 | ) 325 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrS0m30n3/youtube-dl-gui/c5c18e55cb7e04fb6d6d8e64024a7dbac1f6b431/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_dlist.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | 4 | """Contains test cases for the DownloadList object.""" 5 | 6 | from __future__ import unicode_literals 7 | 8 | import sys 9 | import os.path 10 | import unittest 11 | 12 | PATH = os.path.realpath(os.path.abspath(__file__)) 13 | sys.path.insert(0, os.path.dirname(os.path.dirname(PATH))) 14 | 15 | try: 16 | import mock 17 | from youtube_dl_gui.downloadmanager import DownloadList, synchronized 18 | except ImportError as error: 19 | print error 20 | sys.exit(1) 21 | 22 | 23 | class TestInit(unittest.TestCase): 24 | 25 | """Test case for the DownloadList init.""" 26 | 27 | def test_init(self): 28 | mocks = [mock.Mock(object_id=0), mock.Mock(object_id=1)] 29 | 30 | dlist = DownloadList(mocks) 31 | self.assertEqual(dlist._items_list, [0, 1]) 32 | self.assertEqual(dlist._items_dict, {0: mocks[0], 1: mocks[1]}) 33 | 34 | def test_init_empty(self): 35 | dlist = DownloadList() 36 | self.assertEqual(dlist._items_list, []) 37 | self.assertEqual(dlist._items_dict, {}) 38 | 39 | def test_init_invalid_args(self): 40 | self.assertRaises(AssertionError, DownloadList, {}) 41 | self.assertRaises(AssertionError, DownloadList, ()) 42 | self.assertRaises(AssertionError, DownloadList, False) 43 | 44 | 45 | class TestInsert(unittest.TestCase): 46 | 47 | """Test case for the DownloadList insert method.""" 48 | 49 | def test_insert(self): 50 | mock_ditem = mock.Mock(object_id=0) 51 | 52 | dlist = DownloadList() 53 | dlist.insert(mock_ditem) 54 | 55 | self.assertEqual(dlist._items_list, [0]) 56 | self.assertEqual(dlist._items_dict, {0: mock_ditem}) 57 | 58 | 59 | class TestRemove(unittest.TestCase): 60 | 61 | """Test case for the DownloadList remove method.""" 62 | 63 | def setUp(self): 64 | self.mocks = [mock.Mock(object_id=0), mock.Mock(object_id=1), mock.Mock(object_id=2)] 65 | self.dlist = DownloadList(self.mocks) 66 | 67 | def test_remove(self): 68 | self.assertTrue(self.dlist.remove(1)) 69 | 70 | self.assertEqual(self.dlist._items_list, [0, 2]) 71 | self.assertEqual(self.dlist._items_dict, {0: self.mocks[0], 2: self.mocks[2]}) 72 | 73 | def test_remove_not_exist(self): 74 | self.assertRaises(KeyError, self.dlist.remove, 3) 75 | 76 | def test_remove_active(self): 77 | self.mocks[1].stage = "Active" 78 | 79 | self.assertFalse(self.dlist.remove(1)) 80 | self.assertEqual(self.dlist._items_list, [0, 1, 2]) 81 | self.assertEqual(self.dlist._items_dict, {0: self.mocks[0], 1: self.mocks[1], 2: self.mocks[2]}) 82 | 83 | 84 | class TestFetchNext(unittest.TestCase): 85 | 86 | """Test case for the DownloadList fetch_next method.""" 87 | 88 | def test_fetch_next(self): 89 | items_count = 3 90 | 91 | mocks = [mock.Mock(object_id=i, stage="Queued") for i in range(items_count)] 92 | 93 | dlist = DownloadList(mocks) 94 | 95 | for i in range(items_count): 96 | self.assertEqual(dlist.fetch_next(), mocks[i]) 97 | mocks[i].stage = "Active" 98 | 99 | self.assertIsNone(dlist.fetch_next()) 100 | 101 | for i in range(items_count): 102 | mocks[i].stage = "Completed" 103 | 104 | self.assertIsNone(dlist.fetch_next()) 105 | 106 | mocks[1].stage = "Queued" # Re-queue item 107 | self.assertEqual(dlist.fetch_next(), mocks[1]) 108 | 109 | def test_fetch_next_empty_list(self): 110 | dlist = DownloadList() 111 | self.assertIsNone(dlist.fetch_next()) 112 | 113 | 114 | class TestMoveUp(unittest.TestCase): 115 | 116 | """Test case for the DownloadList move_up method.""" 117 | 118 | def setUp(self): 119 | mocks = [mock.Mock(object_id=i, stage="Queued") for i in range(3)] 120 | self.dlist = DownloadList(mocks) 121 | 122 | def test_move_up(self): 123 | self.assertTrue(self.dlist.move_up(1)) 124 | self.assertEqual(self.dlist._items_list, [1, 0, 2]) 125 | 126 | def test_move_up_already_on_top(self): 127 | self.assertFalse(self.dlist.move_up(0)) 128 | self.assertEqual(self.dlist._items_list, [0, 1, 2]) 129 | 130 | def test_move_up_not_exist(self): 131 | self.assertRaises(ValueError, self.dlist.move_up, 666) 132 | 133 | 134 | class TestMoveDown(unittest.TestCase): 135 | 136 | """Test case for the DownloadList move_down method.""" 137 | 138 | def setUp(self): 139 | mocks = [mock.Mock(object_id=i, stage="Queued") for i in range(3)] 140 | self.dlist = DownloadList(mocks) 141 | 142 | def test_move_down(self): 143 | self.assertTrue(self.dlist.move_down(1)) 144 | self.assertEqual(self.dlist._items_list, [0, 2, 1]) 145 | 146 | def test_move_down_already_on_bottom(self): 147 | self.assertFalse(self.dlist.move_down(2)) 148 | self.assertEqual(self.dlist._items_list, [0, 1, 2]) 149 | 150 | def test_move_down_not_exist(self): 151 | self.assertRaises(ValueError, self.dlist.move_down, 666) 152 | 153 | 154 | class TestGetItem(unittest.TestCase): 155 | 156 | """Test case for the DownloadList get_item method.""" 157 | 158 | def test_get_item(self): 159 | mocks = [mock.Mock(object_id=i) for i in range(3)] 160 | dlist = DownloadList(mocks) 161 | 162 | self.assertEqual(dlist.get_item(0), mocks[0]) 163 | self.assertEqual(dlist.get_item(2), mocks[2]) 164 | 165 | def test_get_item_not_exist(self): 166 | dlist = DownloadList() 167 | self.assertRaises(KeyError, dlist.get_item, 0) 168 | 169 | 170 | class TestGetLength(unittest.TestCase): 171 | 172 | """Test case for the DownloadList __len__ method.""" 173 | 174 | def test_get_length(self): 175 | dlist = DownloadList([mock.Mock(), mock.Mock()]) 176 | self.assertEqual(len(dlist), 2) 177 | 178 | def test_get_length_empty_list(self): 179 | dlist = DownloadList() 180 | self.assertEqual(len(dlist), 0) 181 | 182 | 183 | class TestHasItem(unittest.TestCase): 184 | 185 | """Test case for the DownloadList has_item method.""" 186 | 187 | def setUp(self): 188 | mock_ditem = mock.Mock(object_id=1337) 189 | self.dlist = DownloadList([mock_ditem]) 190 | 191 | def test_has_item_true(self): 192 | self.assertTrue(self.dlist.has_item(1337)) 193 | 194 | def test_has_item_false(self): 195 | self.assertFalse(self.dlist.has_item(1000)) 196 | 197 | 198 | class TestGetItems(unittest.TestCase): 199 | 200 | """Test case for the DownloadList get_items method.""" 201 | 202 | def test_get_items(self): 203 | mocks = [mock.Mock() for _ in range(3)] 204 | dlist = DownloadList(mocks) 205 | 206 | self.assertEqual(dlist.get_items(), mocks) 207 | 208 | def test_get_items_empty_list(self): 209 | dlist = DownloadList() 210 | self.assertEqual(dlist.get_items(), []) 211 | 212 | 213 | class TestClear(unittest.TestCase): 214 | 215 | """Test case for the DownloadList clear method.""" 216 | 217 | def test_clear(self): 218 | dlist = DownloadList([mock.Mock() for _ in range(3)]) 219 | 220 | self.assertEqual(len(dlist), 3) 221 | dlist.clear() 222 | self.assertEqual(len(dlist), 0) 223 | 224 | 225 | class TestChangeStage(unittest.TestCase): 226 | 227 | """Test case for the DownloadList change_stage method.""" 228 | 229 | def setUp(self): 230 | self.mocks = [mock.Mock(object_id=i, stage="Queued") for i in range(3)] 231 | self.dlist = DownloadList(self.mocks) 232 | 233 | def test_change_stage(self): 234 | self.dlist.change_stage(0, "Active") 235 | self.assertEqual(self.mocks[0].stage, "Active") 236 | 237 | def test_change_stage_id_not_exist(self): 238 | self.assertRaises(KeyError, self.dlist.change_stage, 3, "Active") 239 | 240 | 241 | class TestIndex(unittest.TestCase): 242 | 243 | """Test case for the DownloadList index method.""" 244 | 245 | def setUp(self): 246 | self.mocks = [mock.Mock(object_id=i) for i in range(3)] 247 | self.dlist = DownloadList(self.mocks) 248 | 249 | def test_index(self): 250 | self.assertEqual(self.dlist.index(2), 2) 251 | 252 | def test_index_not_exist(self): 253 | self.assertEqual(self.dlist.index(3), -1) 254 | 255 | 256 | class TestSynchronizeDecorator(unittest.TestCase): 257 | 258 | def test_synchronize(self): 259 | mock_func = mock.Mock() 260 | mock_lock = mock.Mock() 261 | 262 | decorated_func = synchronized(mock_lock)(mock_func) 263 | 264 | self.assertEqual(decorated_func(1, a=2), mock_func.return_value) 265 | 266 | mock_func.assert_called_once_with(1, a=2) 267 | mock_lock.acquire.assert_called_once() 268 | mock_lock.release.assert_called_once() 269 | 270 | 271 | def main(): 272 | unittest.main() 273 | 274 | 275 | if __name__ == '__main__': 276 | main() 277 | -------------------------------------------------------------------------------- /tests/test_parsers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | 4 | """Contains test cases for the parsers module.""" 5 | 6 | from __future__ import unicode_literals 7 | 8 | import sys 9 | import os.path 10 | import unittest 11 | 12 | PATH = os.path.realpath(os.path.abspath(__file__)) 13 | sys.path.insert(0, os.path.dirname(os.path.dirname(PATH))) 14 | 15 | try: 16 | from youtube_dl_gui.parsers import OptionsParser 17 | except ImportError as error: 18 | print error 19 | sys.exit(1) 20 | 21 | 22 | class TestParse(unittest.TestCase): 23 | 24 | """Test case for OptionsParser parse method.""" 25 | 26 | def setUp(self): 27 | # Create the options_dict based on the OptionHolder 28 | # items inside the OptionsParser object 29 | self.options_dict = {item.name:item.default_value for item in OptionsParser()._ydl_options} 30 | 31 | # Add extra options used by the OptionsParser.parse method 32 | self.options_dict["save_path"] = "/home/user/Workplace/test/youtube" 33 | self.options_dict["cmd_args"] = "" 34 | self.options_dict["output_format"] = 1 # Title 35 | self.options_dict["second_video_format"] = "0" 36 | self.options_dict["min_filesize_unit"] = "" 37 | self.options_dict["max_filesize_unit"] = "" 38 | 39 | def check_options_parse(self, expected_options): 40 | options_parser = OptionsParser() 41 | 42 | self.assertItemsEqual(options_parser.parse(self.options_dict), expected_options) 43 | 44 | def test_parse_to_audio_requirement_bug(self): 45 | """Test case for the 'to_audio' requirement.""" 46 | 47 | self.options_dict["audio_quality"] = "9" 48 | self.options_dict["audio_format"] = "mp3" 49 | self.options_dict["embed_thumbnail"] = True 50 | 51 | expected_cmd_list = ["--newline", 52 | "-x", 53 | "--audio-format", 54 | "mp3", 55 | "--embed-thumbnail", 56 | "--audio-quality", 57 | "9", 58 | "-o", 59 | "/home/user/Workplace/test/youtube/%(title)s.%(ext)s"] 60 | 61 | self.check_options_parse(expected_cmd_list) 62 | 63 | # Setting 'to_audio' to True should return the same results 64 | # since the '-x' flag is already set on audio extraction 65 | self.options_dict["to_audio"] = True 66 | 67 | self.check_options_parse(expected_cmd_list) 68 | 69 | def test_parse_cmd_args_with_quotes(self): 70 | """Test the youtube-dl cmd line args parsing when quotes are presented. 71 | 72 | See: https://github.com/MrS0m30n3/youtube-dl-gui/issues/54 73 | 74 | """ 75 | 76 | self.options_dict["video_format"] = "mp4" 77 | 78 | # Test with three quoted 'cmd_args' 79 | self.options_dict["cmd_args"] = "--recode-video mkv --postprocessor-args \"-codec copy -report\"" 80 | 81 | expected_cmd_list = ["--newline", 82 | "-f", 83 | "mp4", 84 | "-o", 85 | "/home/user/Workplace/test/youtube/%(title)s.%(ext)s", 86 | "--recode-video", 87 | "mkv", 88 | "--postprocessor-args", 89 | "-codec copy -report"] 90 | 91 | self.check_options_parse(expected_cmd_list) 92 | 93 | 94 | # Test with two quoted 'cmd_args' 95 | self.options_dict["cmd_args"] = "--postprocessor-args \"-y -report\"" 96 | 97 | expected_cmd_list = ["--newline", 98 | "-f", 99 | "mp4", 100 | "-o", 101 | "/home/user/Workplace/test/youtube/%(title)s.%(ext)s", 102 | "--postprocessor-args", 103 | "-y -report"] 104 | 105 | self.check_options_parse(expected_cmd_list) 106 | 107 | 108 | # Test with one quoted 'cmd_arg' followed by other cmd line args 109 | self.options_dict["cmd_args"] = "--postprocessor-args \"-y\" -v" 110 | 111 | expected_cmd_list = ["--newline", 112 | "-f", 113 | "mp4", 114 | "-o", 115 | "/home/user/Workplace/test/youtube/%(title)s.%(ext)s", 116 | "--postprocessor-args", 117 | "-y", 118 | "-v"] 119 | 120 | self.check_options_parse(expected_cmd_list) 121 | 122 | 123 | # Test the example presented in issue #54 124 | self.options_dict["cmd_args"] = "-f \"(mp4)[width<1300]\"" 125 | self.options_dict["video_format"] = "0" # Set video format to 'default' 126 | 127 | expected_cmd_list = ["--newline", 128 | "-o", 129 | "/home/user/Workplace/test/youtube/%(title)s.%(ext)s", 130 | "-f", 131 | "(mp4)[width<1300]"] 132 | 133 | self.check_options_parse(expected_cmd_list) 134 | 135 | 136 | def main(): 137 | unittest.main() 138 | 139 | 140 | if __name__ == '__main__': 141 | main() 142 | -------------------------------------------------------------------------------- /tests/test_utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """Contains test cases for the utils.py module.""" 5 | 6 | from __future__ import unicode_literals 7 | 8 | import sys 9 | import os.path 10 | import unittest 11 | 12 | PATH = os.path.realpath(os.path.abspath(__file__)) 13 | sys.path.insert(0, os.path.dirname(os.path.dirname(PATH))) 14 | 15 | try: 16 | import mock 17 | 18 | from youtube_dl_gui import utils 19 | except ImportError as error: 20 | print error 21 | sys.exit(1) 22 | 23 | 24 | class TestToBytes(unittest.TestCase): 25 | 26 | """Test case for the to_bytes method.""" 27 | 28 | def test_to_bytes_bytes(self): 29 | self.assertEqual(utils.to_bytes("596.00B"), 596.00) 30 | self.assertEqual(utils.to_bytes("133.55B"), 133.55) 31 | 32 | def test_to_bytes_kilobytes(self): 33 | self.assertEqual(utils.to_bytes("1.00KiB"), 1024.00) 34 | self.assertEqual(utils.to_bytes("5.55KiB"), 5683.20) 35 | 36 | def test_to_bytes_megabytes(self): 37 | self.assertEqual(utils.to_bytes("13.64MiB"), 14302576.64) 38 | self.assertEqual(utils.to_bytes("1.00MiB"), 1048576.00) 39 | 40 | def test_to_bytes_gigabytes(self): 41 | self.assertEqual(utils.to_bytes("1.00GiB"), 1073741824.00) 42 | self.assertEqual(utils.to_bytes("1.55GiB"), 1664299827.20) 43 | 44 | def test_to_bytes_terabytes(self): 45 | self.assertEqual(utils.to_bytes("1.00TiB"), 1099511627776.00) 46 | 47 | 48 | class TestFormatBytes(unittest.TestCase): 49 | 50 | """Test case for the format_bytes method.""" 51 | 52 | def test_format_bytes_bytes(self): 53 | self.assertEqual(utils.format_bytes(518.00), "518.00B") 54 | 55 | def test_format_bytes_kilobytes(self): 56 | self.assertEqual(utils.format_bytes(1024.00), "1.00KiB") 57 | 58 | def test_format_bytes_megabytes(self): 59 | self.assertEqual(utils.format_bytes(1048576.00), "1.00MiB") 60 | 61 | def test_format_bytes_gigabytes(self): 62 | self.assertEqual(utils.format_bytes(1073741824.00), "1.00GiB") 63 | 64 | def test_format_bytes_terabytes(self): 65 | self.assertEqual(utils.format_bytes(1099511627776.00), "1.00TiB") 66 | 67 | 68 | class TestBuildCommand(unittest.TestCase): 69 | 70 | """Test case for the build_command method.""" 71 | 72 | def setUp(self): 73 | self.url = "https://www.youtube.com/watch?v=aaaaaaaaaaa&list=AAAAAAAAAAA" 74 | 75 | self.options = ["-o", None, "-f", "mp4", "--ignore-config"] 76 | 77 | self.result = "{{ydl_bin}} -o \"{{tmpl}}\" -f mp4 --ignore-config \"{url}\"".format(url=self.url) 78 | 79 | def run_tests(self, ydl_bin, tmpl): 80 | """Run the main test. 81 | 82 | Args: 83 | ydl_bin (str): Name of the youtube-dl binary 84 | tmpl (str): Youtube-dl output template 85 | 86 | """ 87 | utils.YOUTUBEDL_BIN = ydl_bin 88 | self.options[1] = tmpl # Plug the template in our options 89 | 90 | result = self.result.format(ydl_bin=ydl_bin, tmpl=tmpl) 91 | 92 | self.assertEqual(utils.build_command(self.options, self.url), result) 93 | 94 | def test_build_command_with_spaces_linux(self): 95 | tmpl = "/home/user/downloads/%(upload_date)s/%(id)s_%(playlist_id)s - %(format)s.%(ext)s" 96 | 97 | self.run_tests("youtube-dl", tmpl) 98 | 99 | def test_build_command_without_spaces_linux(self): 100 | tmpl = "/home/user/downloads/%(id)s.%(ext)s" 101 | 102 | self.run_tests("youtube-dl", tmpl) 103 | 104 | def test_build_command_with_spaces_windows(self): 105 | tmpl = "C:\\downloads\\%(upload_date)s\\%(id)s_%(playlist_id)s - %(format)s.%(ext)s" 106 | 107 | self.run_tests("youtube-dl.exe", tmpl) 108 | 109 | def test_build_command_without_spaces_windows(self): 110 | tmpl = "C:\\downloads\\%(id)s.%(ext)s" 111 | 112 | self.run_tests("youtube-dl.exe", tmpl) 113 | 114 | 115 | class TestConvertItem(unittest.TestCase): 116 | 117 | """Test case for the convert_item function.""" 118 | 119 | def setUp(self): 120 | self.input_list_u = ["v1", "v2", "v3"] 121 | self.input_list_s = [str("v1"), str("v2"), str("v3")] 122 | 123 | self.input_tuple_u = ("v1", "v2", "v3") 124 | self.input_tuple_s = (str("v1"), str("v2"), str("v3")) 125 | 126 | self.input_dict_u = {"k1": "v1", "k2": "v2"} 127 | self.input_dict_s = {str("k1"): str("v1"), str("k2"): str("v2")} 128 | 129 | def check_iter(self, iterable, iter_type, is_unicode): 130 | check_type = unicode if is_unicode else str 131 | 132 | iterable = utils.convert_item(iterable, is_unicode) 133 | 134 | self.assertIsInstance(iterable, iter_type) 135 | 136 | for item in iterable: 137 | if iter_type == dict: 138 | self.assertIsInstance(iterable[item], check_type) 139 | 140 | self.assertIsInstance(item, check_type) 141 | 142 | def test_convert_item_unicode_str(self): 143 | self.assertIsInstance(utils.convert_item("test"), str) 144 | 145 | def test_convert_item_unicode_unicode(self): 146 | self.assertIsInstance(utils.convert_item("test", True), unicode) 147 | 148 | def test_convert_item_str_unicode(self): 149 | self.assertIsInstance(utils.convert_item(str("test"), True), unicode) 150 | 151 | def test_convert_item_str_str(self): 152 | self.assertIsInstance(utils.convert_item(str("test")), str) 153 | 154 | def test_convert_item_list_empty(self): 155 | self.assertEqual(len(utils.convert_item([])), 0) 156 | 157 | def test_convert_item_dict_empty(self): 158 | self.assertEqual(len(utils.convert_item({})), 0) 159 | 160 | def test_convert_item_list_unicode_str(self): 161 | self.check_iter(self.input_list_u, list, False) 162 | 163 | def test_convert_item_list_str_unicode(self): 164 | self.check_iter(self.input_list_s, list, True) 165 | 166 | def test_convert_item_tuple_unicode_str(self): 167 | self.check_iter(self.input_tuple_u, tuple, False) 168 | 169 | def test_convert_item_tuple_str_unicode(self): 170 | self.check_iter(self.input_tuple_s, tuple, True) 171 | 172 | def test_convert_item_dict_unicode_str(self): 173 | self.check_iter(self.input_dict_u, dict, False) 174 | 175 | def test_convert_item_dict_str_unicode(self): 176 | self.check_iter(self.input_dict_s, dict, True) 177 | 178 | 179 | class TestGetDefaultLang(unittest.TestCase): 180 | 181 | """Test case for the get_default_lang function.""" 182 | 183 | @mock.patch("youtube_dl_gui.utils.locale_getdefaultlocale") 184 | def run_tests(self, ret_value, result, mock_getdefaultlocale): 185 | """Run the main test. 186 | 187 | Args: 188 | ret_value (tuple): Return tuple of the locale.getdefaultlocale module 189 | result (unicode): Result we want to see 190 | mock_getdefaultlocale (MagicMock): Mock object 191 | """ 192 | mock_getdefaultlocale.return_value = ret_value 193 | lang = utils.get_default_lang() 194 | 195 | mock_getdefaultlocale.assert_called_once() 196 | self.assertEqual(lang, result) 197 | 198 | def test_get_default_lang(self): 199 | self.run_tests(("it_IT", "UTF-8"), "it_IT") 200 | 201 | def test_get_default_lang_none(self): 202 | self.run_tests((None, None), "en_US") 203 | 204 | def test_get_default_lang_empty(self): 205 | self.run_tests(("", ""), "en_US") 206 | 207 | 208 | def main(): 209 | unittest.main() 210 | 211 | 212 | if __name__ == "__main__": 213 | main() 214 | -------------------------------------------------------------------------------- /tests/test_widgets.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | 4 | """Contains test cases for the widgets.py module.""" 5 | 6 | from __future__ import unicode_literals 7 | 8 | import sys 9 | import os.path 10 | import unittest 11 | 12 | PATH = os.path.realpath(os.path.abspath(__file__)) 13 | sys.path.insert(0, os.path.dirname(os.path.dirname(PATH))) 14 | 15 | 16 | try: 17 | import wx 18 | import mock 19 | 20 | from youtube_dl_gui.widgets import ( 21 | ListBoxWithHeaders, 22 | CustomComboBox, 23 | ListBoxPopup 24 | ) 25 | except ImportError as error: 26 | print error 27 | sys.exit(1) 28 | 29 | 30 | class TestListBoxWithHeaders(unittest.TestCase): 31 | 32 | """Test cases for the ListBoxWithHeaders widget.""" 33 | 34 | def setUp(self): 35 | self.app = wx.App() 36 | 37 | self.frame = wx.Frame(None) 38 | self.listbox = ListBoxWithHeaders(self.frame) 39 | 40 | self.listbox.add_header("Header") 41 | self.listbox.add_items(["item%s" % i for i in xrange(10)]) 42 | 43 | def tearDown(self): 44 | self.frame.Destroy() 45 | 46 | def test_find_string_header_found(self): 47 | self.assertEqual(self.listbox.FindString("Header"), 0) 48 | 49 | def test_find_string_header_not_found(self): 50 | self.assertEqual(self.listbox.FindString("Header2"), wx.NOT_FOUND) 51 | 52 | def test_find_string_item_found(self): 53 | self.assertEqual(self.listbox.FindString("item1"), 2) 54 | 55 | def test_find_string_item_not_found(self): 56 | self.assertEqual(self.listbox.FindString("item"), wx.NOT_FOUND) 57 | 58 | def test_get_string_header(self): 59 | self.assertEqual(self.listbox.GetString(0), "Header") 60 | 61 | def test_get_string_item(self): 62 | self.assertEqual(self.listbox.GetString(10), "item9") 63 | 64 | def test_get_string_item_not_found(self): 65 | self.assertEqual(self.listbox.GetString(11), "") 66 | 67 | def test_get_string_item_negative_index(self): 68 | self.assertEqual(self.listbox.GetString(-1), "") 69 | 70 | def test_insert_items(self): 71 | self.listbox.SetSelection(1) 72 | 73 | self.listbox.InsertItems(["new_item1", "new_item2"], 1) 74 | self.assertEqual(self.listbox.GetString(1), "new_item1") 75 | self.assertEqual(self.listbox.GetString(2), "new_item2") 76 | self.assertEqual(self.listbox.GetString(3), "item0") 77 | 78 | self.assertTrue(self.listbox.IsSelected(3)) # Old selection + 2 79 | 80 | def test_set_selection_header(self): 81 | self.listbox.SetSelection(0) 82 | self.assertFalse(self.listbox.IsSelected(0)) 83 | 84 | def test_set_selection_item_valid_index(self): 85 | self.listbox.SetSelection(1) 86 | self.assertEqual(self.listbox.GetSelection(), 1) 87 | 88 | def test_set_selection_item_invalid_index(self): 89 | self.listbox.SetSelection(1) 90 | self.assertEqual(self.listbox.GetSelection(), 1) 91 | 92 | self.listbox.SetSelection(wx.NOT_FOUND) 93 | self.assertEqual(self.listbox.GetSelection(), wx.NOT_FOUND) 94 | 95 | def test_set_string_item(self): 96 | self.listbox.SetString(1, "item_mod0") 97 | self.assertEqual(self.listbox.GetString(1), "item_mod0") 98 | 99 | def test_set_string_header(self): 100 | self.listbox.SetString(0, "New header") 101 | self.assertEqual(self.listbox.GetString(0), "New header") 102 | 103 | # Make sure that the header is not selectable 104 | self.listbox.SetSelection(0) 105 | self.assertFalse(self.listbox.IsSelected(0)) 106 | 107 | def test_set_string_selection_header(self): 108 | self.assertFalse(self.listbox.SetStringSelection("Header")) 109 | self.assertFalse(self.listbox.IsSelected(0)) 110 | 111 | def test_set_string_selection_item(self): 112 | self.assertTrue(self.listbox.SetStringSelection("item1")) 113 | self.assertTrue(self.listbox.IsSelected(2)) 114 | 115 | def test_get_string_selection(self): 116 | self.listbox.SetSelection(1) 117 | self.assertEqual(self.listbox.GetStringSelection(), "item0") 118 | 119 | def test_get_string_selection_empty(self): 120 | self.assertEqual(self.listbox.GetStringSelection(), "") 121 | 122 | # wx.ItemContainer methods 123 | 124 | def test_append(self): 125 | self.listbox.Append("item666") 126 | self.assertEqual(self.listbox.GetString(11), "item666") 127 | 128 | def test_append_items(self): 129 | self.listbox.AppendItems(["new_item1", "new_item2"]) 130 | self.assertEqual(self.listbox.GetString(11), "new_item1") 131 | self.assertEqual(self.listbox.GetString(12), "new_item2") 132 | 133 | def test_clear(self): 134 | self.listbox.Clear() 135 | self.assertEqual(self.listbox.GetItems(), []) 136 | 137 | def test_delete(self): 138 | self.listbox.Delete(0) 139 | self.assertEqual(self.listbox.GetString(0), "item0") 140 | 141 | # Test item selection 142 | self.listbox.SetSelection(0) 143 | self.assertTrue(self.listbox.IsSelected(0)) 144 | 145 | # Test object extra methods 146 | 147 | def test_add_header(self): 148 | self.listbox.add_header("Header2") 149 | self.listbox.SetSelection(11) 150 | self.assertFalse(self.listbox.IsSelected(11)) 151 | 152 | @mock.patch("wx.ListBox.Append") 153 | def test_add_item_with_prefix(self, mock_append): 154 | self.listbox.add_item("new_item") 155 | mock_append.assert_called_once_with(ListBoxWithHeaders.TEXT_PREFIX + "new_item") 156 | 157 | @mock.patch("wx.ListBox.Append") 158 | def test_add_item_without_prefix(self, mock_append): 159 | self.listbox.add_item("new_item", with_prefix=False) 160 | mock_append.assert_called_once_with("new_item") 161 | 162 | @mock.patch("wx.ListBox.AppendItems") 163 | def test_add_items_with_prefix(self, mock_append): 164 | self.listbox.add_items(["new_item1", "new_item2"]) 165 | mock_append.assert_called_once_with([ListBoxWithHeaders.TEXT_PREFIX + "new_item1", 166 | ListBoxWithHeaders.TEXT_PREFIX + "new_item2"]) 167 | @mock.patch("wx.ListBox.AppendItems") 168 | def test_add_items_without_prefix(self, mock_append): 169 | self.listbox.add_items(["new_item1", "new_item2"], with_prefix=False) 170 | mock_append.assert_called_once_with(["new_item1", "new_item2"]) 171 | 172 | 173 | class TestCustomComboBox(unittest.TestCase): 174 | 175 | """Test cases for the CustomComboBox widget.""" 176 | 177 | def setUp(self): 178 | self.app = wx.App() 179 | 180 | self.frame = wx.Frame(None) 181 | self.combobox = CustomComboBox(self.frame) 182 | 183 | # Call directly the ListBoxWithHeaders methods 184 | self.combobox.listbox.GetControl().add_header("Header") 185 | self.combobox.listbox.GetControl().add_items(["item%s" % i for i in xrange(10)]) 186 | 187 | def tearDown(self): 188 | self.frame.Destroy() 189 | 190 | def test_init(self): 191 | combobox = CustomComboBox(self.frame, -1, "item1", choices=["item0", "item1", "item2"]) 192 | 193 | self.assertEqual(combobox.GetValue(), "item1") 194 | self.assertEqual(combobox.GetCount(), 3) 195 | self.assertEqual(combobox.GetSelection(), 1) 196 | 197 | # wx.ComboBox methods 198 | # Not all of them since most of them are calls to ListBoxWithHeaders 199 | # methods and we already have tests for those 200 | 201 | def test_is_list_empty_false(self): 202 | self.assertFalse(self.combobox.IsListEmpty()) 203 | 204 | def test_is_list_empty_true(self): 205 | self.combobox.Clear() 206 | self.assertTrue(self.combobox.IsListEmpty()) 207 | 208 | def test_is_text_empty_false(self): 209 | self.combobox.SetValue("somevalue") 210 | self.assertFalse(self.combobox.IsTextEmpty()) 211 | 212 | def test_is_text_empty_true(self): 213 | self.assertTrue(self.combobox.IsTextEmpty()) 214 | 215 | def test_set_selection_item(self): 216 | self.combobox.SetSelection(1) 217 | self.assertEqual(self.combobox.GetSelection(), 1) 218 | self.assertEqual(self.combobox.GetValue(), "item0") 219 | 220 | def test_set_selection_header(self): 221 | self.combobox.SetSelection(0) 222 | self.assertEqual(self.combobox.GetSelection(), wx.NOT_FOUND) 223 | self.assertEqual(self.combobox.GetValue(), "") 224 | 225 | def test_set_string_selection_item(self): 226 | self.combobox.SetStringSelection("item0") 227 | self.assertEqual(self.combobox.GetStringSelection(), "item0") 228 | self.assertEqual(self.combobox.GetValue(), "item0") 229 | 230 | def test_set_string_selection_header(self): 231 | self.combobox.SetStringSelection("Header") 232 | self.assertEqual(self.combobox.GetStringSelection(), "") 233 | self.assertEqual(self.combobox.GetValue(), "") 234 | 235 | def test_set_string_selection_invalid_string(self): 236 | self.combobox.SetStringSelection("abcde") 237 | self.assertEqual(self.combobox.GetStringSelection(), "") 238 | self.assertEqual(self.combobox.GetValue(), "") 239 | 240 | # wx.ItemContainer methods 241 | 242 | def test_clear(self): 243 | self.combobox.SetValue("value") 244 | 245 | self.combobox.Clear() 246 | self.assertEqual(self.combobox.GetCount(), 0) 247 | self.assertTrue(self.combobox.IsTextEmpty()) 248 | 249 | def test_append(self): 250 | self.combobox.Append("item10") 251 | self.assertEqual(self.combobox.GetCount(), 12) 252 | 253 | def test_append_items(self): 254 | self.combobox.AppendItems(["item10", "item11"]) 255 | self.assertEqual(self.combobox.GetCount(), 13) 256 | 257 | def test_delete(self): 258 | self.combobox.Delete(1) 259 | self.assertEqual(self.combobox.GetString(1), "item1") 260 | 261 | # wx.TextEntry methods 262 | 263 | def test_get_value(self): 264 | self.combobox.SetValue("value") 265 | self.assertEqual(self.combobox.GetValue(), "value") 266 | 267 | 268 | def main(): 269 | unittest.main() 270 | 271 | 272 | if __name__ == '__main__': 273 | main() 274 | -------------------------------------------------------------------------------- /youtube-dl-gui.1: -------------------------------------------------------------------------------- 1 | .\" [program name] [section] [date YYYY-MM-DD] [version] [empty] 2 | .TH YOUTUBE\-DL\-GUI 1 "2018-01-13" "Version 0.4" "" 3 | 4 | .SH NAME 5 | youtube\-dl\-gui \- cross platform graphical user interface for youtube\-dl. 6 | 7 | .SH SYNOPSIS 8 | .B youtube\-dl\-gui 9 | 10 | .SH DESCRIPTION 11 | Youtube\-dl\-gui is a graphical frontend of the popular youtube\-dl 12 | command\-line program. As such, it does not take any command\-line parameters. 13 | You may configure the program through the graphical settings dialog, or the 14 | configuration file. 15 | 16 | .SH FILES 17 | .\" .IP text indent_size (.IP = Indented Paragraph) 18 | .IP "\fB$HOME/.config/youtube\-dlg/settings.json\fR" 4 19 | Configuration file. 20 | .\" .IP text indent_size (.IP = Indented Paragraph) 21 | .IP "\fB$HOME/.config/youtube\-dlg/log\fR" 4 22 | Log file. 23 | 24 | .SH NOTES 25 | FAQS: https://github.com/MrS0m30n3/youtube-dl-gui/blob/master/docs/faqs.md 26 | 27 | .SH SEE ALSO 28 | .BR youtube-dl (1) 29 | -------------------------------------------------------------------------------- /youtube_dl_gui/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | # -*- coding: utf-8 -*- 3 | 4 | """Youtubedlg __init__ file. 5 | 6 | Responsible on how the package looks from the outside. 7 | 8 | Example: 9 | In order to load the GUI from a python script. 10 | 11 | import youtube_dl_gui 12 | 13 | youtube_dl_gui.main() 14 | 15 | """ 16 | 17 | from __future__ import unicode_literals 18 | 19 | import sys 20 | import gettext 21 | import os.path 22 | 23 | try: 24 | import wx 25 | except ImportError as error: 26 | print error 27 | sys.exit(1) 28 | 29 | __packagename__ = "youtube_dl_gui" 30 | 31 | # For package use 32 | from .version import __version__ 33 | from .info import ( 34 | __author__, 35 | __appname__, 36 | __contact__, 37 | __license__, 38 | __projecturl__, 39 | __licensefull__, 40 | __description__, 41 | __descriptionfull__, 42 | ) 43 | 44 | gettext.install(__packagename__) 45 | from .formats import reload_strings 46 | 47 | from .logmanager import LogManager 48 | from .optionsmanager import OptionsManager 49 | 50 | from .utils import ( 51 | get_config_path, 52 | get_locale_file, 53 | os_path_exists, 54 | YOUTUBEDL_BIN 55 | ) 56 | 57 | 58 | # Set config path and create options and log managers 59 | config_path = get_config_path() 60 | 61 | opt_manager = OptionsManager(config_path) 62 | log_manager = None 63 | 64 | if opt_manager.options['enable_log']: 65 | log_manager = LogManager(config_path, opt_manager.options['log_time']) 66 | 67 | # Set gettext before MainFrame import 68 | # because the GUI strings are class level attributes 69 | locale_dir = get_locale_file() 70 | 71 | try: 72 | gettext.translation(__packagename__, locale_dir, [opt_manager.options['locale_name']]).install(unicode=True) 73 | except IOError: 74 | opt_manager.options['locale_name'] = 'en_US' 75 | gettext.install(__packagename__) 76 | 77 | reload_strings() 78 | 79 | from .mainframe import MainFrame 80 | 81 | 82 | def main(): 83 | """The real main. Creates and calls the main app windows. """ 84 | youtubedl_path = os.path.join(opt_manager.options["youtubedl_path"], YOUTUBEDL_BIN) 85 | 86 | app = wx.App() 87 | frame = MainFrame(opt_manager, log_manager) 88 | frame.Show() 89 | 90 | if opt_manager.options["disable_update"] and not os_path_exists(youtubedl_path): 91 | wx.MessageBox(_("Failed to locate youtube-dl and updates are disabled"), _("Error"), wx.OK | wx.ICON_ERROR) 92 | frame.close() 93 | 94 | app.MainLoop() 95 | -------------------------------------------------------------------------------- /youtube_dl_gui/__main__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | # -*- coding: utf-8 -*- 3 | 4 | """Youtubedlg __main__ file. 5 | 6 | __main__ file is a python 'executable' file which calls the youtubedlg 7 | main() function in order to start the app. It can be used to start 8 | the app from the package directory OR it can be used to start the app 9 | from a different directory after you have installed the youtube_dl_gui 10 | package. 11 | 12 | Example: 13 | In order to run the app from the package directory. 14 | 15 | $ cd 16 | $ python __main__.py 17 | 18 | In order to run the app from /usr/local/bin etc.. AFTER 19 | you have installed the package using setup.py. 20 | 21 | $ youtube-dl-gui 22 | 23 | """ 24 | 25 | from __future__ import unicode_literals 26 | 27 | import sys 28 | 29 | if __package__ is None and not hasattr(sys, "frozen"): 30 | # direct call of __main__.py 31 | import os.path 32 | PATH = os.path.realpath(os.path.abspath(__file__)) 33 | sys.path.append(os.path.dirname(os.path.dirname(PATH))) 34 | 35 | import youtube_dl_gui 36 | 37 | 38 | if __name__ == '__main__': 39 | youtube_dl_gui.main() 40 | -------------------------------------------------------------------------------- /youtube_dl_gui/data/icons/hicolor/128x128/apps/youtube-dl-gui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrS0m30n3/youtube-dl-gui/c5c18e55cb7e04fb6d6d8e64024a7dbac1f6b431/youtube_dl_gui/data/icons/hicolor/128x128/apps/youtube-dl-gui.png -------------------------------------------------------------------------------- /youtube_dl_gui/data/icons/hicolor/16x16/apps/youtube-dl-gui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrS0m30n3/youtube-dl-gui/c5c18e55cb7e04fb6d6d8e64024a7dbac1f6b431/youtube_dl_gui/data/icons/hicolor/16x16/apps/youtube-dl-gui.png -------------------------------------------------------------------------------- /youtube_dl_gui/data/icons/hicolor/256x256/apps/youtube-dl-gui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrS0m30n3/youtube-dl-gui/c5c18e55cb7e04fb6d6d8e64024a7dbac1f6b431/youtube_dl_gui/data/icons/hicolor/256x256/apps/youtube-dl-gui.png -------------------------------------------------------------------------------- /youtube_dl_gui/data/icons/hicolor/32x32/apps/youtube-dl-gui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrS0m30n3/youtube-dl-gui/c5c18e55cb7e04fb6d6d8e64024a7dbac1f6b431/youtube_dl_gui/data/icons/hicolor/32x32/apps/youtube-dl-gui.png -------------------------------------------------------------------------------- /youtube_dl_gui/data/icons/hicolor/48x48/apps/youtube-dl-gui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrS0m30n3/youtube-dl-gui/c5c18e55cb7e04fb6d6d8e64024a7dbac1f6b431/youtube_dl_gui/data/icons/hicolor/48x48/apps/youtube-dl-gui.png -------------------------------------------------------------------------------- /youtube_dl_gui/data/icons/hicolor/64x64/apps/youtube-dl-gui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrS0m30n3/youtube-dl-gui/c5c18e55cb7e04fb6d6d8e64024a7dbac1f6b431/youtube_dl_gui/data/icons/hicolor/64x64/apps/youtube-dl-gui.png -------------------------------------------------------------------------------- /youtube_dl_gui/data/pixmaps/arrow_down_32px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrS0m30n3/youtube-dl-gui/c5c18e55cb7e04fb6d6d8e64024a7dbac1f6b431/youtube_dl_gui/data/pixmaps/arrow_down_32px.png -------------------------------------------------------------------------------- /youtube_dl_gui/data/pixmaps/arrow_up_32px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrS0m30n3/youtube-dl-gui/c5c18e55cb7e04fb6d6d8e64024a7dbac1f6b431/youtube_dl_gui/data/pixmaps/arrow_up_32px.png -------------------------------------------------------------------------------- /youtube_dl_gui/data/pixmaps/camera_32px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrS0m30n3/youtube-dl-gui/c5c18e55cb7e04fb6d6d8e64024a7dbac1f6b431/youtube_dl_gui/data/pixmaps/camera_32px.png -------------------------------------------------------------------------------- /youtube_dl_gui/data/pixmaps/cloud_download_32px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrS0m30n3/youtube-dl-gui/c5c18e55cb7e04fb6d6d8e64024a7dbac1f6b431/youtube_dl_gui/data/pixmaps/cloud_download_32px.png -------------------------------------------------------------------------------- /youtube_dl_gui/data/pixmaps/delete_32px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrS0m30n3/youtube-dl-gui/c5c18e55cb7e04fb6d6d8e64024a7dbac1f6b431/youtube_dl_gui/data/pixmaps/delete_32px.png -------------------------------------------------------------------------------- /youtube_dl_gui/data/pixmaps/folder_32px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrS0m30n3/youtube-dl-gui/c5c18e55cb7e04fb6d6d8e64024a7dbac1f6b431/youtube_dl_gui/data/pixmaps/folder_32px.png -------------------------------------------------------------------------------- /youtube_dl_gui/data/pixmaps/icons-license: -------------------------------------------------------------------------------- 1 | icons from: https://www.iconfinder.com/iconsets/google-material-design-icons 2 | license: https://creativecommons.org/licenses/by-sa/3.0 3 | -------------------------------------------------------------------------------- /youtube_dl_gui/data/pixmaps/pause_32px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrS0m30n3/youtube-dl-gui/c5c18e55cb7e04fb6d6d8e64024a7dbac1f6b431/youtube_dl_gui/data/pixmaps/pause_32px.png -------------------------------------------------------------------------------- /youtube_dl_gui/data/pixmaps/play_arrow_32px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrS0m30n3/youtube-dl-gui/c5c18e55cb7e04fb6d6d8e64024a7dbac1f6b431/youtube_dl_gui/data/pixmaps/play_arrow_32px.png -------------------------------------------------------------------------------- /youtube_dl_gui/data/pixmaps/reload_32px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrS0m30n3/youtube-dl-gui/c5c18e55cb7e04fb6d6d8e64024a7dbac1f6b431/youtube_dl_gui/data/pixmaps/reload_32px.png -------------------------------------------------------------------------------- /youtube_dl_gui/data/pixmaps/settings_20px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrS0m30n3/youtube-dl-gui/c5c18e55cb7e04fb6d6d8e64024a7dbac1f6b431/youtube_dl_gui/data/pixmaps/settings_20px.png -------------------------------------------------------------------------------- /youtube_dl_gui/data/pixmaps/stop_32px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrS0m30n3/youtube-dl-gui/c5c18e55cb7e04fb6d6d8e64024a7dbac1f6b431/youtube_dl_gui/data/pixmaps/stop_32px.png -------------------------------------------------------------------------------- /youtube_dl_gui/data/pixmaps/youtube-dl-gui.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrS0m30n3/youtube-dl-gui/c5c18e55cb7e04fb6d6d8e64024a7dbac1f6b431/youtube_dl_gui/data/pixmaps/youtube-dl-gui.ico -------------------------------------------------------------------------------- /youtube_dl_gui/data/pixmaps/youtube-dl-gui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrS0m30n3/youtube-dl-gui/c5c18e55cb7e04fb6d6d8e64024a7dbac1f6b431/youtube_dl_gui/data/pixmaps/youtube-dl-gui.png -------------------------------------------------------------------------------- /youtube_dl_gui/formats.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | import gettext 4 | 5 | from .utils import TwoWayOrderedDict as tdict 6 | 7 | 8 | OUTPUT_FORMATS = tdict([ 9 | (0, _("ID")), 10 | (1, _("Title")), 11 | (2, _("Title + ID")), 12 | (4, _("Title + Quality")), 13 | (5, _("Title + ID + Quality")), 14 | (3, _("Custom")) 15 | ]) 16 | 17 | 18 | DEFAULT_FORMATS = tdict([ 19 | ("0", _("default")) 20 | ]) 21 | 22 | 23 | VIDEO_FORMATS = tdict([ 24 | ("3gp", "3gp"), 25 | ("17", "3gp [144p]"), 26 | ("36", "3gp [240p]"), 27 | ("flv", "flv"), 28 | ("5", "flv [240p]"), 29 | ("34", "flv [360p]"), 30 | ("35", "flv [480p]"), 31 | ("webm", "webm"), 32 | ("43", "webm [360p]"), 33 | ("44", "webm [480p]"), 34 | ("45", "webm [720p]"), 35 | ("46", "webm [1080p]"), 36 | ("mp4", "mp4"), 37 | ("18", "mp4 [360p]"), 38 | ("22", "mp4 [720p]"), 39 | ("37", "mp4 [1080p]"), 40 | ("38", "mp4 [4K]"), 41 | ("160", "mp4 [144p] (DASH Video)"), 42 | ("133", "mp4 [240p] (DASH Video)"), 43 | ("134", "mp4 [360p] (DASH Video)"), 44 | ("135", "mp4 [480p] (DASH Video)"), 45 | ("136", "mp4 [720p] (DASH Video)"), 46 | ("137", "mp4 [1080p] (DASH Video)"), 47 | ("264", "mp4 [1440p] (DASH Video)"), 48 | ("138", "mp4 [2160p] (DASH Video)"), 49 | ("242", "webm [240p] (DASH Video)"), 50 | ("243", "webm [360p] (DASH Video)"), 51 | ("244", "webm [480p] (DASH Video)"), 52 | ("247", "webm [720p] (DASH Video)"), 53 | ("248", "webm [1080p] (DASH Video)"), 54 | ("271", "webm [1440p] (DASH Video)"), 55 | ("272", "webm [2160p] (DASH Video)"), 56 | ("82", "mp4 [360p] (3D)"), 57 | ("83", "mp4 [480p] (3D)"), 58 | ("84", "mp4 [720p] (3D)"), 59 | ("85", "mp4 [1080p] (3D)"), 60 | ("100", "webm [360p] (3D)"), 61 | ("101", "webm [480p] (3D)"), 62 | ("102", "webm [720p] (3D)"), 63 | ("139", "m4a 48k (DASH Audio)"), 64 | ("140", "m4a 128k (DASH Audio)"), 65 | ("141", "m4a 256k (DASH Audio)"), 66 | ("171", "webm 48k (DASH Audio)"), 67 | ("172", "webm 256k (DASH Audio)") 68 | ]) 69 | 70 | 71 | AUDIO_FORMATS = tdict([ 72 | ("mp3", "mp3"), 73 | ("wav", "wav"), 74 | ("aac", "aac"), 75 | ("m4a", "m4a"), 76 | ("vorbis", "vorbis"), 77 | ("opus", "opus"), 78 | ("flac", "flac") 79 | ]) 80 | 81 | 82 | FORMATS = DEFAULT_FORMATS.copy() 83 | FORMATS.update(VIDEO_FORMATS) 84 | FORMATS.update(AUDIO_FORMATS) 85 | 86 | 87 | def reload_strings(): 88 | # IF YOU DONT WANT YOUR EYES TO BLEED STOP HERE 89 | # YOU HAVE BEEN WARNED 90 | # DO NOT LOOK THE CODE BELOW 91 | # 92 | # 93 | # 94 | # 95 | # 96 | # 97 | # 98 | # 99 | #NOTE Remove 100 | # Code is so messed up that i need to reload strings else 101 | # the translations wont work on the about gettext tags 102 | global OUTPUT_FORMATS 103 | global DEFAULT_FORMATS 104 | global VIDEO_FORMATS 105 | global AUDIO_FORMATS 106 | global FORMATS 107 | 108 | OUTPUT_FORMATS = tdict([ 109 | (0, _("ID")), 110 | (1, _("Title")), 111 | (2, _("Title + ID")), 112 | (4, _("Title + Quality")), 113 | (5, _("Title + ID + Quality")), 114 | (3, _("Custom")) 115 | ]) 116 | 117 | 118 | DEFAULT_FORMATS = tdict([ 119 | ("0", _("default")) 120 | ]) 121 | 122 | 123 | VIDEO_FORMATS = tdict([ 124 | ("3gp", "3gp"), 125 | ("17", "3gp [144p]"), 126 | ("36", "3gp [240p]"), 127 | ("flv", "flv"), 128 | ("5", "flv [240p]"), 129 | ("34", "flv [360p]"), 130 | ("35", "flv [480p]"), 131 | ("webm", "webm"), 132 | ("43", "webm [360p]"), 133 | ("44", "webm [480p]"), 134 | ("45", "webm [720p]"), 135 | ("46", "webm [1080p]"), 136 | ("mp4", "mp4"), 137 | ("18", "mp4 [360p]"), 138 | ("22", "mp4 [720p]"), 139 | ("37", "mp4 [1080p]"), 140 | ("38", "mp4 [4K]"), 141 | ("160", "mp4 [144p] (DASH Video)"), 142 | ("133", "mp4 [240p] (DASH Video)"), 143 | ("134", "mp4 [360p] (DASH Video)"), 144 | ("135", "mp4 [480p] (DASH Video)"), 145 | ("136", "mp4 [720p] (DASH Video)"), 146 | ("137", "mp4 [1080p] (DASH Video)"), 147 | ("264", "mp4 [1440p] (DASH Video)"), 148 | ("138", "mp4 [2160p] (DASH Video)"), 149 | ("242", "webm [240p] (DASH Video)"), 150 | ("243", "webm [360p] (DASH Video)"), 151 | ("244", "webm [480p] (DASH Video)"), 152 | ("247", "webm [720p] (DASH Video)"), 153 | ("248", "webm [1080p] (DASH Video)"), 154 | ("271", "webm [1440p] (DASH Video)"), 155 | ("272", "webm [2160p] (DASH Video)"), 156 | ("82", "mp4 [360p] (3D)"), 157 | ("83", "mp4 [480p] (3D)"), 158 | ("84", "mp4 [720p] (3D)"), 159 | ("85", "mp4 [1080p] (3D)"), 160 | ("100", "webm [360p] (3D)"), 161 | ("101", "webm [480p] (3D)"), 162 | ("102", "webm [720p] (3D)"), 163 | ("139", "m4a 48k (DASH Audio)"), 164 | ("140", "m4a 128k (DASH Audio)"), 165 | ("141", "m4a 256k (DASH Audio)"), 166 | ("171", "webm 48k (DASH Audio)"), 167 | ("172", "webm 256k (DASH Audio)") 168 | ]) 169 | 170 | 171 | AUDIO_FORMATS = tdict([ 172 | ("mp3", "mp3"), 173 | ("wav", "wav"), 174 | ("aac", "aac"), 175 | ("m4a", "m4a"), 176 | ("vorbis", "vorbis"), 177 | ("opus", "opus"), 178 | ("flac", "flac") 179 | ]) 180 | 181 | 182 | FORMATS = DEFAULT_FORMATS.copy() 183 | FORMATS.update(VIDEO_FORMATS) 184 | FORMATS.update(AUDIO_FORMATS) 185 | -------------------------------------------------------------------------------- /youtube_dl_gui/info.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """Youtubedlg module that holds package information. 4 | 5 | Note: 6 | All those info could be stored in the __init__ file 7 | but we keep them here to keep the code clean. 8 | 9 | """ 10 | 11 | from __future__ import unicode_literals 12 | 13 | __author__ = 'Sotiris Papadopoulos' 14 | __contact__ = 'ytubedlg@gmail.com' 15 | __projecturl__ = 'https://mrs0m30n3.github.io/youtube-dl-gui/' 16 | 17 | __appname__ = 'Youtube-DLG' 18 | __license__ = 'UNLICENSE' 19 | 20 | __description__ = 'Youtube-dl GUI' 21 | 22 | __descriptionfull__ = '''A cross platform front-end GUI of the popular 23 | youtube-dl written in wxPython''' 24 | 25 | __licensefull__ = ''' 26 | This is free and unencumbered software released into the public domain. 27 | 28 | Anyone is free to copy, modify, publish, use, compile, sell, or 29 | distribute this software, either in source code form or as a compiled 30 | binary, for any purpose, commercial or non-commercial, and by any 31 | means. 32 | 33 | In jurisdictions that recognize copyright laws, the author or authors 34 | of this software dedicate any and all copyright interest in the 35 | software to the public domain. We make this dedication for the benefit 36 | of the public at large and to the detriment of our heirs and 37 | successors. We intend this dedication to be an overt act of 38 | relinquishment in perpetuity of all present and future rights to this 39 | software under copyright law. 40 | 41 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 42 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 43 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 44 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 45 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 46 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 47 | OTHER DEALINGS IN THE SOFTWARE. 48 | 49 | For more information, please refer to ''' 50 | -------------------------------------------------------------------------------- /youtube_dl_gui/locale/ar_SA/LC_MESSAGES/youtube_dl_gui.po: -------------------------------------------------------------------------------- 1 | # Youtube-dlG localization file. 2 | # FIRST AUTHOR: Sotiris Papadopoulos , 2015. 3 | # snosi , 2017. 4 | msgid "" 5 | msgstr "" 6 | "Project-Id-Version: youtube-dlg 0.4\n" 7 | "POT-Creation-Date: 2018-01-15 16:42+EET\n" 8 | "PO-Revision-Date: 2017-06-22 17:52+0100\n" 9 | "Last-Translator: snosi \n" 10 | "Language-Team: snosi\n" 11 | "Language: ar\n" 12 | "MIME-Version: 1.0\n" 13 | "Content-Type: text/plain; charset=UTF-8\n" 14 | "Content-Transfer-Encoding: 8bit\n" 15 | "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" 16 | "X-Generator: Virtaal 0.7.1\n" 17 | "Generated-By: pygettext.py 1.5\n" 18 | 19 | #: youtube_dl_gui/__init__.py:91 20 | msgid "Error" 21 | msgstr "" 22 | 23 | #: youtube_dl_gui/__init__.py:91 24 | msgid "Failed to locate youtube-dl and updates are disabled" 25 | msgstr "" 26 | 27 | #: youtube_dl_gui/formats.py:9 youtube_dl_gui/formats.py:109 28 | msgid "ID" 29 | msgstr "ID" 30 | 31 | #: youtube_dl_gui/formats.py:10 youtube_dl_gui/formats.py:110 32 | #: youtube_dl_gui/mainframe.py:140 33 | msgid "Title" 34 | msgstr "العنوان" 35 | 36 | #: youtube_dl_gui/formats.py:11 youtube_dl_gui/formats.py:111 37 | msgid "Title + ID" 38 | msgstr "العنوان + ID" 39 | 40 | #: youtube_dl_gui/formats.py:12 youtube_dl_gui/formats.py:112 41 | msgid "Title + Quality" 42 | msgstr "العنوان + الجودة" 43 | 44 | #: youtube_dl_gui/formats.py:13 youtube_dl_gui/formats.py:113 45 | msgid "Title + ID + Quality" 46 | msgstr "العنوان + ID + الجودة" 47 | 48 | #: youtube_dl_gui/formats.py:14 youtube_dl_gui/formats.py:114 49 | msgid "Custom" 50 | msgstr "مخصص" 51 | 52 | #: youtube_dl_gui/formats.py:19 youtube_dl_gui/formats.py:119 53 | #: youtube_dl_gui/mainframe.py:503 youtube_dl_gui/mainframe.py:506 54 | msgid "default" 55 | msgstr "افتراضي (دون تعديل)" 56 | 57 | #: youtube_dl_gui/mainframe.py:97 58 | msgid "Enter URLs below" 59 | msgstr "ضع الرابط هنا" 60 | 61 | #: youtube_dl_gui/mainframe.py:98 62 | msgid "Update" 63 | msgstr "تحديث" 64 | 65 | #: youtube_dl_gui/mainframe.py:99 youtube_dl_gui/optionsframe.py:41 66 | msgid "Options" 67 | msgstr "الخيارات" 68 | 69 | #: youtube_dl_gui/mainframe.py:100 youtube_dl_gui/optionsframe.py:584 70 | msgid "Stop" 71 | msgstr "إيقاف" 72 | 73 | #: youtube_dl_gui/mainframe.py:101 74 | msgid "Info" 75 | msgstr "معلومات" 76 | 77 | #: youtube_dl_gui/mainframe.py:102 78 | msgid "Welcome" 79 | msgstr "مرحباً" 80 | 81 | #: youtube_dl_gui/mainframe.py:103 82 | msgid "Warning" 83 | msgstr "تحذير" 84 | 85 | #: youtube_dl_gui/mainframe.py:105 86 | msgid "Add" 87 | msgstr "إضافة" 88 | 89 | #: youtube_dl_gui/mainframe.py:106 90 | msgid "Download list" 91 | msgstr "قائمة التنزيلات" 92 | 93 | #: youtube_dl_gui/mainframe.py:107 youtube_dl_gui/mainframe.py:516 94 | #: youtube_dl_gui/mainframe.py:534 95 | msgid "Delete" 96 | msgstr "حذف" 97 | 98 | #: youtube_dl_gui/mainframe.py:108 99 | msgid "Play" 100 | msgstr "تشغيل" 101 | 102 | #: youtube_dl_gui/mainframe.py:109 103 | msgid "Up" 104 | msgstr "فوق" 105 | 106 | #: youtube_dl_gui/mainframe.py:110 107 | msgid "Down" 108 | msgstr "اسفل" 109 | 110 | #: youtube_dl_gui/mainframe.py:111 111 | msgid "Reload" 112 | msgstr "أعد التحميل" 113 | 114 | #: youtube_dl_gui/mainframe.py:112 youtube_dl_gui/mainframe.py:448 115 | #: youtube_dl_gui/mainframe.py:649 116 | msgid "Pause" 117 | msgstr "توقيف مؤقت" 118 | 119 | #: youtube_dl_gui/mainframe.py:113 youtube_dl_gui/mainframe.py:865 120 | #: youtube_dl_gui/mainframe.py:866 youtube_dl_gui/optionsframe.py:582 121 | msgid "Start" 122 | msgstr "إبدأ" 123 | 124 | #: youtube_dl_gui/mainframe.py:114 125 | msgid "About" 126 | msgstr "عن" 127 | 128 | #: youtube_dl_gui/mainframe.py:115 129 | msgid "View Log" 130 | msgstr "شاهد السِجِل" 131 | 132 | #: youtube_dl_gui/mainframe.py:117 133 | msgid "Successfully downloaded {0} URL(s) in {1} day(s) {2} hour(s) {3} minute(s) {4} second(s)" 134 | msgstr "انتهاء التنزيلات {0} URL(s) في {1} يوم {2} ساعة {3} دقيقة {4} ثانية" 135 | 136 | #: youtube_dl_gui/mainframe.py:119 137 | msgid "Downloads completed" 138 | msgstr "اكتمال التنزيلات" 139 | 140 | #: youtube_dl_gui/mainframe.py:120 141 | msgid "Total Progress: {0:.1f}% | Queued ({1}) Paused ({2}) Active ({3}) Completed ({4}) Error ({5})" 142 | msgstr "مجموع العمليات : {0:.1f}% | في الانتظار ({1}) موقف مؤقتا ({2}) نشط ({3}) اكتملت ({4}) أخطاء ({5})" 143 | 144 | #: youtube_dl_gui/mainframe.py:121 145 | msgid "Stopping downloads" 146 | msgstr "إيقاف التنزيل" 147 | 148 | #: youtube_dl_gui/mainframe.py:122 149 | msgid "Downloads stopped" 150 | msgstr "تم إيقاف التنزيل" 151 | 152 | #: youtube_dl_gui/mainframe.py:123 153 | msgid "You need to provide at least one URL" 154 | msgstr "يجب ان تضع رابطا واحدا على الاقل" 155 | 156 | #: youtube_dl_gui/mainframe.py:124 157 | msgid "Downloads started" 158 | msgstr "بدأ التنزيل" 159 | 160 | #: youtube_dl_gui/mainframe.py:125 161 | msgid "Choose Directory" 162 | msgstr "اختر مكان الحفظ" 163 | 164 | #: youtube_dl_gui/mainframe.py:127 165 | msgid "Download in progress. Please wait for all downloads to complete" 166 | msgstr "جاري التنزيل . انتظر حتى تكتمل التنزيلات" 167 | 168 | #: youtube_dl_gui/mainframe.py:128 169 | msgid "Update already in progress" 170 | msgstr "جاري التحديث" 171 | 172 | #: youtube_dl_gui/mainframe.py:130 173 | msgid "Downloading latest youtube-dl. Please wait..." 174 | msgstr "...يرجى الإنتظار youtube-dl تحديث . " 175 | 176 | #: youtube_dl_gui/mainframe.py:131 177 | msgid "Youtube-dl download failed [{0}]" 178 | msgstr "فشل تحديث youtube-dl [{0}]" 179 | 180 | #: youtube_dl_gui/mainframe.py:132 181 | msgid "Successfully downloaded youtube-dl" 182 | msgstr "youtube-dl اكتمل تحديث" 183 | 184 | #: youtube_dl_gui/mainframe.py:134 185 | msgid "Unable to open directory: '{dir}'. The specified path does not exist" 186 | msgstr "فعل لفتح الدليل : '{dir}'. المسار غير موجود" 187 | 188 | #: youtube_dl_gui/mainframe.py:136 189 | msgid "Error while shutting down. Make sure you typed the correct password" 190 | msgstr "تعذر إيقاف الجهاز . تأكد من كلمة المرور" 191 | 192 | #: youtube_dl_gui/mainframe.py:138 193 | msgid "Shutting down system" 194 | msgstr "إيقاف تشغيل النظام" 195 | 196 | #: youtube_dl_gui/mainframe.py:141 197 | msgid "Extension" 198 | msgstr "نوع الملف (الصيغة)" 199 | 200 | #: youtube_dl_gui/mainframe.py:142 201 | msgid "Size" 202 | msgstr "الحجم" 203 | 204 | #: youtube_dl_gui/mainframe.py:143 205 | msgid "Percent" 206 | msgstr "النِسبة المئوية" 207 | 208 | #: youtube_dl_gui/mainframe.py:144 209 | msgid "ETA" 210 | msgstr "الوقت المتبقي" 211 | 212 | #: youtube_dl_gui/mainframe.py:145 213 | msgid "Speed" 214 | msgstr "سرعة التنزيل" 215 | 216 | #: youtube_dl_gui/mainframe.py:146 217 | msgid "Status" 218 | msgstr "الحالة" 219 | 220 | #: youtube_dl_gui/mainframe.py:235 221 | msgid "Get URL" 222 | msgstr "جد الرابط" 223 | 224 | #: youtube_dl_gui/mainframe.py:236 225 | msgid "Get command" 226 | msgstr "الحصول على أمر" 227 | 228 | #: youtube_dl_gui/mainframe.py:237 229 | msgid "Open destination" 230 | msgstr "افتح وجهة" 231 | 232 | #: youtube_dl_gui/mainframe.py:238 233 | msgid "Re-enter" 234 | msgstr "اعادة الادخال" 235 | 236 | #: youtube_dl_gui/mainframe.py:458 237 | msgid "Resume" 238 | msgstr "استئتاف" 239 | 240 | #: youtube_dl_gui/mainframe.py:480 241 | msgid "Video" 242 | msgstr "الفيديو" 243 | 244 | #: youtube_dl_gui/mainframe.py:484 245 | msgid "Audio" 246 | msgstr "الصوتي" 247 | 248 | #: youtube_dl_gui/mainframe.py:516 249 | msgid "No items selected. Please pick an action" 250 | msgstr "لم يتم تحديد أي عنصر. يرجى اختيار إجراء" 251 | 252 | #: youtube_dl_gui/mainframe.py:516 253 | msgid "Remove all" 254 | msgstr "حذف الكل " 255 | 256 | #: youtube_dl_gui/mainframe.py:516 257 | msgid "Remove completed" 258 | msgstr "حذف المكتمل" 259 | 260 | #: youtube_dl_gui/mainframe.py:534 261 | msgid "Are you sure you want to remove selected items?" 262 | msgstr "هل انت متأكد من حذف هذا العنصر ؟" 263 | 264 | #: youtube_dl_gui/mainframe.py:546 265 | msgid "Item is active, cannot remove" 266 | msgstr "لا يمكن الحذف الآن" 267 | 268 | #: youtube_dl_gui/mainframe.py:579 269 | msgid "Item is not completed" 270 | msgstr "لم يكتمل العنصر بعد" 271 | 272 | #: youtube_dl_gui/mainframe.py:668 273 | msgid "Update in progress. Please wait for the update to complete" 274 | msgstr "قيد التحديث .يرجى الانتظار" 275 | 276 | #: youtube_dl_gui/mainframe.py:716 277 | msgid "Logging is disabled" 278 | msgstr "تم تعطيل التسجيل" 279 | 280 | #: youtube_dl_gui/mainframe.py:891 281 | msgid "Shutdown" 282 | msgstr "إيقاف التشغيل" 283 | 284 | #: youtube_dl_gui/mainframe.py:891 285 | msgid "Shutting down in {0} second(s)" 286 | msgstr "إيقاف التشغيل في {0} ثانية(s)" 287 | 288 | #: youtube_dl_gui/mainframe.py:980 289 | msgid "No items to download" 290 | msgstr "لا توجد عناصر للتنزيل" 291 | 292 | #: youtube_dl_gui/mainframe.py:1040 293 | msgid "Updates are disabled for your system. Please use the system's package manager to update youtube-dl." 294 | msgstr "" 295 | 296 | #: youtube_dl_gui/mainframe.py:1065 297 | msgid "Are you sure you want to exit?" 298 | msgstr "هل تريد الخروج من البرنامج ؟" 299 | 300 | #: youtube_dl_gui/mainframe.py:1065 301 | msgid "Exit" 302 | msgstr "خروج" 303 | 304 | #: youtube_dl_gui/mainframe.py:1306 youtube_dl_gui/mainframe.py:1456 305 | msgid "Cancel" 306 | msgstr "إلغاء" 307 | 308 | #: youtube_dl_gui/mainframe.py:1455 309 | msgid "OK" 310 | msgstr "موافق" 311 | 312 | #: youtube_dl_gui/optionsframe.py:65 313 | msgid "Reset" 314 | msgstr "إسترجاع الإعدادات الافتراضية" 315 | 316 | #: youtube_dl_gui/optionsframe.py:66 317 | msgid "Close" 318 | msgstr "إنهاء" 319 | 320 | #: youtube_dl_gui/optionsframe.py:72 321 | msgid "General" 322 | msgstr "عام" 323 | 324 | #: youtube_dl_gui/optionsframe.py:73 325 | msgid "Formats" 326 | msgstr "الصيغ" 327 | 328 | #: youtube_dl_gui/optionsframe.py:74 329 | msgid "Downloads" 330 | msgstr "التنزيلات" 331 | 332 | #: youtube_dl_gui/optionsframe.py:75 333 | msgid "Advanced" 334 | msgstr "متقدم" 335 | 336 | #: youtube_dl_gui/optionsframe.py:76 337 | msgid "Extra" 338 | msgstr "إضافي" 339 | 340 | #: youtube_dl_gui/optionsframe.py:310 341 | msgid "Language" 342 | msgstr "اللغة" 343 | 344 | #: youtube_dl_gui/optionsframe.py:313 345 | msgid "Filename format" 346 | msgstr "اسم الملف الصيغة" 347 | 348 | #: youtube_dl_gui/optionsframe.py:318 349 | msgid "Filename options" 350 | msgstr "اسم الملف خيارات" 351 | 352 | #: youtube_dl_gui/optionsframe.py:319 353 | msgid "Restrict filenames to ASCII" 354 | msgstr "قيّد اسم الملف الى ASCII" 355 | 356 | #: youtube_dl_gui/optionsframe.py:321 357 | msgid "More options" 358 | msgstr "مزيد من الخيارات" 359 | 360 | #: youtube_dl_gui/optionsframe.py:322 361 | msgid "Confirm on exit" 362 | msgstr "التأكيد عند الإنهاء" 363 | 364 | #: youtube_dl_gui/optionsframe.py:323 365 | msgid "Confirm item deletion" 366 | msgstr "التأكيد عند حذف عنصر" 367 | 368 | #: youtube_dl_gui/optionsframe.py:324 369 | msgid "Inform me on download completion" 370 | msgstr "نبهني عند انتهاء التنزيلات" 371 | 372 | #: youtube_dl_gui/optionsframe.py:326 373 | msgid "Shutdown on download completion" 374 | msgstr "اوقف التشغيل عند انتهاء التنزيلات" 375 | 376 | #: youtube_dl_gui/optionsframe.py:337 377 | msgid "SUDO password" 378 | msgstr "كلمة مرور المسؤول" 379 | 380 | #: youtube_dl_gui/optionsframe.py:415 youtube_dl_gui/optionsframe.py:816 381 | msgid "In order for the changes to take effect please restart {0}" 382 | msgstr "تطبق التغييرات بعد اعادة التشغيل {0}" 383 | 384 | #: youtube_dl_gui/optionsframe.py:416 youtube_dl_gui/optionsframe.py:817 385 | msgid "Restart" 386 | msgstr "إعادة التشغيل" 387 | 388 | #: youtube_dl_gui/optionsframe.py:463 389 | msgid "high" 390 | msgstr "عالي" 391 | 392 | #: youtube_dl_gui/optionsframe.py:463 393 | msgid "low" 394 | msgstr "منخفض" 395 | 396 | #: youtube_dl_gui/optionsframe.py:463 397 | msgid "mid" 398 | msgstr "متوسط" 399 | 400 | #: youtube_dl_gui/optionsframe.py:468 401 | msgid "Video formats" 402 | msgstr "صيغ الفيديو" 403 | 404 | #: youtube_dl_gui/optionsframe.py:471 405 | msgid "Audio formats" 406 | msgstr "صيغ الصوت" 407 | 408 | #: youtube_dl_gui/optionsframe.py:474 409 | msgid "Post-Process options" 410 | msgstr "خيارات مابعد العملية" 411 | 412 | #: youtube_dl_gui/optionsframe.py:475 413 | msgid "Keep original files" 414 | msgstr "احتفظ بالملفات الأصلية" 415 | 416 | #: youtube_dl_gui/optionsframe.py:476 417 | msgid "Extract audio from video file" 418 | msgstr "استخرج ملف صوتي من ملف الفيديو" 419 | 420 | #: youtube_dl_gui/optionsframe.py:477 421 | msgid "Embed thumbnail in audio file" 422 | msgstr "تضمين الصورة المصغرة في الملف الصوتي" 423 | 424 | #: youtube_dl_gui/optionsframe.py:478 425 | msgid "Add metadata to file" 426 | msgstr "أضف بيانات وصفية للملف" 427 | 428 | #: youtube_dl_gui/optionsframe.py:480 429 | msgid "Audio quality" 430 | msgstr "جودة الصوت" 431 | 432 | #: youtube_dl_gui/optionsframe.py:538 433 | msgid "English" 434 | msgstr "الانجليزية" 435 | 436 | #: youtube_dl_gui/optionsframe.py:539 437 | msgid "French" 438 | msgstr "الفرنسية" 439 | 440 | #: youtube_dl_gui/optionsframe.py:540 441 | msgid "German" 442 | msgstr "الالمانية" 443 | 444 | #: youtube_dl_gui/optionsframe.py:541 445 | msgid "Greek" 446 | msgstr "اليونانية" 447 | 448 | #: youtube_dl_gui/optionsframe.py:542 449 | msgid "Hebrew" 450 | msgstr "العبرية" 451 | 452 | #: youtube_dl_gui/optionsframe.py:543 453 | msgid "Italian" 454 | msgstr "الايطالية" 455 | 456 | #: youtube_dl_gui/optionsframe.py:544 457 | msgid "Portuguese" 458 | msgstr "البرتغالية" 459 | 460 | #: youtube_dl_gui/optionsframe.py:545 461 | msgid "Russian" 462 | msgstr "الروسية" 463 | 464 | #: youtube_dl_gui/optionsframe.py:546 465 | msgid "Spanish" 466 | msgstr "الاسبانية" 467 | 468 | #: youtube_dl_gui/optionsframe.py:547 469 | msgid "Swedish" 470 | msgstr "السويدية" 471 | 472 | #: youtube_dl_gui/optionsframe.py:548 473 | msgid "Turkish" 474 | msgstr "التركية" 475 | 476 | #: youtube_dl_gui/optionsframe.py:564 477 | msgid "None" 478 | msgstr "لاشيء" 479 | 480 | #: youtube_dl_gui/optionsframe.py:565 481 | msgid "Automatic subtitles (YOUTUBE ONLY)" 482 | msgstr "الترجمة التلقائية (YOUTUBE ONLY)" 483 | 484 | #: youtube_dl_gui/optionsframe.py:566 485 | msgid "All available subtitles" 486 | msgstr "كل الترجمات المتوفرة" 487 | 488 | #: youtube_dl_gui/optionsframe.py:567 489 | msgid "Subtitles by language" 490 | msgstr "الترجمات حسب اللغة" 491 | 492 | #: youtube_dl_gui/optionsframe.py:573 493 | msgid "Subtitles" 494 | msgstr "الترجمات" 495 | 496 | #: youtube_dl_gui/optionsframe.py:577 497 | msgid "Subtitles options" 498 | msgstr "خيارات الترجمة" 499 | 500 | #: youtube_dl_gui/optionsframe.py:578 501 | msgid "Embed subtitles into video file (mp4 ONLY)" 502 | msgstr "تضمين ترجمات مصاحبة مع ملف الفيديو (mp4 فقط)" 503 | 504 | #: youtube_dl_gui/optionsframe.py:580 505 | msgid "Playlist" 506 | msgstr "قائمة التشغيل" 507 | 508 | #: youtube_dl_gui/optionsframe.py:586 youtube_dl_gui/optionsframe.py:591 509 | msgid "Max" 510 | msgstr "اقصى" 511 | 512 | #: youtube_dl_gui/optionsframe.py:589 513 | msgid "Filesize" 514 | msgstr "حجم الملف" 515 | 516 | #: youtube_dl_gui/optionsframe.py:594 517 | msgid "Min" 518 | msgstr "اقل" 519 | 520 | #: youtube_dl_gui/optionsframe.py:723 521 | msgid "Retries" 522 | msgstr "فحص" 523 | 524 | #: youtube_dl_gui/optionsframe.py:726 525 | msgid "Authentication" 526 | msgstr "إستيثاق" 527 | 528 | #: youtube_dl_gui/optionsframe.py:728 529 | msgid "Username" 530 | msgstr "اسم المستخدم" 531 | 532 | #: youtube_dl_gui/optionsframe.py:730 533 | msgid "Password" 534 | msgstr "كلمة المرور" 535 | 536 | #: youtube_dl_gui/optionsframe.py:732 537 | msgid "Video password" 538 | msgstr "كلمة المرور الخاصة بالفيديو" 539 | 540 | #: youtube_dl_gui/optionsframe.py:735 541 | msgid "Network" 542 | msgstr "الشبكة" 543 | 544 | #: youtube_dl_gui/optionsframe.py:737 545 | msgid "Proxy" 546 | msgstr "الخادم الوكيل (البروكسي)" 547 | 548 | #: youtube_dl_gui/optionsframe.py:739 549 | msgid "User agent" 550 | msgstr "مستخدم وكيل" 551 | 552 | #: youtube_dl_gui/optionsframe.py:741 553 | msgid "Referer" 554 | msgstr "يدل" 555 | 556 | #: youtube_dl_gui/optionsframe.py:744 557 | msgid "Logging" 558 | msgstr "تسجيل" 559 | 560 | #: youtube_dl_gui/optionsframe.py:746 561 | msgid "Enable log" 562 | msgstr "تفعيل السجّل" 563 | 564 | #: youtube_dl_gui/optionsframe.py:747 565 | msgid "View" 566 | msgstr "عرض" 567 | 568 | #: youtube_dl_gui/optionsframe.py:748 569 | msgid "Clear" 570 | msgstr "مسح" 571 | 572 | #: youtube_dl_gui/optionsframe.py:858 573 | msgid "Youtube-dl command line options (e.g. --help)" 574 | msgstr "Youtube-dl خيارات موجه الاوامر (e.g. --help)" 575 | 576 | #: youtube_dl_gui/optionsframe.py:861 577 | msgid "Extra options" 578 | msgstr "خيارات إضافية" 579 | 580 | #: youtube_dl_gui/optionsframe.py:863 581 | msgid "Debug youtube-dl" 582 | msgstr "youtube-dl تصحيح أخطاء" 583 | 584 | #: youtube_dl_gui/optionsframe.py:864 585 | msgid "Ignore errors" 586 | msgstr "تجاهل الأخطاء" 587 | 588 | #: youtube_dl_gui/optionsframe.py:865 589 | msgid "Ignore youtube-dl config" 590 | msgstr "youtube-dl تجاهل إعدادات" 591 | 592 | #: youtube_dl_gui/optionsframe.py:866 593 | msgid "No mtime" 594 | msgstr "No mtime" 595 | 596 | #: youtube_dl_gui/optionsframe.py:867 597 | msgid "Prefer native HLS" 598 | msgstr "الأصلي HLS تفضيل" 599 | 600 | #: youtube_dl_gui/optionsframe.py:928 601 | msgid "Log Viewer" 602 | msgstr "عارض السجل" 603 | -------------------------------------------------------------------------------- /youtube_dl_gui/locale/cs_CZ/LC_MESSAGES/youtube_dl_gui.po: -------------------------------------------------------------------------------- 1 | # Youtube-dlG localization file. 2 | # FIRST AUTHOR: Sotiris Papadopoulos , 2015. 3 | # 4 | msgid "" 5 | msgstr "" 6 | "Project-Id-Version: youtube-dlg 0.4\n" 7 | "POT-Creation-Date: 2018-01-15 16:42+EET\n" 8 | "PO-Revision-Date: 2017-10-08 19:27+0200\n" 9 | "Last-Translator: Pavel Řehák \n" 10 | "Language-Team: \n" 11 | "Language: cs_CZ\n" 12 | "MIME-Version: 1.0\n" 13 | "Content-Type: text/plain; charset=UTF-8\n" 14 | "Content-Transfer-Encoding: 8bit\n" 15 | "Generated-By: pygettext.py 1.5\n" 16 | "X-Generator: Poedit 1.8.7.1\n" 17 | 18 | #: youtube_dl_gui/__init__.py:91 19 | msgid "Error" 20 | msgstr "" 21 | 22 | #: youtube_dl_gui/__init__.py:91 23 | msgid "Failed to locate youtube-dl and updates are disabled" 24 | msgstr "" 25 | 26 | #: youtube_dl_gui/formats.py:9 youtube_dl_gui/formats.py:109 27 | msgid "ID" 28 | msgstr "ID" 29 | 30 | #: youtube_dl_gui/formats.py:10 youtube_dl_gui/formats.py:110 31 | #: youtube_dl_gui/mainframe.py:140 32 | msgid "Title" 33 | msgstr "Název" 34 | 35 | #: youtube_dl_gui/formats.py:11 youtube_dl_gui/formats.py:111 36 | msgid "Title + ID" 37 | msgstr "Název + ID" 38 | 39 | #: youtube_dl_gui/formats.py:12 youtube_dl_gui/formats.py:112 40 | msgid "Title + Quality" 41 | msgstr "Název + Kvalita" 42 | 43 | #: youtube_dl_gui/formats.py:13 youtube_dl_gui/formats.py:113 44 | msgid "Title + ID + Quality" 45 | msgstr "Název + ID + Kvalita" 46 | 47 | #: youtube_dl_gui/formats.py:14 youtube_dl_gui/formats.py:114 48 | msgid "Custom" 49 | msgstr "Vlastní" 50 | 51 | #: youtube_dl_gui/formats.py:19 youtube_dl_gui/formats.py:119 52 | #: youtube_dl_gui/mainframe.py:503 youtube_dl_gui/mainframe.py:506 53 | msgid "default" 54 | msgstr "výchozí" 55 | 56 | #: youtube_dl_gui/mainframe.py:97 57 | msgid "Enter URLs below" 58 | msgstr "Níže vložte URL adresy" 59 | 60 | #: youtube_dl_gui/mainframe.py:98 61 | msgid "Update" 62 | msgstr "Aktualizovat" 63 | 64 | #: youtube_dl_gui/mainframe.py:99 youtube_dl_gui/optionsframe.py:41 65 | msgid "Options" 66 | msgstr "Volby" 67 | 68 | #: youtube_dl_gui/mainframe.py:100 youtube_dl_gui/optionsframe.py:584 69 | msgid "Stop" 70 | msgstr "Zastavit" 71 | 72 | #: youtube_dl_gui/mainframe.py:101 73 | msgid "Info" 74 | msgstr "Informace" 75 | 76 | #: youtube_dl_gui/mainframe.py:102 77 | msgid "Welcome" 78 | msgstr "Vítejte" 79 | 80 | #: youtube_dl_gui/mainframe.py:103 81 | msgid "Warning" 82 | msgstr "Upozornění" 83 | 84 | #: youtube_dl_gui/mainframe.py:105 85 | msgid "Add" 86 | msgstr "Přidat" 87 | 88 | #: youtube_dl_gui/mainframe.py:106 89 | msgid "Download list" 90 | msgstr "Seznam stahování" 91 | 92 | #: youtube_dl_gui/mainframe.py:107 youtube_dl_gui/mainframe.py:516 93 | #: youtube_dl_gui/mainframe.py:534 94 | msgid "Delete" 95 | msgstr "Smazat" 96 | 97 | #: youtube_dl_gui/mainframe.py:108 98 | msgid "Play" 99 | msgstr "Přehrát" 100 | 101 | #: youtube_dl_gui/mainframe.py:109 102 | msgid "Up" 103 | msgstr "Nahoru" 104 | 105 | #: youtube_dl_gui/mainframe.py:110 106 | msgid "Down" 107 | msgstr "Dolů" 108 | 109 | #: youtube_dl_gui/mainframe.py:111 110 | msgid "Reload" 111 | msgstr "Obnovit" 112 | 113 | #: youtube_dl_gui/mainframe.py:112 youtube_dl_gui/mainframe.py:448 114 | #: youtube_dl_gui/mainframe.py:649 115 | msgid "Pause" 116 | msgstr "Pozastavit" 117 | 118 | #: youtube_dl_gui/mainframe.py:113 youtube_dl_gui/mainframe.py:865 119 | #: youtube_dl_gui/mainframe.py:866 youtube_dl_gui/optionsframe.py:582 120 | msgid "Start" 121 | msgstr "Spustit" 122 | 123 | #: youtube_dl_gui/mainframe.py:114 124 | msgid "About" 125 | msgstr "O aplikaci" 126 | 127 | #: youtube_dl_gui/mainframe.py:115 128 | msgid "View Log" 129 | msgstr "Zobrazit záznam" 130 | 131 | #: youtube_dl_gui/mainframe.py:117 132 | msgid "Successfully downloaded {0} URL(s) in {1} day(s) {2} hour(s) {3} minute(s) {4} second(s)" 133 | msgstr "Úspěšně staženo {0} URL za {1} dní {2} hodin {3} minut {4} sekund" 134 | 135 | #: youtube_dl_gui/mainframe.py:119 136 | msgid "Downloads completed" 137 | msgstr "Stahování dokončeno" 138 | 139 | #: youtube_dl_gui/mainframe.py:120 140 | msgid "Total Progress: {0:.1f}% | Queued ({1}) Paused ({2}) Active ({3}) Completed ({4}) Error ({5})" 141 | msgstr "Celkový postup: {0:.1f}% | Ve frontě ({1}) Pozastaveno ({2}) Aktivní ({3}) Dokončené ({4}) Chyby ({5})" 142 | 143 | #: youtube_dl_gui/mainframe.py:121 144 | msgid "Stopping downloads" 145 | msgstr "Stahování se zastavuje" 146 | 147 | #: youtube_dl_gui/mainframe.py:122 148 | msgid "Downloads stopped" 149 | msgstr "Stahování zastaveno" 150 | 151 | #: youtube_dl_gui/mainframe.py:123 152 | msgid "You need to provide at least one URL" 153 | msgstr "Je třeba zadat alespoň jednu URL adresu" 154 | 155 | #: youtube_dl_gui/mainframe.py:124 156 | msgid "Downloads started" 157 | msgstr "Stahování zahájeno" 158 | 159 | #: youtube_dl_gui/mainframe.py:125 160 | msgid "Choose Directory" 161 | msgstr "Vyberte adresář" 162 | 163 | #: youtube_dl_gui/mainframe.py:127 164 | msgid "Download in progress. Please wait for all downloads to complete" 165 | msgstr "Stahuje se. Počkejte prosím, až se dokončí všechna stahování" 166 | 167 | #: youtube_dl_gui/mainframe.py:128 168 | msgid "Update already in progress" 169 | msgstr "Aktualizace již probíhá" 170 | 171 | #: youtube_dl_gui/mainframe.py:130 172 | msgid "Downloading latest youtube-dl. Please wait..." 173 | msgstr "Stahuje se nejnovější youtube-dl. Počkejte prosím..." 174 | 175 | #: youtube_dl_gui/mainframe.py:131 176 | msgid "Youtube-dl download failed [{0}]" 177 | msgstr "Stahování youtube-dl selhalo [{0}]" 178 | 179 | #: youtube_dl_gui/mainframe.py:132 180 | msgid "Successfully downloaded youtube-dl" 181 | msgstr "Youtube-dl byl úspěšně stažen" 182 | 183 | #: youtube_dl_gui/mainframe.py:134 184 | msgid "Unable to open directory: '{dir}'. The specified path does not exist" 185 | msgstr "Nelze otevřít adresář: '{dir}'. Uvedená cesta neexistuje" 186 | 187 | #: youtube_dl_gui/mainframe.py:136 188 | msgid "Error while shutting down. Make sure you typed the correct password" 189 | msgstr "Při vypínání nastala chyba. Ujistěte se, že jste zadali správné heslo" 190 | 191 | #: youtube_dl_gui/mainframe.py:138 192 | msgid "Shutting down system" 193 | msgstr "Systém se vypíná" 194 | 195 | #: youtube_dl_gui/mainframe.py:141 196 | msgid "Extension" 197 | msgstr "Přípona" 198 | 199 | #: youtube_dl_gui/mainframe.py:142 200 | msgid "Size" 201 | msgstr "Velikost" 202 | 203 | #: youtube_dl_gui/mainframe.py:143 204 | msgid "Percent" 205 | msgstr "Procenta" 206 | 207 | #: youtube_dl_gui/mainframe.py:144 208 | msgid "ETA" 209 | msgstr "ETA" 210 | 211 | #: youtube_dl_gui/mainframe.py:145 212 | msgid "Speed" 213 | msgstr "Rychlost" 214 | 215 | #: youtube_dl_gui/mainframe.py:146 216 | msgid "Status" 217 | msgstr "Stav" 218 | 219 | #: youtube_dl_gui/mainframe.py:235 220 | msgid "Get URL" 221 | msgstr "Kopírovat URL" 222 | 223 | #: youtube_dl_gui/mainframe.py:236 224 | msgid "Get command" 225 | msgstr "Kopírovat příkaz" 226 | 227 | #: youtube_dl_gui/mainframe.py:237 228 | msgid "Open destination" 229 | msgstr "Otevřít cíl" 230 | 231 | #: youtube_dl_gui/mainframe.py:238 232 | msgid "Re-enter" 233 | msgstr "Znovu vložit" 234 | 235 | #: youtube_dl_gui/mainframe.py:458 236 | msgid "Resume" 237 | msgstr "Pokračovat" 238 | 239 | #: youtube_dl_gui/mainframe.py:480 240 | msgid "Video" 241 | msgstr "Video" 242 | 243 | #: youtube_dl_gui/mainframe.py:484 244 | msgid "Audio" 245 | msgstr "Zvuk" 246 | 247 | #: youtube_dl_gui/mainframe.py:516 248 | msgid "No items selected. Please pick an action" 249 | msgstr "Není vybrána žádná položka. Prosím vyberte akci" 250 | 251 | #: youtube_dl_gui/mainframe.py:516 252 | msgid "Remove all" 253 | msgstr "Odstranit vše" 254 | 255 | #: youtube_dl_gui/mainframe.py:516 256 | msgid "Remove completed" 257 | msgstr "Odstranit dokončené" 258 | 259 | #: youtube_dl_gui/mainframe.py:534 260 | msgid "Are you sure you want to remove selected items?" 261 | msgstr "Jste si jistí, že chcete odstranit vybranou položku?" 262 | 263 | #: youtube_dl_gui/mainframe.py:546 264 | msgid "Item is active, cannot remove" 265 | msgstr "Položka je aktivní, nelze ji odstranit" 266 | 267 | #: youtube_dl_gui/mainframe.py:579 268 | msgid "Item is not completed" 269 | msgstr "Položka není dokončena" 270 | 271 | #: youtube_dl_gui/mainframe.py:668 272 | msgid "Update in progress. Please wait for the update to complete" 273 | msgstr "Probíhá aktualizace. Počkejte prosím, až se aktualizace dokončí" 274 | 275 | #: youtube_dl_gui/mainframe.py:716 276 | msgid "Logging is disabled" 277 | msgstr "Záznamy jsou zakázány" 278 | 279 | #: youtube_dl_gui/mainframe.py:891 280 | msgid "Shutdown" 281 | msgstr "Vypnout" 282 | 283 | #: youtube_dl_gui/mainframe.py:891 284 | msgid "Shutting down in {0} second(s)" 285 | msgstr "Vypnutí za {0} sekund" 286 | 287 | #: youtube_dl_gui/mainframe.py:980 288 | msgid "No items to download" 289 | msgstr "Nejsou žádné položky ke stažení" 290 | 291 | #: youtube_dl_gui/mainframe.py:1040 292 | msgid "Updates are disabled for your system. Please use the system's package manager to update youtube-dl." 293 | msgstr "" 294 | 295 | #: youtube_dl_gui/mainframe.py:1065 296 | msgid "Are you sure you want to exit?" 297 | msgstr "Jste si jistí, že chcete aplikaci ukončit?" 298 | 299 | #: youtube_dl_gui/mainframe.py:1065 300 | msgid "Exit" 301 | msgstr "Ukončit" 302 | 303 | #: youtube_dl_gui/mainframe.py:1306 youtube_dl_gui/mainframe.py:1456 304 | msgid "Cancel" 305 | msgstr "Zrušit" 306 | 307 | #: youtube_dl_gui/mainframe.py:1455 308 | msgid "OK" 309 | msgstr "OK" 310 | 311 | #: youtube_dl_gui/optionsframe.py:65 312 | msgid "Reset" 313 | msgstr "Resetovat" 314 | 315 | #: youtube_dl_gui/optionsframe.py:66 316 | msgid "Close" 317 | msgstr "Zavřít" 318 | 319 | #: youtube_dl_gui/optionsframe.py:72 320 | msgid "General" 321 | msgstr "Obecné" 322 | 323 | #: youtube_dl_gui/optionsframe.py:73 324 | msgid "Formats" 325 | msgstr "Formáty" 326 | 327 | #: youtube_dl_gui/optionsframe.py:74 328 | msgid "Downloads" 329 | msgstr "Stahování" 330 | 331 | #: youtube_dl_gui/optionsframe.py:75 332 | msgid "Advanced" 333 | msgstr "Pokročilé" 334 | 335 | #: youtube_dl_gui/optionsframe.py:76 336 | msgid "Extra" 337 | msgstr "Extra" 338 | 339 | #: youtube_dl_gui/optionsframe.py:310 340 | msgid "Language" 341 | msgstr "Jazyk" 342 | 343 | #: youtube_dl_gui/optionsframe.py:313 344 | msgid "Filename format" 345 | msgstr "Formát názvu souboru" 346 | 347 | #: youtube_dl_gui/optionsframe.py:318 348 | msgid "Filename options" 349 | msgstr "Volby pro název souboru" 350 | 351 | #: youtube_dl_gui/optionsframe.py:319 352 | msgid "Restrict filenames to ASCII" 353 | msgstr "Omezit názvy souborů na ASCII" 354 | 355 | #: youtube_dl_gui/optionsframe.py:321 356 | msgid "More options" 357 | msgstr "Další volby" 358 | 359 | #: youtube_dl_gui/optionsframe.py:322 360 | msgid "Confirm on exit" 361 | msgstr "Potvrdit při ukončení" 362 | 363 | #: youtube_dl_gui/optionsframe.py:323 364 | msgid "Confirm item deletion" 365 | msgstr "Potvrdit mazání položky" 366 | 367 | #: youtube_dl_gui/optionsframe.py:324 368 | msgid "Inform me on download completion" 369 | msgstr "Informovat mě, když je stahování dokončeno" 370 | 371 | #: youtube_dl_gui/optionsframe.py:326 372 | msgid "Shutdown on download completion" 373 | msgstr "Vypnout, když je stahování dokončeno" 374 | 375 | #: youtube_dl_gui/optionsframe.py:337 376 | msgid "SUDO password" 377 | msgstr "Heslo SUDO" 378 | 379 | #: youtube_dl_gui/optionsframe.py:415 youtube_dl_gui/optionsframe.py:816 380 | msgid "In order for the changes to take effect please restart {0}" 381 | msgstr "Aby se projevily změny, prosím restartujte {0}" 382 | 383 | #: youtube_dl_gui/optionsframe.py:416 youtube_dl_gui/optionsframe.py:817 384 | msgid "Restart" 385 | msgstr "Restartovat" 386 | 387 | #: youtube_dl_gui/optionsframe.py:463 388 | msgid "high" 389 | msgstr "vysoká" 390 | 391 | #: youtube_dl_gui/optionsframe.py:463 392 | msgid "low" 393 | msgstr "nízká" 394 | 395 | #: youtube_dl_gui/optionsframe.py:463 396 | msgid "mid" 397 | msgstr "střední" 398 | 399 | #: youtube_dl_gui/optionsframe.py:468 400 | msgid "Video formats" 401 | msgstr "Video formáty" 402 | 403 | #: youtube_dl_gui/optionsframe.py:471 404 | msgid "Audio formats" 405 | msgstr "Zvukové formáty" 406 | 407 | #: youtube_dl_gui/optionsframe.py:474 408 | msgid "Post-Process options" 409 | msgstr "Volby po stažení" 410 | 411 | #: youtube_dl_gui/optionsframe.py:475 412 | msgid "Keep original files" 413 | msgstr "Zachovat původní soubory" 414 | 415 | #: youtube_dl_gui/optionsframe.py:476 416 | msgid "Extract audio from video file" 417 | msgstr "Extrahovat zvuk z video souboru" 418 | 419 | #: youtube_dl_gui/optionsframe.py:477 420 | msgid "Embed thumbnail in audio file" 421 | msgstr "Vložit náhled do zvukového souboru" 422 | 423 | #: youtube_dl_gui/optionsframe.py:478 424 | msgid "Add metadata to file" 425 | msgstr "Přidat do souboru metadata" 426 | 427 | #: youtube_dl_gui/optionsframe.py:480 428 | msgid "Audio quality" 429 | msgstr "Kvalita zvuku" 430 | 431 | #: youtube_dl_gui/optionsframe.py:538 432 | msgid "English" 433 | msgstr "Angličtina" 434 | 435 | #: youtube_dl_gui/optionsframe.py:539 436 | msgid "French" 437 | msgstr "Francouzština" 438 | 439 | #: youtube_dl_gui/optionsframe.py:540 440 | msgid "German" 441 | msgstr "Němčina" 442 | 443 | #: youtube_dl_gui/optionsframe.py:541 444 | msgid "Greek" 445 | msgstr "Řečtina" 446 | 447 | #: youtube_dl_gui/optionsframe.py:542 448 | msgid "Hebrew" 449 | msgstr "Hebrejština" 450 | 451 | #: youtube_dl_gui/optionsframe.py:543 452 | msgid "Italian" 453 | msgstr "Italština" 454 | 455 | #: youtube_dl_gui/optionsframe.py:544 456 | msgid "Portuguese" 457 | msgstr "Portugalština" 458 | 459 | #: youtube_dl_gui/optionsframe.py:545 460 | msgid "Russian" 461 | msgstr "Ruština" 462 | 463 | #: youtube_dl_gui/optionsframe.py:546 464 | msgid "Spanish" 465 | msgstr "Španělština" 466 | 467 | #: youtube_dl_gui/optionsframe.py:547 468 | msgid "Swedish" 469 | msgstr "Švédština" 470 | 471 | #: youtube_dl_gui/optionsframe.py:548 472 | msgid "Turkish" 473 | msgstr "Turečtina" 474 | 475 | #: youtube_dl_gui/optionsframe.py:564 476 | msgid "None" 477 | msgstr "Žádné" 478 | 479 | #: youtube_dl_gui/optionsframe.py:565 480 | msgid "Automatic subtitles (YOUTUBE ONLY)" 481 | msgstr "Automatické titulky (POUZE YOUTUBE)" 482 | 483 | #: youtube_dl_gui/optionsframe.py:566 484 | msgid "All available subtitles" 485 | msgstr "Všechny dostupné titulky" 486 | 487 | #: youtube_dl_gui/optionsframe.py:567 488 | msgid "Subtitles by language" 489 | msgstr "Titulky dle jazyka" 490 | 491 | #: youtube_dl_gui/optionsframe.py:573 492 | msgid "Subtitles" 493 | msgstr "Titulky" 494 | 495 | #: youtube_dl_gui/optionsframe.py:577 496 | msgid "Subtitles options" 497 | msgstr "Volby pro titulky" 498 | 499 | #: youtube_dl_gui/optionsframe.py:578 500 | msgid "Embed subtitles into video file (mp4 ONLY)" 501 | msgstr "Vložit titulky do video souboru (POUZE mp4)" 502 | 503 | #: youtube_dl_gui/optionsframe.py:580 504 | msgid "Playlist" 505 | msgstr "Seznam k přehrání" 506 | 507 | #: youtube_dl_gui/optionsframe.py:586 youtube_dl_gui/optionsframe.py:591 508 | msgid "Max" 509 | msgstr "Max" 510 | 511 | #: youtube_dl_gui/optionsframe.py:589 512 | msgid "Filesize" 513 | msgstr "Velikost souboru" 514 | 515 | #: youtube_dl_gui/optionsframe.py:594 516 | msgid "Min" 517 | msgstr "Min" 518 | 519 | #: youtube_dl_gui/optionsframe.py:723 520 | msgid "Retries" 521 | msgstr "Opakování" 522 | 523 | #: youtube_dl_gui/optionsframe.py:726 524 | msgid "Authentication" 525 | msgstr "Ověření" 526 | 527 | #: youtube_dl_gui/optionsframe.py:728 528 | msgid "Username" 529 | msgstr "Uživatelské jméno" 530 | 531 | #: youtube_dl_gui/optionsframe.py:730 532 | msgid "Password" 533 | msgstr "Heslo" 534 | 535 | #: youtube_dl_gui/optionsframe.py:732 536 | msgid "Video password" 537 | msgstr "Heslo videa" 538 | 539 | #: youtube_dl_gui/optionsframe.py:735 540 | msgid "Network" 541 | msgstr "Síť" 542 | 543 | #: youtube_dl_gui/optionsframe.py:737 544 | msgid "Proxy" 545 | msgstr "Proxy" 546 | 547 | #: youtube_dl_gui/optionsframe.py:739 548 | msgid "User agent" 549 | msgstr "Uživatelský agent" 550 | 551 | #: youtube_dl_gui/optionsframe.py:741 552 | msgid "Referer" 553 | msgstr "" 554 | 555 | #: youtube_dl_gui/optionsframe.py:744 556 | msgid "Logging" 557 | msgstr "Záznamy" 558 | 559 | #: youtube_dl_gui/optionsframe.py:746 560 | msgid "Enable log" 561 | msgstr "Povolit záznam" 562 | 563 | #: youtube_dl_gui/optionsframe.py:747 564 | msgid "View" 565 | msgstr "Zobrazit" 566 | 567 | #: youtube_dl_gui/optionsframe.py:748 568 | msgid "Clear" 569 | msgstr "Vymazat" 570 | 571 | #: youtube_dl_gui/optionsframe.py:858 572 | msgid "Youtube-dl command line options (e.g. --help)" 573 | msgstr "Volby příkazového řádku pro youtube-dl (např. --help)" 574 | 575 | #: youtube_dl_gui/optionsframe.py:861 576 | msgid "Extra options" 577 | msgstr "Další volby" 578 | 579 | #: youtube_dl_gui/optionsframe.py:863 580 | msgid "Debug youtube-dl" 581 | msgstr "Debug youtube-dl" 582 | 583 | #: youtube_dl_gui/optionsframe.py:864 584 | msgid "Ignore errors" 585 | msgstr "Ignorovat chyby" 586 | 587 | #: youtube_dl_gui/optionsframe.py:865 588 | msgid "Ignore youtube-dl config" 589 | msgstr "Ignorovat nastavení youtube-dl" 590 | 591 | #: youtube_dl_gui/optionsframe.py:866 592 | msgid "No mtime" 593 | msgstr "" 594 | 595 | #: youtube_dl_gui/optionsframe.py:867 596 | msgid "Prefer native HLS" 597 | msgstr "Preferovat nativní HLS" 598 | 599 | #: youtube_dl_gui/optionsframe.py:928 600 | msgid "Log Viewer" 601 | msgstr "Prohlížeč záznamu událostí" 602 | -------------------------------------------------------------------------------- /youtube_dl_gui/locale/en_US/LC_MESSAGES/youtube_dl_gui.po: -------------------------------------------------------------------------------- 1 | # Youtube-dlG localization file. 2 | # FIRST AUTHOR: Sotiris Papadopoulos , 2015. 3 | # 4 | msgid "" 5 | msgstr "" 6 | "Project-Id-Version: youtube-dlg 0.4\n" 7 | "POT-Creation-Date: 2018-01-15 16:42+EET\n" 8 | "PO-Revision-Date: 2017-06-15 17:14+EEST\n" 9 | "Last-Translator: Sotiris Papadopoulos \n" 10 | "Language-Team: en\n" 11 | "Language: \n" 12 | "MIME-Version: 1.0\n" 13 | "Content-Type: text/plain; charset=UTF-8\n" 14 | "Content-Transfer-Encoding: 8bit\n" 15 | "Generated-By: pygettext.py 1.5\n" 16 | 17 | #: youtube_dl_gui/__init__.py:91 18 | msgid "Error" 19 | msgstr "" 20 | 21 | #: youtube_dl_gui/__init__.py:91 22 | msgid "Failed to locate youtube-dl and updates are disabled" 23 | msgstr "" 24 | 25 | #: youtube_dl_gui/formats.py:9 youtube_dl_gui/formats.py:109 26 | msgid "ID" 27 | msgstr "" 28 | 29 | #: youtube_dl_gui/formats.py:10 youtube_dl_gui/formats.py:110 30 | #: youtube_dl_gui/mainframe.py:140 31 | msgid "Title" 32 | msgstr "" 33 | 34 | #: youtube_dl_gui/formats.py:11 youtube_dl_gui/formats.py:111 35 | msgid "Title + ID" 36 | msgstr "" 37 | 38 | #: youtube_dl_gui/formats.py:12 youtube_dl_gui/formats.py:112 39 | msgid "Title + Quality" 40 | msgstr "" 41 | 42 | #: youtube_dl_gui/formats.py:13 youtube_dl_gui/formats.py:113 43 | msgid "Title + ID + Quality" 44 | msgstr "" 45 | 46 | #: youtube_dl_gui/formats.py:14 youtube_dl_gui/formats.py:114 47 | msgid "Custom" 48 | msgstr "" 49 | 50 | #: youtube_dl_gui/formats.py:19 youtube_dl_gui/formats.py:119 51 | #: youtube_dl_gui/mainframe.py:503 youtube_dl_gui/mainframe.py:506 52 | msgid "default" 53 | msgstr "" 54 | 55 | #: youtube_dl_gui/mainframe.py:97 56 | msgid "Enter URLs below" 57 | msgstr "" 58 | 59 | #: youtube_dl_gui/mainframe.py:98 60 | msgid "Update" 61 | msgstr "" 62 | 63 | #: youtube_dl_gui/mainframe.py:99 youtube_dl_gui/optionsframe.py:41 64 | msgid "Options" 65 | msgstr "" 66 | 67 | #: youtube_dl_gui/mainframe.py:100 youtube_dl_gui/optionsframe.py:584 68 | msgid "Stop" 69 | msgstr "" 70 | 71 | #: youtube_dl_gui/mainframe.py:101 72 | msgid "Info" 73 | msgstr "" 74 | 75 | #: youtube_dl_gui/mainframe.py:102 76 | msgid "Welcome" 77 | msgstr "" 78 | 79 | #: youtube_dl_gui/mainframe.py:103 80 | msgid "Warning" 81 | msgstr "" 82 | 83 | #: youtube_dl_gui/mainframe.py:105 84 | msgid "Add" 85 | msgstr "" 86 | 87 | #: youtube_dl_gui/mainframe.py:106 88 | msgid "Download list" 89 | msgstr "" 90 | 91 | #: youtube_dl_gui/mainframe.py:107 youtube_dl_gui/mainframe.py:516 92 | #: youtube_dl_gui/mainframe.py:534 93 | msgid "Delete" 94 | msgstr "" 95 | 96 | #: youtube_dl_gui/mainframe.py:108 97 | msgid "Play" 98 | msgstr "" 99 | 100 | #: youtube_dl_gui/mainframe.py:109 101 | msgid "Up" 102 | msgstr "" 103 | 104 | #: youtube_dl_gui/mainframe.py:110 105 | msgid "Down" 106 | msgstr "" 107 | 108 | #: youtube_dl_gui/mainframe.py:111 109 | msgid "Reload" 110 | msgstr "" 111 | 112 | #: youtube_dl_gui/mainframe.py:112 youtube_dl_gui/mainframe.py:448 113 | #: youtube_dl_gui/mainframe.py:649 114 | msgid "Pause" 115 | msgstr "" 116 | 117 | #: youtube_dl_gui/mainframe.py:113 youtube_dl_gui/mainframe.py:865 118 | #: youtube_dl_gui/mainframe.py:866 youtube_dl_gui/optionsframe.py:582 119 | msgid "Start" 120 | msgstr "" 121 | 122 | #: youtube_dl_gui/mainframe.py:114 123 | msgid "About" 124 | msgstr "" 125 | 126 | #: youtube_dl_gui/mainframe.py:115 127 | msgid "View Log" 128 | msgstr "" 129 | 130 | #: youtube_dl_gui/mainframe.py:117 131 | msgid "Successfully downloaded {0} URL(s) in {1} day(s) {2} hour(s) {3} minute(s) {4} second(s)" 132 | msgstr "" 133 | 134 | #: youtube_dl_gui/mainframe.py:119 135 | msgid "Downloads completed" 136 | msgstr "" 137 | 138 | #: youtube_dl_gui/mainframe.py:120 139 | msgid "Total Progress: {0:.1f}% | Queued ({1}) Paused ({2}) Active ({3}) Completed ({4}) Error ({5})" 140 | msgstr "" 141 | 142 | #: youtube_dl_gui/mainframe.py:121 143 | msgid "Stopping downloads" 144 | msgstr "" 145 | 146 | #: youtube_dl_gui/mainframe.py:122 147 | msgid "Downloads stopped" 148 | msgstr "" 149 | 150 | #: youtube_dl_gui/mainframe.py:123 151 | msgid "You need to provide at least one URL" 152 | msgstr "" 153 | 154 | #: youtube_dl_gui/mainframe.py:124 155 | msgid "Downloads started" 156 | msgstr "" 157 | 158 | #: youtube_dl_gui/mainframe.py:125 159 | msgid "Choose Directory" 160 | msgstr "" 161 | 162 | #: youtube_dl_gui/mainframe.py:127 163 | msgid "Download in progress. Please wait for all downloads to complete" 164 | msgstr "" 165 | 166 | #: youtube_dl_gui/mainframe.py:128 167 | msgid "Update already in progress" 168 | msgstr "" 169 | 170 | #: youtube_dl_gui/mainframe.py:130 171 | msgid "Downloading latest youtube-dl. Please wait..." 172 | msgstr "" 173 | 174 | #: youtube_dl_gui/mainframe.py:131 175 | msgid "Youtube-dl download failed [{0}]" 176 | msgstr "" 177 | 178 | #: youtube_dl_gui/mainframe.py:132 179 | msgid "Successfully downloaded youtube-dl" 180 | msgstr "" 181 | 182 | #: youtube_dl_gui/mainframe.py:134 183 | msgid "Unable to open directory: '{dir}'. The specified path does not exist" 184 | msgstr "" 185 | 186 | #: youtube_dl_gui/mainframe.py:136 187 | msgid "Error while shutting down. Make sure you typed the correct password" 188 | msgstr "" 189 | 190 | #: youtube_dl_gui/mainframe.py:138 191 | msgid "Shutting down system" 192 | msgstr "" 193 | 194 | #: youtube_dl_gui/mainframe.py:141 195 | msgid "Extension" 196 | msgstr "" 197 | 198 | #: youtube_dl_gui/mainframe.py:142 199 | msgid "Size" 200 | msgstr "" 201 | 202 | #: youtube_dl_gui/mainframe.py:143 203 | msgid "Percent" 204 | msgstr "" 205 | 206 | #: youtube_dl_gui/mainframe.py:144 207 | msgid "ETA" 208 | msgstr "" 209 | 210 | #: youtube_dl_gui/mainframe.py:145 211 | msgid "Speed" 212 | msgstr "" 213 | 214 | #: youtube_dl_gui/mainframe.py:146 215 | msgid "Status" 216 | msgstr "" 217 | 218 | #: youtube_dl_gui/mainframe.py:235 219 | msgid "Get URL" 220 | msgstr "" 221 | 222 | #: youtube_dl_gui/mainframe.py:236 223 | msgid "Get command" 224 | msgstr "" 225 | 226 | #: youtube_dl_gui/mainframe.py:237 227 | msgid "Open destination" 228 | msgstr "" 229 | 230 | #: youtube_dl_gui/mainframe.py:238 231 | msgid "Re-enter" 232 | msgstr "" 233 | 234 | #: youtube_dl_gui/mainframe.py:458 235 | msgid "Resume" 236 | msgstr "" 237 | 238 | #: youtube_dl_gui/mainframe.py:480 239 | msgid "Video" 240 | msgstr "" 241 | 242 | #: youtube_dl_gui/mainframe.py:484 243 | msgid "Audio" 244 | msgstr "" 245 | 246 | #: youtube_dl_gui/mainframe.py:516 247 | msgid "No items selected. Please pick an action" 248 | msgstr "" 249 | 250 | #: youtube_dl_gui/mainframe.py:516 251 | msgid "Remove all" 252 | msgstr "" 253 | 254 | #: youtube_dl_gui/mainframe.py:516 255 | msgid "Remove completed" 256 | msgstr "" 257 | 258 | #: youtube_dl_gui/mainframe.py:534 259 | msgid "Are you sure you want to remove selected items?" 260 | msgstr "" 261 | 262 | #: youtube_dl_gui/mainframe.py:546 263 | msgid "Item is active, cannot remove" 264 | msgstr "" 265 | 266 | #: youtube_dl_gui/mainframe.py:579 267 | msgid "Item is not completed" 268 | msgstr "" 269 | 270 | #: youtube_dl_gui/mainframe.py:668 271 | msgid "Update in progress. Please wait for the update to complete" 272 | msgstr "" 273 | 274 | #: youtube_dl_gui/mainframe.py:716 275 | msgid "Logging is disabled" 276 | msgstr "" 277 | 278 | #: youtube_dl_gui/mainframe.py:891 279 | msgid "Shutdown" 280 | msgstr "" 281 | 282 | #: youtube_dl_gui/mainframe.py:891 283 | msgid "Shutting down in {0} second(s)" 284 | msgstr "" 285 | 286 | #: youtube_dl_gui/mainframe.py:980 287 | msgid "No items to download" 288 | msgstr "" 289 | 290 | #: youtube_dl_gui/mainframe.py:1040 291 | msgid "Updates are disabled for your system. Please use the system's package manager to update youtube-dl." 292 | msgstr "" 293 | 294 | #: youtube_dl_gui/mainframe.py:1065 295 | msgid "Are you sure you want to exit?" 296 | msgstr "" 297 | 298 | #: youtube_dl_gui/mainframe.py:1065 299 | msgid "Exit" 300 | msgstr "" 301 | 302 | #: youtube_dl_gui/mainframe.py:1306 youtube_dl_gui/mainframe.py:1456 303 | msgid "Cancel" 304 | msgstr "" 305 | 306 | #: youtube_dl_gui/mainframe.py:1455 307 | msgid "OK" 308 | msgstr "" 309 | 310 | #: youtube_dl_gui/optionsframe.py:65 311 | msgid "Reset" 312 | msgstr "" 313 | 314 | #: youtube_dl_gui/optionsframe.py:66 315 | msgid "Close" 316 | msgstr "" 317 | 318 | #: youtube_dl_gui/optionsframe.py:72 319 | msgid "General" 320 | msgstr "" 321 | 322 | #: youtube_dl_gui/optionsframe.py:73 323 | msgid "Formats" 324 | msgstr "" 325 | 326 | #: youtube_dl_gui/optionsframe.py:74 327 | msgid "Downloads" 328 | msgstr "" 329 | 330 | #: youtube_dl_gui/optionsframe.py:75 331 | msgid "Advanced" 332 | msgstr "" 333 | 334 | #: youtube_dl_gui/optionsframe.py:76 335 | msgid "Extra" 336 | msgstr "" 337 | 338 | #: youtube_dl_gui/optionsframe.py:310 339 | msgid "Language" 340 | msgstr "" 341 | 342 | #: youtube_dl_gui/optionsframe.py:313 343 | msgid "Filename format" 344 | msgstr "" 345 | 346 | #: youtube_dl_gui/optionsframe.py:318 347 | msgid "Filename options" 348 | msgstr "" 349 | 350 | #: youtube_dl_gui/optionsframe.py:319 351 | msgid "Restrict filenames to ASCII" 352 | msgstr "" 353 | 354 | #: youtube_dl_gui/optionsframe.py:321 355 | msgid "More options" 356 | msgstr "" 357 | 358 | #: youtube_dl_gui/optionsframe.py:322 359 | msgid "Confirm on exit" 360 | msgstr "" 361 | 362 | #: youtube_dl_gui/optionsframe.py:323 363 | msgid "Confirm item deletion" 364 | msgstr "" 365 | 366 | #: youtube_dl_gui/optionsframe.py:324 367 | msgid "Inform me on download completion" 368 | msgstr "" 369 | 370 | #: youtube_dl_gui/optionsframe.py:326 371 | msgid "Shutdown on download completion" 372 | msgstr "" 373 | 374 | #: youtube_dl_gui/optionsframe.py:337 375 | msgid "SUDO password" 376 | msgstr "" 377 | 378 | #: youtube_dl_gui/optionsframe.py:415 youtube_dl_gui/optionsframe.py:816 379 | msgid "In order for the changes to take effect please restart {0}" 380 | msgstr "" 381 | 382 | #: youtube_dl_gui/optionsframe.py:416 youtube_dl_gui/optionsframe.py:817 383 | msgid "Restart" 384 | msgstr "" 385 | 386 | #: youtube_dl_gui/optionsframe.py:463 387 | msgid "high" 388 | msgstr "" 389 | 390 | #: youtube_dl_gui/optionsframe.py:463 391 | msgid "low" 392 | msgstr "" 393 | 394 | #: youtube_dl_gui/optionsframe.py:463 395 | msgid "mid" 396 | msgstr "" 397 | 398 | #: youtube_dl_gui/optionsframe.py:468 399 | msgid "Video formats" 400 | msgstr "" 401 | 402 | #: youtube_dl_gui/optionsframe.py:471 403 | msgid "Audio formats" 404 | msgstr "" 405 | 406 | #: youtube_dl_gui/optionsframe.py:474 407 | msgid "Post-Process options" 408 | msgstr "" 409 | 410 | #: youtube_dl_gui/optionsframe.py:475 411 | msgid "Keep original files" 412 | msgstr "" 413 | 414 | #: youtube_dl_gui/optionsframe.py:476 415 | msgid "Extract audio from video file" 416 | msgstr "" 417 | 418 | #: youtube_dl_gui/optionsframe.py:477 419 | msgid "Embed thumbnail in audio file" 420 | msgstr "" 421 | 422 | #: youtube_dl_gui/optionsframe.py:478 423 | msgid "Add metadata to file" 424 | msgstr "" 425 | 426 | #: youtube_dl_gui/optionsframe.py:480 427 | msgid "Audio quality" 428 | msgstr "" 429 | 430 | #: youtube_dl_gui/optionsframe.py:538 431 | msgid "English" 432 | msgstr "" 433 | 434 | #: youtube_dl_gui/optionsframe.py:539 435 | msgid "French" 436 | msgstr "" 437 | 438 | #: youtube_dl_gui/optionsframe.py:540 439 | msgid "German" 440 | msgstr "" 441 | 442 | #: youtube_dl_gui/optionsframe.py:541 443 | msgid "Greek" 444 | msgstr "" 445 | 446 | #: youtube_dl_gui/optionsframe.py:542 447 | msgid "Hebrew" 448 | msgstr "" 449 | 450 | #: youtube_dl_gui/optionsframe.py:543 451 | msgid "Italian" 452 | msgstr "" 453 | 454 | #: youtube_dl_gui/optionsframe.py:544 455 | msgid "Portuguese" 456 | msgstr "" 457 | 458 | #: youtube_dl_gui/optionsframe.py:545 459 | msgid "Russian" 460 | msgstr "" 461 | 462 | #: youtube_dl_gui/optionsframe.py:546 463 | msgid "Spanish" 464 | msgstr "" 465 | 466 | #: youtube_dl_gui/optionsframe.py:547 467 | msgid "Swedish" 468 | msgstr "" 469 | 470 | #: youtube_dl_gui/optionsframe.py:548 471 | msgid "Turkish" 472 | msgstr "" 473 | 474 | #: youtube_dl_gui/optionsframe.py:564 475 | msgid "None" 476 | msgstr "" 477 | 478 | #: youtube_dl_gui/optionsframe.py:565 479 | msgid "Automatic subtitles (YOUTUBE ONLY)" 480 | msgstr "" 481 | 482 | #: youtube_dl_gui/optionsframe.py:566 483 | msgid "All available subtitles" 484 | msgstr "" 485 | 486 | #: youtube_dl_gui/optionsframe.py:567 487 | msgid "Subtitles by language" 488 | msgstr "" 489 | 490 | #: youtube_dl_gui/optionsframe.py:573 491 | msgid "Subtitles" 492 | msgstr "" 493 | 494 | #: youtube_dl_gui/optionsframe.py:577 495 | msgid "Subtitles options" 496 | msgstr "" 497 | 498 | #: youtube_dl_gui/optionsframe.py:578 499 | msgid "Embed subtitles into video file (mp4 ONLY)" 500 | msgstr "" 501 | 502 | #: youtube_dl_gui/optionsframe.py:580 503 | msgid "Playlist" 504 | msgstr "" 505 | 506 | #: youtube_dl_gui/optionsframe.py:586 youtube_dl_gui/optionsframe.py:591 507 | msgid "Max" 508 | msgstr "" 509 | 510 | #: youtube_dl_gui/optionsframe.py:589 511 | msgid "Filesize" 512 | msgstr "" 513 | 514 | #: youtube_dl_gui/optionsframe.py:594 515 | msgid "Min" 516 | msgstr "" 517 | 518 | #: youtube_dl_gui/optionsframe.py:723 519 | msgid "Retries" 520 | msgstr "" 521 | 522 | #: youtube_dl_gui/optionsframe.py:726 523 | msgid "Authentication" 524 | msgstr "" 525 | 526 | #: youtube_dl_gui/optionsframe.py:728 527 | msgid "Username" 528 | msgstr "" 529 | 530 | #: youtube_dl_gui/optionsframe.py:730 531 | msgid "Password" 532 | msgstr "" 533 | 534 | #: youtube_dl_gui/optionsframe.py:732 535 | msgid "Video password" 536 | msgstr "" 537 | 538 | #: youtube_dl_gui/optionsframe.py:735 539 | msgid "Network" 540 | msgstr "" 541 | 542 | #: youtube_dl_gui/optionsframe.py:737 543 | msgid "Proxy" 544 | msgstr "" 545 | 546 | #: youtube_dl_gui/optionsframe.py:739 547 | msgid "User agent" 548 | msgstr "" 549 | 550 | #: youtube_dl_gui/optionsframe.py:741 551 | msgid "Referer" 552 | msgstr "" 553 | 554 | #: youtube_dl_gui/optionsframe.py:744 555 | msgid "Logging" 556 | msgstr "" 557 | 558 | #: youtube_dl_gui/optionsframe.py:746 559 | msgid "Enable log" 560 | msgstr "" 561 | 562 | #: youtube_dl_gui/optionsframe.py:747 563 | msgid "View" 564 | msgstr "" 565 | 566 | #: youtube_dl_gui/optionsframe.py:748 567 | msgid "Clear" 568 | msgstr "" 569 | 570 | #: youtube_dl_gui/optionsframe.py:858 571 | msgid "Youtube-dl command line options (e.g. --help)" 572 | msgstr "" 573 | 574 | #: youtube_dl_gui/optionsframe.py:861 575 | msgid "Extra options" 576 | msgstr "" 577 | 578 | #: youtube_dl_gui/optionsframe.py:863 579 | msgid "Debug youtube-dl" 580 | msgstr "" 581 | 582 | #: youtube_dl_gui/optionsframe.py:864 583 | msgid "Ignore errors" 584 | msgstr "" 585 | 586 | #: youtube_dl_gui/optionsframe.py:865 587 | msgid "Ignore youtube-dl config" 588 | msgstr "" 589 | 590 | #: youtube_dl_gui/optionsframe.py:866 591 | msgid "No mtime" 592 | msgstr "" 593 | 594 | #: youtube_dl_gui/optionsframe.py:867 595 | msgid "Prefer native HLS" 596 | msgstr "" 597 | 598 | #: youtube_dl_gui/optionsframe.py:928 599 | msgid "Log Viewer" 600 | msgstr "" 601 | -------------------------------------------------------------------------------- /youtube_dl_gui/locale/ja_JP/LC_MESSAGES/youtube_dl_gui.po: -------------------------------------------------------------------------------- 1 | # Youtube-dlG localization file. 2 | # FIRST AUTHOR: Sotiris Papadopoulos , 2015. 3 | # 4 | msgid "" 5 | msgstr "" 6 | "Project-Id-Version: youtube-dlg 0.4\n" 7 | "POT-Creation-Date: 2018-01-15 16:42+EET\n" 8 | "PO-Revision-Date: 2017-09-26 00:44+JST\n" 9 | "Last-Translator: Mitsuya Tsujikawa \n" 10 | "Language-Team: ja\n" 11 | "Language: JP\n" 12 | "MIME-Version: 1.0\n" 13 | "Content-Type: text/plain; charset=UTF-8\n" 14 | "Content-Transfer-Encoding: 8bit\n" 15 | "Generated-By: pygettext.py 1.5\n" 16 | 17 | #: youtube_dl_gui/__init__.py:91 18 | msgid "Error" 19 | msgstr "" 20 | 21 | #: youtube_dl_gui/__init__.py:91 22 | msgid "Failed to locate youtube-dl and updates are disabled" 23 | msgstr "" 24 | 25 | #: youtube_dl_gui/formats.py:9 youtube_dl_gui/formats.py:109 26 | msgid "ID" 27 | msgstr "ID" 28 | 29 | #: youtube_dl_gui/formats.py:10 youtube_dl_gui/formats.py:110 30 | #: youtube_dl_gui/mainframe.py:140 31 | msgid "Title" 32 | msgstr "タイトル" 33 | 34 | #: youtube_dl_gui/formats.py:11 youtube_dl_gui/formats.py:111 35 | msgid "Title + ID" 36 | msgstr "タイトル + ID" 37 | 38 | #: youtube_dl_gui/formats.py:12 youtube_dl_gui/formats.py:112 39 | msgid "Title + Quality" 40 | msgstr "タイトル + 品質" 41 | 42 | #: youtube_dl_gui/formats.py:13 youtube_dl_gui/formats.py:113 43 | msgid "Title + ID + Quality" 44 | msgstr "タイトル + ID + 品質" 45 | 46 | #: youtube_dl_gui/formats.py:14 youtube_dl_gui/formats.py:114 47 | msgid "Custom" 48 | msgstr "カスタム" 49 | 50 | #: youtube_dl_gui/formats.py:19 youtube_dl_gui/formats.py:119 51 | #: youtube_dl_gui/mainframe.py:503 youtube_dl_gui/mainframe.py:506 52 | msgid "default" 53 | msgstr "既定値" 54 | 55 | #: youtube_dl_gui/mainframe.py:97 56 | msgid "Enter URLs below" 57 | msgstr "下の欄にURLを入力してください" 58 | 59 | #: youtube_dl_gui/mainframe.py:98 60 | msgid "Update" 61 | msgstr "更新" 62 | 63 | #: youtube_dl_gui/mainframe.py:99 youtube_dl_gui/optionsframe.py:41 64 | msgid "Options" 65 | msgstr "設定" 66 | 67 | #: youtube_dl_gui/mainframe.py:100 youtube_dl_gui/optionsframe.py:584 68 | msgid "Stop" 69 | msgstr "停止" 70 | 71 | #: youtube_dl_gui/mainframe.py:101 72 | msgid "Info" 73 | msgstr "情報" 74 | 75 | #: youtube_dl_gui/mainframe.py:102 76 | msgid "Welcome" 77 | msgstr "ようこそ" 78 | 79 | #: youtube_dl_gui/mainframe.py:103 80 | msgid "Warning" 81 | msgstr "警告" 82 | 83 | #: youtube_dl_gui/mainframe.py:105 84 | msgid "Add" 85 | msgstr "追加" 86 | 87 | #: youtube_dl_gui/mainframe.py:106 88 | msgid "Download list" 89 | msgstr "ダウンロードリスト" 90 | 91 | #: youtube_dl_gui/mainframe.py:107 youtube_dl_gui/mainframe.py:516 92 | #: youtube_dl_gui/mainframe.py:534 93 | msgid "Delete" 94 | msgstr "削除" 95 | 96 | #: youtube_dl_gui/mainframe.py:108 97 | msgid "Play" 98 | msgstr "再生" 99 | 100 | #: youtube_dl_gui/mainframe.py:109 101 | msgid "Up" 102 | msgstr "上へ" 103 | 104 | #: youtube_dl_gui/mainframe.py:110 105 | msgid "Down" 106 | msgstr "下へ" 107 | 108 | #: youtube_dl_gui/mainframe.py:111 109 | msgid "Reload" 110 | msgstr "再読込み" 111 | 112 | #: youtube_dl_gui/mainframe.py:112 youtube_dl_gui/mainframe.py:448 113 | #: youtube_dl_gui/mainframe.py:649 114 | msgid "Pause" 115 | msgstr "一時停止" 116 | 117 | #: youtube_dl_gui/mainframe.py:113 youtube_dl_gui/mainframe.py:865 118 | #: youtube_dl_gui/mainframe.py:866 youtube_dl_gui/optionsframe.py:582 119 | msgid "Start" 120 | msgstr "ダウンロード開始" 121 | 122 | #: youtube_dl_gui/mainframe.py:114 123 | msgid "About" 124 | msgstr "製品情報" 125 | 126 | #: youtube_dl_gui/mainframe.py:115 127 | msgid "View Log" 128 | msgstr "ログの表示" 129 | 130 | #: youtube_dl_gui/mainframe.py:117 131 | msgid "Successfully downloaded {0} URL(s) in {1} day(s) {2} hour(s) {3} minute(s) {4} second(s)" 132 | msgstr "正常にダウンロードしました: {0} URL(s) in {1} 日 {2} 時 {3} 分 {4} 秒" 133 | 134 | #: youtube_dl_gui/mainframe.py:119 135 | msgid "Downloads completed" 136 | msgstr "ダウンロード完了" 137 | 138 | #: youtube_dl_gui/mainframe.py:120 139 | msgid "Total Progress: {0:.1f}% | Queued ({1}) Paused ({2}) Active ({3}) Completed ({4}) Error ({5})" 140 | msgstr "進捗状況: {0:.1f}% | 待機 ({1}) 停止 ({2}) 進行中 ({3}) 完了 ({4}) エラー ({5})" 141 | 142 | #: youtube_dl_gui/mainframe.py:121 143 | msgid "Stopping downloads" 144 | msgstr "ダウンロードを停止中" 145 | 146 | #: youtube_dl_gui/mainframe.py:122 147 | msgid "Downloads stopped" 148 | msgstr "ダウンロードを停止しました" 149 | 150 | #: youtube_dl_gui/mainframe.py:123 151 | msgid "You need to provide at least one URL" 152 | msgstr "最低でも1つはURLを入力する必要があります" 153 | 154 | #: youtube_dl_gui/mainframe.py:124 155 | msgid "Downloads started" 156 | msgstr "ダウンロードを開始しました" 157 | 158 | #: youtube_dl_gui/mainframe.py:125 159 | msgid "Choose Directory" 160 | msgstr "フォルダを選択してください" 161 | 162 | #: youtube_dl_gui/mainframe.py:127 163 | msgid "Download in progress. Please wait for all downloads to complete" 164 | msgstr "ダウンロード中です。すべて完了するまでお待ちください。" 165 | 166 | #: youtube_dl_gui/mainframe.py:128 167 | msgid "Update already in progress" 168 | msgstr "更新中です" 169 | 170 | #: youtube_dl_gui/mainframe.py:130 171 | msgid "Downloading latest youtube-dl. Please wait..." 172 | msgstr "youtube-dlの最新版をダウンロードしています。お待ちください..." 173 | 174 | #: youtube_dl_gui/mainframe.py:131 175 | msgid "Youtube-dl download failed [{0}]" 176 | msgstr "Youtube-dlのダウンロードに失敗しました [{0}]" 177 | 178 | #: youtube_dl_gui/mainframe.py:132 179 | msgid "Successfully downloaded youtube-dl" 180 | msgstr "Youtube-dlのダウンロードに成功しました" 181 | 182 | #: youtube_dl_gui/mainframe.py:134 183 | msgid "Unable to open directory: '{dir}'. The specified path does not exist" 184 | msgstr "フォルダを開くことができません: '{dir}'。指定されたパスが存在しません。" 185 | 186 | #: youtube_dl_gui/mainframe.py:136 187 | msgid "Error while shutting down. Make sure you typed the correct password" 188 | msgstr "シャットダウンの際にエラーが発生しました。必ず正確なパスワードを入力したことを確認してください" 189 | 190 | #: youtube_dl_gui/mainframe.py:138 191 | msgid "Shutting down system" 192 | msgstr "システムをシャットダウンします" 193 | 194 | #: youtube_dl_gui/mainframe.py:141 195 | msgid "Extension" 196 | msgstr "拡張子" 197 | 198 | #: youtube_dl_gui/mainframe.py:142 199 | msgid "Size" 200 | msgstr "サイズ" 201 | 202 | #: youtube_dl_gui/mainframe.py:143 203 | msgid "Percent" 204 | msgstr "パーセント" 205 | 206 | #: youtube_dl_gui/mainframe.py:144 207 | msgid "ETA" 208 | msgstr "ETA" 209 | 210 | #: youtube_dl_gui/mainframe.py:145 211 | msgid "Speed" 212 | msgstr "速度" 213 | 214 | #: youtube_dl_gui/mainframe.py:146 215 | msgid "Status" 216 | msgstr "状態" 217 | 218 | #: youtube_dl_gui/mainframe.py:235 219 | msgid "Get URL" 220 | msgstr "URLをコピー" 221 | 222 | #: youtube_dl_gui/mainframe.py:236 223 | msgid "Get command" 224 | msgstr "コマンドをコピー" 225 | 226 | #: youtube_dl_gui/mainframe.py:237 227 | msgid "Open destination" 228 | msgstr "保存先フォルダを開く" 229 | 230 | #: youtube_dl_gui/mainframe.py:238 231 | msgid "Re-enter" 232 | msgstr "再登録" 233 | 234 | #: youtube_dl_gui/mainframe.py:458 235 | msgid "Resume" 236 | msgstr "再開" 237 | 238 | #: youtube_dl_gui/mainframe.py:480 239 | msgid "Video" 240 | msgstr "動画" 241 | 242 | #: youtube_dl_gui/mainframe.py:484 243 | msgid "Audio" 244 | msgstr "音声" 245 | 246 | #: youtube_dl_gui/mainframe.py:516 247 | msgid "No items selected. Please pick an action" 248 | msgstr "項目が選択されていません。操作項目を選択してください" 249 | 250 | #: youtube_dl_gui/mainframe.py:516 251 | msgid "Remove all" 252 | msgstr "全消去" 253 | 254 | #: youtube_dl_gui/mainframe.py:516 255 | msgid "Remove completed" 256 | msgstr "消去完了" 257 | 258 | #: youtube_dl_gui/mainframe.py:534 259 | msgid "Are you sure you want to remove selected items?" 260 | msgstr "選択アイテムを消去してよろしいですか?" 261 | 262 | #: youtube_dl_gui/mainframe.py:546 263 | msgid "Item is active, cannot remove" 264 | msgstr "使用中のため削除できません" 265 | 266 | #: youtube_dl_gui/mainframe.py:579 267 | msgid "Item is not completed" 268 | msgstr "未完了のアイテムです" 269 | 270 | #: youtube_dl_gui/mainframe.py:668 271 | msgid "Update in progress. Please wait for the update to complete" 272 | msgstr "アップデート中です。完了までお待ちください" 273 | 274 | #: youtube_dl_gui/mainframe.py:716 275 | msgid "Logging is disabled" 276 | msgstr "ロギング無効" 277 | 278 | #: youtube_dl_gui/mainframe.py:891 279 | msgid "Shutdown" 280 | msgstr "シャットダウン" 281 | 282 | #: youtube_dl_gui/mainframe.py:891 283 | msgid "Shutting down in {0} second(s)" 284 | msgstr "{0} 秒後にシャットダウンします" 285 | 286 | #: youtube_dl_gui/mainframe.py:980 287 | msgid "No items to download" 288 | msgstr "ダウンロードする項目がありません" 289 | 290 | #: youtube_dl_gui/mainframe.py:1040 291 | msgid "Updates are disabled for your system. Please use the system's package manager to update youtube-dl." 292 | msgstr "" 293 | 294 | #: youtube_dl_gui/mainframe.py:1065 295 | msgid "Are you sure you want to exit?" 296 | msgstr "終了してよろしいですか?" 297 | 298 | #: youtube_dl_gui/mainframe.py:1065 299 | msgid "Exit" 300 | msgstr "終了" 301 | 302 | #: youtube_dl_gui/mainframe.py:1306 youtube_dl_gui/mainframe.py:1456 303 | msgid "Cancel" 304 | msgstr "キャンセル" 305 | 306 | #: youtube_dl_gui/mainframe.py:1455 307 | msgid "OK" 308 | msgstr "OK" 309 | 310 | #: youtube_dl_gui/optionsframe.py:65 311 | msgid "Reset" 312 | msgstr "リセット" 313 | 314 | #: youtube_dl_gui/optionsframe.py:66 315 | msgid "Close" 316 | msgstr "閉じる" 317 | 318 | #: youtube_dl_gui/optionsframe.py:72 319 | msgid "General" 320 | msgstr "一般" 321 | 322 | #: youtube_dl_gui/optionsframe.py:73 323 | msgid "Formats" 324 | msgstr "形式" 325 | 326 | #: youtube_dl_gui/optionsframe.py:74 327 | msgid "Downloads" 328 | msgstr "ダウンロード" 329 | 330 | #: youtube_dl_gui/optionsframe.py:75 331 | msgid "Advanced" 332 | msgstr "高度" 333 | 334 | #: youtube_dl_gui/optionsframe.py:76 335 | msgid "Extra" 336 | msgstr "その他" 337 | 338 | #: youtube_dl_gui/optionsframe.py:310 339 | msgid "Language" 340 | msgstr "言語" 341 | 342 | #: youtube_dl_gui/optionsframe.py:313 343 | msgid "Filename format" 344 | msgstr "ファイル名の形式" 345 | 346 | #: youtube_dl_gui/optionsframe.py:318 347 | msgid "Filename options" 348 | msgstr "ファイル名のオプション" 349 | 350 | #: youtube_dl_gui/optionsframe.py:319 351 | msgid "Restrict filenames to ASCII" 352 | msgstr "ファイル名をASCIIに制限する" 353 | 354 | #: youtube_dl_gui/optionsframe.py:321 355 | msgid "More options" 356 | msgstr "詳細設定" 357 | 358 | #: youtube_dl_gui/optionsframe.py:322 359 | msgid "Confirm on exit" 360 | msgstr "終了時の確認" 361 | 362 | #: youtube_dl_gui/optionsframe.py:323 363 | msgid "Confirm item deletion" 364 | msgstr "アイテム削除時の確認" 365 | 366 | #: youtube_dl_gui/optionsframe.py:324 367 | msgid "Inform me on download completion" 368 | msgstr "ダウンロード完了時に通知" 369 | 370 | #: youtube_dl_gui/optionsframe.py:326 371 | msgid "Shutdown on download completion" 372 | msgstr "ダウンロード完了後シャットダウン" 373 | 374 | #: youtube_dl_gui/optionsframe.py:337 375 | msgid "SUDO password" 376 | msgstr "SUDO パスワード" 377 | 378 | #: youtube_dl_gui/optionsframe.py:415 youtube_dl_gui/optionsframe.py:816 379 | msgid "In order for the changes to take effect please restart {0}" 380 | msgstr "変更を反映するに再起動してください {0}" 381 | 382 | #: youtube_dl_gui/optionsframe.py:416 youtube_dl_gui/optionsframe.py:817 383 | msgid "Restart" 384 | msgstr "再起動" 385 | 386 | #: youtube_dl_gui/optionsframe.py:463 387 | msgid "high" 388 | msgstr "高" 389 | 390 | #: youtube_dl_gui/optionsframe.py:463 391 | msgid "low" 392 | msgstr "低" 393 | 394 | #: youtube_dl_gui/optionsframe.py:463 395 | msgid "mid" 396 | msgstr "中" 397 | 398 | #: youtube_dl_gui/optionsframe.py:468 399 | msgid "Video formats" 400 | msgstr "動画形式" 401 | 402 | #: youtube_dl_gui/optionsframe.py:471 403 | msgid "Audio formats" 404 | msgstr "音声形式" 405 | 406 | #: youtube_dl_gui/optionsframe.py:474 407 | msgid "Post-Process options" 408 | msgstr "後処理の設定" 409 | 410 | #: youtube_dl_gui/optionsframe.py:475 411 | msgid "Keep original files" 412 | msgstr "オリジナルファイルを残す" 413 | 414 | #: youtube_dl_gui/optionsframe.py:476 415 | msgid "Extract audio from video file" 416 | msgstr "動画から音声を抽出" 417 | 418 | #: youtube_dl_gui/optionsframe.py:477 419 | msgid "Embed thumbnail in audio file" 420 | msgstr "音声ファイルにサムネイル埋め込み" 421 | 422 | #: youtube_dl_gui/optionsframe.py:478 423 | msgid "Add metadata to file" 424 | msgstr "メタデータの追加" 425 | 426 | #: youtube_dl_gui/optionsframe.py:480 427 | msgid "Audio quality" 428 | msgstr "音声品質" 429 | 430 | #: youtube_dl_gui/optionsframe.py:538 431 | msgid "English" 432 | msgstr "英語" 433 | 434 | #: youtube_dl_gui/optionsframe.py:539 435 | msgid "French" 436 | msgstr "フランス語" 437 | 438 | #: youtube_dl_gui/optionsframe.py:540 439 | msgid "German" 440 | msgstr "ドイツ語" 441 | 442 | #: youtube_dl_gui/optionsframe.py:541 443 | msgid "Greek" 444 | msgstr "ギリシャ語" 445 | 446 | #: youtube_dl_gui/optionsframe.py:542 447 | msgid "Hebrew" 448 | msgstr "ヘブライ語" 449 | 450 | #: youtube_dl_gui/optionsframe.py:543 451 | msgid "Italian" 452 | msgstr "イタリア語" 453 | 454 | #: youtube_dl_gui/optionsframe.py:544 455 | msgid "Portuguese" 456 | msgstr "ポルトガル語" 457 | 458 | #: youtube_dl_gui/optionsframe.py:545 459 | msgid "Russian" 460 | msgstr "ロシア語" 461 | 462 | #: youtube_dl_gui/optionsframe.py:546 463 | msgid "Spanish" 464 | msgstr "スペイン語" 465 | 466 | #: youtube_dl_gui/optionsframe.py:547 467 | msgid "Swedish" 468 | msgstr "スウェーデン語" 469 | 470 | #: youtube_dl_gui/optionsframe.py:548 471 | msgid "Turkish" 472 | msgstr "トルコ語" 473 | 474 | #: youtube_dl_gui/optionsframe.py:564 475 | msgid "None" 476 | msgstr "なし" 477 | 478 | #: youtube_dl_gui/optionsframe.py:565 479 | msgid "Automatic subtitles (YOUTUBE ONLY)" 480 | msgstr "自動字幕 (Youtubeのみ)" 481 | 482 | #: youtube_dl_gui/optionsframe.py:566 483 | msgid "All available subtitles" 484 | msgstr "すべての字幕" 485 | 486 | #: youtube_dl_gui/optionsframe.py:567 487 | msgid "Subtitles by language" 488 | msgstr "字幕言語" 489 | 490 | #: youtube_dl_gui/optionsframe.py:573 491 | msgid "Subtitles" 492 | msgstr "字幕" 493 | 494 | #: youtube_dl_gui/optionsframe.py:577 495 | msgid "Subtitles options" 496 | msgstr "字幕設定" 497 | 498 | #: youtube_dl_gui/optionsframe.py:578 499 | msgid "Embed subtitles into video file (mp4 ONLY)" 500 | msgstr "動画に字幕埋め込み (mp4のみ)" 501 | 502 | #: youtube_dl_gui/optionsframe.py:580 503 | msgid "Playlist" 504 | msgstr "プレイリスト" 505 | 506 | #: youtube_dl_gui/optionsframe.py:586 youtube_dl_gui/optionsframe.py:591 507 | msgid "Max" 508 | msgstr "最大" 509 | 510 | #: youtube_dl_gui/optionsframe.py:589 511 | msgid "Filesize" 512 | msgstr "ファイルサイズ" 513 | 514 | #: youtube_dl_gui/optionsframe.py:594 515 | msgid "Min" 516 | msgstr "最小" 517 | 518 | #: youtube_dl_gui/optionsframe.py:723 519 | msgid "Retries" 520 | msgstr "リトライ" 521 | 522 | #: youtube_dl_gui/optionsframe.py:726 523 | msgid "Authentication" 524 | msgstr "認証情報" 525 | 526 | #: youtube_dl_gui/optionsframe.py:728 527 | msgid "Username" 528 | msgstr "アカウント" 529 | 530 | #: youtube_dl_gui/optionsframe.py:730 531 | msgid "Password" 532 | msgstr "パスワード" 533 | 534 | #: youtube_dl_gui/optionsframe.py:732 535 | msgid "Video password" 536 | msgstr "ビデオパスワード" 537 | 538 | #: youtube_dl_gui/optionsframe.py:735 539 | msgid "Network" 540 | msgstr "ネットワーク" 541 | 542 | #: youtube_dl_gui/optionsframe.py:737 543 | msgid "Proxy" 544 | msgstr "プロキシ" 545 | 546 | #: youtube_dl_gui/optionsframe.py:739 547 | msgid "User agent" 548 | msgstr "ユーザーエージェント(UA)" 549 | 550 | #: youtube_dl_gui/optionsframe.py:741 551 | msgid "Referer" 552 | msgstr "リファラー(Referer)" 553 | 554 | #: youtube_dl_gui/optionsframe.py:744 555 | msgid "Logging" 556 | msgstr "ロギング" 557 | 558 | #: youtube_dl_gui/optionsframe.py:746 559 | msgid "Enable log" 560 | msgstr "ログを有効化" 561 | 562 | #: youtube_dl_gui/optionsframe.py:747 563 | msgid "View" 564 | msgstr "表示" 565 | 566 | #: youtube_dl_gui/optionsframe.py:748 567 | msgid "Clear" 568 | msgstr "消去" 569 | 570 | #: youtube_dl_gui/optionsframe.py:858 571 | msgid "Youtube-dl command line options (e.g. --help)" 572 | msgstr "Youtube-dl コマンドラインオプション (例 --help)" 573 | 574 | #: youtube_dl_gui/optionsframe.py:861 575 | msgid "Extra options" 576 | msgstr "追加設定" 577 | 578 | #: youtube_dl_gui/optionsframe.py:863 579 | msgid "Debug youtube-dl" 580 | msgstr "youtube-dlのデバッグ" 581 | 582 | #: youtube_dl_gui/optionsframe.py:864 583 | msgid "Ignore errors" 584 | msgstr "エラーを無視" 585 | 586 | #: youtube_dl_gui/optionsframe.py:865 587 | msgid "Ignore youtube-dl config" 588 | msgstr "youtube-dlのconfig無視" 589 | 590 | #: youtube_dl_gui/optionsframe.py:866 591 | msgid "No mtime" 592 | msgstr "mtime無し" 593 | 594 | #: youtube_dl_gui/optionsframe.py:867 595 | msgid "Prefer native HLS" 596 | msgstr "ネイティブHLS優先" 597 | 598 | #: youtube_dl_gui/optionsframe.py:928 599 | msgid "Log Viewer" 600 | msgstr "ログビュワー" 601 | -------------------------------------------------------------------------------- /youtube_dl_gui/locale/ko_KR/LC_MESSAGES/youtube_dl_gui.po: -------------------------------------------------------------------------------- 1 | # Youtube-dlG localization file. 2 | # FIRST AUTHOR: Sotiris Papadopoulos , 2015. 3 | # 4 | msgid "" 5 | msgstr "" 6 | "Project-Id-Version: youtube-dlg 0.4\n" 7 | "POT-Creation-Date: 2018-01-15 16:42+EET\n" 8 | "PO-Revision-Date: 2017-02-22 02:49+GMT\n" 9 | "Last-Translator: Yi Soo, An \n" 10 | "Language-Team: kr\n" 11 | "Language: \n" 12 | "MIME-Version: 1.0\n" 13 | "Content-Type: text/plain; charset=UTF-8\n" 14 | "Content-Transfer-Encoding: 8bit\n" 15 | "Generated-By: vim\n" 16 | 17 | #: youtube_dl_gui/__init__.py:91 18 | msgid "Error" 19 | msgstr "" 20 | 21 | #: youtube_dl_gui/__init__.py:91 22 | msgid "Failed to locate youtube-dl and updates are disabled" 23 | msgstr "" 24 | 25 | #: youtube_dl_gui/formats.py:9 youtube_dl_gui/formats.py:109 26 | msgid "ID" 27 | msgstr "ID" 28 | 29 | #: youtube_dl_gui/formats.py:10 youtube_dl_gui/formats.py:110 30 | #: youtube_dl_gui/mainframe.py:140 31 | msgid "Title" 32 | msgstr "제목" 33 | 34 | #: youtube_dl_gui/formats.py:11 youtube_dl_gui/formats.py:111 35 | msgid "Title + ID" 36 | msgstr "제목 + ID" 37 | 38 | #: youtube_dl_gui/formats.py:12 youtube_dl_gui/formats.py:112 39 | msgid "Title + Quality" 40 | msgstr "제목 + 화질" 41 | 42 | #: youtube_dl_gui/formats.py:13 youtube_dl_gui/formats.py:113 43 | msgid "Title + ID + Quality" 44 | msgstr "제목 + ID + 화질" 45 | 46 | #: youtube_dl_gui/formats.py:14 youtube_dl_gui/formats.py:114 47 | msgid "Custom" 48 | msgstr "사용자 설정" 49 | 50 | #: youtube_dl_gui/formats.py:19 youtube_dl_gui/formats.py:119 51 | #: youtube_dl_gui/mainframe.py:503 youtube_dl_gui/mainframe.py:506 52 | msgid "default" 53 | msgstr "기본값" 54 | 55 | #: youtube_dl_gui/mainframe.py:97 56 | msgid "Enter URLs below" 57 | msgstr "URL을 입력하세요." 58 | 59 | #: youtube_dl_gui/mainframe.py:98 60 | msgid "Update" 61 | msgstr "업데이트" 62 | 63 | #: youtube_dl_gui/mainframe.py:99 youtube_dl_gui/optionsframe.py:41 64 | msgid "Options" 65 | msgstr "옵션" 66 | 67 | #: youtube_dl_gui/mainframe.py:100 youtube_dl_gui/optionsframe.py:584 68 | msgid "Stop" 69 | msgstr "정지" 70 | 71 | #: youtube_dl_gui/mainframe.py:101 72 | msgid "Info" 73 | msgstr "정보" 74 | 75 | #: youtube_dl_gui/mainframe.py:102 76 | msgid "Welcome" 77 | msgstr "환영합니다." 78 | 79 | #: youtube_dl_gui/mainframe.py:103 80 | msgid "Warning" 81 | msgstr "주의" 82 | 83 | #: youtube_dl_gui/mainframe.py:105 84 | msgid "Add" 85 | msgstr "추가" 86 | 87 | #: youtube_dl_gui/mainframe.py:106 88 | msgid "Download list" 89 | msgstr "다운로드 목록" 90 | 91 | #: youtube_dl_gui/mainframe.py:107 youtube_dl_gui/mainframe.py:516 92 | #: youtube_dl_gui/mainframe.py:534 93 | msgid "Delete" 94 | msgstr "삭제" 95 | 96 | #: youtube_dl_gui/mainframe.py:108 97 | msgid "Play" 98 | msgstr "재생" 99 | 100 | #: youtube_dl_gui/mainframe.py:109 101 | msgid "Up" 102 | msgstr "위로" 103 | 104 | #: youtube_dl_gui/mainframe.py:110 105 | msgid "Down" 106 | msgstr "아래로" 107 | 108 | #: youtube_dl_gui/mainframe.py:111 109 | msgid "Reload" 110 | msgstr "새로고침" 111 | 112 | #: youtube_dl_gui/mainframe.py:112 youtube_dl_gui/mainframe.py:448 113 | #: youtube_dl_gui/mainframe.py:649 114 | msgid "Pause" 115 | msgstr "일시정지" 116 | 117 | #: youtube_dl_gui/mainframe.py:113 youtube_dl_gui/mainframe.py:865 118 | #: youtube_dl_gui/mainframe.py:866 youtube_dl_gui/optionsframe.py:582 119 | msgid "Start" 120 | msgstr "시작" 121 | 122 | #: youtube_dl_gui/mainframe.py:114 123 | msgid "About" 124 | msgstr "정보" 125 | 126 | #: youtube_dl_gui/mainframe.py:115 127 | msgid "View Log" 128 | msgstr "로그 보기" 129 | 130 | #: youtube_dl_gui/mainframe.py:117 131 | msgid "Successfully downloaded {0} URL(s) in {1} day(s) {2} hour(s) {3} minute(s) {4} second(s)" 132 | msgstr "{0}개의 URL을 받는데 총 {1}일 {2}시간 {3}분 {4}초가 걸렸습니다." 133 | 134 | #: youtube_dl_gui/mainframe.py:119 135 | msgid "Downloads completed" 136 | msgstr "다운로드 완료" 137 | 138 | #: youtube_dl_gui/mainframe.py:120 139 | msgid "Total Progress: {0:.1f}% | Queued ({1}) Paused ({2}) Active ({3}) Completed ({4}) Error ({5})" 140 | msgstr "진행 상태: {0:.1f}% | 대기 ({1}) 일시정지 ({2}) 진행중 ({3}) 완료 ({4}) 오류 ({5})" 141 | 142 | #: youtube_dl_gui/mainframe.py:121 143 | msgid "Stopping downloads" 144 | msgstr "다운로드 정지중" 145 | 146 | #: youtube_dl_gui/mainframe.py:122 147 | msgid "Downloads stopped" 148 | msgstr "다운로드 정지" 149 | 150 | #: youtube_dl_gui/mainframe.py:123 151 | msgid "You need to provide at least one URL" 152 | msgstr "최소 한 개 이상의 URL을 입력해야 합니다." 153 | 154 | #: youtube_dl_gui/mainframe.py:124 155 | msgid "Downloads started" 156 | msgstr "다운로드 시작됨" 157 | 158 | #: youtube_dl_gui/mainframe.py:125 159 | msgid "Choose Directory" 160 | msgstr "저장 경로" 161 | 162 | #: youtube_dl_gui/mainframe.py:127 163 | msgid "Download in progress. Please wait for all downloads to complete" 164 | msgstr "다운로드가 완료될 때까지 기다려주세요." 165 | 166 | #: youtube_dl_gui/mainframe.py:128 167 | msgid "Update already in progress" 168 | msgstr "업데이트 진행중입니다." 169 | 170 | #: youtube_dl_gui/mainframe.py:130 171 | msgid "Downloading latest youtube-dl. Please wait..." 172 | msgstr "최신 youtube-dl을 다운로드하고 있습니다. 잠시만 기다려주세요." 173 | 174 | #: youtube_dl_gui/mainframe.py:131 175 | msgid "Youtube-dl download failed [{0}]" 176 | msgstr "Youtube-dl 다운로드 실패 [{0}]" 177 | 178 | #: youtube_dl_gui/mainframe.py:132 179 | msgid "Successfully downloaded youtube-dl" 180 | msgstr "Youtube-dl 다운로드가 완료되었습니다." 181 | 182 | #: youtube_dl_gui/mainframe.py:134 183 | msgid "Unable to open directory: '{dir}'. The specified path does not exist" 184 | msgstr "'{dir}'을 열 수 없습니다. 올바른 경로가 아닙니다." 185 | 186 | #: youtube_dl_gui/mainframe.py:136 187 | msgid "Error while shutting down. Make sure you typed the correct password" 188 | msgstr "시스템을 종료할 수 없습니다. 패스워드가 올바른지 확인하세요." 189 | 190 | #: youtube_dl_gui/mainframe.py:138 191 | msgid "Shutting down system" 192 | msgstr "시스템 종료 중.." 193 | 194 | #: youtube_dl_gui/mainframe.py:141 195 | msgid "Extension" 196 | msgstr "확장자" 197 | 198 | #: youtube_dl_gui/mainframe.py:142 199 | msgid "Size" 200 | msgstr "크기" 201 | 202 | #: youtube_dl_gui/mainframe.py:143 203 | msgid "Percent" 204 | msgstr "퍼센트" 205 | 206 | #: youtube_dl_gui/mainframe.py:144 207 | msgid "ETA" 208 | msgstr "기타" 209 | 210 | #: youtube_dl_gui/mainframe.py:145 211 | msgid "Speed" 212 | msgstr "속도" 213 | 214 | #: youtube_dl_gui/mainframe.py:146 215 | msgid "Status" 216 | msgstr "상태" 217 | 218 | #: youtube_dl_gui/mainframe.py:235 219 | msgid "Get URL" 220 | msgstr "클립보드로 URL 복사" 221 | 222 | #: youtube_dl_gui/mainframe.py:236 223 | msgid "Get command" 224 | msgstr "커맨드라인 명령어 복사" 225 | 226 | #: youtube_dl_gui/mainframe.py:237 227 | msgid "Open destination" 228 | msgstr "폴더 열기" 229 | 230 | #: youtube_dl_gui/mainframe.py:238 231 | msgid "Re-enter" 232 | msgstr "대기열에 넣기" 233 | 234 | #: youtube_dl_gui/mainframe.py:458 235 | msgid "Resume" 236 | msgstr "이어받기" 237 | 238 | #: youtube_dl_gui/mainframe.py:480 239 | msgid "Video" 240 | msgstr "비디오" 241 | 242 | #: youtube_dl_gui/mainframe.py:484 243 | msgid "Audio" 244 | msgstr "오디오" 245 | 246 | #: youtube_dl_gui/mainframe.py:516 247 | msgid "No items selected. Please pick an action" 248 | msgstr "선택된 아이템이 없습니다. 다음 중 하나를 실행하시겠습니까?" 249 | 250 | #: youtube_dl_gui/mainframe.py:516 251 | msgid "Remove all" 252 | msgstr "전체삭제" 253 | 254 | #: youtube_dl_gui/mainframe.py:516 255 | msgid "Remove completed" 256 | msgstr "완료된 것만 삭제" 257 | 258 | #: youtube_dl_gui/mainframe.py:534 259 | msgid "Are you sure you want to remove selected items?" 260 | msgstr "해당 아이템을 삭제하시겠습니까?" 261 | 262 | #: youtube_dl_gui/mainframe.py:546 263 | msgid "Item is active, cannot remove" 264 | msgstr "삭제할 수 없습니다." 265 | 266 | #: youtube_dl_gui/mainframe.py:579 267 | msgid "Item is not completed" 268 | msgstr "다운로드가 완료되지 않았습니다." 269 | 270 | #: youtube_dl_gui/mainframe.py:668 271 | msgid "Update in progress. Please wait for the update to complete" 272 | msgstr "업데이트가 완료될 때까지 기다려주세요." 273 | 274 | #: youtube_dl_gui/mainframe.py:716 275 | msgid "Logging is disabled" 276 | msgstr "로깅이 비활성화되었습니다." 277 | 278 | #: youtube_dl_gui/mainframe.py:891 279 | msgid "Shutdown" 280 | msgstr "시스템 종료" 281 | 282 | #: youtube_dl_gui/mainframe.py:891 283 | msgid "Shutting down in {0} second(s)" 284 | msgstr "{0}초 후에 시스템이 종료됩니다." 285 | 286 | #: youtube_dl_gui/mainframe.py:980 287 | msgid "No items to download" 288 | msgstr "다운로드할 아이템이 없습니다." 289 | 290 | #: youtube_dl_gui/mainframe.py:1040 291 | msgid "Updates are disabled for your system. Please use the system's package manager to update youtube-dl." 292 | msgstr "" 293 | 294 | #: youtube_dl_gui/mainframe.py:1065 295 | msgid "Are you sure you want to exit?" 296 | msgstr "종료하시겠습니까?" 297 | 298 | #: youtube_dl_gui/mainframe.py:1065 299 | msgid "Exit" 300 | msgstr "종료" 301 | 302 | #: youtube_dl_gui/mainframe.py:1306 youtube_dl_gui/mainframe.py:1456 303 | msgid "Cancel" 304 | msgstr "취소" 305 | 306 | #: youtube_dl_gui/mainframe.py:1455 307 | msgid "OK" 308 | msgstr "확인" 309 | 310 | #: youtube_dl_gui/optionsframe.py:65 311 | msgid "Reset" 312 | msgstr "초기화" 313 | 314 | #: youtube_dl_gui/optionsframe.py:66 315 | msgid "Close" 316 | msgstr "닫기" 317 | 318 | #: youtube_dl_gui/optionsframe.py:72 319 | msgid "General" 320 | msgstr "일반" 321 | 322 | #: youtube_dl_gui/optionsframe.py:73 323 | msgid "Formats" 324 | msgstr "포맷" 325 | 326 | #: youtube_dl_gui/optionsframe.py:74 327 | msgid "Downloads" 328 | msgstr "다운로드" 329 | 330 | #: youtube_dl_gui/optionsframe.py:75 331 | msgid "Advanced" 332 | msgstr "고급설정" 333 | 334 | #: youtube_dl_gui/optionsframe.py:76 335 | msgid "Extra" 336 | msgstr "기타" 337 | 338 | #: youtube_dl_gui/optionsframe.py:310 339 | msgid "Language" 340 | msgstr "언어" 341 | 342 | #: youtube_dl_gui/optionsframe.py:313 343 | msgid "Filename format" 344 | msgstr "파일이름 형식" 345 | 346 | #: youtube_dl_gui/optionsframe.py:318 347 | msgid "Filename options" 348 | msgstr "파일이름 옵션" 349 | 350 | #: youtube_dl_gui/optionsframe.py:319 351 | msgid "Restrict filenames to ASCII" 352 | msgstr "ASCII 문자로 제한" 353 | 354 | #: youtube_dl_gui/optionsframe.py:321 355 | msgid "More options" 356 | msgstr "추가옵션" 357 | 358 | #: youtube_dl_gui/optionsframe.py:322 359 | msgid "Confirm on exit" 360 | msgstr "종료시 확인" 361 | 362 | #: youtube_dl_gui/optionsframe.py:323 363 | msgid "Confirm item deletion" 364 | msgstr "아이템 삭제시 확인" 365 | 366 | #: youtube_dl_gui/optionsframe.py:324 367 | msgid "Inform me on download completion" 368 | msgstr "완료시 알림" 369 | 370 | #: youtube_dl_gui/optionsframe.py:326 371 | msgid "Shutdown on download completion" 372 | msgstr "완료시 시스템종료" 373 | 374 | #: youtube_dl_gui/optionsframe.py:337 375 | msgid "SUDO password" 376 | msgstr "SUDO 암호" 377 | 378 | #: youtube_dl_gui/optionsframe.py:415 youtube_dl_gui/optionsframe.py:816 379 | msgid "In order for the changes to take effect please restart {0}" 380 | msgstr "설정값을 적용하기 위해 {0}을 재시작하세요." 381 | 382 | #: youtube_dl_gui/optionsframe.py:416 youtube_dl_gui/optionsframe.py:817 383 | msgid "Restart" 384 | msgstr "재시작" 385 | 386 | #: youtube_dl_gui/optionsframe.py:463 387 | msgid "high" 388 | msgstr "높음" 389 | 390 | #: youtube_dl_gui/optionsframe.py:463 391 | msgid "low" 392 | msgstr "낮음" 393 | 394 | #: youtube_dl_gui/optionsframe.py:463 395 | msgid "mid" 396 | msgstr "중간" 397 | 398 | #: youtube_dl_gui/optionsframe.py:468 399 | msgid "Video formats" 400 | msgstr "비디오 형식" 401 | 402 | #: youtube_dl_gui/optionsframe.py:471 403 | msgid "Audio formats" 404 | msgstr "오디오 형식" 405 | 406 | #: youtube_dl_gui/optionsframe.py:474 407 | msgid "Post-Process options" 408 | msgstr "후처리 옵션" 409 | 410 | #: youtube_dl_gui/optionsframe.py:475 411 | msgid "Keep original files" 412 | msgstr "원본파일 유지" 413 | 414 | #: youtube_dl_gui/optionsframe.py:476 415 | msgid "Extract audio from video file" 416 | msgstr "오디오 추출" 417 | 418 | #: youtube_dl_gui/optionsframe.py:477 419 | msgid "Embed thumbnail in audio file" 420 | msgstr "오디오에 섬네일 추가" 421 | 422 | #: youtube_dl_gui/optionsframe.py:478 423 | msgid "Add metadata to file" 424 | msgstr "메타데이터 추가" 425 | 426 | #: youtube_dl_gui/optionsframe.py:480 427 | msgid "Audio quality" 428 | msgstr "오디오 음질" 429 | 430 | #: youtube_dl_gui/optionsframe.py:538 431 | msgid "English" 432 | msgstr "English" 433 | 434 | #: youtube_dl_gui/optionsframe.py:539 435 | msgid "French" 436 | msgstr "French" 437 | 438 | #: youtube_dl_gui/optionsframe.py:540 439 | msgid "German" 440 | msgstr "German" 441 | 442 | #: youtube_dl_gui/optionsframe.py:541 443 | msgid "Greek" 444 | msgstr "Greek" 445 | 446 | #: youtube_dl_gui/optionsframe.py:542 447 | msgid "Hebrew" 448 | msgstr "Hebrew" 449 | 450 | #: youtube_dl_gui/optionsframe.py:543 451 | msgid "Italian" 452 | msgstr "Italian" 453 | 454 | #: youtube_dl_gui/optionsframe.py:544 455 | msgid "Portuguese" 456 | msgstr "Portuguese" 457 | 458 | #: youtube_dl_gui/optionsframe.py:545 459 | msgid "Russian" 460 | msgstr "Russian" 461 | 462 | #: youtube_dl_gui/optionsframe.py:546 463 | msgid "Spanish" 464 | msgstr "Spanish" 465 | 466 | #: youtube_dl_gui/optionsframe.py:547 467 | msgid "Swedish" 468 | msgstr "Swedish" 469 | 470 | #: youtube_dl_gui/optionsframe.py:548 471 | msgid "Turkish" 472 | msgstr "Turkish" 473 | 474 | #: youtube_dl_gui/optionsframe.py:564 475 | msgid "None" 476 | msgstr "없음" 477 | 478 | #: youtube_dl_gui/optionsframe.py:565 479 | msgid "Automatic subtitles (YOUTUBE ONLY)" 480 | msgstr "오토매틱 자막 (유튜브 전용)" 481 | 482 | #: youtube_dl_gui/optionsframe.py:566 483 | msgid "All available subtitles" 484 | msgstr "사용가능한 모든 자막" 485 | 486 | #: youtube_dl_gui/optionsframe.py:567 487 | msgid "Subtitles by language" 488 | msgstr "언어에 따른 자막" 489 | 490 | #: youtube_dl_gui/optionsframe.py:573 491 | msgid "Subtitles" 492 | msgstr "자막" 493 | 494 | #: youtube_dl_gui/optionsframe.py:577 495 | msgid "Subtitles options" 496 | msgstr "자막 옵션" 497 | 498 | #: youtube_dl_gui/optionsframe.py:578 499 | msgid "Embed subtitles into video file (mp4 ONLY)" 500 | msgstr "비디오에 자막 삽입 (mp4 전용)" 501 | 502 | #: youtube_dl_gui/optionsframe.py:580 503 | msgid "Playlist" 504 | msgstr "재생목록" 505 | 506 | #: youtube_dl_gui/optionsframe.py:586 youtube_dl_gui/optionsframe.py:591 507 | msgid "Max" 508 | msgstr "최대" 509 | 510 | #: youtube_dl_gui/optionsframe.py:589 511 | msgid "Filesize" 512 | msgstr "파일크기" 513 | 514 | #: youtube_dl_gui/optionsframe.py:594 515 | msgid "Min" 516 | msgstr "최소" 517 | 518 | #: youtube_dl_gui/optionsframe.py:723 519 | msgid "Retries" 520 | msgstr "재시도 횟수" 521 | 522 | #: youtube_dl_gui/optionsframe.py:726 523 | msgid "Authentication" 524 | msgstr "인증" 525 | 526 | #: youtube_dl_gui/optionsframe.py:728 527 | msgid "Username" 528 | msgstr "사용자 이름" 529 | 530 | #: youtube_dl_gui/optionsframe.py:730 531 | msgid "Password" 532 | msgstr "비밀번호" 533 | 534 | #: youtube_dl_gui/optionsframe.py:732 535 | msgid "Video password" 536 | msgstr "비디오 비밀번호" 537 | 538 | #: youtube_dl_gui/optionsframe.py:735 539 | msgid "Network" 540 | msgstr "네트워크" 541 | 542 | #: youtube_dl_gui/optionsframe.py:737 543 | msgid "Proxy" 544 | msgstr "프록시" 545 | 546 | #: youtube_dl_gui/optionsframe.py:739 547 | msgid "User agent" 548 | msgstr "User agent" 549 | 550 | #: youtube_dl_gui/optionsframe.py:741 551 | msgid "Referer" 552 | msgstr "Referer" 553 | 554 | #: youtube_dl_gui/optionsframe.py:744 555 | msgid "Logging" 556 | msgstr "로그" 557 | 558 | #: youtube_dl_gui/optionsframe.py:746 559 | msgid "Enable log" 560 | msgstr "로그 사용" 561 | 562 | #: youtube_dl_gui/optionsframe.py:747 563 | msgid "View" 564 | msgstr "보기" 565 | 566 | #: youtube_dl_gui/optionsframe.py:748 567 | msgid "Clear" 568 | msgstr "초기화" 569 | 570 | #: youtube_dl_gui/optionsframe.py:858 571 | msgid "Youtube-dl command line options (e.g. --help)" 572 | msgstr "Youtube-dl 커맨드라인 옵션 (예: --help)" 573 | 574 | #: youtube_dl_gui/optionsframe.py:861 575 | msgid "Extra options" 576 | msgstr "기타 옵션" 577 | 578 | #: youtube_dl_gui/optionsframe.py:863 579 | msgid "Debug youtube-dl" 580 | msgstr "Youtube-dl 디버그" 581 | 582 | #: youtube_dl_gui/optionsframe.py:864 583 | msgid "Ignore errors" 584 | msgstr "오류 무시" 585 | 586 | #: youtube_dl_gui/optionsframe.py:865 587 | msgid "Ignore youtube-dl config" 588 | msgstr "Youtube-dl 설정 무시" 589 | 590 | #: youtube_dl_gui/optionsframe.py:866 591 | msgid "No mtime" 592 | msgstr "mtime 사용안함" 593 | 594 | #: youtube_dl_gui/optionsframe.py:867 595 | msgid "Prefer native HLS" 596 | msgstr "네이티브 HLS 사용" 597 | 598 | #: youtube_dl_gui/optionsframe.py:928 599 | msgid "Log Viewer" 600 | msgstr "로그 뷰어" 601 | -------------------------------------------------------------------------------- /youtube_dl_gui/logmanager.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | # -*- coding: utf-8 -*- 3 | 4 | """Youtubedlg module responsible for handling the log stuff. """ 5 | 6 | from __future__ import unicode_literals 7 | 8 | import os.path 9 | from time import strftime 10 | 11 | from .utils import ( 12 | os_path_exists, 13 | get_encoding, 14 | check_path 15 | ) 16 | 17 | 18 | class LogManager(object): 19 | 20 | """Simple log manager for youtube-dl. 21 | 22 | This class is mainly used to log the youtube-dl STDERR. 23 | 24 | Attributes: 25 | LOG_FILENAME (string): Filename of the log file. 26 | TIME_TEMPLATE (string): Custom template to log the time. 27 | MAX_LOGSIZE (int): Maximum size(Bytes) of the log file. 28 | 29 | Args: 30 | config_path (string): Absolute path where LogManager should 31 | store the log file. 32 | 33 | add_time (boolean): If True LogManager will also log the time. 34 | 35 | """ 36 | 37 | LOG_FILENAME = "log" 38 | TIME_TEMPLATE = "[{time}] {error_msg}" 39 | MAX_LOGSIZE = 524288 # Bytes 40 | 41 | def __init__(self, config_path, add_time=False): 42 | self.config_path = config_path 43 | self.add_time = add_time 44 | self.log_file = os.path.join(config_path, self.LOG_FILENAME) 45 | self._encoding = get_encoding() 46 | self._init_log() 47 | self._auto_clear_log() 48 | 49 | def log_size(self): 50 | """Return log file size in Bytes. """ 51 | if not os_path_exists(self.log_file): 52 | return 0 53 | 54 | return os.path.getsize(self.log_file) 55 | 56 | def clear(self): 57 | """Clear log file. """ 58 | self._write('', 'w') 59 | 60 | def log(self, data): 61 | """Log data to the log file. 62 | 63 | Args: 64 | data (string): String to write to the log file. 65 | 66 | """ 67 | if isinstance(data, basestring): 68 | self._write(data + '\n', 'a') 69 | 70 | def _write(self, data, mode): 71 | """Write data to the log file. 72 | 73 | That's the main method for writing to the log file. 74 | 75 | Args: 76 | data (string): String to write on the log file. 77 | mode (string): Can be any IO mode supported by python. 78 | 79 | """ 80 | check_path(self.config_path) 81 | 82 | with open(self.log_file, mode) as log: 83 | if mode == 'a' and self.add_time: 84 | msg = self.TIME_TEMPLATE.format(time=strftime('%c'), error_msg=data) 85 | else: 86 | msg = data 87 | 88 | log.write(msg.encode(self._encoding, 'ignore')) 89 | 90 | def _init_log(self): 91 | """Initialize the log file if not exist. """ 92 | if not os_path_exists(self.log_file): 93 | self._write('', 'w') 94 | 95 | def _auto_clear_log(self): 96 | """Auto clear the log file. """ 97 | if self.log_size() > self.MAX_LOGSIZE: 98 | self.clear() 99 | -------------------------------------------------------------------------------- /youtube_dl_gui/parsers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | # -*- coding: utf-8 -*- 3 | 4 | """Youtubedlg module responsible for parsing the options. """ 5 | 6 | from __future__ import unicode_literals 7 | 8 | import os.path 9 | 10 | from .utils import ( 11 | remove_shortcuts, 12 | to_string 13 | ) 14 | 15 | 16 | class OptionHolder(object): 17 | 18 | """Simple data structure that holds informations for the given option. 19 | 20 | Args: 21 | name (string): Option name. Must be a valid option name 22 | from the optionsmanager.OptionsManager class. 23 | See optionsmanager.OptionsManager load_default() method. 24 | 25 | flag (string): The option command line switch. 26 | See https://github.com/rg3/youtube-dl/#options 27 | 28 | default_value (any): The option default value. Must be the same type 29 | with the corresponding option from the optionsmanager.OptionsManager 30 | class. 31 | 32 | requirements (list): The requirements for the given option. This 33 | argument is a list of strings with the name of all the options 34 | that this specific option needs. For example 'subs_lang' needs the 35 | 'write_subs' option to be enabled. 36 | 37 | """ 38 | 39 | def __init__(self, name, flag, default_value, requirements=None): 40 | self.name = name 41 | self.flag = flag 42 | self.requirements = requirements 43 | self.default_value = default_value 44 | 45 | def is_boolean(self): 46 | """Returns True if the option is a boolean switch else False. """ 47 | return type(self.default_value) is bool 48 | 49 | def check_requirements(self, options_dict): 50 | """Check if the required options are enabled. 51 | 52 | Args: 53 | options_dict (dict): Dictionary with all the options. 54 | 55 | Returns: 56 | True if any of the required options is enabled else False. 57 | 58 | """ 59 | if self.requirements is None: 60 | return True 61 | 62 | return any([options_dict[req] for req in self.requirements]) 63 | 64 | 65 | class OptionsParser(object): 66 | 67 | """Parse optionsmanager.OptionsManager options. 68 | 69 | This class is responsible for turning some of the youtube-dlg options 70 | to youtube-dl command line options. 71 | 72 | """ 73 | 74 | def __init__(self): 75 | self._ydl_options = [ 76 | OptionHolder('playlist_start', '--playlist-start', 1), 77 | OptionHolder('playlist_end', '--playlist-end', 0), 78 | OptionHolder('max_downloads', '--max-downloads', 0), 79 | OptionHolder('username', '-u', ''), 80 | OptionHolder('password', '-p', ''), 81 | OptionHolder('video_password', '--video-password', ''), 82 | OptionHolder('retries', '-R', 10), 83 | OptionHolder('proxy', '--proxy', ''), 84 | OptionHolder('user_agent', '--user-agent', ''), 85 | OptionHolder('referer', '--referer', ''), 86 | OptionHolder('ignore_errors', '-i', False), 87 | OptionHolder('write_description', '--write-description', False), 88 | OptionHolder('write_info', '--write-info-json', False), 89 | OptionHolder('write_thumbnail', '--write-thumbnail', False), 90 | OptionHolder('min_filesize', '--min-filesize', 0), 91 | OptionHolder('max_filesize', '--max-filesize', 0), 92 | OptionHolder('write_all_subs', '--all-subs', False), 93 | OptionHolder('write_auto_subs', '--write-auto-sub', False), 94 | OptionHolder('write_subs', '--write-sub', False), 95 | OptionHolder('keep_video', '-k', False), 96 | OptionHolder('restrict_filenames', '--restrict-filenames', False), 97 | OptionHolder('save_path', '-o', ''), 98 | OptionHolder('embed_subs', '--embed-subs', False, ['write_auto_subs', 'write_subs']), 99 | OptionHolder('to_audio', '-x', False), 100 | OptionHolder('audio_format', '--audio-format', ''), 101 | OptionHolder('video_format', '-f', '0'), 102 | OptionHolder('subs_lang', '--sub-lang', '', ['write_subs']), 103 | OptionHolder('audio_quality', '--audio-quality', '5', ['to_audio']), 104 | OptionHolder('youtube_dl_debug', '-v', False), 105 | OptionHolder('ignore_config', '--ignore-config', False), 106 | OptionHolder('native_hls', '--hls-prefer-native', False), 107 | OptionHolder('nomtime', '--no-mtime', False), 108 | OptionHolder('embed_thumbnail', '--embed-thumbnail', False), 109 | OptionHolder('add_metadata', '--add-metadata', False) 110 | ] 111 | 112 | def parse(self, options_dictionary): 113 | """Parse optionsmanager.OptionsManager options. 114 | 115 | Parses the given options to youtube-dl command line arguments. 116 | 117 | Args: 118 | options_dictionary (dict): Dictionary with all the options. 119 | 120 | Returns: 121 | List of strings with all the youtube-dl command line options. 122 | 123 | """ 124 | # REFACTOR 125 | options_list = ['--newline'] 126 | 127 | # Create a copy of options_dictionary 128 | # We don't want to edit the original options dictionary 129 | # and change some of the options values like 'save_path' etc.. 130 | options_dict = options_dictionary.copy() 131 | 132 | self._build_savepath(options_dict) 133 | self._build_videoformat(options_dict) 134 | self._build_filesizes(options_dict) 135 | 136 | # Parse basic youtube-dl command line options 137 | for option in self._ydl_options: 138 | #NOTE Special case should be removed 139 | if option.name == "to_audio": 140 | if options_dict["audio_format"] == "": 141 | value = options_dict[option.name] 142 | 143 | if value != option.default_value: 144 | options_list.append(option.flag) 145 | elif option.name == "audio_format": 146 | value = options_dict[option.name] 147 | 148 | if value != option.default_value: 149 | options_list.append("-x") 150 | options_list.append(option.flag) 151 | options_list.append(to_string(value)) 152 | 153 | #NOTE Temp fix 154 | # If current 'audio_quality' is not the default one ('5') 155 | # then append the audio quality flag and value to the 156 | # options list 157 | if options_dict["audio_quality"] != "5": 158 | options_list.append("--audio-quality") 159 | options_list.append(to_string(options_dict["audio_quality"])) 160 | 161 | elif option.name == "audio_quality": 162 | # If the '--audio-quality' is not already in the options list 163 | # from the above branch then follow the standard procedure. 164 | # We don't have to worry for the sequence in which the code 165 | # will be executed since the 'audio_quality' option is placed 166 | # after the 'audio_format' option in the self._ydl_options list 167 | if option.flag not in options_list: 168 | if option.check_requirements(options_dict): 169 | value = options_dict[option.name] 170 | 171 | if value != option.default_value: 172 | options_list.append(option.flag) 173 | options_list.append(to_string(value)) 174 | 175 | elif option.check_requirements(options_dict): 176 | value = options_dict[option.name] 177 | 178 | if value != option.default_value: 179 | options_list.append(option.flag) 180 | 181 | if not option.is_boolean(): 182 | options_list.append(to_string(value)) 183 | 184 | # Parse cmd_args 185 | 186 | # Indicates whether an item needs special handling 187 | special_case = False 188 | 189 | # Temp list to hold special items 190 | special_items = [] 191 | 192 | for item in options_dict["cmd_args"].split(): 193 | 194 | # Its a special case if its already a special case 195 | # or an item starts with double quotes 196 | special_case = (special_case or item[0] == "\"") 197 | 198 | if special_case: 199 | special_items.append(item) 200 | else: 201 | options_list.append(item) 202 | 203 | # If its a special case and we meet a double quote 204 | # at the end of the item, special case is over and 205 | # we need to join, filter and append our special items 206 | # to the options list 207 | if special_case and item[-1] == "\"": 208 | options_list.append(" ".join(special_items)[1:-1]) 209 | 210 | special_case = False 211 | special_items = [] 212 | 213 | return options_list 214 | 215 | def _build_savepath(self, options_dict): 216 | """Build the save path. 217 | 218 | We use this method to build the value of the 'save_path' option and 219 | store it back to the options dictionary. 220 | 221 | Args: 222 | options_dict (dict): Copy of the original options dictionary. 223 | 224 | """ 225 | save_path = remove_shortcuts(options_dict['save_path']) 226 | 227 | if options_dict["output_format"] == 0: 228 | template = "%(id)s.%(ext)s" 229 | elif options_dict["output_format"] == 1: 230 | template = "%(title)s.%(ext)s" 231 | elif options_dict["output_format"] == 2: 232 | template = "%(title)s-%(id)s.%(ext)s" 233 | elif options_dict["output_format"] == 4: 234 | template = "%(title)s-%(height)sp.%(ext)s" 235 | elif options_dict["output_format"] == 5: 236 | template = "%(title)s-%(id)s-%(height)sp.%(ext)s" 237 | else: 238 | template = options_dict["output_template"] 239 | 240 | options_dict["save_path"] = os.path.join(save_path, template) 241 | 242 | def _build_videoformat(self, options_dict): 243 | """Build the video format. 244 | 245 | We use this method to build the value of the 'video_format' option and 246 | store it back to the options dictionary. 247 | 248 | Args: 249 | options_dict (dict): Copy of the original options dictionary. 250 | 251 | """ 252 | if options_dict['video_format'] != '0' and options_dict['second_video_format'] != '0': 253 | options_dict['video_format'] = options_dict['video_format'] + '+' + options_dict['second_video_format'] 254 | 255 | def _build_filesizes(self, options_dict): 256 | """Build the filesize options values. 257 | 258 | We use this method to build the values of 'min_filesize' and 259 | 'max_filesize' options and store them back to options dictionary. 260 | 261 | Args: 262 | options_dict (dict): Copy of the original options dictionary. 263 | 264 | """ 265 | if options_dict['min_filesize']: 266 | options_dict['min_filesize'] = to_string(options_dict['min_filesize']) + options_dict['min_filesize_unit'] 267 | 268 | if options_dict['max_filesize']: 269 | options_dict['max_filesize'] = to_string(options_dict['max_filesize']) + options_dict['max_filesize_unit'] 270 | -------------------------------------------------------------------------------- /youtube_dl_gui/updatemanager.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | # -*- coding: utf-8 -*- 3 | 4 | """Youtubedlg module to update youtube-dl binary. 5 | 6 | Attributes: 7 | UPDATE_PUB_TOPIC (string): wxPublisher subscription topic of the 8 | UpdateThread thread. 9 | 10 | """ 11 | 12 | from __future__ import unicode_literals 13 | 14 | import os.path 15 | from threading import Thread 16 | from urllib2 import urlopen, URLError, HTTPError 17 | 18 | from wx import CallAfter 19 | from wx.lib.pubsub import setuparg1 20 | from wx.lib.pubsub import pub as Publisher 21 | 22 | from .utils import ( 23 | YOUTUBEDL_BIN, 24 | check_path 25 | ) 26 | 27 | UPDATE_PUB_TOPIC = 'update' 28 | 29 | 30 | class UpdateThread(Thread): 31 | 32 | """Python Thread that downloads youtube-dl binary. 33 | 34 | Attributes: 35 | LATEST_YOUTUBE_DL (string): URL with the latest youtube-dl binary. 36 | DOWNLOAD_TIMEOUT (int): Download timeout in seconds. 37 | 38 | Args: 39 | download_path (string): Absolute path where UpdateThread will download 40 | the latest youtube-dl. 41 | 42 | quiet (boolean): If True UpdateThread won't send the finish signal 43 | back to the caller. Finish signal can be used to make sure that 44 | the UpdateThread has been completed in an asynchronous way. 45 | 46 | """ 47 | 48 | LATEST_YOUTUBE_DL = 'https://yt-dl.org/latest/' 49 | DOWNLOAD_TIMEOUT = 10 50 | 51 | def __init__(self, download_path, quiet=False): 52 | super(UpdateThread, self).__init__() 53 | self.download_path = download_path 54 | self.quiet = quiet 55 | self.start() 56 | 57 | def run(self): 58 | self._talk_to_gui('download') 59 | 60 | source_file = self.LATEST_YOUTUBE_DL + YOUTUBEDL_BIN 61 | destination_file = os.path.join(self.download_path, YOUTUBEDL_BIN) 62 | 63 | check_path(self.download_path) 64 | 65 | try: 66 | stream = urlopen(source_file, timeout=self.DOWNLOAD_TIMEOUT) 67 | 68 | with open(destination_file, 'wb') as dest_file: 69 | dest_file.write(stream.read()) 70 | 71 | self._talk_to_gui('correct') 72 | except (HTTPError, URLError, IOError) as error: 73 | self._talk_to_gui('error', unicode(error)) 74 | 75 | if not self.quiet: 76 | self._talk_to_gui('finish') 77 | 78 | def _talk_to_gui(self, signal, data=None): 79 | """Communicate with the GUI using wxCallAfter and wxPublisher. 80 | 81 | Args: 82 | signal (string): Unique signal string that informs the GUI for the 83 | update process. 84 | 85 | data (string): Can be any string data to pass along with the 86 | given signal. Default is None. 87 | 88 | Note: 89 | UpdateThread supports 4 signals. 90 | 1) download: The update process started 91 | 2) correct: The update process completed successfully 92 | 3) error: An error occured while downloading youtube-dl binary 93 | 4) finish: The update thread is ready to join 94 | 95 | """ 96 | CallAfter(Publisher.sendMessage, UPDATE_PUB_TOPIC, (signal, data)) 97 | -------------------------------------------------------------------------------- /youtube_dl_gui/utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | # -*- coding: utf-8 -*- 3 | 4 | """Youtubedlg module that contains util functions. 5 | 6 | Attributes: 7 | _RANDOM_OBJECT (object): Object that it's used as a default parameter. 8 | 9 | YOUTUBEDL_BIN (string): Youtube-dl binary filename. 10 | 11 | """ 12 | 13 | from __future__ import unicode_literals 14 | 15 | import os 16 | import sys 17 | import json 18 | import math 19 | import locale 20 | import subprocess 21 | 22 | try: 23 | from twodict import TwoWayOrderedDict 24 | except ImportError as error: 25 | print error 26 | sys.exit(1) 27 | 28 | from .info import __appname__ 29 | from .version import __version__ 30 | 31 | 32 | _RANDOM_OBJECT = object() 33 | 34 | 35 | YOUTUBEDL_BIN = 'youtube-dl' 36 | if os.name == 'nt': 37 | YOUTUBEDL_BIN += '.exe' 38 | 39 | 40 | FILESIZE_METRICS = ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"] 41 | 42 | KILO_SIZE = 1024.0 43 | 44 | 45 | def get_encoding(): 46 | """Return system encoding. """ 47 | try: 48 | encoding = locale.getpreferredencoding() 49 | 'TEST'.encode(encoding) 50 | except: 51 | encoding = 'UTF-8' 52 | 53 | return encoding 54 | 55 | 56 | def convert_item(item, to_unicode=False): 57 | """Convert item between 'unicode' and 'str'. 58 | 59 | Args: 60 | item (-): Can be any python item. 61 | 62 | to_unicode (boolean): When True it will convert all the 'str' types 63 | to 'unicode'. When False it will convert all the 'unicode' 64 | types back to 'str'. 65 | 66 | """ 67 | if to_unicode and isinstance(item, str): 68 | # Convert str to unicode 69 | return item.decode(get_encoding(), 'ignore') 70 | 71 | if not to_unicode and isinstance(item, unicode): 72 | # Convert unicode to str 73 | return item.encode(get_encoding(), 'ignore') 74 | 75 | if hasattr(item, '__iter__'): 76 | # Handle iterables 77 | temp_list = [] 78 | 79 | for sub_item in item: 80 | if isinstance(item, dict): 81 | temp_list.append((convert_item(sub_item, to_unicode), convert_item(item[sub_item], to_unicode))) 82 | else: 83 | temp_list.append(convert_item(sub_item, to_unicode)) 84 | 85 | return type(item)(temp_list) 86 | 87 | return item 88 | 89 | 90 | def convert_on_bounds(func): 91 | """Decorator to convert string inputs & outputs. 92 | 93 | Covert string inputs & outputs between 'str' and 'unicode' at the 94 | application bounds using the preferred system encoding. It will convert 95 | all the string params (args, kwargs) to 'str' type and all the 96 | returned strings values back to 'unicode'. 97 | 98 | """ 99 | def wrapper(*args, **kwargs): 100 | returned_value = func(*convert_item(args), **convert_item(kwargs)) 101 | 102 | return convert_item(returned_value, True) 103 | 104 | return wrapper 105 | 106 | 107 | # See: https://github.com/MrS0m30n3/youtube-dl-gui/issues/57 108 | # Patch os functions to convert between 'str' and 'unicode' on app bounds 109 | os_sep = unicode(os.sep) 110 | os_getenv = convert_on_bounds(os.getenv) 111 | os_makedirs = convert_on_bounds(os.makedirs) 112 | os_path_isdir = convert_on_bounds(os.path.isdir) 113 | os_path_exists = convert_on_bounds(os.path.exists) 114 | os_path_dirname = convert_on_bounds(os.path.dirname) 115 | os_path_abspath = convert_on_bounds(os.path.abspath) 116 | os_path_realpath = convert_on_bounds(os.path.realpath) 117 | os_path_expanduser = convert_on_bounds(os.path.expanduser) 118 | 119 | # Patch locale functions 120 | locale_getdefaultlocale = convert_on_bounds(locale.getdefaultlocale) 121 | 122 | # Patch Windows specific functions 123 | if os.name == 'nt': 124 | os_startfile = convert_on_bounds(os.startfile) 125 | 126 | def remove_file(filename): 127 | if os_path_exists(filename): 128 | os.remove(filename) 129 | return True 130 | 131 | return False 132 | 133 | def remove_shortcuts(path): 134 | """Return given path after removing the shortcuts. """ 135 | return path.replace('~', os_path_expanduser('~')) 136 | 137 | 138 | def absolute_path(filename): 139 | """Return absolute path to the given file. """ 140 | return os_path_dirname(os_path_realpath(os_path_abspath(filename))) 141 | 142 | 143 | def open_file(file_path): 144 | """Open file in file_path using the default OS application. 145 | 146 | Returns: 147 | True on success else False. 148 | 149 | """ 150 | file_path = remove_shortcuts(file_path) 151 | 152 | if not os_path_exists(file_path): 153 | return False 154 | 155 | if os.name == "nt": 156 | os_startfile(file_path) 157 | else: 158 | subprocess.call(("xdg-open", file_path)) 159 | 160 | return True 161 | 162 | 163 | def encode_tuple(tuple_to_encode): 164 | """Turn size tuple into string. """ 165 | return '%s/%s' % (tuple_to_encode[0], tuple_to_encode[1]) 166 | 167 | 168 | def decode_tuple(encoded_tuple): 169 | """Turn tuple string back to tuple. """ 170 | s = encoded_tuple.split('/') 171 | return int(s[0]), int(s[1]) 172 | 173 | 174 | def check_path(path): 175 | """Create path if not exist. """ 176 | if not os_path_exists(path): 177 | os_makedirs(path) 178 | 179 | 180 | def get_config_path(): 181 | """Return user config path. 182 | 183 | Note: 184 | Windows = %AppData% + app_name 185 | Linux = ~/.config + app_name 186 | 187 | """ 188 | if os.name == 'nt': 189 | path = os_getenv('APPDATA') 190 | else: 191 | path = os.path.join(os_path_expanduser('~'), '.config') 192 | 193 | return os.path.join(path, __appname__.lower()) 194 | 195 | 196 | def shutdown_sys(password=None): 197 | """Shuts down the system. 198 | Returns True if no errors occur else False. 199 | 200 | Args: 201 | password (string): SUDO password for linux. 202 | 203 | Note: 204 | On Linux you need to provide sudo password if you don't 205 | have elevated privileges. 206 | 207 | """ 208 | _stderr = subprocess.PIPE 209 | _stdin = None 210 | info = None 211 | encoding = get_encoding() 212 | 213 | if os.name == 'nt': 214 | cmd = ['shutdown', '/s', '/t', '1'] 215 | 216 | # Hide subprocess window 217 | info = subprocess.STARTUPINFO() 218 | info.dwFlags |= subprocess.STARTF_USESHOWWINDOW 219 | else: 220 | if password: 221 | _stdin = subprocess.PIPE 222 | password = ('%s\n' % password).encode(encoding) 223 | cmd = ['sudo', '-S', '/sbin/shutdown', '-h', 'now'] 224 | else: 225 | cmd = ['/sbin/shutdown', '-h', 'now'] 226 | 227 | cmd = [item.encode(encoding, 'ignore') for item in cmd] 228 | 229 | shutdown_proc = subprocess.Popen(cmd, 230 | stderr=_stderr, 231 | stdin=_stdin, 232 | startupinfo=info) 233 | 234 | output = shutdown_proc.communicate(password)[1] 235 | 236 | return not output or output == "Password:" 237 | 238 | 239 | def to_string(data): 240 | """Convert data to string. 241 | Works for both Python2 & Python3. """ 242 | return '%s' % data 243 | 244 | 245 | def get_time(seconds): 246 | """Convert given seconds to days, hours, minutes and seconds. 247 | 248 | Args: 249 | seconds (float): Time in seconds. 250 | 251 | Returns: 252 | Dictionary that contains the corresponding days, hours, minutes 253 | and seconds of the given seconds. 254 | 255 | """ 256 | dtime = dict(seconds=0, minutes=0, hours=0, days=0) 257 | 258 | dtime['days'] = int(seconds / 86400) 259 | dtime['hours'] = int(seconds % 86400 / 3600) 260 | dtime['minutes'] = int(seconds % 86400 % 3600 / 60) 261 | dtime['seconds'] = int(seconds % 86400 % 3600 % 60) 262 | 263 | return dtime 264 | 265 | 266 | def get_locale_file(): 267 | """Search for youtube-dlg locale file. 268 | 269 | Returns: 270 | The path to youtube-dlg locale file if exists else None. 271 | 272 | Note: 273 | Paths that get_locale_file() func searches. 274 | 275 | __main__ dir, library dir 276 | 277 | """ 278 | DIR_NAME = "locale" 279 | 280 | SEARCH_DIRS = [ 281 | os.path.join(absolute_path(sys.argv[0]), DIR_NAME), 282 | os.path.join(os_path_dirname(__file__), DIR_NAME), 283 | ] 284 | 285 | for directory in SEARCH_DIRS: 286 | if os_path_isdir(directory): 287 | return directory 288 | 289 | return None 290 | 291 | 292 | def get_icon_file(): 293 | """Search for youtube-dlg app icon. 294 | 295 | Returns: 296 | The path to youtube-dlg icon file if exists, else returns None. 297 | 298 | """ 299 | ICON_NAME = "youtube-dl-gui.png" 300 | 301 | pixmaps_dir = get_pixmaps_dir() 302 | 303 | if pixmaps_dir is not None: 304 | icon_file = os.path.join(pixmaps_dir, ICON_NAME) 305 | 306 | if os_path_exists(icon_file): 307 | return icon_file 308 | 309 | return None 310 | 311 | 312 | def get_pixmaps_dir(): 313 | """Return absolute path to the pixmaps icons folder. 314 | 315 | Note: 316 | Paths we search: __main__ dir, library dir 317 | 318 | """ 319 | search_dirs = [ 320 | os.path.join(absolute_path(sys.argv[0]), "data"), 321 | os.path.join(os_path_dirname(__file__), "data") 322 | ] 323 | 324 | for directory in search_dirs: 325 | pixmaps_dir = os.path.join(directory, "pixmaps") 326 | 327 | if os_path_exists(pixmaps_dir): 328 | return pixmaps_dir 329 | 330 | return None 331 | 332 | 333 | def to_bytes(string): 334 | """Convert given youtube-dl size string to bytes.""" 335 | value = 0.0 336 | 337 | for index, metric in enumerate(reversed(FILESIZE_METRICS)): 338 | if metric in string: 339 | value = float(string.split(metric)[0]) 340 | break 341 | 342 | exponent = index * (-1) + (len(FILESIZE_METRICS) - 1) 343 | 344 | return round(value * (KILO_SIZE ** exponent), 2) 345 | 346 | 347 | def format_bytes(bytes): 348 | """Format bytes to youtube-dl size output strings.""" 349 | if bytes == 0.0: 350 | exponent = 0 351 | else: 352 | exponent = int(math.log(bytes, KILO_SIZE)) 353 | 354 | suffix = FILESIZE_METRICS[exponent] 355 | output_value = bytes / (KILO_SIZE ** exponent) 356 | 357 | return "%.2f%s" % (output_value, suffix) 358 | 359 | 360 | def build_command(options_list, url): 361 | """Build the youtube-dl command line string.""" 362 | 363 | def escape(option): 364 | """Wrap option with double quotes if it contains special symbols.""" 365 | special_symbols = [" ", "(", ")"] 366 | 367 | for symbol in special_symbols: 368 | if symbol in option: 369 | return "\"{}\"".format(option) 370 | 371 | return option 372 | 373 | # If option has special symbols wrap it with double quotes 374 | # Probably not the best solution since if the option already contains 375 | # double quotes it will be a mess, see issue #173 376 | options = [escape(option) for option in options_list] 377 | 378 | # Always wrap the url with double quotes 379 | url = "\"{}\"".format(url) 380 | 381 | return " ".join([YOUTUBEDL_BIN] + options + [url]) 382 | 383 | 384 | def get_default_lang(): 385 | """Get default language using the 'locale' module.""" 386 | default_lang, _ = locale_getdefaultlocale() 387 | 388 | if not default_lang: 389 | default_lang = "en_US" 390 | 391 | return default_lang 392 | -------------------------------------------------------------------------------- /youtube_dl_gui/version.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from __future__ import unicode_literals 4 | 5 | __version__ = '0.4' 6 | -------------------------------------------------------------------------------- /youtube_dl_gui/widgets.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | 4 | from __future__ import unicode_literals 5 | 6 | import sys 7 | 8 | try: 9 | import wx 10 | except ImportError as error: 11 | print error 12 | sys.exit(1) 13 | 14 | 15 | def crt_command_event(event_type, event_id=0): 16 | """Shortcut to create command events.""" 17 | return wx.CommandEvent(event_type.typeId, event_id) 18 | 19 | 20 | class ListBoxWithHeaders(wx.ListBox): 21 | 22 | """Custom ListBox object that supports 'headers'. 23 | 24 | Attributes: 25 | NAME (string): Default name for the name argument of the __init__. 26 | 27 | TEXT_PREFIX (string): Text to add before normal items in order to 28 | distinguish them (normal items) from headers. 29 | 30 | EVENTS (list): List with events to overwrite to avoid header selection. 31 | 32 | """ 33 | 34 | NAME = "listBoxWithHeaders" 35 | 36 | TEXT_PREFIX = " " 37 | 38 | EVENTS = [ 39 | wx.EVT_LEFT_DOWN, 40 | wx.EVT_LEFT_DCLICK, 41 | wx.EVT_RIGHT_DOWN, 42 | wx.EVT_RIGHT_DCLICK, 43 | wx.EVT_MIDDLE_DOWN, 44 | wx.EVT_MIDDLE_DCLICK 45 | ] 46 | 47 | def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, 48 | size=wx.DefaultSize, choices=[], style=0, validator=wx.DefaultValidator, name=NAME): 49 | super(ListBoxWithHeaders, self).__init__(parent, id, pos, size, [], style, validator, name) 50 | self.__headers = set() 51 | 52 | # Ignore all key events i'm bored to handle the header selection 53 | self.Bind(wx.EVT_KEY_DOWN, lambda event: None) 54 | 55 | # Make sure that a header is never selected 56 | self.Bind(wx.EVT_LISTBOX, self._on_listbox) 57 | for event in self.EVENTS: 58 | self.Bind(event, self._disable_header_selection) 59 | 60 | # Append the items in our own way in order to add the TEXT_PREFIX 61 | self.AppendItems(choices) 62 | 63 | def _disable_header_selection(self, event): 64 | """Stop event propagation if the selected item is a header.""" 65 | row = self.HitTest(event.GetPosition()) 66 | event_skip = True 67 | 68 | if row != wx.NOT_FOUND and self.GetString(row) in self.__headers: 69 | event_skip = False 70 | 71 | event.Skip(event_skip) 72 | 73 | def _on_listbox(self, event): 74 | """Make sure no header is selected.""" 75 | if event.GetString() in self.__headers: 76 | self.Deselect(event.GetSelection()) 77 | event.Skip() 78 | 79 | def _add_prefix(self, string): 80 | return self.TEXT_PREFIX + string 81 | 82 | def _remove_prefix(self, string): 83 | if string[:len(self.TEXT_PREFIX)] == self.TEXT_PREFIX: 84 | return string[len(self.TEXT_PREFIX):] 85 | return string 86 | 87 | # wx.ListBox methods 88 | 89 | def FindString(self, string): 90 | index = super(ListBoxWithHeaders, self).FindString(string) 91 | 92 | if index == wx.NOT_FOUND: 93 | # This time try with prefix 94 | index = super(ListBoxWithHeaders, self).FindString(self._add_prefix(string)) 95 | 96 | return index 97 | 98 | def GetStringSelection(self): 99 | return self._remove_prefix(super(ListBoxWithHeaders, self).GetStringSelection()) 100 | 101 | def GetString(self, index): 102 | if index < 0 or index >= self.GetCount(): 103 | # Return empty string based on the wx.ListBox docs 104 | # for some reason parent GetString does not handle 105 | # invalid indices 106 | return "" 107 | 108 | return self._remove_prefix(super(ListBoxWithHeaders, self).GetString(index)) 109 | 110 | def InsertItems(self, items, pos): 111 | items = [self._add_prefix(item) for item in items] 112 | super(ListBoxWithHeaders, self).InsertItems(items, pos) 113 | 114 | def SetSelection(self, index): 115 | if index == wx.NOT_FOUND: 116 | self.Deselect(self.GetSelection()) 117 | elif self.GetString(index) not in self.__headers: 118 | super(ListBoxWithHeaders, self).SetSelection(index) 119 | 120 | def SetString(self, index, string): 121 | old_string = self.GetString(index) 122 | 123 | if old_string in self.__headers and string != old_string: 124 | self.__headers.remove(old_string) 125 | self.__headers.add(string) 126 | 127 | super(ListBoxWithHeaders, self).SetString(index, string) 128 | 129 | def SetStringSelection(self, string): 130 | if string in self.__headers: 131 | return False 132 | 133 | self.SetSelection(self.FindString(string)) 134 | return True 135 | 136 | # wx.ItemContainer methods 137 | 138 | def Append(self, string): 139 | super(ListBoxWithHeaders, self).Append(self._add_prefix(string)) 140 | 141 | def AppendItems(self, strings): 142 | strings = [self._add_prefix(string) for string in strings] 143 | super(ListBoxWithHeaders, self).AppendItems(strings) 144 | 145 | def Clear(self): 146 | self.__headers.clear() 147 | super(ListBoxWithHeaders, self).Clear() 148 | 149 | def Delete(self, index): 150 | string = self.GetString(index) 151 | 152 | if string in self.__headers: 153 | self.__headers.remove(string) 154 | 155 | super(ListBoxWithHeaders, self).Delete(index) 156 | 157 | # Extra methods 158 | 159 | def add_header(self, header_string): 160 | self.__headers.add(header_string) 161 | super(ListBoxWithHeaders, self).Append(header_string) 162 | 163 | def add_item(self, item, with_prefix=True): 164 | if with_prefix: 165 | item = self._add_prefix(item) 166 | 167 | super(ListBoxWithHeaders, self).Append(item) 168 | 169 | def add_items(self, items, with_prefix=True): 170 | if with_prefix: 171 | items = [self._add_prefix(item) for item in items] 172 | 173 | super(ListBoxWithHeaders, self).AppendItems(items) 174 | 175 | 176 | class ListBoxPopup(wx.PopupTransientWindow): 177 | 178 | """ListBoxWithHeaders as a popup. 179 | 180 | This class uses the wx.PopupTransientWindow to create the popup and the 181 | API is based on the wx.combo.ComboPopup class. 182 | 183 | Attributes: 184 | EVENTS_TABLE (dict): Dictionary that contains all the events 185 | that this class emits. 186 | 187 | """ 188 | 189 | EVENTS_TABLE = { 190 | "EVT_COMBOBOX": crt_command_event(wx.EVT_COMBOBOX), 191 | "EVT_COMBOBOX_DROPDOWN" : crt_command_event(wx.EVT_COMBOBOX_DROPDOWN), 192 | "EVT_COMBOBOX_CLOSEUP": crt_command_event(wx.EVT_COMBOBOX_CLOSEUP) 193 | } 194 | 195 | def __init__(self, parent=None, flags=wx.BORDER_NONE): 196 | super(ListBoxPopup, self).__init__(parent, flags) 197 | self.__listbox = None 198 | 199 | def _on_motion(self, event): 200 | row = self.__listbox.HitTest(event.GetPosition()) 201 | 202 | if row != wx.NOT_FOUND: 203 | self.__listbox.SetSelection(row) 204 | 205 | if self.__listbox.IsSelected(row): 206 | self.curitem = row 207 | 208 | def _on_left_down(self, event): 209 | self.value = self.curitem 210 | self.Dismiss() 211 | 212 | # Send EVT_COMBOBOX to inform the parent for changes 213 | wx.PostEvent(self, self.EVENTS_TABLE["EVT_COMBOBOX"]) 214 | 215 | def Popup(self): 216 | super(ListBoxPopup, self).Popup() 217 | wx.PostEvent(self, self.EVENTS_TABLE["EVT_COMBOBOX_DROPDOWN"]) 218 | 219 | def OnDismiss(self): 220 | wx.PostEvent(self, self.EVENTS_TABLE["EVT_COMBOBOX_CLOSEUP"]) 221 | 222 | # wx.combo.ComboPopup methods 223 | 224 | def Init(self): 225 | self.value = self.curitem = -1 226 | 227 | def Create(self, parent): 228 | self.__listbox = ListBoxWithHeaders(parent, style=wx.LB_SINGLE) 229 | 230 | self.__listbox.Bind(wx.EVT_MOTION, self._on_motion) 231 | self.__listbox.Bind(wx.EVT_LEFT_DOWN, self._on_left_down) 232 | 233 | sizer = wx.BoxSizer() 234 | sizer.Add(self.__listbox, 1, wx.EXPAND) 235 | self.SetSizer(sizer) 236 | return True 237 | 238 | def GetAdjustedSize(self, min_width, pref_height, max_height): 239 | width, height = self.GetBestSize() 240 | 241 | if width < min_width: 242 | width = min_width 243 | 244 | if pref_height != -1: 245 | height = pref_height * self.__listbox.GetCount() + 5 246 | 247 | if height > max_height: 248 | height = max_height 249 | 250 | return wx.Size(width, height) 251 | 252 | def GetControl(self): 253 | return self.__listbox 254 | 255 | def GetStringValue(self): 256 | return self.__listbox.GetString(self.value) 257 | 258 | #def SetStringValue(self, string): 259 | #self.__listbox.SetStringSelection(string) 260 | 261 | 262 | class CustomComboBox(wx.Panel): 263 | 264 | """Custom combobox. 265 | 266 | Attributes: 267 | CB_READONLY (long): Read-only style. The only one supported from the 268 | wx.ComboBox styles. 269 | 270 | NAME (string): Default name for the name argument of the __init__. 271 | 272 | """ 273 | #NOTE wx.ComboBox does not support EVT_MOTION inside the popup 274 | #NOTE Tried with ComboCtrl but i was not able to draw the button 275 | 276 | CB_READONLY = wx.TE_READONLY 277 | 278 | NAME = "customComboBox" 279 | 280 | def __init__(self, parent, id=wx.ID_ANY, value="", pos=wx.DefaultPosition, 281 | size=wx.DefaultSize, choices=[], style=0, validator=wx.DefaultValidator, name=NAME): 282 | super(CustomComboBox, self).__init__(parent, id, pos, size, 0, name) 283 | 284 | assert style == self.CB_READONLY or style == 0 285 | 286 | # Create components 287 | self.textctrl = wx.TextCtrl(self, wx.ID_ANY, style=style, validator=validator) 288 | tc_height = self.textctrl.GetSize()[1] 289 | 290 | self.button = wx.Button(self, wx.ID_ANY, "▾", size=(tc_height, tc_height)) 291 | 292 | # Create the ListBoxPopup in two steps 293 | self.listbox = ListBoxPopup(self) 294 | self.listbox.Init() 295 | self.listbox.Create(self.listbox) 296 | 297 | # Set layout 298 | sizer = wx.BoxSizer() 299 | sizer.Add(self.textctrl, 1, wx.ALIGN_CENTER_VERTICAL) 300 | sizer.Add(self.button) 301 | self.SetSizer(sizer) 302 | 303 | # Bind events 304 | self.button.Bind(wx.EVT_BUTTON, self._on_button) 305 | 306 | for event in ListBoxPopup.EVENTS_TABLE.values(): 307 | self.listbox.Bind(wx.PyEventBinder(event.GetEventType()), self._propagate) 308 | 309 | # Append items since the ListBoxPopup does not have the 'choices' arg 310 | self.listbox.GetControl().AppendItems(choices) 311 | self.SetStringSelection(value) 312 | 313 | def _propagate(self, event): 314 | if event.GetEventType() == wx.EVT_COMBOBOX.typeId: 315 | self.textctrl.SetValue(self.listbox.GetStringValue()) 316 | 317 | wx.PostEvent(self, event) 318 | 319 | def _on_button(self, event): 320 | self.Popup() 321 | 322 | def _calc_popup_position(self): 323 | tc_x_axis, tc_y_axis = self.textctrl.ClientToScreen((0, 0)) 324 | _, tc_height = self.textctrl.GetSize() 325 | 326 | return tc_x_axis, tc_y_axis + tc_height 327 | 328 | def _calc_popup_size(self): 329 | me_width, _ = self.GetSize() 330 | _, tc_height = self.textctrl.GetSize() 331 | _, screen_height = wx.DisplaySize() 332 | 333 | _, me_y_axis = self.GetScreenPosition() 334 | 335 | available_height = screen_height - (me_y_axis + tc_height) 336 | sug_width, sug_height = self.listbox.GetAdjustedSize(me_width, tc_height, available_height) 337 | 338 | return me_width, sug_height 339 | 340 | # wx.ComboBox methods 341 | 342 | def Dismiss(self): 343 | self.listbox.Dismiss() 344 | 345 | def FindString(self, string, caseSensitive=False): 346 | #TODO handle caseSensitive 347 | return self.listbox.GetControl().FindString(string) 348 | 349 | def GetCount(self): 350 | return self.listbox.GetControl().GetCount() 351 | 352 | def GetCurrentSelection(self): 353 | return self.GetSelection() 354 | 355 | def GetInsertionPoint(self): 356 | return self.textctrl.GetInsertionPoint() 357 | 358 | def GetSelection(self): 359 | return self.listbox.value 360 | 361 | def GetTextSelection(self): 362 | return self.textctrl.GetSelection() 363 | 364 | def GetString(self, index): 365 | return self.listbox.GetControl().GetString(index) 366 | 367 | def GetStringSelection(self): 368 | return self.listbox.GetStringValue() 369 | 370 | def IsListEmpty(self): 371 | return self.listbox.GetControl().GetCount() == 0 372 | 373 | def IsTextEmpty(self): 374 | return not self.textctrl.GetValue() 375 | 376 | def Popup(self): 377 | self.listbox.SetPosition(self._calc_popup_position()) 378 | self.listbox.SetSize(self._calc_popup_size()) 379 | 380 | self.listbox.Popup() 381 | 382 | def SetSelection(self, index): 383 | self.listbox.GetControl().SetSelection(index) 384 | if self.listbox.GetControl().IsSelected(index): 385 | self.listbox.value = index 386 | self.textctrl.SetValue(self.listbox.GetStringValue()) 387 | 388 | def SetString(self, index, string): 389 | self.listbox.GetControl().SetString(index, string) 390 | 391 | def SetTextSelection(self, from_, to_): 392 | self.textctrl.SetSelection(from_, to_) 393 | 394 | def SetStringSelection(self, string): 395 | index = self.listbox.GetControl().FindString(string) 396 | self.listbox.GetControl().SetSelection(index) 397 | 398 | if index != wx.NOT_FOUND and self.listbox.GetControl().GetSelection() == index: 399 | self.listbox.value = index 400 | self.textctrl.SetValue(string) 401 | 402 | def SetValue(self, value): 403 | self.textctrl.SetValue(value) 404 | 405 | # wx.ItemContainer methods 406 | 407 | def Clear(self): 408 | self.textctrl.Clear() 409 | self.listbox.GetControl().Clear() 410 | 411 | def Append(self, item): 412 | self.listbox.GetControl().Append(item) 413 | 414 | def AppendItems(self, items): 415 | self.listbox.GetControl().AppendItems(items) 416 | 417 | def Delete(self, index): 418 | self.listbox.GetControl().Delete(index) 419 | 420 | # wx.TextEntry methods 421 | 422 | def GetValue(self): 423 | return self.textctrl.GetValue() 424 | 425 | # ListBoxWithHeaders methods 426 | 427 | def add_header(self, header): 428 | self.listbox.GetControl().add_header(header) 429 | 430 | def add_item(self, item, with_prefix=True): 431 | self.listbox.GetControl().add_item(item, with_prefix) 432 | 433 | def add_items(self, items, with_prefix=True): 434 | self.listbox.GetControl().add_items(items, with_prefix) 435 | --------------------------------------------------------------------------------