Merge branch 'master' of https://github.com/rg3/youtube-dl
Conflicts: .gitignore LATEST_VERSION Makefile youtube-dl youtube-dl.exe youtube_dl/InfoExtractors.py youtube_dl/__init__.pymaster
commit
258d5850c9
43 changed files with 6953 additions and 5483 deletions
@ -1,17 +1,19 @@ |
|||||||
*.pyc |
*.pyc |
||||||
*.pyo |
*.pyo |
||||||
*~ |
*~ |
||||||
|
*.DS_Store |
||||||
wine-py2exe/ |
wine-py2exe/ |
||||||
py2exe.log |
py2exe.log |
||||||
youtube-dl |
*.kate-swp |
||||||
|
build/ |
||||||
|
dist/ |
||||||
|
MANIFEST |
||||||
|
README.txt |
||||||
youtube-dl.1 |
youtube-dl.1 |
||||||
LATEST_VERSION |
youtube-dl.bash-completion |
||||||
|
youtube-dl |
||||||
#OS X |
youtube-dl.exe |
||||||
.DS_Store |
youtube-dl.tar.gz |
||||||
.AppleDouble |
.coverage |
||||||
.LSOverride |
cover/ |
||||||
Icon |
updates_key.pem |
||||||
._* |
|
||||||
.Spotlight-V100 |
|
||||||
.Trashes |
|
||||||
|
@ -0,0 +1,17 @@ |
|||||||
|
updates_key.pem |
||||||
|
*.pyc |
||||||
|
*.pyo |
||||||
|
youtube-dl.exe |
||||||
|
wine-py2exe/ |
||||||
|
py2exe.log |
||||||
|
*.kate-swp |
||||||
|
build/ |
||||||
|
dist/ |
||||||
|
MANIFEST |
||||||
|
*.DS_Store |
||||||
|
youtube-dl.tar.gz |
||||||
|
.coverage |
||||||
|
cover/ |
||||||
|
__pycache__/ |
||||||
|
.git/ |
||||||
|
*~ |
@ -1,9 +1,14 @@ |
|||||||
language: python |
language: python |
||||||
#specify the python version |
|
||||||
python: |
python: |
||||||
- "2.6" |
- "2.6" |
||||||
- "2.7" |
- "2.7" |
||||||
#command to install the setup |
- "3.3" |
||||||
install: |
script: nosetests test --verbose |
||||||
# command to run tests |
notifications: |
||||||
script: nosetests test --nocapture |
email: |
||||||
|
- filippo.valsorda@gmail.com |
||||||
|
- phihag@phihag.de |
||||||
|
irc: |
||||||
|
channels: |
||||||
|
- "irc.freenode.org#youtube-dl" |
||||||
|
skip_join: true |
||||||
|
@ -0,0 +1,14 @@ |
|||||||
|
2013.01.02 Codename: GIULIA |
||||||
|
|
||||||
|
* Add support for ComedyCentral clips <nto> |
||||||
|
* Corrected Vimeo description fetching <Nick Daniels> |
||||||
|
* Added the --no-post-overwrites argument <Barbu Paul - Gheorghe> |
||||||
|
* --verbose offers more environment info |
||||||
|
* New info_dict field: uploader_id |
||||||
|
* New updates system, with signature checking |
||||||
|
* New IEs: NBA, JustinTV, FunnyOrDie, TweetReel, Steam, Ustream |
||||||
|
* Fixed IEs: BlipTv |
||||||
|
* Fixed for Python 3 IEs: Xvideo, Youku, XNXX, Dailymotion, Vimeo, InfoQ |
||||||
|
* Simplified IEs and test code |
||||||
|
* Various (Python 3 and other) fixes |
||||||
|
* Revamped and expanded tests |
@ -0,0 +1 @@ |
|||||||
|
2012.10.09 |
@ -0,0 +1,24 @@ |
|||||||
|
This is free and unencumbered software released into the public domain. |
||||||
|
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or |
||||||
|
distribute this software, either in source code form or as a compiled |
||||||
|
binary, for any purpose, commercial or non-commercial, and by any |
||||||
|
means. |
||||||
|
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors |
||||||
|
of this software dedicate any and all copyright interest in the |
||||||
|
software to the public domain. We make this dedication for the benefit |
||||||
|
of the public at large and to the detriment of our heirs and |
||||||
|
successors. We intend this dedication to be an overt act of |
||||||
|
relinquishment in perpetuity of all present and future rights to this |
||||||
|
software under copyright law. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
||||||
|
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR |
||||||
|
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
||||||
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
||||||
|
OTHER DEALINGS IN THE SOFTWARE. |
||||||
|
|
||||||
|
For more information, please refer to <http://unlicense.org/> |
@ -0,0 +1,3 @@ |
|||||||
|
include README.md |
||||||
|
include test/*.py |
||||||
|
include test/*.json |
@ -0,0 +1,6 @@ |
|||||||
|
#!/usr/bin/env python |
||||||
|
|
||||||
|
import youtube_dl |
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
youtube_dl.main() |
@ -1,48 +0,0 @@ |
|||||||
from distutils.core import setup |
|
||||||
import py2exe |
|
||||||
import sys, os |
|
||||||
|
|
||||||
"""This will create an exe that needs Microsoft Visual C++ 2008 Redistributable Package""" |
|
||||||
|
|
||||||
# If run without args, build executables |
|
||||||
if len(sys.argv) == 1: |
|
||||||
sys.argv.append("py2exe") |
|
||||||
|
|
||||||
# os.chdir(os.path.dirname(os.path.abspath(sys.argv[0]))) # conflict with wine-py2exe.sh |
|
||||||
sys.path.append('./youtube_dl') |
|
||||||
|
|
||||||
options = { |
|
||||||
"bundle_files": 1, |
|
||||||
"compressed": 1, |
|
||||||
"optimize": 2, |
|
||||||
"dist_dir": '.', |
|
||||||
"dll_excludes": ['w9xpopen.exe'] |
|
||||||
} |
|
||||||
|
|
||||||
console = [{ |
|
||||||
"script":"./youtube_dl/__main__.py", |
|
||||||
"dest_base": "youtube-dl", |
|
||||||
}] |
|
||||||
|
|
||||||
init_file = open('./youtube_dl/__init__.py') |
|
||||||
for line in init_file.readlines(): |
|
||||||
if line.startswith('__version__'): |
|
||||||
version = line[11:].strip(" ='\n") |
|
||||||
break |
|
||||||
else: |
|
||||||
version = '' |
|
||||||
|
|
||||||
setup(name='youtube-dl', |
|
||||||
version=version, |
|
||||||
description='Small command-line program to download videos from YouTube.com and other video sites', |
|
||||||
url='https://github.com/rg3/youtube-dl', |
|
||||||
packages=['youtube_dl'], |
|
||||||
|
|
||||||
console = console, |
|
||||||
options = {"py2exe": options}, |
|
||||||
zipfile = None, |
|
||||||
) |
|
||||||
|
|
||||||
import shutil |
|
||||||
shutil.rmtree("build") |
|
||||||
|
|
@ -0,0 +1,14 @@ |
|||||||
|
__youtube-dl() |
||||||
|
{ |
||||||
|
local cur prev opts |
||||||
|
COMPREPLY=() |
||||||
|
cur="${COMP_WORDS[COMP_CWORD]}" |
||||||
|
opts="{{flags}}" |
||||||
|
|
||||||
|
if [[ ${cur} == * ]] ; then |
||||||
|
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) |
||||||
|
return 0 |
||||||
|
fi |
||||||
|
} |
||||||
|
|
||||||
|
complete -F __youtube-dl youtube-dl |
@ -0,0 +1,26 @@ |
|||||||
|
#!/usr/bin/env python |
||||||
|
import os |
||||||
|
from os.path import dirname as dirn |
||||||
|
import sys |
||||||
|
|
||||||
|
sys.path.append(dirn(dirn((os.path.abspath(__file__))))) |
||||||
|
import youtube_dl |
||||||
|
|
||||||
|
BASH_COMPLETION_FILE = "youtube-dl.bash-completion" |
||||||
|
BASH_COMPLETION_TEMPLATE = "devscripts/bash-completion.in" |
||||||
|
|
||||||
|
def build_completion(opt_parser): |
||||||
|
opts_flag = [] |
||||||
|
for group in opt_parser.option_groups: |
||||||
|
for option in group.option_list: |
||||||
|
#for every long flag |
||||||
|
opts_flag.append(option.get_opt_string()) |
||||||
|
with open(BASH_COMPLETION_TEMPLATE) as f: |
||||||
|
template = f.read() |
||||||
|
with open(BASH_COMPLETION_FILE, "w") as f: |
||||||
|
#just using the special char |
||||||
|
filled_template = template.replace("{{flags}}", " ".join(opts_flag)) |
||||||
|
f.write(filled_template) |
||||||
|
|
||||||
|
parser = youtube_dl.parseOpts()[0] |
||||||
|
build_completion(parser) |
@ -0,0 +1,33 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
|
||||||
|
import json |
||||||
|
import sys |
||||||
|
import hashlib |
||||||
|
import urllib.request |
||||||
|
|
||||||
|
if len(sys.argv) <= 1: |
||||||
|
print('Specify the version number as parameter') |
||||||
|
sys.exit() |
||||||
|
version = sys.argv[1] |
||||||
|
|
||||||
|
with open('update/LATEST_VERSION', 'w') as f: |
||||||
|
f.write(version) |
||||||
|
|
||||||
|
versions_info = json.load(open('update/versions.json')) |
||||||
|
if 'signature' in versions_info: |
||||||
|
del versions_info['signature'] |
||||||
|
|
||||||
|
new_version = {} |
||||||
|
|
||||||
|
filenames = {'bin': 'youtube-dl', 'exe': 'youtube-dl.exe', 'tar': 'youtube-dl-%s.tar.gz' % version} |
||||||
|
for key, filename in filenames.items(): |
||||||
|
print('Downloading and checksumming %s...' %filename) |
||||||
|
url = 'http://youtube-dl.org/downloads/%s/%s' % (version, filename) |
||||||
|
data = urllib.request.urlopen(url).read() |
||||||
|
sha256sum = hashlib.sha256(data).hexdigest() |
||||||
|
new_version[key] = (url, sha256sum) |
||||||
|
|
||||||
|
versions_info['versions'][version] = new_version |
||||||
|
versions_info['latest'] = version |
||||||
|
|
||||||
|
json.dump(versions_info, open('update/versions.json', 'w'), indent=4, sort_keys=True) |
@ -0,0 +1,32 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
import hashlib |
||||||
|
import shutil |
||||||
|
import subprocess |
||||||
|
import tempfile |
||||||
|
import urllib.request |
||||||
|
import json |
||||||
|
|
||||||
|
versions_info = json.load(open('update/versions.json')) |
||||||
|
version = versions_info['latest'] |
||||||
|
URL = versions_info['versions'][version]['bin'][0] |
||||||
|
|
||||||
|
data = urllib.request.urlopen(URL).read() |
||||||
|
|
||||||
|
# Read template page |
||||||
|
with open('download.html.in', 'r', encoding='utf-8') as tmplf: |
||||||
|
template = tmplf.read() |
||||||
|
|
||||||
|
md5sum = hashlib.md5(data).hexdigest() |
||||||
|
sha1sum = hashlib.sha1(data).hexdigest() |
||||||
|
sha256sum = hashlib.sha256(data).hexdigest() |
||||||
|
template = template.replace('@PROGRAM_VERSION@', version) |
||||||
|
template = template.replace('@PROGRAM_URL@', URL) |
||||||
|
template = template.replace('@PROGRAM_MD5SUM@', md5sum) |
||||||
|
template = template.replace('@PROGRAM_SHA1SUM@', sha1sum) |
||||||
|
template = template.replace('@PROGRAM_SHA256SUM@', sha256sum) |
||||||
|
template = template.replace('@EXE_URL@', versions_info['versions'][version]['exe'][0]) |
||||||
|
template = template.replace('@EXE_SHA256SUM@', versions_info['versions'][version]['exe'][1]) |
||||||
|
template = template.replace('@TAR_URL@', versions_info['versions'][version]['tar'][0]) |
||||||
|
template = template.replace('@TAR_SHA256SUM@', versions_info['versions'][version]['tar'][1]) |
||||||
|
with open('download.html', 'w', encoding='utf-8') as dlf: |
||||||
|
dlf.write(template) |
@ -0,0 +1,28 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
|
||||||
|
import rsa |
||||||
|
import json |
||||||
|
from binascii import hexlify |
||||||
|
|
||||||
|
versions_info = json.load(open('update/versions.json')) |
||||||
|
if 'signature' in versions_info: |
||||||
|
del versions_info['signature'] |
||||||
|
|
||||||
|
print('Enter the PKCS1 private key, followed by a blank line:') |
||||||
|
privkey = '' |
||||||
|
while True: |
||||||
|
try: |
||||||
|
line = input() |
||||||
|
except EOFError: |
||||||
|
break |
||||||
|
if line == '': |
||||||
|
break |
||||||
|
privkey += line + '\n' |
||||||
|
privkey = bytes(privkey, 'ascii') |
||||||
|
privkey = rsa.PrivateKey.load_pkcs1(privkey) |
||||||
|
|
||||||
|
signature = hexlify(rsa.pkcs1.sign(json.dumps(versions_info, sort_keys=True).encode('utf-8'), privkey, 'SHA-256')).decode() |
||||||
|
print('signature: ' + signature) |
||||||
|
|
||||||
|
versions_info['signature'] = signature |
||||||
|
json.dump(versions_info, open('update/versions.json', 'w'), indent=4, sort_keys=True) |
@ -0,0 +1,21 @@ |
|||||||
|
#!/usr/bin/env python |
||||||
|
# coding: utf-8 |
||||||
|
|
||||||
|
from __future__ import with_statement |
||||||
|
|
||||||
|
import datetime |
||||||
|
import glob |
||||||
|
import io # For Python 2 compatibilty |
||||||
|
import os |
||||||
|
import re |
||||||
|
|
||||||
|
year = str(datetime.datetime.now().year) |
||||||
|
for fn in glob.glob('*.html*'): |
||||||
|
with io.open(fn, encoding='utf-8') as f: |
||||||
|
content = f.read() |
||||||
|
newc = re.sub(u'(?P<copyright>Copyright © 2006-)(?P<year>[0-9]{4})', u'Copyright © 2006-' + year, content) |
||||||
|
if content != newc: |
||||||
|
tmpFn = fn + '.part' |
||||||
|
with io.open(tmpFn, 'wt', encoding='utf-8') as outf: |
||||||
|
outf.write(newc) |
||||||
|
os.rename(tmpFn, fn) |
@ -0,0 +1,20 @@ |
|||||||
|
import sys |
||||||
|
import re |
||||||
|
|
||||||
|
README_FILE = 'README.md' |
||||||
|
helptext = sys.stdin.read() |
||||||
|
|
||||||
|
with open(README_FILE) as f: |
||||||
|
oldreadme = f.read() |
||||||
|
|
||||||
|
header = oldreadme[:oldreadme.index('# OPTIONS')] |
||||||
|
footer = oldreadme[oldreadme.index('# CONFIGURATION'):] |
||||||
|
|
||||||
|
options = helptext[helptext.index(' General Options:')+19:] |
||||||
|
options = re.sub(r'^ (\w.+)$', r'## \1', options, flags=re.M) |
||||||
|
options = '# OPTIONS\n' + options + '\n' |
||||||
|
|
||||||
|
with open(README_FILE, 'w') as f: |
||||||
|
f.write(header) |
||||||
|
f.write(options) |
||||||
|
f.write(footer) |
@ -1,11 +1,85 @@ |
|||||||
#!/bin/sh |
#!/bin/sh |
||||||
|
|
||||||
|
# IMPORTANT: the following assumptions are made |
||||||
|
# * the GH repo is on the origin remote |
||||||
|
# * the gh-pages branch is named so locally |
||||||
|
# * the git config user.signingkey is properly set |
||||||
|
|
||||||
|
# You will need |
||||||
|
# pip install coverage nose rsa |
||||||
|
|
||||||
|
# TODO |
||||||
|
# release notes |
||||||
|
# make hash on local files |
||||||
|
|
||||||
|
set -e |
||||||
|
|
||||||
if [ -z "$1" ]; then echo "ERROR: specify version number like this: $0 1994.09.06"; exit 1; fi |
if [ -z "$1" ]; then echo "ERROR: specify version number like this: $0 1994.09.06"; exit 1; fi |
||||||
version="$1" |
version="$1" |
||||||
if [ ! -z "`git tag | grep "$version"`" ]; then echo 'ERROR: version already present'; exit 1; fi |
if [ ! -z "`git tag | grep "$version"`" ]; then echo 'ERROR: version already present'; exit 1; fi |
||||||
if [ ! -z "`git status --porcelain`" ]; then echo 'ERROR: the working directory is not clean; commit or stash changes'; exit 1; fi |
if [ ! -z "`git status --porcelain | grep -v CHANGELOG`" ]; then echo 'ERROR: the working directory is not clean; commit or stash changes'; exit 1; fi |
||||||
sed -i "s/__version__ = '.*'/__version__ = '$version'/" youtube_dl/__init__.py |
if [ ! -f "updates_key.pem" ]; then echo 'ERROR: updates_key.pem missing'; exit 1; fi |
||||||
make all |
|
||||||
git add -A |
echo "\n### First of all, testing..." |
||||||
|
make clean |
||||||
|
nosetests --with-coverage --cover-package=youtube_dl --cover-html test || exit 1 |
||||||
|
|
||||||
|
echo "\n### Changing version in version.py..." |
||||||
|
sed -i~ "s/__version__ = '.*'/__version__ = '$version'/" youtube_dl/version.py |
||||||
|
|
||||||
|
echo "\n### Committing CHANGELOG README.md and youtube_dl/version.py..." |
||||||
|
make README.md |
||||||
|
git add CHANGELOG README.md youtube_dl/version.py |
||||||
git commit -m "release $version" |
git commit -m "release $version" |
||||||
git tag -m "Release $version" "$version" |
|
||||||
|
echo "\n### Now tagging, signing and pushing..." |
||||||
|
git tag -s -m "Release $version" "$version" |
||||||
|
git show "$version" |
||||||
|
read -p "Is it good, can I push? (y/n) " -n 1 |
||||||
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then exit 1; fi |
||||||
|
echo |
||||||
|
MASTER=$(git rev-parse --abbrev-ref HEAD) |
||||||
|
git push origin $MASTER:master |
||||||
|
git push origin "$version" |
||||||
|
|
||||||
|
echo "\n### OK, now it is time to build the binaries..." |
||||||
|
REV=$(git rev-parse HEAD) |
||||||
|
make youtube-dl youtube-dl.tar.gz |
||||||
|
wget "http://jeromelaheurte.net:8142/download/rg3/youtube-dl/youtube-dl.exe?rev=$REV" -O youtube-dl.exe || \ |
||||||
|
wget "http://jeromelaheurte.net:8142/build/rg3/youtube-dl/youtube-dl.exe?rev=$REV" -O youtube-dl.exe |
||||||
|
mkdir -p "update_staging/$version" |
||||||
|
mv youtube-dl youtube-dl.exe "update_staging/$version" |
||||||
|
mv youtube-dl.tar.gz "update_staging/$version/youtube-dl-$version.tar.gz" |
||||||
|
RELEASE_FILES=youtube-dl youtube-dl.exe youtube-dl-$version.tar.gz |
||||||
|
(cd update_staging/$version/ && md5sum $RELEASE_FILES > MD5SUMS) |
||||||
|
(cd update_staging/$version/ && sha1sum $RELEASE_FILES > SHA1SUMS) |
||||||
|
(cd update_staging/$version/ && sha256sum $RELEASE_FILES > SHA2-256SUMS) |
||||||
|
(cd update_staging/$version/ && sha512sum $RELEASE_FILES > SHA2-512SUMS) |
||||||
|
git checkout HEAD -- youtube-dl youtube-dl.exe |
||||||
|
|
||||||
|
echo "\n### Signing and uploading the new binaries to youtube-dl.org..." |
||||||
|
for f in $RELEASE_FILES; do gpg --detach-sig "update_staging/$version/$f"; done |
||||||
|
scp -r "update_staging/$version" ytdl@youtube-dl.org:html/downloads/ |
||||||
|
rm -r update_staging |
||||||
|
|
||||||
|
echo "\n### Now switching to gh-pages..." |
||||||
|
git checkout gh-pages |
||||||
|
git checkout "$MASTER" -- devscripts/gh-pages/ |
||||||
|
git reset devscripts/gh-pages/ |
||||||
|
devscripts/gh-pages/add-version.py $version |
||||||
|
devscripts/gh-pages/sign-versions.py < updates_key.pem |
||||||
|
devscripts/gh-pages/generate-download.py |
||||||
|
devscripts/gh-pages/update-copyright.py |
||||||
|
rm -r test_coverage |
||||||
|
mv cover test_coverage |
||||||
|
git add *.html *.html.in update test_coverage |
||||||
|
git commit -m "release $version" |
||||||
|
git show HEAD |
||||||
|
read -p "Is it good, can I push? (y/n) " -n 1 |
||||||
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then exit 1; fi |
||||||
|
echo |
||||||
|
git push origin gh-pages |
||||||
|
|
||||||
|
echo "\n### DONE!" |
||||||
|
rm -r devscripts |
||||||
|
git checkout $MASTER |
||||||
|
@ -0,0 +1,40 @@ |
|||||||
|
#!/usr/bin/env python |
||||||
|
|
||||||
|
import sys, os |
||||||
|
|
||||||
|
try: |
||||||
|
import urllib.request as compat_urllib_request |
||||||
|
except ImportError: # Python 2 |
||||||
|
import urllib2 as compat_urllib_request |
||||||
|
|
||||||
|
sys.stderr.write(u'Hi! We changed distribution method and now youtube-dl needs to update itself one more time.\n') |
||||||
|
sys.stderr.write(u'This will only happen once. Simply press enter to go on. Sorry for the trouble!\n') |
||||||
|
sys.stderr.write(u'The new location of the binaries is https://github.com/rg3/youtube-dl/downloads, not the git repository.\n\n') |
||||||
|
|
||||||
|
try: |
||||||
|
raw_input() |
||||||
|
except NameError: # Python 3 |
||||||
|
input() |
||||||
|
|
||||||
|
filename = sys.argv[0] |
||||||
|
|
||||||
|
API_URL = "https://api.github.com/repos/rg3/youtube-dl/downloads" |
||||||
|
BIN_URL = "https://github.com/downloads/rg3/youtube-dl/youtube-dl" |
||||||
|
|
||||||
|
if not os.access(filename, os.W_OK): |
||||||
|
sys.exit('ERROR: no write permissions on %s' % filename) |
||||||
|
|
||||||
|
try: |
||||||
|
urlh = compat_urllib_request.urlopen(BIN_URL) |
||||||
|
newcontent = urlh.read() |
||||||
|
urlh.close() |
||||||
|
except (IOError, OSError) as err: |
||||||
|
sys.exit('ERROR: unable to download latest version') |
||||||
|
|
||||||
|
try: |
||||||
|
with open(filename, 'wb') as outf: |
||||||
|
outf.write(newcontent) |
||||||
|
except (IOError, OSError) as err: |
||||||
|
sys.exit('ERROR: unable to overwrite current version') |
||||||
|
|
||||||
|
sys.stderr.write(u'Done! Now you can run youtube-dl.\n') |
@ -0,0 +1,12 @@ |
|||||||
|
from distutils.core import setup |
||||||
|
import py2exe |
||||||
|
|
||||||
|
py2exe_options = { |
||||||
|
"bundle_files": 1, |
||||||
|
"compressed": 1, |
||||||
|
"optimize": 2, |
||||||
|
"dist_dir": '.', |
||||||
|
"dll_excludes": ['w9xpopen.exe'] |
||||||
|
} |
||||||
|
|
||||||
|
setup(console=['youtube-dl.py'], options={ "py2exe": py2exe_options }, zipfile=None) |
@ -0,0 +1,102 @@ |
|||||||
|
#!/usr/bin/env python |
||||||
|
|
||||||
|
import sys, os |
||||||
|
import urllib2 |
||||||
|
import json, hashlib |
||||||
|
|
||||||
|
def rsa_verify(message, signature, key): |
||||||
|
from struct import pack |
||||||
|
from hashlib import sha256 |
||||||
|
from sys import version_info |
||||||
|
def b(x): |
||||||
|
if version_info[0] == 2: return x |
||||||
|
else: return x.encode('latin1') |
||||||
|
assert(type(message) == type(b(''))) |
||||||
|
block_size = 0 |
||||||
|
n = key[0] |
||||||
|
while n: |
||||||
|
block_size += 1 |
||||||
|
n >>= 8 |
||||||
|
signature = pow(int(signature, 16), key[1], key[0]) |
||||||
|
raw_bytes = [] |
||||||
|
while signature: |
||||||
|
raw_bytes.insert(0, pack("B", signature & 0xFF)) |
||||||
|
signature >>= 8 |
||||||
|
signature = (block_size - len(raw_bytes)) * b('\x00') + b('').join(raw_bytes) |
||||||
|
if signature[0:2] != b('\x00\x01'): return False |
||||||
|
signature = signature[2:] |
||||||
|
if not b('\x00') in signature: return False |
||||||
|
signature = signature[signature.index(b('\x00'))+1:] |
||||||
|
if not signature.startswith(b('\x30\x31\x30\x0D\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20')): return False |
||||||
|
signature = signature[19:] |
||||||
|
if signature != sha256(message).digest(): return False |
||||||
|
return True |
||||||
|
|
||||||
|
sys.stderr.write(u'Hi! We changed distribution method and now youtube-dl needs to update itself one more time.\n') |
||||||
|
sys.stderr.write(u'This will only happen once. Simply press enter to go on. Sorry for the trouble!\n') |
||||||
|
sys.stderr.write(u'From now on, get the binaries from http://rg3.github.com/youtube-dl/download.html, not from the git repository.\n\n') |
||||||
|
|
||||||
|
raw_input() |
||||||
|
|
||||||
|
filename = sys.argv[0] |
||||||
|
|
||||||
|
UPDATE_URL = "http://rg3.github.com/youtube-dl/update/" |
||||||
|
VERSION_URL = UPDATE_URL + 'LATEST_VERSION' |
||||||
|
JSON_URL = UPDATE_URL + 'versions.json' |
||||||
|
UPDATES_RSA_KEY = (0x9d60ee4d8f805312fdb15a62f87b95bd66177b91df176765d13514a0f1754bcd2057295c5b6f1d35daa6742c3ffc9a82d3e118861c207995a8031e151d863c9927e304576bc80692bc8e094896fcf11b66f3e29e04e3a71e9a11558558acea1840aec37fc396fb6b65dc81a1c4144e03bd1c011de62e3f1357b327d08426fe93, 65537) |
||||||
|
|
||||||
|
if not os.access(filename, os.W_OK): |
||||||
|
sys.exit('ERROR: no write permissions on %s' % filename) |
||||||
|
|
||||||
|
exe = os.path.abspath(filename) |
||||||
|
directory = os.path.dirname(exe) |
||||||
|
if not os.access(directory, os.W_OK): |
||||||
|
sys.exit('ERROR: no write permissions on %s' % directory) |
||||||
|
|
||||||
|
try: |
||||||
|
versions_info = urllib2.urlopen(JSON_URL).read().decode('utf-8') |
||||||
|
versions_info = json.loads(versions_info) |
||||||
|
except: |
||||||
|
sys.exit(u'ERROR: can\'t obtain versions info. Please try again later.') |
||||||
|
if not 'signature' in versions_info: |
||||||
|
sys.exit(u'ERROR: the versions file is not signed or corrupted. Aborting.') |
||||||
|
signature = versions_info['signature'] |
||||||
|
del versions_info['signature'] |
||||||
|
if not rsa_verify(json.dumps(versions_info, sort_keys=True), signature, UPDATES_RSA_KEY): |
||||||
|
sys.exit(u'ERROR: the versions file signature is invalid. Aborting.') |
||||||
|
|
||||||
|
version = versions_info['versions'][versions_info['latest']] |
||||||
|
|
||||||
|
try: |
||||||
|
urlh = urllib2.urlopen(version['exe'][0]) |
||||||
|
newcontent = urlh.read() |
||||||
|
urlh.close() |
||||||
|
except (IOError, OSError) as err: |
||||||
|
sys.exit('ERROR: unable to download latest version') |
||||||
|
|
||||||
|
newcontent_hash = hashlib.sha256(newcontent).hexdigest() |
||||||
|
if newcontent_hash != version['exe'][1]: |
||||||
|
sys.exit(u'ERROR: the downloaded file hash does not match. Aborting.') |
||||||
|
|
||||||
|
try: |
||||||
|
with open(exe + '.new', 'wb') as outf: |
||||||
|
outf.write(newcontent) |
||||||
|
except (IOError, OSError) as err: |
||||||
|
sys.exit(u'ERROR: unable to write the new version') |
||||||
|
|
||||||
|
try: |
||||||
|
bat = os.path.join(directory, 'youtube-dl-updater.bat') |
||||||
|
b = open(bat, 'w') |
||||||
|
b.write(""" |
||||||
|
echo Updating youtube-dl... |
||||||
|
ping 127.0.0.1 -n 5 -w 1000 > NUL |
||||||
|
move /Y "%s.new" "%s" |
||||||
|
del "%s" |
||||||
|
\n""" %(exe, exe, bat)) |
||||||
|
b.close() |
||||||
|
|
||||||
|
os.startfile(bat) |
||||||
|
except (IOError, OSError) as err: |
||||||
|
sys.exit('ERROR: unable to overwrite current version') |
||||||
|
|
||||||
|
sys.stderr.write(u'Done! Now you can run youtube-dl.\n') |
@ -0,0 +1,74 @@ |
|||||||
|
#!/usr/bin/env python |
||||||
|
# -*- coding: utf-8 -*- |
||||||
|
|
||||||
|
from __future__ import print_function |
||||||
|
from distutils.core import setup |
||||||
|
import pkg_resources |
||||||
|
import sys |
||||||
|
|
||||||
|
try: |
||||||
|
import py2exe |
||||||
|
"""This will create an exe that needs Microsoft Visual C++ 2008 Redistributable Package""" |
||||||
|
except ImportError: |
||||||
|
if len(sys.argv) >= 2 and sys.argv[1] == 'py2exe': |
||||||
|
print("Cannot import py2exe", file=sys.stderr) |
||||||
|
exit(1) |
||||||
|
|
||||||
|
py2exe_options = { |
||||||
|
"bundle_files": 1, |
||||||
|
"compressed": 1, |
||||||
|
"optimize": 2, |
||||||
|
"dist_dir": '.', |
||||||
|
"dll_excludes": ['w9xpopen.exe'] |
||||||
|
} |
||||||
|
py2exe_console = [{ |
||||||
|
"script": "./youtube_dl/__main__.py", |
||||||
|
"dest_base": "youtube-dl", |
||||||
|
}] |
||||||
|
py2exe_params = { |
||||||
|
'console': py2exe_console, |
||||||
|
'options': { "py2exe": py2exe_options }, |
||||||
|
'zipfile': None |
||||||
|
} |
||||||
|
|
||||||
|
if len(sys.argv) >= 2 and sys.argv[1] == 'py2exe': |
||||||
|
params = py2exe_params |
||||||
|
else: |
||||||
|
params = { |
||||||
|
'scripts': ['bin/youtube-dl'], |
||||||
|
'data_files': [('etc/bash_completion.d', ['youtube-dl.bash-completion']), # Installing system-wide would require sudo... |
||||||
|
('share/doc/youtube_dl', ['README.txt']), |
||||||
|
('share/man/man1/', ['youtube-dl.1'])] |
||||||
|
} |
||||||
|
|
||||||
|
# Get the version from youtube_dl/version.py without importing the package |
||||||
|
exec(compile(open('youtube_dl/version.py').read(), 'youtube_dl/version.py', 'exec')) |
||||||
|
|
||||||
|
setup( |
||||||
|
name = 'youtube_dl', |
||||||
|
version = __version__, |
||||||
|
description = 'YouTube video downloader', |
||||||
|
long_description = 'Small command-line program to download videos from YouTube.com and other video sites.', |
||||||
|
url = 'https://github.com/rg3/youtube-dl', |
||||||
|
author = 'Ricardo Garcia', |
||||||
|
maintainer = 'Philipp Hagemeister', |
||||||
|
maintainer_email = 'phihag@phihag.de', |
||||||
|
packages = ['youtube_dl'], |
||||||
|
|
||||||
|
# Provokes warning on most systems (why?!) |
||||||
|
#test_suite = 'nose.collector', |
||||||
|
#test_requires = ['nosetest'], |
||||||
|
|
||||||
|
classifiers = [ |
||||||
|
"Topic :: Multimedia :: Video", |
||||||
|
"Development Status :: 5 - Production/Stable", |
||||||
|
"Environment :: Console", |
||||||
|
"License :: Public Domain", |
||||||
|
"Programming Language :: Python :: 2.6", |
||||||
|
"Programming Language :: Python :: 2.7", |
||||||
|
"Programming Language :: Python :: 3", |
||||||
|
"Programming Language :: Python :: 3.3" |
||||||
|
], |
||||||
|
|
||||||
|
**params |
||||||
|
) |
@ -1 +1,40 @@ |
|||||||
{"username": null, "listformats": null, "skip_download": false, "usenetrc": false, "max_downloads": null, "noprogress": false, "forcethumbnail": false, "forceformat": false, "format_limit": null, "ratelimit": null, "nooverwrites": false, "forceurl": false, "writeinfojson": false, "simulate": false, "playliststart": 1, "continuedl": true, "password": null, "prefer_free_formats": false, "nopart": false, "retries": 10, "updatetime": true, "consoletitle": false, "verbose": true, "forcefilename": false, "ignoreerrors": false, "logtostderr": false, "format": null, "subtitleslang": null, "quiet": false, "outtmpl": "%(id)s.%(ext)s", "rejecttitle": null, "playlistend": -1, "writedescription": false, "forcetitle": false, "forcedescription": false, "writesubtitles": false, "matchtitle": null} |
{ |
||||||
|
"consoletitle": false, |
||||||
|
"continuedl": true, |
||||||
|
"forcedescription": false, |
||||||
|
"forcefilename": false, |
||||||
|
"forceformat": false, |
||||||
|
"forcethumbnail": false, |
||||||
|
"forcetitle": false, |
||||||
|
"forceurl": false, |
||||||
|
"format": null, |
||||||
|
"format_limit": null, |
||||||
|
"ignoreerrors": false, |
||||||
|
"listformats": null, |
||||||
|
"logtostderr": false, |
||||||
|
"matchtitle": null, |
||||||
|
"max_downloads": null, |
||||||
|
"nooverwrites": false, |
||||||
|
"nopart": false, |
||||||
|
"noprogress": false, |
||||||
|
"outtmpl": "%(id)s.%(ext)s", |
||||||
|
"password": null, |
||||||
|
"playlistend": -1, |
||||||
|
"playliststart": 1, |
||||||
|
"prefer_free_formats": false, |
||||||
|
"quiet": false, |
||||||
|
"ratelimit": null, |
||||||
|
"rejecttitle": null, |
||||||
|
"retries": 10, |
||||||
|
"simulate": false, |
||||||
|
"skip_download": false, |
||||||
|
"subtitleslang": null, |
||||||
|
"test": true, |
||||||
|
"updatetime": true, |
||||||
|
"usenetrc": false, |
||||||
|
"username": null, |
||||||
|
"verbose": true, |
||||||
|
"writedescription": false, |
||||||
|
"writeinfojson": true, |
||||||
|
"writesubtitles": false |
||||||
|
} |
@ -0,0 +1,27 @@ |
|||||||
|
#!/usr/bin/env python |
||||||
|
|
||||||
|
import sys |
||||||
|
import unittest |
||||||
|
|
||||||
|
# Allow direct execution |
||||||
|
import os |
||||||
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
||||||
|
|
||||||
|
from youtube_dl.InfoExtractors import YoutubeIE, YoutubePlaylistIE |
||||||
|
|
||||||
|
class TestAllURLsMatching(unittest.TestCase): |
||||||
|
def test_youtube_playlist_matching(self): |
||||||
|
self.assertTrue(YoutubePlaylistIE().suitable(u'ECUl4u3cNGP61MdtwGTqZA0MreSaDybji8')) |
||||||
|
self.assertTrue(YoutubePlaylistIE().suitable(u'PL63F0C78739B09958')) |
||||||
|
self.assertFalse(YoutubePlaylistIE().suitable(u'PLtS2H6bU1M')) |
||||||
|
|
||||||
|
def test_youtube_matching(self): |
||||||
|
self.assertTrue(YoutubeIE().suitable(u'PLtS2H6bU1M')) |
||||||
|
|
||||||
|
def test_youtube_extract(self): |
||||||
|
self.assertEqual(YoutubeIE()._extract_id('http://www.youtube.com/watch?&v=BaW_jenozKc'), 'BaW_jenozKc') |
||||||
|
self.assertEqual(YoutubeIE()._extract_id('https://www.youtube.com/watch?&v=BaW_jenozKc'), 'BaW_jenozKc') |
||||||
|
self.assertEqual(YoutubeIE()._extract_id('https://www.youtube.com/watch?feature=player_embedded&v=BaW_jenozKc'), 'BaW_jenozKc') |
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
unittest.main() |
@ -1,93 +1,125 @@ |
|||||||
#!/usr/bin/env python2 |
#!/usr/bin/env python |
||||||
import unittest |
|
||||||
|
import errno |
||||||
import hashlib |
import hashlib |
||||||
|
import io |
||||||
import os |
import os |
||||||
import json |
import json |
||||||
|
import unittest |
||||||
|
import sys |
||||||
|
import hashlib |
||||||
|
import socket |
||||||
|
|
||||||
|
# Allow direct execution |
||||||
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
||||||
|
|
||||||
|
import youtube_dl.FileDownloader |
||||||
|
import youtube_dl.InfoExtractors |
||||||
|
from youtube_dl.utils import * |
||||||
|
|
||||||
|
DEF_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'tests.json') |
||||||
|
PARAMETERS_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "parameters.json") |
||||||
|
|
||||||
|
# General configuration (from __init__, not very elegant...) |
||||||
|
jar = compat_cookiejar.CookieJar() |
||||||
|
cookie_processor = compat_urllib_request.HTTPCookieProcessor(jar) |
||||||
|
proxy_handler = compat_urllib_request.ProxyHandler() |
||||||
|
opener = compat_urllib_request.build_opener(proxy_handler, cookie_processor, YoutubeDLHandler()) |
||||||
|
compat_urllib_request.install_opener(opener) |
||||||
|
|
||||||
|
def _try_rm(filename): |
||||||
|
""" Remove a file if it exists """ |
||||||
|
try: |
||||||
|
os.remove(filename) |
||||||
|
except OSError as ose: |
||||||
|
if ose.errno != errno.ENOENT: |
||||||
|
raise |
||||||
|
|
||||||
|
class FileDownloader(youtube_dl.FileDownloader): |
||||||
|
def __init__(self, *args, **kwargs): |
||||||
|
self.to_stderr = self.to_screen |
||||||
|
self.processed_info_dicts = [] |
||||||
|
return youtube_dl.FileDownloader.__init__(self, *args, **kwargs) |
||||||
|
def process_info(self, info_dict): |
||||||
|
self.processed_info_dicts.append(info_dict) |
||||||
|
return youtube_dl.FileDownloader.process_info(self, info_dict) |
||||||
|
|
||||||
|
def _file_md5(fn): |
||||||
|
with open(fn, 'rb') as f: |
||||||
|
return hashlib.md5(f.read()).hexdigest() |
||||||
|
|
||||||
|
with io.open(DEF_FILE, encoding='utf-8') as deff: |
||||||
|
defs = json.load(deff) |
||||||
|
with io.open(PARAMETERS_FILE, encoding='utf-8') as pf: |
||||||
|
parameters = json.load(pf) |
||||||
|
|
||||||
|
|
||||||
|
class TestDownload(unittest.TestCase): |
||||||
|
def setUp(self): |
||||||
|
self.parameters = parameters |
||||||
|
self.defs = defs |
||||||
|
|
||||||
|
### Dynamically generate tests |
||||||
|
def generator(test_case): |
||||||
|
|
||||||
|
def test_template(self): |
||||||
|
ie = getattr(youtube_dl.InfoExtractors, test_case['name'] + 'IE') |
||||||
|
if not ie._WORKING: |
||||||
|
print('Skipping: IE marked as not _WORKING') |
||||||
|
return |
||||||
|
if 'playlist' not in test_case and not test_case['file']: |
||||||
|
print('Skipping: No output file specified') |
||||||
|
return |
||||||
|
if 'skip' in test_case: |
||||||
|
print('Skipping: {0}'.format(test_case['skip'])) |
||||||
|
return |
||||||
|
|
||||||
|
params = self.parameters.copy() |
||||||
|
params.update(test_case.get('params', {})) |
||||||
|
|
||||||
|
fd = FileDownloader(params) |
||||||
|
fd.add_info_extractor(ie()) |
||||||
|
for ien in test_case.get('add_ie', []): |
||||||
|
fd.add_info_extractor(getattr(youtube_dl.InfoExtractors, ien + 'IE')()) |
||||||
|
|
||||||
|
test_cases = test_case.get('playlist', [test_case]) |
||||||
|
for tc in test_cases: |
||||||
|
_try_rm(tc['file']) |
||||||
|
_try_rm(tc['file'] + '.part') |
||||||
|
_try_rm(tc['file'] + '.info.json') |
||||||
|
try: |
||||||
|
fd.download([test_case['url']]) |
||||||
|
|
||||||
|
for tc in test_cases: |
||||||
|
if not test_case.get('params', {}).get('skip_download', False): |
||||||
|
self.assertTrue(os.path.exists(tc['file'])) |
||||||
|
self.assertTrue(os.path.exists(tc['file'] + '.info.json')) |
||||||
|
if 'md5' in tc: |
||||||
|
md5_for_file = _file_md5(tc['file']) |
||||||
|
self.assertEqual(md5_for_file, tc['md5']) |
||||||
|
with io.open(tc['file'] + '.info.json', encoding='utf-8') as infof: |
||||||
|
info_dict = json.load(infof) |
||||||
|
for (info_field, value) in tc.get('info_dict', {}).items(): |
||||||
|
if value.startswith('md5:'): |
||||||
|
md5_info_value = hashlib.md5(info_dict.get(info_field, '')).hexdigest() |
||||||
|
self.assertEqual(value[3:], md5_info_value) |
||||||
|
else: |
||||||
|
self.assertEqual(value, info_dict.get(info_field)) |
||||||
|
finally: |
||||||
|
for tc in test_cases: |
||||||
|
_try_rm(tc['file']) |
||||||
|
_try_rm(tc['file'] + '.part') |
||||||
|
_try_rm(tc['file'] + '.info.json') |
||||||
|
|
||||||
|
return test_template |
||||||
|
|
||||||
|
### And add them to TestDownload |
||||||
|
for test_case in defs: |
||||||
|
test_method = generator(test_case) |
||||||
|
test_method.__name__ = "test_{0}".format(test_case["name"]) |
||||||
|
setattr(TestDownload, test_method.__name__, test_method) |
||||||
|
del test_method |
||||||
|
|
||||||
|
|
||||||
from youtube_dl.FileDownloader import FileDownloader |
if __name__ == '__main__': |
||||||
from youtube_dl.InfoExtractors import YoutubeIE, DailymotionIE |
unittest.main() |
||||||
from youtube_dl.InfoExtractors import MetacafeIE, BlipTVIE |
|
||||||
|
|
||||||
|
|
||||||
class DownloadTest(unittest.TestCase): |
|
||||||
PARAMETERS_FILE = "test/parameters.json" |
|
||||||
#calculated with md5sum: |
|
||||||
#md5sum (GNU coreutils) 8.19 |
|
||||||
|
|
||||||
YOUTUBE_SIZE = 1993883 |
|
||||||
YOUTUBE_URL = "http://www.youtube.com/watch?v=BaW_jenozKc" |
|
||||||
YOUTUBE_FILE = "BaW_jenozKc.mp4" |
|
||||||
|
|
||||||
DAILYMOTION_MD5 = "d363a50e9eb4f22ce90d08d15695bb47" |
|
||||||
DAILYMOTION_URL = "http://www.dailymotion.com/video/x33vw9_tutoriel-de-youtubeur-dl-des-video_tech" |
|
||||||
DAILYMOTION_FILE = "x33vw9.mp4" |
|
||||||
|
|
||||||
METACAFE_SIZE = 5754305 |
|
||||||
METACAFE_URL = "http://www.metacafe.com/watch/yt-_aUehQsCQtM/the_electric_company_short_i_pbs_kids_go/" |
|
||||||
METACAFE_FILE = "_aUehQsCQtM.flv" |
|
||||||
|
|
||||||
BLIP_MD5 = "93c24d2f4e0782af13b8a7606ea97ba7" |
|
||||||
BLIP_URL = "http://blip.tv/cbr/cbr-exclusive-gotham-city-imposters-bats-vs-jokerz-short-3-5796352" |
|
||||||
BLIP_FILE = "5779306.m4v" |
|
||||||
|
|
||||||
XVIDEO_MD5 = "" |
|
||||||
XVIDEO_URL = "" |
|
||||||
XVIDEO_FILE = "" |
|
||||||
|
|
||||||
|
|
||||||
def test_youtube(self): |
|
||||||
#let's download a file from youtube |
|
||||||
with open(DownloadTest.PARAMETERS_FILE) as f: |
|
||||||
fd = FileDownloader(json.load(f)) |
|
||||||
fd.add_info_extractor(YoutubeIE()) |
|
||||||
fd.download([DownloadTest.YOUTUBE_URL]) |
|
||||||
self.assertTrue(os.path.exists(DownloadTest.YOUTUBE_FILE)) |
|
||||||
self.assertEqual(os.path.getsize(DownloadTest.YOUTUBE_FILE), DownloadTest.YOUTUBE_SIZE) |
|
||||||
|
|
||||||
def test_dailymotion(self): |
|
||||||
with open(DownloadTest.PARAMETERS_FILE) as f: |
|
||||||
fd = FileDownloader(json.load(f)) |
|
||||||
fd.add_info_extractor(DailymotionIE()) |
|
||||||
fd.download([DownloadTest.DAILYMOTION_URL]) |
|
||||||
self.assertTrue(os.path.exists(DownloadTest.DAILYMOTION_FILE)) |
|
||||||
md5_down_file = md5_for_file(DownloadTest.DAILYMOTION_FILE) |
|
||||||
self.assertEqual(md5_down_file, DownloadTest.DAILYMOTION_MD5) |
|
||||||
|
|
||||||
def test_metacafe(self): |
|
||||||
#this emulate a skip,to be 2.6 compatible |
|
||||||
with open(DownloadTest.PARAMETERS_FILE) as f: |
|
||||||
fd = FileDownloader(json.load(f)) |
|
||||||
fd.add_info_extractor(MetacafeIE()) |
|
||||||
fd.add_info_extractor(YoutubeIE()) |
|
||||||
fd.download([DownloadTest.METACAFE_URL]) |
|
||||||
self.assertTrue(os.path.exists(DownloadTest.METACAFE_FILE)) |
|
||||||
self.assertEqual(os.path.getsize(DownloadTest.METACAFE_FILE), DownloadTest.METACAFE_SIZE) |
|
||||||
|
|
||||||
def test_blip(self): |
|
||||||
with open(DownloadTest.PARAMETERS_FILE) as f: |
|
||||||
fd = FileDownloader(json.load(f)) |
|
||||||
fd.add_info_extractor(BlipTVIE()) |
|
||||||
fd.download([DownloadTest.BLIP_URL]) |
|
||||||
self.assertTrue(os.path.exists(DownloadTest.BLIP_FILE)) |
|
||||||
md5_down_file = md5_for_file(DownloadTest.BLIP_FILE) |
|
||||||
self.assertEqual(md5_down_file, DownloadTest.BLIP_MD5) |
|
||||||
|
|
||||||
def tearDown(self): |
|
||||||
if os.path.exists(DownloadTest.YOUTUBE_FILE): |
|
||||||
os.remove(DownloadTest.YOUTUBE_FILE) |
|
||||||
if os.path.exists(DownloadTest.DAILYMOTION_FILE): |
|
||||||
os.remove(DownloadTest.DAILYMOTION_FILE) |
|
||||||
if os.path.exists(DownloadTest.METACAFE_FILE): |
|
||||||
os.remove(DownloadTest.METACAFE_FILE) |
|
||||||
if os.path.exists(DownloadTest.BLIP_FILE): |
|
||||||
os.remove(DownloadTest.BLIP_FILE) |
|
||||||
|
|
||||||
def md5_for_file(filename, block_size=2**20): |
|
||||||
with open(filename) as f: |
|
||||||
md5 = hashlib.md5() |
|
||||||
while True: |
|
||||||
data = f.read(block_size) |
|
||||||
if not data: |
|
||||||
break |
|
||||||
md5.update(data) |
|
||||||
return md5.hexdigest() |
|
||||||
|
@ -0,0 +1,26 @@ |
|||||||
|
import unittest |
||||||
|
|
||||||
|
import sys |
||||||
|
import os |
||||||
|
import subprocess |
||||||
|
|
||||||
|
rootDir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
||||||
|
|
||||||
|
try: |
||||||
|
_DEV_NULL = subprocess.DEVNULL |
||||||
|
except AttributeError: |
||||||
|
_DEV_NULL = open(os.devnull, 'wb') |
||||||
|
|
||||||
|
class TestExecution(unittest.TestCase): |
||||||
|
def test_import(self): |
||||||
|
subprocess.check_call([sys.executable, '-c', 'import youtube_dl'], cwd=rootDir) |
||||||
|
|
||||||
|
def test_module_exec(self): |
||||||
|
if sys.version_info >= (2,7): # Python 2.6 doesn't support package execution |
||||||
|
subprocess.check_call([sys.executable, '-m', 'youtube_dl', '--version'], cwd=rootDir, stdout=_DEV_NULL) |
||||||
|
|
||||||
|
def test_main_exec(self): |
||||||
|
subprocess.check_call([sys.executable, 'youtube_dl/__main__.py', '--version'], cwd=rootDir, stdout=_DEV_NULL) |
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
unittest.main() |
@ -0,0 +1,77 @@ |
|||||||
|
#!/usr/bin/env python |
||||||
|
# coding: utf-8 |
||||||
|
|
||||||
|
import json |
||||||
|
import os |
||||||
|
import sys |
||||||
|
import unittest |
||||||
|
|
||||||
|
# Allow direct execution |
||||||
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
||||||
|
|
||||||
|
import youtube_dl.FileDownloader |
||||||
|
import youtube_dl.InfoExtractors |
||||||
|
from youtube_dl.utils import * |
||||||
|
|
||||||
|
PARAMETERS_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "parameters.json") |
||||||
|
|
||||||
|
# General configuration (from __init__, not very elegant...) |
||||||
|
jar = compat_cookiejar.CookieJar() |
||||||
|
cookie_processor = compat_urllib_request.HTTPCookieProcessor(jar) |
||||||
|
proxy_handler = compat_urllib_request.ProxyHandler() |
||||||
|
opener = compat_urllib_request.build_opener(proxy_handler, cookie_processor, YoutubeDLHandler()) |
||||||
|
compat_urllib_request.install_opener(opener) |
||||||
|
|
||||||
|
class FileDownloader(youtube_dl.FileDownloader): |
||||||
|
def __init__(self, *args, **kwargs): |
||||||
|
youtube_dl.FileDownloader.__init__(self, *args, **kwargs) |
||||||
|
self.to_stderr = self.to_screen |
||||||
|
|
||||||
|
with io.open(PARAMETERS_FILE, encoding='utf-8') as pf: |
||||||
|
params = json.load(pf) |
||||||
|
params['writeinfojson'] = True |
||||||
|
params['skip_download'] = True |
||||||
|
params['writedescription'] = True |
||||||
|
|
||||||
|
TEST_ID = 'BaW_jenozKc' |
||||||
|
INFO_JSON_FILE = TEST_ID + '.mp4.info.json' |
||||||
|
DESCRIPTION_FILE = TEST_ID + '.mp4.description' |
||||||
|
EXPECTED_DESCRIPTION = u'''test chars: "'/\ä↭𝕐 |
||||||
|
|
||||||
|
This is a test video for youtube-dl. |
||||||
|
|
||||||
|
For more information, contact phihag@phihag.de .''' |
||||||
|
|
||||||
|
class TestInfoJSON(unittest.TestCase): |
||||||
|
def setUp(self): |
||||||
|
# Clear old files |
||||||
|
self.tearDown() |
||||||
|
|
||||||
|
def test_info_json(self): |
||||||
|
ie = youtube_dl.InfoExtractors.YoutubeIE() |
||||||
|
fd = FileDownloader(params) |
||||||
|
fd.add_info_extractor(ie) |
||||||
|
fd.download([TEST_ID]) |
||||||
|
self.assertTrue(os.path.exists(INFO_JSON_FILE)) |
||||||
|
with io.open(INFO_JSON_FILE, 'r', encoding='utf-8') as jsonf: |
||||||
|
jd = json.load(jsonf) |
||||||
|
self.assertEqual(jd['upload_date'], u'20121002') |
||||||
|
self.assertEqual(jd['description'], EXPECTED_DESCRIPTION) |
||||||
|
self.assertEqual(jd['id'], TEST_ID) |
||||||
|
self.assertEqual(jd['extractor'], 'youtube') |
||||||
|
self.assertEqual(jd['title'], u'''youtube-dl test video "'/\ä↭𝕐''') |
||||||
|
self.assertEqual(jd['uploader'], 'Philipp Hagemeister') |
||||||
|
|
||||||
|
self.assertTrue(os.path.exists(DESCRIPTION_FILE)) |
||||||
|
with io.open(DESCRIPTION_FILE, 'r', encoding='utf-8') as descf: |
||||||
|
descr = descf.read() |
||||||
|
self.assertEqual(descr, EXPECTED_DESCRIPTION) |
||||||
|
|
||||||
|
def tearDown(self): |
||||||
|
if os.path.exists(INFO_JSON_FILE): |
||||||
|
os.remove(INFO_JSON_FILE) |
||||||
|
if os.path.exists(DESCRIPTION_FILE): |
||||||
|
os.remove(DESCRIPTION_FILE) |
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
unittest.main() |
@ -0,0 +1,73 @@ |
|||||||
|
#!/usr/bin/env python |
||||||
|
|
||||||
|
import sys |
||||||
|
import unittest |
||||||
|
import json |
||||||
|
|
||||||
|
# Allow direct execution |
||||||
|
import os |
||||||
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
||||||
|
|
||||||
|
from youtube_dl.InfoExtractors import YoutubeUserIE,YoutubePlaylistIE |
||||||
|
from youtube_dl.utils import * |
||||||
|
|
||||||
|
PARAMETERS_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "parameters.json") |
||||||
|
with io.open(PARAMETERS_FILE, encoding='utf-8') as pf: |
||||||
|
parameters = json.load(pf) |
||||||
|
|
||||||
|
# General configuration (from __init__, not very elegant...) |
||||||
|
jar = compat_cookiejar.CookieJar() |
||||||
|
cookie_processor = compat_urllib_request.HTTPCookieProcessor(jar) |
||||||
|
proxy_handler = compat_urllib_request.ProxyHandler() |
||||||
|
opener = compat_urllib_request.build_opener(proxy_handler, cookie_processor, YoutubeDLHandler()) |
||||||
|
compat_urllib_request.install_opener(opener) |
||||||
|
|
||||||
|
class FakeDownloader(object): |
||||||
|
def __init__(self): |
||||||
|
self.result = [] |
||||||
|
self.params = parameters |
||||||
|
def to_screen(self, s): |
||||||
|
print(s) |
||||||
|
def trouble(self, s): |
||||||
|
raise Exception(s) |
||||||
|
def download(self, x): |
||||||
|
self.result.append(x) |
||||||
|
|
||||||
|
class TestYoutubeLists(unittest.TestCase): |
||||||
|
def test_youtube_playlist(self): |
||||||
|
DL = FakeDownloader() |
||||||
|
IE = YoutubePlaylistIE(DL) |
||||||
|
IE.extract('https://www.youtube.com/playlist?list=PLwiyx1dc3P2JR9N8gQaQN_BCvlSlap7re') |
||||||
|
self.assertEqual(DL.result, [ |
||||||
|
['http://www.youtube.com/watch?v=bV9L5Ht9LgY'], |
||||||
|
['http://www.youtube.com/watch?v=FXxLjLQi3Fg'], |
||||||
|
['http://www.youtube.com/watch?v=tU3Bgo5qJZE'] |
||||||
|
]) |
||||||
|
|
||||||
|
def test_youtube_playlist_long(self): |
||||||
|
DL = FakeDownloader() |
||||||
|
IE = YoutubePlaylistIE(DL) |
||||||
|
IE.extract('https://www.youtube.com/playlist?list=UUBABnxM4Ar9ten8Mdjj1j0Q') |
||||||
|
self.assertTrue(len(DL.result) >= 799) |
||||||
|
|
||||||
|
def test_youtube_course(self): |
||||||
|
DL = FakeDownloader() |
||||||
|
IE = YoutubePlaylistIE(DL) |
||||||
|
# TODO find a > 100 (paginating?) videos course |
||||||
|
IE.extract('https://www.youtube.com/course?list=ECUl4u3cNGP61MdtwGTqZA0MreSaDybji8') |
||||||
|
self.assertEqual(DL.result[0], ['http://www.youtube.com/watch?v=j9WZyLZCBzs']) |
||||||
|
self.assertEqual(len(DL.result), 25) |
||||||
|
self.assertEqual(DL.result[-1], ['http://www.youtube.com/watch?v=rYefUsYuEp0']) |
||||||
|
|
||||||
|
def test_youtube_channel(self): |
||||||
|
# I give up, please find a channel that does paginate and test this like test_youtube_playlist_long |
||||||
|
pass # TODO |
||||||
|
|
||||||
|
def test_youtube_user(self): |
||||||
|
DL = FakeDownloader() |
||||||
|
IE = YoutubeUserIE(DL) |
||||||
|
IE.extract('https://www.youtube.com/user/TheLinuxFoundation') |
||||||
|
self.assertTrue(len(DL.result) >= 320) |
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
unittest.main() |
@ -0,0 +1,57 @@ |
|||||||
|
#!/usr/bin/env python |
||||||
|
|
||||||
|
import sys |
||||||
|
import unittest |
||||||
|
import json |
||||||
|
import io |
||||||
|
import hashlib |
||||||
|
|
||||||
|
# Allow direct execution |
||||||
|
import os |
||||||
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
||||||
|
|
||||||
|
from youtube_dl.InfoExtractors import YoutubeIE |
||||||
|
from youtube_dl.utils import * |
||||||
|
|
||||||
|
PARAMETERS_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "parameters.json") |
||||||
|
with io.open(PARAMETERS_FILE, encoding='utf-8') as pf: |
||||||
|
parameters = json.load(pf) |
||||||
|
|
||||||
|
# General configuration (from __init__, not very elegant...) |
||||||
|
jar = compat_cookiejar.CookieJar() |
||||||
|
cookie_processor = compat_urllib_request.HTTPCookieProcessor(jar) |
||||||
|
proxy_handler = compat_urllib_request.ProxyHandler() |
||||||
|
opener = compat_urllib_request.build_opener(proxy_handler, cookie_processor, YoutubeDLHandler()) |
||||||
|
compat_urllib_request.install_opener(opener) |
||||||
|
|
||||||
|
class FakeDownloader(object): |
||||||
|
def __init__(self): |
||||||
|
self.result = [] |
||||||
|
self.params = parameters |
||||||
|
def to_screen(self, s): |
||||||
|
print(s) |
||||||
|
def trouble(self, s): |
||||||
|
raise Exception(s) |
||||||
|
def download(self, x): |
||||||
|
self.result.append(x) |
||||||
|
|
||||||
|
md5 = lambda s: hashlib.md5(s.encode('utf-8')).hexdigest() |
||||||
|
|
||||||
|
class TestYoutubeSubtitles(unittest.TestCase): |
||||||
|
def test_youtube_subtitles(self): |
||||||
|
DL = FakeDownloader() |
||||||
|
DL.params['writesubtitles'] = True |
||||||
|
IE = YoutubeIE(DL) |
||||||
|
info_dict = IE.extract('QRS8MkLhQmM') |
||||||
|
self.assertEqual(md5(info_dict[0]['subtitles']), 'c3228550d59116f3c29fba370b55d033') |
||||||
|
|
||||||
|
def test_youtube_subtitles_it(self): |
||||||
|
DL = FakeDownloader() |
||||||
|
DL.params['writesubtitles'] = True |
||||||
|
DL.params['subtitleslang'] = 'it' |
||||||
|
IE = YoutubeIE(DL) |
||||||
|
info_dict = IE.extract('QRS8MkLhQmM') |
||||||
|
self.assertEqual(md5(info_dict[0]['subtitles']), '132a88a0daf8e1520f393eb58f1f646a') |
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
unittest.main() |
@ -0,0 +1,164 @@ |
|||||||
|
[ |
||||||
|
{ |
||||||
|
"name": "Youtube", |
||||||
|
"url": "http://www.youtube.com/watch?v=BaW_jenozKc", |
||||||
|
"file": "BaW_jenozKc.mp4", |
||||||
|
"info_dict": { |
||||||
|
"title": "youtube-dl test video \"'/\\ä↭𝕐", |
||||||
|
"uploader": "Philipp Hagemeister", |
||||||
|
"uploader_id": "phihag", |
||||||
|
"upload_date": "20121002", |
||||||
|
"description": "test chars: \"'/\\ä↭𝕐\n\nThis is a test video for youtube-dl.\n\nFor more information, contact phihag@phihag.de ." |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Dailymotion", |
||||||
|
"md5": "392c4b85a60a90dc4792da41ce3144eb", |
||||||
|
"url": "http://www.dailymotion.com/video/x33vw9_tutoriel-de-youtubeur-dl-des-video_tech", |
||||||
|
"file": "x33vw9.mp4" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Metacafe", |
||||||
|
"add_ie": ["Youtube"], |
||||||
|
"url": "http://metacafe.com/watch/yt-_aUehQsCQtM/the_electric_company_short_i_pbs_kids_go/", |
||||||
|
"file": "_aUehQsCQtM.flv" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "BlipTV", |
||||||
|
"md5": "b2d849efcf7ee18917e4b4d9ff37cafe", |
||||||
|
"url": "http://blip.tv/cbr/cbr-exclusive-gotham-city-imposters-bats-vs-jokerz-short-3-5796352", |
||||||
|
"file": "5779306.m4v" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "XVideos", |
||||||
|
"md5": "1d0c835822f0a71a7bf011855db929d0", |
||||||
|
"url": "http://www.xvideos.com/video939581/funny_porns_by_s_-1", |
||||||
|
"file": "939581.flv" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Vimeo", |
||||||
|
"md5": "8879b6cc097e987f02484baf890129e5", |
||||||
|
"url": "http://vimeo.com/56015672", |
||||||
|
"file": "56015672.mp4", |
||||||
|
"info_dict": { |
||||||
|
"title": "youtube-dl test video - ★ \" ' 幸 / \\ ä ↭ 𝕐", |
||||||
|
"uploader": "Filippo Valsorda", |
||||||
|
"uploader_id": "user7108434", |
||||||
|
"upload_date": "20121220", |
||||||
|
"description": "This is a test case for youtube-dl.\nFor more information, see github.com/rg3/youtube-dl\nTest chars: ★ \" ' 幸 / \\ ä ↭ 𝕐" |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Soundcloud", |
||||||
|
"md5": "ebef0a451b909710ed1d7787dddbf0d7", |
||||||
|
"url": "http://soundcloud.com/ethmusic/lostin-powers-she-so-heavy", |
||||||
|
"file": "62986583.mp3" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "StanfordOpenClassroom", |
||||||
|
"md5": "544a9468546059d4e80d76265b0443b8", |
||||||
|
"url": "http://openclassroom.stanford.edu/MainFolder/VideoPage.php?course=PracticalUnix&video=intro-environment&speed=100", |
||||||
|
"file": "PracticalUnix_intro-environment.mp4" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "XNXX", |
||||||
|
"md5": "0831677e2b4761795f68d417e0b7b445", |
||||||
|
"url": "http://video.xnxx.com/video1135332/lida_naked_funny_actress_5_", |
||||||
|
"file": "1135332.flv" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Youku", |
||||||
|
"url": "http://v.youku.com/v_show/id_XNDgyMDQ2NTQw.html", |
||||||
|
"file": "XNDgyMDQ2NTQw_part00.flv", |
||||||
|
"md5": "ffe3f2e435663dc2d1eea34faeff5b5b", |
||||||
|
"params": { "test": false } |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "NBA", |
||||||
|
"url": "http://www.nba.com/video/games/nets/2012/12/04/0021200253-okc-bkn-recap.nba/index.html", |
||||||
|
"file": "0021200253-okc-bkn-recap.nba.mp4", |
||||||
|
"md5": "c0edcfc37607344e2ff8f13c378c88a4" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "JustinTV", |
||||||
|
"url": "http://www.twitch.tv/thegamedevhub/b/296128360", |
||||||
|
"file": "296128360.flv", |
||||||
|
"md5": "ecaa8a790c22a40770901460af191c9a" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "MyVideo", |
||||||
|
"url": "http://www.myvideo.de/watch/8229274/bowling_fail_or_win", |
||||||
|
"file": "8229274.flv", |
||||||
|
"md5": "2d2753e8130479ba2cb7e0a37002053e" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Escapist", |
||||||
|
"url": "http://www.escapistmagazine.com/videos/view/the-escapist-presents/6618-Breaking-Down-Baldurs-Gate", |
||||||
|
"file": "6618-Breaking-Down-Baldurs-Gate.flv", |
||||||
|
"md5": "c6793dbda81388f4264c1ba18684a74d", |
||||||
|
"skip": "Fails with timeout on Travis" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "GooglePlus", |
||||||
|
"url": "https://plus.google.com/u/0/108897254135232129896/posts/ZButuJc6CtH", |
||||||
|
"file": "ZButuJc6CtH.flv" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "FunnyOrDie", |
||||||
|
"url": "http://www.funnyordie.com/videos/0732f586d7/heart-shaped-box-literal-video-version", |
||||||
|
"file": "0732f586d7.mp4", |
||||||
|
"md5": "f647e9e90064b53b6e046e75d0241fbd" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "TweetReel", |
||||||
|
"url": "http://tweetreel.com/?77smq", |
||||||
|
"file": "77smq.mov", |
||||||
|
"md5": "56b4d9ca9de467920f3f99a6d91255d6", |
||||||
|
"info_dict": { |
||||||
|
"uploader": "itszero", |
||||||
|
"uploader_id": "itszero", |
||||||
|
"upload_date": "20091225", |
||||||
|
"description": "Installing Gentoo Linux on Powerbook G4, it turns out the sleep indicator becomes HDD activity indicator :D" |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Steam", |
||||||
|
"url": "http://store.steampowered.com/video/105600/", |
||||||
|
"playlist": [ |
||||||
|
{ |
||||||
|
"file": "81300.flv", |
||||||
|
"md5": "f870007cee7065d7c76b88f0a45ecc07", |
||||||
|
"info_dict": { |
||||||
|
"title": "Terraria 1.1 Trailer" |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"file": "80859.flv", |
||||||
|
"md5": "61aaf31a5c5c3041afb58fb83cbb5751", |
||||||
|
"info_dict": { |
||||||
|
"title": "Terraria Trailer" |
||||||
|
} |
||||||
|
} |
||||||
|
] |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Ustream", |
||||||
|
"url": "http://www.ustream.tv/recorded/20274954", |
||||||
|
"file": "20274954.flv", |
||||||
|
"md5": "088f151799e8f572f84eb62f17d73e5c", |
||||||
|
"info_dict": { |
||||||
|
"title": "Young Americans for Liberty February 7, 2012 2:28 AM" |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "InfoQ", |
||||||
|
"url": "http://www.infoq.com/presentations/A-Few-of-My-Favorite-Python-Things", |
||||||
|
"file": "12-jan-pythonthings.mp4", |
||||||
|
"info_dict": { |
||||||
|
"title": "A Few of My Favorite [Python] Things" |
||||||
|
}, |
||||||
|
"params": { |
||||||
|
"skip_download": true |
||||||
|
} |
||||||
|
} |
||||||
|
] |
Binary file not shown.
@ -1,14 +0,0 @@ |
|||||||
__youtube-dl() |
|
||||||
{ |
|
||||||
local cur prev opts |
|
||||||
COMPREPLY=() |
|
||||||
cur="${COMP_WORDS[COMP_CWORD]}" |
|
||||||
opts="--all-formats --audio-format --audio-quality --auto-number --batch-file --console-title --continue --cookies --dump-user-agent --extract-audio --format --get-description --get-filename --get-format --get-thumbnail --get-title --get-url --help --id --ignore-errors --keep-video --list-extractors --list-formats --literal --match-title --max-downloads --max-quality --netrc --no-continue --no-mtime --no-overwrites --no-part --no-progress --output --password --playlist-end --playlist-start --prefer-free-formats --quiet --rate-limit --reject-title --retries --simulate --skip-download --srt-lang --title --update --user-agent --username --verbose --version --write-description --write-info-json --write-srt" |
|
||||||
|
|
||||||
if [[ ${cur} == * ]] ; then |
|
||||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) |
|
||||||
return 0 |
|
||||||
fi |
|
||||||
} |
|
||||||
|
|
||||||
complete -F __youtube-dl youtube-dl |
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,17 @@ |
|||||||
#!/usr/bin/env python |
#!/usr/bin/env python |
||||||
# -*- coding: utf-8 -*- |
|
||||||
|
|
||||||
import __init__ |
# Execute with |
||||||
|
# $ python youtube_dl/__main__.py (2.6+) |
||||||
|
# $ python -m youtube_dl (2.7+) |
||||||
|
|
||||||
|
import sys |
||||||
|
|
||||||
|
if __package__ is None and not hasattr(sys, "frozen"): |
||||||
|
# direct call of __main__.py |
||||||
|
import os.path |
||||||
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
||||||
|
|
||||||
|
import youtube_dl |
||||||
|
|
||||||
if __name__ == '__main__': |
if __name__ == '__main__': |
||||||
__init__.main() |
youtube_dl.main() |
||||||
|
@ -0,0 +1,160 @@ |
|||||||
|
import json |
||||||
|
import traceback |
||||||
|
import hashlib |
||||||
|
from zipimport import zipimporter |
||||||
|
|
||||||
|
from .utils import * |
||||||
|
from .version import __version__ |
||||||
|
|
||||||
|
def rsa_verify(message, signature, key): |
||||||
|
from struct import pack |
||||||
|
from hashlib import sha256 |
||||||
|
from sys import version_info |
||||||
|
def b(x): |
||||||
|
if version_info[0] == 2: return x |
||||||
|
else: return x.encode('latin1') |
||||||
|
assert(type(message) == type(b(''))) |
||||||
|
block_size = 0 |
||||||
|
n = key[0] |
||||||
|
while n: |
||||||
|
block_size += 1 |
||||||
|
n >>= 8 |
||||||
|
signature = pow(int(signature, 16), key[1], key[0]) |
||||||
|
raw_bytes = [] |
||||||
|
while signature: |
||||||
|
raw_bytes.insert(0, pack("B", signature & 0xFF)) |
||||||
|
signature >>= 8 |
||||||
|
signature = (block_size - len(raw_bytes)) * b('\x00') + b('').join(raw_bytes) |
||||||
|
if signature[0:2] != b('\x00\x01'): return False |
||||||
|
signature = signature[2:] |
||||||
|
if not b('\x00') in signature: return False |
||||||
|
signature = signature[signature.index(b('\x00'))+1:] |
||||||
|
if not signature.startswith(b('\x30\x31\x30\x0D\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20')): return False |
||||||
|
signature = signature[19:] |
||||||
|
if signature != sha256(message).digest(): return False |
||||||
|
return True |
||||||
|
|
||||||
|
def update_self(to_screen, verbose, filename): |
||||||
|
"""Update the program file with the latest version from the repository""" |
||||||
|
|
||||||
|
UPDATE_URL = "http://rg3.github.com/youtube-dl/update/" |
||||||
|
VERSION_URL = UPDATE_URL + 'LATEST_VERSION' |
||||||
|
JSON_URL = UPDATE_URL + 'versions.json' |
||||||
|
UPDATES_RSA_KEY = (0x9d60ee4d8f805312fdb15a62f87b95bd66177b91df176765d13514a0f1754bcd2057295c5b6f1d35daa6742c3ffc9a82d3e118861c207995a8031e151d863c9927e304576bc80692bc8e094896fcf11b66f3e29e04e3a71e9a11558558acea1840aec37fc396fb6b65dc81a1c4144e03bd1c011de62e3f1357b327d08426fe93, 65537) |
||||||
|
|
||||||
|
|
||||||
|
if not isinstance(globals().get('__loader__'), zipimporter) and not hasattr(sys, "frozen"): |
||||||
|
to_screen(u'It looks like you installed youtube-dl with pip, setup.py or a tarball. Please use that to update.') |
||||||
|
return |
||||||
|
|
||||||
|
# Check if there is a new version |
||||||
|
try: |
||||||
|
newversion = compat_urllib_request.urlopen(VERSION_URL).read().decode('utf-8').strip() |
||||||
|
except: |
||||||
|
if verbose: to_screen(compat_str(traceback.format_exc())) |
||||||
|
to_screen(u'ERROR: can\'t find the current version. Please try again later.') |
||||||
|
return |
||||||
|
if newversion == __version__: |
||||||
|
to_screen(u'youtube-dl is up-to-date (' + __version__ + ')') |
||||||
|
return |
||||||
|
|
||||||
|
# Download and check versions info |
||||||
|
try: |
||||||
|
versions_info = compat_urllib_request.urlopen(JSON_URL).read().decode('utf-8') |
||||||
|
versions_info = json.loads(versions_info) |
||||||
|
except: |
||||||
|
if verbose: to_screen(compat_str(traceback.format_exc())) |
||||||
|
to_screen(u'ERROR: can\'t obtain versions info. Please try again later.') |
||||||
|
return |
||||||
|
if not 'signature' in versions_info: |
||||||
|
to_screen(u'ERROR: the versions file is not signed or corrupted. Aborting.') |
||||||
|
return |
||||||
|
signature = versions_info['signature'] |
||||||
|
del versions_info['signature'] |
||||||
|
if not rsa_verify(json.dumps(versions_info, sort_keys=True).encode('utf-8'), signature, UPDATES_RSA_KEY): |
||||||
|
to_screen(u'ERROR: the versions file signature is invalid. Aborting.') |
||||||
|
return |
||||||
|
|
||||||
|
to_screen(u'Updating to version ' + versions_info['latest'] + '...') |
||||||
|
version = versions_info['versions'][versions_info['latest']] |
||||||
|
if version.get('notes'): |
||||||
|
to_screen(u'PLEASE NOTE:') |
||||||
|
for note in version['notes']: |
||||||
|
to_screen(note) |
||||||
|
|
||||||
|
if not os.access(filename, os.W_OK): |
||||||
|
to_screen(u'ERROR: no write permissions on %s' % filename) |
||||||
|
return |
||||||
|
|
||||||
|
# Py2EXE |
||||||
|
if hasattr(sys, "frozen"): |
||||||
|
exe = os.path.abspath(filename) |
||||||
|
directory = os.path.dirname(exe) |
||||||
|
if not os.access(directory, os.W_OK): |
||||||
|
to_screen(u'ERROR: no write permissions on %s' % directory) |
||||||
|
return |
||||||
|
|
||||||
|
try: |
||||||
|
urlh = compat_urllib_request.urlopen(version['exe'][0]) |
||||||
|
newcontent = urlh.read() |
||||||
|
urlh.close() |
||||||
|
except (IOError, OSError) as err: |
||||||
|
if verbose: to_screen(compat_str(traceback.format_exc())) |
||||||
|
to_screen(u'ERROR: unable to download latest version') |
||||||
|
return |
||||||
|
|
||||||
|
newcontent_hash = hashlib.sha256(newcontent).hexdigest() |
||||||
|
if newcontent_hash != version['exe'][1]: |
||||||
|
to_screen(u'ERROR: the downloaded file hash does not match. Aborting.') |
||||||
|
return |
||||||
|
|
||||||
|
try: |
||||||
|
with open(exe + '.new', 'wb') as outf: |
||||||
|
outf.write(newcontent) |
||||||
|
except (IOError, OSError) as err: |
||||||
|
if verbose: to_screen(compat_str(traceback.format_exc())) |
||||||
|
to_screen(u'ERROR: unable to write the new version') |
||||||
|
return |
||||||
|
|
||||||
|
try: |
||||||
|
bat = os.path.join(directory, 'youtube-dl-updater.bat') |
||||||
|
b = open(bat, 'w') |
||||||
|
b.write(""" |
||||||
|
echo Updating youtube-dl... |
||||||
|
ping 127.0.0.1 -n 5 -w 1000 > NUL |
||||||
|
move /Y "%s.new" "%s" |
||||||
|
del "%s" |
||||||
|
\n""" %(exe, exe, bat)) |
||||||
|
b.close() |
||||||
|
|
||||||
|
os.startfile(bat) |
||||||
|
except (IOError, OSError) as err: |
||||||
|
if verbose: to_screen(compat_str(traceback.format_exc())) |
||||||
|
to_screen(u'ERROR: unable to overwrite current version') |
||||||
|
return |
||||||
|
|
||||||
|
# Zip unix package |
||||||
|
elif isinstance(globals().get('__loader__'), zipimporter): |
||||||
|
try: |
||||||
|
urlh = compat_urllib_request.urlopen(version['bin'][0]) |
||||||
|
newcontent = urlh.read() |
||||||
|
urlh.close() |
||||||
|
except (IOError, OSError) as err: |
||||||
|
if verbose: to_screen(compat_str(traceback.format_exc())) |
||||||
|
to_screen(u'ERROR: unable to download latest version') |
||||||
|
return |
||||||
|
|
||||||
|
newcontent_hash = hashlib.sha256(newcontent).hexdigest() |
||||||
|
if newcontent_hash != version['bin'][1]: |
||||||
|
to_screen(u'ERROR: the downloaded file hash does not match. Aborting.') |
||||||
|
return |
||||||
|
|
||||||
|
try: |
||||||
|
with open(filename, 'wb') as outf: |
||||||
|
outf.write(newcontent) |
||||||
|
except (IOError, OSError) as err: |
||||||
|
if verbose: to_screen(compat_str(traceback.format_exc())) |
||||||
|
to_screen(u'ERROR: unable to overwrite current version') |
||||||
|
return |
||||||
|
|
||||||
|
to_screen(u'Updated youtube-dl. Restart youtube-dl to use the new version.') |
@ -0,0 +1,2 @@ |
|||||||
|
|
||||||
|
__version__ = '2013.01.02' |
Loading…
Reference in new issue