Compare commits

..

67 Commits

Author SHA1 Message Date
Philipp Hagemeister
df928d500f release 2014.10.15 2014-10-15 12:39:30 +02:00
Jaime Marquínez Ferrándiz
a72cbfacf0 [ted] Add support for external videos (fixes #3948) 2014-10-15 12:24:11 +02:00
Philipp Hagemeister
62a164e713 [mixcloud] Output downloading progress 2014-10-15 00:53:54 +02:00
Sergey M․
5f58165def [extractor/common] Fix dumping requests with long file abspath on Windows 2014-10-14 21:43:48 +07:00
Jaime Marquínez Ferrándiz
bd4e40df1a [brightcove] Add a test for playlists 2014-10-13 16:26:53 +02:00
Sergey M․
1419fafd36 [condenast] Add support for embedded videos (Closes #3929) 2014-10-13 19:59:35 +07:00
Philipp Hagemeister
9b36dcbd65 release 2014.10.13 2014-10-13 10:12:51 +02:00
Philipp Hagemeister
2aefb886fa [ffmpeg] Improve format merging (Closes #3935) 2014-10-13 10:12:43 +02:00
Philipp Hagemeister
72961c2a8a Merge remote-tracking branch 'Dineshs91/KontrTube-fix' 2014-10-13 10:09:57 +02:00
Philipp Hagemeister
4c1ce987b0 [huffpost] Modernize 2014-10-13 10:08:59 +02:00
dinesh
8a2300a597 [kontrtube] Fix video title extraction 2014-10-13 10:03:55 +05:30
Philipp Hagemeister
1cc887cbf0 [youtube] Add support for format 278 2014-10-13 00:09:19 +02:00
Philipp Hagemeister
203fb43f36 [youtube] Download DASH manifest by default (Closes #3887) 2014-10-13 00:03:08 +02:00
Philipp Hagemeister
4d7b03f1f2 [zsh-completion] Ignore generated file (#3890) 2014-10-12 23:31:15 +02:00
Philipp Hagemeister
72ebb5e4b4 Merge remote-tracking branch 'xu-cheng/zsh-completion' 2014-10-12 23:30:17 +02:00
Philipp Hagemeister
8450c15c25 [options] Consistent formatting and general niceness 2014-10-12 23:10:11 +02:00
Philipp Hagemeister
b88b45e46c [options] Mention login in --username documentation (#3753) 2014-10-12 22:45:36 +02:00
Philipp Hagemeister
2417dc1715 release 2014.10.12 2014-10-12 22:25:18 +02:00
Naglis Jonaitis
23d83ad4d5 [niconico] Fix ignored --netrc flag
See issue #3753
2014-10-12 23:18:42 +03:00
Jaime Marquínez Ferrándiz
772ece3571 Merge pull request #3932 from Dineshs91/ndr-fix
Description changed
2014-10-12 17:54:51 +02:00
dinesh
2c9f31188b Description changed 2014-10-12 20:09:12 +05:30
Naglis Jonaitis
d18be55533 [theonion] Fix a small mistake in string formatting 2014-10-12 15:47:31 +03:00
Naglis Jonaitis
ac20fc047a [theonion] Add new extractor (closes #3928) 2014-10-12 15:42:35 +03:00
Jaime Marquínez Ferrándiz
b4c3c8c172 [mixcloud] Fix metadata extraction (fixes #3930) 2014-10-12 13:06:31 +02:00
Jaime Marquínez Ferrándiz
3357110a4c [vimeo] Make the protocol mandatory in the url (fixes #3926)
If it's missing, it will be correctly handled by the generic IE.
2014-10-11 22:26:26 +02:00
Sergey M.
e29fdedb45 Merge pull request #3923 from Dineshs91/howstuffworks-fix
Replace 404 url
2014-10-12 01:48:11 +07:00
Sergey M․
4828703f14 [googleplus] Modernize and extract all formats 2014-10-12 01:44:13 +07:00
Sergey M.
afe08e0d4a Merge pull request #3924 from Dineshs91/googleplus-fix
Fix download error in GooglePlus
2014-10-12 00:48:58 +07:00
dinesh
071420e136 Fix download error in GooglePlus 2014-10-11 21:10:53 +05:30
dinesh
f4cf848d1d Replace 404 url 2014-10-11 15:59:42 +05:30
Sergey M.
b7b2ca6e2b Merge pull request #3921 from Dineshs91/ndr-fix
Fix ndr.de outdated test url
2014-10-11 16:07:39 +07:00
dinesh
1409704afa Fix ndr.de outdated test url 2014-10-11 12:20:13 +05:30
Sergey M.
c8e390c2b0 Merge pull request #3911 from Dockheas23/master
KeyError on initialising YoutubeDL in python3 #3910
2014-10-10 22:28:18 +07:00
Sergey M․
823f1e015a [yahoo] Wipe out yahoo news extractor 2014-10-10 22:18:37 +07:00
Sergey M․
3c06d3715e [yahoo] Generalize, support arbitrary subdomains, support iframe videos, capture error message (Closes #2470) 2014-10-10 22:11:30 +07:00
Sergey M․
762958d5af [yahoo] Add support for regional subdomains and extract duration (Closes #3915) 2014-10-10 19:50:29 +07:00
George Boyle
53d9009bdb KeyError on initialising YoutubeDL in python3 #3910 2014-10-10 10:03:24 +01:00
George Boyle
1b725173a5 Fixed typo 2014-10-10 09:35:41 +01:00
Sergey M․
0ca41c3d9c [walla] Fix typo 2014-10-09 21:10:07 +07:00
Sergey M․
fc6861b175 [sportbox] Add extractor (Closes #3906) 2014-10-09 21:05:39 +07:00
Sergey M․
b097b5f246 [mlb] Remove unused import 2014-10-09 20:07:34 +07:00
Sergey M․
385009fc44 [mlb] Fix thumbnails extraction (Closes #3905) 2014-10-09 19:56:55 +07:00
Sergey M․
ced659bb4d [generic] Ignore some non-video file extensions during generic extraction (Closes #3900) 2014-10-09 19:26:23 +07:00
Sergey M․
842cca7d56 [pornhd] Fix formats extraction (Closes #3898) 2014-10-08 20:08:29 +07:00
Sergey M․
b3826f6c8d Merge branch 'lenaten-walla' 2014-10-07 22:23:22 +07:00
Sergey M․
7bc8780c57 [walla] Fix extractor and add subtitle tests 2014-10-07 22:23:05 +07:00
Sergey M․
c59c3c84ed Merge branch 'walla' of https://github.com/lenaten/youtube-dl into lenaten-walla 2014-10-07 20:24:52 +07:00
Xu Cheng
24f7fb5e1e add zsh completion support into Makefile 2014-10-07 13:19:59 +08:00
Xu Cheng
3b700f8d43 support zsh completion 2014-10-07 13:19:59 +08:00
net
31d06400ec add missed init file 2014-10-06 03:03:05 +03:00
Philipp Hagemeister
642b76ac15 release 2014.10.05.2 2014-10-05 22:04:02 +02:00
Philipp Hagemeister
4c4de296d4 release 2014.10.05.1 2014-10-05 22:00:16 +02:00
Philipp Hagemeister
b10609d98c [dailymotion] Alternative title search (Fixes #3882) 2014-10-05 21:59:53 +02:00
Sergey M
3ae165aa10 [gorillavid] Add check for non existing videos 2014-10-06 01:48:01 +07:00
Sergey M
e4b85e35d0 [gorillavid] Fix title extraction and make thumbnail optional (Closes #3884) 2014-10-06 01:47:22 +07:00
Philipp Hagemeister
bb0c206f59 release 2014.10.05 2014-10-05 07:53:25 +02:00
Philipp Hagemeister
b81f484b60 [gorillavid] Add support for movpod.in (Fixes #3881) 2014-10-05 07:53:02 +02:00
Naglis Jonaitis
5e69192ef7 [thesixtyone] Add new extractor (closes #3781) 2014-10-04 22:40:36 +03:00
Naglis Jonaitis
e9be9a6acd [utils] Add additional format to unified_strdate 2014-10-04 22:38:23 +03:00
Sergey M․
f47754f061 [globo] Initial extractor implementation (Closes #3823) 2014-10-04 18:56:36 +07:00
Philipp Hagemeister
d838b1bd4a [utils] Default age_limit to None
If we can't parse it, it means we don't have any information, not that the content is unrestricted.
2014-10-03 20:17:12 +02:00
Naglis Jonaitis
fe506288bd [planetaplay] Add new extractor (closes #3839) 2014-10-03 19:43:36 +03:00
Sergey M․
d397c0b3dd [breakcom] Extract all formats 2014-10-03 19:37:47 +07:00
Sergey M․
146c80e256 [utils] Add parse_age_limit 2014-10-03 19:37:25 +07:00
Sergey M․
f78c01f68b [breakcom] Cover more URLs with _VALID_URL (Closes #3876) 2014-10-03 18:57:18 +07:00
Naglis Jonaitis
8489578df4 [generic] Support embedded Dailymotion playlists (fixes #3822) 2014-10-02 21:42:45 +03:00
net
e4d6cca0c1 [walla] Add new extractor 2014-10-01 23:45:35 +03:00
38 changed files with 1721 additions and 500 deletions

1
.gitignore vendored
View File

@@ -30,3 +30,4 @@ updates_key.pem
*.swp *.swp
test/testdata test/testdata
.tox .tox
youtube-dl.zsh

View File

@@ -1,7 +1,7 @@
all: youtube-dl README.md README.txt youtube-dl.1 youtube-dl.bash-completion youtube-dl.fish all: youtube-dl README.md README.txt youtube-dl.1 youtube-dl.bash-completion youtube-dl.zsh youtube-dl.fish
clean: clean:
rm -rf youtube-dl.1.temp.md youtube-dl.1 youtube-dl.bash-completion README.txt MANIFEST build/ dist/ .coverage cover/ youtube-dl.tar.gz youtube-dl.fish rm -rf youtube-dl.1.temp.md youtube-dl.1 youtube-dl.bash-completion README.txt MANIFEST build/ dist/ .coverage cover/ youtube-dl.tar.gz youtube-dl.zsh youtube-dl.fish
cleanall: clean cleanall: clean
rm -f youtube-dl youtube-dl.exe rm -f youtube-dl youtube-dl.exe
@@ -9,6 +9,7 @@ cleanall: clean
PREFIX ?= /usr/local PREFIX ?= /usr/local
BINDIR ?= $(PREFIX)/bin BINDIR ?= $(PREFIX)/bin
MANDIR ?= $(PREFIX)/man MANDIR ?= $(PREFIX)/man
SHAREDIR ?= $(PREFIX)/share
PYTHON ?= /usr/bin/env python PYTHON ?= /usr/bin/env python
# set SYSCONFDIR to /etc if PREFIX=/usr or PREFIX=/usr/local # set SYSCONFDIR to /etc if PREFIX=/usr or PREFIX=/usr/local
@@ -22,13 +23,15 @@ else
endif endif
endif endif
install: youtube-dl youtube-dl.1 youtube-dl.bash-completion install: youtube-dl youtube-dl.1 youtube-dl.bash-completion youtube-dl.zsh youtube-dl.fish
install -d $(DESTDIR)$(BINDIR) install -d $(DESTDIR)$(BINDIR)
install -m 755 youtube-dl $(DESTDIR)$(BINDIR) install -m 755 youtube-dl $(DESTDIR)$(BINDIR)
install -d $(DESTDIR)$(MANDIR)/man1 install -d $(DESTDIR)$(MANDIR)/man1
install -m 644 youtube-dl.1 $(DESTDIR)$(MANDIR)/man1 install -m 644 youtube-dl.1 $(DESTDIR)$(MANDIR)/man1
install -d $(DESTDIR)$(SYSCONFDIR)/bash_completion.d install -d $(DESTDIR)$(SYSCONFDIR)/bash_completion.d
install -m 644 youtube-dl.bash-completion $(DESTDIR)$(SYSCONFDIR)/bash_completion.d/youtube-dl install -m 644 youtube-dl.bash-completion $(DESTDIR)$(SYSCONFDIR)/bash_completion.d/youtube-dl
install -d $(DESTDIR)$(SHAREDIR)/zsh/site-functions
install -m 644 youtube-dl.zsh $(DESTDIR)$(SHAREDIR)/zsh/site-functions/_youtube-dl
install -d $(DESTDIR)$(SYSCONFDIR)/fish/completions install -d $(DESTDIR)$(SYSCONFDIR)/fish/completions
install -m 644 youtube-dl.fish $(DESTDIR)$(SYSCONFDIR)/fish/completions/youtube-dl.fish install -m 644 youtube-dl.fish $(DESTDIR)$(SYSCONFDIR)/fish/completions/youtube-dl.fish
@@ -38,7 +41,7 @@ test:
tar: youtube-dl.tar.gz tar: youtube-dl.tar.gz
.PHONY: all clean install test tar bash-completion pypi-files fish-completion .PHONY: all clean install test tar bash-completion pypi-files zsh-completion fish-completion
pypi-files: youtube-dl.bash-completion README.txt youtube-dl.1 youtube-dl.fish pypi-files: youtube-dl.bash-completion README.txt youtube-dl.1 youtube-dl.fish
@@ -66,12 +69,17 @@ youtube-dl.bash-completion: youtube_dl/*.py youtube_dl/*/*.py devscripts/bash-co
bash-completion: youtube-dl.bash-completion bash-completion: youtube-dl.bash-completion
youtube-dl.zsh: youtube_dl/*.py youtube_dl/*/*.py devscripts/zsh-completion.in
python devscripts/zsh-completion.py
zsh-completion: youtube-dl.zsh
youtube-dl.fish: youtube_dl/*.py youtube_dl/*/*.py devscripts/fish-completion.in youtube-dl.fish: youtube_dl/*.py youtube_dl/*/*.py devscripts/fish-completion.in
python devscripts/fish-completion.py python devscripts/fish-completion.py
fish-completion: youtube-dl.fish fish-completion: youtube-dl.fish
youtube-dl.tar.gz: youtube-dl README.md README.txt youtube-dl.1 youtube-dl.bash-completion youtube-dl.fish youtube-dl.tar.gz: youtube-dl README.md README.txt youtube-dl.1 youtube-dl.bash-completion youtube-dl.zsh youtube-dl.fish
@tar -czf youtube-dl.tar.gz --transform "s|^|youtube-dl/|" --owner 0 --group 0 \ @tar -czf youtube-dl.tar.gz --transform "s|^|youtube-dl/|" --owner 0 --group 0 \
--exclude '*.DS_Store' \ --exclude '*.DS_Store' \
--exclude '*.kate-swp' \ --exclude '*.kate-swp' \
@@ -86,5 +94,5 @@ youtube-dl.tar.gz: youtube-dl README.md README.txt youtube-dl.1 youtube-dl.bash-
bin devscripts test youtube_dl docs \ bin devscripts test youtube_dl docs \
LICENSE README.md README.txt \ LICENSE README.md README.txt \
Makefile MANIFEST.in youtube-dl.1 youtube-dl.bash-completion \ Makefile MANIFEST.in youtube-dl.1 youtube-dl.bash-completion \
youtube-dl.fish setup.py \ youtube-dl.zsh youtube-dl.fish setup.py \
youtube-dl youtube-dl

View File

@@ -99,8 +99,6 @@ which means you can modify it, redistribute it or use it however you like.
downloaded videos in it. downloaded videos in it.
--include-ads Download advertisements as well --include-ads Download advertisements as well
(experimental) (experimental)
--youtube-include-dash-manifest Try to download the DASH manifest on
YouTube videos (experimental)
## Download Options: ## Download Options:
-r, --rate-limit LIMIT maximum download rate in bytes per second -r, --rate-limit LIMIT maximum download rate in bytes per second
@@ -158,7 +156,8 @@ which means you can modify it, redistribute it or use it however you like.
downloads if possible. downloads if possible.
--no-continue do not resume partially downloaded files --no-continue do not resume partially downloaded files
(restart from beginning) (restart from beginning)
--no-part do not use .part files --no-part do not use .part files - write directly
into output file
--no-mtime do not use the Last-modified header to set --no-mtime do not use the Last-modified header to set
the file modification time the file modification time
--write-description write video description to a .description --write-description write video description to a .description
@@ -216,7 +215,7 @@ which means you can modify it, redistribute it or use it however you like.
information about the video. (Currently information about the video. (Currently
supported only for YouTube) supported only for YouTube)
--user-agent UA specify a custom user agent --user-agent UA specify a custom user agent
--referer REF specify a custom referer, use if the video --referer URL specify a custom referer, use if the video
access is restricted to one domain access is restricted to one domain
--add-header FIELD:VALUE specify a custom HTTP header and its value, --add-header FIELD:VALUE specify a custom HTTP header and its value,
separated by a colon ':'. You can use this separated by a colon ':'. You can use this
@@ -241,6 +240,8 @@ which means you can modify it, redistribute it or use it however you like.
one is requested one is requested
--max-quality FORMAT highest quality format to download --max-quality FORMAT highest quality format to download
-F, --list-formats list all available formats -F, --list-formats list all available formats
--youtube-skip-dash-manifest Do not download the DASH manifest on
YouTube videos
## Subtitle Options: ## Subtitle Options:
--write-sub write subtitle file --write-sub write subtitle file
@@ -256,7 +257,7 @@ which means you can modify it, redistribute it or use it however you like.
language tags like 'en,pt' language tags like 'en,pt'
## Authentication Options: ## Authentication Options:
-u, --username USERNAME account username -u, --username USERNAME login with this account ID
-p, --password PASSWORD account password -p, --password PASSWORD account password
-2, --twofactor TWOFACTOR two-factor auth code -2, --twofactor TWOFACTOR two-factor auth code
-n, --netrc use .netrc authentication data -n, --netrc use .netrc authentication data
@@ -267,7 +268,7 @@ which means you can modify it, redistribute it or use it however you like.
(requires ffmpeg or avconv and ffprobe or (requires ffmpeg or avconv and ffprobe or
avprobe) avprobe)
--audio-format FORMAT "best", "aac", "vorbis", "mp3", "m4a", --audio-format FORMAT "best", "aac", "vorbis", "mp3", "m4a",
"opus", or "wav"; best by default "opus", or "wav"; "best" by default
--audio-quality QUALITY ffmpeg/avconv audio quality specification, --audio-quality QUALITY ffmpeg/avconv audio quality specification,
insert a value between 0 (better) and 9 insert a value between 0 (better) and 9
(worse) for VBR or a specific bitrate like (worse) for VBR or a specific bitrate like

View File

@@ -0,0 +1,28 @@
#compdef youtube-dl
__youtube_dl() {
local curcontext="$curcontext" fileopts diropts cur prev
typeset -A opt_args
fileopts="{{fileopts}}"
diropts="{{diropts}}"
cur=$words[CURRENT]
case $cur in
:)
_arguments '*: :(::ytfavorites ::ytrecommended ::ytsubscriptions ::ytwatchlater ::ythistory)'
;;
*)
prev=$words[CURRENT-1]
if [[ ${prev} =~ ${fileopts} ]]; then
_path_files
elif [[ ${prev} =~ ${diropts} ]]; then
_path_files -/
elif [[ ${prev} == "--recode-video" ]]; then
_arguments '*: :(mp4 flv ogg webm mkv)'
else
_arguments '*: :({{flags}})'
fi
;;
esac
}
__youtube_dl

46
devscripts/zsh-completion.py Executable file
View File

@@ -0,0 +1,46 @@
#!/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
ZSH_COMPLETION_FILE = "youtube-dl.zsh"
ZSH_COMPLETION_TEMPLATE = "devscripts/zsh-completion.in"
def build_completion(opt_parser):
opts = [opt for group in opt_parser.option_groups
for opt in group.option_list]
opts_file = [opt for opt in opts if opt.metavar == "FILE"]
opts_dir = [opt for opt in opts if opt.metavar == "DIR"]
fileopts = []
for opt in opts_file:
if opt._short_opts:
fileopts.extend(opt._short_opts)
if opt._long_opts:
fileopts.extend(opt._long_opts)
diropts = []
for opt in opts_dir:
if opt._short_opts:
diropts.extend(opt._short_opts)
if opt._long_opts:
diropts.extend(opt._long_opts)
flags = [opt.get_opt_string() for opt in opts]
with open(ZSH_COMPLETION_TEMPLATE) as f:
template = f.read()
template = template.replace("{{fileopts}}", "|".join(fileopts))
template = template.replace("{{diropts}}", "|".join(diropts))
template = template.replace("{{flags}}", " ".join(flags))
with open(ZSH_COMPLETION_FILE, "w") as f:
f.write(template)
parser = youtube_dl.parseOpts()[0]
build_completion(parser)

View File

@@ -15,6 +15,7 @@ from youtube_dl.extractor import (
DailymotionIE, DailymotionIE,
TEDIE, TEDIE,
VimeoIE, VimeoIE,
WallaIE,
) )
@@ -279,5 +280,32 @@ class TestVimeoSubtitles(BaseTestSubtitles):
self.assertTrue(subtitles.get(lang) is not None, u'Subtitles for \'%s\' not extracted' % lang) self.assertTrue(subtitles.get(lang) is not None, u'Subtitles for \'%s\' not extracted' % lang)
class TestWallaSubtitles(BaseTestSubtitles):
url = 'http://vod.walla.co.il/movie/2705958/the-yes-men'
IE = WallaIE
def test_list_subtitles(self):
self.DL.expect_warning(u'Automatic Captions not supported by this server')
self.DL.params['listsubtitles'] = True
info_dict = self.getInfoDict()
self.assertEqual(info_dict, None)
def test_allsubtitles(self):
self.DL.expect_warning(u'Automatic Captions not supported by this server')
self.DL.params['writesubtitles'] = True
self.DL.params['allsubtitles'] = True
subtitles = self.getSubtitles()
self.assertEqual(set(subtitles.keys()), set(['heb']))
self.assertEqual(md5(subtitles['heb']), 'e758c5d7cb982f6bef14f377ec7a3920')
def test_nosubtitles(self):
self.DL.expect_warning(u'video doesn\'t have subtitles')
self.url = 'http://vod.walla.co.il/movie/2642630/one-direction-all-for-one'
self.DL.params['writesubtitles'] = True
self.DL.params['allsubtitles'] = True
subtitles = self.getSubtitles()
self.assertEqual(len(subtitles), 0)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@@ -228,11 +228,11 @@ class YoutubeDL(object):
if (sys.version_info >= (3,) and sys.platform != 'win32' and if (sys.version_info >= (3,) and sys.platform != 'win32' and
sys.getfilesystemencoding() in ['ascii', 'ANSI_X3.4-1968'] sys.getfilesystemencoding() in ['ascii', 'ANSI_X3.4-1968']
and not params['restrictfilenames']): and not params.get('restrictfilenames', False)):
# On Python 3, the Unicode filesystem API will throw errors (#1474) # On Python 3, the Unicode filesystem API will throw errors (#1474)
self.report_warning( self.report_warning(
'Assuming --restrict-filenames since file system encoding ' 'Assuming --restrict-filenames since file system encoding '
'cannot encode all charactes. ' 'cannot encode all characters. '
'Set the LC_ALL environment variable to fix this.') 'Set the LC_ALL environment variable to fix this.')
self.params['restrictfilenames'] = True self.params['restrictfilenames'] = True

View File

@@ -134,6 +134,7 @@ from .gamestar import GameStarIE
from .gametrailers import GametrailersIE from .gametrailers import GametrailersIE
from .gdcvault import GDCVaultIE from .gdcvault import GDCVaultIE
from .generic import GenericIE from .generic import GenericIE
from .globo import GloboIE
from .godtube import GodTubeIE from .godtube import GodTubeIE
from .golem import GolemIE from .golem import GolemIE
from .googleplus import GooglePlusIE from .googleplus import GooglePlusIE
@@ -275,6 +276,7 @@ from .parliamentliveuk import ParliamentLiveUKIE
from .patreon import PatreonIE from .patreon import PatreonIE
from .pbs import PBSIE from .pbs import PBSIE
from .photobucket import PhotobucketIE from .photobucket import PhotobucketIE
from .planetaplay import PlanetaPlayIE
from .played import PlayedIE from .played import PlayedIE
from .playfm import PlayFMIE from .playfm import PlayFMIE
from .playvid import PlayvidIE from .playvid import PlayvidIE
@@ -345,6 +347,7 @@ from .spiegel import SpiegelIE, SpiegelArticleIE
from .spiegeltv import SpiegeltvIE from .spiegeltv import SpiegeltvIE
from .spike import SpikeIE from .spike import SpikeIE
from .sport5 import Sport5IE from .sport5 import Sport5IE
from .sportbox import SportBoxIE
from .sportdeutschland import SportDeutschlandIE from .sportdeutschland import SportDeutschlandIE
from .stanfordoc import StanfordOpenClassroomIE from .stanfordoc import StanfordOpenClassroomIE
from .steam import SteamIE from .steam import SteamIE
@@ -368,7 +371,9 @@ from .telemb import TeleMBIE
from .tenplay import TenPlayIE from .tenplay import TenPlayIE
from .testurl import TestURLIE from .testurl import TestURLIE
from .tf1 import TF1IE from .tf1 import TF1IE
from .theonion import TheOnionIE
from .theplatform import ThePlatformIE from .theplatform import ThePlatformIE
from .thesixtyone import TheSixtyOneIE
from .thisav import ThisAVIE from .thisav import ThisAVIE
from .tinypic import TinyPicIE from .tinypic import TinyPicIE
from .tlc import TlcIE, TlcDeIE from .tlc import TlcIE, TlcDeIE
@@ -437,6 +442,7 @@ from .vporn import VpornIE
from .vube import VubeIE from .vube import VubeIE
from .vuclip import VuClipIE from .vuclip import VuClipIE
from .vulture import VultureIE from .vulture import VultureIE
from .walla import WallaIE
from .washingtonpost import WashingtonPostIE from .washingtonpost import WashingtonPostIE
from .wat import WatIE from .wat import WatIE
from .wayofthemaster import WayOfTheMasterIE from .wayofthemaster import WayOfTheMasterIE
@@ -458,7 +464,6 @@ from .xvideos import XVideosIE
from .xtube import XTubeUserIE, XTubeIE from .xtube import XTubeUserIE, XTubeIE
from .yahoo import ( from .yahoo import (
YahooIE, YahooIE,
YahooNewsIE,
YahooSearchIE, YahooSearchIE,
) )
from .ynet import YnetIE from .ynet import YnetIE

View File

@@ -4,37 +4,61 @@ import re
import json import json
from .common import InfoExtractor from .common import InfoExtractor
from ..utils import (
int_or_none,
parse_age_limit,
)
class BreakIE(InfoExtractor): class BreakIE(InfoExtractor):
_VALID_URL = r'http://(?:www\.)?break\.com/video/([^/]+)' _VALID_URL = r'http://(?:www\.)?break\.com/video/(?:[^/]+/)*.+-(?P<id>\d+)'
_TEST = { _TESTS = [{
'url': 'http://www.break.com/video/when-girls-act-like-guys-2468056', 'url': 'http://www.break.com/video/when-girls-act-like-guys-2468056',
'md5': 'a3513fb1547fba4fb6cfac1bffc6c46b', 'md5': '33aa4ff477ecd124d18d7b5d23b87ce5',
'info_dict': { 'info_dict': {
'id': '2468056', 'id': '2468056',
'ext': 'mp4', 'ext': 'mp4',
'title': 'When Girls Act Like D-Bags', 'title': 'When Girls Act Like D-Bags',
} }
} }, {
'url': 'http://www.break.com/video/ugc/baby-flex-2773063',
'only_matching': True,
}]
def _real_extract(self, url): def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url) video_id = self._match_id(url)
video_id = mobj.group(1).split("-")[-1] webpage = self._download_webpage(
embed_url = 'http://www.break.com/embed/%s' % video_id 'http://www.break.com/embed/%s' % video_id, video_id)
webpage = self._download_webpage(embed_url, video_id) info = json.loads(self._search_regex(
info_json = self._search_regex(r'var embedVars = ({.*})\s*?</script>', r'var embedVars = ({.*})\s*?</script>',
webpage, 'info json', flags=re.DOTALL) webpage, 'info json', flags=re.DOTALL))
info = json.loads(info_json)
video_url = info['videoUri']
youtube_id = info.get('youtubeId') youtube_id = info.get('youtubeId')
if youtube_id: if youtube_id:
return self.url_result(youtube_id, 'Youtube') return self.url_result(youtube_id, 'Youtube')
final_url = video_url + '?' + info['AuthToken'] formats = [{
'url': media['uri'] + '?' + info['AuthToken'],
'tbr': media['bitRate'],
'width': media['width'],
'height': media['height'],
} for media in info['media']]
if not formats:
formats.append({
'url': info['videoUri']
})
self._sort_formats(formats)
duration = int_or_none(info.get('videoLengthInSeconds'))
age_limit = parse_age_limit(info.get('audienceRating'))
return { return {
'id': video_id, 'id': video_id,
'url': final_url,
'title': info['contentName'], 'title': info['contentName'],
'thumbnail': info['thumbUri'], 'thumbnail': info['thumbUri'],
'duration': duration,
'age_limit': age_limit,
'formats': formats,
} }

View File

@@ -87,6 +87,15 @@ class BrightcoveIE(InfoExtractor):
'description': 'UCI MTB World Cup 2014: Fort William, UK - Downhill Finals', 'description': 'UCI MTB World Cup 2014: Fort William, UK - Downhill Finals',
}, },
}, },
{
# playlist test
# from http://support.brightcove.com/en/video-cloud/docs/playlist-support-single-video-players
'url': 'http://c.brightcove.com/services/viewer/htmlFederated?playerID=3550052898001&playerKey=AQ%7E%7E%2CAAABmA9XpXk%7E%2C-Kp7jNgisre1fG5OdqpAFUTcs0lP_ZoL',
'info_dict': {
'title': 'Sealife',
},
'playlist_mincount': 7,
},
] ]
@classmethod @classmethod

View File

@@ -138,6 +138,8 @@ class InfoExtractor(object):
Unless mentioned otherwise, the fields should be Unicode strings. Unless mentioned otherwise, the fields should be Unicode strings.
Unless mentioned otherwise, None is equivalent to absence of information.
Subclasses of this one should re-define the _real_initialize() and Subclasses of this one should re-define the _real_initialize() and
_real_extract() methods and define a _VALID_URL regexp. _real_extract() methods and define a _VALID_URL regexp.
Probably, they should also be added to the list of extractors. Probably, they should also be added to the list of extractors.
@@ -279,6 +281,12 @@ class InfoExtractor(object):
raw_filename = basen + '.dump' raw_filename = basen + '.dump'
filename = sanitize_filename(raw_filename, restricted=True) filename = sanitize_filename(raw_filename, restricted=True)
self.to_screen('Saving request to ' + filename) self.to_screen('Saving request to ' + filename)
# Working around MAX_PATH limitation on Windows (see
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx)
if os.name == 'nt':
absfilepath = os.path.abspath(filename)
if len(absfilepath) > 259:
filename = '\\\\?\\' + absfilepath
with open(filename, 'wb') as outf: with open(filename, 'wb') as outf:
outf.write(webpage_bytes) outf.write(webpage_bytes)

View File

@@ -34,6 +34,8 @@ class CondeNastIE(InfoExtractor):
_VALID_URL = r'http://(video|www|player)\.(?P<site>%s)\.com/(?P<type>watch|series|video|embed)/(?P<id>[^/?#]+)' % '|'.join(_SITES.keys()) _VALID_URL = r'http://(video|www|player)\.(?P<site>%s)\.com/(?P<type>watch|series|video|embed)/(?P<id>[^/?#]+)' % '|'.join(_SITES.keys())
IE_DESC = 'Condé Nast media group: %s' % ', '.join(sorted(_SITES.values())) IE_DESC = 'Condé Nast media group: %s' % ', '.join(sorted(_SITES.values()))
EMBED_URL = r'(?:https?:)?//player\.(?P<site>%s)\.com/(?P<type>embed)/.+?' % '|'.join(_SITES.keys())
_TEST = { _TEST = {
'url': 'http://video.wired.com/watch/3d-printed-speakers-lit-with-led', 'url': 'http://video.wired.com/watch/3d-printed-speakers-lit-with-led',
'md5': '1921f713ed48aabd715691f774c451f7', 'md5': '1921f713ed48aabd715691f774c451f7',

View File

@@ -82,11 +82,7 @@ class DailymotionIE(DailymotionBaseInfoExtractor, SubtitlesInfoExtractor):
] ]
def _real_extract(self, url): def _real_extract(self, url):
# Extract id and simplified title from URL video_id = self._match_id(url)
mobj = re.match(self._VALID_URL, url)
video_id = mobj.group('id')
url = 'http://www.dailymotion.com/video/%s' % video_id url = 'http://www.dailymotion.com/video/%s' % video_id
# Retrieve video webpage to extract further information # Retrieve video webpage to extract further information
@@ -147,18 +143,23 @@ class DailymotionIE(DailymotionBaseInfoExtractor, SubtitlesInfoExtractor):
self._list_available_subtitles(video_id, webpage) self._list_available_subtitles(video_id, webpage)
return return
view_count = self._search_regex( view_count = str_to_int(self._search_regex(
r'video_views_count[^>]+>\s+([\d\.,]+)', webpage, 'view count', fatal=False) r'video_views_count[^>]+>\s+([\d\.,]+)',
if view_count is not None: webpage, 'view count', fatal=False))
view_count = str_to_int(view_count)
title = self._og_search_title(webpage, default=None)
if title is None:
title = self._html_search_regex(
r'(?s)<span\s+id="video_title"[^>]*>(.*?)</span>', webpage,
'title')
return { return {
'id': video_id, 'id': video_id,
'formats': formats, 'formats': formats,
'uploader': info['owner.screenname'], 'uploader': info['owner.screenname'],
'upload_date': video_upload_date, 'upload_date': video_upload_date,
'title': self._og_search_title(webpage), 'title': title,
'subtitles': video_subtitles, 'subtitles': video_subtitles,
'thumbnail': info['thumbnail_url'], 'thumbnail': info['thumbnail_url'],
'age_limit': age_limit, 'age_limit': age_limit,
'view_count': view_count, 'view_count': view_count,

View File

@@ -28,6 +28,7 @@ from .brightcove import BrightcoveIE
from .ooyala import OoyalaIE from .ooyala import OoyalaIE
from .rutv import RUTVIE from .rutv import RUTVIE
from .smotri import SmotriIE from .smotri import SmotriIE
from .condenast import CondeNastIE
class GenericIE(InfoExtractor): class GenericIE(InfoExtractor):
@@ -639,6 +640,16 @@ class GenericIE(InfoExtractor):
return _playlist_from_matches( return _playlist_from_matches(
matches, lambda m: unescapeHTML(m[1])) matches, lambda m: unescapeHTML(m[1]))
# Look for embedded Dailymotion playlist player (#3822)
m = re.search(
r'<iframe[^>]+?src=(["\'])(?P<url>(?:https?:)?//(?:www\.)?dailymotion\.[a-z]{2,3}/widget/jukebox\?.+?)\1', webpage)
if m:
playlists = re.findall(
r'list\[\]=/playlist/([^/]+)/', unescapeHTML(m.group('url')))
if playlists:
return _playlist_from_matches(
playlists, lambda p: '//dailymotion.com/playlist/%s' % p)
# Look for embedded Wistia player # Look for embedded Wistia player
match = re.search( match = re.search(
r'<iframe[^>]+?src=(["\'])(?P<url>(?:https?:)?//(?:fast\.)?wistia\.net/embed/iframe/.+?)\1', webpage) r'<iframe[^>]+?src=(["\'])(?P<url>(?:https?:)?//(?:fast\.)?wistia\.net/embed/iframe/.+?)\1', webpage)
@@ -837,47 +848,57 @@ class GenericIE(InfoExtractor):
if mobj is not None: if mobj is not None:
return self.url_result(mobj.group('url'), 'MLB') return self.url_result(mobj.group('url'), 'MLB')
mobj = re.search(
r'<iframe[^>]+?src=(["\'])(?P<url>%s)\1' % CondeNastIE.EMBED_URL,
webpage)
if mobj is not None:
return self.url_result(self._proto_relative_url(mobj.group('url'), scheme='http:'), 'CondeNast')
def check_video(vurl):
vpath = compat_urlparse.urlparse(vurl).path
vext = determine_ext(vpath)
return '.' in vpath and vext not in ('swf', 'png', 'jpg', 'srt', 'sbv', 'sub', 'vtt', 'ttml')
def filter_video(urls):
return list(filter(check_video, urls))
# Start with something easy: JW Player in SWFObject # Start with something easy: JW Player in SWFObject
found = re.findall(r'flashvars: [\'"](?:.*&)?file=(http[^\'"&]*)', webpage) found = filter_video(re.findall(r'flashvars: [\'"](?:.*&)?file=(http[^\'"&]*)', webpage))
if not found: if not found:
# Look for gorilla-vid style embedding # Look for gorilla-vid style embedding
found = re.findall(r'''(?sx) found = filter_video(re.findall(r'''(?sx)
(?: (?:
jw_plugins| jw_plugins|
JWPlayerOptions| JWPlayerOptions|
jwplayer\s*\(\s*["'][^'"]+["']\s*\)\s*\.setup jwplayer\s*\(\s*["'][^'"]+["']\s*\)\s*\.setup
) )
.*?file\s*:\s*["\'](.*?)["\']''', webpage) .*?file\s*:\s*["\'](.*?)["\']''', webpage))
if not found: if not found:
# Broaden the search a little bit # Broaden the search a little bit
found = re.findall(r'[^A-Za-z0-9]?(?:file|source)=(http[^\'"&]*)', webpage) found = filter_video(re.findall(r'[^A-Za-z0-9]?(?:file|source)=(http[^\'"&]*)', webpage))
if not found: if not found:
# Broaden the findall a little bit: JWPlayer JS loader # Broaden the findall a little bit: JWPlayer JS loader
found = re.findall(r'[^A-Za-z0-9]?file["\']?:\s*["\'](http(?![^\'"]+\.[0-9]+[\'"])[^\'"]+)["\']', webpage) found = filter_video(re.findall(
r'[^A-Za-z0-9]?file["\']?:\s*["\'](http(?![^\'"]+\.[0-9]+[\'"])[^\'"]+)["\']', webpage))
if not found: if not found:
# Flow player # Flow player
found = re.findall(r'''(?xs) found = filter_video(re.findall(r'''(?xs)
flowplayer\("[^"]+",\s* flowplayer\("[^"]+",\s*
\{[^}]+?\}\s*, \{[^}]+?\}\s*,
\s*{[^}]+? ["']?clip["']?\s*:\s*\{\s* \s*{[^}]+? ["']?clip["']?\s*:\s*\{\s*
["']?url["']?\s*:\s*["']([^"']+)["'] ["']?url["']?\s*:\s*["']([^"']+)["']
''', webpage) ''', webpage))
if not found: if not found:
# Try to find twitter cards info # Try to find twitter cards info
found = re.findall(r'<meta (?:property|name)="twitter:player:stream" (?:content|value)="(.+?)"', webpage) found = filter_video(re.findall(
r'<meta (?:property|name)="twitter:player:stream" (?:content|value)="(.+?)"', webpage))
if not found: if not found:
# We look for Open Graph info: # We look for Open Graph info:
# We have to match any number spaces between elements, some sites try to align them (eg.: statigr.am) # We have to match any number spaces between elements, some sites try to align them (eg.: statigr.am)
m_video_type = re.findall(r'<meta.*?property="og:video:type".*?content="video/(.*?)"', webpage) m_video_type = re.findall(r'<meta.*?property="og:video:type".*?content="video/(.*?)"', webpage)
# We only look in og:video if the MIME type is a video, don't try if it's a Flash player: # We only look in og:video if the MIME type is a video, don't try if it's a Flash player:
if m_video_type is not None: if m_video_type is not None:
def check_video(vurl): found = filter_video(re.findall(r'<meta.*?property="og:video".*?content="(.*?)"', webpage))
vpath = compat_urlparse.urlparse(vurl).path
vext = determine_ext(vpath)
return '.' in vpath and vext not in ('swf', 'png', 'jpg')
found = list(filter(
check_video,
re.findall(r'<meta.*?property="og:video".*?content="(.*?)"', webpage)))
if not found: if not found:
# HTML5 video # HTML5 video
found = re.findall(r'(?s)<video[^<]*(?:>.*?<source[^>]+)? src="([^"]+)"', webpage) found = re.findall(r'(?s)<video[^<]*(?:>.*?<source[^>]+)? src="([^"]+)"', webpage)

View File

@@ -0,0 +1,398 @@
# coding: utf-8
from __future__ import unicode_literals
import random
import math
from .common import InfoExtractor
from ..utils import (
ExtractorError,
float_or_none,
compat_str,
compat_chr,
compat_ord,
)
class GloboIE(InfoExtractor):
_VALID_URL = 'https?://.+?\.globo\.com/(?P<id>.+)'
_API_URL_TEMPLATE = 'http://api.globovideos.com/videos/%s/playlist'
_SECURITY_URL_TEMPLATE = 'http://security.video.globo.com/videos/%s/hash?player=flash&version=2.9.9.50&resource_id=%s'
_VIDEOID_REGEXES = [
r'\bdata-video-id="(\d+)"',
r'\bdata-player-videosids="(\d+)"',
r'<div[^>]+\bid="(\d+)"',
]
_RESIGN_EXPIRATION = 86400
_TESTS = [
{
'url': 'http://globotv.globo.com/sportv/futebol-nacional/v/os-gols-de-atletico-mg-3-x-2-santos-pela-24a-rodada-do-brasileirao/3654973/',
'md5': '03ebf41cb7ade43581608b7d9b71fab0',
'info_dict': {
'id': '3654973',
'ext': 'mp4',
'title': 'Os gols de Atlético-MG 3 x 2 Santos pela 24ª rodada do Brasileirão',
'duration': 251.585,
'uploader': 'SporTV',
'uploader_id': 698,
'like_count': int,
}
},
{
'url': 'http://g1.globo.com/carros/autoesporte/videos/t/exclusivos-do-g1/v/mercedes-benz-gla-passa-por-teste-de-colisao-na-europa/3607726/',
'md5': 'b3ccc801f75cd04a914d51dadb83a78d',
'info_dict': {
'id': '3607726',
'ext': 'mp4',
'title': 'Mercedes-Benz GLA passa por teste de colisão na Europa',
'duration': 103.204,
'uploader': 'Globo.com',
'uploader_id': 265,
'like_count': int,
}
},
{
'url': 'http://g1.globo.com/jornal-nacional/noticia/2014/09/novidade-na-fiscalizacao-de-bagagem-pela-receita-provoca-discussoes.html',
'md5': '307fdeae4390ccfe6ba1aa198cf6e72b',
'info_dict': {
'id': '3652183',
'ext': 'mp4',
'title': 'Receita Federal explica como vai fiscalizar bagagens de quem retorna ao Brasil de avião',
'duration': 110.711,
'uploader': 'Rede Globo',
'uploader_id': 196,
'like_count': int,
}
},
]
class MD5():
HEX_FORMAT_LOWERCASE = 0
HEX_FORMAT_UPPERCASE = 1
BASE64_PAD_CHARACTER_DEFAULT_COMPLIANCE = ''
BASE64_PAD_CHARACTER_RFC_COMPLIANCE = '='
PADDING = '=0xFF01DD'
hexcase = 0
b64pad = ''
def __init__(self):
pass
class JSArray(list):
def __getitem__(self, y):
try:
return list.__getitem__(self, y)
except IndexError:
return 0
def __setitem__(self, i, y):
try:
return list.__setitem__(self, i, y)
except IndexError:
self.extend([0] * (i - len(self) + 1))
self[-1] = y
@classmethod
def hex_md5(cls, param1):
return cls.rstr2hex(cls.rstr_md5(cls.str2rstr_utf8(param1)))
@classmethod
def b64_md5(cls, param1, param2=None):
return cls.rstr2b64(cls.rstr_md5(cls.str2rstr_utf8(param1, param2)))
@classmethod
def any_md5(cls, param1, param2):
return cls.rstr2any(cls.rstr_md5(cls.str2rstr_utf8(param1)), param2)
@classmethod
def rstr_md5(cls, param1):
return cls.binl2rstr(cls.binl_md5(cls.rstr2binl(param1), len(param1) * 8))
@classmethod
def rstr2hex(cls, param1):
_loc_2 = '0123456789ABCDEF' if cls.hexcase else '0123456789abcdef'
_loc_3 = ''
for _loc_5 in range(0, len(param1)):
_loc_4 = compat_ord(param1[_loc_5])
_loc_3 += _loc_2[_loc_4 >> 4 & 15] + _loc_2[_loc_4 & 15]
return _loc_3
@classmethod
def rstr2b64(cls, param1):
_loc_2 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'
_loc_3 = ''
_loc_4 = len(param1)
for _loc_5 in range(0, _loc_4, 3):
_loc_6_1 = compat_ord(param1[_loc_5]) << 16
_loc_6_2 = compat_ord(param1[_loc_5 + 1]) << 8 if _loc_5 + 1 < _loc_4 else 0
_loc_6_3 = compat_ord(param1[_loc_5 + 2]) if _loc_5 + 2 < _loc_4 else 0
_loc_6 = _loc_6_1 | _loc_6_2 | _loc_6_3
for _loc_7 in range(0, 4):
if _loc_5 * 8 + _loc_7 * 6 > len(param1) * 8:
_loc_3 += cls.b64pad
else:
_loc_3 += _loc_2[_loc_6 >> 6 * (3 - _loc_7) & 63]
return _loc_3
@staticmethod
def rstr2any(param1, param2):
_loc_3 = len(param2)
_loc_4 = []
_loc_9 = [0] * ((len(param1) >> 2) + 1)
for _loc_5 in range(0, len(_loc_9)):
_loc_9[_loc_5] = compat_ord(param1[_loc_5 * 2]) << 8 | compat_ord(param1[_loc_5 * 2 + 1])
while len(_loc_9) > 0:
_loc_8 = []
_loc_7 = 0
for _loc_5 in range(0, len(_loc_9)):
_loc_7 = (_loc_7 << 16) + _loc_9[_loc_5]
_loc_6 = math.floor(_loc_7 / _loc_3)
_loc_7 -= _loc_6 * _loc_3
if len(_loc_8) > 0 or _loc_6 > 0:
_loc_8[len(_loc_8)] = _loc_6
_loc_4[len(_loc_4)] = _loc_7
_loc_9 = _loc_8
_loc_10 = ''
_loc_5 = len(_loc_4) - 1
while _loc_5 >= 0:
_loc_10 += param2[_loc_4[_loc_5]]
_loc_5 -= 1
return _loc_10
@classmethod
def str2rstr_utf8(cls, param1, param2=None):
_loc_3 = ''
_loc_4 = -1
if not param2:
param2 = cls.PADDING
param1 = param1 + param2[1:9]
while True:
_loc_4 += 1
if _loc_4 >= len(param1):
break
_loc_5 = compat_ord(param1[_loc_4])
_loc_6 = compat_ord(param1[_loc_4 + 1]) if _loc_4 + 1 < len(param1) else 0
if 55296 <= _loc_5 <= 56319 and 56320 <= _loc_6 <= 57343:
_loc_5 = 65536 + ((_loc_5 & 1023) << 10) + (_loc_6 & 1023)
_loc_4 += 1
if _loc_5 <= 127:
_loc_3 += compat_chr(_loc_5)
continue
if _loc_5 <= 2047:
_loc_3 += compat_chr(192 | _loc_5 >> 6 & 31) + compat_chr(128 | _loc_5 & 63)
continue
if _loc_5 <= 65535:
_loc_3 += compat_chr(224 | _loc_5 >> 12 & 15) + compat_chr(128 | _loc_5 >> 6 & 63) + compat_chr(
128 | _loc_5 & 63)
continue
if _loc_5 <= 2097151:
_loc_3 += compat_chr(240 | _loc_5 >> 18 & 7) + compat_chr(128 | _loc_5 >> 12 & 63) + compat_chr(
128 | _loc_5 >> 6 & 63) + compat_chr(128 | _loc_5 & 63)
return _loc_3
@staticmethod
def rstr2binl(param1):
_loc_2 = [0] * ((len(param1) >> 2) + 1)
for _loc_3 in range(0, len(_loc_2)):
_loc_2[_loc_3] = 0
for _loc_3 in range(0, len(param1) * 8, 8):
_loc_2[_loc_3 >> 5] |= (compat_ord(param1[_loc_3 // 8]) & 255) << _loc_3 % 32
return _loc_2
@staticmethod
def binl2rstr(param1):
_loc_2 = ''
for _loc_3 in range(0, len(param1) * 32, 8):
_loc_2 += compat_chr(param1[_loc_3 >> 5] >> _loc_3 % 32 & 255)
return _loc_2
@classmethod
def binl_md5(cls, param1, param2):
param1 = cls.JSArray(param1)
param1[param2 >> 5] |= 128 << param2 % 32
param1[(param2 + 64 >> 9 << 4) + 14] = param2
_loc_3 = 1732584193
_loc_4 = -271733879
_loc_5 = -1732584194
_loc_6 = 271733878
for _loc_7 in range(0, len(param1), 16):
_loc_8 = _loc_3
_loc_9 = _loc_4
_loc_10 = _loc_5
_loc_11 = _loc_6
_loc_3 = cls.md5_ff(_loc_3, _loc_4, _loc_5, _loc_6, param1[_loc_7 + 0], 7, -680876936)
_loc_6 = cls.md5_ff(_loc_6, _loc_3, _loc_4, _loc_5, param1[_loc_7 + 1], 12, -389564586)
_loc_5 = cls.md5_ff(_loc_5, _loc_6, _loc_3, _loc_4, param1[_loc_7 + 2], 17, 606105819)
_loc_4 = cls.md5_ff(_loc_4, _loc_5, _loc_6, _loc_3, param1[_loc_7 + 3], 22, -1044525330)
_loc_3 = cls.md5_ff(_loc_3, _loc_4, _loc_5, _loc_6, param1[_loc_7 + 4], 7, -176418897)
_loc_6 = cls.md5_ff(_loc_6, _loc_3, _loc_4, _loc_5, param1[_loc_7 + 5], 12, 1200080426)
_loc_5 = cls.md5_ff(_loc_5, _loc_6, _loc_3, _loc_4, param1[_loc_7 + 6], 17, -1473231341)
_loc_4 = cls.md5_ff(_loc_4, _loc_5, _loc_6, _loc_3, param1[_loc_7 + 7], 22, -45705983)
_loc_3 = cls.md5_ff(_loc_3, _loc_4, _loc_5, _loc_6, param1[_loc_7 + 8], 7, 1770035416)
_loc_6 = cls.md5_ff(_loc_6, _loc_3, _loc_4, _loc_5, param1[_loc_7 + 9], 12, -1958414417)
_loc_5 = cls.md5_ff(_loc_5, _loc_6, _loc_3, _loc_4, param1[_loc_7 + 10], 17, -42063)
_loc_4 = cls.md5_ff(_loc_4, _loc_5, _loc_6, _loc_3, param1[_loc_7 + 11], 22, -1990404162)
_loc_3 = cls.md5_ff(_loc_3, _loc_4, _loc_5, _loc_6, param1[_loc_7 + 12], 7, 1804603682)
_loc_6 = cls.md5_ff(_loc_6, _loc_3, _loc_4, _loc_5, param1[_loc_7 + 13], 12, -40341101)
_loc_5 = cls.md5_ff(_loc_5, _loc_6, _loc_3, _loc_4, param1[_loc_7 + 14], 17, -1502002290)
_loc_4 = cls.md5_ff(_loc_4, _loc_5, _loc_6, _loc_3, param1[_loc_7 + 15], 22, 1236535329)
_loc_3 = cls.md5_gg(_loc_3, _loc_4, _loc_5, _loc_6, param1[_loc_7 + 1], 5, -165796510)
_loc_6 = cls.md5_gg(_loc_6, _loc_3, _loc_4, _loc_5, param1[_loc_7 + 6], 9, -1069501632)
_loc_5 = cls.md5_gg(_loc_5, _loc_6, _loc_3, _loc_4, param1[_loc_7 + 11], 14, 643717713)
_loc_4 = cls.md5_gg(_loc_4, _loc_5, _loc_6, _loc_3, param1[_loc_7 + 0], 20, -373897302)
_loc_3 = cls.md5_gg(_loc_3, _loc_4, _loc_5, _loc_6, param1[_loc_7 + 5], 5, -701558691)
_loc_6 = cls.md5_gg(_loc_6, _loc_3, _loc_4, _loc_5, param1[_loc_7 + 10], 9, 38016083)
_loc_5 = cls.md5_gg(_loc_5, _loc_6, _loc_3, _loc_4, param1[_loc_7 + 15], 14, -660478335)
_loc_4 = cls.md5_gg(_loc_4, _loc_5, _loc_6, _loc_3, param1[_loc_7 + 4], 20, -405537848)
_loc_3 = cls.md5_gg(_loc_3, _loc_4, _loc_5, _loc_6, param1[_loc_7 + 9], 5, 568446438)
_loc_6 = cls.md5_gg(_loc_6, _loc_3, _loc_4, _loc_5, param1[_loc_7 + 14], 9, -1019803690)
_loc_5 = cls.md5_gg(_loc_5, _loc_6, _loc_3, _loc_4, param1[_loc_7 + 3], 14, -187363961)
_loc_4 = cls.md5_gg(_loc_4, _loc_5, _loc_6, _loc_3, param1[_loc_7 + 8], 20, 1163531501)
_loc_3 = cls.md5_gg(_loc_3, _loc_4, _loc_5, _loc_6, param1[_loc_7 + 13], 5, -1444681467)
_loc_6 = cls.md5_gg(_loc_6, _loc_3, _loc_4, _loc_5, param1[_loc_7 + 2], 9, -51403784)
_loc_5 = cls.md5_gg(_loc_5, _loc_6, _loc_3, _loc_4, param1[_loc_7 + 7], 14, 1735328473)
_loc_4 = cls.md5_gg(_loc_4, _loc_5, _loc_6, _loc_3, param1[_loc_7 + 12], 20, -1926607734)
_loc_3 = cls.md5_hh(_loc_3, _loc_4, _loc_5, _loc_6, param1[_loc_7 + 5], 4, -378558)
_loc_6 = cls.md5_hh(_loc_6, _loc_3, _loc_4, _loc_5, param1[_loc_7 + 8], 11, -2022574463)
_loc_5 = cls.md5_hh(_loc_5, _loc_6, _loc_3, _loc_4, param1[_loc_7 + 11], 16, 1839030562)
_loc_4 = cls.md5_hh(_loc_4, _loc_5, _loc_6, _loc_3, param1[_loc_7 + 14], 23, -35309556)
_loc_3 = cls.md5_hh(_loc_3, _loc_4, _loc_5, _loc_6, param1[_loc_7 + 1], 4, -1530992060)
_loc_6 = cls.md5_hh(_loc_6, _loc_3, _loc_4, _loc_5, param1[_loc_7 + 4], 11, 1272893353)
_loc_5 = cls.md5_hh(_loc_5, _loc_6, _loc_3, _loc_4, param1[_loc_7 + 7], 16, -155497632)
_loc_4 = cls.md5_hh(_loc_4, _loc_5, _loc_6, _loc_3, param1[_loc_7 + 10], 23, -1094730640)
_loc_3 = cls.md5_hh(_loc_3, _loc_4, _loc_5, _loc_6, param1[_loc_7 + 13], 4, 681279174)
_loc_6 = cls.md5_hh(_loc_6, _loc_3, _loc_4, _loc_5, param1[_loc_7 + 0], 11, -358537222)
_loc_5 = cls.md5_hh(_loc_5, _loc_6, _loc_3, _loc_4, param1[_loc_7 + 3], 16, -722521979)
_loc_4 = cls.md5_hh(_loc_4, _loc_5, _loc_6, _loc_3, param1[_loc_7 + 6], 23, 76029189)
_loc_3 = cls.md5_hh(_loc_3, _loc_4, _loc_5, _loc_6, param1[_loc_7 + 9], 4, -640364487)
_loc_6 = cls.md5_hh(_loc_6, _loc_3, _loc_4, _loc_5, param1[_loc_7 + 12], 11, -421815835)
_loc_5 = cls.md5_hh(_loc_5, _loc_6, _loc_3, _loc_4, param1[_loc_7 + 15], 16, 530742520)
_loc_4 = cls.md5_hh(_loc_4, _loc_5, _loc_6, _loc_3, param1[_loc_7 + 2], 23, -995338651)
_loc_3 = cls.md5_ii(_loc_3, _loc_4, _loc_5, _loc_6, param1[_loc_7 + 0], 6, -198630844)
_loc_6 = cls.md5_ii(_loc_6, _loc_3, _loc_4, _loc_5, param1[_loc_7 + 7], 10, 1126891415)
_loc_5 = cls.md5_ii(_loc_5, _loc_6, _loc_3, _loc_4, param1[_loc_7 + 14], 15, -1416354905)
_loc_4 = cls.md5_ii(_loc_4, _loc_5, _loc_6, _loc_3, param1[_loc_7 + 5], 21, -57434055)
_loc_3 = cls.md5_ii(_loc_3, _loc_4, _loc_5, _loc_6, param1[_loc_7 + 12], 6, 1700485571)
_loc_6 = cls.md5_ii(_loc_6, _loc_3, _loc_4, _loc_5, param1[_loc_7 + 3], 10, -1894986606)
_loc_5 = cls.md5_ii(_loc_5, _loc_6, _loc_3, _loc_4, param1[_loc_7 + 10], 15, -1051523)
_loc_4 = cls.md5_ii(_loc_4, _loc_5, _loc_6, _loc_3, param1[_loc_7 + 1], 21, -2054922799)
_loc_3 = cls.md5_ii(_loc_3, _loc_4, _loc_5, _loc_6, param1[_loc_7 + 8], 6, 1873313359)
_loc_6 = cls.md5_ii(_loc_6, _loc_3, _loc_4, _loc_5, param1[_loc_7 + 15], 10, -30611744)
_loc_5 = cls.md5_ii(_loc_5, _loc_6, _loc_3, _loc_4, param1[_loc_7 + 6], 15, -1560198380)
_loc_4 = cls.md5_ii(_loc_4, _loc_5, _loc_6, _loc_3, param1[_loc_7 + 13], 21, 1309151649)
_loc_3 = cls.md5_ii(_loc_3, _loc_4, _loc_5, _loc_6, param1[_loc_7 + 4], 6, -145523070)
_loc_6 = cls.md5_ii(_loc_6, _loc_3, _loc_4, _loc_5, param1[_loc_7 + 11], 10, -1120210379)
_loc_5 = cls.md5_ii(_loc_5, _loc_6, _loc_3, _loc_4, param1[_loc_7 + 2], 15, 718787259)
_loc_4 = cls.md5_ii(_loc_4, _loc_5, _loc_6, _loc_3, param1[_loc_7 + 9], 21, -343485551)
_loc_3 = cls.safe_add(_loc_3, _loc_8)
_loc_4 = cls.safe_add(_loc_4, _loc_9)
_loc_5 = cls.safe_add(_loc_5, _loc_10)
_loc_6 = cls.safe_add(_loc_6, _loc_11)
return [_loc_3, _loc_4, _loc_5, _loc_6]
@classmethod
def md5_cmn(cls, param1, param2, param3, param4, param5, param6):
return cls.safe_add(
cls.bit_rol(cls.safe_add(cls.safe_add(param2, param1), cls.safe_add(param4, param6)), param5), param3)
@classmethod
def md5_ff(cls, param1, param2, param3, param4, param5, param6, param7):
return cls.md5_cmn(param2 & param3 | ~param2 & param4, param1, param2, param5, param6, param7)
@classmethod
def md5_gg(cls, param1, param2, param3, param4, param5, param6, param7):
return cls.md5_cmn(param2 & param4 | param3 & ~param4, param1, param2, param5, param6, param7)
@classmethod
def md5_hh(cls, param1, param2, param3, param4, param5, param6, param7):
return cls.md5_cmn(param2 ^ param3 ^ param4, param1, param2, param5, param6, param7)
@classmethod
def md5_ii(cls, param1, param2, param3, param4, param5, param6, param7):
return cls.md5_cmn(param3 ^ (param2 | ~param4), param1, param2, param5, param6, param7)
@classmethod
def safe_add(cls, param1, param2):
_loc_3 = (param1 & 65535) + (param2 & 65535)
_loc_4 = (param1 >> 16) + (param2 >> 16) + (_loc_3 >> 16)
return cls.lshift(_loc_4, 16) | _loc_3 & 65535
@classmethod
def bit_rol(cls, param1, param2):
return cls.lshift(param1, param2) | (param1 & 0xFFFFFFFF) >> (32 - param2)
@staticmethod
def lshift(value, count):
r = (0xFFFFFFFF & value) << count
return -(~(r - 1) & 0xFFFFFFFF) if r > 0x7FFFFFFF else r
def _real_extract(self, url):
video_id = self._match_id(url)
webpage = self._download_webpage(url, video_id)
video_id = self._search_regex(self._VIDEOID_REGEXES, webpage, 'video id')
video = self._download_json(
self._API_URL_TEMPLATE % video_id, video_id)['videos'][0]
title = video['title']
duration = float_or_none(video['duration'], 1000)
like_count = video['likes']
uploader = video['channel']
uploader_id = video['channel_id']
formats = []
for resource in video['resources']:
resource_id = resource.get('_id')
if not resource_id:
continue
security = self._download_json(
self._SECURITY_URL_TEMPLATE % (video_id, resource_id),
video_id, 'Downloading security hash for %s' % resource_id)
security_hash = security.get('hash')
if not security_hash:
message = security.get('message')
if message:
raise ExtractorError(
'%s returned error: %s' % (self.IE_NAME, message), expected=True)
continue
hash_code = security_hash[:2]
received_time = int(security_hash[2:12])
received_random = security_hash[12:22]
received_md5 = security_hash[22:]
sign_time = received_time + self._RESIGN_EXPIRATION
padding = '%010d' % random.randint(1, 10000000000)
signed_md5 = self.MD5.b64_md5(received_md5 + compat_str(sign_time) + padding)
signed_hash = hash_code + compat_str(received_time) + received_random + compat_str(sign_time) + padding + signed_md5
formats.append({
'url': '%s?h=%s&k=%s' % (resource['url'], signed_hash, 'flash'),
'format_id': resource_id,
'height': resource['height']
})
self._sort_formats(formats)
return {
'id': video_id,
'title': title,
'duration': duration,
'uploader': uploader,
'uploader_id': uploader_id,
'like_count': like_count,
'formats': formats
}

View File

@@ -1,13 +1,11 @@
# coding: utf-8 # coding: utf-8
from __future__ import unicode_literals from __future__ import unicode_literals
import datetime
import re import re
import codecs
from .common import InfoExtractor from .common import InfoExtractor
from ..utils import ( from ..utils import unified_strdate
ExtractorError,
)
class GooglePlusIE(InfoExtractor): class GooglePlusIE(InfoExtractor):
@@ -19,74 +17,57 @@ class GooglePlusIE(InfoExtractor):
'info_dict': { 'info_dict': {
'id': 'ZButuJc6CtH', 'id': 'ZButuJc6CtH',
'ext': 'flv', 'ext': 'flv',
'title': '嘆きの天使 降臨',
'upload_date': '20120613', 'upload_date': '20120613',
'uploader': '井上ヨシマサ', 'uploader': '井上ヨシマサ',
'title': '嘆きの天使 降臨',
} }
} }
def _real_extract(self, url): def _real_extract(self, url):
# Extract id from URL video_id = self._match_id(url)
mobj = re.match(self._VALID_URL, url)
video_id = mobj.group('id')
# Step 1, Retrieve post webpage to extract further information # Step 1, Retrieve post webpage to extract further information
webpage = self._download_webpage(url, video_id, 'Downloading entry webpage') webpage = self._download_webpage(url, video_id, 'Downloading entry webpage')
self.report_extraction(video_id) title = self._og_search_description(webpage).splitlines()[0]
upload_date = unified_strdate(self._html_search_regex(
# Extract update date
upload_date = self._html_search_regex(
r'''(?x)<a.+?class="o-U-s\s[^"]+"\s+style="display:\s*none"\s*> r'''(?x)<a.+?class="o-U-s\s[^"]+"\s+style="display:\s*none"\s*>
([0-9]{4}-[0-9]{2}-[0-9]{2})</a>''', ([0-9]{4}-[0-9]{2}-[0-9]{2})</a>''',
webpage, 'upload date', fatal=False, flags=re.VERBOSE) webpage, 'upload date', fatal=False, flags=re.VERBOSE))
if upload_date: uploader = self._html_search_regex(
# Convert timestring to a format suitable for filename r'rel="author".*?>(.*?)</a>', webpage, 'uploader', fatal=False)
upload_date = datetime.datetime.strptime(upload_date, "%Y-%m-%d")
upload_date = upload_date.strftime('%Y%m%d')
# Extract uploader
uploader = self._html_search_regex(r'rel\="author".*?>(.*?)</a>',
webpage, 'uploader', fatal=False)
# Extract title
# Get the first line for title
video_title = self._og_search_description(webpage).splitlines()[0]
# Step 2, Simulate clicking the image box to launch video # Step 2, Simulate clicking the image box to launch video
DOMAIN = 'https://plus.google.com/' DOMAIN = 'https://plus.google.com/'
video_page = self._search_regex(r'<a href="((?:%s)?photos/.*?)"' % re.escape(DOMAIN), video_page = self._search_regex(
r'<a href="((?:%s)?photos/.*?)"' % re.escape(DOMAIN),
webpage, 'video page URL') webpage, 'video page URL')
if not video_page.startswith(DOMAIN): if not video_page.startswith(DOMAIN):
video_page = DOMAIN + video_page video_page = DOMAIN + video_page
webpage = self._download_webpage(video_page, video_id, 'Downloading video page') webpage = self._download_webpage(video_page, video_id, 'Downloading video page')
def unicode_escape(s):
decoder = codecs.getdecoder('unicode_escape')
return re.sub(
r'\\u[0-9a-fA-F]{4,}',
lambda m: decoder(m.group(0))[0],
s)
# Extract video links all sizes # Extract video links all sizes
pattern = r'\d+,\d+,(\d+),"(http\://redirector\.googlevideo\.com.*?)"' formats = [{
mobj = re.findall(pattern, webpage) 'url': unicode_escape(video_url),
if len(mobj) == 0: 'ext': 'flv',
raise ExtractorError('Unable to extract video links') 'width': int(width),
'height': int(height),
# Sort in resolution } for width, height, video_url in re.findall(
links = sorted(mobj) r'\d+,(\d+),(\d+),"(https?://redirector\.googlevideo\.com.*?)"', webpage)]
self._sort_formats(formats)
# Choose the lowest of the sort, i.e. highest resolution
video_url = links[-1]
# Only get the url. The resolution part in the tuple has no use anymore
video_url = video_url[-1]
# Treat escaped \u0026 style hex
try:
video_url = video_url.decode("unicode_escape")
except AttributeError: # Python 3
video_url = bytes(video_url, 'ascii').decode('unicode-escape')
return { return {
'id': video_id, 'id': video_id,
'url': video_url, 'title': title,
'uploader': uploader, 'uploader': uploader,
'upload_date': upload_date, 'upload_date': upload_date,
'title': video_title, 'formats': formats,
'ext': 'flv',
} }

View File

@@ -5,6 +5,7 @@ import re
from .common import InfoExtractor from .common import InfoExtractor
from ..utils import ( from ..utils import (
ExtractorError,
determine_ext, determine_ext,
compat_urllib_parse, compat_urllib_parse,
compat_urllib_request, compat_urllib_request,
@@ -12,20 +13,22 @@ from ..utils import (
class GorillaVidIE(InfoExtractor): class GorillaVidIE(InfoExtractor):
IE_DESC = 'GorillaVid.in and daclips.in' IE_DESC = 'GorillaVid.in, daclips.in and movpod.in'
_VALID_URL = r'''(?x) _VALID_URL = r'''(?x)
https?://(?P<host>(?:www\.)? https?://(?P<host>(?:www\.)?
(?:daclips\.in|gorillavid\.in))/ (?:daclips\.in|gorillavid\.in|movpod\.in))/
(?:embed-)?(?P<id>[0-9a-zA-Z]+)(?:-[0-9]+x[0-9]+\.html)? (?:embed-)?(?P<id>[0-9a-zA-Z]+)(?:-[0-9]+x[0-9]+\.html)?
''' '''
_FILE_NOT_FOUND_REGEX = r'>(?:404 - )?File Not Found<'
_TESTS = [{ _TESTS = [{
'url': 'http://gorillavid.in/06y9juieqpmi', 'url': 'http://gorillavid.in/06y9juieqpmi',
'md5': '5ae4a3580620380619678ee4875893ba', 'md5': '5ae4a3580620380619678ee4875893ba',
'info_dict': { 'info_dict': {
'id': '06y9juieqpmi', 'id': '06y9juieqpmi',
'ext': 'flv', 'ext': 'flv',
'title': 'Rebecca Black My Moment Official Music Video Reaction', 'title': 'Rebecca Black My Moment Official Music Video Reaction-6GK87Rc8bzQ',
'thumbnail': 're:http://.*\.jpg', 'thumbnail': 're:http://.*\.jpg',
}, },
}, { }, {
@@ -46,6 +49,9 @@ class GorillaVidIE(InfoExtractor):
'title': 'Micro Pig piglets ready on 16th July 2009', 'title': 'Micro Pig piglets ready on 16th July 2009',
'thumbnail': 're:http://.*\.jpg', 'thumbnail': 're:http://.*\.jpg',
}, },
}, {
'url': 'http://movpod.in/0wguyyxi1yca',
'only_matching': True,
}] }]
def _real_extract(self, url): def _real_extract(self, url):
@@ -54,6 +60,9 @@ class GorillaVidIE(InfoExtractor):
webpage = self._download_webpage('http://%s/%s' % (mobj.group('host'), video_id), video_id) webpage = self._download_webpage('http://%s/%s' % (mobj.group('host'), video_id), video_id)
if re.search(self._FILE_NOT_FOUND_REGEX, webpage) is not None:
raise ExtractorError('Video %s does not exist' % video_id, expected=True)
fields = dict(re.findall(r'''(?x)<input\s+ fields = dict(re.findall(r'''(?x)<input\s+
type="hidden"\s+ type="hidden"\s+
name="([^"]+)"\s+ name="([^"]+)"\s+
@@ -69,14 +78,14 @@ class GorillaVidIE(InfoExtractor):
webpage = self._download_webpage(req, video_id, 'Downloading video page') webpage = self._download_webpage(req, video_id, 'Downloading video page')
title = self._search_regex(r'style="z-index: [0-9]+;">([0-9a-zA-Z ]+)(?:-.+)?</span>', webpage, 'title') title = self._search_regex(r'style="z-index: [0-9]+;">([^<]+)</span>', webpage, 'title')
thumbnail = self._search_regex(r'image:\'(http[^\']+)\',', webpage, 'thumbnail') video_url = self._search_regex(r'file\s*:\s*\'(http[^\']+)\',', webpage, 'file url')
url = self._search_regex(r'file: \'(http[^\']+)\',', webpage, 'file url') thumbnail = self._search_regex(r'image\s*:\s*\'(http[^\']+)\',', webpage, 'thumbnail', fatal=False)
formats = [{ formats = [{
'format_id': 'sd', 'format_id': 'sd',
'url': url, 'url': video_url,
'ext': determine_ext(url), 'ext': determine_ext(video_url),
'quality': 1, 'quality': 1,
}] }]

View File

@@ -28,13 +28,13 @@ class HowStuffWorksIE(InfoExtractor):
} }
}, },
{ {
'url': 'http://adventure.howstuffworks.com/39516-deadliest-catch-jakes-farewell-pots-video.htm', 'url': 'http://adventure.howstuffworks.com/7199-survival-zone-food-and-water-in-the-savanna-video.htm',
'info_dict': { 'info_dict': {
'id': '553470', 'id': '453464',
'display_id': 'deadliest-catch-jakes-farewell-pots', 'display_id': 'survival-zone-food-and-water-in-the-savanna',
'ext': 'mp4', 'ext': 'mp4',
'title': 'Deadliest Catch: Jake\'s Farewell Pots', 'title': 'Survival Zone: Food and Water In the Savanna',
'description': 'md5:9632c346d5e43ee238028c9cefd8dbbc', 'description': 'md5:7e1c89f6411434970c15fa094170c371',
'thumbnail': 're:^https?://.*\.jpg$', 'thumbnail': 're:^https?://.*\.jpg$',
}, },
'params': { 'params': {

View File

@@ -33,8 +33,7 @@ class HuffPostIE(InfoExtractor):
} }
def _real_extract(self, url): def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url) video_id = self._match_id(url)
video_id = mobj.group('id')
api_url = 'http://embed.live.huffingtonpost.com/api/segments/%s.json' % video_id api_url = 'http://embed.live.huffingtonpost.com/api/segments/%s.json' % video_id
data = self._download_json(api_url, video_id)['data'] data = self._download_json(api_url, video_id)['data']

View File

@@ -34,7 +34,7 @@ class KontrTubeIE(InfoExtractor):
video_url = self._html_search_regex(r"video_url: '(.+?)/?',", webpage, 'video URL') video_url = self._html_search_regex(r"video_url: '(.+?)/?',", webpage, 'video URL')
thumbnail = self._html_search_regex(r"preview_url: '(.+?)/?',", webpage, 'video thumbnail', fatal=False) thumbnail = self._html_search_regex(r"preview_url: '(.+?)/?',", webpage, 'video thumbnail', fatal=False)
title = self._html_search_regex( title = self._html_search_regex(
r'<title>(.+?) - Труба зовёт - Интересный видеохостинг</title>', webpage, 'video title') r'<title>(.+?)</title>', webpage, 'video title')
description = self._html_search_meta('description', webpage, 'video description') description = self._html_search_meta('description', webpage, 'video description')
mobj = re.search( mobj = re.search(

View File

@@ -33,22 +33,22 @@ class MixcloudIE(InfoExtractor):
}, },
} }
def check_urls(self, url_list): def _get_url(self, track_id, template_url):
"""Returns 1st active url from list""" server_count = 30
for url in url_list: for i in range(server_count):
url = template_url % i
try: try:
# We only want to know if the request succeed # We only want to know if the request succeed
# don't download the whole file # don't download the whole file
self._request_webpage(HEADRequest(url), None, False) self._request_webpage(
HEADRequest(url), track_id,
'Checking URL %d/%d ...' % (i + 1, server_count + 1))
return url return url
except ExtractorError: except ExtractorError:
url = None pass
return None return None
def _get_url(self, template_url):
return self.check_urls(template_url % i for i in range(30))
def _real_extract(self, url): def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url) mobj = re.match(self._VALID_URL, url)
uploader = mobj.group(1) uploader = mobj.group(1)
@@ -61,16 +61,16 @@ class MixcloudIE(InfoExtractor):
r'\s(?:data-preview-url|m-preview)="(.+?)"', webpage, 'preview url') r'\s(?:data-preview-url|m-preview)="(.+?)"', webpage, 'preview url')
song_url = preview_url.replace('/previews/', '/c/originals/') song_url = preview_url.replace('/previews/', '/c/originals/')
template_url = re.sub(r'(stream\d*)', 'stream%d', song_url) template_url = re.sub(r'(stream\d*)', 'stream%d', song_url)
final_song_url = self._get_url(template_url) final_song_url = self._get_url(track_id, template_url)
if final_song_url is None: if final_song_url is None:
self.to_screen('Trying with m4a extension') self.to_screen('Trying with m4a extension')
template_url = template_url.replace('.mp3', '.m4a').replace('originals/', 'm4a/64/') template_url = template_url.replace('.mp3', '.m4a').replace('originals/', 'm4a/64/')
final_song_url = self._get_url(template_url) final_song_url = self._get_url(track_id, template_url)
if final_song_url is None: if final_song_url is None:
raise ExtractorError('Unable to extract track url') raise ExtractorError('Unable to extract track url')
PREFIX = ( PREFIX = (
r'<div class="cloudcast-play-button-container"' r'<div class="cloudcast-play-button-container[^"]*?"'
r'(?:\s+[a-zA-Z0-9-]+(?:="[^"]+")?)*?\s+') r'(?:\s+[a-zA-Z0-9-]+(?:="[^"]+")?)*?\s+')
title = self._html_search_regex( title = self._html_search_regex(
PREFIX + r'm-title="([^"]+)"', webpage, 'title') PREFIX + r'm-title="([^"]+)"', webpage, 'title')

View File

@@ -6,7 +6,6 @@ from .common import InfoExtractor
from ..utils import ( from ..utils import (
parse_duration, parse_duration,
parse_iso8601, parse_iso8601,
find_xpath_attr,
) )
@@ -88,8 +87,9 @@ class MLBIE(InfoExtractor):
duration = parse_duration(detail.find('./duration').text) duration = parse_duration(detail.find('./duration').text)
timestamp = parse_iso8601(detail.attrib['date'][:-5]) timestamp = parse_iso8601(detail.attrib['date'][:-5])
thumbnail = find_xpath_attr( thumbnails = [{
detail, './thumbnailScenarios/thumbnailScenario', 'type', '45').text 'url': thumbnail.text,
} for thumbnail in detail.findall('./thumbnailScenarios/thumbnailScenario')]
formats = [] formats = []
for media_url in detail.findall('./url'): for media_url in detail.findall('./url'):
@@ -116,5 +116,5 @@ class MLBIE(InfoExtractor):
'duration': duration, 'duration': duration,
'timestamp': timestamp, 'timestamp': timestamp,
'formats': formats, 'formats': formats,
'thumbnail': thumbnail, 'thumbnails': thumbnails,
} }

View File

@@ -18,16 +18,16 @@ class NDRIE(InfoExtractor):
_TESTS = [ _TESTS = [
{ {
'url': 'http://www.ndr.de/fernsehen/media/dienordreportage325.html', 'url': 'http://www.ndr.de/fernsehen/sendungen/nordmagazin/Kartoffeltage-in-der-Lewitz,nordmagazin25866.html',
'md5': '4a4eeafd17c3058b65f0c8f091355855', 'md5': '5bc5f5b92c82c0f8b26cddca34f8bb2c',
'note': 'Video file', 'note': 'Video file',
'info_dict': { 'info_dict': {
'id': '325', 'id': '25866',
'ext': 'mp4', 'ext': 'mp4',
'title': 'Blaue Bohnen aus Blocken', 'title': 'Kartoffeltage in der Lewitz',
'description': 'md5:190d71ba2ccddc805ed01547718963bc', 'description': 'md5:48c4c04dde604c8a9971b3d4e3b9eaa8',
'duration': 1715, 'duration': 166,
}, }
}, },
{ {
'url': 'http://www.ndr.de/info/audio51535.html', 'url': 'http://www.ndr.de/info/audio51535.html',

View File

@@ -39,18 +39,17 @@ class NiconicoIE(InfoExtractor):
_VALID_URL = r'https?://(?:www\.|secure\.)?nicovideo\.jp/watch/((?:[a-z]{2})?[0-9]+)' _VALID_URL = r'https?://(?:www\.|secure\.)?nicovideo\.jp/watch/((?:[a-z]{2})?[0-9]+)'
_NETRC_MACHINE = 'niconico' _NETRC_MACHINE = 'niconico'
# Determine whether the downloader uses authentication to download video # Determine whether the downloader used authentication to download video
_AUTHENTICATE = False _AUTHENTICATED = False
def _real_initialize(self): def _real_initialize(self):
if self._downloader.params.get('username', None) is not None: self._login()
self._AUTHENTICATE = True
if self._AUTHENTICATE:
self._login()
def _login(self): def _login(self):
(username, password) = self._get_login_info() (username, password) = self._get_login_info()
# No authentication to be performed
if not username:
return True
# Log in # Log in
login_form_strs = { login_form_strs = {
@@ -68,6 +67,8 @@ class NiconicoIE(InfoExtractor):
if re.search(r'(?i)<h1 class="mb8p4">Log in error</h1>', login_results) is not None: if re.search(r'(?i)<h1 class="mb8p4">Log in error</h1>', login_results) is not None:
self._downloader.report_warning('unable to log in: bad username or password') self._downloader.report_warning('unable to log in: bad username or password')
return False return False
# Successful login
self._AUTHENTICATED = True
return True return True
def _real_extract(self, url): def _real_extract(self, url):
@@ -82,7 +83,7 @@ class NiconicoIE(InfoExtractor):
'http://ext.nicovideo.jp/api/getthumbinfo/' + video_id, video_id, 'http://ext.nicovideo.jp/api/getthumbinfo/' + video_id, video_id,
note='Downloading video info page') note='Downloading video info page')
if self._AUTHENTICATE: if self._AUTHENTICATED:
# Get flv info # Get flv info
flv_info_webpage = self._download_webpage( flv_info_webpage = self._download_webpage(
'http://flapi.nicovideo.jp/api/getflv?v=' + video_id, 'http://flapi.nicovideo.jp/api/getflv?v=' + video_id,

View File

@@ -0,0 +1,60 @@
# coding: utf-8
from __future__ import unicode_literals
import re
from .common import InfoExtractor
from ..utils import ExtractorError
class PlanetaPlayIE(InfoExtractor):
_VALID_URL = r'https?://(?:www\.)?planetaplay\.com/\?sng=(?P<id>[0-9]+)'
_API_URL = 'http://planetaplay.com/action/playlist/?sng={0:}'
_THUMBNAIL_URL = 'http://planetaplay.com/img/thumb/{thumb:}'
_TEST = {
'url': 'http://planetaplay.com/?sng=3586',
'md5': '9d569dceb7251a4e01355d5aea60f9db',
'info_dict': {
'id': '3586',
'ext': 'flv',
'title': 'md5:e829428ee28b1deed00de90de49d1da1',
}
}
_SONG_FORMATS = {
'lq': (0, 'http://www.planetaplay.com/videoplayback/{med_hash:}'),
'hq': (1, 'http://www.planetaplay.com/videoplayback/hi/{med_hash:}'),
}
def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url)
video_id = mobj.group('id')
response = self._download_json(
self._API_URL.format(video_id), video_id)['response']
try:
data = response.get('data')[0]
except IndexError:
raise ExtractorError(
'%s: failed to get the playlist' % self.IE_NAME, expected=True)
title = '{song_artists:} - {sng_name:}'.format(**data)
thumbnail = self._THUMBNAIL_URL.format(**data)
formats = []
for format_id, (quality, url_template) in self._SONG_FORMATS.items():
formats.append({
'format_id': format_id,
'url': url_template.format(**data),
'quality': quality,
'ext': 'flv',
})
self._sort_formats(formats)
return {
'id': video_id,
'title': title,
'formats': formats,
'thumbnail': thumbnail,
}

View File

@@ -4,19 +4,27 @@ import re
import json import json
from .common import InfoExtractor from .common import InfoExtractor
from ..utils import int_or_none from ..utils import (
int_or_none,
js_to_json,
qualities,
determine_ext,
)
class PornHdIE(InfoExtractor): class PornHdIE(InfoExtractor):
_VALID_URL = r'http://(?:www\.)?pornhd\.com/(?:[a-z]{2,4}/)?videos/(?P<id>\d+)' _VALID_URL = r'http://(?:www\.)?pornhd\.com/(?:[a-z]{2,4}/)?videos/(?P<id>\d+)(?:/(?P<display_id>.+))?'
_TEST = { _TEST = {
'url': 'http://www.pornhd.com/videos/1962/sierra-day-gets-his-cum-all-over-herself-hd-porn-video', 'url': 'http://www.pornhd.com/videos/1962/sierra-day-gets-his-cum-all-over-herself-hd-porn-video',
'md5': '956b8ca569f7f4d8ec563e2c41598441', 'md5': '956b8ca569f7f4d8ec563e2c41598441',
'info_dict': { 'info_dict': {
'id': '1962', 'id': '1962',
'display_id': 'sierra-day-gets-his-cum-all-over-herself-hd-porn-video',
'ext': 'mp4', 'ext': 'mp4',
'title': 'Sierra loves doing laundry', 'title': 'Sierra loves doing laundry',
'description': 'md5:8ff0523848ac2b8f9b065ba781ccf294', 'description': 'md5:8ff0523848ac2b8f9b065ba781ccf294',
'thumbnail': 're:^https?://.*\.jpg',
'view_count': int,
'age_limit': 18, 'age_limit': 18,
} }
} }
@@ -24,8 +32,9 @@ class PornHdIE(InfoExtractor):
def _real_extract(self, url): def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url) mobj = re.match(self._VALID_URL, url)
video_id = mobj.group('id') video_id = mobj.group('id')
display_id = mobj.group('display_id')
webpage = self._download_webpage(url, video_id) webpage = self._download_webpage(url, display_id or video_id)
title = self._html_search_regex( title = self._html_search_regex(
r'<title>(.+) porn HD.+?</title>', webpage, 'title') r'<title>(.+) porn HD.+?</title>', webpage, 'title')
@@ -33,38 +42,21 @@ class PornHdIE(InfoExtractor):
r'<div class="description">([^<]+)</div>', webpage, 'description', fatal=False) r'<div class="description">([^<]+)</div>', webpage, 'description', fatal=False)
view_count = int_or_none(self._html_search_regex( view_count = int_or_none(self._html_search_regex(
r'(\d+) views\s*</span>', webpage, 'view count', fatal=False)) r'(\d+) views\s*</span>', webpage, 'view count', fatal=False))
thumbnail = self._search_regex(
r"'poster'\s*:\s*'([^']+)'", webpage, 'thumbnail', fatal=False)
videos = re.findall( quality = qualities(['SD', 'HD'])
r'var __video([\da-zA-Z]+?)(Low|High)StreamUrl = \'(http://.+?)\?noProxy=1\'', webpage) formats = [{
'url': source['file'],
mobj = re.search(r'flashVars = (?P<flashvars>{.+?});', webpage) 'format_id': '%s-%s' % (source['label'], determine_ext(source['file'])),
if mobj: 'quality': quality(source['label']),
flashvars = json.loads(mobj.group('flashvars')) } for source in json.loads(js_to_json(self._search_regex(
for key, quality in [('hashlink', 'low'), ('hd', 'high')]: r"(?s)'sources'\s*:\s*(\[.+?\])", webpage, 'sources')))]
redirect_url = flashvars.get(key)
if redirect_url:
videos.append(('flv', quality, redirect_url))
thumbnail = flashvars['urlWallpaper']
else:
thumbnail = self._og_search_thumbnail(webpage)
formats = []
for format_, quality, redirect_url in videos:
format_id = '%s-%s' % (format_.lower(), quality.lower())
video_url = self._download_webpage(
redirect_url, video_id, 'Downloading %s video link' % format_id, fatal=False)
if not video_url:
continue
formats.append({
'url': video_url,
'ext': format_.lower(),
'format_id': format_id,
'quality': 1 if quality.lower() == 'high' else 0,
})
self._sort_formats(formats) self._sort_formats(formats)
return { return {
'id': video_id, 'id': video_id,
'display_id': display_id,
'title': title, 'title': title,
'description': description, 'description': description,
'thumbnail': thumbnail, 'thumbnail': thumbnail,

View File

@@ -0,0 +1,81 @@
# coding: utf-8
from __future__ import unicode_literals
import re
from .common import InfoExtractor
from ..utils import (
parse_duration,
parse_iso8601,
int_or_none,
)
class SportBoxIE(InfoExtractor):
_VALID_URL = r'https?://news\.sportbox\.ru/Vidy_sporta/(?:[^/]+/)+spbvideo_NI\d+_(?P<display_id>.+)'
_TESTS = [
{
'url': 'http://news.sportbox.ru/Vidy_sporta/Avtosport/Rossijskij/spbvideo_NI483529_Gonka-2-zaezd-Obyedinenniy-2000-klassi-Turing-i-S',
'md5': 'ff56a598c2cf411a9a38a69709e97079',
'info_dict': {
'id': '80822',
'ext': 'mp4',
'title': 'Гонка 2 заезд ««Объединенный 2000»: классы Туринг и Супер-продакшн',
'description': 'md5:81715fa9c4ea3d9e7915dc8180c778ed',
'thumbnail': 're:^https?://.*\.jpg$',
'timestamp': 1411896237,
'upload_date': '20140928',
'duration': 4846,
'view_count': int,
},
'params': {
# m3u8 download
'skip_download': True,
},
}, {
'url': 'http://news.sportbox.ru/Vidy_sporta/billiard/spbvideo_NI486287_CHempionat-mira-po-dinamichnoy-piramide-4',
'only_matching': True,
}
]
def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url)
display_id = mobj.group('display_id')
webpage = self._download_webpage(url, display_id)
video_id = self._search_regex(
r'src="/vdl/player/media/(\d+)"', webpage, 'video id')
player = self._download_webpage(
'http://news.sportbox.ru/vdl/player/media/%s' % video_id,
display_id, 'Downloading player webpage')
hls = self._search_regex(
r"var\s+original_hls_file\s*=\s*'([^']+)'", player, 'hls file')
formats = self._extract_m3u8_formats(hls, display_id, 'mp4')
title = self._html_search_regex(
r'<h1 itemprop="name">([^<]+)</h1>', webpage, 'title')
description = self._html_search_regex(
r'(?s)<div itemprop="description">(.+?)</div>', webpage, 'description', fatal=False)
thumbnail = self._og_search_thumbnail(webpage)
timestamp = parse_iso8601(self._search_regex(
r'<span itemprop="uploadDate">([^<]+)</span>', webpage, 'timestamp', fatal=False))
duration = parse_duration(self._html_search_regex(
r'<meta itemprop="duration" content="PT([^"]+)">', webpage, 'duration', fatal=False))
view_count = int_or_none(self._html_search_regex(
r'<span>Просмотров: (\d+)</span>', player, 'view count', fatal=False))
return {
'id': video_id,
'display_id': display_id,
'title': title,
'description': description,
'thumbnail': thumbnail,
'timestamp': timestamp,
'duration': duration,
'view_count': view_count,
'formats': formats,
}

View File

@@ -65,6 +65,22 @@ class TEDIE(SubtitlesInfoExtractor):
'title': 'Who are the hackers?', 'title': 'Who are the hackers?',
}, },
'playlist_mincount': 6, 'playlist_mincount': 6,
}, {
# contains a youtube video
'url': 'https://www.ted.com/talks/douglas_adams_parrots_the_universe_and_everything',
'add_ie': ['Youtube'],
'info_dict': {
'id': '_ZG8HBuDjgc',
'ext': 'mp4',
'title': 'Douglas Adams: Parrots the Universe and Everything',
'description': 'md5:01ad1e199c49ac640cb1196c0e9016af',
'uploader': 'University of California Television (UCTV)',
'uploader_id': 'UCtelevision',
'upload_date': '20080522',
},
'params': {
'skip_download': True,
},
}] }]
_NATIVE_FORMATS = { _NATIVE_FORMATS = {
@@ -114,6 +130,13 @@ class TEDIE(SubtitlesInfoExtractor):
talk_info = self._extract_info(webpage)['talks'][0] talk_info = self._extract_info(webpage)['talks'][0]
if talk_info.get('external') is not None:
self.to_screen('Found video from %s' % talk_info['external']['service'])
return {
'_type': 'url',
'url': talk_info['external']['uri'],
}
formats = [{ formats = [{
'url': format_url, 'url': format_url,
'format_id': format_id, 'format_id': format_id,

View File

@@ -0,0 +1,70 @@
# coding: utf-8
from __future__ import unicode_literals
import re
from .common import InfoExtractor
from ..utils import ExtractorError
class TheOnionIE(InfoExtractor):
_VALID_URL = r'(?x)https?://(?:www\.)?theonion\.com/video/[^,]+,(?P<article_id>[0-9]+)/?'
_TEST = {
'url': 'http://www.theonion.com/video/man-wearing-mm-jacket-gods-image,36918/',
'md5': '19eaa9a39cf9b9804d982e654dc791ee',
'info_dict': {
'id': '2133',
'ext': 'mp4',
'title': 'Man Wearing M&M Jacket Apparently Made In God\'s Image',
'description': 'md5:cc12448686b5600baae9261d3e180910',
'thumbnail': 're:^https?://.*\.jpg\?\d+$',
}
}
def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url)
article_id = mobj.group('article_id')
webpage = self._download_webpage(url, article_id)
video_id = self._search_regex(
r'"videoId":\s(\d+),', webpage, 'video ID')
title = self._og_search_title(webpage)
description = self._og_search_description(webpage)
thumbnail = self._og_search_thumbnail(webpage)
sources = re.findall(r'<source src="([^"]+)" type="([^"]+)"', webpage)
if not sources:
raise ExtractorError(
'No sources found for video %s' % video_id, expected=True)
formats = []
for src, type_ in sources:
if type_ == 'video/mp4':
formats.append({
'format_id': 'mp4_sd',
'preference': 1,
'url': src,
})
elif type_ == 'video/webm':
formats.append({
'format_id': 'webm_sd',
'preference': 0,
'url': src,
})
elif type_ == 'application/x-mpegURL':
formats.extend(
self._extract_m3u8_formats(src, video_id, preference=-1))
else:
self.report_warning(
'Encountered unexpected format: %s' % type_)
self._sort_formats(formats)
return {
'id': video_id,
'title': title,
'formats': formats,
'thumbnail': thumbnail,
'description': description,
}

View File

@@ -0,0 +1,100 @@
# coding: utf-8
from __future__ import unicode_literals
import json
import re
from .common import InfoExtractor
from ..utils import unified_strdate
class TheSixtyOneIE(InfoExtractor):
_VALID_URL = r'''(?x)https?://(?:www\.)?thesixtyone\.com/
(?:.*?/)*
(?:
s|
song/comments/list|
song
)/(?P<id>[A-Za-z0-9]+)/?$'''
_SONG_URL_TEMPLATE = 'http://thesixtyone.com/s/{0:}'
_SONG_FILE_URL_TEMPLATE = 'http://{audio_server:}.thesixtyone.com/thesixtyone_production/audio/{0:}_stream'
_THUMBNAIL_URL_TEMPLATE = '{photo_base_url:}_desktop'
_TESTS = [
{
'url': 'http://www.thesixtyone.com/s/SrE3zD7s1jt/',
'md5': '821cc43b0530d3222e3e2b70bb4622ea',
'info_dict': {
'id': 'SrE3zD7s1jt',
'ext': 'mp3',
'title': 'CASIO - Unicorn War Mixtape',
'thumbnail': 're:^https?://.*_desktop$',
'upload_date': '20071217',
'duration': 3208,
}
},
{
'url': 'http://www.thesixtyone.com/song/comments/list/SrE3zD7s1jt',
'only_matching': True,
},
{
'url': 'http://www.thesixtyone.com/s/ULoiyjuJWli#/s/SrE3zD7s1jt/',
'only_matching': True,
},
{
'url': 'http://www.thesixtyone.com/#/s/SrE3zD7s1jt/',
'only_matching': True,
},
{
'url': 'http://www.thesixtyone.com/song/SrE3zD7s1jt/',
'only_matching': True,
},
]
_DECODE_MAP = {
"x": "a",
"m": "b",
"w": "c",
"q": "d",
"n": "e",
"p": "f",
"a": "0",
"h": "1",
"e": "2",
"u": "3",
"s": "4",
"i": "5",
"o": "6",
"y": "7",
"r": "8",
"c": "9"
}
def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url)
song_id = mobj.group('id')
webpage = self._download_webpage(
self._SONG_URL_TEMPLATE.format(song_id), song_id)
song_data = json.loads(self._search_regex(
r'"%s":\s(\{.*?\})' % song_id, webpage, 'song_data'))
keys = [self._DECODE_MAP.get(s, s) for s in song_data['key']]
url = self._SONG_FILE_URL_TEMPLATE.format(
"".join(reversed(keys)), **song_data)
formats = [{
'format_id': 'sd',
'url': url,
'ext': 'mp3',
}]
return {
'id': song_id,
'title': '{artist:} - {name:}'.format(**song_data),
'formats': formats,
'comment_count': song_data.get('comments_count'),
'duration': song_data.get('play_time'),
'like_count': song_data.get('score'),
'thumbnail': self._THUMBNAIL_URL_TEMPLATE.format(**song_data),
'upload_date': unified_strdate(song_data.get('publish_date')),
}

View File

@@ -56,7 +56,7 @@ class VimeoIE(VimeoBaseInfoExtractor, SubtitlesInfoExtractor):
# _VALID_URL matches Vimeo URLs # _VALID_URL matches Vimeo URLs
_VALID_URL = r'''(?x) _VALID_URL = r'''(?x)
(?P<proto>(?:https?:)?//)? https?://
(?:(?:www|(?P<player>player))\.)? (?:(?:www|(?P<player>player))\.)?
vimeo(?P<pro>pro)?\.com/ vimeo(?P<pro>pro)?\.com/
(?!channels/[^/?#]+/?(?:$|[?#])|album/) (?!channels/[^/?#]+/?(?:$|[?#])|album/)

View File

@@ -0,0 +1,89 @@
# coding: utf-8
from __future__ import unicode_literals
import re
from .subtitles import SubtitlesInfoExtractor
from ..utils import (
xpath_text,
int_or_none,
)
class WallaIE(SubtitlesInfoExtractor):
_VALID_URL = r'http://vod\.walla\.co\.il/[^/]+/(?P<id>\d+)/(?P<display_id>.+)'
_TEST = {
'url': 'http://vod.walla.co.il/movie/2642630/one-direction-all-for-one',
'info_dict': {
'id': '2642630',
'display_id': 'one-direction-all-for-one',
'ext': 'flv',
'title': 'וואן דיירקשן: ההיסטריה',
'description': 'md5:de9e2512a92442574cdb0913c49bc4d8',
'thumbnail': 're:^https?://.*\.jpg',
'duration': 3600,
},
'params': {
# rtmp download
'skip_download': True,
}
}
_SUBTITLE_LANGS = {
'עברית': 'heb',
}
def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url)
video_id = mobj.group('id')
display_id = mobj.group('display_id')
video = self._download_xml(
'http://video2.walla.co.il/?w=null/null/%s/@@/video/flv_pl' % video_id,
display_id)
item = video.find('./items/item')
title = xpath_text(item, './title', 'title')
description = xpath_text(item, './synopsis', 'description')
thumbnail = xpath_text(item, './preview_pic', 'thumbnail')
duration = int_or_none(xpath_text(item, './duration', 'duration'))
subtitles = {}
for subtitle in item.findall('./subtitles/subtitle'):
lang = xpath_text(subtitle, './title')
subtitles[self._SUBTITLE_LANGS.get(lang, lang)] = xpath_text(subtitle, './src')
if self._downloader.params.get('listsubtitles', False):
self._list_available_subtitles(video_id, subtitles)
return
subtitles = self.extract_subtitles(video_id, subtitles)
formats = []
for quality in item.findall('./qualities/quality'):
format_id = xpath_text(quality, './title')
fmt = {
'url': 'rtmp://wafla.walla.co.il/vod',
'play_path': xpath_text(quality, './src'),
'player_url': 'http://isc.walla.co.il/w9/swf/video_swf/vod/WallaMediaPlayerAvod.swf',
'page_url': url,
'ext': 'flv',
'format_id': xpath_text(quality, './title'),
}
m = re.search(r'^(?P<height>\d+)[Pp]', format_id)
if m:
fmt['height'] = int(m.group('height'))
formats.append(fmt)
self._sort_formats(formats)
return {
'id': video_id,
'display_id': display_id,
'title': title,
'description': description,
'thumbnail': thumbnail,
'duration': duration,
'formats': formats,
'subtitles': subtitles,
}

View File

@@ -1,3 +1,4 @@
# coding: utf-8
from __future__ import unicode_literals from __future__ import unicode_literals
import itertools import itertools
@@ -6,6 +7,7 @@ import re
from .common import InfoExtractor, SearchInfoExtractor from .common import InfoExtractor, SearchInfoExtractor
from ..utils import ( from ..utils import (
ExtractorError,
compat_urllib_parse, compat_urllib_parse,
compat_urlparse, compat_urlparse,
clean_html, clean_html,
@@ -15,7 +17,7 @@ from ..utils import (
class YahooIE(InfoExtractor): class YahooIE(InfoExtractor):
IE_DESC = 'Yahoo screen and movies' IE_DESC = 'Yahoo screen and movies'
_VALID_URL = r'(?P<url>https?://(?:screen|movies)\.yahoo\.com/.*?-(?P<id>[0-9]+)(?:-[a-z]+)?\.html)' _VALID_URL = r'(?P<url>(?P<host>https?://(?:[a-zA-Z]{2}\.)?[\da-zA-Z_-]+\.yahoo\.com)/(?:[^/]+/)*(?P<display_id>.+?)-(?P<id>[0-9]+)(?:-[a-z]+)?\.html)'
_TESTS = [ _TESTS = [
{ {
'url': 'http://screen.yahoo.com/julian-smith-travis-legg-watch-214727115.html', 'url': 'http://screen.yahoo.com/julian-smith-travis-legg-watch-214727115.html',
@@ -25,6 +27,7 @@ class YahooIE(InfoExtractor):
'ext': 'mp4', 'ext': 'mp4',
'title': 'Julian Smith & Travis Legg Watch Julian Smith', 'title': 'Julian Smith & Travis Legg Watch Julian Smith',
'description': 'Julian and Travis watch Julian Smith', 'description': 'Julian and Travis watch Julian Smith',
'duration': 6863,
}, },
}, },
{ {
@@ -34,7 +37,8 @@ class YahooIE(InfoExtractor):
'id': 'd1dedf8c-d58c-38c3-8963-e899929ae0a9', 'id': 'd1dedf8c-d58c-38c3-8963-e899929ae0a9',
'ext': 'mp4', 'ext': 'mp4',
'title': 'Codefellas - The Cougar Lies with Spanish Moss', 'title': 'Codefellas - The Cougar Lies with Spanish Moss',
'description': 'Agent Topple\'s mustache does its dirty work, and Nicole brokers a deal for peace. But why is the NSA collecting millions of Instagram brunch photos? And if your waffles have nothing to hide, what are they so worried about?', 'description': 'md5:66b627ab0a282b26352136ca96ce73c1',
'duration': 151,
}, },
}, },
{ {
@@ -45,15 +49,95 @@ class YahooIE(InfoExtractor):
'ext': 'mp4', 'ext': 'mp4',
'title': "Yahoo Saves 'Community'", 'title': "Yahoo Saves 'Community'",
'description': 'md5:4d4145af2fd3de00cbb6c1d664105053', 'description': 'md5:4d4145af2fd3de00cbb6c1d664105053',
'duration': 170,
} }
}, },
{
'url': 'https://tw.screen.yahoo.com/taipei-opinion-poll/選情站報-街頭民調-台北市篇-102823042.html',
'md5': '92a7fdd8a08783c68a174d7aa067dde8',
'info_dict': {
'id': '7a23b569-7bea-36cb-85b9-bd5301a0a1fb',
'ext': 'mp4',
'title': '選情站報 街頭民調 台北市篇',
'description': '選情站報 街頭民調 台北市篇',
'duration': 429,
}
},
{
'url': 'https://uk.screen.yahoo.com/editor-picks/cute-raccoon-freed-drain-using-091756545.html',
'md5': '0b51660361f0e27c9789e7037ef76f4b',
'info_dict': {
'id': 'b3affa53-2e14-3590-852b-0e0db6cd1a58',
'ext': 'mp4',
'title': 'Cute Raccoon Freed From Drain\u00a0Using Angle Grinder',
'description': 'md5:f66c890e1490f4910a9953c941dee944',
'duration': 97,
}
},
{
'url': 'https://ca.sports.yahoo.com/video/program-makes-hockey-more-affordable-013127711.html',
'md5': '57e06440778b1828a6079d2f744212c4',
'info_dict': {
'id': 'c9fa2a36-0d4d-3937-b8f6-cc0fb1881e73',
'ext': 'mp4',
'title': 'Program that makes hockey more affordable not offered in Manitoba',
'description': 'md5:c54a609f4c078d92b74ffb9bf1f496f4',
'duration': 121,
}
}, {
'url': 'https://ca.finance.yahoo.com/news/20-most-valuable-brands-world-112600775.html',
'md5': '3e401e4eed6325aa29d9b96125fd5b4f',
'info_dict': {
'id': 'c1b4c09c-8ed8-3b65-8b05-169c55358a83',
'ext': 'mp4',
'title': "Apple Is The World's Most Valuable Brand",
'description': 'md5:73eabc1a11c6f59752593b2ceefa1262',
'duration': 21,
}
}, {
'url': 'http://news.yahoo.com/video/china-moses-crazy-blues-104538833.html',
'md5': '67010fdf3a08d290e060a4dd96baa07b',
'info_dict': {
'id': 'f885cf7f-43d4-3450-9fac-46ac30ece521',
'ext': 'mp4',
'title': 'China Moses Is Crazy About the Blues',
'description': 'md5:9900ab8cd5808175c7b3fe55b979bed0',
'duration': 128,
}
}, {
'url': 'https://in.lifestyle.yahoo.com/video/connect-dots-dark-side-virgo-090247395.html',
'md5': 'd9a083ccf1379127bf25699d67e4791b',
'info_dict': {
'id': '52aeeaa3-b3d1-30d8-9ef8-5d0cf05efb7c',
'ext': 'mp4',
'title': 'Connect the Dots: Dark Side of Virgo',
'description': 'md5:1428185051cfd1949807ad4ff6d3686a',
'duration': 201,
}
}, {
'url': 'https://gma.yahoo.com/pizza-delivery-man-surprised-huge-tip-college-kids-195200785.html',
'only_matching': True,
}
] ]
def _real_extract(self, url): def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url) mobj = re.match(self._VALID_URL, url)
video_id = mobj.group('id') display_id = mobj.group('display_id')
url = mobj.group('url') url = mobj.group('url')
webpage = self._download_webpage(url, video_id) host = mobj.group('host')
webpage = self._download_webpage(url, display_id)
# Look for iframed media first
iframe_m = re.search(r'<iframe[^>]+src="(/video/.+?-\d+\.html\?format=embed.*?)"', webpage)
if iframe_m:
iframepage = self._download_webpage(
host + iframe_m.group(1), display_id, 'Downloading iframe webpage')
items_json = self._search_regex(
r'mediaItems: (\[.+?\])$', iframepage, 'items', flags=re.MULTILINE, default=None)
if items_json:
items = json.loads(items_json)
video_id = items[0]['id']
return self._get_info(video_id, display_id, webpage)
items_json = self._search_regex( items_json = self._search_regex(
r'mediaItems: ({.*?})$', webpage, 'items', flags=re.MULTILINE, r'mediaItems: ({.*?})$', webpage, 'items', flags=re.MULTILINE,
@@ -64,20 +148,22 @@ class YahooIE(InfoExtractor):
r'root\.App\.Cache\.context\.videoCache\.curVideo = \{"([^"]+)"', r'root\.App\.Cache\.context\.videoCache\.curVideo = \{"([^"]+)"',
r'"first_videoid"\s*:\s*"([^"]+)"', r'"first_videoid"\s*:\s*"([^"]+)"',
] ]
long_id = self._search_regex(CONTENT_ID_REGEXES, webpage, 'content ID') video_id = self._search_regex(CONTENT_ID_REGEXES, webpage, 'content ID')
video_id = long_id
else: else:
items = json.loads(items_json) items = json.loads(items_json)
info = items['mediaItems']['query']['results']['mediaObj'][0] info = items['mediaItems']['query']['results']['mediaObj'][0]
# The 'meta' field is not always in the video webpage, we request it # The 'meta' field is not always in the video webpage, we request it
# from another page # from another page
long_id = info['id'] video_id = info['id']
return self._get_info(long_id, video_id, webpage) return self._get_info(video_id, display_id, webpage)
def _get_info(self, long_id, video_id, webpage): def _get_info(self, video_id, display_id, webpage):
region = self._search_regex(
r'\\?"region\\?"\s*:\s*\\?"([^"]+?)\\?"',
webpage, 'region', fatal=False, default='US')
query = ('SELECT * FROM yahoo.media.video.streams WHERE id="%s"' query = ('SELECT * FROM yahoo.media.video.streams WHERE id="%s"'
' AND plrs="86Gj0vCaSzV_Iuf6hNylf2" AND region="US"' ' AND plrs="86Gj0vCaSzV_Iuf6hNylf2" AND region="%s"'
' AND protocol="http"' % long_id) ' AND protocol="http"' % (video_id, region))
data = compat_urllib_parse.urlencode({ data = compat_urllib_parse.urlencode({
'q': query, 'q': query,
'env': 'prod', 'env': 'prod',
@@ -85,9 +171,17 @@ class YahooIE(InfoExtractor):
}) })
query_result = self._download_json( query_result = self._download_json(
'http://video.query.yahoo.com/v1/public/yql?' + data, 'http://video.query.yahoo.com/v1/public/yql?' + data,
video_id, 'Downloading video info') display_id, 'Downloading video info')
info = query_result['query']['results']['mediaObj'][0] info = query_result['query']['results']['mediaObj'][0]
meta = info['meta'] meta = info.get('meta')
if not meta:
msg = info['status'].get('msg')
if msg:
raise ExtractorError(
'%s returned error: %s' % (self.IE_NAME, msg), expected=True)
raise ExtractorError('Unable to extract media object meta')
formats = [] formats = []
for s in info['streams']: for s in info['streams']:
@@ -114,36 +208,15 @@ class YahooIE(InfoExtractor):
return { return {
'id': video_id, 'id': video_id,
'display_id': display_id,
'title': meta['title'], 'title': meta['title'],
'formats': formats, 'formats': formats,
'description': clean_html(meta['description']), 'description': clean_html(meta['description']),
'thumbnail': meta['thumbnail'] if meta.get('thumbnail') else self._og_search_thumbnail(webpage), 'thumbnail': meta['thumbnail'] if meta.get('thumbnail') else self._og_search_thumbnail(webpage),
'duration': int_or_none(meta.get('duration')),
} }
class YahooNewsIE(YahooIE):
IE_NAME = 'yahoo:news'
_VALID_URL = r'http://news\.yahoo\.com/video/.*?-(?P<id>\d*?)\.html'
_TESTS = [{
'url': 'http://news.yahoo.com/video/china-moses-crazy-blues-104538833.html',
'md5': '67010fdf3a08d290e060a4dd96baa07b',
'info_dict': {
'id': '104538833',
'ext': 'mp4',
'title': 'China Moses Is Crazy About the Blues',
'description': 'md5:9900ab8cd5808175c7b3fe55b979bed0',
},
}]
def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url)
video_id = mobj.group('id')
webpage = self._download_webpage(url, video_id)
long_id = self._search_regex(r'contentId: \'(.+?)\',', webpage, 'long id')
return self._get_info(long_id, video_id, webpage)
class YahooSearchIE(SearchInfoExtractor): class YahooSearchIE(SearchInfoExtractor):
IE_DESC = 'Yahoo screen search' IE_DESC = 'Yahoo screen search'
_MAX_RESULTS = 1000 _MAX_RESULTS = 1000

View File

@@ -286,6 +286,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor, SubtitlesInfoExtractor):
'170': {'ext': 'webm', 'height': 1080, 'width': 1920, 'format_note': 'DASH video', 'acodec': 'none', 'container': 'webm', 'vcodec': 'VP8', 'preference': -40}, '170': {'ext': 'webm', 'height': 1080, 'width': 1920, 'format_note': 'DASH video', 'acodec': 'none', 'container': 'webm', 'vcodec': 'VP8', 'preference': -40},
'218': {'ext': 'webm', 'height': 480, 'width': 854, 'format_note': 'DASH video', 'acodec': 'none', 'container': 'webm', 'vcodec': 'VP8', 'preference': -40}, '218': {'ext': 'webm', 'height': 480, 'width': 854, 'format_note': 'DASH video', 'acodec': 'none', 'container': 'webm', 'vcodec': 'VP8', 'preference': -40},
'219': {'ext': 'webm', 'height': 480, 'width': 854, 'format_note': 'DASH video', 'acodec': 'none', 'container': 'webm', 'vcodec': 'VP8', 'preference': -40}, '219': {'ext': 'webm', 'height': 480, 'width': 854, 'format_note': 'DASH video', 'acodec': 'none', 'container': 'webm', 'vcodec': 'VP8', 'preference': -40},
'278': {'ext': 'webm', 'height': 144, 'format_note': 'DASH video', 'acodec': 'none', 'preference': -40, 'container': 'webm', 'vcodec': 'VP9'},
'242': {'ext': 'webm', 'height': 240, 'format_note': 'DASH video', 'acodec': 'none', 'preference': -40}, '242': {'ext': 'webm', 'height': 240, 'format_note': 'DASH video', 'acodec': 'none', 'preference': -40},
'243': {'ext': 'webm', 'height': 360, 'format_note': 'DASH video', 'acodec': 'none', 'preference': -40}, '243': {'ext': 'webm', 'height': 360, 'format_note': 'DASH video', 'acodec': 'none', 'preference': -40},
'244': {'ext': 'webm', 'height': 480, 'format_note': 'DASH video', 'acodec': 'none', 'preference': -40}, '244': {'ext': 'webm', 'height': 480, 'format_note': 'DASH video', 'acodec': 'none', 'preference': -40},
@@ -938,7 +939,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor, SubtitlesInfoExtractor):
raise ExtractorError('no conn, hlsvp or url_encoded_fmt_stream_map information found in video info') raise ExtractorError('no conn, hlsvp or url_encoded_fmt_stream_map information found in video info')
# Look for the DASH manifest # Look for the DASH manifest
if (self._downloader.params.get('youtube_include_dash_manifest', False)): if self._downloader.params.get('youtube_include_dash_manifest', True):
try: try:
# The DASH manifest used needs to be the one from the original video_webpage. # The DASH manifest used needs to be the one from the original video_webpage.
# The one found in get_video_info seems to be using different signatures. # The one found in get_video_info seems to be using different signatures.

View File

@@ -75,7 +75,8 @@ def parseOpts(overrideArguments=None):
if len(opts) > 1: if len(opts) > 1:
opts.insert(1, ', ') opts.insert(1, ', ')
if option.takes_value(): opts.append(' %s' % option.metavar) if option.takes_value():
opts.append(' %s' % option.metavar)
return "".join(opts) return "".join(opts)
@@ -87,68 +88,69 @@ def parseOpts(overrideArguments=None):
for private_opt in ['-p', '--password', '-u', '--username', '--video-password']: for private_opt in ['-p', '--password', '-u', '--username', '--video-password']:
try: try:
i = opts.index(private_opt) i = opts.index(private_opt)
opts[i+1] = 'PRIVATE' opts[i + 1] = 'PRIVATE'
except ValueError: except ValueError:
pass pass
return opts return opts
max_width = 80
max_help_position = 80
# No need to wrap help messages if we're on a wide console # No need to wrap help messages if we're on a wide console
columns = get_term_width() columns = get_term_width()
if columns: max_width = columns max_width = columns if columns else 80
max_help_position = 80
fmt = optparse.IndentedHelpFormatter(width=max_width, max_help_position=max_help_position) fmt = optparse.IndentedHelpFormatter(width=max_width, max_help_position=max_help_position)
fmt.format_option_strings = _format_option_string fmt.format_option_strings = _format_option_string
kw = { kw = {
'version' : __version__, 'version': __version__,
'formatter' : fmt, 'formatter': fmt,
'usage' : '%prog [options] url [url...]', 'usage': '%prog [options] url [url...]',
'conflict_handler' : 'resolve', 'conflict_handler': 'resolve',
} }
parser = optparse.OptionParser(**kw) parser = optparse.OptionParser(**kw)
# option groups general = optparse.OptionGroup(parser, 'General Options')
general = optparse.OptionGroup(parser, 'General Options')
selection = optparse.OptionGroup(parser, 'Video Selection')
authentication = optparse.OptionGroup(parser, 'Authentication Options')
video_format = optparse.OptionGroup(parser, 'Video Format Options')
subtitles = optparse.OptionGroup(parser, 'Subtitle Options')
downloader = optparse.OptionGroup(parser, 'Download Options')
postproc = optparse.OptionGroup(parser, 'Post-processing Options')
filesystem = optparse.OptionGroup(parser, 'Filesystem Options')
workarounds = optparse.OptionGroup(parser, 'Workarounds')
verbosity = optparse.OptionGroup(parser, 'Verbosity / Simulation Options')
general.add_option('-h', '--help',
action='help', help='print this help text and exit')
general.add_option('-v', '--version',
action='version', help='print program version and exit')
general.add_option('-U', '--update',
action='store_true', dest='update_self', help='update this program to latest version. Make sure that you have sufficient permissions (run with sudo if needed)')
general.add_option('-i', '--ignore-errors',
action='store_true', dest='ignoreerrors', help='continue on download errors, for example to skip unavailable videos in a playlist', default=False)
general.add_option('--abort-on-error',
action='store_false', dest='ignoreerrors',
help='Abort downloading of further videos (in the playlist or the command line) if an error occurs')
general.add_option('--dump-user-agent',
action='store_true', dest='dump_user_agent',
help='display the current browser identification', default=False)
general.add_option('--list-extractors',
action='store_true', dest='list_extractors',
help='List all supported extractors and the URLs they would handle', default=False)
general.add_option('--extractor-descriptions',
action='store_true', dest='list_extractor_descriptions',
help='Output descriptions of all supported extractors', default=False)
general.add_option( general.add_option(
'--proxy', dest='proxy', default=None, metavar='URL', '-h', '--help',
action='help',
help='print this help text and exit')
general.add_option(
'-v', '--version',
action='version',
help='print program version and exit')
general.add_option(
'-U', '--update',
action='store_true', dest='update_self',
help='update this program to latest version. Make sure that you have sufficient permissions (run with sudo if needed)')
general.add_option(
'-i', '--ignore-errors',
action='store_true', dest='ignoreerrors', default=False,
help='continue on download errors, for example to skip unavailable videos in a playlist')
general.add_option(
'--abort-on-error',
action='store_false', dest='ignoreerrors',
help='Abort downloading of further videos (in the playlist or the command line) if an error occurs')
general.add_option(
'--dump-user-agent',
action='store_true', dest='dump_user_agent', default=False,
help='display the current browser identification')
general.add_option(
'--list-extractors',
action='store_true', dest='list_extractors', default=False,
help='List all supported extractors and the URLs they would handle')
general.add_option(
'--extractor-descriptions',
action='store_true', dest='list_extractor_descriptions', default=False,
help='Output descriptions of all supported extractors')
general.add_option(
'--proxy', dest='proxy',
default=None, metavar='URL',
help='Use the specified HTTP/HTTPS proxy. Pass in an empty string (--proxy "") for direct connection') help='Use the specified HTTP/HTTPS proxy. Pass in an empty string (--proxy "") for direct connection')
general.add_option( general.add_option(
'--socket-timeout', dest='socket_timeout', '--socket-timeout',
type=float, default=None, help=u'Time to wait before giving up, in seconds') dest='socket_timeout', type=float, default=None,
help='Time to wait before giving up, in seconds')
general.add_option( general.add_option(
'--default-search', '--default-search',
dest='default_search', metavar='PREFIX', dest='default_search', metavar='PREFIX',
@@ -158,6 +160,7 @@ def parseOpts(overrideArguments=None):
action='store_true', action='store_true',
help='Do not read configuration files. When given in the global configuration file /etc/youtube-dl.conf: do not read the user configuration in ~/.config/youtube-dl.conf (%APPDATA%/youtube-dl/config.txt on Windows)') help='Do not read configuration files. When given in the global configuration file /etc/youtube-dl.conf: do not read the user configuration in ~/.config/youtube-dl.conf (%APPDATA%/youtube-dl/config.txt on Windows)')
selection = optparse.OptionGroup(parser, 'Video Selection')
selection.add_option( selection.add_option(
'--playlist-start', '--playlist-start',
dest='playliststart', metavar='NUMBER', default=1, type=int, dest='playliststart', metavar='NUMBER', default=1, type=int,
@@ -166,245 +169,371 @@ def parseOpts(overrideArguments=None):
'--playlist-end', '--playlist-end',
dest='playlistend', metavar='NUMBER', default=None, type=int, dest='playlistend', metavar='NUMBER', default=None, type=int,
help='playlist video to end at (default is last)') help='playlist video to end at (default is last)')
selection.add_option('--match-title', dest='matchtitle', metavar='REGEX',help='download only matching titles (regex or caseless sub-string)')
selection.add_option('--reject-title', dest='rejecttitle', metavar='REGEX',help='skip download for matching titles (regex or caseless sub-string)')
selection.add_option('--max-downloads', metavar='NUMBER',
dest='max_downloads', type=int, default=None,
help='Abort after downloading NUMBER files')
selection.add_option('--min-filesize', metavar='SIZE', dest='min_filesize', help="Do not download any videos smaller than SIZE (e.g. 50k or 44.6m)", default=None)
selection.add_option('--max-filesize', metavar='SIZE', dest='max_filesize', help="Do not download any videos larger than SIZE (e.g. 50k or 44.6m)", default=None)
selection.add_option('--date', metavar='DATE', dest='date', help='download only videos uploaded in this date', default=None)
selection.add_option( selection.add_option(
'--datebefore', metavar='DATE', dest='datebefore', default=None, '--match-title',
dest='matchtitle', metavar='REGEX',
help='download only matching titles (regex or caseless sub-string)')
selection.add_option(
'--reject-title',
dest='rejecttitle', metavar='REGEX',
help='skip download for matching titles (regex or caseless sub-string)')
selection.add_option(
'--max-downloads',
dest='max_downloads', metavar='NUMBER', type=int, default=None,
help='Abort after downloading NUMBER files')
selection.add_option(
'--min-filesize',
metavar='SIZE', dest='min_filesize', default=None,
help='Do not download any videos smaller than SIZE (e.g. 50k or 44.6m)')
selection.add_option(
'--max-filesize',
metavar='SIZE', dest='max_filesize', default=None,
help='Do not download any videos larger than SIZE (e.g. 50k or 44.6m)')
selection.add_option(
'--date',
metavar='DATE', dest='date', default=None,
help='download only videos uploaded in this date')
selection.add_option(
'--datebefore',
metavar='DATE', dest='datebefore', default=None,
help='download only videos uploaded on or before this date (i.e. inclusive)') help='download only videos uploaded on or before this date (i.e. inclusive)')
selection.add_option( selection.add_option(
'--dateafter', metavar='DATE', dest='dateafter', default=None, '--dateafter',
metavar='DATE', dest='dateafter', default=None,
help='download only videos uploaded on or after this date (i.e. inclusive)') help='download only videos uploaded on or after this date (i.e. inclusive)')
selection.add_option( selection.add_option(
'--min-views', metavar='COUNT', dest='min_views', '--min-views',
default=None, type=int, metavar='COUNT', dest='min_views', default=None, type=int,
help="Do not download any videos with less than COUNT views",) help='Do not download any videos with less than COUNT views',)
selection.add_option( selection.add_option(
'--max-views', metavar='COUNT', dest='max_views', '--max-views',
default=None, type=int, metavar='COUNT', dest='max_views', default=None, type=int,
help="Do not download any videos with more than COUNT views",) help='Do not download any videos with more than COUNT views')
selection.add_option('--no-playlist', action='store_true', dest='noplaylist', help='download only the currently playing video', default=False)
selection.add_option('--age-limit', metavar='YEARS', dest='age_limit',
help='download only videos suitable for the given age',
default=None, type=int)
selection.add_option('--download-archive', metavar='FILE',
dest='download_archive',
help='Download only videos not listed in the archive file. Record the IDs of all downloaded videos in it.')
selection.add_option( selection.add_option(
'--include-ads', dest='include_ads', '--no-playlist',
action='store_true', action='store_true', dest='noplaylist', default=False,
help='download only the currently playing video')
selection.add_option(
'--age-limit',
metavar='YEARS', dest='age_limit', default=None, type=int,
help='download only videos suitable for the given age')
selection.add_option(
'--download-archive', metavar='FILE',
dest='download_archive',
help='Download only videos not listed in the archive file. Record the IDs of all downloaded videos in it.')
selection.add_option(
'--include-ads',
dest='include_ads', action='store_true',
help='Download advertisements as well (experimental)') help='Download advertisements as well (experimental)')
selection.add_option(
'--youtube-include-dash-manifest', action='store_true',
dest='youtube_include_dash_manifest', default=False,
help='Try to download the DASH manifest on YouTube videos (experimental)')
authentication.add_option('-u', '--username', authentication = optparse.OptionGroup(parser, 'Authentication Options')
dest='username', metavar='USERNAME', help='account username') authentication.add_option(
authentication.add_option('-p', '--password', '-u', '--username',
dest='password', metavar='PASSWORD', help='account password') dest='username', metavar='USERNAME',
authentication.add_option('-2', '--twofactor', help='login with this account ID')
dest='twofactor', metavar='TWOFACTOR', help='two-factor auth code') authentication.add_option(
authentication.add_option('-n', '--netrc', '-p', '--password',
action='store_true', dest='usenetrc', help='use .netrc authentication data', default=False) dest='password', metavar='PASSWORD',
authentication.add_option('--video-password', help='account password')
dest='videopassword', metavar='PASSWORD', help='video password (vimeo, smotri)') authentication.add_option(
'-2', '--twofactor',
dest='twofactor', metavar='TWOFACTOR',
help='two-factor auth code')
authentication.add_option(
'-n', '--netrc',
action='store_true', dest='usenetrc', default=False,
help='use .netrc authentication data')
authentication.add_option(
'--video-password',
dest='videopassword', metavar='PASSWORD',
help='video password (vimeo, smotri)')
video_format = optparse.OptionGroup(parser, 'Video Format Options')
video_format.add_option(
'-f', '--format',
action='store', dest='format', metavar='FORMAT', default=None,
help='video format code, specify the order of preference using slashes: -f 22/17/18 . -f mp4 , -f m4a and -f flv are also supported. You can also use the special names "best", "bestvideo", "bestaudio", "worst", "worstvideo" and "worstaudio". By default, youtube-dl will pick the best quality. Use commas to download multiple audio formats, such as -f 136/137/mp4/bestvideo,140/m4a/bestaudio')
video_format.add_option(
'--all-formats',
action='store_const', dest='format', const='all',
help='download all available video formats')
video_format.add_option(
'--prefer-free-formats',
action='store_true', dest='prefer_free_formats', default=False,
help='prefer free video formats unless a specific one is requested')
video_format.add_option(
'--max-quality',
action='store', dest='format_limit', metavar='FORMAT',
help='highest quality format to download')
video_format.add_option(
'-F', '--list-formats',
action='store_true', dest='listformats',
help='list all available formats')
video_format.add_option(
'--youtube-include-dash-manifest',
action='store_true', dest='youtube_include_dash_manifest', default=True,
help=optparse.SUPPRESS_HELP)
video_format.add_option(
'--youtube-skip-dash-manifest',
action='store_false', dest='youtube_include_dash_manifest',
help='Do not download the DASH manifest on YouTube videos')
video_format.add_option('-f', '--format', subtitles = optparse.OptionGroup(parser, 'Subtitle Options')
action='store', dest='format', metavar='FORMAT', default=None, subtitles.add_option(
help='video format code, specify the order of preference using slashes: -f 22/17/18 . -f mp4 , -f m4a and -f flv are also supported. You can also use the special names "best", "bestvideo", "bestaudio", "worst", "worstvideo" and "worstaudio". By default, youtube-dl will pick the best quality. Use commas to download multiple audio formats, such as -f 136/137/mp4/bestvideo,140/m4a/bestaudio') '--write-sub', '--write-srt',
video_format.add_option('--all-formats', action='store_true', dest='writesubtitles', default=False,
action='store_const', dest='format', help='download all available video formats', const='all') help='write subtitle file')
video_format.add_option('--prefer-free-formats', subtitles.add_option(
action='store_true', dest='prefer_free_formats', default=False, help='prefer free video formats unless a specific one is requested') '--write-auto-sub', '--write-automatic-sub',
video_format.add_option('--max-quality', action='store_true', dest='writeautomaticsub', default=False,
action='store', dest='format_limit', metavar='FORMAT', help='highest quality format to download') help='write automatic subtitle file (youtube only)')
video_format.add_option('-F', '--list-formats', subtitles.add_option(
action='store_true', dest='listformats', help='list all available formats') '--all-subs',
action='store_true', dest='allsubtitles', default=False,
help='downloads all the available subtitles of the video')
subtitles.add_option(
'--list-subs',
action='store_true', dest='listsubtitles', default=False,
help='lists all available subtitles for the video')
subtitles.add_option(
'--sub-format',
action='store', dest='subtitlesformat', metavar='FORMAT', default='srt',
help='subtitle format (default=srt) ([sbv/vtt] youtube only)')
subtitles.add_option(
'--sub-lang', '--sub-langs', '--srt-lang',
action='callback', dest='subtitleslangs', metavar='LANGS', type='str',
default=[], callback=_comma_separated_values_options_callback,
help='languages of the subtitles to download (optional) separated by commas, use IETF language tags like \'en,pt\'')
subtitles.add_option('--write-sub', '--write-srt', downloader = optparse.OptionGroup(parser, 'Download Options')
action='store_true', dest='writesubtitles', downloader.add_option(
help='write subtitle file', default=False) '-r', '--rate-limit',
subtitles.add_option('--write-auto-sub', '--write-automatic-sub', dest='ratelimit', metavar='LIMIT',
action='store_true', dest='writeautomaticsub', help='maximum download rate in bytes per second (e.g. 50K or 4.2M)')
help='write automatic subtitle file (youtube only)', default=False) downloader.add_option(
subtitles.add_option('--all-subs', '-R', '--retries',
action='store_true', dest='allsubtitles', dest='retries', metavar='RETRIES', default=10,
help='downloads all the available subtitles of the video', default=False) help='number of retries (default is %default)')
subtitles.add_option('--list-subs', downloader.add_option(
action='store_true', dest='listsubtitles', '--buffer-size',
help='lists all available subtitles for the video', default=False) dest='buffersize', metavar='SIZE', default='1024',
subtitles.add_option('--sub-format', help='size of download buffer (e.g. 1024 or 16K) (default is %default)')
action='store', dest='subtitlesformat', metavar='FORMAT', downloader.add_option(
help='subtitle format (default=srt) ([sbv/vtt] youtube only)', default='srt') '--no-resize-buffer',
subtitles.add_option('--sub-lang', '--sub-langs', '--srt-lang', action='store_true', dest='noresizebuffer', default=False,
action='callback', dest='subtitleslangs', metavar='LANGS', type='str', help='do not automatically adjust the buffer size. By default, the buffer size is automatically resized from an initial value of SIZE.')
default=[], callback=_comma_separated_values_options_callback, downloader.add_option(
help='languages of the subtitles to download (optional) separated by commas, use IETF language tags like \'en,pt\'') '--test',
action='store_true', dest='test', default=False,
downloader.add_option('-r', '--rate-limit', help=optparse.SUPPRESS_HELP)
dest='ratelimit', metavar='LIMIT', help='maximum download rate in bytes per second (e.g. 50K or 4.2M)')
downloader.add_option('-R', '--retries',
dest='retries', metavar='RETRIES', help='number of retries (default is %default)', default=10)
downloader.add_option('--buffer-size',
dest='buffersize', metavar='SIZE', help='size of download buffer (e.g. 1024 or 16K) (default is %default)', default="1024")
downloader.add_option('--no-resize-buffer',
action='store_true', dest='noresizebuffer',
help='do not automatically adjust the buffer size. By default, the buffer size is automatically resized from an initial value of SIZE.', default=False)
downloader.add_option('--test', action='store_true', dest='test', default=False, help=optparse.SUPPRESS_HELP)
workarounds = optparse.OptionGroup(parser, 'Workarounds')
workarounds.add_option( workarounds.add_option(
'--encoding', dest='encoding', metavar='ENCODING', '--encoding',
dest='encoding', metavar='ENCODING',
help='Force the specified encoding (experimental)') help='Force the specified encoding (experimental)')
workarounds.add_option( workarounds.add_option(
'--no-check-certificate', action='store_true', '--no-check-certificate',
dest='no_check_certificate', default=False, action='store_true', dest='no_check_certificate', default=False,
help='Suppress HTTPS certificate validation.') help='Suppress HTTPS certificate validation.')
workarounds.add_option( workarounds.add_option(
'--prefer-insecure', '--prefer-unsecure', action='store_true', dest='prefer_insecure', '--prefer-insecure',
'--prefer-unsecure', action='store_true', dest='prefer_insecure',
help='Use an unencrypted connection to retrieve information about the video. (Currently supported only for YouTube)') help='Use an unencrypted connection to retrieve information about the video. (Currently supported only for YouTube)')
workarounds.add_option( workarounds.add_option(
'--user-agent', metavar='UA', '--user-agent',
dest='user_agent', help='specify a custom user agent') metavar='UA', dest='user_agent',
help='specify a custom user agent')
workarounds.add_option( workarounds.add_option(
'--referer', metavar='REF', '--referer',
dest='referer', default=None, metavar='URL', dest='referer', default=None,
help='specify a custom referer, use if the video access is restricted to one domain', help='specify a custom referer, use if the video access is restricted to one domain',
) )
workarounds.add_option( workarounds.add_option(
'--add-header', metavar='FIELD:VALUE', '--add-header',
dest='headers', action='append', metavar='FIELD:VALUE', dest='headers', action='append',
help='specify a custom HTTP header and its value, separated by a colon \':\'. You can use this option multiple times', help='specify a custom HTTP header and its value, separated by a colon \':\'. You can use this option multiple times',
) )
workarounds.add_option( workarounds.add_option(
'--bidi-workaround', dest='bidi_workaround', action='store_true', '--bidi-workaround',
help=u'Work around terminals that lack bidirectional text support. Requires bidiv or fribidi executable in PATH') dest='bidi_workaround', action='store_true',
help='Work around terminals that lack bidirectional text support. Requires bidiv or fribidi executable in PATH')
verbosity.add_option('-q', '--quiet', verbosity = optparse.OptionGroup(parser, 'Verbosity / Simulation Options')
action='store_true', dest='quiet', help='activates quiet mode', default=False) verbosity.add_option(
'-q', '--quiet',
action='store_true', dest='quiet', default=False,
help='activates quiet mode')
verbosity.add_option( verbosity.add_option(
'--no-warnings', '--no-warnings',
dest='no_warnings', action='store_true', default=False, dest='no_warnings', action='store_true', default=False,
help='Ignore warnings') help='Ignore warnings')
verbosity.add_option('-s', '--simulate', verbosity.add_option(
action='store_true', dest='simulate', help='do not download the video and do not write anything to disk', default=False) '-s', '--simulate',
verbosity.add_option('--skip-download', action='store_true', dest='simulate', default=False,
action='store_true', dest='skip_download', help='do not download the video', default=False) help='do not download the video and do not write anything to disk',)
verbosity.add_option('-g', '--get-url', verbosity.add_option(
action='store_true', dest='geturl', help='simulate, quiet but print URL', default=False) '--skip-download',
verbosity.add_option('-e', '--get-title', action='store_true', dest='skip_download', default=False,
action='store_true', dest='gettitle', help='simulate, quiet but print title', default=False) help='do not download the video',)
verbosity.add_option('--get-id', verbosity.add_option(
action='store_true', dest='getid', help='simulate, quiet but print id', default=False) '-g', '--get-url',
verbosity.add_option('--get-thumbnail', action='store_true', dest='geturl', default=False,
action='store_true', dest='getthumbnail', help='simulate, quiet but print URL')
help='simulate, quiet but print thumbnail URL', default=False) verbosity.add_option(
verbosity.add_option('--get-description', '-e', '--get-title',
action='store_true', dest='getdescription', action='store_true', dest='gettitle', default=False,
help='simulate, quiet but print video description', default=False) help='simulate, quiet but print title')
verbosity.add_option('--get-duration', verbosity.add_option(
action='store_true', dest='getduration', '--get-id',
help='simulate, quiet but print video length', default=False) action='store_true', dest='getid', default=False,
verbosity.add_option('--get-filename', help='simulate, quiet but print id')
action='store_true', dest='getfilename', verbosity.add_option(
help='simulate, quiet but print output filename', default=False) '--get-thumbnail',
verbosity.add_option('--get-format', action='store_true', dest='getthumbnail', default=False,
action='store_true', dest='getformat', help='simulate, quiet but print thumbnail URL')
help='simulate, quiet but print output format', default=False) verbosity.add_option(
verbosity.add_option('-j', '--dump-json', '--get-description',
action='store_true', dest='dumpjson', action='store_true', dest='getdescription', default=False,
help='simulate, quiet but print JSON information. See --output for a description of available keys.', default=False) help='simulate, quiet but print video description')
verbosity.add_option('--newline', verbosity.add_option(
action='store_true', dest='progress_with_newline', help='output progress bar as new lines', default=False) '--get-duration',
verbosity.add_option('--no-progress', action='store_true', dest='getduration', default=False,
action='store_true', dest='noprogress', help='do not print progress bar', default=False) help='simulate, quiet but print video length')
verbosity.add_option('--console-title', verbosity.add_option(
action='store_true', dest='consoletitle', '--get-filename',
help='display progress in console titlebar', default=False) action='store_true', dest='getfilename', default=False,
verbosity.add_option('-v', '--verbose', help='simulate, quiet but print output filename')
action='store_true', dest='verbose', help='print various debugging information', default=False) verbosity.add_option(
verbosity.add_option('--dump-intermediate-pages', '--get-format',
action='store_true', dest='dump_intermediate_pages', default=False, action='store_true', dest='getformat', default=False,
help='print downloaded pages to debug problems (very verbose)') help='simulate, quiet but print output format')
verbosity.add_option('--write-pages', verbosity.add_option(
action='store_true', dest='write_pages', default=False, '-j', '--dump-json',
help='Write downloaded intermediary pages to files in the current directory to debug problems') action='store_true', dest='dumpjson', default=False,
verbosity.add_option('--youtube-print-sig-code', help='simulate, quiet but print JSON information. See --output for a description of available keys.')
action='store_true', dest='youtube_print_sig_code', default=False, verbosity.add_option(
help=optparse.SUPPRESS_HELP) '--newline',
verbosity.add_option('--print-traffic', action='store_true', dest='progress_with_newline', default=False,
dest='debug_printtraffic', action='store_true', default=False, help='output progress bar as new lines')
help='Display sent and read HTTP traffic') verbosity.add_option(
'--no-progress',
action='store_true', dest='noprogress', default=False,
help='do not print progress bar')
verbosity.add_option(
'--console-title',
action='store_true', dest='consoletitle', default=False,
help='display progress in console titlebar')
verbosity.add_option(
'-v', '--verbose',
action='store_true', dest='verbose', default=False,
help='print various debugging information')
verbosity.add_option(
'--dump-intermediate-pages',
action='store_true', dest='dump_intermediate_pages', default=False,
help='print downloaded pages to debug problems (very verbose)')
verbosity.add_option(
'--write-pages',
action='store_true', dest='write_pages', default=False,
help='Write downloaded intermediary pages to files in the current directory to debug problems')
verbosity.add_option(
'--youtube-print-sig-code',
action='store_true', dest='youtube_print_sig_code', default=False,
help=optparse.SUPPRESS_HELP)
verbosity.add_option(
'--print-traffic',
dest='debug_printtraffic', action='store_true', default=False,
help='Display sent and read HTTP traffic')
filesystem = optparse.OptionGroup(parser, 'Filesystem Options')
filesystem.add_option('-a', '--batch-file', filesystem.add_option(
dest='batchfile', metavar='FILE', help='file containing URLs to download (\'-\' for stdin)') '-a', '--batch-file',
filesystem.add_option('--id', dest='batchfile', metavar='FILE',
action='store_true', dest='useid', help='use only video ID in file name', default=False) help='file containing URLs to download (\'-\' for stdin)')
filesystem.add_option('-A', '--auto-number', filesystem.add_option(
action='store_true', dest='autonumber', '--id', default=False,
help='number downloaded files starting from 00000', default=False) action='store_true', dest='useid', help='use only video ID in file name')
filesystem.add_option('-o', '--output', filesystem.add_option(
dest='outtmpl', metavar='TEMPLATE', '-A', '--auto-number',
help=('output filename template. Use %(title)s to get the title, ' action='store_true', dest='autonumber', default=False,
'%(uploader)s for the uploader name, %(uploader_id)s for the uploader nickname if different, ' help='number downloaded files starting from 00000')
'%(autonumber)s to get an automatically incremented number, ' filesystem.add_option(
'%(ext)s for the filename extension, ' '-o', '--output',
'%(format)s for the format description (like "22 - 1280x720" or "HD"), ' dest='outtmpl', metavar='TEMPLATE',
'%(format_id)s for the unique id of the format (like Youtube\'s itags: "137"), ' help=('output filename template. Use %(title)s to get the title, '
'%(upload_date)s for the upload date (YYYYMMDD), ' '%(uploader)s for the uploader name, %(uploader_id)s for the uploader nickname if different, '
'%(extractor)s for the provider (youtube, metacafe, etc), ' '%(autonumber)s to get an automatically incremented number, '
'%(id)s for the video id, %(playlist)s for the playlist the video is in, ' '%(ext)s for the filename extension, '
'%(playlist_index)s for the position in the playlist and %% for a literal percent. ' '%(format)s for the format description (like "22 - 1280x720" or "HD"), '
'%(height)s and %(width)s for the width and height of the video format. ' '%(format_id)s for the unique id of the format (like Youtube\'s itags: "137"), '
'%(resolution)s for a textual description of the resolution of the video format. ' '%(upload_date)s for the upload date (YYYYMMDD), '
'Use - to output to stdout. Can also be used to download to a different directory, ' '%(extractor)s for the provider (youtube, metacafe, etc), '
'for example with -o \'/my/downloads/%(uploader)s/%(title)s-%(id)s.%(ext)s\' .')) '%(id)s for the video id, %(playlist)s for the playlist the video is in, '
filesystem.add_option('--autonumber-size', '%(playlist_index)s for the position in the playlist and %% for a literal percent. '
dest='autonumber_size', metavar='NUMBER', '%(height)s and %(width)s for the width and height of the video format. '
help='Specifies the number of digits in %(autonumber)s when it is present in output filename template or --auto-number option is given') '%(resolution)s for a textual description of the resolution of the video format. '
filesystem.add_option('--restrict-filenames', 'Use - to output to stdout. Can also be used to download to a different directory, '
action='store_true', dest='restrictfilenames', 'for example with -o \'/my/downloads/%(uploader)s/%(title)s-%(id)s.%(ext)s\' .'))
help='Restrict filenames to only ASCII characters, and avoid "&" and spaces in filenames', default=False) filesystem.add_option(
filesystem.add_option('-t', '--title', '--autonumber-size',
action='store_true', dest='usetitle', help='[deprecated] use title in file name (default)', default=False) dest='autonumber_size', metavar='NUMBER',
filesystem.add_option('-l', '--literal', help='Specifies the number of digits in %(autonumber)s when it is present in output filename template or --auto-number option is given')
action='store_true', dest='usetitle', help='[deprecated] alias of --title', default=False) filesystem.add_option(
filesystem.add_option('-w', '--no-overwrites', '--restrict-filenames',
action='store_true', dest='nooverwrites', help='do not overwrite files', default=False) action='store_true', dest='restrictfilenames', default=False,
filesystem.add_option('-c', '--continue', help='Restrict filenames to only ASCII characters, and avoid "&" and spaces in filenames')
action='store_true', dest='continue_dl', help='force resume of partially downloaded files. By default, youtube-dl will resume downloads if possible.', default=True) filesystem.add_option(
filesystem.add_option('--no-continue', '-t', '--title',
action='store_false', dest='continue_dl', action='store_true', dest='usetitle', default=False,
help='do not resume partially downloaded files (restart from beginning)') help='[deprecated] use title in file name (default)')
filesystem.add_option('--no-part', filesystem.add_option(
action='store_true', dest='nopart', help='do not use .part files', default=False) '-l', '--literal', default=False,
filesystem.add_option('--no-mtime', action='store_true', dest='usetitle',
action='store_false', dest='updatetime', help='[deprecated] alias of --title')
help='do not use the Last-modified header to set the file modification time', default=True) filesystem.add_option(
filesystem.add_option('--write-description', '-w', '--no-overwrites',
action='store_true', dest='writedescription', action='store_true', dest='nooverwrites', default=False,
help='write video description to a .description file', default=False) help='do not overwrite files')
filesystem.add_option('--write-info-json', filesystem.add_option(
action='store_true', dest='writeinfojson', '-c', '--continue',
help='write video metadata to a .info.json file', default=False) action='store_true', dest='continue_dl', default=True,
filesystem.add_option('--write-annotations', help='force resume of partially downloaded files. By default, youtube-dl will resume downloads if possible.')
action='store_true', dest='writeannotations', filesystem.add_option(
help='write video annotations to a .annotation file', default=False) '--no-continue',
filesystem.add_option('--write-thumbnail', action='store_false', dest='continue_dl',
action='store_true', dest='writethumbnail', help='do not resume partially downloaded files (restart from beginning)')
help='write thumbnail image to disk', default=False) filesystem.add_option(
filesystem.add_option('--load-info', '--no-part',
dest='load_info_filename', metavar='FILE', action='store_true', dest='nopart', default=False,
help='json file containing the video information (created with the "--write-json" option)') help='do not use .part files - write directly into output file')
filesystem.add_option('--cookies', filesystem.add_option(
dest='cookiefile', metavar='FILE', help='file to read cookies from and dump cookie jar in') '--no-mtime',
action='store_false', dest='updatetime', default=True,
help='do not use the Last-modified header to set the file modification time')
filesystem.add_option(
'--write-description',
action='store_true', dest='writedescription', default=False,
help='write video description to a .description file')
filesystem.add_option(
'--write-info-json',
action='store_true', dest='writeinfojson', default=False,
help='write video metadata to a .info.json file')
filesystem.add_option(
'--write-annotations',
action='store_true', dest='writeannotations', default=False,
help='write video annotations to a .annotation file')
filesystem.add_option(
'--write-thumbnail',
action='store_true', dest='writethumbnail', default=False,
help='write thumbnail image to disk')
filesystem.add_option(
'--load-info',
dest='load_info_filename', metavar='FILE',
help='json file containing the video information (created with the "--write-json" option)')
filesystem.add_option(
'--cookies',
dest='cookiefile', metavar='FILE',
help='file to read cookies from and dump cookie jar in')
filesystem.add_option( filesystem.add_option(
'--cache-dir', dest='cachedir', default=None, metavar='DIR', '--cache-dir', dest='cachedir', default=None, metavar='DIR',
help='Location in the filesystem where youtube-dl can store some downloaded information permanently. By default $XDG_CACHE_HOME/youtube-dl or ~/.cache/youtube-dl . At the moment, only YouTube player files (for videos with obfuscated signatures) are cached, but that may change.') help='Location in the filesystem where youtube-dl can store some downloaded information permanently. By default $XDG_CACHE_HOME/youtube-dl or ~/.cache/youtube-dl . At the moment, only YouTube player files (for videos with obfuscated signatures) are cached, but that may change.')
@@ -412,36 +541,61 @@ def parseOpts(overrideArguments=None):
'--no-cache-dir', action='store_const', const=False, dest='cachedir', '--no-cache-dir', action='store_const', const=False, dest='cachedir',
help='Disable filesystem caching') help='Disable filesystem caching')
filesystem.add_option( filesystem.add_option(
'--rm-cache-dir', action='store_true', dest='rm_cachedir', '--rm-cache-dir',
action='store_true', dest='rm_cachedir',
help='Delete all filesystem cache files') help='Delete all filesystem cache files')
postproc = optparse.OptionGroup(parser, 'Post-processing Options')
postproc.add_option('-x', '--extract-audio', action='store_true', dest='extractaudio', default=False, postproc.add_option(
help='convert video files to audio-only files (requires ffmpeg or avconv and ffprobe or avprobe)') '-x', '--extract-audio',
postproc.add_option('--audio-format', metavar='FORMAT', dest='audioformat', default='best', action='store_true', dest='extractaudio', default=False,
help='"best", "aac", "vorbis", "mp3", "m4a", "opus", or "wav"; best by default') help='convert video files to audio-only files (requires ffmpeg or avconv and ffprobe or avprobe)')
postproc.add_option('--audio-quality', metavar='QUALITY', dest='audioquality', default='5', postproc.add_option(
help='ffmpeg/avconv audio quality specification, insert a value between 0 (better) and 9 (worse) for VBR or a specific bitrate like 128K (default 5)') '--audio-format', metavar='FORMAT', dest='audioformat', default='best',
postproc.add_option('--recode-video', metavar='FORMAT', dest='recodevideo', default=None, help='"best", "aac", "vorbis", "mp3", "m4a", "opus", or "wav"; "%default" by default')
help='Encode the video to another format if necessary (currently supported: mp4|flv|ogg|webm|mkv)') postproc.add_option(
postproc.add_option('-k', '--keep-video', action='store_true', dest='keepvideo', default=False, '--audio-quality', metavar='QUALITY',
help='keeps the video file on disk after the post-processing; the video is erased by default') dest='audioquality', default='5',
postproc.add_option('--no-post-overwrites', action='store_true', dest='nopostoverwrites', default=False, help='ffmpeg/avconv audio quality specification, insert a value between 0 (better) and 9 (worse) for VBR or a specific bitrate like 128K (default %default)')
help='do not overwrite post-processed files; the post-processed files are overwritten by default') postproc.add_option(
postproc.add_option('--embed-subs', action='store_true', dest='embedsubtitles', default=False, '--recode-video',
help='embed subtitles in the video (only for mp4 videos)') metavar='FORMAT', dest='recodevideo', default=None,
postproc.add_option('--embed-thumbnail', action='store_true', dest='embedthumbnail', default=False, help='Encode the video to another format if necessary (currently supported: mp4|flv|ogg|webm|mkv)')
help='embed thumbnail in the audio as cover art') postproc.add_option(
postproc.add_option('--add-metadata', action='store_true', dest='addmetadata', default=False, '-k', '--keep-video',
help='write metadata to the video file') action='store_true', dest='keepvideo', default=False,
postproc.add_option('--xattrs', action='store_true', dest='xattrs', default=False, help='keeps the video file on disk after the post-processing; the video is erased by default')
help='write metadata to the video file\'s xattrs (using dublin core and xdg standards)') postproc.add_option(
postproc.add_option('--prefer-avconv', action='store_false', dest='prefer_ffmpeg', '--no-post-overwrites',
action='store_true', dest='nopostoverwrites', default=False,
help='do not overwrite post-processed files; the post-processed files are overwritten by default')
postproc.add_option(
'--embed-subs',
action='store_true', dest='embedsubtitles', default=False,
help='embed subtitles in the video (only for mp4 videos)')
postproc.add_option(
'--embed-thumbnail',
action='store_true', dest='embedthumbnail', default=False,
help='embed thumbnail in the audio as cover art')
postproc.add_option(
'--add-metadata',
action='store_true', dest='addmetadata', default=False,
help='write metadata to the video file')
postproc.add_option(
'--xattrs',
action='store_true', dest='xattrs', default=False,
help='write metadata to the video file\'s xattrs (using dublin core and xdg standards)')
postproc.add_option(
'--prefer-avconv',
action='store_false', dest='prefer_ffmpeg',
help='Prefer avconv over ffmpeg for running the postprocessors (default)') help='Prefer avconv over ffmpeg for running the postprocessors (default)')
postproc.add_option('--prefer-ffmpeg', action='store_true', dest='prefer_ffmpeg', postproc.add_option(
'--prefer-ffmpeg',
action='store_true', dest='prefer_ffmpeg',
help='Prefer ffmpeg over avconv for running the postprocessors') help='Prefer ffmpeg over avconv for running the postprocessors')
postproc.add_option( postproc.add_option(
'--exec', metavar='CMD', dest='exec_cmd', '--exec',
metavar='CMD', dest='exec_cmd',
help='Execute a command on the file after downloading, similar to find\'s -exec syntax. Example: --exec \'adb push {} /sdcard/Music/ && rm {}\'' ) help='Execute a command on the file after downloading, similar to find\'s -exec syntax. Example: --exec \'adb push {} /sdcard/Music/ && rm {}\'' )
parser.add_option_group(general) parser.add_option_group(general)
@@ -458,7 +612,7 @@ def parseOpts(overrideArguments=None):
if overrideArguments is not None: if overrideArguments is not None:
opts, args = parser.parse_args(overrideArguments) opts, args = parser.parse_args(overrideArguments)
if opts.verbose: if opts.verbose:
write_string(u'[debug] Override config: ' + repr(overrideArguments) + '\n') write_string('[debug] Override config: ' + repr(overrideArguments) + '\n')
else: else:
commandLineConf = sys.argv[1:] commandLineConf = sys.argv[1:]
if '--ignore-config' in commandLineConf: if '--ignore-config' in commandLineConf:
@@ -474,8 +628,8 @@ def parseOpts(overrideArguments=None):
opts, args = parser.parse_args(argv) opts, args = parser.parse_args(argv)
if opts.verbose: if opts.verbose:
write_string(u'[debug] System config: ' + repr(_hide_login_info(systemConf)) + '\n') write_string('[debug] System config: ' + repr(_hide_login_info(systemConf)) + '\n')
write_string(u'[debug] User config: ' + repr(_hide_login_info(userConf)) + '\n') write_string('[debug] User config: ' + repr(_hide_login_info(userConf)) + '\n')
write_string(u'[debug] Command-line args: ' + repr(_hide_login_info(commandLineConf)) + '\n') write_string('[debug] Command-line args: ' + repr(_hide_login_info(commandLineConf)) + '\n')
return parser, opts, args return parser, opts, args

View File

@@ -487,7 +487,7 @@ class FFmpegMetadataPP(FFmpegPostProcessor):
class FFmpegMergerPP(FFmpegPostProcessor): class FFmpegMergerPP(FFmpegPostProcessor):
def run(self, info): def run(self, info):
filename = info['filepath'] filename = info['filepath']
args = ['-c', 'copy'] args = ['-c', 'copy', '-map', '0:v:0', '-map', '1:a:0', '-shortest']
self._downloader.to_screen(u'[ffmpeg] Merging formats into "%s"' % filename) self._downloader.to_screen(u'[ffmpeg] Merging formats into "%s"' % filename)
self.run_ffmpeg_multiple_files(info['__files_to_merge'], filename, args) self.run_ffmpeg_multiple_files(info['__files_to_merge'], filename, args)
return True, info return True, info

View File

@@ -894,6 +894,7 @@ def unified_strdate(date_str):
'%Y/%m/%d %H:%M:%S', '%Y/%m/%d %H:%M:%S',
'%d/%m/%Y %H:%M:%S', '%d/%m/%Y %H:%M:%S',
'%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M:%S',
'%Y-%m-%d %H:%M:%S.%f',
'%d.%m.%Y %H:%M', '%d.%m.%Y %H:%M',
'%d.%m.%Y %H.%M', '%d.%m.%Y %H.%M',
'%Y-%m-%dT%H:%M:%SZ', '%Y-%m-%dT%H:%M:%SZ',
@@ -1574,6 +1575,13 @@ US_RATINGS = {
} }
def parse_age_limit(s):
if s is None:
return None
m = re.match(r'^(?P<age>\d{1,2})\+?$', s)
return int(m.group('age')) if m else US_RATINGS.get(s, None)
def strip_jsonp(code): def strip_jsonp(code):
return re.sub(r'(?s)^[a-zA-Z0-9_]+\s*\(\s*(.*)\);?\s*?\s*$', r'\1', code) return re.sub(r'(?s)^[a-zA-Z0-9_]+\s*\(\s*(.*)\);?\s*?\s*$', r'\1', code)

View File

@@ -1,2 +1,2 @@
__version__ = '2014.10.02' __version__ = '2014.10.15'