mirror of
https://github.com/ytdl-org/youtube-dl.git
synced 2025-08-02 18:39:51 -05:00
Compare commits
85 Commits
2013.05.01
...
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 | ||
![]() |
e74c504f91 |
12
Makefile
12
Makefile
@@ -9,9 +9,19 @@ cleanall: clean
|
||||
PREFIX=/usr/local
|
||||
BINDIR=$(PREFIX)/bin
|
||||
MANDIR=$(PREFIX)/man
|
||||
SYSCONFDIR=/etc
|
||||
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 -d $(DESTDIR)$(BINDIR)
|
||||
install -m 755 youtube-dl $(DESTDIR)$(BINDIR)
|
||||
|
@@ -32,6 +32,7 @@ which means you can modify it, redistribute it or use it however you like.
|
||||
--list-extractors List all supported extractors and the URLs they
|
||||
would handle
|
||||
--proxy URL Use the specified HTTP/HTTPS proxy
|
||||
--no-check-certificate Suppress HTTPS certificate validation.
|
||||
|
||||
## Video Selection:
|
||||
--playlist-start NUMBER playlist video to start at (default is 1)
|
||||
@@ -93,6 +94,7 @@ which means you can modify it, redistribute it or use it however you like.
|
||||
--skip-download do not download the video
|
||||
-g, --get-url simulate, quiet but print URL
|
||||
-e, --get-title simulate, quiet but print title
|
||||
--get-id simulate, quiet but print id
|
||||
--get-thumbnail simulate, quiet but print thumbnail URL
|
||||
--get-description simulate, quiet but print video description
|
||||
--get-filename simulate, quiet but print output filename
|
||||
@@ -114,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
|
||||
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
|
||||
video (currently youtube only)
|
||||
--list-subs lists all available subtitles for the video
|
||||
|
@@ -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.utils import *
|
||||
from youtube_dl import FileDownloader
|
||||
|
||||
PARAMETERS_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "parameters.json")
|
||||
with io.open(PARAMETERS_FILE, encoding='utf-8') as pf:
|
||||
@@ -24,7 +25,7 @@ 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):
|
||||
class FakeDownloader(FileDownloader):
|
||||
def __init__(self):
|
||||
self.result = []
|
||||
self.params = parameters
|
||||
|
@@ -112,9 +112,8 @@
|
||||
{
|
||||
"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"
|
||||
"file": "6618-Breaking-Down-Baldurs-Gate.mp4",
|
||||
"md5": "c6793dbda81388f4264c1ba18684a74d"
|
||||
},
|
||||
{
|
||||
"name": "GooglePlus",
|
||||
@@ -153,7 +152,8 @@
|
||||
"file": "20274954.flv",
|
||||
"md5": "088f151799e8f572f84eb62f17d73e5c",
|
||||
"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,7 +344,7 @@
|
||||
"file": "17258355236.mp4",
|
||||
"md5": "7c6a514d691b034ccf8567999e9e88a3",
|
||||
"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..."
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -394,6 +394,93 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"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."
|
||||
}
|
||||
}
|
||||
|
||||
]
|
||||
|
@@ -54,6 +54,7 @@ class FileDownloader(object):
|
||||
quiet: Do not print messages to stdout.
|
||||
forceurl: Force printing final URL.
|
||||
forcetitle: Force printing title.
|
||||
forceid: Force printing ID.
|
||||
forcethumbnail: Force printing thumbnail URL.
|
||||
forcedescription: Force printing description.
|
||||
forcefilename: Force printing final filename.
|
||||
@@ -82,7 +83,6 @@ class FileDownloader(object):
|
||||
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
|
||||
onlysubtitles: Downloads only the subtitles of the video
|
||||
allsubtitles: Downloads all the subtitles of the video
|
||||
listsubtitles: Lists all available subtitles for the video
|
||||
subtitlesformat: Subtitle format [sbv/srt] (default=srt)
|
||||
@@ -92,6 +92,7 @@ class FileDownloader(object):
|
||||
min_filesize: Skip files smaller 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.
|
||||
skip_download: Skip the actual download of the video file
|
||||
"""
|
||||
|
||||
params = None
|
||||
@@ -435,47 +436,45 @@ class FileDownloader(object):
|
||||
return u'[download] %s upload date is not in range %s' % (date_from_str(date).isoformat(), dateRange)
|
||||
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.
|
||||
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
|
||||
ies = list(self._ies)
|
||||
|
||||
if ie_name is not None:
|
||||
#We put in the first place the given info extractor
|
||||
first_ie = get_info_extractor(ie_name)()
|
||||
first_ie.set_downloader(self)
|
||||
ies.insert(0, first_ie)
|
||||
if ie_key:
|
||||
ie = get_info_extractor(ie_key)()
|
||||
ie.set_downloader(self)
|
||||
ies = [ie]
|
||||
else:
|
||||
ies = self._ies
|
||||
|
||||
for ie in ies:
|
||||
# Go to next InfoExtractor if not suitable
|
||||
if not ie.suitable(url):
|
||||
continue
|
||||
|
||||
# Warn if the _WORKING attribute is False
|
||||
if not ie.working():
|
||||
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.')
|
||||
self.report_warning(u'The program functionality for this site has been marked as broken, '
|
||||
u'and will probably not work.')
|
||||
|
||||
# Suitable InfoExtractor found
|
||||
suitable_found = True
|
||||
|
||||
# Extract information from URL and process it
|
||||
try:
|
||||
ie_results = ie.extract(url)
|
||||
if ie_results is None: # Finished already (backwards compatibility; listformats and friends should be moved here)
|
||||
ie_result = ie.extract(url)
|
||||
if ie_result is None: # Finished already (backwards compatibility; listformats and friends should be moved here)
|
||||
break
|
||||
results = []
|
||||
for ie_result in ie_results:
|
||||
if not 'extractor' in ie_result:
|
||||
#The extractor has already been set somewhere else
|
||||
ie_result['extractor'] = ie.IE_NAME
|
||||
results.append(self.process_ie_result(ie_result, download))
|
||||
return results
|
||||
if isinstance(ie_result, list):
|
||||
# Backwards compatibility: old IE result format
|
||||
for result in ie_result:
|
||||
result.update(extra_info)
|
||||
ie_result = {
|
||||
'_type': 'compat_list',
|
||||
'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
|
||||
self.report_error(compat_str(de), de.format_traceback())
|
||||
break
|
||||
@@ -485,33 +484,36 @@ class FileDownloader(object):
|
||||
break
|
||||
else:
|
||||
raise
|
||||
if not suitable_found:
|
||||
self.report_error(u'no suitable InfoExtractor: %s' % url)
|
||||
else:
|
||||
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.
|
||||
For url elements it will search the suitable ie and get the videos
|
||||
For playlist elements it will process each of the elements of the 'entries' key
|
||||
|
||||
Take the result of the ie(may be modified) and resolve all unresolved
|
||||
references (URLs, playlist items).
|
||||
|
||||
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 '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_index'] = None
|
||||
if download:
|
||||
#Do the download:
|
||||
self.process_info(ie_result)
|
||||
return ie_result
|
||||
elif result_type == 'url':
|
||||
#We get the video pointed by the url
|
||||
result = self.extract_info(ie_result['url'], download, ie_name = ie_result['ie_key'])[0]
|
||||
return result
|
||||
# We have to add extra_info to the results because it may be
|
||||
# contained in a playlist
|
||||
return self.extract_info(ie_result['url'],
|
||||
download,
|
||||
ie_key=ie_result.get('ie_key'),
|
||||
extra_info=extra_info)
|
||||
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)
|
||||
self.to_screen(u'[download] Downloading playlist: %s' % playlist)
|
||||
|
||||
@@ -533,23 +535,35 @@ class FileDownloader(object):
|
||||
|
||||
for i,entry in enumerate(entries,1):
|
||||
self.to_screen(u'[download] Downloading video #%s of %s' %(i, n_entries))
|
||||
entry_result = self.process_ie_result(entry, False)
|
||||
entry_result['playlist'] = playlist
|
||||
entry_result['playlist_index'] = i + playliststart
|
||||
#We must do the download here to correctly set the 'playlist' key
|
||||
if download:
|
||||
self.process_info(entry_result)
|
||||
extra = {
|
||||
'playlist': playlist,
|
||||
'playlist_index': i + playliststart,
|
||||
}
|
||||
entry_result = self.process_ie_result(entry,
|
||||
download=download,
|
||||
extra_info=extra)
|
||||
playlist_results.append(entry_result)
|
||||
result = ie_result.copy()
|
||||
result['entries'] = playlist_results
|
||||
return result
|
||||
ie_result['entries'] = playlist_results
|
||||
return ie_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):
|
||||
"""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.
|
||||
self.increment_downloads()
|
||||
|
||||
|
||||
info_dict['fulltitle'] = info_dict['title']
|
||||
if len(info_dict['title']) > 200:
|
||||
info_dict['title'] = info_dict['title'][:197] + u'...'
|
||||
@@ -575,6 +589,8 @@ class FileDownloader(object):
|
||||
# Forced printings
|
||||
if self.params.get('forcetitle', False):
|
||||
compat_print(info_dict['title'])
|
||||
if self.params.get('forceid', False):
|
||||
compat_print(info_dict['id'])
|
||||
if self.params.get('forceurl', False):
|
||||
compat_print(info_dict['url'])
|
||||
if self.params.get('forcethumbnail', False) and 'thumbnail' in info_dict:
|
||||
@@ -595,7 +611,7 @@ class FileDownloader(object):
|
||||
|
||||
try:
|
||||
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)
|
||||
except (OSError, IOError) as err:
|
||||
self.report_error(u'unable to create directory ' + compat_str(err))
|
||||
@@ -628,8 +644,6 @@ class FileDownloader(object):
|
||||
except (OSError, IOError):
|
||||
self.report_error(u'Cannot write subtitles file ' + descfn)
|
||||
return
|
||||
if self.params.get('onlysubtitles', False):
|
||||
return
|
||||
|
||||
if self.params.get('allsubtitles', False) and 'subtitles' in info_dict and info_dict['subtitles']:
|
||||
subtitles = info_dict['subtitles']
|
||||
@@ -647,8 +661,6 @@ class FileDownloader(object):
|
||||
except (OSError, IOError):
|
||||
self.report_error(u'Cannot write subtitles file ' + descfn)
|
||||
return
|
||||
if self.params.get('onlysubtitles', False):
|
||||
return
|
||||
|
||||
if self.params.get('writeinfojson', False):
|
||||
infofn = filename + u'.info.json'
|
||||
@@ -736,7 +748,7 @@ class FileDownloader(object):
|
||||
except (IOError, OSError):
|
||||
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)
|
||||
tmpfilename = self.temp_name(filename)
|
||||
|
||||
@@ -751,12 +763,15 @@ class FileDownloader(object):
|
||||
# the connection was interrumpted and resuming appears to be
|
||||
# possible. This is part of rtmpdump's normal usage, AFAIK.
|
||||
basic_args = ['rtmpdump', '-q', '-r', url, '-o', tmpfilename]
|
||||
if self.params.get('verbose', False): basic_args[1] = '-v'
|
||||
if player_url is not None:
|
||||
basic_args += ['-W', player_url]
|
||||
if page_url is not None:
|
||||
basic_args += ['--pageUrl', page_url]
|
||||
if play_path is not None:
|
||||
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)]
|
||||
if self.params.get('verbose', False):
|
||||
try:
|
||||
@@ -812,7 +827,8 @@ class FileDownloader(object):
|
||||
return self._download_with_rtmpdump(filename, url,
|
||||
info_dict.get('player_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)
|
||||
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)
|
||||
stdout,stderr = p.communicate()
|
||||
if p.returncode != 0:
|
||||
stderr = stderr.decode('utf-8', 'replace')
|
||||
msg = stderr.strip().split('\n')[-1]
|
||||
raise FFmpegPostProcessorError(msg.decode('utf-8', 'replace'))
|
||||
raise FFmpegPostProcessorError(msg)
|
||||
|
||||
def _ffmpeg_filename_argument(self, fn):
|
||||
# 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
|
||||
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:
|
||||
if self._nopostoverwrites and os.path.exists(encodeFilename(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')
|
||||
|
||||
information['filepath'] = new_path
|
||||
return False,information
|
||||
return self._nopostoverwrites,information
|
||||
|
||||
class FFmpegVideoConvertor(FFmpegPostProcessor):
|
||||
def __init__(self, downloader=None,preferedformat=None):
|
||||
|
@@ -25,10 +25,14 @@ __authors__ = (
|
||||
'Jeff Crouse',
|
||||
'Osama Khalid',
|
||||
'Michael Walter',
|
||||
'M. Yasoob Ullah Khalid',
|
||||
'Julien Fraichard',
|
||||
'Johny Mo Swag',
|
||||
)
|
||||
|
||||
__license__ = 'Public Domain'
|
||||
|
||||
import codecs
|
||||
import getpass
|
||||
import optparse
|
||||
import os
|
||||
@@ -147,6 +151,7 @@ def parseOpts(overrideArguments=None):
|
||||
action='store_true', dest='list_extractors',
|
||||
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', 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)
|
||||
|
||||
selection.add_option('--playlist-start',
|
||||
@@ -186,8 +191,8 @@ def parseOpts(overrideArguments=None):
|
||||
action='store_true', dest='writesubtitles',
|
||||
help='write subtitle file (currently youtube only)', default=False)
|
||||
video_format.add_option('--only-sub',
|
||||
action='store_true', dest='onlysubtitles',
|
||||
help='downloads only the subtitles (no video)', default=False)
|
||||
action='store_true', dest='skip_download',
|
||||
help='[deprecated] alias of --skip-download', default=False)
|
||||
video_format.add_option('--all-subs',
|
||||
action='store_true', dest='allsubtitles',
|
||||
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)
|
||||
verbosity.add_option('-e', '--get-title',
|
||||
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',
|
||||
action='store_true', dest='getthumbnail',
|
||||
help='simulate, quiet but print thumbnail URL', default=False)
|
||||
@@ -334,6 +341,11 @@ def parseOpts(overrideArguments=None):
|
||||
return parser, opts, args
|
||||
|
||||
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)
|
||||
|
||||
# Open appropriate CookieJar
|
||||
@@ -388,7 +400,8 @@ def _real_main(argv=None):
|
||||
if 'http' in proxies and 'https' not in proxies:
|
||||
proxies['https'] = proxies['http']
|
||||
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)
|
||||
socket.setdefaulttimeout(300) # 5 minutes should be enough (famous last words)
|
||||
|
||||
@@ -484,15 +497,16 @@ def _real_main(argv=None):
|
||||
'usenetrc': opts.usenetrc,
|
||||
'username': opts.username,
|
||||
'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,
|
||||
'forcetitle': opts.gettitle,
|
||||
'forceid': opts.getid,
|
||||
'forcethumbnail': opts.getthumbnail,
|
||||
'forcedescription': opts.getdescription,
|
||||
'forcefilename': opts.getfilename,
|
||||
'forceformat': opts.getformat,
|
||||
'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_limit': opts.format_limit,
|
||||
'listformats': opts.listformats,
|
||||
@@ -518,7 +532,6 @@ def _real_main(argv=None):
|
||||
'writeinfojson': opts.writeinfojson,
|
||||
'writethumbnail': opts.writethumbnail,
|
||||
'writesubtitles': opts.writesubtitles,
|
||||
'onlysubtitles': opts.onlysubtitles,
|
||||
'allsubtitles': opts.allsubtitles,
|
||||
'listsubtitles': opts.listsubtitles,
|
||||
'subtitlesformat': opts.subtitlesformat,
|
||||
|
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import errno
|
||||
import gzip
|
||||
import io
|
||||
import json
|
||||
@@ -149,6 +150,10 @@ try:
|
||||
except NameError:
|
||||
compat_chr = chr
|
||||
|
||||
def compat_ord(c):
|
||||
if type(c) is int: return c
|
||||
else: return ord(c)
|
||||
|
||||
std_headers = {
|
||||
'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',
|
||||
@@ -334,12 +339,20 @@ def sanitize_open(filename, open_mode):
|
||||
stream = open(encodeFilename(filename), open_mode)
|
||||
return (stream, filename)
|
||||
except (IOError, OSError) as err:
|
||||
# In case of error, try to remove win32 forbidden chars
|
||||
filename = re.sub(u'[/<>:"\\|\\\\?\\*]', u'#', filename)
|
||||
if err.errno in (errno.EACCES,):
|
||||
raise
|
||||
|
||||
# An exception here should be caught in the caller
|
||||
stream = open(encodeFilename(filename), open_mode)
|
||||
return (stream, filename)
|
||||
# In case of error, try to remove win32 forbidden chars
|
||||
alt_filename = os.path.join(
|
||||
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):
|
||||
@@ -430,6 +443,28 @@ def decodeOption(optval):
|
||||
assert isinstance(optval, compat_str)
|
||||
return optval
|
||||
|
||||
def formatSeconds(secs):
|
||||
if secs > 3600:
|
||||
return '%d:%02d:%02d' % (secs // 3600, (secs % 3600) // 60, secs % 60)
|
||||
elif secs > 60:
|
||||
return '%d:%02d' % (secs // 60, secs % 60)
|
||||
else:
|
||||
return '%d' % secs
|
||||
|
||||
def make_HTTPS_handler(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):
|
||||
"""Error during info extraction."""
|
||||
def __init__(self, msg, tb=None):
|
||||
|
@@ -1,2 +1,2 @@
|
||||
|
||||
__version__ = '2013.05.01'
|
||||
__version__ = '2013.05.23'
|
||||
|
Reference in New Issue
Block a user