encuentro-4.0/0000775000175000017500000000000012642560467014455 5ustar facundofacundo00000000000000encuentro-4.0/AYUDA.txt0000644000175000017500000000325312133130037016037 0ustar facundofacundo00000000000000¡Bienvenido al Visualizador de contenidos del Canal Encuentro y otros! Este es un simple programa que permite buscar, descargar y ver contenido del Canal Encuentro y otros. Para más información de cómo instalar el programa y licencias, leer el archivo LEEME.txt. Cómo ejecutar el programa ------------------------- Una vez que está instalado se puede ejecutar el programa escribiendo... encuentro ...desde la linea de comandos, aunque también se puede ejecutarlo de la siguiente manera estando parado en el directorio donde se descomprimió el tarball: bin/encuentro Para ver no solo la salida estándar sino también el log del programa, ejecutar: bin/encuentro -v Cómo usarlo ----------- Lo primero que hay que hacer es actualizar la metadata de los programas. Para ello está el botón Actualizar en la barra superior, hacé click ahí, y luego de bajar la info tendrás centenares de programas para elegir. Para descargar cualquiera de ellos, tenés que seleccionarlo y hacer click en el botón "Descargar". Pero antes, como cada descarga es personal, hay que configurar el programa con el usuario y clave que saques en el sitio web del Canal Encuentro o del backend que corresponda (no todos necesitan autenticación) Entonces, en el programa hacé click en el botón Configurar de la barra de arriba. Ahí vas a ver algunas pestañas, con configuración general y específica para los distintos backends, incluso con la URL para ir a registrarte y obtener las credenciales). Listo! Ahora puedes descargar cualquier programa de cualquier canal! Una vez descargado, para verlos sólo necesitás seleccionarlo y hacer click en el botón de Play. ¡Que los disfrutes! encuentro-4.0/encuentro.desktop0000644000175000017500000000062211776557665020065 0ustar facundofacundo00000000000000[Desktop Entry] Version=1.0 Encoding=UTF-8 Type=Application Name=Encuentro GenericName=Encuentro Comment=Simple program to download and see the content from Canal Encuentro y otros Comment[es]=Simple programa que permite buscar, descargar y ver contenido del Canal Encuentro and others Icon=@ INSTALLED_ICON @ Exec=encuentro Terminal=false StartupNotify=false Categories=Application;Education;Network; encuentro-4.0/README.txt0000644000175000017500000000126012253337073016141 0ustar facundofacundo00000000000000Welcome to the Canal Encuentro visualization program! This is a simple program to search, download and see the content of the Canal Encuentro and others. This program is strongly oriented to Spanish speaking people, as the content of Canal Encuentro and the other channels is only in Spanish... for further information please check the LEEME.txt file. Notes regarding licenses: - The content of the channels is not distributed at all, but downloaded personally by the user, please check here to see the licenses about that content in http://conectate.gov.ar/ - The code is licensed under the GNU GPL v3, see file COPYING here or... http://www.gnu.org/licenses/gpl.html encuentro-4.0/man/0000775000175000017500000000000012642560467015230 5ustar facundofacundo00000000000000encuentro-4.0/man/encuentro.10000664000175000017500000000174312153136024017302 0ustar facundofacundo00000000000000.TH ENCUENTRO 1 .SH NAME encuentro \- busque, descargue, y vea el maravilloso contenido ofrecido por el Canal Encuentro, Paka Paka, BACUA, Educ.ar y otros. Note: This program is strongly oriented to Spanish speaking people, as the content of Canal Encuentro and the other channels is only in Spanish. .SH SYNOPSYS .B encuentro [-v] .SH DESCRIPTION Este es un simple programa que permite buscar, descargar y ver contenido de Encuentro y otros canales. Notar que este programa no distribuye contenido directamente, sino que permite un mejor uso personal de esos contenidos. Por favor, referirse a los sitios web correspondientes para saber qué se puede y qué no se puede hacer con los contenidos de tales sitios. Si se ejecuta con .B -v mostrará las lineas de log en stdout. .SH AUTHOR Facundo Batista .SH SEE ALSO La página del proyecto es http://encuentro.taniquetil.com.ar/ El desarrollo está centralizado en https://launchpad.net/launcherposta encuentro-4.0/version.txt0000644000175000017500000000000412642540420016656 0ustar facundofacundo000000000000004.0 encuentro-4.0/source_encuentro.py0000644000175000017500000000222112330722234020366 0ustar facundofacundo00000000000000 # Copyright 2011-2013 Facundo Batista # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR # PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # For further info, check https://launchpad.net/encuentro """Configuration file for Apport.""" from apport.hookutils import attach_related_packages, attach_file_if_exists from encuentro import logger def add_info(report): """Add info to the report.""" # attach the log fname = logger.get_filename() attach_file_if_exists(report, fname, "EncuentroLog") # info about dependencies packages = ["python-requests", "python-defer", "python-qt4", "python-xdg"] attach_related_packages(report, packages) encuentro-4.0/LEEME.txt0000644000175000017500000000547612542135444016047 0ustar facundofacundo00000000000000¡Bienvenido al Visualizador de contenidos del Canal Encuentro! Este es un simple programa que permite buscar, descargar y ver contenido del Canal Encuentro y otros. Para más información de cómo usar el programa, leer el archivo AYUDA.txt. Si querés conocer más sobre el proyecto, el mismo se gestiona desde aquí: https://launchpad.net/encuentro *Nota importante*: Si tenés una versión anterior a la 3.1, el programa no te va a funcionar. Tenés que actualizar sí o sí la versión. Esto es porque Encuentro migró sus contenidos al portal Conectate, por lo que las versiones viejas no te van a funcionar correctamente. La buena noticia es que ahora podrás descargar no sólo contenido de Encuentro, sino también de Paka Paka, Educ.ar, y otros. Cómo ejecutarlo directamente ---------------------------- Tanto si hacés un branch del proyecto, como si abrís el tarball, el programa puede ejecutarse fácilmente sin instalarlo, haciendo: bin/encuentro Cómo instalarlo --------------- Es bastante sencillo, sólo tenés que hacer: sudo python setup.py install Para que funcione correctamente, tenés que tener Python 2 instalado, y las siguientes dependencias (paquete y número mínimo de versión): python 2.6.6 python-requests 2.2.1 python-defer 1.0.6 python-qt4 4.9.1 python-xdg 0.15 python-bs4 4.1.0 python-notify 0.1.1 (este último, python-notify, no es realmente necesario, pero si está el programa notificará las descargas finalizadas) Cómo instalarlo en un virtualenv -------------------------------- Si no tenés la menor idea de qué es un virtualenv y el detalle anterior para instalar te sirve podés omitir este punto. Si querés instalarlo en un virtualenv para colaborar en el proyecto o por cualquier otra causa, tenés que seguir los siguientes pasos: Es necesario hacer un branch del proyecto, o descargar el tarball, tener instalado python-qt4 y python-notify. Al generar el virtualenv tenés que utilizar la opción '--system-site-packages'. Crear el virtualenv: virtualenv --system-site-packages path/to/encuentro_venv Activarlo: source path/to/encuentro_venv/bin/activate Instalar las dependencias: cd path/to/encuentro/code/trunk pip install -r requirements_py2.txt Ejecutar Encuentro: bin/encuentro Licencias --------- Este programa no distribuye contenido de Canal Encuentro, sino que permite un mejor uso personal de esos contenidos. Por favor, referirse al sitio web de Conectate (http://conectate.gov.ar/) para saber qué se puede y qué no se puede hacer con los contenidos de ese sitio. El código de este programa está licenciado bajo la GNU GPL v3, podés encontrar esta licencia en el archivo COPYING o acá: http://www.gnu.org/licenses/gpl.html encuentro-4.0/setup.py0000755000175000017500000001256012642557516016175 0ustar facundofacundo00000000000000#!/usr/bin/env python # Copyright 2011-2013 Facundo Batista # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR # PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # For further info, check https://launchpad.net/encuentro """Build tar.gz for encuentro. Needed packages to run (using Debian/Ubuntu package names): python 2.7 python-requests 0.12.1 python-defer 1.0.6 python-qt4 4.9.1 python-xdg 0.15 python-notify 0.1.1 # not really needed, but provides notifications python-bs4 4.1.0 youtube-dl # whatever version and optional; if too old or not # present, an included one will be used """ import os import shutil from distutils.command.install import install from distutils.core import setup class CustomInstall(install): """Custom installation class on package files. It copies all the files into the "PREFIX/share/PROJECTNAME" dir. """ def run(self): """Run parent install, and then save the install dir in the script.""" install.run(self) # fix installation path in the script(s) for script in self.distribution.scripts: script_path = os.path.join(self.install_scripts, os.path.basename(script)) with open(script_path, 'rb') as fh: content = fh.read() content = content.replace('@ INSTALLED_BASE_DIR @', self._custom_data_dir) with open(script_path, 'wb') as fh: fh.write(content) # fix the icon path, and save the .desktop file where it should be src_desktop = self.distribution.get_name() + '.desktop' if not os.path.exists(self._custom_apps_dir): os.makedirs(self._custom_apps_dir) dst_desktop = os.path.join(self._custom_apps_dir, src_desktop) with open(src_desktop, 'rb') as fh: content = fh.read() icon = os.path.join(self._custom_data_dir, 'encuentro', 'logos', 'icon-32.png') content = content.replace('@ INSTALLED_ICON @', icon) with open(dst_desktop, 'wb') as fh: fh.write(content) # install apport file if not os.path.exists(self._custom_apport_dir): os.makedirs(self._custom_apport_dir) shutil.copy("source_encuentro.py", self._custom_apport_dir) # man directory if not os.path.exists(self._custom_man_dir): os.makedirs(self._custom_man_dir) shutil.copy("man/encuentro.1", self._custom_man_dir) # version file shutil.copy("version.txt", self.install_lib) def finalize_options(self): """Alter the installation path.""" install.finalize_options(self) # the data path is under 'prefix' data_dir = os.path.join(self.prefix, "share", self.distribution.get_name()) apps_dir = os.path.join(self.prefix, "share", "applications") apport_dir = os.path.join(self.prefix, "share", "apport", "package-hooks") man_dir = os.path.join(self.prefix, "share", "man", "man1") # if we have 'root', put the building path also under it (used normally # by pbuilder) if self.root is None: build_dir = data_dir else: build_dir = os.path.join(self.root, data_dir[1:]) apps_dir = os.path.join(self.root, apps_dir[1:]) apport_dir = os.path.join(self.root, apport_dir[1:]) man_dir = os.path.join(self.root, man_dir[1:]) # change the lib install directory so all package files go inside here self.install_lib = build_dir # save this custom data dir to later change the scripts self._custom_data_dir = data_dir self._custom_apps_dir = apps_dir self._custom_apport_dir = apport_dir self._custom_man_dir = man_dir LONG_DESCRIPTION = ( 'Simple application that allows to search, download ' 'and see the content of the Encuentro channel.' ) setup( name='encuentro', version=open('version.txt').read().strip(), license='GPL-3', author='Facundo Batista', author_email='facundo@taniquetil.com.ar', description='Search, download and see the wonderful Encuentro content.', long_description=LONG_DESCRIPTION, url='https://launchpad.net/encuentro', packages=[ "encuentro", "encuentro.ui", "external/youtube-dl/youtube_dl", "external/youtube-dl/youtube_dl/downloader", "external/youtube-dl/youtube_dl/extractor", "external/youtube-dl/youtube_dl/postprocessor", ], package_data={ "encuentro": ["ui/media/*", "logos/icon-*.png"], "": ["encuentro.desktop", "source_encuentro.py", "version.txt"], "external": ["youtube-dl/*"], }, scripts=["bin/encuentro"], cmdclass={ 'install': CustomInstall, }, ) encuentro-4.0/PKG-INFO0000664000175000017500000000056012642560467015553 0ustar facundofacundo00000000000000Metadata-Version: 1.0 Name: encuentro Version: 4.0 Summary: Search, download and see the wonderful Encuentro content. Home-page: https://launchpad.net/encuentro Author: Facundo Batista Author-email: facundo@taniquetil.com.ar License: GPL-3 Description: Simple application that allows to search, download and see the content of the Encuentro channel. Platform: UNKNOWN encuentro-4.0/external/0000775000175000017500000000000012642560467016277 5ustar facundofacundo00000000000000encuentro-4.0/external/youtube-dl/0000775000175000017500000000000012642560467020370 5ustar facundofacundo00000000000000encuentro-4.0/external/youtube-dl/LICENSE0000664000175000017500000000227312637746176021410 0ustar facundofacundo00000000000000This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to encuentro-4.0/external/youtube-dl/README.md0000664000175000017500000017115612637746176021671 0ustar facundofacundo00000000000000youtube-dl - download videos from youtube.com or other video platforms - [INSTALLATION](#installation) - [DESCRIPTION](#description) - [OPTIONS](#options) - [CONFIGURATION](#configuration) - [OUTPUT TEMPLATE](#output-template) - [FORMAT SELECTION](#format-selection) - [VIDEO SELECTION](#video-selection) - [FAQ](#faq) - [DEVELOPER INSTRUCTIONS](#developer-instructions) - [EMBEDDING YOUTUBE-DL](#embedding-youtube-dl) - [BUGS](#bugs) - [COPYRIGHT](#copyright) # INSTALLATION To install it right away for all UNIX users (Linux, OS X, etc.), type: sudo curl https://yt-dl.org/latest/youtube-dl -o /usr/local/bin/youtube-dl sudo chmod a+rx /usr/local/bin/youtube-dl If you do not have curl, you can alternatively use a recent wget: sudo wget https://yt-dl.org/downloads/latest/youtube-dl -O /usr/local/bin/youtube-dl sudo chmod a+rx /usr/local/bin/youtube-dl Windows users can [download a .exe file](https://yt-dl.org/latest/youtube-dl.exe) and place it in their home directory or any other location on their [PATH](http://en.wikipedia.org/wiki/PATH_%28variable%29). OS X users can install **youtube-dl** with [Homebrew](http://brew.sh/). brew install youtube-dl You can also use pip: sudo pip install youtube-dl Alternatively, refer to the [developer instructions](#developer-instructions) for how to check out and work with the git repository. For further options, including PGP signatures, see the [youtube-dl Download Page](https://rg3.github.io/youtube-dl/download.html). # DESCRIPTION **youtube-dl** is a small command-line program to download videos from YouTube.com and a few more sites. It requires the Python interpreter, version 2.6, 2.7, or 3.2+, and it is not platform specific. It should work on your Unix box, on Windows or on Mac OS X. It is released to the public domain, which means you can modify it, redistribute it or use it however you like. youtube-dl [OPTIONS] URL [URL...] # OPTIONS -h, --help Print this help text and exit --version Print program version and exit -U, --update Update this program to latest version. Make sure that you have sufficient permissions (run with sudo if needed) -i, --ignore-errors Continue on download errors, for example to skip unavailable videos in a playlist --abort-on-error Abort downloading of further videos (in the playlist or the command line) if an error occurs --dump-user-agent Display the current browser identification --list-extractors List all supported extractors --extractor-descriptions Output descriptions of all supported extractors --force-generic-extractor Force extraction to use the generic extractor --default-search PREFIX Use this prefix for unqualified URLs. For example "gvsearch2:" downloads two videos from google videos for youtube-dl "large apple". Use the value "auto" to let youtube-dl guess ("auto_warning" to emit a warning when guessing). "error" just throws an error. The default value "fixup_error" repairs broken URLs, but emits an error if this is not possible instead of searching. --ignore-config Do not read configuration files. When given in the global configuration file /etc /youtube-dl.conf: Do not read the user configuration in ~/.config/youtube- dl/config (%APPDATA%/youtube-dl/config.txt on Windows) --flat-playlist Do not extract the videos of a playlist, only list them. --no-color Do not emit color codes in output ## Network Options: --proxy URL Use the specified HTTP/HTTPS proxy. Pass in an empty string (--proxy "") for direct connection --socket-timeout SECONDS Time to wait before giving up, in seconds --source-address IP Client-side IP address to bind to (experimental) -4, --force-ipv4 Make all connections via IPv4 (experimental) -6, --force-ipv6 Make all connections via IPv6 (experimental) --cn-verification-proxy URL Use this proxy to verify the IP address for some Chinese sites. The default proxy specified by --proxy (or none, if the options is not present) is used for the actual downloading. (experimental) ## Video Selection: --playlist-start NUMBER Playlist video to start at (default is 1) --playlist-end NUMBER Playlist video to end at (default is last) --playlist-items ITEM_SPEC Playlist video items to download. Specify indices of the videos in the playlist separated by commas like: "--playlist-items 1,2,5,8" if you want to download videos indexed 1, 2, 5, 8 in the playlist. You can specify range: "--playlist-items 1-3,7,10-13", it will download the videos at index 1, 2, 3, 7, 10, 11, 12 and 13. --match-title REGEX Download only matching titles (regex or caseless sub-string) --reject-title REGEX Skip download for matching titles (regex or caseless sub-string) --max-downloads NUMBER Abort after downloading NUMBER files --min-filesize SIZE Do not download any videos smaller than SIZE (e.g. 50k or 44.6m) --max-filesize SIZE Do not download any videos larger than SIZE (e.g. 50k or 44.6m) --date DATE Download only videos uploaded in this date --datebefore DATE Download only videos uploaded on or before this date (i.e. inclusive) --dateafter DATE Download only videos uploaded on or after this date (i.e. inclusive) --min-views COUNT Do not download any videos with less than COUNT views --max-views COUNT Do not download any videos with more than COUNT views --match-filter FILTER Generic video filter (experimental). Specify any key (see help for -o for a list of available keys) to match if the key is present, !key to check if the key is not present,key > NUMBER (like "comment_count > 12", also works with >=, <, <=, !=, =) to compare against a number, and & to require multiple matches. Values which are not known are excluded unless you put a question mark (?) after the operator.For example, to only match videos that have been liked more than 100 times and disliked less than 50 times (or the dislike functionality is not available at the given service), but who also have a description, use --match-filter "like_count > 100 & dislike_count \youtube-dl.conf`. For example, with the following configuration file youtube-dl will always extract the audio, not copy the mtime and use a proxy: ``` --extract-audio --no-mtime --proxy 127.0.0.1:3128 ``` You can use `--ignore-config` if you want to disable the configuration file for a particular youtube-dl run. ### Authentication with `.netrc` file You may also want to configure automatic credentials storage for extractors that support authentication (by providing login and password with `--username` and `--password`) in order not to pass credentials as command line arguments on every youtube-dl execution and prevent tracking plain text passwords in the shell command history. You can achieve this using a [`.netrc` file](http://stackoverflow.com/tags/.netrc/info) on per extractor basis. For that you will need to create a`.netrc` file in your `$HOME` and restrict permissions to read/write by you only: ``` touch $HOME/.netrc chmod a-rwx,u+rw $HOME/.netrc ``` After that you can add credentials for extractor in the following format, where *extractor* is the name of extractor in lowercase: ``` machine login password ``` For example: ``` machine youtube login myaccount@gmail.com password my_youtube_password machine twitch login my_twitch_account_name password my_twitch_password ``` To activate authentication with the `.netrc` file you should pass `--netrc` to youtube-dl or place it in the [configuration file](#configuration). On Windows you may also need to setup the `%HOME%` environment variable manually. # OUTPUT TEMPLATE The `-o` option allows users to indicate a template for the output file names. The basic usage is not to set any template arguments when downloading a single file, like in `youtube-dl -o funny_video.flv "http://some/video"`. However, it may contain special sequences that will be replaced when downloading each video. The special sequences have the format `%(NAME)s`. To clarify, that is a percent symbol followed by a name in parentheses, followed by a lowercase S. Allowed names are: - `id`: The sequence will be replaced by the video identifier. - `url`: The sequence will be replaced by the video URL. - `uploader`: The sequence will be replaced by the nickname of the person who uploaded the video. - `upload_date`: The sequence will be replaced by the upload date in YYYYMMDD format. - `title`: The sequence will be replaced by the video title. - `ext`: The sequence will be replaced by the appropriate extension (like flv or mp4). - `epoch`: The sequence will be replaced by the Unix epoch when creating the file. - `autonumber`: The sequence will be replaced by a five-digit number that will be increased with each download, starting at zero. - `playlist`: The sequence will be replaced by the name or the id of the playlist that contains the video. - `playlist_index`: The sequence will be replaced by the index of the video in the playlist padded with leading zeros according to the total length of the playlist. - `format_id`: The sequence will be replaced by the format code specified by `--format`. - `duration`: The sequence will be replaced by the length of the video in seconds. The current default template is `%(title)s-%(id)s.%(ext)s`. In some cases, you don't want special characters such as 中, spaces, or &, such as when transferring the downloaded filename to a Windows system or the filename through an 8bit-unsafe channel. In these cases, add the `--restrict-filenames` flag to get a shorter title: ```bash $ youtube-dl --get-filename -o "%(title)s.%(ext)s" BaW_jenozKc youtube-dl test video ''_ä↭𝕐.mp4 # All kinds of weird characters $ youtube-dl --get-filename -o "%(title)s.%(ext)s" BaW_jenozKc --restrict-filenames youtube-dl_test_video_.mp4 # A simple file name ``` # FORMAT SELECTION By default youtube-dl tries to download the best quality, but sometimes you may want to download in a different format. The simplest case is requesting a specific format, for example `-f 22`. You can get the list of available formats using `--list-formats`, you can also use a file extension (currently it supports aac, m4a, mp3, mp4, ogg, wav, webm) or the special names `best`, `bestvideo`, `bestaudio` and `worst`. If you want to download multiple videos and they don't have the same formats available, you can specify the order of preference using slashes, as in `-f 22/17/18`. You can also filter the video results by putting a condition in brackets, as in `-f "best[height=720]"` (or `-f "[filesize>10M]"`). This works for filesize, height, width, tbr, abr, vbr, asr, and fps and the comparisons <, <=, >, >=, =, != and for ext, acodec, vcodec, container, and protocol and the comparisons =, != . Formats for which the value is not known are excluded unless you put a question mark (?) after the operator. You can combine format filters, so `-f "[height <=? 720][tbr>500]"` selects up to 720p videos (or videos where the height is not known) with a bitrate of at least 500 KBit/s. Use commas to download multiple formats, such as `-f 136/137/mp4/bestvideo,140/m4a/bestaudio`. You can merge the video and audio of two formats into a single file using `-f +` (requires ffmpeg or avconv), for example `-f bestvideo+bestaudio`. Format selectors can also be grouped using parentheses, for example if you want to download the best mp4 and webm formats with a height lower than 480 you can use `-f '(mp4,webm)[height<480]'`. Since the end of April 2015 and version 2015.04.26 youtube-dl uses `-f bestvideo+bestaudio/best` as default format selection (see #5447, #5456). If ffmpeg or avconv are installed this results in downloading `bestvideo` and `bestaudio` separately and muxing them together into a single file giving the best overall quality available. Otherwise it falls back to `best` and results in downloading the best available quality served as a single file. `best` is also needed for videos that don't come from YouTube because they don't provide the audio and video in two different files. If you want to only download some dash formats (for example if you are not interested in getting videos with a resolution higher than 1080p), you can add `-f bestvideo[height<=?1080]+bestaudio/best` to your configuration file. Note that if you use youtube-dl to stream to `stdout` (and most likely to pipe it to your media player then), i.e. you explicitly specify output template as `-o -`, youtube-dl still uses `-f best` format selection in order to start content delivery immediately to your player and not to wait until `bestvideo` and `bestaudio` are downloaded and muxed. If you want to preserve the old format selection behavior (prior to youtube-dl 2015.04.26), i.e. you want to download the best available quality media served as a single file, you should explicitly specify your choice with `-f best`. You may want to add it to the [configuration file](#configuration) in order not to type it every time you run youtube-dl. # VIDEO SELECTION Videos can be filtered by their upload date using the options `--date`, `--datebefore` or `--dateafter`. They accept dates in two formats: - Absolute dates: Dates in the format `YYYYMMDD`. - Relative dates: Dates in the format `(now|today)[+-][0-9](day|week|month|year)(s)?` Examples: ```bash # Download only the videos uploaded in the last 6 months $ youtube-dl --dateafter now-6months # Download only the videos uploaded on January 1, 1970 $ youtube-dl --date 19700101 $ # Download only the videos uploaded in the 200x decade $ youtube-dl --dateafter 20000101 --datebefore 20091231 ``` # FAQ ### How do I update youtube-dl? If you've followed [our manual installation instructions](http://rg3.github.io/youtube-dl/download.html), you can simply run `youtube-dl -U` (or, on Linux, `sudo youtube-dl -U`). If you have used pip, a simple `sudo pip install -U youtube-dl` is sufficient to update. If you have installed youtube-dl using a package manager like *apt-get* or *yum*, use the standard system update mechanism to update. Note that distribution packages are often outdated. As a rule of thumb, youtube-dl releases at least once a month, and often weekly or even daily. Simply go to http://yt-dl.org/ to find out the current version. Unfortunately, there is nothing we youtube-dl developers can do if your distribution serves a really outdated version. You can (and should) complain to your distribution in their bugtracker or support forum. As a last resort, you can also uninstall the version installed by your package manager and follow our manual installation instructions. For that, remove the distribution's package, with a line like sudo apt-get remove -y youtube-dl Afterwards, simply follow [our manual installation instructions](http://rg3.github.io/youtube-dl/download.html): ``` sudo wget https://yt-dl.org/latest/youtube-dl -O /usr/local/bin/youtube-dl sudo chmod a+x /usr/local/bin/youtube-dl hash -r ``` Again, from then on you'll be able to update with `sudo youtube-dl -U`. ### I'm getting an error `Unable to extract OpenGraph title` on YouTube playlists YouTube changed their playlist format in March 2014 and later on, so you'll need at least youtube-dl 2014.07.25 to download all YouTube videos. If you have installed youtube-dl with a package manager, pip, setup.py or a tarball, please use that to update. Note that Ubuntu packages do not seem to get updated anymore. Since we are not affiliated with Ubuntu, there is little we can do. Feel free to [report bugs](https://bugs.launchpad.net/ubuntu/+source/youtube-dl/+filebug) to the [Ubuntu packaging guys](mailto:ubuntu-motu@lists.ubuntu.com?subject=outdated%20version%20of%20youtube-dl) - all they have to do is update the package to a somewhat recent version. See above for a way to update. ### Do I always have to pass `-citw`? By default, youtube-dl intends to have the best options (incidentally, if you have a convincing case that these should be different, [please file an issue where you explain that](https://yt-dl.org/bug)). Therefore, it is unnecessary and sometimes harmful to copy long option strings from webpages. In particular, the only option out of `-citw` that is regularly useful is `-i`. ### Can you please put the `-b` option back? Most people asking this question are not aware that youtube-dl now defaults to downloading the highest available quality as reported by YouTube, which will be 1080p or 720p in some cases, so you no longer need the `-b` option. For some specific videos, maybe YouTube does not report them to be available in a specific high quality format you're interested in. In that case, simply request it with the `-f` option and youtube-dl will try to download it. ### I get HTTP error 402 when trying to download a video. What's this? Apparently YouTube requires you to pass a CAPTCHA test if you download too much. We're [considering to provide a way to let you solve the CAPTCHA](https://github.com/rg3/youtube-dl/issues/154), but at the moment, your best course of action is pointing a webbrowser to the youtube URL, solving the CAPTCHA, and restart youtube-dl. ### Do I need any other programs? youtube-dl works fine on its own on most sites. However, if you want to convert video/audio, you'll need [avconv](https://libav.org/) or [ffmpeg](https://www.ffmpeg.org/). On some sites - most notably YouTube - videos can be retrieved in a higher quality format without sound. youtube-dl will detect whether avconv/ffmpeg is present and automatically pick the best option. Videos or video formats streamed via RTMP protocol can only be downloaded when [rtmpdump](https://rtmpdump.mplayerhq.hu/) is installed. Downloading MMS and RTSP videos requires either [mplayer](http://mplayerhq.hu/) or [mpv](https://mpv.io/) to be installed. ### I have downloaded a video but how can I play it? Once the video is fully downloaded, use any video player, such as [vlc](http://www.videolan.org) or [mplayer](http://www.mplayerhq.hu/). ### I extracted a video URL with `-g`, but it does not play on another machine / in my webbrowser. It depends a lot on the service. In many cases, requests for the video (to download/play it) must come from the same IP address and with the same cookies. Use the `--cookies` option to write the required cookies into a file, and advise your downloader to read cookies from that file. Some sites also require a common user agent to be used, use `--dump-user-agent` to see the one in use by youtube-dl. It may be beneficial to use IPv6; in some cases, the restrictions are only applied to IPv4. Some services (sometimes only for a subset of videos) do not restrict the video URL by IP address, cookie, or user-agent, but these are the exception rather than the rule. Please bear in mind that some URL protocols are **not** supported by browsers out of the box, including RTMP. If you are using `-g`, your own downloader must support these as well. If you want to play the video on a machine that is not running youtube-dl, you can relay the video content from the machine that runs youtube-dl. You can use `-o -` to let youtube-dl stream a video to stdout, or simply allow the player to download the files written by youtube-dl in turn. ### ERROR: no fmt_url_map or conn information found in video info YouTube has switched to a new video info format in July 2011 which is not supported by old versions of youtube-dl. See [above](#how-do-i-update-youtube-dl) for how to update youtube-dl. ### ERROR: unable to download video YouTube requires an additional signature since September 2012 which is not supported by old versions of youtube-dl. See [above](#how-do-i-update-youtube-dl) for how to update youtube-dl. ### Video URL contains an ampersand and I'm getting some strange output `[1] 2839` or `'v' is not recognized as an internal or external command` That's actually the output from your shell. Since ampersand is one of the special shell characters it's interpreted by the shell preventing you from passing the whole URL to youtube-dl. To disable your shell from interpreting the ampersands (or any other special characters) you have to either put the whole URL in quotes or escape them with a backslash (which approach will work depends on your shell). For example if your URL is https://www.youtube.com/watch?t=4&v=BaW_jenozKc you should end up with following command: ```youtube-dl 'https://www.youtube.com/watch?t=4&v=BaW_jenozKc'``` or ```youtube-dl https://www.youtube.com/watch?t=4\&v=BaW_jenozKc``` For Windows you have to use the double quotes: ```youtube-dl "https://www.youtube.com/watch?t=4&v=BaW_jenozKc"``` ### ExtractorError: Could not find JS function u'OF' In February 2015, the new YouTube player contained a character sequence in a string that was misinterpreted by old versions of youtube-dl. See [above](#how-do-i-update-youtube-dl) for how to update youtube-dl. ### HTTP Error 429: Too Many Requests or 402: Payment Required These two error codes indicate that the service is blocking your IP address because of overuse. Contact the service and ask them to unblock your IP address, or - if you have acquired a whitelisted IP address already - use the [`--proxy` or `--source-address` options](#network-options) to select another IP address. ### SyntaxError: Non-ASCII character The error File "youtube-dl", line 2 SyntaxError: Non-ASCII character '\x93' ... means you're using an outdated version of Python. Please update to Python 2.6 or 2.7. ### What is this binary file? Where has the code gone? Since June 2012 (#342) youtube-dl is packed as an executable zipfile, simply unzip it (might need renaming to `youtube-dl.zip` first on some systems) or clone the git repository, as laid out above. If you modify the code, you can run it by executing the `__main__.py` file. To recompile the executable, run `make youtube-dl`. ### The exe throws a *Runtime error from Visual C++* To run the exe you need to install first the [Microsoft Visual C++ 2008 Redistributable Package](http://www.microsoft.com/en-us/download/details.aspx?id=29). ### On Windows, how should I set up ffmpeg and youtube-dl? Where should I put the exe files? If you put youtube-dl and ffmpeg in the same directory that you're running the command from, it will work, but that's rather cumbersome. To make a different directory work - either for ffmpeg, or for youtube-dl, or for both - simply create the directory (say, `C:\bin`, or `C:\Users\\bin`), put all the executables directly in there, and then [set your PATH environment variable](https://www.java.com/en/download/help/path.xml) to include that directory. From then on, after restarting your shell, you will be able to access both youtube-dl and ffmpeg (and youtube-dl will be able to find ffmpeg) by simply typing `youtube-dl` or `ffmpeg`, no matter what directory you're in. ### How do I put downloads into a specific folder? Use the `-o` to specify an [output template](#output-template), for example `-o "/home/user/videos/%(title)s-%(id)s.%(ext)s"`. If you want this for all of your downloads, put the option into your [configuration file](#configuration). ### How do I download a video starting with a `-`? Either prepend `http://www.youtube.com/watch?v=` or separate the ID from the options with `--`: youtube-dl -- -wNyEUrxzFU youtube-dl "http://www.youtube.com/watch?v=-wNyEUrxzFU" ### How do I pass cookies to youtube-dl? Use the `--cookies` option, for example `--cookies /path/to/cookies/file.txt`. Note that the cookies file must be in Mozilla/Netscape format and the first line of the cookies file must be either `# HTTP Cookie File` or `# Netscape HTTP Cookie File`. Make sure you have correct [newline format](https://en.wikipedia.org/wiki/Newline) in the cookies file and convert newlines if necessary to correspond with your OS, namely `CRLF` (`\r\n`) for Windows, `LF` (`\n`) for Linux and `CR` (`\r`) for Mac OS. `HTTP Error 400: Bad Request` when using `--cookies` is a good sign of invalid newline format. Passing cookies to youtube-dl is a good way to workaround login when a particular extractor does not implement it explicitly. ### Can you add support for this anime video site, or site which shows current movies for free? As a matter of policy (as well as legality), youtube-dl does not include support for services that specialize in infringing copyright. As a rule of thumb, if you cannot easily find a video that the service is quite obviously allowed to distribute (i.e. that has been uploaded by the creator, the creator's distributor, or is published under a free license), the service is probably unfit for inclusion to youtube-dl. A note on the service that they don't host the infringing content, but just link to those who do, is evidence that the service should **not** be included into youtube-dl. The same goes for any DMCA note when the whole front page of the service is filled with videos they are not allowed to distribute. A "fair use" note is equally unconvincing if the service shows copyright-protected videos in full without authorization. Support requests for services that **do** purchase the rights to distribute their content are perfectly fine though. If in doubt, you can simply include a source that mentions the legitimate purchase of content. ### How can I speed up work on my issue? (Also known as: Help, my important issue not being solved!) The youtube-dl core developer team is quite small. While we do our best to solve as many issues as possible, sometimes that can take quite a while. To speed up your issue, here's what you can do: First of all, please do report the issue [at our issue tracker](https://yt-dl.org/bugs). That allows us to coordinate all efforts by users and developers, and serves as a unified point. Unfortunately, the youtube-dl project has grown too large to use personal email as an effective communication channel. Please read the [bug reporting instructions](#bugs) below. A lot of bugs lack all the necessary information. If you can, offer proxy, VPN, or shell access to the youtube-dl developers. If you are able to, test the issue from multiple computers in multiple countries to exclude local censorship or misconfiguration issues. If nobody is interested in solving your issue, you are welcome to take matters into your own hands and submit a pull request (or coerce/pay somebody else to do so). Feel free to bump the issue from time to time by writing a small comment ("Issue is still present in youtube-dl version ...from France, but fixed from Belgium"), but please not more than once a month. Please do not declare your issue as `important` or `urgent`. ### How can I detect whether a given URL is supported by youtube-dl? For one, have a look at the [list of supported sites](docs/supportedsites.md). Note that it can sometimes happen that the site changes its URL scheme (say, from http://example.com/video/1234567 to http://example.com/v/1234567 ) and youtube-dl reports an URL of a service in that list as unsupported. In that case, simply report a bug. It is *not* possible to detect whether a URL is supported or not. That's because youtube-dl contains a generic extractor which matches **all** URLs. You may be tempted to disable, exclude, or remove the generic extractor, but the generic extractor not only allows users to extract videos from lots of websites that embed a video from another service, but may also be used to extract video from a service that it's hosting itself. Therefore, we neither recommend nor support disabling, excluding, or removing the generic extractor. If you want to find out whether a given URL is supported, simply call youtube-dl with it. If you get no videos back, chances are the URL is either not referring to a video or unsupported. You can find out which by examining the output (if you run youtube-dl on the console) or catching an `UnsupportedError` exception if you run it from a Python program. # DEVELOPER INSTRUCTIONS Most users do not need to build youtube-dl and can [download the builds](http://rg3.github.io/youtube-dl/download.html) or get them from their distribution. To run youtube-dl as a developer, you don't need to build anything either. Simply execute python -m youtube_dl To run the test, simply invoke your favorite test runner, or execute a test file directly; any of the following work: python -m unittest discover python test/test_download.py nosetests If you want to create a build of youtube-dl yourself, you'll need * python * make * pandoc * zip * nosetests ### Adding support for a new site If you want to add support for a new site, you can follow this quick list (assuming your service is called `yourextractor`): 1. [Fork this repository](https://github.com/rg3/youtube-dl/fork) 2. Check out the source code with `git clone git@github.com:YOUR_GITHUB_USERNAME/youtube-dl.git` 3. Start a new git branch with `cd youtube-dl; git checkout -b yourextractor` 4. Start with this simple template and save it to `youtube_dl/extractor/yourextractor.py`: ```python # coding: utf-8 from __future__ import unicode_literals from .common import InfoExtractor class YourExtractorIE(InfoExtractor): _VALID_URL = r'https?://(?:www\.)?yourextractor\.com/watch/(?P[0-9]+)' _TEST = { 'url': 'http://yourextractor.com/watch/42', 'md5': 'TODO: md5 sum of the first 10241 bytes of the video file (use --test)', 'info_dict': { 'id': '42', 'ext': 'mp4', 'title': 'Video title goes here', 'thumbnail': 're:^https?://.*\.jpg$', # TODO more properties, either as: # * A value # * MD5 checksum; start the string with md5: # * A regular expression; start the string with re: # * Any Python type (for example int or float) } } def _real_extract(self, url): video_id = self._match_id(url) webpage = self._download_webpage(url, video_id) # TODO more code goes here, for example ... title = self._html_search_regex(r'

(.+?)

', webpage, 'title') return { 'id': video_id, 'title': title, 'description': self._og_search_description(webpage), 'uploader': self._search_regex(r']+id="uploader"[^>]*>([^<]+)<', webpage, 'uploader', fatal=False), # TODO more properties (see youtube_dl/extractor/common.py) } ``` 5. Add an import in [`youtube_dl/extractor/__init__.py`](https://github.com/rg3/youtube-dl/blob/master/youtube_dl/extractor/__init__.py). 6. Run `python test/test_download.py TestDownload.test_YourExtractor`. This *should fail* at first, but you can continually re-run it until you're done. If you decide to add more than one test, then rename ``_TEST`` to ``_TESTS`` and make it into a list of dictionaries. The tests will then be named `TestDownload.test_YourExtractor`, `TestDownload.test_YourExtractor_1`, `TestDownload.test_YourExtractor_2`, etc. 7. Have a look at [`youtube_dl/extractor/common.py`](https://github.com/rg3/youtube-dl/blob/master/youtube_dl/extractor/common.py) for possible helper methods and a [detailed description of what your extractor should and may return](https://github.com/rg3/youtube-dl/blob/master/youtube_dl/extractor/common.py#L62-L200). Add tests and code for as many as you want. 8. If you can, check the code with [flake8](https://pypi.python.org/pypi/flake8). 9. When the tests pass, [add](http://git-scm.com/docs/git-add) the new files and [commit](http://git-scm.com/docs/git-commit) them and [push](http://git-scm.com/docs/git-push) the result, like this: $ git add youtube_dl/extractor/__init__.py $ git add youtube_dl/extractor/yourextractor.py $ git commit -m '[yourextractor] Add new extractor' $ git push origin yourextractor 10. Finally, [create a pull request](https://help.github.com/articles/creating-a-pull-request). We'll then review and merge it. In any case, thank you very much for your contributions! # EMBEDDING YOUTUBE-DL youtube-dl makes the best effort to be a good command-line program, and thus should be callable from any programming language. If you encounter any problems parsing its output, feel free to [create a report](https://github.com/rg3/youtube-dl/issues/new). From a Python program, you can embed youtube-dl in a more powerful fashion, like this: ```python from __future__ import unicode_literals import youtube_dl ydl_opts = {} with youtube_dl.YoutubeDL(ydl_opts) as ydl: ydl.download(['http://www.youtube.com/watch?v=BaW_jenozKc']) ``` Most likely, you'll want to use various options. For a list of what can be done, have a look at [`youtube_dl/YoutubeDL.py`](https://github.com/rg3/youtube-dl/blob/master/youtube_dl/YoutubeDL.py#L121-L269). For a start, if you want to intercept youtube-dl's output, set a `logger` object. Here's a more complete example of a program that outputs only errors (and a short message after the download is finished), and downloads/converts the video to an mp3 file: ```python from __future__ import unicode_literals import youtube_dl class MyLogger(object): def debug(self, msg): pass def warning(self, msg): pass def error(self, msg): print(msg) def my_hook(d): if d['status'] == 'finished': print('Done downloading, now converting ...') ydl_opts = { 'format': 'bestaudio/best', 'postprocessors': [{ 'key': 'FFmpegExtractAudio', 'preferredcodec': 'mp3', 'preferredquality': '192', }], 'logger': MyLogger(), 'progress_hooks': [my_hook], } with youtube_dl.YoutubeDL(ydl_opts) as ydl: ydl.download(['http://www.youtube.com/watch?v=BaW_jenozKc']) ``` # BUGS Bugs and suggestions should be reported at: . Unless you were prompted so or there is another pertinent reason (e.g. GitHub fails to accept the bug report), please do not send bug reports via personal email. For discussions, join us in the IRC channel [#youtube-dl](irc://chat.freenode.net/#youtube-dl) on freenode ([webchat](http://webchat.freenode.net/?randomnick=1&channels=youtube-dl)). **Please include the full output of youtube-dl when run with `-v`**, i.e. add `-v` flag to your command line, copy the **whole** output and post it in the issue body wrapped in \`\`\` for better formatting. It should look similar to this: ``` $ youtube-dl -v http://www.youtube.com/watch?v=BaW_jenozKcj [debug] System config: [] [debug] User config: [] [debug] Command-line args: [u'-v', u'http://www.youtube.com/watch?v=BaW_jenozKcj'] [debug] Encodings: locale cp1251, fs mbcs, out cp866, pref cp1251 [debug] youtube-dl version 2015.12.06 [debug] Git HEAD: 135392e [debug] Python version 2.6.6 - Windows-2003Server-5.2.3790-SP2 [debug] exe versions: ffmpeg N-75573-g1d0487f, ffprobe N-75573-g1d0487f, rtmpdump 2.4 [debug] Proxy map: {} ... ``` **Do not post screenshots of verbose log only plain text is acceptable.** The output (including the first lines) contains important debugging information. Issues without the full output are often not reproducible and therefore do not get solved in short order, if ever. Please re-read your issue once again to avoid a couple of common mistakes (you can and should use this as a checklist): ### Is the description of the issue itself sufficient? We often get issue reports that we cannot really decipher. While in most cases we eventually get the required information after asking back multiple times, this poses an unnecessary drain on our resources. Many contributors, including myself, are also not native speakers, so we may misread some parts. So please elaborate on what feature you are requesting, or what bug you want to be fixed. Make sure that it's obvious - What the problem is - How it could be fixed - How your proposed solution would look like If your report is shorter than two lines, it is almost certainly missing some of these, which makes it hard for us to respond to it. We're often too polite to close the issue outright, but the missing info makes misinterpretation likely. As a commiter myself, I often get frustrated by these issues, since the only possible way for me to move forward on them is to ask for clarification over and over. For bug reports, this means that your report should contain the *complete* output of youtube-dl when called with the `-v` flag. The error message you get for (most) bugs even says so, but you would not believe how many of our bug reports do not contain this information. If your server has multiple IPs or you suspect censorship, adding `--call-home` may be a good idea to get more diagnostics. If the error is `ERROR: Unable to extract ...` and you cannot reproduce it from multiple countries, add `--dump-pages` (warning: this will yield a rather large output, redirect it to the file `log.txt` by adding `>log.txt 2>&1` to your command-line) or upload the `.dump` files you get when you add `--write-pages` [somewhere](https://gist.github.com/). **Site support requests must contain an example URL**. An example URL is a URL you might want to download, like `http://www.youtube.com/watch?v=BaW_jenozKc`. There should be an obvious video present. Except under very special circumstances, the main page of a video service (e.g. `http://www.youtube.com/`) is *not* an example URL. ### Are you using the latest version? Before reporting any issue, type `youtube-dl -U`. This should report that you're up-to-date. About 20% of the reports we receive are already fixed, but people are using outdated versions. This goes for feature requests as well. ### Is the issue already documented? Make sure that someone has not already opened the issue you're trying to open. Search at the top of the window or browse the [GitHub Issues](https://github.com/rg3/youtube-dl/search?type=Issues) of this repository. If there is an issue, feel free to write something along the lines of "This affects me as well, with version 2015.01.01. Here is some more information on the issue: ...". While some issues may be old, a new post into them often spurs rapid activity. ### Why are existing options not enough? Before requesting a new feature, please have a quick peek at [the list of supported options](https://github.com/rg3/youtube-dl/blob/master/README.md#synopsis). Many feature requests are for features that actually exist already! Please, absolutely do show off your work in the issue report and detail how the existing similar options do *not* solve your problem. ### Is there enough context in your bug report? People want to solve problems, and often think they do us a favor by breaking down their larger problems (e.g. wanting to skip already downloaded files) to a specific request (e.g. requesting us to look whether the file exists before downloading the info page). However, what often happens is that they break down the problem into two steps: One simple, and one impossible (or extremely complicated one). We are then presented with a very complicated request when the original problem could be solved far easier, e.g. by recording the downloaded video IDs in a separate file. To avoid this, you must include the greater context where it is non-obvious. In particular, every feature request that does not consist of adding support for a new site should contain a use case scenario that explains in what situation the missing feature would be useful. ### Does the issue involve one problem, and one problem only? Some of our users seem to think there is a limit of issues they can or should open. There is no limit of issues they can or should open. While it may seem appealing to be able to dump all your issues into one ticket, that means that someone who solves one of your issues cannot mark the issue as closed. Typically, reporting a bunch of issues leads to the ticket lingering since nobody wants to attack that behemoth, until someone mercifully splits the issue into multiple ones. In particular, every site support request issue should only pertain to services at one site (generally under a common domain, but always using the same backend technology). Do not request support for vimeo user videos, Whitehouse podcasts, and Google Plus pages in the same issue. Also, make sure that you don't post bug reports alongside feature requests. As a rule of thumb, a feature request does not include outputs of youtube-dl that are not immediately related to the feature at hand. Do not post reports of a network error alongside the request for a new video service. ### Is anyone going to need the feature? Only post features that you (or an incapacitated friend you can personally talk to) require. Do not post features because they seem like a good idea. If they are really useful, they will be requested by someone who requires them. ### Is your question about youtube-dl? It may sound strange, but some bug reports we receive are completely unrelated to youtube-dl and relate to a different or even the reporter's own application. Please make sure that you are actually using youtube-dl. If you are using a UI for youtube-dl, report the bug to the maintainer of the actual application providing the UI. On the other hand, if your UI for youtube-dl fails in some way you believe is related to youtube-dl, by all means, go ahead and report the bug. # COPYRIGHT youtube-dl is released into the public domain by the copyright holders. This README file was originally written by [Daniel Bolton](https://github.com/dbbolton) and is likewise released into the public domain. encuentro-4.0/external/youtube-dl/youtube_dl/0000775000175000017500000000000012642560467022543 5ustar facundofacundo00000000000000encuentro-4.0/external/youtube-dl/youtube_dl/swfinterp.py0000664000175000017500000007527712637746176025167 0ustar facundofacundo00000000000000from __future__ import unicode_literals import collections import io import zlib from .compat import compat_str from .utils import ( ExtractorError, struct_unpack, ) def _extract_tags(file_contents): if file_contents[1:3] != b'WS': raise ExtractorError( 'Not an SWF file; header is %r' % file_contents[:3]) if file_contents[:1] == b'C': content = zlib.decompress(file_contents[8:]) else: raise NotImplementedError( 'Unsupported compression format %r' % file_contents[:1]) # Determine number of bits in framesize rectangle framesize_nbits = struct_unpack('!B', content[:1])[0] >> 3 framesize_len = (5 + 4 * framesize_nbits + 7) // 8 pos = framesize_len + 2 + 2 while pos < len(content): header16 = struct_unpack('> 6 tag_len = header16 & 0x3f if tag_len == 0x3f: tag_len = struct_unpack('= 0x80) else b'\x00' return struct_unpack('= 0 resb = reader.read(count) assert len(resb) == count return resb def _read_byte(reader): resb = _read_bytes(1, reader=reader) res = struct_unpack('> 4 methods = {} constants = None if kind == 0x00: # Slot u30() # Slot id u30() # type_name_idx vindex = u30() if vindex != 0: read_byte() # vkind elif kind == 0x06: # Const u30() # Slot id u30() # type_name_idx vindex = u30() vkind = 'any' if vindex != 0: vkind = read_byte() if vkind == 0x03: # Constant_Int value = self.constant_ints[vindex] elif vkind == 0x04: # Constant_UInt value = self.constant_uints[vindex] else: return {}, None # Ignore silently for now constants = {self.multinames[trait_name_idx]: value} elif kind in (0x01, 0x02, 0x03): # Method / Getter / Setter u30() # disp_id method_idx = u30() methods[self.multinames[trait_name_idx]] = method_idx elif kind == 0x04: # Class u30() # slot_id u30() # classi elif kind == 0x05: # Function u30() # slot_id function_idx = u30() methods[function_idx] = self.multinames[trait_name_idx] else: raise ExtractorError('Unsupported trait kind %d' % kind) if attrs & 0x4 != 0: # Metadata present metadata_count = u30() for _c3 in range(metadata_count): u30() # metadata index return methods, constants # Classes class_count = u30() classes = [] for class_id in range(class_count): name_idx = u30() cname = self.multinames[name_idx] avm_class = _AVMClass(name_idx, cname) classes.append(avm_class) u30() # super_name idx flags = read_byte() if flags & 0x08 != 0: # Protected namespace is present u30() # protected_ns_idx intrf_count = u30() for _c2 in range(intrf_count): u30() u30() # iinit trait_count = u30() for _c2 in range(trait_count): trait_methods, trait_constants = parse_traits_info() avm_class.register_methods(trait_methods) if trait_constants: avm_class.constants.update(trait_constants) assert len(classes) == class_count self._classes_by_name = dict((c.name, c) for c in classes) for avm_class in classes: avm_class.cinit_idx = u30() trait_count = u30() for _c2 in range(trait_count): trait_methods, trait_constants = parse_traits_info() avm_class.register_methods(trait_methods) if trait_constants: avm_class.constants.update(trait_constants) # Scripts script_count = u30() for _c in range(script_count): u30() # init trait_count = u30() for _c2 in range(trait_count): parse_traits_info() # Method bodies method_body_count = u30() Method = collections.namedtuple('Method', ['code', 'local_count']) self._all_methods = [] for _c in range(method_body_count): method_idx = u30() u30() # max_stack local_count = u30() u30() # init_scope_depth u30() # max_scope_depth code_length = u30() code = read_bytes(code_length) m = Method(code, local_count) self._all_methods.append(m) for avm_class in classes: if method_idx in avm_class.method_idxs: avm_class.methods[avm_class.method_idxs[method_idx]] = m exception_count = u30() for _c2 in range(exception_count): u30() # from u30() # to u30() # target u30() # exc_type u30() # var_name trait_count = u30() for _c2 in range(trait_count): parse_traits_info() assert p + code_reader.tell() == len(code_tag) def patch_function(self, avm_class, func_name, f): self._patched_functions[(avm_class, func_name)] = f def extract_class(self, class_name, call_cinit=True): try: res = self._classes_by_name[class_name] except KeyError: raise ExtractorError('Class %r not found' % class_name) if call_cinit and hasattr(res, 'cinit_idx'): res.register_methods({'$cinit': res.cinit_idx}) res.methods['$cinit'] = self._all_methods[res.cinit_idx] cinit = self.extract_function(res, '$cinit') cinit([]) return res def extract_function(self, avm_class, func_name): p = self._patched_functions.get((avm_class, func_name)) if p: return p if func_name in avm_class.method_pyfunctions: return avm_class.method_pyfunctions[func_name] if func_name in self._classes_by_name: return self._classes_by_name[func_name].make_object() if func_name not in avm_class.methods: raise ExtractorError('Cannot find function %s.%s' % ( avm_class.name, func_name)) m = avm_class.methods[func_name] def resfunc(args): # Helper functions coder = io.BytesIO(m.code) s24 = lambda: _s24(coder) u30 = lambda: _u30(coder) registers = [avm_class.variables] + list(args) + [None] * m.local_count stack = [] scopes = collections.deque([ self._classes_by_name, avm_class.constants, avm_class.variables]) while True: opcode = _read_byte(coder) if opcode == 9: # label pass # Spec says: "Do nothing." elif opcode == 16: # jump offset = s24() coder.seek(coder.tell() + offset) elif opcode == 17: # iftrue offset = s24() value = stack.pop() if value: coder.seek(coder.tell() + offset) elif opcode == 18: # iffalse offset = s24() value = stack.pop() if not value: coder.seek(coder.tell() + offset) elif opcode == 19: # ifeq offset = s24() value2 = stack.pop() value1 = stack.pop() if value2 == value1: coder.seek(coder.tell() + offset) elif opcode == 20: # ifne offset = s24() value2 = stack.pop() value1 = stack.pop() if value2 != value1: coder.seek(coder.tell() + offset) elif opcode == 21: # iflt offset = s24() value2 = stack.pop() value1 = stack.pop() if value1 < value2: coder.seek(coder.tell() + offset) elif opcode == 32: # pushnull stack.append(None) elif opcode == 33: # pushundefined stack.append(undefined) elif opcode == 36: # pushbyte v = _read_byte(coder) stack.append(v) elif opcode == 37: # pushshort v = u30() stack.append(v) elif opcode == 38: # pushtrue stack.append(True) elif opcode == 39: # pushfalse stack.append(False) elif opcode == 40: # pushnan stack.append(float('NaN')) elif opcode == 42: # dup value = stack[-1] stack.append(value) elif opcode == 44: # pushstring idx = u30() stack.append(self.constant_strings[idx]) elif opcode == 48: # pushscope new_scope = stack.pop() scopes.append(new_scope) elif opcode == 66: # construct arg_count = u30() args = list(reversed( [stack.pop() for _ in range(arg_count)])) obj = stack.pop() res = obj.avm_class.make_object() stack.append(res) elif opcode == 70: # callproperty index = u30() mname = self.multinames[index] arg_count = u30() args = list(reversed( [stack.pop() for _ in range(arg_count)])) obj = stack.pop() if obj == StringClass: if mname == 'String': assert len(args) == 1 assert isinstance(args[0], ( int, compat_str, _Undefined)) if args[0] == undefined: res = 'undefined' else: res = compat_str(args[0]) stack.append(res) continue else: raise NotImplementedError( 'Function String.%s is not yet implemented' % mname) elif isinstance(obj, _AVMClass_Object): func = self.extract_function(obj.avm_class, mname) res = func(args) stack.append(res) continue elif isinstance(obj, _AVMClass): func = self.extract_function(obj, mname) res = func(args) stack.append(res) continue elif isinstance(obj, _ScopeDict): if mname in obj.avm_class.method_names: func = self.extract_function(obj.avm_class, mname) res = func(args) else: res = obj[mname] stack.append(res) continue elif isinstance(obj, compat_str): if mname == 'split': assert len(args) == 1 assert isinstance(args[0], compat_str) if args[0] == '': res = list(obj) else: res = obj.split(args[0]) stack.append(res) continue elif mname == 'charCodeAt': assert len(args) <= 1 idx = 0 if len(args) == 0 else args[0] assert isinstance(idx, int) res = ord(obj[idx]) stack.append(res) continue elif isinstance(obj, list): if mname == 'slice': assert len(args) == 1 assert isinstance(args[0], int) res = obj[args[0]:] stack.append(res) continue elif mname == 'join': assert len(args) == 1 assert isinstance(args[0], compat_str) res = args[0].join(obj) stack.append(res) continue raise NotImplementedError( 'Unsupported property %r on %r' % (mname, obj)) elif opcode == 71: # returnvoid res = undefined return res elif opcode == 72: # returnvalue res = stack.pop() return res elif opcode == 73: # constructsuper # Not yet implemented, just hope it works without it arg_count = u30() args = list(reversed( [stack.pop() for _ in range(arg_count)])) obj = stack.pop() elif opcode == 74: # constructproperty index = u30() arg_count = u30() args = list(reversed( [stack.pop() for _ in range(arg_count)])) obj = stack.pop() mname = self.multinames[index] assert isinstance(obj, _AVMClass) # We do not actually call the constructor for now; # we just pretend it does nothing stack.append(obj.make_object()) elif opcode == 79: # callpropvoid index = u30() mname = self.multinames[index] arg_count = u30() args = list(reversed( [stack.pop() for _ in range(arg_count)])) obj = stack.pop() if isinstance(obj, _AVMClass_Object): func = self.extract_function(obj.avm_class, mname) res = func(args) assert res is undefined continue if isinstance(obj, _ScopeDict): assert mname in obj.avm_class.method_names func = self.extract_function(obj.avm_class, mname) res = func(args) assert res is undefined continue if mname == 'reverse': assert isinstance(obj, list) obj.reverse() else: raise NotImplementedError( 'Unsupported (void) property %r on %r' % (mname, obj)) elif opcode == 86: # newarray arg_count = u30() arr = [] for i in range(arg_count): arr.append(stack.pop()) arr = arr[::-1] stack.append(arr) elif opcode == 93: # findpropstrict index = u30() mname = self.multinames[index] for s in reversed(scopes): if mname in s: res = s break else: res = scopes[0] if mname not in res and mname in _builtin_classes: stack.append(_builtin_classes[mname]) else: stack.append(res[mname]) elif opcode == 94: # findproperty index = u30() mname = self.multinames[index] for s in reversed(scopes): if mname in s: res = s break else: res = avm_class.variables stack.append(res) elif opcode == 96: # getlex index = u30() mname = self.multinames[index] for s in reversed(scopes): if mname in s: scope = s break else: scope = avm_class.variables if mname in scope: res = scope[mname] elif mname in _builtin_classes: res = _builtin_classes[mname] else: # Assume unitialized # TODO warn here res = undefined stack.append(res) elif opcode == 97: # setproperty index = u30() value = stack.pop() idx = self.multinames[index] if isinstance(idx, _Multiname): idx = stack.pop() obj = stack.pop() obj[idx] = value elif opcode == 98: # getlocal index = u30() stack.append(registers[index]) elif opcode == 99: # setlocal index = u30() value = stack.pop() registers[index] = value elif opcode == 102: # getproperty index = u30() pname = self.multinames[index] if pname == 'length': obj = stack.pop() assert isinstance(obj, (compat_str, list)) stack.append(len(obj)) elif isinstance(pname, compat_str): # Member access obj = stack.pop() if isinstance(obj, _AVMClass): res = obj.static_properties[pname] stack.append(res) continue assert isinstance(obj, (dict, _ScopeDict)),\ 'Accessing member %r on %r' % (pname, obj) res = obj.get(pname, undefined) stack.append(res) else: # Assume attribute access idx = stack.pop() assert isinstance(idx, int) obj = stack.pop() assert isinstance(obj, list) stack.append(obj[idx]) elif opcode == 104: # initproperty index = u30() value = stack.pop() idx = self.multinames[index] if isinstance(idx, _Multiname): idx = stack.pop() obj = stack.pop() obj[idx] = value elif opcode == 115: # convert_ value = stack.pop() intvalue = int(value) stack.append(intvalue) elif opcode == 128: # coerce u30() elif opcode == 130: # coerce_a value = stack.pop() # um, yes, it's any value stack.append(value) elif opcode == 133: # coerce_s assert isinstance(stack[-1], (type(None), compat_str)) elif opcode == 147: # decrement value = stack.pop() assert isinstance(value, int) stack.append(value - 1) elif opcode == 149: # typeof value = stack.pop() return { _Undefined: 'undefined', compat_str: 'String', int: 'Number', float: 'Number', }[type(value)] elif opcode == 160: # add value2 = stack.pop() value1 = stack.pop() res = value1 + value2 stack.append(res) elif opcode == 161: # subtract value2 = stack.pop() value1 = stack.pop() res = value1 - value2 stack.append(res) elif opcode == 162: # multiply value2 = stack.pop() value1 = stack.pop() res = value1 * value2 stack.append(res) elif opcode == 164: # modulo value2 = stack.pop() value1 = stack.pop() res = value1 % value2 stack.append(res) elif opcode == 168: # bitand value2 = stack.pop() value1 = stack.pop() assert isinstance(value1, int) assert isinstance(value2, int) res = value1 & value2 stack.append(res) elif opcode == 171: # equals value2 = stack.pop() value1 = stack.pop() result = value1 == value2 stack.append(result) elif opcode == 175: # greaterequals value2 = stack.pop() value1 = stack.pop() result = value1 >= value2 stack.append(result) elif opcode == 192: # increment_i value = stack.pop() assert isinstance(value, int) stack.append(value + 1) elif opcode == 208: # getlocal_0 stack.append(registers[0]) elif opcode == 209: # getlocal_1 stack.append(registers[1]) elif opcode == 210: # getlocal_2 stack.append(registers[2]) elif opcode == 211: # getlocal_3 stack.append(registers[3]) elif opcode == 212: # setlocal_0 registers[0] = stack.pop() elif opcode == 213: # setlocal_1 registers[1] = stack.pop() elif opcode == 214: # setlocal_2 registers[2] = stack.pop() elif opcode == 215: # setlocal_3 registers[3] = stack.pop() else: raise NotImplementedError( 'Unsupported opcode %d' % opcode) avm_class.method_pyfunctions[func_name] = resfunc return resfunc encuentro-4.0/external/youtube-dl/youtube_dl/utils.py0000664000175000017500000023124712637746176024275 0ustar facundofacundo00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- from __future__ import unicode_literals import base64 import calendar import codecs import contextlib import ctypes import datetime import email.utils import errno import functools import gzip import itertools import io import json import locale import math import operator import os import pipes import platform import re import ssl import socket import struct import subprocess import sys import tempfile import traceback import xml.etree.ElementTree import zlib from .compat import ( compat_basestring, compat_chr, compat_etree_fromstring, compat_html_entities, compat_http_client, compat_kwargs, compat_parse_qs, compat_socket_create_connection, compat_str, compat_urllib_error, compat_urllib_parse, compat_urllib_parse_urlparse, compat_urllib_request, compat_urlparse, shlex_quote, ) # This is not clearly defined otherwise compiled_regex_type = type(re.compile('')) std_headers = { 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:10.0) Gecko/20150101 Firefox/20.0 (Chrome)', 'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Encoding': 'gzip, deflate', 'Accept-Language': 'en-us,en;q=0.5', } NO_DEFAULT = object() ENGLISH_MONTH_NAMES = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] def preferredencoding(): """Get preferred encoding. Returns the best encoding scheme for the system, based on locale.getpreferredencoding() and some further tweaks. """ try: pref = locale.getpreferredencoding() 'TEST'.encode(pref) except Exception: pref = 'UTF-8' return pref def write_json_file(obj, fn): """ Encode obj as JSON and write it to fn, atomically if possible """ fn = encodeFilename(fn) if sys.version_info < (3, 0) and sys.platform != 'win32': encoding = get_filesystem_encoding() # os.path.basename returns a bytes object, but NamedTemporaryFile # will fail if the filename contains non ascii characters unless we # use a unicode object path_basename = lambda f: os.path.basename(fn).decode(encoding) # the same for os.path.dirname path_dirname = lambda f: os.path.dirname(fn).decode(encoding) else: path_basename = os.path.basename path_dirname = os.path.dirname args = { 'suffix': '.tmp', 'prefix': path_basename(fn) + '.', 'dir': path_dirname(fn), 'delete': False, } # In Python 2.x, json.dump expects a bytestream. # In Python 3.x, it writes to a character stream if sys.version_info < (3, 0): args['mode'] = 'wb' else: args.update({ 'mode': 'w', 'encoding': 'utf-8', }) tf = tempfile.NamedTemporaryFile(**compat_kwargs(args)) try: with tf: json.dump(obj, tf) if sys.platform == 'win32': # Need to remove existing file on Windows, else os.rename raises # WindowsError or FileExistsError. try: os.unlink(fn) except OSError: pass os.rename(tf.name, fn) except Exception: try: os.remove(tf.name) except OSError: pass raise if sys.version_info >= (2, 7): def find_xpath_attr(node, xpath, key, val=None): """ Find the xpath xpath[@key=val] """ assert re.match(r'^[a-zA-Z_-]+$', key) if val: assert re.match(r'^[a-zA-Z0-9@\s:._-]*$', val) expr = xpath + ('[@%s]' % key if val is None else "[@%s='%s']" % (key, val)) return node.find(expr) else: def find_xpath_attr(node, xpath, key, val=None): # Here comes the crazy part: In 2.6, if the xpath is a unicode, # .//node does not match if a node is a direct child of . ! if isinstance(xpath, compat_str): xpath = xpath.encode('ascii') for f in node.findall(xpath): if key not in f.attrib: continue if val is None or f.attrib.get(key) == val: return f return None # On python2.6 the xml.etree.ElementTree.Element methods don't support # the namespace parameter def xpath_with_ns(path, ns_map): components = [c.split(':') for c in path.split('/')] replaced = [] for c in components: if len(c) == 1: replaced.append(c[0]) else: ns, tag = c replaced.append('{%s}%s' % (ns_map[ns], tag)) return '/'.join(replaced) def xpath_element(node, xpath, name=None, fatal=False, default=NO_DEFAULT): def _find_xpath(xpath): if sys.version_info < (2, 7): # Crazy 2.6 xpath = xpath.encode('ascii') return node.find(xpath) if isinstance(xpath, (str, compat_str)): n = _find_xpath(xpath) else: for xp in xpath: n = _find_xpath(xp) if n is not None: break if n is None: if default is not NO_DEFAULT: return default elif fatal: name = xpath if name is None else name raise ExtractorError('Could not find XML element %s' % name) else: return None return n def xpath_text(node, xpath, name=None, fatal=False, default=NO_DEFAULT): n = xpath_element(node, xpath, name, fatal=fatal, default=default) if n is None or n == default: return n if n.text is None: if default is not NO_DEFAULT: return default elif fatal: name = xpath if name is None else name raise ExtractorError('Could not find XML element\'s text %s' % name) else: return None return n.text def xpath_attr(node, xpath, key, name=None, fatal=False, default=NO_DEFAULT): n = find_xpath_attr(node, xpath, key) if n is None: if default is not NO_DEFAULT: return default elif fatal: name = '%s[@%s]' % (xpath, key) if name is None else name raise ExtractorError('Could not find XML attribute %s' % name) else: return None return n.attrib[key] def get_element_by_id(id, html): """Return the content of the tag with the specified ID in the passed HTML document""" return get_element_by_attribute("id", id, html) def get_element_by_attribute(attribute, value, html): """Return the content of the tag with the specified attribute in the passed HTML document""" m = re.search(r'''(?xs) <([a-zA-Z0-9:._-]+) (?:\s+[a-zA-Z0-9:._-]+(?:=[a-zA-Z0-9:._-]+|="[^"]+"|='[^']+'))*? \s+%s=['"]?%s['"]? (?:\s+[a-zA-Z0-9:._-]+(?:=[a-zA-Z0-9:._-]+|="[^"]+"|='[^']+'))*? \s*> (?P.*?) ''' % (re.escape(attribute), re.escape(value)), html) if not m: return None res = m.group('content') if res.startswith('"') or res.startswith("'"): res = res[1:-1] return unescapeHTML(res) def clean_html(html): """Clean an HTML snippet into a readable string""" if html is None: # Convenience for sanitizing descriptions etc. return html # Newline vs
html = html.replace('\n', ' ') html = re.sub(r'\s*<\s*br\s*/?\s*>\s*', '\n', html) html = re.sub(r'<\s*/\s*p\s*>\s*<\s*p[^>]*>', '\n', html) # Strip html tags html = re.sub('<.*?>', '', html) # Replace html entities html = unescapeHTML(html) return html.strip() def sanitize_open(filename, open_mode): """Try to open the given filename, and slightly tweak it if this fails. Attempts to open the given filename. If this fails, it tries to change the filename slightly, step by step, until it's either able to open it or it fails and raises a final exception, like the standard open() function. It returns the tuple (stream, definitive_file_name). """ try: if filename == '-': if sys.platform == 'win32': import msvcrt msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) return (sys.stdout.buffer if hasattr(sys.stdout, 'buffer') else sys.stdout, filename) stream = open(encodeFilename(filename), open_mode) return (stream, filename) except (IOError, OSError) as err: if err.errno in (errno.EACCES,): raise # In case of error, try to remove win32 forbidden chars alt_filename = sanitize_path(filename) if alt_filename == filename: raise else: # An exception here should be caught in the caller stream = open(encodeFilename(alt_filename), open_mode) return (stream, alt_filename) def timeconvert(timestr): """Convert RFC 2822 defined time string into system timestamp""" timestamp = None timetuple = email.utils.parsedate_tz(timestr) if timetuple is not None: timestamp = email.utils.mktime_tz(timetuple) return timestamp def sanitize_filename(s, restricted=False, is_id=False): """Sanitizes a string so it could be used as part of a filename. If restricted is set, use a stricter subset of allowed characters. Set is_id if this is not an arbitrary string, but an ID that should be kept if possible """ def replace_insane(char): if char == '?' or ord(char) < 32 or ord(char) == 127: return '' elif char == '"': return '' if restricted else '\'' elif char == ':': return '_-' if restricted else ' -' elif char in '\\/|*<>': return '_' if restricted and (char in '!&\'()[]{}$;`^,#' or char.isspace()): return '_' if restricted and ord(char) > 127: return '_' return char # Handle timestamps s = re.sub(r'[0-9]+(?::[0-9]+)+', lambda m: m.group(0).replace(':', '_'), s) result = ''.join(map(replace_insane, s)) if not is_id: while '__' in result: result = result.replace('__', '_') result = result.strip('_') # Common case of "Foreign band name - English song title" if restricted and result.startswith('-_'): result = result[2:] if result.startswith('-'): result = '_' + result[len('-'):] result = result.lstrip('.') if not result: result = '_' return result def sanitize_path(s): """Sanitizes and normalizes path on Windows""" if sys.platform != 'win32': return s drive_or_unc, _ = os.path.splitdrive(s) if sys.version_info < (2, 7) and not drive_or_unc: drive_or_unc, _ = os.path.splitunc(s) norm_path = os.path.normpath(remove_start(s, drive_or_unc)).split(os.path.sep) if drive_or_unc: norm_path.pop(0) sanitized_path = [ path_part if path_part in ['.', '..'] else re.sub('(?:[/<>:"\\|\\\\?\\*]|[\s.]$)', '#', path_part) for path_part in norm_path] if drive_or_unc: sanitized_path.insert(0, drive_or_unc + os.path.sep) return os.path.join(*sanitized_path) # Prepend protocol-less URLs with `http:` scheme in order to mitigate the number of # unwanted failures due to missing protocol def sanitized_Request(url, *args, **kwargs): return compat_urllib_request.Request( 'http:%s' % url if url.startswith('//') else url, *args, **kwargs) def orderedSet(iterable): """ Remove all duplicates from the input iterable """ res = [] for el in iterable: if el not in res: res.append(el) return res def _htmlentity_transform(entity): """Transforms an HTML entity to a character.""" # Known non-numeric HTML entity if entity in compat_html_entities.name2codepoint: return compat_chr(compat_html_entities.name2codepoint[entity]) mobj = re.match(r'#(x[0-9a-fA-F]+|[0-9]+)', entity) if mobj is not None: numstr = mobj.group(1) if numstr.startswith('x'): base = 16 numstr = '0%s' % numstr else: base = 10 # See https://github.com/rg3/youtube-dl/issues/7518 try: return compat_chr(int(numstr, base)) except ValueError: pass # Unknown entity in name, return its literal representation return '&%s;' % entity def unescapeHTML(s): if s is None: return None assert type(s) == compat_str return re.sub( r'&([^;]+);', lambda m: _htmlentity_transform(m.group(1)), s) def get_subprocess_encoding(): if sys.platform == 'win32' and sys.getwindowsversion()[0] >= 5: # For subprocess calls, encode with locale encoding # Refer to http://stackoverflow.com/a/9951851/35070 encoding = preferredencoding() else: encoding = sys.getfilesystemencoding() if encoding is None: encoding = 'utf-8' return encoding def encodeFilename(s, for_subprocess=False): """ @param s The name of the file """ assert type(s) == compat_str # Python 3 has a Unicode API if sys.version_info >= (3, 0): return s # Pass '' directly to use Unicode APIs on Windows 2000 and up # (Detecting Windows NT 4 is tricky because 'major >= 4' would # match Windows 9x series as well. Besides, NT 4 is obsolete.) if not for_subprocess and sys.platform == 'win32' and sys.getwindowsversion()[0] >= 5: return s return s.encode(get_subprocess_encoding(), 'ignore') def decodeFilename(b, for_subprocess=False): if sys.version_info >= (3, 0): return b if not isinstance(b, bytes): return b return b.decode(get_subprocess_encoding(), 'ignore') def encodeArgument(s): if not isinstance(s, compat_str): # Legacy code that uses byte strings # Uncomment the following line after fixing all post processors # assert False, 'Internal error: %r should be of type %r, is %r' % (s, compat_str, type(s)) s = s.decode('ascii') return encodeFilename(s, True) def decodeArgument(b): return decodeFilename(b, True) def decodeOption(optval): if optval is None: return optval if isinstance(optval, bytes): optval = optval.decode(preferredencoding()) assert isinstance(optval, compat_str) return optval def formatSeconds(secs): if secs > 3600: return '%d:%02d:%02d' % (secs // 3600, (secs % 3600) // 60, secs % 60) elif secs > 60: return '%d:%02d' % (secs // 60, secs % 60) else: return '%d' % secs def make_HTTPS_handler(params, **kwargs): opts_no_check_certificate = params.get('nocheckcertificate', False) if hasattr(ssl, 'create_default_context'): # Python >= 3.4 or 2.7.9 context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH) if opts_no_check_certificate: context.check_hostname = False context.verify_mode = ssl.CERT_NONE try: return YoutubeDLHTTPSHandler(params, context=context, **kwargs) except TypeError: # Python 2.7.8 # (create_default_context present but HTTPSHandler has no context=) pass if sys.version_info < (3, 2): return YoutubeDLHTTPSHandler(params, **kwargs) else: # Python < 3.4 context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) context.verify_mode = (ssl.CERT_NONE if opts_no_check_certificate else ssl.CERT_REQUIRED) context.set_default_verify_paths() return YoutubeDLHTTPSHandler(params, context=context, **kwargs) def bug_reports_message(): if ytdl_is_updateable(): update_cmd = 'type youtube-dl -U to update' else: update_cmd = 'see https://yt-dl.org/update on how to update' msg = '; please report this issue on https://yt-dl.org/bug .' msg += ' Make sure you are using the latest version; %s.' % update_cmd msg += ' Be sure to call youtube-dl with the --verbose flag and include its complete output.' return msg class ExtractorError(Exception): """Error during info extraction.""" def __init__(self, msg, tb=None, expected=False, cause=None, video_id=None): """ tb, if given, is the original traceback (so that it can be printed out). If expected is set, this is a normal error message and most likely not a bug in youtube-dl. """ if sys.exc_info()[0] in (compat_urllib_error.URLError, socket.timeout, UnavailableVideoError): expected = True if video_id is not None: msg = video_id + ': ' + msg if cause: msg += ' (caused by %r)' % cause if not expected: msg += bug_reports_message() super(ExtractorError, self).__init__(msg) self.traceback = tb self.exc_info = sys.exc_info() # preserve original exception self.cause = cause self.video_id = video_id def format_traceback(self): if self.traceback is None: return None return ''.join(traceback.format_tb(self.traceback)) class UnsupportedError(ExtractorError): def __init__(self, url): super(UnsupportedError, self).__init__( 'Unsupported URL: %s' % url, expected=True) self.url = url class RegexNotFoundError(ExtractorError): """Error when a regex didn't match""" pass class DownloadError(Exception): """Download Error exception. This exception may be thrown by FileDownloader objects if they are not configured to continue on errors. They will contain the appropriate error message. """ def __init__(self, msg, exc_info=None): """ exc_info, if given, is the original exception that caused the trouble (as returned by sys.exc_info()). """ super(DownloadError, self).__init__(msg) self.exc_info = exc_info class SameFileError(Exception): """Same File exception. This exception will be thrown by FileDownloader objects if they detect multiple files would have to be downloaded to the same file on disk. """ pass class PostProcessingError(Exception): """Post Processing exception. This exception may be raised by PostProcessor's .run() method to indicate an error in the postprocessing task. """ def __init__(self, msg): self.msg = msg class MaxDownloadsReached(Exception): """ --max-downloads limit has been reached. """ pass class UnavailableVideoError(Exception): """Unavailable Format exception. This exception will be thrown when a video is requested in a format that is not available for that video. """ pass class ContentTooShortError(Exception): """Content Too Short exception. This exception may be raised by FileDownloader objects when a file they download is too small for what the server announced first, indicating the connection was probably interrupted. """ def __init__(self, downloaded, expected): # Both in bytes self.downloaded = downloaded self.expected = expected def _create_http_connection(ydl_handler, http_class, is_https, *args, **kwargs): # Working around python 2 bug (see http://bugs.python.org/issue17849) by limiting # expected HTTP responses to meet HTTP/1.0 or later (see also # https://github.com/rg3/youtube-dl/issues/6727) if sys.version_info < (3, 0): kwargs[b'strict'] = True hc = http_class(*args, **kwargs) source_address = ydl_handler._params.get('source_address') if source_address is not None: sa = (source_address, 0) if hasattr(hc, 'source_address'): # Python 2.7+ hc.source_address = sa else: # Python 2.6 def _hc_connect(self, *args, **kwargs): sock = compat_socket_create_connection( (self.host, self.port), self.timeout, sa) if is_https: self.sock = ssl.wrap_socket( sock, self.key_file, self.cert_file, ssl_version=ssl.PROTOCOL_TLSv1) else: self.sock = sock hc.connect = functools.partial(_hc_connect, hc) return hc def handle_youtubedl_headers(headers): filtered_headers = headers if 'Youtubedl-no-compression' in filtered_headers: filtered_headers = dict((k, v) for k, v in filtered_headers.items() if k.lower() != 'accept-encoding') del filtered_headers['Youtubedl-no-compression'] return filtered_headers class YoutubeDLHandler(compat_urllib_request.HTTPHandler): """Handler for HTTP requests and responses. This class, when installed with an OpenerDirector, automatically adds the standard headers to every HTTP request and handles gzipped and deflated responses from web servers. If compression is to be avoided in a particular request, the original request in the program code only has to include the HTTP header "Youtubedl-no-compression", which will be removed before making the real request. Part of this code was copied from: http://techknack.net/python-urllib2-handlers/ Andrew Rowls, the author of that code, agreed to release it to the public domain. """ def __init__(self, params, *args, **kwargs): compat_urllib_request.HTTPHandler.__init__(self, *args, **kwargs) self._params = params def http_open(self, req): return self.do_open(functools.partial( _create_http_connection, self, compat_http_client.HTTPConnection, False), req) @staticmethod def deflate(data): try: return zlib.decompress(data, -zlib.MAX_WBITS) except zlib.error: return zlib.decompress(data) @staticmethod def addinfourl_wrapper(stream, headers, url, code): if hasattr(compat_urllib_request.addinfourl, 'getcode'): return compat_urllib_request.addinfourl(stream, headers, url, code) ret = compat_urllib_request.addinfourl(stream, headers, url) ret.code = code return ret def http_request(self, req): # According to RFC 3986, URLs can not contain non-ASCII characters, however this is not # always respected by websites, some tend to give out URLs with non percent-encoded # non-ASCII characters (see telemb.py, ard.py [#3412]) # urllib chokes on URLs with non-ASCII characters (see http://bugs.python.org/issue3991) # To work around aforementioned issue we will replace request's original URL with # percent-encoded one # Since redirects are also affected (e.g. http://www.southpark.de/alle-episoden/s18e09) # the code of this workaround has been moved here from YoutubeDL.urlopen() url = req.get_full_url() url_escaped = escape_url(url) # Substitute URL if any change after escaping if url != url_escaped: req_type = HEADRequest if req.get_method() == 'HEAD' else compat_urllib_request.Request new_req = req_type( url_escaped, data=req.data, headers=req.headers, origin_req_host=req.origin_req_host, unverifiable=req.unverifiable) new_req.timeout = req.timeout req = new_req for h, v in std_headers.items(): # Capitalize is needed because of Python bug 2275: http://bugs.python.org/issue2275 # The dict keys are capitalized because of this bug by urllib if h.capitalize() not in req.headers: req.add_header(h, v) req.headers = handle_youtubedl_headers(req.headers) if sys.version_info < (2, 7) and '#' in req.get_full_url(): # Python 2.6 is brain-dead when it comes to fragments req._Request__original = req._Request__original.partition('#')[0] req._Request__r_type = req._Request__r_type.partition('#')[0] return req def http_response(self, req, resp): old_resp = resp # gzip if resp.headers.get('Content-encoding', '') == 'gzip': content = resp.read() gz = gzip.GzipFile(fileobj=io.BytesIO(content), mode='rb') try: uncompressed = io.BytesIO(gz.read()) except IOError as original_ioerror: # There may be junk add the end of the file # See http://stackoverflow.com/q/4928560/35070 for details for i in range(1, 1024): try: gz = gzip.GzipFile(fileobj=io.BytesIO(content[:-i]), mode='rb') uncompressed = io.BytesIO(gz.read()) except IOError: continue break else: raise original_ioerror resp = self.addinfourl_wrapper(uncompressed, old_resp.headers, old_resp.url, old_resp.code) resp.msg = old_resp.msg # deflate if resp.headers.get('Content-encoding', '') == 'deflate': gz = io.BytesIO(self.deflate(resp.read())) resp = self.addinfourl_wrapper(gz, old_resp.headers, old_resp.url, old_resp.code) resp.msg = old_resp.msg # Percent-encode redirect URL of Location HTTP header to satisfy RFC 3986 (see # https://github.com/rg3/youtube-dl/issues/6457). if 300 <= resp.code < 400: location = resp.headers.get('Location') if location: # As of RFC 2616 default charset is iso-8859-1 that is respected by python 3 if sys.version_info >= (3, 0): location = location.encode('iso-8859-1').decode('utf-8') location_escaped = escape_url(location) if location != location_escaped: del resp.headers['Location'] resp.headers['Location'] = location_escaped return resp https_request = http_request https_response = http_response class YoutubeDLHTTPSHandler(compat_urllib_request.HTTPSHandler): def __init__(self, params, https_conn_class=None, *args, **kwargs): compat_urllib_request.HTTPSHandler.__init__(self, *args, **kwargs) self._https_conn_class = https_conn_class or compat_http_client.HTTPSConnection self._params = params def https_open(self, req): kwargs = {} if hasattr(self, '_context'): # python > 2.6 kwargs['context'] = self._context if hasattr(self, '_check_hostname'): # python 3.x kwargs['check_hostname'] = self._check_hostname return self.do_open(functools.partial( _create_http_connection, self, self._https_conn_class, True), req, **kwargs) class YoutubeDLCookieProcessor(compat_urllib_request.HTTPCookieProcessor): def __init__(self, cookiejar=None): compat_urllib_request.HTTPCookieProcessor.__init__(self, cookiejar) def http_response(self, request, response): # Python 2 will choke on next HTTP request in row if there are non-ASCII # characters in Set-Cookie HTTP header of last response (see # https://github.com/rg3/youtube-dl/issues/6769). # In order to at least prevent crashing we will percent encode Set-Cookie # header before HTTPCookieProcessor starts processing it. # if sys.version_info < (3, 0) and response.headers: # for set_cookie_header in ('Set-Cookie', 'Set-Cookie2'): # set_cookie = response.headers.get(set_cookie_header) # if set_cookie: # set_cookie_escaped = compat_urllib_parse.quote(set_cookie, b"%/;:@&=+$,!~*'()?#[] ") # if set_cookie != set_cookie_escaped: # del response.headers[set_cookie_header] # response.headers[set_cookie_header] = set_cookie_escaped return compat_urllib_request.HTTPCookieProcessor.http_response(self, request, response) https_request = compat_urllib_request.HTTPCookieProcessor.http_request https_response = http_response def parse_iso8601(date_str, delimiter='T', timezone=None): """ Return a UNIX timestamp from the given date """ if date_str is None: return None date_str = re.sub(r'\.[0-9]+', '', date_str) if timezone is None: m = re.search( r'(?:Z$| ?(?P\+|-)(?P[0-9]{2}):?(?P[0-9]{2})$)', date_str) if not m: timezone = datetime.timedelta() else: date_str = date_str[:-len(m.group(0))] if not m.group('sign'): timezone = datetime.timedelta() else: sign = 1 if m.group('sign') == '+' else -1 timezone = datetime.timedelta( hours=sign * int(m.group('hours')), minutes=sign * int(m.group('minutes'))) try: date_format = '%Y-%m-%d{0}%H:%M:%S'.format(delimiter) dt = datetime.datetime.strptime(date_str, date_format) - timezone return calendar.timegm(dt.timetuple()) except ValueError: pass def unified_strdate(date_str, day_first=True): """Return a string with the date in the format YYYYMMDD""" if date_str is None: return None upload_date = None # Replace commas date_str = date_str.replace(',', ' ') # %z (UTC offset) is only supported in python>=3.2 if not re.match(r'^[0-9]{1,2}-[0-9]{1,2}-[0-9]{4}$', date_str): date_str = re.sub(r' ?(\+|-)[0-9]{2}:?[0-9]{2}$', '', date_str) # Remove AM/PM + timezone date_str = re.sub(r'(?i)\s*(?:AM|PM)(?:\s+[A-Z]+)?', '', date_str) format_expressions = [ '%d %B %Y', '%d %b %Y', '%B %d %Y', '%b %d %Y', '%b %dst %Y %I:%M%p', '%b %dnd %Y %I:%M%p', '%b %dth %Y %I:%M%p', '%Y %m %d', '%Y-%m-%d', '%Y/%m/%d', '%Y/%m/%d %H:%M:%S', '%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M:%S.%f', '%d.%m.%Y %H:%M', '%d.%m.%Y %H.%M', '%Y-%m-%dT%H:%M:%SZ', '%Y-%m-%dT%H:%M:%S.%fZ', '%Y-%m-%dT%H:%M:%S.%f0Z', '%Y-%m-%dT%H:%M:%S', '%Y-%m-%dT%H:%M:%S.%f', '%Y-%m-%dT%H:%M', ] if day_first: format_expressions.extend([ '%d-%m-%Y', '%d.%m.%Y', '%d/%m/%Y', '%d/%m/%y', '%d/%m/%Y %H:%M:%S', ]) else: format_expressions.extend([ '%m-%d-%Y', '%m.%d.%Y', '%m/%d/%Y', '%m/%d/%y', '%m/%d/%Y %H:%M:%S', ]) for expression in format_expressions: try: upload_date = datetime.datetime.strptime(date_str, expression).strftime('%Y%m%d') except ValueError: pass if upload_date is None: timetuple = email.utils.parsedate_tz(date_str) if timetuple: upload_date = datetime.datetime(*timetuple[:6]).strftime('%Y%m%d') if upload_date is not None: return compat_str(upload_date) def determine_ext(url, default_ext='unknown_video'): if url is None: return default_ext guess = url.partition('?')[0].rpartition('.')[2] if re.match(r'^[A-Za-z0-9]+$', guess): return guess elif guess.rstrip('/') in ( 'mp4', 'm4a', 'm4p', 'm4b', 'm4r', 'm4v', 'aac', 'flv', 'f4v', 'f4a', 'f4b', 'webm', 'ogg', 'ogv', 'oga', 'ogx', 'spx', 'opus', 'mkv', 'mka', 'mk3d', 'avi', 'divx', 'mov', 'asf', 'wmv', 'wma', '3gp', '3g2', 'mp3', 'flac', 'ape', 'wav', 'f4f', 'f4m', 'm3u8', 'smil'): return guess.rstrip('/') else: return default_ext def subtitles_filename(filename, sub_lang, sub_format): return filename.rsplit('.', 1)[0] + '.' + sub_lang + '.' + sub_format def date_from_str(date_str): """ Return a datetime object from a string in the format YYYYMMDD or (now|today)[+-][0-9](day|week|month|year)(s)?""" today = datetime.date.today() if date_str in ('now', 'today'): return today if date_str == 'yesterday': return today - datetime.timedelta(days=1) match = re.match('(now|today)(?P[+-])(?P