mirror of
https://source.netsyms.com/Mirrors/youtube-dl
synced 2026-04-24 15:03:16 +00:00
Compare commits
93 Commits
2013.04.30
...
2013.05.23
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
57adeaea87 | ||
|
|
8f3f1aef05 | ||
|
|
51d2453c7a | ||
|
|
45014296be | ||
|
|
afef36c950 | ||
|
|
b31756c18e | ||
|
|
f008688520 | ||
|
|
5b68ea215b | ||
|
|
b1d568f0bc | ||
|
|
17bd1b2f41 | ||
|
|
5b0d3cc0cd | ||
|
|
d4f76f1674 | ||
|
|
340fa21198 | ||
|
|
de5d66d431 | ||
|
|
7bdb17d4d5 | ||
|
|
419c64b107 | ||
|
|
99a5ae3f8e | ||
|
|
c7563c528b | ||
|
|
e30e9318da | ||
|
|
5c51028d38 | ||
|
|
c1d58e1c67 | ||
|
|
02030ff7fe | ||
|
|
f45c185fa9 | ||
|
|
1bd96c3a60 | ||
|
|
929f85d851 | ||
|
|
98d4a4e6bc | ||
|
|
fb2f83360c | ||
|
|
3c5e7729e1 | ||
|
|
5a853e1423 | ||
|
|
2f58b12dad | ||
|
|
59f4fd4dc6 | ||
|
|
5738240ee8 | ||
|
|
86fd453ea8 | ||
|
|
c83411b9ee | ||
|
|
057c9938a1 | ||
|
|
9259966132 | ||
|
|
b08980412e | ||
|
|
532a1e0429 | ||
|
|
2a36c352a0 | ||
|
|
1a2adf3f49 | ||
|
|
43b62accbb | ||
|
|
be74864ace | ||
|
|
0ae456f08a | ||
|
|
0f75d25991 | ||
|
|
67129e4a15 | ||
|
|
dfb9323cf9 | ||
|
|
7f5bd09baf | ||
|
|
02d5eb935f | ||
|
|
94ca71b7cc | ||
|
|
b338f1b154 | ||
|
|
486f0c9476 | ||
|
|
d96680f58d | ||
|
|
f8602d3242 | ||
|
|
0c021ad171 | ||
|
|
086d7b4500 | ||
|
|
891629c84a | ||
|
|
ea6d901e51 | ||
|
|
4539dd30e6 | ||
|
|
c43e57242e | ||
|
|
db8fd71ca9 | ||
|
|
f4f316881d | ||
|
|
0e16f09474 | ||
|
|
09dd418f53 | ||
|
|
decd1d1737 | ||
|
|
180e689f7e | ||
|
|
7da5556ac2 | ||
|
|
f23a03a89b | ||
|
|
84e4682f0e | ||
|
|
1f99511210 | ||
|
|
0d94f2474c | ||
|
|
480b6c1e8b | ||
|
|
95464f14d1 | ||
|
|
c34407d16c | ||
|
|
5e34d2ebbf | ||
|
|
815dd2ffa8 | ||
|
|
ecd5fb49c5 | ||
|
|
b86174e7a3 | ||
|
|
2e2038dc35 | ||
|
|
46bfb42258 | ||
|
|
feecf22511 | ||
|
|
4c4f15eb78 | ||
|
|
104ccdb8b4 | ||
|
|
6ccff79594 | ||
|
|
aed523ecc1 | ||
|
|
d496a75d0a | ||
|
|
5c01dd1e73 | ||
|
|
11d9224e3b | ||
|
|
34c29ba1d7 | ||
|
|
6cd657f9f2 | ||
|
|
4ae9e55822 | ||
|
|
8749b71273 | ||
|
|
dbc50fdf82 | ||
|
|
e74c504f91 |
12
Makefile
12
Makefile
@@ -9,9 +9,19 @@ cleanall: clean
|
|||||||
PREFIX=/usr/local
|
PREFIX=/usr/local
|
||||||
BINDIR=$(PREFIX)/bin
|
BINDIR=$(PREFIX)/bin
|
||||||
MANDIR=$(PREFIX)/man
|
MANDIR=$(PREFIX)/man
|
||||||
SYSCONFDIR=/etc
|
|
||||||
PYTHON=/usr/bin/env python
|
PYTHON=/usr/bin/env python
|
||||||
|
|
||||||
|
# set SYSCONFDIR to /etc if PREFIX=/usr or PREFIX=/usr/local
|
||||||
|
ifeq ($(PREFIX),/usr)
|
||||||
|
SYSCONFDIR=/etc
|
||||||
|
else
|
||||||
|
ifeq ($(PREFIX),/usr/local)
|
||||||
|
SYSCONFDIR=/etc
|
||||||
|
else
|
||||||
|
SYSCONFDIR=$(PREFIX)/etc
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
install: youtube-dl youtube-dl.1 youtube-dl.bash-completion
|
install: youtube-dl youtube-dl.1 youtube-dl.bash-completion
|
||||||
install -d $(DESTDIR)$(BINDIR)
|
install -d $(DESTDIR)$(BINDIR)
|
||||||
install -m 755 youtube-dl $(DESTDIR)$(BINDIR)
|
install -m 755 youtube-dl $(DESTDIR)$(BINDIR)
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ which means you can modify it, redistribute it or use it however you like.
|
|||||||
is restricted to one domain
|
is restricted to one domain
|
||||||
--list-extractors List all supported extractors and the URLs they
|
--list-extractors List all supported extractors and the URLs they
|
||||||
would handle
|
would handle
|
||||||
--proxy None Use the specified HTTP/HTTPS proxy
|
--proxy URL Use the specified HTTP/HTTPS proxy
|
||||||
|
--no-check-certificate Suppress HTTPS certificate validation.
|
||||||
|
|
||||||
## Video Selection:
|
## Video Selection:
|
||||||
--playlist-start NUMBER playlist video to start at (default is 1)
|
--playlist-start NUMBER playlist video to start at (default is 1)
|
||||||
@@ -84,6 +85,7 @@ which means you can modify it, redistribute it or use it however you like.
|
|||||||
file modification time
|
file modification time
|
||||||
--write-description write video description to a .description file
|
--write-description write video description to a .description file
|
||||||
--write-info-json write video metadata to a .info.json file
|
--write-info-json write video metadata to a .info.json file
|
||||||
|
--write-thumbnail write thumbnail image to disk
|
||||||
|
|
||||||
## Verbosity / Simulation Options:
|
## Verbosity / Simulation Options:
|
||||||
-q, --quiet activates quiet mode
|
-q, --quiet activates quiet mode
|
||||||
@@ -92,6 +94,7 @@ which means you can modify it, redistribute it or use it however you like.
|
|||||||
--skip-download do not download the video
|
--skip-download do not download the video
|
||||||
-g, --get-url simulate, quiet but print URL
|
-g, --get-url simulate, quiet but print URL
|
||||||
-e, --get-title simulate, quiet but print title
|
-e, --get-title simulate, quiet but print title
|
||||||
|
--get-id simulate, quiet but print id
|
||||||
--get-thumbnail simulate, quiet but print thumbnail URL
|
--get-thumbnail simulate, quiet but print thumbnail URL
|
||||||
--get-description simulate, quiet but print video description
|
--get-description simulate, quiet but print video description
|
||||||
--get-filename simulate, quiet but print output filename
|
--get-filename simulate, quiet but print output filename
|
||||||
@@ -113,7 +116,7 @@ which means you can modify it, redistribute it or use it however you like.
|
|||||||
-F, --list-formats list all available formats (currently youtube
|
-F, --list-formats list all available formats (currently youtube
|
||||||
only)
|
only)
|
||||||
--write-sub write subtitle file (currently youtube only)
|
--write-sub write subtitle file (currently youtube only)
|
||||||
--only-sub downloads only the subtitles (no video)
|
--only-sub [deprecated] alias of --skip-download
|
||||||
--all-subs downloads all the available subtitles of the
|
--all-subs downloads all the available subtitles of the
|
||||||
video (currently youtube only)
|
video (currently youtube only)
|
||||||
--list-subs lists all available subtitles for the video
|
--list-subs lists all available subtitles for the video
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class FakeDownloader(FileDownloader):
|
|||||||
self.params = parameters
|
self.params = parameters
|
||||||
def to_screen(self, s):
|
def to_screen(self, s):
|
||||||
print(s)
|
print(s)
|
||||||
def trouble(self, s):
|
def trouble(self, s, tb=None):
|
||||||
raise Exception(s)
|
raise Exception(s)
|
||||||
def extract_info(self, url):
|
def extract_info(self, url):
|
||||||
self.result.append(url)
|
self.result.append(url)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|||||||
|
|
||||||
from youtube_dl.InfoExtractors import YoutubeIE
|
from youtube_dl.InfoExtractors import YoutubeIE
|
||||||
from youtube_dl.utils import *
|
from youtube_dl.utils import *
|
||||||
|
from youtube_dl import FileDownloader
|
||||||
|
|
||||||
PARAMETERS_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "parameters.json")
|
PARAMETERS_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "parameters.json")
|
||||||
with io.open(PARAMETERS_FILE, encoding='utf-8') as pf:
|
with io.open(PARAMETERS_FILE, encoding='utf-8') as pf:
|
||||||
@@ -24,13 +25,13 @@ proxy_handler = compat_urllib_request.ProxyHandler()
|
|||||||
opener = compat_urllib_request.build_opener(proxy_handler, cookie_processor, YoutubeDLHandler())
|
opener = compat_urllib_request.build_opener(proxy_handler, cookie_processor, YoutubeDLHandler())
|
||||||
compat_urllib_request.install_opener(opener)
|
compat_urllib_request.install_opener(opener)
|
||||||
|
|
||||||
class FakeDownloader(object):
|
class FakeDownloader(FileDownloader):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.result = []
|
self.result = []
|
||||||
self.params = parameters
|
self.params = parameters
|
||||||
def to_screen(self, s):
|
def to_screen(self, s):
|
||||||
print(s)
|
print(s)
|
||||||
def trouble(self, s):
|
def trouble(self, s, tb=None):
|
||||||
raise Exception(s)
|
raise Exception(s)
|
||||||
def download(self, x):
|
def download(self, x):
|
||||||
self.result.append(x)
|
self.result.append(x)
|
||||||
|
|||||||
147
test/tests.json
147
test/tests.json
@@ -112,9 +112,8 @@
|
|||||||
{
|
{
|
||||||
"name": "Escapist",
|
"name": "Escapist",
|
||||||
"url": "http://www.escapistmagazine.com/videos/view/the-escapist-presents/6618-Breaking-Down-Baldurs-Gate",
|
"url": "http://www.escapistmagazine.com/videos/view/the-escapist-presents/6618-Breaking-Down-Baldurs-Gate",
|
||||||
"file": "6618-Breaking-Down-Baldurs-Gate.flv",
|
"file": "6618-Breaking-Down-Baldurs-Gate.mp4",
|
||||||
"md5": "c6793dbda81388f4264c1ba18684a74d",
|
"md5": "c6793dbda81388f4264c1ba18684a74d"
|
||||||
"skip": "Fails with timeout on Travis"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "GooglePlus",
|
"name": "GooglePlus",
|
||||||
@@ -153,7 +152,8 @@
|
|||||||
"file": "20274954.flv",
|
"file": "20274954.flv",
|
||||||
"md5": "088f151799e8f572f84eb62f17d73e5c",
|
"md5": "088f151799e8f572f84eb62f17d73e5c",
|
||||||
"info_dict": {
|
"info_dict": {
|
||||||
"title": "Young Americans for Liberty February 7, 2012 2:28 AM"
|
"title": "Young Americans for Liberty February 7, 2012 2:28 AM",
|
||||||
|
"uploader": "Young Americans for Liberty"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -344,8 +344,143 @@
|
|||||||
"file": "17258355236.mp4",
|
"file": "17258355236.mp4",
|
||||||
"md5": "7c6a514d691b034ccf8567999e9e88a3",
|
"md5": "7c6a514d691b034ccf8567999e9e88a3",
|
||||||
"info_dict": {
|
"info_dict": {
|
||||||
"title": "A sample video from LeeAnn. (If you need an idea..."
|
"title": "Calling all Pris! - A sample video from LeeAnn. (If you need an idea..."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "SoundcloudSet",
|
||||||
|
"url":"https://soundcloud.com/the-concept-band/sets/the-royal-concept-ep",
|
||||||
|
"playlist":[
|
||||||
|
{
|
||||||
|
"file":"30510138.mp3",
|
||||||
|
"md5":"f9136bf103901728f29e419d2c70f55d",
|
||||||
|
"info_dict": {
|
||||||
|
"title":"D-D-Dance"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file":"47127625.mp3",
|
||||||
|
"md5":"09b6758a018470570f8fd423c9453dd8",
|
||||||
|
"info_dict": {
|
||||||
|
"title":"The Royal Concept - Gimme Twice"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file":"47127627.mp3",
|
||||||
|
"md5":"154abd4e418cea19c3b901f1e1306d9c",
|
||||||
|
"info_dict": {
|
||||||
|
"title":"Goldrushed"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file":"47127629.mp3",
|
||||||
|
"md5":"2f5471edc79ad3f33a683153e96a79c1",
|
||||||
|
"info_dict": {
|
||||||
|
"title":"In the End"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file":"47127631.mp3",
|
||||||
|
"md5":"f9ba87aa940af7213f98949254f1c6e2",
|
||||||
|
"info_dict": {
|
||||||
|
"title":"Knocked Up"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file":"75206121.mp3",
|
||||||
|
"md5":"f9d1fe9406717e302980c30de4af9353",
|
||||||
|
"info_dict": {
|
||||||
|
"title":"World On Fire"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"Bandcamp",
|
||||||
|
"url":"http://youtube-dl.bandcamp.com/track/youtube-dl-test-song",
|
||||||
|
"file":"1812978515.mp3",
|
||||||
|
"md5":"cdeb30cdae1921719a3cbcab696ef53c",
|
||||||
|
"info_dict": {
|
||||||
|
"title":"youtube-dl test song \"'/\\ä↭"
|
||||||
|
},
|
||||||
|
"skip": "There is a limit of 200 free downloads / month for the test song"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "RedTube",
|
||||||
|
"url": "http://www.redtube.com/66418",
|
||||||
|
"file": "66418.mp4",
|
||||||
|
"md5": "7b8c22b5e7098a3e1c09709df1126d2d",
|
||||||
|
"info_dict":{
|
||||||
|
"title":"Sucked on a toilet"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Photobucket",
|
||||||
|
"url": "http://media.photobucket.com/user/rachaneronas/media/TiredofLinkBuildingTryBacklinkMyDomaincom_zpsc0c3b9fa.mp4.html?filters[term]=search&filters[primary]=videos&filters[secondary]=images&sort=1&o=0",
|
||||||
|
"file": "zpsc0c3b9fa.mp4",
|
||||||
|
"md5": "7dabfb92b0a31f6c16cebc0f8e60ff99",
|
||||||
|
"info_dict":{
|
||||||
|
"title":"Tired of Link Building? Try BacklinkMyDomain.com!"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ina",
|
||||||
|
"url": "www.ina.fr/video/I12055569/francois-hollande-je-crois-que-c-est-clair-video.html",
|
||||||
|
"file": "I12055569.mp4",
|
||||||
|
"md5": "a667021bf2b41f8dc6049479d9bb38a3",
|
||||||
|
"info_dict":{
|
||||||
|
"title":"François Hollande \"Je crois que c'est clair\""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Yahoo",
|
||||||
|
"url": "http://screen.yahoo.com/obama-celebrates-iraq-victory-27592561.html",
|
||||||
|
"file": "27592561.flv",
|
||||||
|
"md5": "c6179bed843512823fd284fa2e7f012d",
|
||||||
|
"info_dict": {
|
||||||
|
"title": "Obama Celebrates Iraq Victory"
|
||||||
|
},
|
||||||
|
"skip": "Requires rtmpdump"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Howcast",
|
||||||
|
"url": "http://www.howcast.com/videos/390161-How-to-Tie-a-Square-Knot-Properly",
|
||||||
|
"file": "390161.mp4",
|
||||||
|
"md5": "1d7ba54e2c9d7dc6935ef39e00529138",
|
||||||
|
"info_dict":{
|
||||||
|
"title":"How to Tie a Square Knot Properly",
|
||||||
|
"description":"The square knot, also known as the reef knot, is one of the oldest, most basic knots to tie, and can be used in many different ways. Here's the proper way to tie a square knot."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Vine",
|
||||||
|
"url": "https://vine.co/v/b9KOOWX7HUx",
|
||||||
|
"file": "b9KOOWX7HUx.mp4",
|
||||||
|
"md5": "2f36fed6235b16da96ce9b4dc890940d",
|
||||||
|
"info_dict":{
|
||||||
|
"title": "Chicken.",
|
||||||
|
"uploader": "Jack Dorsey"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Flickr",
|
||||||
|
"url": "http://www.flickr.com/photos/forestwander-nature-pictures/5645318632/in/photostream/",
|
||||||
|
"file": "5645318632.mp4",
|
||||||
|
"md5": "6fdc01adbc89d72fc9c4f15b4a4ba87b",
|
||||||
|
"info_dict":{
|
||||||
|
"title": "Dark Hollow Waterfalls",
|
||||||
|
"uploader_id": "forestwander-nature-pictures",
|
||||||
|
"description": "Waterfalls in the Springtime at Dark Hollow Waterfalls. These are located just off of Skyline Drive in Virginia. They are only about 6/10 of a mile hike but it is a pretty steep hill and a good climb back up."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Teamcoco",
|
||||||
|
"url": "http://teamcoco.com/video/louis-ck-interview-george-w-bush",
|
||||||
|
"file": "19705.mp4",
|
||||||
|
"md5": "27b6f7527da5acf534b15f21b032656e",
|
||||||
|
"info_dict":{
|
||||||
|
"title": "Louis C.K. Interview Pt. 1 11/3/11",
|
||||||
|
"description": "Louis C.K. got starstruck by George W. Bush, so what? Part one."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import math
|
|||||||
import io
|
import io
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import shutil
|
||||||
import socket
|
import socket
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
@@ -53,6 +54,7 @@ class FileDownloader(object):
|
|||||||
quiet: Do not print messages to stdout.
|
quiet: Do not print messages to stdout.
|
||||||
forceurl: Force printing final URL.
|
forceurl: Force printing final URL.
|
||||||
forcetitle: Force printing title.
|
forcetitle: Force printing title.
|
||||||
|
forceid: Force printing ID.
|
||||||
forcethumbnail: Force printing thumbnail URL.
|
forcethumbnail: Force printing thumbnail URL.
|
||||||
forcedescription: Force printing description.
|
forcedescription: Force printing description.
|
||||||
forcefilename: Force printing final filename.
|
forcefilename: Force printing final filename.
|
||||||
@@ -79,8 +81,8 @@ class FileDownloader(object):
|
|||||||
updatetime: Use the Last-modified header to set output file timestamps.
|
updatetime: Use the Last-modified header to set output file timestamps.
|
||||||
writedescription: Write the video description to a .description file
|
writedescription: Write the video description to a .description file
|
||||||
writeinfojson: Write the video description to a .info.json file
|
writeinfojson: Write the video description to a .info.json file
|
||||||
|
writethumbnail: Write the thumbnail image to a file
|
||||||
writesubtitles: Write the video subtitles to a file
|
writesubtitles: Write the video subtitles to a file
|
||||||
onlysubtitles: Downloads only the subtitles of the video
|
|
||||||
allsubtitles: Downloads all the subtitles of the video
|
allsubtitles: Downloads all the subtitles of the video
|
||||||
listsubtitles: Lists all available subtitles for the video
|
listsubtitles: Lists all available subtitles for the video
|
||||||
subtitlesformat: Subtitle format [sbv/srt] (default=srt)
|
subtitlesformat: Subtitle format [sbv/srt] (default=srt)
|
||||||
@@ -90,6 +92,7 @@ class FileDownloader(object):
|
|||||||
min_filesize: Skip files smaller than this size
|
min_filesize: Skip files smaller than this size
|
||||||
max_filesize: Skip files larger than this size
|
max_filesize: Skip files larger than this size
|
||||||
daterange: A DateRange object, download only if the upload_date is in the range.
|
daterange: A DateRange object, download only if the upload_date is in the range.
|
||||||
|
skip_download: Skip the actual download of the video file
|
||||||
"""
|
"""
|
||||||
|
|
||||||
params = None
|
params = None
|
||||||
@@ -345,12 +348,13 @@ class FileDownloader(object):
|
|||||||
"""Report download progress."""
|
"""Report download progress."""
|
||||||
if self.params.get('noprogress', False):
|
if self.params.get('noprogress', False):
|
||||||
return
|
return
|
||||||
|
clear_line = (u'\x1b[K' if sys.stderr.isatty() and os.name != 'nt' else u'')
|
||||||
if self.params.get('progress_with_newline', False):
|
if self.params.get('progress_with_newline', False):
|
||||||
self.to_screen(u'[download] %s of %s at %s ETA %s' %
|
self.to_screen(u'[download] %s of %s at %s ETA %s' %
|
||||||
(percent_str, data_len_str, speed_str, eta_str))
|
(percent_str, data_len_str, speed_str, eta_str))
|
||||||
else:
|
else:
|
||||||
self.to_screen(u'\r[download] %s of %s at %s ETA %s' %
|
self.to_screen(u'\r%s[download] %s of %s at %s ETA %s' %
|
||||||
(percent_str, data_len_str, speed_str, eta_str), skip_eol=True)
|
(clear_line, percent_str, data_len_str, speed_str, eta_str), skip_eol=True)
|
||||||
self.to_cons_title(u'youtube-dl - %s of %s at %s ETA %s' %
|
self.to_cons_title(u'youtube-dl - %s of %s at %s ETA %s' %
|
||||||
(percent_str.strip(), data_len_str.strip(), speed_str.strip(), eta_str.strip()))
|
(percent_str.strip(), data_len_str.strip(), speed_str.strip(), eta_str.strip()))
|
||||||
|
|
||||||
@@ -432,47 +436,45 @@ class FileDownloader(object):
|
|||||||
return u'[download] %s upload date is not in range %s' % (date_from_str(date).isoformat(), dateRange)
|
return u'[download] %s upload date is not in range %s' % (date_from_str(date).isoformat(), dateRange)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def extract_info(self, url, download = True, ie_name = None):
|
def extract_info(self, url, download=True, ie_key=None, extra_info={}):
|
||||||
'''
|
'''
|
||||||
Returns a list with a dictionary for each video we find.
|
Returns a list with a dictionary for each video we find.
|
||||||
If 'download', also downloads the videos.
|
If 'download', also downloads the videos.
|
||||||
|
extra_info is a dict containing the extra values to add to each result
|
||||||
'''
|
'''
|
||||||
suitable_found = False
|
|
||||||
|
|
||||||
#We copy the original list
|
if ie_key:
|
||||||
ies = list(self._ies)
|
ie = get_info_extractor(ie_key)()
|
||||||
|
ie.set_downloader(self)
|
||||||
if ie_name is not None:
|
ies = [ie]
|
||||||
#We put in the first place the given info extractor
|
else:
|
||||||
first_ie = get_info_extractor(ie_name)()
|
ies = self._ies
|
||||||
first_ie.set_downloader(self)
|
|
||||||
ies.insert(0, first_ie)
|
|
||||||
|
|
||||||
for ie in ies:
|
for ie in ies:
|
||||||
# Go to next InfoExtractor if not suitable
|
|
||||||
if not ie.suitable(url):
|
if not ie.suitable(url):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Warn if the _WORKING attribute is False
|
|
||||||
if not ie.working():
|
if not ie.working():
|
||||||
self.report_warning(u'the program functionality for this site has been marked as broken, '
|
self.report_warning(u'The program functionality for this site has been marked as broken, '
|
||||||
u'and will probably not work. If you want to go on, use the -i option.')
|
u'and will probably not work.')
|
||||||
|
|
||||||
# Suitable InfoExtractor found
|
|
||||||
suitable_found = True
|
|
||||||
|
|
||||||
# Extract information from URL and process it
|
|
||||||
try:
|
try:
|
||||||
ie_results = ie.extract(url)
|
ie_result = ie.extract(url)
|
||||||
if ie_results is None: # Finished already (backwards compatibility; listformats and friends should be moved here)
|
if ie_result is None: # Finished already (backwards compatibility; listformats and friends should be moved here)
|
||||||
break
|
break
|
||||||
results = []
|
if isinstance(ie_result, list):
|
||||||
for ie_result in ie_results:
|
# Backwards compatibility: old IE result format
|
||||||
if not 'extractor' in ie_result:
|
for result in ie_result:
|
||||||
#The extractor has already been set somewhere else
|
result.update(extra_info)
|
||||||
ie_result['extractor'] = ie.IE_NAME
|
ie_result = {
|
||||||
results.append(self.process_ie_result(ie_result, download))
|
'_type': 'compat_list',
|
||||||
return results
|
'entries': ie_result,
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
ie_result.update(extra_info)
|
||||||
|
if 'extractor' not in ie_result:
|
||||||
|
ie_result['extractor'] = ie.IE_NAME
|
||||||
|
return self.process_ie_result(ie_result, download=download)
|
||||||
except ExtractorError as de: # An error we somewhat expected
|
except ExtractorError as de: # An error we somewhat expected
|
||||||
self.report_error(compat_str(de), de.format_traceback())
|
self.report_error(compat_str(de), de.format_traceback())
|
||||||
break
|
break
|
||||||
@@ -482,33 +484,36 @@ class FileDownloader(object):
|
|||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
if not suitable_found:
|
else:
|
||||||
self.report_error(u'no suitable InfoExtractor: %s' % url)
|
self.report_error(u'no suitable InfoExtractor: %s' % url)
|
||||||
|
|
||||||
def process_ie_result(self, ie_result, download = True):
|
def process_ie_result(self, ie_result, download=True, extra_info={}):
|
||||||
"""
|
"""
|
||||||
Take the result of the ie and return a list of videos.
|
Take the result of the ie(may be modified) and resolve all unresolved
|
||||||
For url elements it will search the suitable ie and get the videos
|
references (URLs, playlist items).
|
||||||
For playlist elements it will process each of the elements of the 'entries' key
|
|
||||||
|
|
||||||
It will also download the videos if 'download'.
|
It will also download the videos if 'download'.
|
||||||
|
Returns the resolved ie_result.
|
||||||
"""
|
"""
|
||||||
result_type = ie_result.get('_type', 'video') #If not given we suppose it's a video, support the dafault old system
|
|
||||||
|
result_type = ie_result.get('_type', 'video') # If not given we suppose it's a video, support the default old system
|
||||||
if result_type == 'video':
|
if result_type == 'video':
|
||||||
if 'playlist' not in ie_result:
|
if 'playlist' not in ie_result:
|
||||||
#It isn't part of a playlist
|
# It isn't part of a playlist
|
||||||
ie_result['playlist'] = None
|
ie_result['playlist'] = None
|
||||||
ie_result['playlist_index'] = None
|
ie_result['playlist_index'] = None
|
||||||
if download:
|
if download:
|
||||||
#Do the download:
|
|
||||||
self.process_info(ie_result)
|
self.process_info(ie_result)
|
||||||
return ie_result
|
return ie_result
|
||||||
elif result_type == 'url':
|
elif result_type == 'url':
|
||||||
#We get the video pointed by the url
|
# We have to add extra_info to the results because it may be
|
||||||
result = self.extract_info(ie_result['url'], download, ie_name = ie_result['ie_key'])[0]
|
# contained in a playlist
|
||||||
return result
|
return self.extract_info(ie_result['url'],
|
||||||
|
download,
|
||||||
|
ie_key=ie_result.get('ie_key'),
|
||||||
|
extra_info=extra_info)
|
||||||
elif result_type == 'playlist':
|
elif result_type == 'playlist':
|
||||||
#We process each entry in the playlist
|
# We process each entry in the playlist
|
||||||
playlist = ie_result.get('title', None) or ie_result.get('id', None)
|
playlist = ie_result.get('title', None) or ie_result.get('id', None)
|
||||||
self.to_screen(u'[download] Downloading playlist: %s' % playlist)
|
self.to_screen(u'[download] Downloading playlist: %s' % playlist)
|
||||||
|
|
||||||
@@ -530,23 +535,35 @@ class FileDownloader(object):
|
|||||||
|
|
||||||
for i,entry in enumerate(entries,1):
|
for i,entry in enumerate(entries,1):
|
||||||
self.to_screen(u'[download] Downloading video #%s of %s' %(i, n_entries))
|
self.to_screen(u'[download] Downloading video #%s of %s' %(i, n_entries))
|
||||||
entry_result = self.process_ie_result(entry, False)
|
extra = {
|
||||||
entry_result['playlist'] = playlist
|
'playlist': playlist,
|
||||||
entry_result['playlist_index'] = i + playliststart
|
'playlist_index': i + playliststart,
|
||||||
#We must do the download here to correctly set the 'playlist' key
|
}
|
||||||
if download:
|
entry_result = self.process_ie_result(entry,
|
||||||
self.process_info(entry_result)
|
download=download,
|
||||||
|
extra_info=extra)
|
||||||
playlist_results.append(entry_result)
|
playlist_results.append(entry_result)
|
||||||
result = ie_result.copy()
|
ie_result['entries'] = playlist_results
|
||||||
result['entries'] = playlist_results
|
return ie_result
|
||||||
return result
|
elif result_type == 'compat_list':
|
||||||
|
def _fixup(r):
|
||||||
|
r.setdefault('extractor', ie_result['extractor'])
|
||||||
|
return r
|
||||||
|
ie_result['entries'] = [
|
||||||
|
self.process_ie_result(_fixup(r), download=download)
|
||||||
|
for r in ie_result['entries']
|
||||||
|
]
|
||||||
|
return ie_result
|
||||||
|
else:
|
||||||
|
raise Exception('Invalid result type: %s' % result_type)
|
||||||
|
|
||||||
def process_info(self, info_dict):
|
def process_info(self, info_dict):
|
||||||
"""Process a single dictionary returned by an InfoExtractor."""
|
"""Process a single resolved IE result."""
|
||||||
|
|
||||||
|
assert info_dict.get('_type', 'video') == 'video'
|
||||||
#We increment the download the download count here to match the previous behaviour.
|
#We increment the download the download count here to match the previous behaviour.
|
||||||
self.increment_downloads()
|
self.increment_downloads()
|
||||||
|
|
||||||
info_dict['fulltitle'] = info_dict['title']
|
info_dict['fulltitle'] = info_dict['title']
|
||||||
if len(info_dict['title']) > 200:
|
if len(info_dict['title']) > 200:
|
||||||
info_dict['title'] = info_dict['title'][:197] + u'...'
|
info_dict['title'] = info_dict['title'][:197] + u'...'
|
||||||
@@ -572,6 +589,8 @@ class FileDownloader(object):
|
|||||||
# Forced printings
|
# Forced printings
|
||||||
if self.params.get('forcetitle', False):
|
if self.params.get('forcetitle', False):
|
||||||
compat_print(info_dict['title'])
|
compat_print(info_dict['title'])
|
||||||
|
if self.params.get('forceid', False):
|
||||||
|
compat_print(info_dict['id'])
|
||||||
if self.params.get('forceurl', False):
|
if self.params.get('forceurl', False):
|
||||||
compat_print(info_dict['url'])
|
compat_print(info_dict['url'])
|
||||||
if self.params.get('forcethumbnail', False) and 'thumbnail' in info_dict:
|
if self.params.get('forcethumbnail', False) and 'thumbnail' in info_dict:
|
||||||
@@ -592,7 +611,7 @@ class FileDownloader(object):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
dn = os.path.dirname(encodeFilename(filename))
|
dn = os.path.dirname(encodeFilename(filename))
|
||||||
if dn != '' and not os.path.exists(dn): # dn is already encoded
|
if dn != '' and not os.path.exists(dn):
|
||||||
os.makedirs(dn)
|
os.makedirs(dn)
|
||||||
except (OSError, IOError) as err:
|
except (OSError, IOError) as err:
|
||||||
self.report_error(u'unable to create directory ' + compat_str(err))
|
self.report_error(u'unable to create directory ' + compat_str(err))
|
||||||
@@ -625,8 +644,6 @@ class FileDownloader(object):
|
|||||||
except (OSError, IOError):
|
except (OSError, IOError):
|
||||||
self.report_error(u'Cannot write subtitles file ' + descfn)
|
self.report_error(u'Cannot write subtitles file ' + descfn)
|
||||||
return
|
return
|
||||||
if self.params.get('onlysubtitles', False):
|
|
||||||
return
|
|
||||||
|
|
||||||
if self.params.get('allsubtitles', False) and 'subtitles' in info_dict and info_dict['subtitles']:
|
if self.params.get('allsubtitles', False) and 'subtitles' in info_dict and info_dict['subtitles']:
|
||||||
subtitles = info_dict['subtitles']
|
subtitles = info_dict['subtitles']
|
||||||
@@ -644,8 +661,6 @@ class FileDownloader(object):
|
|||||||
except (OSError, IOError):
|
except (OSError, IOError):
|
||||||
self.report_error(u'Cannot write subtitles file ' + descfn)
|
self.report_error(u'Cannot write subtitles file ' + descfn)
|
||||||
return
|
return
|
||||||
if self.params.get('onlysubtitles', False):
|
|
||||||
return
|
|
||||||
|
|
||||||
if self.params.get('writeinfojson', False):
|
if self.params.get('writeinfojson', False):
|
||||||
infofn = filename + u'.info.json'
|
infofn = filename + u'.info.json'
|
||||||
@@ -657,6 +672,20 @@ class FileDownloader(object):
|
|||||||
self.report_error(u'Cannot write metadata to JSON file ' + infofn)
|
self.report_error(u'Cannot write metadata to JSON file ' + infofn)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if self.params.get('writethumbnail', False):
|
||||||
|
if 'thumbnail' in info_dict:
|
||||||
|
thumb_format = info_dict['thumbnail'].rpartition(u'/')[2].rpartition(u'.')[2]
|
||||||
|
if not thumb_format:
|
||||||
|
thumb_format = 'jpg'
|
||||||
|
thumb_filename = filename.rpartition('.')[0] + u'.' + thumb_format
|
||||||
|
self.to_screen(u'[%s] %s: Downloading thumbnail ...' %
|
||||||
|
(info_dict['extractor'], info_dict['id']))
|
||||||
|
uf = compat_urllib_request.urlopen(info_dict['thumbnail'])
|
||||||
|
with open(thumb_filename, 'wb') as thumbf:
|
||||||
|
shutil.copyfileobj(uf, thumbf)
|
||||||
|
self.to_screen(u'[%s] %s: Writing thumbnail to: %s' %
|
||||||
|
(info_dict['extractor'], info_dict['id'], thumb_filename))
|
||||||
|
|
||||||
if not self.params.get('skip_download', False):
|
if not self.params.get('skip_download', False):
|
||||||
if self.params.get('nooverwrites', False) and os.path.exists(encodeFilename(filename)):
|
if self.params.get('nooverwrites', False) and os.path.exists(encodeFilename(filename)):
|
||||||
success = True
|
success = True
|
||||||
@@ -719,7 +748,7 @@ class FileDownloader(object):
|
|||||||
except (IOError, OSError):
|
except (IOError, OSError):
|
||||||
self.report_warning(u'Unable to remove downloaded video file')
|
self.report_warning(u'Unable to remove downloaded video file')
|
||||||
|
|
||||||
def _download_with_rtmpdump(self, filename, url, player_url, page_url, play_path):
|
def _download_with_rtmpdump(self, filename, url, player_url, page_url, play_path, tc_url):
|
||||||
self.report_destination(filename)
|
self.report_destination(filename)
|
||||||
tmpfilename = self.temp_name(filename)
|
tmpfilename = self.temp_name(filename)
|
||||||
|
|
||||||
@@ -734,12 +763,15 @@ class FileDownloader(object):
|
|||||||
# the connection was interrumpted and resuming appears to be
|
# the connection was interrumpted and resuming appears to be
|
||||||
# possible. This is part of rtmpdump's normal usage, AFAIK.
|
# possible. This is part of rtmpdump's normal usage, AFAIK.
|
||||||
basic_args = ['rtmpdump', '-q', '-r', url, '-o', tmpfilename]
|
basic_args = ['rtmpdump', '-q', '-r', url, '-o', tmpfilename]
|
||||||
|
if self.params.get('verbose', False): basic_args[1] = '-v'
|
||||||
if player_url is not None:
|
if player_url is not None:
|
||||||
basic_args += ['-W', player_url]
|
basic_args += ['-W', player_url]
|
||||||
if page_url is not None:
|
if page_url is not None:
|
||||||
basic_args += ['--pageUrl', page_url]
|
basic_args += ['--pageUrl', page_url]
|
||||||
if play_path is not None:
|
if play_path is not None:
|
||||||
basic_args += ['-y', play_path]
|
basic_args += ['-y', play_path]
|
||||||
|
if tc_url is not None:
|
||||||
|
basic_args += ['--tcUrl', url]
|
||||||
args = basic_args + [[], ['-e', '-k', '1']][self.params.get('continuedl', False)]
|
args = basic_args + [[], ['-e', '-k', '1']][self.params.get('continuedl', False)]
|
||||||
if self.params.get('verbose', False):
|
if self.params.get('verbose', False):
|
||||||
try:
|
try:
|
||||||
@@ -795,7 +827,8 @@ class FileDownloader(object):
|
|||||||
return self._download_with_rtmpdump(filename, url,
|
return self._download_with_rtmpdump(filename, url,
|
||||||
info_dict.get('player_url', None),
|
info_dict.get('player_url', None),
|
||||||
info_dict.get('page_url', None),
|
info_dict.get('page_url', None),
|
||||||
info_dict.get('play_path', None))
|
info_dict.get('play_path', None),
|
||||||
|
info_dict.get('tc_url', None))
|
||||||
|
|
||||||
tmpfilename = self.temp_name(filename)
|
tmpfilename = self.temp_name(filename)
|
||||||
stream = None
|
stream = None
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -85,8 +85,9 @@ class FFmpegPostProcessor(PostProcessor):
|
|||||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
stdout,stderr = p.communicate()
|
stdout,stderr = p.communicate()
|
||||||
if p.returncode != 0:
|
if p.returncode != 0:
|
||||||
|
stderr = stderr.decode('utf-8', 'replace')
|
||||||
msg = stderr.strip().split('\n')[-1]
|
msg = stderr.strip().split('\n')[-1]
|
||||||
raise FFmpegPostProcessorError(msg.decode('utf-8', 'replace'))
|
raise FFmpegPostProcessorError(msg)
|
||||||
|
|
||||||
def _ffmpeg_filename_argument(self, fn):
|
def _ffmpeg_filename_argument(self, fn):
|
||||||
# ffmpeg broke --, see https://ffmpeg.org/trac/ffmpeg/ticket/2127 for details
|
# ffmpeg broke --, see https://ffmpeg.org/trac/ffmpeg/ticket/2127 for details
|
||||||
@@ -188,6 +189,11 @@ class FFmpegExtractAudioPP(FFmpegPostProcessor):
|
|||||||
|
|
||||||
prefix, sep, ext = path.rpartition(u'.') # not os.path.splitext, since the latter does not work on unicode in all setups
|
prefix, sep, ext = path.rpartition(u'.') # not os.path.splitext, since the latter does not work on unicode in all setups
|
||||||
new_path = prefix + sep + extension
|
new_path = prefix + sep + extension
|
||||||
|
|
||||||
|
# If we download foo.mp3 and convert it to... foo.mp3, then don't delete foo.mp3, silly.
|
||||||
|
if new_path == path:
|
||||||
|
self._nopostoverwrites = True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if self._nopostoverwrites and os.path.exists(encodeFilename(new_path)):
|
if self._nopostoverwrites and os.path.exists(encodeFilename(new_path)):
|
||||||
self._downloader.to_screen(u'[youtube] Post-process file %s exists, skipping' % new_path)
|
self._downloader.to_screen(u'[youtube] Post-process file %s exists, skipping' % new_path)
|
||||||
@@ -210,7 +216,7 @@ class FFmpegExtractAudioPP(FFmpegPostProcessor):
|
|||||||
self._downloader.to_stderr(u'WARNING: Cannot update utime of audio file')
|
self._downloader.to_stderr(u'WARNING: Cannot update utime of audio file')
|
||||||
|
|
||||||
information['filepath'] = new_path
|
information['filepath'] = new_path
|
||||||
return False,information
|
return self._nopostoverwrites,information
|
||||||
|
|
||||||
class FFmpegVideoConvertor(FFmpegPostProcessor):
|
class FFmpegVideoConvertor(FFmpegPostProcessor):
|
||||||
def __init__(self, downloader=None,preferedformat=None):
|
def __init__(self, downloader=None,preferedformat=None):
|
||||||
|
|||||||
@@ -25,10 +25,14 @@ __authors__ = (
|
|||||||
'Jeff Crouse',
|
'Jeff Crouse',
|
||||||
'Osama Khalid',
|
'Osama Khalid',
|
||||||
'Michael Walter',
|
'Michael Walter',
|
||||||
|
'M. Yasoob Ullah Khalid',
|
||||||
|
'Julien Fraichard',
|
||||||
|
'Johny Mo Swag',
|
||||||
)
|
)
|
||||||
|
|
||||||
__license__ = 'Public Domain'
|
__license__ = 'Public Domain'
|
||||||
|
|
||||||
|
import codecs
|
||||||
import getpass
|
import getpass
|
||||||
import optparse
|
import optparse
|
||||||
import os
|
import os
|
||||||
@@ -146,7 +150,8 @@ def parseOpts(overrideArguments=None):
|
|||||||
general.add_option('--list-extractors',
|
general.add_option('--list-extractors',
|
||||||
action='store_true', dest='list_extractors',
|
action='store_true', dest='list_extractors',
|
||||||
help='List all supported extractors and the URLs they would handle', default=False)
|
help='List all supported extractors and the URLs they would handle', default=False)
|
||||||
general.add_option('--proxy', dest='proxy', default=None, help='Use the specified HTTP/HTTPS proxy')
|
general.add_option('--proxy', dest='proxy', default=None, help='Use the specified HTTP/HTTPS proxy', metavar='URL')
|
||||||
|
general.add_option('--no-check-certificate', action='store_true', dest='no_check_certificate', default=False, help='Suppress HTTPS certificate validation.')
|
||||||
general.add_option('--test', action='store_true', dest='test', default=False, help=optparse.SUPPRESS_HELP)
|
general.add_option('--test', action='store_true', dest='test', default=False, help=optparse.SUPPRESS_HELP)
|
||||||
|
|
||||||
selection.add_option('--playlist-start',
|
selection.add_option('--playlist-start',
|
||||||
@@ -186,8 +191,8 @@ def parseOpts(overrideArguments=None):
|
|||||||
action='store_true', dest='writesubtitles',
|
action='store_true', dest='writesubtitles',
|
||||||
help='write subtitle file (currently youtube only)', default=False)
|
help='write subtitle file (currently youtube only)', default=False)
|
||||||
video_format.add_option('--only-sub',
|
video_format.add_option('--only-sub',
|
||||||
action='store_true', dest='onlysubtitles',
|
action='store_true', dest='skip_download',
|
||||||
help='downloads only the subtitles (no video)', default=False)
|
help='[deprecated] alias of --skip-download', default=False)
|
||||||
video_format.add_option('--all-subs',
|
video_format.add_option('--all-subs',
|
||||||
action='store_true', dest='allsubtitles',
|
action='store_true', dest='allsubtitles',
|
||||||
help='downloads all the available subtitles of the video (currently youtube only)', default=False)
|
help='downloads all the available subtitles of the video (currently youtube only)', default=False)
|
||||||
@@ -211,6 +216,8 @@ def parseOpts(overrideArguments=None):
|
|||||||
action='store_true', dest='geturl', help='simulate, quiet but print URL', default=False)
|
action='store_true', dest='geturl', help='simulate, quiet but print URL', default=False)
|
||||||
verbosity.add_option('-e', '--get-title',
|
verbosity.add_option('-e', '--get-title',
|
||||||
action='store_true', dest='gettitle', help='simulate, quiet but print title', default=False)
|
action='store_true', dest='gettitle', help='simulate, quiet but print title', default=False)
|
||||||
|
verbosity.add_option('--get-id',
|
||||||
|
action='store_true', dest='getid', help='simulate, quiet but print id', default=False)
|
||||||
verbosity.add_option('--get-thumbnail',
|
verbosity.add_option('--get-thumbnail',
|
||||||
action='store_true', dest='getthumbnail',
|
action='store_true', dest='getthumbnail',
|
||||||
help='simulate, quiet but print thumbnail URL', default=False)
|
help='simulate, quiet but print thumbnail URL', default=False)
|
||||||
@@ -284,6 +291,9 @@ def parseOpts(overrideArguments=None):
|
|||||||
filesystem.add_option('--write-info-json',
|
filesystem.add_option('--write-info-json',
|
||||||
action='store_true', dest='writeinfojson',
|
action='store_true', dest='writeinfojson',
|
||||||
help='write video metadata to a .info.json file', default=False)
|
help='write video metadata to a .info.json file', default=False)
|
||||||
|
filesystem.add_option('--write-thumbnail',
|
||||||
|
action='store_true', dest='writethumbnail',
|
||||||
|
help='write thumbnail image to disk', default=False)
|
||||||
|
|
||||||
|
|
||||||
postproc.add_option('-x', '--extract-audio', action='store_true', dest='extractaudio', default=False,
|
postproc.add_option('-x', '--extract-audio', action='store_true', dest='extractaudio', default=False,
|
||||||
@@ -331,6 +341,11 @@ def parseOpts(overrideArguments=None):
|
|||||||
return parser, opts, args
|
return parser, opts, args
|
||||||
|
|
||||||
def _real_main(argv=None):
|
def _real_main(argv=None):
|
||||||
|
# Compatibility fixes for Windows
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
# https://github.com/rg3/youtube-dl/issues/820
|
||||||
|
codecs.register(lambda name: codecs.lookup('utf-8') if name == 'cp65001' else None)
|
||||||
|
|
||||||
parser, opts, args = parseOpts(argv)
|
parser, opts, args = parseOpts(argv)
|
||||||
|
|
||||||
# Open appropriate CookieJar
|
# Open appropriate CookieJar
|
||||||
@@ -385,7 +400,8 @@ def _real_main(argv=None):
|
|||||||
if 'http' in proxies and 'https' not in proxies:
|
if 'http' in proxies and 'https' not in proxies:
|
||||||
proxies['https'] = proxies['http']
|
proxies['https'] = proxies['http']
|
||||||
proxy_handler = compat_urllib_request.ProxyHandler(proxies)
|
proxy_handler = compat_urllib_request.ProxyHandler(proxies)
|
||||||
opener = compat_urllib_request.build_opener(proxy_handler, cookie_processor, YoutubeDLHandler())
|
https_handler = make_HTTPS_handler(opts)
|
||||||
|
opener = compat_urllib_request.build_opener(https_handler, proxy_handler, cookie_processor, YoutubeDLHandler())
|
||||||
compat_urllib_request.install_opener(opener)
|
compat_urllib_request.install_opener(opener)
|
||||||
socket.setdefaulttimeout(300) # 5 minutes should be enough (famous last words)
|
socket.setdefaulttimeout(300) # 5 minutes should be enough (famous last words)
|
||||||
|
|
||||||
@@ -481,15 +497,16 @@ def _real_main(argv=None):
|
|||||||
'usenetrc': opts.usenetrc,
|
'usenetrc': opts.usenetrc,
|
||||||
'username': opts.username,
|
'username': opts.username,
|
||||||
'password': opts.password,
|
'password': opts.password,
|
||||||
'quiet': (opts.quiet or opts.geturl or opts.gettitle or opts.getthumbnail or opts.getdescription or opts.getfilename or opts.getformat),
|
'quiet': (opts.quiet or opts.geturl or opts.gettitle or opts.getid or opts.getthumbnail or opts.getdescription or opts.getfilename or opts.getformat),
|
||||||
'forceurl': opts.geturl,
|
'forceurl': opts.geturl,
|
||||||
'forcetitle': opts.gettitle,
|
'forcetitle': opts.gettitle,
|
||||||
|
'forceid': opts.getid,
|
||||||
'forcethumbnail': opts.getthumbnail,
|
'forcethumbnail': opts.getthumbnail,
|
||||||
'forcedescription': opts.getdescription,
|
'forcedescription': opts.getdescription,
|
||||||
'forcefilename': opts.getfilename,
|
'forcefilename': opts.getfilename,
|
||||||
'forceformat': opts.getformat,
|
'forceformat': opts.getformat,
|
||||||
'simulate': opts.simulate,
|
'simulate': opts.simulate,
|
||||||
'skip_download': (opts.skip_download or opts.simulate or opts.geturl or opts.gettitle or opts.getthumbnail or opts.getdescription or opts.getfilename or opts.getformat),
|
'skip_download': (opts.skip_download or opts.simulate or opts.geturl or opts.gettitle or opts.getid or opts.getthumbnail or opts.getdescription or opts.getfilename or opts.getformat),
|
||||||
'format': opts.format,
|
'format': opts.format,
|
||||||
'format_limit': opts.format_limit,
|
'format_limit': opts.format_limit,
|
||||||
'listformats': opts.listformats,
|
'listformats': opts.listformats,
|
||||||
@@ -513,8 +530,8 @@ def _real_main(argv=None):
|
|||||||
'updatetime': opts.updatetime,
|
'updatetime': opts.updatetime,
|
||||||
'writedescription': opts.writedescription,
|
'writedescription': opts.writedescription,
|
||||||
'writeinfojson': opts.writeinfojson,
|
'writeinfojson': opts.writeinfojson,
|
||||||
|
'writethumbnail': opts.writethumbnail,
|
||||||
'writesubtitles': opts.writesubtitles,
|
'writesubtitles': opts.writesubtitles,
|
||||||
'onlysubtitles': opts.onlysubtitles,
|
|
||||||
'allsubtitles': opts.allsubtitles,
|
'allsubtitles': opts.allsubtitles,
|
||||||
'listsubtitles': opts.listsubtitles,
|
'listsubtitles': opts.listsubtitles,
|
||||||
'subtitlesformat': opts.subtitlesformat,
|
'subtitlesformat': opts.subtitlesformat,
|
||||||
@@ -529,7 +546,7 @@ def _real_main(argv=None):
|
|||||||
'keepvideo': opts.keepvideo,
|
'keepvideo': opts.keepvideo,
|
||||||
'min_filesize': opts.min_filesize,
|
'min_filesize': opts.min_filesize,
|
||||||
'max_filesize': opts.max_filesize,
|
'max_filesize': opts.max_filesize,
|
||||||
'daterange': date
|
'daterange': date,
|
||||||
})
|
})
|
||||||
|
|
||||||
if opts.verbose:
|
if opts.verbose:
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import errno
|
||||||
import gzip
|
import gzip
|
||||||
import io
|
import io
|
||||||
import json
|
import json
|
||||||
@@ -149,6 +150,10 @@ try:
|
|||||||
except NameError:
|
except NameError:
|
||||||
compat_chr = chr
|
compat_chr = chr
|
||||||
|
|
||||||
|
def compat_ord(c):
|
||||||
|
if type(c) is int: return c
|
||||||
|
else: return ord(c)
|
||||||
|
|
||||||
std_headers = {
|
std_headers = {
|
||||||
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:10.0) Gecko/20100101 Firefox/10.0',
|
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:10.0) Gecko/20100101 Firefox/10.0',
|
||||||
'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
|
'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
|
||||||
@@ -334,12 +339,20 @@ def sanitize_open(filename, open_mode):
|
|||||||
stream = open(encodeFilename(filename), open_mode)
|
stream = open(encodeFilename(filename), open_mode)
|
||||||
return (stream, filename)
|
return (stream, filename)
|
||||||
except (IOError, OSError) as err:
|
except (IOError, OSError) as err:
|
||||||
# In case of error, try to remove win32 forbidden chars
|
if err.errno in (errno.EACCES,):
|
||||||
filename = re.sub(u'[/<>:"\\|\\\\?\\*]', u'#', filename)
|
raise
|
||||||
|
|
||||||
# An exception here should be caught in the caller
|
# In case of error, try to remove win32 forbidden chars
|
||||||
stream = open(encodeFilename(filename), open_mode)
|
alt_filename = os.path.join(
|
||||||
return (stream, filename)
|
re.sub(u'[/<>:"\\|\\\\?\\*]', u'#', path_part)
|
||||||
|
for path_part in os.path.split(filename)
|
||||||
|
)
|
||||||
|
if alt_filename == filename:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
# An exception here should be caught in the caller
|
||||||
|
stream = open(encodeFilename(filename), open_mode)
|
||||||
|
return (stream, alt_filename)
|
||||||
|
|
||||||
|
|
||||||
def timeconvert(timestr):
|
def timeconvert(timestr):
|
||||||
@@ -430,6 +443,28 @@ def decodeOption(optval):
|
|||||||
assert isinstance(optval, compat_str)
|
assert isinstance(optval, compat_str)
|
||||||
return optval
|
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(opts):
|
||||||
|
if sys.version_info < (3,2):
|
||||||
|
# Python's 2.x handler is very simplistic
|
||||||
|
return compat_urllib_request.HTTPSHandler()
|
||||||
|
else:
|
||||||
|
import ssl
|
||||||
|
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
|
||||||
|
context.set_default_verify_paths()
|
||||||
|
|
||||||
|
context.verify_mode = (ssl.CERT_NONE
|
||||||
|
if opts.no_check_certificate
|
||||||
|
else ssl.CERT_REQUIRED)
|
||||||
|
return compat_urllib_request.HTTPSHandler(context=context)
|
||||||
|
|
||||||
class ExtractorError(Exception):
|
class ExtractorError(Exception):
|
||||||
"""Error during info extraction."""
|
"""Error during info extraction."""
|
||||||
def __init__(self, msg, tb=None):
|
def __init__(self, msg, tb=None):
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
|
|
||||||
__version__ = '2013.04.30'
|
__version__ = '2013.05.23'
|
||||||
|
|||||||
Reference in New Issue
Block a user