Compare commits
372 Commits
2015.11.10
...
2015.12.13
Author | SHA1 | Date | |
---|---|---|---|
|
f3711edcf1 | ||
|
22d07ba4e4 | ||
|
f6abca506e | ||
|
b5424acdb9 | ||
|
47c7f3d995 | ||
|
0014ffa829 | ||
|
c03943f394 | ||
|
deb1e8d20e | ||
|
174964a7bc | ||
|
9c568178fb | ||
|
dbb7d7e26c | ||
|
ade2340971 | ||
|
4d77550cf0 | ||
|
c683454e7e | ||
|
f133fd326b | ||
|
1faa66f005 | ||
|
8773f3158f | ||
|
7e37c39485 | ||
|
14c17cafa1 | ||
|
8696a7fd13 | ||
|
d63cfc3f0f | ||
|
f377f44dae | ||
|
0b1bb1ac3a | ||
|
f208e52a76 | ||
|
b091529a3c | ||
|
b323a3cbff | ||
|
b59623ef43 | ||
|
9c163950da | ||
|
d357bbd375 | ||
|
f542a3d26b | ||
|
59a4ff482a | ||
|
40ca5b04f4 | ||
|
411e5b88c9 | ||
|
b4c299bad0 | ||
|
ab4bdc913f | ||
|
1fe248a51b | ||
|
2559b9d017 | ||
|
4db43567e8 | ||
|
5333842a1d | ||
|
98c3806b15 | ||
|
b6afc225c8 | ||
|
ad30dc1e20 | ||
|
ff51983e15 | ||
|
fdf01663d1 | ||
|
4b94288301 | ||
|
4bf99ade15 | ||
|
75ed53320e | ||
|
17b786ae73 | ||
|
dfd42a43c3 | ||
|
f7b8dd63f0 | ||
|
a8abf124c8 | ||
|
176ccefcd8 | ||
|
cbd2ffd031 | ||
|
0b534d2adc | ||
|
526a20bd16 | ||
|
51094b1b08 | ||
|
f1ac2033ab | ||
|
a1b8d815f5 | ||
|
8b756bd98e | ||
|
46047c58d0 | ||
|
374c761e77 | ||
|
6c7b26e13f | ||
|
b51b108045 | ||
|
86e8c89488 | ||
|
47f48f5d85 | ||
|
e15e2ef7a0 | ||
|
d0c8b279da | ||
|
612d83b51d | ||
|
9c30efeb7e | ||
|
39fa4cc107 | ||
|
b09c122373 | ||
|
3348243b7b | ||
|
b46b65ed37 | ||
|
18e4088fad | ||
|
5fd6cd64f9 | ||
|
3d24bbfbe4 | ||
|
1775612512 | ||
|
0d2d967cc7 | ||
|
a5e52a1fd4 | ||
|
291a93bafa | ||
|
c4737bea17 | ||
|
45dad7ba1b | ||
|
db7c9da871 | ||
|
bc92621ade | ||
|
fd8e559c3a | ||
|
222e11d4ae | ||
|
7d682f0acb | ||
|
8364b6b0b1 | ||
|
7ac40e5521 | ||
|
36066dd3ee | ||
|
636aa83ed3 | ||
|
33d152b6cc | ||
|
51c4fec0d5 | ||
|
0017486dca | ||
|
edc70f4aaf | ||
|
756926ff00 | ||
|
cb160dd531 | ||
|
77334ccb44 | ||
|
796db21295 | ||
|
535d7b681b | ||
|
960e038886 | ||
|
ea14422ff1 | ||
|
38d05d17e5 | ||
|
db9bd5267f | ||
|
ab3b773bbe | ||
|
0bc4ee60e0 | ||
|
a3ef0e1cdd | ||
|
679bacf0b5 | ||
|
02e3952f3b | ||
|
64b7e89c0c | ||
|
bee4c5571a | ||
|
96929dd1e8 | ||
|
53e06b2507 | ||
|
b80d4bebf3 | ||
|
55bec9b658 | ||
|
2a63b0f110 | ||
|
07b88cffce | ||
|
58c8451f36 | ||
|
3047121c63 | ||
|
7079f8ff1f | ||
|
2c3b9f3570 | ||
|
fad2428f47 | ||
|
c3d3110f6a | ||
|
79ec00276c | ||
|
9c117d345f | ||
|
46cc1c65a4 | ||
|
71d9fe7818 | ||
|
4ccabf93db | ||
|
6612a34939 | ||
|
e5b4225f7c | ||
|
b2ca35ddbc | ||
|
76ab842d9b | ||
|
24dc1ed715 | ||
|
682d8dcd21 | ||
|
640bb54e73 | ||
|
e0977d7686 | ||
|
112ab398db | ||
|
af93fcfa05 | ||
|
62d231c004 | ||
|
49358274d7 | ||
|
7b1e379ca9 | ||
|
22d7368dfb | ||
|
24121bc703 | ||
|
9fc87fa767 | ||
|
328f82d59a | ||
|
78717fc328 | ||
|
3b35c3425e | ||
|
874ae0354e | ||
|
4c6b4764f0 | ||
|
59ee8a8647 | ||
|
af284305d5 | ||
|
d53a4af1a4 | ||
|
2e1b928540 | ||
|
040ac68679 | ||
|
049d71d874 | ||
|
bf2c8c8f82 | ||
|
ef428960c9 | ||
|
992fc9d6e1 | ||
|
8639f89f51 | ||
|
0424ec307b | ||
|
ac5a69af45 | ||
|
94e8c80473 | ||
|
87f0e62d94 | ||
|
46b4070f3f | ||
|
2a776f9788 | ||
|
f4c7ef9862 | ||
|
50e12e9df1 | ||
|
b7faebbac8 | ||
|
4191fdf147 | ||
|
9a4f12be98 | ||
|
7ad4258add | ||
|
9945c4994c | ||
|
5faf9fed7e | ||
|
13a9b69b09 | ||
|
4975650e00 | ||
|
0cc7178546 | ||
|
41f24c321d | ||
|
4b3fbafdd2 | ||
|
7ac40086f5 | ||
|
313dfc45f5 | ||
|
78a55d7a28 | ||
|
bb6ac83698 | ||
|
9d0e366880 | ||
|
9bff48a0e7 | ||
|
60121eb514 | ||
|
527ca1da4f | ||
|
7689413e42 | ||
|
ba7a92b0ce | ||
|
4c7d816dd7 | ||
|
032f2f260f | ||
|
20e98bf6c0 | ||
|
5c2266df4b | ||
|
67dda51722 | ||
|
e4c4bcf36f | ||
|
82d8a8b6e2 | ||
|
13a10d5aa3 | ||
|
9022726446 | ||
|
94bfcd23b7 | ||
|
526b3b0716 | ||
|
61f92af1cf | ||
|
a72778d364 | ||
|
5ae17037a3 | ||
|
02f0da20b0 | ||
|
b41631c4e6 | ||
|
0e49d9a6b0 | ||
|
4a7d108ab3 | ||
|
3cfd000849 | ||
|
1b38185361 | ||
|
9cb9a5df77 | ||
|
5035536e3f | ||
|
3e12bc583a | ||
|
e568c2233e | ||
|
061a75edd6 | ||
|
82c4d7b0ce | ||
|
136dadde95 | ||
|
0c14841585 | ||
|
0eebf34d9d | ||
|
cf186b77a7 | ||
|
a3372437bf | ||
|
4c57b4853d | ||
|
38eb2968ab | ||
|
bea56c9569 | ||
|
7e508ff2cf | ||
|
563772eda4 | ||
|
0533915aad | ||
|
c3a227d1c4 | ||
|
f6c903e708 | ||
|
7dc011c063 | ||
|
4e3b303016 | ||
|
7e1f5447e7 | ||
|
7e3472758b | ||
|
328a22e175 | ||
|
417b453699 | ||
|
6ea7190a3e | ||
|
b54b08c91b | ||
|
c30943b1c0 | ||
|
2abf7cab80 | ||
|
4137196899 | ||
|
019839faaa | ||
|
f52183a878 | ||
|
750b9ff032 | ||
|
28602e747c | ||
|
6cc37c69e2 | ||
|
a5cd0eb8a4 | ||
|
c23e266427 | ||
|
651acffbe5 | ||
|
71bd93b89c | ||
|
6da620de58 | ||
|
bdceea7afd | ||
|
d80a39cec8 | ||
|
5b5fae5f20 | ||
|
01b06aedcf | ||
|
c711383811 | ||
|
17cc153435 | ||
|
67446fd49b | ||
|
325bb615a7 | ||
|
ee5cd8418e | ||
|
342609a1b4 | ||
|
f270cf1a26 | ||
|
371c3b796c | ||
|
6b7ceee1b9 | ||
|
fdb20a27a3 | ||
|
2c94198eb6 | ||
|
e8110b8125 | ||
|
c39fd7b1ca | ||
|
a9c09a7c62 | ||
|
82beaabb41 | ||
|
63b4295d20 | ||
|
312a3f389b | ||
|
609af1ae1c | ||
|
4cd759f73d | ||
|
e156e70281 | ||
|
9b464929fe | ||
|
0c176d7bde | ||
|
7a3f0c00ad | ||
|
7aefc49c40 | ||
|
741dd8ea65 | ||
|
76adc82068 | ||
|
bd1512d196 | ||
|
9a4acbfaf5 | ||
|
ad1f4e7902 | ||
|
b328295910 | ||
|
828b2a5cd9 | ||
|
2ff7cbeaaa | ||
|
b2f7738830 | ||
|
dc0279532a | ||
|
0c59d02bdc | ||
|
0f72beb515 | ||
|
d781e29316 | ||
|
3b3e8ed332 | ||
|
dcdfeb33d2 | ||
|
0d85c3a732 | ||
|
903d136942 | ||
|
9d584da7d0 | ||
|
31752f76f7 | ||
|
5f1b2aea80 | ||
|
4479600d57 | ||
|
a90189c3ad | ||
|
d8a1caf04f | ||
|
cb33d389ed | ||
|
967e0955f0 | ||
|
e01b432ad3 | ||
|
fd91257c40 | ||
|
c7b959ce38 | ||
|
75eac8961e | ||
|
3b7d9aa487 | ||
|
1f4b722b00 | ||
|
f6519f89b0 | ||
|
24af85298e | ||
|
e721d857c2 | ||
|
5c17f0a67a | ||
|
4fcaa4f4a5 | ||
|
536f819eda | ||
|
a662489877 | ||
|
a2973eb597 | ||
|
4e21b3a94f | ||
|
b703ebeeaf | ||
|
b84a5f0337 | ||
|
a1ec9a7553 | ||
|
91d644b5ba | ||
|
5d6c3d6a66 | ||
|
1ebb4717df | ||
|
cf5881fc4d | ||
|
fcd817a326 | ||
|
031ec536f0 | ||
|
668db403f9 | ||
|
b9ad101926 | ||
|
435911029f | ||
|
699ed30cee | ||
|
9eab37dca0 | ||
|
9a8a12b7d8 | ||
|
a4c2ab35c1 | ||
|
3d9c4bf09a | ||
|
8b8a39e279 | ||
|
82393e2bb2 | ||
|
2eb99a4b98 | ||
|
6abce58a12 | ||
|
990e6e8fa3 | ||
|
bfd88516eb | ||
|
d8b7e80d29 | ||
|
37120974dc | ||
|
42fc93c709 | ||
|
a625e56543 | ||
|
9b738b2caa | ||
|
90bb5667bf | ||
|
d3d3e2e3aa | ||
|
9550ca506f | ||
|
240384afe6 | ||
|
9a605c8859 | ||
|
402ca40c9d | ||
|
30bd1c16c8 | ||
|
497f5fd93f | ||
|
4bf5614195 | ||
|
520e753390 | ||
|
355c7ad361 | ||
|
55af2b26e0 | ||
|
d90e40305b | ||
|
cce9d15d01 | ||
|
dd414c970b | ||
|
497ca088a6 | ||
|
90bddb6cdd | ||
|
6a11bb77ba | ||
|
ecf6de5b02 | ||
|
139f27827e | ||
|
30787f7259 | ||
|
c233e6bcc3 | ||
|
28809ab07a | ||
|
8fc226ef99 | ||
|
c01e1a96aa | ||
|
53407e3f38 | ||
|
ed1269000f | ||
|
689fb748ee |
5
AUTHORS
5
AUTHORS
@@ -144,3 +144,8 @@ Lee Jenkins
|
|||||||
Anssi Hannula
|
Anssi Hannula
|
||||||
Lukáš Lalinský
|
Lukáš Lalinský
|
||||||
Qijiang Fan
|
Qijiang Fan
|
||||||
|
Rémy Léone
|
||||||
|
Marco Ferragina
|
||||||
|
reiv
|
||||||
|
Muratcan Simsek
|
||||||
|
Evan Lu
|
||||||
|
@@ -1,4 +1,18 @@
|
|||||||
**Please include the full output of youtube-dl when run with `-v`**.
|
**Please include the full output of youtube-dl when run with `-v`**, i.e. add `-v` flag to your command line, copy the **whole** output and post it in the issue body wrapped in \`\`\` for better formatting. It should look similar to this:
|
||||||
|
```
|
||||||
|
$ youtube-dl -v http://www.youtube.com/watch?v=BaW_jenozKcj
|
||||||
|
[debug] System config: []
|
||||||
|
[debug] User config: []
|
||||||
|
[debug] Command-line args: [u'-v', u'http://www.youtube.com/watch?v=BaW_jenozKcj']
|
||||||
|
[debug] Encodings: locale cp1251, fs mbcs, out cp866, pref cp1251
|
||||||
|
[debug] youtube-dl version 2015.12.06
|
||||||
|
[debug] Git HEAD: 135392e
|
||||||
|
[debug] Python version 2.6.6 - Windows-2003Server-5.2.3790-SP2
|
||||||
|
[debug] exe versions: ffmpeg N-75573-g1d0487f, ffprobe N-75573-g1d0487f, rtmpdump 2.4
|
||||||
|
[debug] Proxy map: {}
|
||||||
|
...
|
||||||
|
```
|
||||||
|
**Do not post screenshots of verbose log only plain text is acceptable.**
|
||||||
|
|
||||||
The output (including the first lines) contains important debugging information. Issues without the full output are often not reproducible and therefore do not get solved in short order, if ever.
|
The output (including the first lines) contains important debugging information. Issues without the full output are often not reproducible and therefore do not get solved in short order, if ever.
|
||||||
|
|
||||||
@@ -20,7 +34,7 @@ For bug reports, this means that your report should contain the *complete* outpu
|
|||||||
|
|
||||||
If your server has multiple IPs or you suspect censorship, adding `--call-home` may be a good idea to get more diagnostics. If the error is `ERROR: Unable to extract ...` and you cannot reproduce it from multiple countries, add `--dump-pages` (warning: this will yield a rather large output, redirect it to the file `log.txt` by adding `>log.txt 2>&1` to your command-line) or upload the `.dump` files you get when you add `--write-pages` [somewhere](https://gist.github.com/).
|
If your server has multiple IPs or you suspect censorship, adding `--call-home` may be a good idea to get more diagnostics. If the error is `ERROR: Unable to extract ...` and you cannot reproduce it from multiple countries, add `--dump-pages` (warning: this will yield a rather large output, redirect it to the file `log.txt` by adding `>log.txt 2>&1` to your command-line) or upload the `.dump` files you get when you add `--write-pages` [somewhere](https://gist.github.com/).
|
||||||
|
|
||||||
**Site support requests must contain an example URL**. An example URL is a URL you might want to download, like http://www.youtube.com/watch?v=BaW_jenozKc . There should be an obvious video present. Except under very special circumstances, the main page of a video service (e.g. http://www.youtube.com/ ) is *not* an example URL.
|
**Site support requests must contain an example URL**. An example URL is a URL you might want to download, like `http://www.youtube.com/watch?v=BaW_jenozKc`. There should be an obvious video present. Except under very special circumstances, the main page of a video service (e.g. `http://www.youtube.com/`) is *not* an example URL.
|
||||||
|
|
||||||
### Are you using the latest version?
|
### Are you using the latest version?
|
||||||
|
|
||||||
@@ -28,7 +42,7 @@ Before reporting any issue, type `youtube-dl -U`. This should report that you're
|
|||||||
|
|
||||||
### Is the issue already documented?
|
### Is the issue already documented?
|
||||||
|
|
||||||
Make sure that someone has not already opened the issue you're trying to open. Search at the top of the window or at https://github.com/rg3/youtube-dl/search?type=Issues . If there is an issue, feel free to write something along the lines of "This affects me as well, with version 2015.01.01. Here is some more information on the issue: ...". While some issues may be old, a new post into them often spurs rapid activity.
|
Make sure that someone has not already opened the issue you're trying to open. Search at the top of the window or browse the [GitHub Issues](https://github.com/rg3/youtube-dl/search?type=Issues) of this repository. If there is an issue, feel free to write something along the lines of "This affects me as well, with version 2015.01.01. Here is some more information on the issue: ...". While some issues may be old, a new post into them often spurs rapid activity.
|
||||||
|
|
||||||
### Why are existing options not enough?
|
### Why are existing options not enough?
|
||||||
|
|
||||||
|
14
Makefile
14
Makefile
@@ -61,34 +61,34 @@ youtube-dl: youtube_dl/*.py youtube_dl/*/*.py
|
|||||||
chmod a+x youtube-dl
|
chmod a+x youtube-dl
|
||||||
|
|
||||||
README.md: youtube_dl/*.py youtube_dl/*/*.py
|
README.md: youtube_dl/*.py youtube_dl/*/*.py
|
||||||
COLUMNS=80 python youtube_dl/__main__.py --help | python devscripts/make_readme.py
|
COLUMNS=80 $(PYTHON) youtube_dl/__main__.py --help | $(PYTHON) devscripts/make_readme.py
|
||||||
|
|
||||||
CONTRIBUTING.md: README.md
|
CONTRIBUTING.md: README.md
|
||||||
python devscripts/make_contributing.py README.md CONTRIBUTING.md
|
$(PYTHON) devscripts/make_contributing.py README.md CONTRIBUTING.md
|
||||||
|
|
||||||
supportedsites:
|
supportedsites:
|
||||||
python devscripts/make_supportedsites.py docs/supportedsites.md
|
$(PYTHON) devscripts/make_supportedsites.py docs/supportedsites.md
|
||||||
|
|
||||||
README.txt: README.md
|
README.txt: README.md
|
||||||
pandoc -f markdown -t plain README.md -o README.txt
|
pandoc -f markdown -t plain README.md -o README.txt
|
||||||
|
|
||||||
youtube-dl.1: README.md
|
youtube-dl.1: README.md
|
||||||
python devscripts/prepare_manpage.py >youtube-dl.1.temp.md
|
$(PYTHON) devscripts/prepare_manpage.py >youtube-dl.1.temp.md
|
||||||
pandoc -s -f markdown -t man youtube-dl.1.temp.md -o youtube-dl.1
|
pandoc -s -f markdown -t man youtube-dl.1.temp.md -o youtube-dl.1
|
||||||
rm -f youtube-dl.1.temp.md
|
rm -f youtube-dl.1.temp.md
|
||||||
|
|
||||||
youtube-dl.bash-completion: youtube_dl/*.py youtube_dl/*/*.py devscripts/bash-completion.in
|
youtube-dl.bash-completion: youtube_dl/*.py youtube_dl/*/*.py devscripts/bash-completion.in
|
||||||
python devscripts/bash-completion.py
|
$(PYTHON) devscripts/bash-completion.py
|
||||||
|
|
||||||
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
|
youtube-dl.zsh: youtube_dl/*.py youtube_dl/*/*.py devscripts/zsh-completion.in
|
||||||
python devscripts/zsh-completion.py
|
$(PYTHON) devscripts/zsh-completion.py
|
||||||
|
|
||||||
zsh-completion: youtube-dl.zsh
|
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
|
||||||
|
|
||||||
|
49
README.md
49
README.md
@@ -35,7 +35,7 @@ You can also use pip:
|
|||||||
|
|
||||||
sudo pip install youtube-dl
|
sudo pip install youtube-dl
|
||||||
|
|
||||||
Alternatively, refer to the [developer instructions](#developer-instructions) for how to check out and work with the git repository. For further options, including PGP signatures, see https://rg3.github.io/youtube-dl/download.html .
|
Alternatively, refer to the [developer instructions](#developer-instructions) for how to check out and work with the git repository. For further options, including PGP signatures, see the [youtube-dl Download Page](https://rg3.github.io/youtube-dl/download.html).
|
||||||
|
|
||||||
# DESCRIPTION
|
# DESCRIPTION
|
||||||
**youtube-dl** is a small command-line program to download videos from
|
**youtube-dl** is a small command-line program to download videos from
|
||||||
@@ -319,7 +319,8 @@ which means you can modify it, redistribute it or use it however you like.
|
|||||||
--all-formats Download all available video formats
|
--all-formats Download all available video formats
|
||||||
--prefer-free-formats Prefer free video formats unless a specific
|
--prefer-free-formats Prefer free video formats unless a specific
|
||||||
one is requested
|
one is requested
|
||||||
-F, --list-formats List all available formats
|
-F, --list-formats List all available formats of requested
|
||||||
|
videos
|
||||||
--youtube-skip-dash-manifest Do not download the DASH manifests and
|
--youtube-skip-dash-manifest Do not download the DASH manifests and
|
||||||
related data on YouTube videos
|
related data on YouTube videos
|
||||||
--merge-output-format FORMAT If a merge is required (e.g.
|
--merge-output-format FORMAT If a merge is required (e.g.
|
||||||
@@ -329,8 +330,8 @@ which means you can modify it, redistribute it or use it however you like.
|
|||||||
|
|
||||||
## Subtitle Options:
|
## Subtitle Options:
|
||||||
--write-sub Write subtitle file
|
--write-sub Write subtitle file
|
||||||
--write-auto-sub Write automatic subtitle file (YouTube
|
--write-auto-sub Write automatically generated subtitle file
|
||||||
only)
|
(YouTube only)
|
||||||
--all-subs Download all the available subtitles of the
|
--all-subs Download all the available subtitles of the
|
||||||
video
|
video
|
||||||
--list-subs List all available subtitles for the video
|
--list-subs List all available subtitles for the video
|
||||||
@@ -413,7 +414,7 @@ You can configure youtube-dl by placing any supported command line option to a c
|
|||||||
|
|
||||||
You can use `--ignore-config` if you want to disable the configuration file for a particular youtube-dl run.
|
You can use `--ignore-config` if you want to disable the configuration file for a particular youtube-dl run.
|
||||||
|
|
||||||
### Authentication with `.netrc` file ###
|
### Authentication with `.netrc` file
|
||||||
|
|
||||||
You may also want to configure automatic credentials storage for extractors that support authentication (by providing login and password with `--username` and `--password`) in order not to pass credentials as command line arguments on every youtube-dl execution and prevent tracking plain text passwords in the shell command history. You can achieve this using a [`.netrc` file](http://stackoverflow.com/tags/.netrc/info) on per extractor basis. For that you will need to create a`.netrc` file in your `$HOME` and restrict permissions to read/write by you only:
|
You may also want to configure automatic credentials storage for extractors that support authentication (by providing login and password with `--username` and `--password`) in order not to pass credentials as command line arguments on every youtube-dl execution and prevent tracking plain text passwords in the shell command history. You can achieve this using a [`.netrc` file](http://stackoverflow.com/tags/.netrc/info) on per extractor basis. For that you will need to create a`.netrc` file in your `$HOME` and restrict permissions to read/write by you only:
|
||||||
```
|
```
|
||||||
@@ -534,6 +535,12 @@ Most people asking this question are not aware that youtube-dl now defaults to d
|
|||||||
|
|
||||||
Apparently YouTube requires you to pass a CAPTCHA test if you download too much. We're [considering to provide a way to let you solve the CAPTCHA](https://github.com/rg3/youtube-dl/issues/154), but at the moment, your best course of action is pointing a webbrowser to the youtube URL, solving the CAPTCHA, and restart youtube-dl.
|
Apparently YouTube requires you to pass a CAPTCHA test if you download too much. We're [considering to provide a way to let you solve the CAPTCHA](https://github.com/rg3/youtube-dl/issues/154), but at the moment, your best course of action is pointing a webbrowser to the youtube URL, solving the CAPTCHA, and restart youtube-dl.
|
||||||
|
|
||||||
|
### Do I need any other programs?
|
||||||
|
|
||||||
|
youtube-dl works fine on its own on most sites. However, if you want to convert video/audio, you'll need [avconv](https://libav.org/) or [ffmpeg](https://www.ffmpeg.org/). On some sites - most notably YouTube - videos can be retrieved in a higher quality format without sound. youtube-dl will detect whether avconv/ffmpeg is present and automatically pick the best option.
|
||||||
|
|
||||||
|
Videos or video formats streamed via RTMP protocol can only be downloaded when [rtmpdump](https://rtmpdump.mplayerhq.hu/) is installed. Downloading MMS and RTSP videos requires either [mplayer](http://mplayerhq.hu/) or [mpv](https://mpv.io/) to be installed.
|
||||||
|
|
||||||
### I have downloaded a video but how can I play it?
|
### I have downloaded a video but how can I play it?
|
||||||
|
|
||||||
Once the video is fully downloaded, use any video player, such as [vlc](http://www.videolan.org) or [mplayer](http://www.mplayerhq.hu/).
|
Once the video is fully downloaded, use any video player, such as [vlc](http://www.videolan.org) or [mplayer](http://www.mplayerhq.hu/).
|
||||||
@@ -552,11 +559,11 @@ If you want to play the video on a machine that is not running youtube-dl, you c
|
|||||||
|
|
||||||
YouTube has switched to a new video info format in July 2011 which is not supported by old versions of youtube-dl. See [above](#how-do-i-update-youtube-dl) for how to update youtube-dl.
|
YouTube has switched to a new video info format in July 2011 which is not supported by old versions of youtube-dl. See [above](#how-do-i-update-youtube-dl) for how to update youtube-dl.
|
||||||
|
|
||||||
### ERROR: unable to download video ###
|
### ERROR: unable to download video
|
||||||
|
|
||||||
YouTube requires an additional signature since September 2012 which is not supported by old versions of youtube-dl. See [above](#how-do-i-update-youtube-dl) for how to update youtube-dl.
|
YouTube requires an additional signature since September 2012 which is not supported by old versions of youtube-dl. See [above](#how-do-i-update-youtube-dl) for how to update youtube-dl.
|
||||||
|
|
||||||
### Video URL contains an ampersand and I'm getting some strange output `[1] 2839` or `'v' is not recognized as an internal or external command` ###
|
### Video URL contains an ampersand and I'm getting some strange output `[1] 2839` or `'v' is not recognized as an internal or external command`
|
||||||
|
|
||||||
That's actually the output from your shell. Since ampersand is one of the special shell characters it's interpreted by the shell preventing you from passing the whole URL to youtube-dl. To disable your shell from interpreting the ampersands (or any other special characters) you have to either put the whole URL in quotes or escape them with a backslash (which approach will work depends on your shell).
|
That's actually the output from your shell. Since ampersand is one of the special shell characters it's interpreted by the shell preventing you from passing the whole URL to youtube-dl. To disable your shell from interpreting the ampersands (or any other special characters) you have to either put the whole URL in quotes or escape them with a backslash (which approach will work depends on your shell).
|
||||||
|
|
||||||
@@ -580,7 +587,7 @@ In February 2015, the new YouTube player contained a character sequence in a str
|
|||||||
|
|
||||||
These two error codes indicate that the service is blocking your IP address because of overuse. Contact the service and ask them to unblock your IP address, or - if you have acquired a whitelisted IP address already - use the [`--proxy` or `--source-address` options](#network-options) to select another IP address.
|
These two error codes indicate that the service is blocking your IP address because of overuse. Contact the service and ask them to unblock your IP address, or - if you have acquired a whitelisted IP address already - use the [`--proxy` or `--source-address` options](#network-options) to select another IP address.
|
||||||
|
|
||||||
### SyntaxError: Non-ASCII character ###
|
### SyntaxError: Non-ASCII character
|
||||||
|
|
||||||
The error
|
The error
|
||||||
|
|
||||||
@@ -609,7 +616,7 @@ From then on, after restarting your shell, you will be able to access both youtu
|
|||||||
|
|
||||||
Use the `-o` to specify an [output template](#output-template), for example `-o "/home/user/videos/%(title)s-%(id)s.%(ext)s"`. If you want this for all of your downloads, put the option into your [configuration file](#configuration).
|
Use the `-o` to specify an [output template](#output-template), for example `-o "/home/user/videos/%(title)s-%(id)s.%(ext)s"`. If you want this for all of your downloads, put the option into your [configuration file](#configuration).
|
||||||
|
|
||||||
### How do I download a video starting with a `-` ?
|
### How do I download a video starting with a `-`?
|
||||||
|
|
||||||
Either prepend `http://www.youtube.com/watch?v=` or separate the ID from the options with `--`:
|
Either prepend `http://www.youtube.com/watch?v=` or separate the ID from the options with `--`:
|
||||||
|
|
||||||
@@ -791,9 +798,23 @@ with youtube_dl.YoutubeDL(ydl_opts) as ydl:
|
|||||||
|
|
||||||
# BUGS
|
# BUGS
|
||||||
|
|
||||||
Bugs and suggestions should be reported at: <https://github.com/rg3/youtube-dl/issues> . Unless you were prompted so or there is another pertinent reason (e.g. GitHub fails to accept the bug report), please do not send bug reports via personal email. For discussions, join us in the irc channel #youtube-dl on freenode.
|
Bugs and suggestions should be reported at: <https://github.com/rg3/youtube-dl/issues>. Unless you were prompted so or there is another pertinent reason (e.g. GitHub fails to accept the bug report), please do not send bug reports via personal email. For discussions, join us in the IRC channel [#youtube-dl](irc://chat.freenode.net/#youtube-dl) on freenode ([webchat](http://webchat.freenode.net/?randomnick=1&channels=youtube-dl)).
|
||||||
|
|
||||||
**Please include the full output of youtube-dl when run with `-v`**.
|
**Please include the full output of youtube-dl when run with `-v`**, i.e. add `-v` flag to your command line, copy the **whole** output and post it in the issue body wrapped in \`\`\` for better formatting. It should look similar to this:
|
||||||
|
```
|
||||||
|
$ youtube-dl -v http://www.youtube.com/watch?v=BaW_jenozKcj
|
||||||
|
[debug] System config: []
|
||||||
|
[debug] User config: []
|
||||||
|
[debug] Command-line args: [u'-v', u'http://www.youtube.com/watch?v=BaW_jenozKcj']
|
||||||
|
[debug] Encodings: locale cp1251, fs mbcs, out cp866, pref cp1251
|
||||||
|
[debug] youtube-dl version 2015.12.06
|
||||||
|
[debug] Git HEAD: 135392e
|
||||||
|
[debug] Python version 2.6.6 - Windows-2003Server-5.2.3790-SP2
|
||||||
|
[debug] exe versions: ffmpeg N-75573-g1d0487f, ffprobe N-75573-g1d0487f, rtmpdump 2.4
|
||||||
|
[debug] Proxy map: {}
|
||||||
|
...
|
||||||
|
```
|
||||||
|
**Do not post screenshots of verbose log only plain text is acceptable.**
|
||||||
|
|
||||||
The output (including the first lines) contains important debugging information. Issues without the full output are often not reproducible and therefore do not get solved in short order, if ever.
|
The output (including the first lines) contains important debugging information. Issues without the full output are often not reproducible and therefore do not get solved in short order, if ever.
|
||||||
|
|
||||||
@@ -815,7 +836,7 @@ For bug reports, this means that your report should contain the *complete* outpu
|
|||||||
|
|
||||||
If your server has multiple IPs or you suspect censorship, adding `--call-home` may be a good idea to get more diagnostics. If the error is `ERROR: Unable to extract ...` and you cannot reproduce it from multiple countries, add `--dump-pages` (warning: this will yield a rather large output, redirect it to the file `log.txt` by adding `>log.txt 2>&1` to your command-line) or upload the `.dump` files you get when you add `--write-pages` [somewhere](https://gist.github.com/).
|
If your server has multiple IPs or you suspect censorship, adding `--call-home` may be a good idea to get more diagnostics. If the error is `ERROR: Unable to extract ...` and you cannot reproduce it from multiple countries, add `--dump-pages` (warning: this will yield a rather large output, redirect it to the file `log.txt` by adding `>log.txt 2>&1` to your command-line) or upload the `.dump` files you get when you add `--write-pages` [somewhere](https://gist.github.com/).
|
||||||
|
|
||||||
**Site support requests must contain an example URL**. An example URL is a URL you might want to download, like http://www.youtube.com/watch?v=BaW_jenozKc . There should be an obvious video present. Except under very special circumstances, the main page of a video service (e.g. http://www.youtube.com/ ) is *not* an example URL.
|
**Site support requests must contain an example URL**. An example URL is a URL you might want to download, like `http://www.youtube.com/watch?v=BaW_jenozKc`. There should be an obvious video present. Except under very special circumstances, the main page of a video service (e.g. `http://www.youtube.com/`) is *not* an example URL.
|
||||||
|
|
||||||
### Are you using the latest version?
|
### Are you using the latest version?
|
||||||
|
|
||||||
@@ -823,7 +844,7 @@ Before reporting any issue, type `youtube-dl -U`. This should report that you're
|
|||||||
|
|
||||||
### Is the issue already documented?
|
### Is the issue already documented?
|
||||||
|
|
||||||
Make sure that someone has not already opened the issue you're trying to open. Search at the top of the window or at https://github.com/rg3/youtube-dl/search?type=Issues . If there is an issue, feel free to write something along the lines of "This affects me as well, with version 2015.01.01. Here is some more information on the issue: ...". While some issues may be old, a new post into them often spurs rapid activity.
|
Make sure that someone has not already opened the issue you're trying to open. Search at the top of the window or browse the [GitHub Issues](https://github.com/rg3/youtube-dl/search?type=Issues) of this repository. If there is an issue, feel free to write something along the lines of "This affects me as well, with version 2015.01.01. Here is some more information on the issue: ...". While some issues may be old, a new post into them often spurs rapid activity.
|
||||||
|
|
||||||
### Why are existing options not enough?
|
### Why are existing options not enough?
|
||||||
|
|
||||||
@@ -853,4 +874,4 @@ It may sound strange, but some bug reports we receive are completely unrelated t
|
|||||||
|
|
||||||
youtube-dl is released into the public domain by the copyright holders.
|
youtube-dl is released into the public domain by the copyright holders.
|
||||||
|
|
||||||
This README file was originally written by Daniel Bolton (<https://github.com/dbbolton>) and is likewise released into the public domain.
|
This README file was originally written by [Daniel Bolton](https://github.com/dbbolton) and is likewise released into the public domain.
|
||||||
|
@@ -15,8 +15,12 @@
|
|||||||
- **abc.net.au**
|
- **abc.net.au**
|
||||||
- **Abc7News**
|
- **Abc7News**
|
||||||
- **AcademicEarth:Course**
|
- **AcademicEarth:Course**
|
||||||
|
- **acast**
|
||||||
|
- **acast:channel**
|
||||||
- **AddAnime**
|
- **AddAnime**
|
||||||
- **AdobeTV**
|
- **AdobeTV**
|
||||||
|
- **AdobeTVChannel**
|
||||||
|
- **AdobeTVShow**
|
||||||
- **AdobeTVVideo**
|
- **AdobeTVVideo**
|
||||||
- **AdultSwim**
|
- **AdultSwim**
|
||||||
- **Aftenposten**
|
- **Aftenposten**
|
||||||
@@ -43,6 +47,7 @@
|
|||||||
- **arte.tv:future**
|
- **arte.tv:future**
|
||||||
- **AtresPlayer**
|
- **AtresPlayer**
|
||||||
- **ATTTechChannel**
|
- **ATTTechChannel**
|
||||||
|
- **AudiMedia**
|
||||||
- **audiomack**
|
- **audiomack**
|
||||||
- **audiomack:album**
|
- **audiomack:album**
|
||||||
- **Azubu**
|
- **Azubu**
|
||||||
@@ -67,7 +72,8 @@
|
|||||||
- **Bpb**: Bundeszentrale für politische Bildung
|
- **Bpb**: Bundeszentrale für politische Bildung
|
||||||
- **BR**: Bayerischer Rundfunk Mediathek
|
- **BR**: Bayerischer Rundfunk Mediathek
|
||||||
- **Break**
|
- **Break**
|
||||||
- **Brightcove**
|
- **brightcove:legacy**
|
||||||
|
- **brightcove:new**
|
||||||
- **bt:article**: Bergens Tidende Articles
|
- **bt:article**: Bergens Tidende Articles
|
||||||
- **bt:vestlendingen**: Bergens Tidende - Vestlendingen
|
- **bt:vestlendingen**: Bergens Tidende - Vestlendingen
|
||||||
- **BuzzFeed**
|
- **BuzzFeed**
|
||||||
@@ -91,6 +97,7 @@
|
|||||||
- **Clipfish**
|
- **Clipfish**
|
||||||
- **cliphunter**
|
- **cliphunter**
|
||||||
- **Clipsyndicate**
|
- **Clipsyndicate**
|
||||||
|
- **cloudtime**: CloudTime
|
||||||
- **Cloudy**
|
- **Cloudy**
|
||||||
- **Clubic**
|
- **Clubic**
|
||||||
- **Clyp**
|
- **Clyp**
|
||||||
@@ -128,6 +135,7 @@
|
|||||||
- **Discovery**
|
- **Discovery**
|
||||||
- **Dotsub**
|
- **Dotsub**
|
||||||
- **DouyuTV**: 斗鱼
|
- **DouyuTV**: 斗鱼
|
||||||
|
- **DPlay**
|
||||||
- **dramafever**
|
- **dramafever**
|
||||||
- **dramafever:series**
|
- **dramafever:series**
|
||||||
- **DRBonanza**
|
- **DRBonanza**
|
||||||
@@ -180,7 +188,9 @@
|
|||||||
- **Freesound**
|
- **Freesound**
|
||||||
- **freespeech.org**
|
- **freespeech.org**
|
||||||
- **FreeVideo**
|
- **FreeVideo**
|
||||||
|
- **Funimation**
|
||||||
- **FunnyOrDie**
|
- **FunnyOrDie**
|
||||||
|
- **GameInformer**
|
||||||
- **Gamekings**
|
- **Gamekings**
|
||||||
- **GameOne**
|
- **GameOne**
|
||||||
- **gameone:playlist**
|
- **gameone:playlist**
|
||||||
@@ -200,7 +210,6 @@
|
|||||||
- **GodTube**
|
- **GodTube**
|
||||||
- **GoldenMoustache**
|
- **GoldenMoustache**
|
||||||
- **Golem**
|
- **Golem**
|
||||||
- **GorillaVid**: GorillaVid.in, daclips.in, movpod.in, fastvideo.in, realvid.net and filehoot.com
|
|
||||||
- **Goshgay**
|
- **Goshgay**
|
||||||
- **Groupon**
|
- **Groupon**
|
||||||
- **Hark**
|
- **Hark**
|
||||||
@@ -306,7 +315,6 @@
|
|||||||
- **MovieClips**
|
- **MovieClips**
|
||||||
- **MovieFap**
|
- **MovieFap**
|
||||||
- **Moviezine**
|
- **Moviezine**
|
||||||
- **movshare**: MovShare
|
|
||||||
- **MPORA**
|
- **MPORA**
|
||||||
- **MSNBC**
|
- **MSNBC**
|
||||||
- **MTV**
|
- **MTV**
|
||||||
@@ -367,6 +375,7 @@
|
|||||||
- **nowness:playlist**
|
- **nowness:playlist**
|
||||||
- **nowness:series**
|
- **nowness:series**
|
||||||
- **NowTV**
|
- **NowTV**
|
||||||
|
- **NowTVList**
|
||||||
- **nowvideo**: NowVideo
|
- **nowvideo**: NowVideo
|
||||||
- **npo**: npo.nl and ntr.nl
|
- **npo**: npo.nl and ntr.nl
|
||||||
- **npo.nl:live**
|
- **npo.nl:live**
|
||||||
@@ -392,7 +401,7 @@
|
|||||||
- **orf:tvthek**: ORF TVthek
|
- **orf:tvthek**: ORF TVthek
|
||||||
- **parliamentlive.tv**: UK parliament videos
|
- **parliamentlive.tv**: UK parliament videos
|
||||||
- **Patreon**
|
- **Patreon**
|
||||||
- **PBS**
|
- **pbs**: Public Broadcasting Service (PBS) and member stations: PBS: Public Broadcasting Service, APT - Alabama Public Television (WBIQ), GPB/Georgia Public Broadcasting (WGTV), Mississippi Public Broadcasting (WMPN), Nashville Public Television (WNPT), WFSU-TV (WFSU), WSRE (WSRE), WTCI (WTCI), WPBA/Channel 30 (WPBA), Alaska Public Media (KAKM), Arizona PBS (KAET), KNME-TV/Channel 5 (KNME), Vegas PBS (KLVX), AETN/ARKANSAS ETV NETWORK (KETS), KET (WKLE), WKNO/Channel 10 (WKNO), LPB/LOUISIANA PUBLIC BROADCASTING (WLPB), OETA (KETA), Ozarks Public Television (KOZK), WSIU Public Broadcasting (WSIU), KEET TV (KEET), KIXE/Channel 9 (KIXE), KPBS San Diego (KPBS), KQED (KQED), KVIE Public Television (KVIE), PBS SoCal/KOCE (KOCE), ValleyPBS (KVPT), CONNECTICUT PUBLIC TELEVISION (WEDH), KNPB Channel 5 (KNPB), SOPTV (KSYS), Rocky Mountain PBS (KRMA), KENW-TV3 (KENW), KUED Channel 7 (KUED), Wyoming PBS (KCWC), Colorado Public Television / KBDI 12 (KBDI), KBYU-TV (KBYU), Thirteen/WNET New York (WNET), WGBH/Channel 2 (WGBH), WGBY (WGBY), NJTV Public Media NJ (WNJT), WLIW21 (WLIW), mpt/Maryland Public Television (WMPB), WETA Television and Radio (WETA), WHYY (WHYY), PBS 39 (WLVT), WVPT - Your Source for PBS and More! (WVPT), Howard University Television (WHUT), WEDU PBS (WEDU), WGCU Public Media (WGCU), WPBT2 (WPBT), WUCF TV (WUCF), WUFT/Channel 5 (WUFT), WXEL/Channel 42 (WXEL), WLRN/Channel 17 (WLRN), WUSF Public Broadcasting (WUSF), ETV (WRLK), UNC-TV (WUNC), PBS Hawaii - Oceanic Cable Channel 10 (KHET), Idaho Public Television (KAID), KSPS (KSPS), OPB (KOPB), KWSU/Channel 10 & KTNW/Channel 31 (KWSU), WILL-TV (WILL), Network Knowledge - WSEC/Springfield (WSEC), WTTW11 (WTTW), Iowa Public Television/IPTV (KDIN), Nine Network (KETC), PBS39 Fort Wayne (WFWA), WFYI Indianapolis (WFYI), Milwaukee Public Television (WMVS), WNIN (WNIN), WNIT Public Television (WNIT), WPT (WPNE), WVUT/Channel 22 (WVUT), WEIU/Channel 51 (WEIU), WQPT-TV (WQPT), WYCC PBS Chicago (WYCC), WIPB-TV (WIPB), WTIU (WTIU), CET (WCET), ThinkTVNetwork (WPTD), WBGU-TV (WBGU), WGVU TV (WGVU), NET1 (KUON), Pioneer Public Television (KWCM), SDPB Television (KUSD), TPT (KTCA), KSMQ (KSMQ), KPTS/Channel 8 (KPTS), KTWU/Channel 11 (KTWU), East Tennessee PBS (WSJK), WCTE-TV (WCTE), WLJT, Channel 11 (WLJT), WOSU TV (WOSU), WOUB/WOUC (WOUB), WVPB (WVPB), WKYU-PBS (WKYU), KERA 13 (KERA), MPBN (WCBB), Mountain Lake PBS (WCFE), NHPTV (WENH), Vermont PBS (WETK), witf (WITF), WQED Multimedia (WQED), WMHT Educational Telecommunications (WMHT), Q-TV (WDCQ), WTVS Detroit Public TV (WTVS), CMU Public Television (WCMU), WKAR-TV (WKAR), WNMU-TV Public TV 13 (WNMU), WDSE - WRPT (WDSE), WGTE TV (WGTE), Lakeland Public Television (KAWE), KMOS-TV - Channels 6.1, 6.2 and 6.3 (KMOS), MontanaPBS (KUSM), KRWG/Channel 22 (KRWG), KACV (KACV), KCOS/Channel 13 (KCOS), WCNY/Channel 24 (WCNY), WNED (WNED), WPBS (WPBS), WSKG Public TV (WSKG), WXXI (WXXI), WPSU (WPSU), WVIA Public Media Studios (WVIA), WTVI (WTVI), Western Reserve PBS (WNEO), WVIZ/PBS ideastream (WVIZ), KCTS 9 (KCTS), Basin PBS (KPBT), KUHT / Channel 8 (KUHT), KLRN (KLRN), KLRU (KLRU), WTJX Channel 12 (WTJX), WCVE PBS (WCVE), KBTC Public Television (KBTC)
|
||||||
- **Periscope**: Periscope
|
- **Periscope**: Periscope
|
||||||
- **PhilharmonieDeParis**: Philharmonie de Paris
|
- **PhilharmonieDeParis**: Philharmonie de Paris
|
||||||
- **Phoenix**
|
- **Phoenix**
|
||||||
@@ -426,7 +435,6 @@
|
|||||||
- **qqmusic:playlist**: QQ音乐 - 歌单
|
- **qqmusic:playlist**: QQ音乐 - 歌单
|
||||||
- **qqmusic:singer**: QQ音乐 - 歌手
|
- **qqmusic:singer**: QQ音乐 - 歌手
|
||||||
- **qqmusic:toplist**: QQ音乐 - 排行榜
|
- **qqmusic:toplist**: QQ音乐 - 排行榜
|
||||||
- **Quickscope**: Quick Scope
|
|
||||||
- **QuickVid**
|
- **QuickVid**
|
||||||
- **R7**
|
- **R7**
|
||||||
- **radio.de**
|
- **radio.de**
|
||||||
@@ -479,6 +487,8 @@
|
|||||||
- **Shared**: shared.sx and vivo.sx
|
- **Shared**: shared.sx and vivo.sx
|
||||||
- **ShareSix**
|
- **ShareSix**
|
||||||
- **Sina**
|
- **Sina**
|
||||||
|
- **skynewsarabia:video**
|
||||||
|
- **skynewsarabia:video**
|
||||||
- **Slideshare**
|
- **Slideshare**
|
||||||
- **Slutload**
|
- **Slutload**
|
||||||
- **smotri**: Smotri.com
|
- **smotri**: Smotri.com
|
||||||
@@ -493,6 +503,7 @@
|
|||||||
- **soompi:show**
|
- **soompi:show**
|
||||||
- **soundcloud**
|
- **soundcloud**
|
||||||
- **soundcloud:playlist**
|
- **soundcloud:playlist**
|
||||||
|
- **soundcloud:search**: Soundcloud search
|
||||||
- **soundcloud:set**
|
- **soundcloud:set**
|
||||||
- **soundcloud:user**
|
- **soundcloud:user**
|
||||||
- **soundgasm**
|
- **soundgasm**
|
||||||
@@ -663,6 +674,7 @@
|
|||||||
- **WebOfStories**
|
- **WebOfStories**
|
||||||
- **WebOfStoriesPlaylist**
|
- **WebOfStoriesPlaylist**
|
||||||
- **Weibo**
|
- **Weibo**
|
||||||
|
- **wholecloud**: WholeCloud
|
||||||
- **Wimp**
|
- **Wimp**
|
||||||
- **Wistia**
|
- **Wistia**
|
||||||
- **WNL**
|
- **WNL**
|
||||||
@@ -671,6 +683,7 @@
|
|||||||
- **WSJ**: Wall Street Journal
|
- **WSJ**: Wall Street Journal
|
||||||
- **XBef**
|
- **XBef**
|
||||||
- **XboxClips**
|
- **XboxClips**
|
||||||
|
- **XFileShare**: XFileShare based sites: GorillaVid.in, daclips.in, movpod.in, fastvideo.in, realvid.net, filehoot.com and vidto.me
|
||||||
- **XHamster**
|
- **XHamster**
|
||||||
- **XHamsterEmbed**
|
- **XHamsterEmbed**
|
||||||
- **XMinus**
|
- **XMinus**
|
||||||
@@ -705,6 +718,7 @@
|
|||||||
- **youtube:show**: YouTube.com (multi-season) shows
|
- **youtube:show**: YouTube.com (multi-season) shows
|
||||||
- **youtube:subscriptions**: YouTube.com subscriptions feed, "ytsubs" keyword (requires authentication)
|
- **youtube:subscriptions**: YouTube.com subscriptions feed, "ytsubs" keyword (requires authentication)
|
||||||
- **youtube:user**: YouTube.com user videos (URL or "ytuser" keyword)
|
- **youtube:user**: YouTube.com user videos (URL or "ytuser" keyword)
|
||||||
|
- **youtube:user:playlists**: YouTube.com user playlists
|
||||||
- **youtube:watchlater**: Youtube watch later list, ":ytwatchlater" for short (requires authentication)
|
- **youtube:watchlater**: Youtube watch later list, ":ytwatchlater" for short (requires authentication)
|
||||||
- **Zapiks**
|
- **Zapiks**
|
||||||
- **ZDF**
|
- **ZDF**
|
||||||
|
@@ -121,8 +121,8 @@ class TestAllURLsMatching(unittest.TestCase):
|
|||||||
|
|
||||||
def test_pbs(self):
|
def test_pbs(self):
|
||||||
# https://github.com/rg3/youtube-dl/issues/2350
|
# https://github.com/rg3/youtube-dl/issues/2350
|
||||||
self.assertMatch('http://video.pbs.org/viralplayer/2365173446/', ['PBS'])
|
self.assertMatch('http://video.pbs.org/viralplayer/2365173446/', ['pbs'])
|
||||||
self.assertMatch('http://video.pbs.org/widget/partnerplayer/980042464/', ['PBS'])
|
self.assertMatch('http://video.pbs.org/widget/partnerplayer/980042464/', ['pbs'])
|
||||||
|
|
||||||
def test_yahoo_https(self):
|
def test_yahoo_https(self):
|
||||||
# https://github.com/rg3/youtube-dl/issues/2701
|
# https://github.com/rg3/youtube-dl/issues/2701
|
||||||
|
@@ -21,6 +21,7 @@ from youtube_dl.utils import (
|
|||||||
clean_html,
|
clean_html,
|
||||||
DateRange,
|
DateRange,
|
||||||
detect_exe_version,
|
detect_exe_version,
|
||||||
|
determine_ext,
|
||||||
encodeFilename,
|
encodeFilename,
|
||||||
escape_rfc3986,
|
escape_rfc3986,
|
||||||
escape_url,
|
escape_url,
|
||||||
@@ -210,8 +211,8 @@ class TestUtil(unittest.TestCase):
|
|||||||
self.assertEqual(unescapeHTML('%20;'), '%20;')
|
self.assertEqual(unescapeHTML('%20;'), '%20;')
|
||||||
self.assertEqual(unescapeHTML('/'), '/')
|
self.assertEqual(unescapeHTML('/'), '/')
|
||||||
self.assertEqual(unescapeHTML('/'), '/')
|
self.assertEqual(unescapeHTML('/'), '/')
|
||||||
self.assertEqual(
|
self.assertEqual(unescapeHTML('é'), 'é')
|
||||||
unescapeHTML('é'), 'é')
|
self.assertEqual(unescapeHTML('�'), '�')
|
||||||
|
|
||||||
def test_daterange(self):
|
def test_daterange(self):
|
||||||
_20century = DateRange("19000101", "20000101")
|
_20century = DateRange("19000101", "20000101")
|
||||||
@@ -238,6 +239,13 @@ class TestUtil(unittest.TestCase):
|
|||||||
self.assertEqual(unified_strdate('25-09-2014'), '20140925')
|
self.assertEqual(unified_strdate('25-09-2014'), '20140925')
|
||||||
self.assertEqual(unified_strdate('UNKNOWN DATE FORMAT'), None)
|
self.assertEqual(unified_strdate('UNKNOWN DATE FORMAT'), None)
|
||||||
|
|
||||||
|
def test_determine_ext(self):
|
||||||
|
self.assertEqual(determine_ext('http://example.com/foo/bar.mp4/?download'), 'mp4')
|
||||||
|
self.assertEqual(determine_ext('http://example.com/foo/bar/?download', None), None)
|
||||||
|
self.assertEqual(determine_ext('http://example.com/foo/bar.nonext/?download', None), None)
|
||||||
|
self.assertEqual(determine_ext('http://example.com/foo/bar/mp4?download', None), None)
|
||||||
|
self.assertEqual(determine_ext('http://example.com/foo/bar.m3u8//?download'), 'm3u8')
|
||||||
|
|
||||||
def test_find_xpath_attr(self):
|
def test_find_xpath_attr(self):
|
||||||
testxml = '''<root>
|
testxml = '''<root>
|
||||||
<node/>
|
<node/>
|
||||||
|
@@ -28,6 +28,7 @@ if os.name == 'nt':
|
|||||||
import ctypes
|
import ctypes
|
||||||
|
|
||||||
from .compat import (
|
from .compat import (
|
||||||
|
compat_basestring,
|
||||||
compat_cookiejar,
|
compat_cookiejar,
|
||||||
compat_expanduser,
|
compat_expanduser,
|
||||||
compat_get_terminal_size,
|
compat_get_terminal_size,
|
||||||
@@ -63,6 +64,7 @@ from .utils import (
|
|||||||
SameFileError,
|
SameFileError,
|
||||||
sanitize_filename,
|
sanitize_filename,
|
||||||
sanitize_path,
|
sanitize_path,
|
||||||
|
sanitized_Request,
|
||||||
std_headers,
|
std_headers,
|
||||||
subtitles_filename,
|
subtitles_filename,
|
||||||
UnavailableVideoError,
|
UnavailableVideoError,
|
||||||
@@ -156,7 +158,7 @@ class YoutubeDL(object):
|
|||||||
writethumbnail: Write the thumbnail image to a file
|
writethumbnail: Write the thumbnail image to a file
|
||||||
write_all_thumbnails: Write all thumbnail formats to files
|
write_all_thumbnails: Write all thumbnail formats to files
|
||||||
writesubtitles: Write the video subtitles to a file
|
writesubtitles: Write the video subtitles to a file
|
||||||
writeautomaticsub: Write the automatic subtitles to a file
|
writeautomaticsub: Write the automatically generated subtitles to a file
|
||||||
allsubtitles: Downloads all the subtitles of the video
|
allsubtitles: Downloads all the subtitles of the video
|
||||||
(requires writesubtitles or writeautomaticsub)
|
(requires writesubtitles or writeautomaticsub)
|
||||||
listsubtitles: Lists all available subtitles for the video
|
listsubtitles: Lists all available subtitles for the video
|
||||||
@@ -833,6 +835,7 @@ class YoutubeDL(object):
|
|||||||
extra_info=extra)
|
extra_info=extra)
|
||||||
playlist_results.append(entry_result)
|
playlist_results.append(entry_result)
|
||||||
ie_result['entries'] = playlist_results
|
ie_result['entries'] = playlist_results
|
||||||
|
self.to_screen('[download] Finished downloading playlist: %s' % playlist)
|
||||||
return ie_result
|
return ie_result
|
||||||
elif result_type == 'compat_list':
|
elif result_type == 'compat_list':
|
||||||
self.report_warning(
|
self.report_warning(
|
||||||
@@ -937,7 +940,7 @@ class YoutubeDL(object):
|
|||||||
filter_parts.append(string)
|
filter_parts.append(string)
|
||||||
|
|
||||||
def _remove_unused_ops(tokens):
|
def _remove_unused_ops(tokens):
|
||||||
# Remove operators that we don't use and join them with the sourrounding strings
|
# Remove operators that we don't use and join them with the surrounding strings
|
||||||
# for example: 'mp4' '-' 'baseline' '-' '16x9' is converted to 'mp4-baseline-16x9'
|
# for example: 'mp4' '-' 'baseline' '-' '16x9' is converted to 'mp4-baseline-16x9'
|
||||||
ALLOWED_OPS = ('/', '+', ',', '(', ')')
|
ALLOWED_OPS = ('/', '+', ',', '(', ')')
|
||||||
last_string, last_start, last_end, last_line = None, None, None, None
|
last_string, last_start, last_end, last_line = None, None, None, None
|
||||||
@@ -1107,6 +1110,12 @@ class YoutubeDL(object):
|
|||||||
'contain the video, try using '
|
'contain the video, try using '
|
||||||
'"-f %s+%s"' % (format_2, format_1))
|
'"-f %s+%s"' % (format_2, format_1))
|
||||||
return
|
return
|
||||||
|
# Formats must be opposite (video+audio)
|
||||||
|
if formats_info[0].get('acodec') == 'none' and formats_info[1].get('acodec') == 'none':
|
||||||
|
self.report_error(
|
||||||
|
'Both formats %s and %s are video-only, you must specify "-f video+audio"'
|
||||||
|
% (format_1, format_2))
|
||||||
|
return
|
||||||
output_ext = (
|
output_ext = (
|
||||||
formats_info[0]['ext']
|
formats_info[0]['ext']
|
||||||
if self.params.get('merge_output_format') is None
|
if self.params.get('merge_output_format') is None
|
||||||
@@ -1186,7 +1195,7 @@ class YoutubeDL(object):
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
def _calc_cookies(self, info_dict):
|
def _calc_cookies(self, info_dict):
|
||||||
pr = compat_urllib_request.Request(info_dict['url'])
|
pr = sanitized_Request(info_dict['url'])
|
||||||
self.cookiejar.add_cookie_header(pr)
|
self.cookiejar.add_cookie_header(pr)
|
||||||
return pr.get_header('Cookie')
|
return pr.get_header('Cookie')
|
||||||
|
|
||||||
@@ -1870,6 +1879,8 @@ class YoutubeDL(object):
|
|||||||
|
|
||||||
def urlopen(self, req):
|
def urlopen(self, req):
|
||||||
""" Start an HTTP download """
|
""" Start an HTTP download """
|
||||||
|
if isinstance(req, compat_basestring):
|
||||||
|
req = sanitized_Request(req)
|
||||||
return self._opener.open(req, timeout=self._socket_timeout)
|
return self._opener.open(req, timeout=self._socket_timeout)
|
||||||
|
|
||||||
def print_debug_header(self):
|
def print_debug_header(self):
|
||||||
|
@@ -377,7 +377,7 @@ def _real_main(argv=None):
|
|||||||
with YoutubeDL(ydl_opts) as ydl:
|
with YoutubeDL(ydl_opts) as ydl:
|
||||||
# Update version
|
# Update version
|
||||||
if opts.update_self:
|
if opts.update_self:
|
||||||
update_self(ydl.to_screen, opts.verbose)
|
update_self(ydl.to_screen, opts.verbose, ydl._opener)
|
||||||
|
|
||||||
# Remove cache dir
|
# Remove cache dir
|
||||||
if opts.rm_cachedir:
|
if opts.rm_cachedir:
|
||||||
|
@@ -42,7 +42,7 @@ class FileDownloader(object):
|
|||||||
min_filesize: Skip files smaller than this size
|
min_filesize: Skip files smaller than this size
|
||||||
max_filesize: Skip files larger than this size
|
max_filesize: Skip files larger than this size
|
||||||
xattr_set_filesize: Set ytdl.filesize user xattribute with expected size.
|
xattr_set_filesize: Set ytdl.filesize user xattribute with expected size.
|
||||||
(experimenatal)
|
(experimental)
|
||||||
external_downloader_args: A list of additional command-line arguments for the
|
external_downloader_args: A list of additional command-line arguments for the
|
||||||
external downloader.
|
external downloader.
|
||||||
|
|
||||||
|
@@ -3,7 +3,7 @@ from __future__ import unicode_literals
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from .common import FileDownloader
|
from .common import FileDownloader
|
||||||
from ..compat import compat_urllib_request
|
from ..utils import sanitized_Request
|
||||||
|
|
||||||
|
|
||||||
class DashSegmentsFD(FileDownloader):
|
class DashSegmentsFD(FileDownloader):
|
||||||
@@ -22,7 +22,7 @@ class DashSegmentsFD(FileDownloader):
|
|||||||
|
|
||||||
def append_url_to_file(outf, target_url, target_name, remaining_bytes=None):
|
def append_url_to_file(outf, target_url, target_name, remaining_bytes=None):
|
||||||
self.to_screen('[DashSegments] %s: Downloading %s' % (info_dict['id'], target_name))
|
self.to_screen('[DashSegments] %s: Downloading %s' % (info_dict['id'], target_name))
|
||||||
req = compat_urllib_request.Request(target_url)
|
req = sanitized_Request(target_url)
|
||||||
if remaining_bytes is not None:
|
if remaining_bytes is not None:
|
||||||
req.add_header('Range', 'bytes=0-%d' % (remaining_bytes - 1))
|
req.add_header('Range', 'bytes=0-%d' % (remaining_bytes - 1))
|
||||||
|
|
||||||
|
@@ -15,6 +15,7 @@ from ..compat import (
|
|||||||
)
|
)
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
encodeFilename,
|
encodeFilename,
|
||||||
|
fix_xml_ampersands,
|
||||||
sanitize_open,
|
sanitize_open,
|
||||||
struct_pack,
|
struct_pack,
|
||||||
struct_unpack,
|
struct_unpack,
|
||||||
@@ -288,7 +289,10 @@ class F4mFD(FragmentFD):
|
|||||||
self.to_screen('[%s] Downloading f4m manifest' % self.FD_NAME)
|
self.to_screen('[%s] Downloading f4m manifest' % self.FD_NAME)
|
||||||
urlh = self.ydl.urlopen(man_url)
|
urlh = self.ydl.urlopen(man_url)
|
||||||
man_url = urlh.geturl()
|
man_url = urlh.geturl()
|
||||||
manifest = urlh.read()
|
# Some manifests may be malformed, e.g. prosiebensat1 generated manifests
|
||||||
|
# (see https://github.com/rg3/youtube-dl/issues/6215#issuecomment-121704244
|
||||||
|
# and https://github.com/rg3/youtube-dl/issues/7823)
|
||||||
|
manifest = fix_xml_ampersands(urlh.read().decode('utf-8', 'ignore')).strip()
|
||||||
|
|
||||||
doc = compat_etree_fromstring(manifest)
|
doc = compat_etree_fromstring(manifest)
|
||||||
formats = [(int(f.attrib.get('bitrate', -1)), f)
|
formats = [(int(f.attrib.get('bitrate', -1)), f)
|
||||||
|
@@ -13,6 +13,7 @@ from ..utils import (
|
|||||||
encodeArgument,
|
encodeArgument,
|
||||||
encodeFilename,
|
encodeFilename,
|
||||||
sanitize_open,
|
sanitize_open,
|
||||||
|
handle_youtubedl_headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -33,9 +34,10 @@ class HlsFD(FileDownloader):
|
|||||||
if info_dict['http_headers'] and re.match(r'^https?://', url):
|
if info_dict['http_headers'] and re.match(r'^https?://', url):
|
||||||
# Trailing \r\n after each HTTP header is important to prevent warning from ffmpeg/avconv:
|
# Trailing \r\n after each HTTP header is important to prevent warning from ffmpeg/avconv:
|
||||||
# [http @ 00000000003d2fa0] No trailing CRLF found in HTTP header.
|
# [http @ 00000000003d2fa0] No trailing CRLF found in HTTP header.
|
||||||
|
headers = handle_youtubedl_headers(info_dict['http_headers'])
|
||||||
args += [
|
args += [
|
||||||
'-headers',
|
'-headers',
|
||||||
''.join('%s: %s\r\n' % (key, val) for key, val in info_dict['http_headers'].items())]
|
''.join('%s: %s\r\n' % (key, val) for key, val in headers.items())]
|
||||||
|
|
||||||
args += ['-i', url, '-f', 'mp4', '-c', 'copy', '-bsf:a', 'aac_adtstoasc']
|
args += ['-i', url, '-f', 'mp4', '-c', 'copy', '-bsf:a', 'aac_adtstoasc']
|
||||||
|
|
||||||
|
@@ -7,14 +7,12 @@ import time
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from .common import FileDownloader
|
from .common import FileDownloader
|
||||||
from ..compat import (
|
from ..compat import compat_urllib_error
|
||||||
compat_urllib_request,
|
|
||||||
compat_urllib_error,
|
|
||||||
)
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
ContentTooShortError,
|
ContentTooShortError,
|
||||||
encodeFilename,
|
encodeFilename,
|
||||||
sanitize_open,
|
sanitize_open,
|
||||||
|
sanitized_Request,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -29,8 +27,8 @@ class HttpFD(FileDownloader):
|
|||||||
add_headers = info_dict.get('http_headers')
|
add_headers = info_dict.get('http_headers')
|
||||||
if add_headers:
|
if add_headers:
|
||||||
headers.update(add_headers)
|
headers.update(add_headers)
|
||||||
basic_request = compat_urllib_request.Request(url, None, headers)
|
basic_request = sanitized_Request(url, None, headers)
|
||||||
request = compat_urllib_request.Request(url, None, headers)
|
request = sanitized_Request(url, None, headers)
|
||||||
|
|
||||||
is_test = self.params.get('test', False)
|
is_test = self.params.get('test', False)
|
||||||
|
|
||||||
|
@@ -117,7 +117,7 @@ class RtmpFD(FileDownloader):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
# Download using rtmpdump. rtmpdump returns exit code 2 when
|
# Download using rtmpdump. rtmpdump returns exit code 2 when
|
||||||
# the connection was interrumpted and resuming appears to be
|
# the connection was interrupted and resuming appears to be
|
||||||
# possible. This is part of rtmpdump's normal usage, AFAIK.
|
# possible. This is part of rtmpdump's normal usage, AFAIK.
|
||||||
basic_args = [
|
basic_args = [
|
||||||
'rtmpdump', '--verbose', '-r', url,
|
'rtmpdump', '--verbose', '-r', url,
|
||||||
|
@@ -3,9 +3,15 @@ from __future__ import unicode_literals
|
|||||||
from .abc import ABCIE
|
from .abc import ABCIE
|
||||||
from .abc7news import Abc7NewsIE
|
from .abc7news import Abc7NewsIE
|
||||||
from .academicearth import AcademicEarthCourseIE
|
from .academicearth import AcademicEarthCourseIE
|
||||||
|
from .acast import (
|
||||||
|
ACastIE,
|
||||||
|
ACastChannelIE,
|
||||||
|
)
|
||||||
from .addanime import AddAnimeIE
|
from .addanime import AddAnimeIE
|
||||||
from .adobetv import (
|
from .adobetv import (
|
||||||
AdobeTVIE,
|
AdobeTVIE,
|
||||||
|
AdobeTVShowIE,
|
||||||
|
AdobeTVChannelIE,
|
||||||
AdobeTVVideoIE,
|
AdobeTVVideoIE,
|
||||||
)
|
)
|
||||||
from .adultswim import AdultSwimIE
|
from .adultswim import AdultSwimIE
|
||||||
@@ -38,6 +44,7 @@ from .arte import (
|
|||||||
)
|
)
|
||||||
from .atresplayer import AtresPlayerIE
|
from .atresplayer import AtresPlayerIE
|
||||||
from .atttechchannel import ATTTechChannelIE
|
from .atttechchannel import ATTTechChannelIE
|
||||||
|
from .audimedia import AudiMediaIE
|
||||||
from .audiomack import AudiomackIE, AudiomackAlbumIE
|
from .audiomack import AudiomackIE, AudiomackAlbumIE
|
||||||
from .azubu import AzubuIE
|
from .azubu import AzubuIE
|
||||||
from .baidu import BaiduVideoIE
|
from .baidu import BaiduVideoIE
|
||||||
@@ -60,7 +67,10 @@ from .bloomberg import BloombergIE
|
|||||||
from .bpb import BpbIE
|
from .bpb import BpbIE
|
||||||
from .br import BRIE
|
from .br import BRIE
|
||||||
from .breakcom import BreakIE
|
from .breakcom import BreakIE
|
||||||
from .brightcove import BrightcoveIE
|
from .brightcove import (
|
||||||
|
BrightcoveLegacyIE,
|
||||||
|
BrightcoveNewIE,
|
||||||
|
)
|
||||||
from .buzzfeed import BuzzFeedIE
|
from .buzzfeed import BuzzFeedIE
|
||||||
from .byutv import BYUtvIE
|
from .byutv import BYUtvIE
|
||||||
from .c56 import C56IE
|
from .c56 import C56IE
|
||||||
@@ -129,6 +139,7 @@ from .dfb import DFBIE
|
|||||||
from .dhm import DHMIE
|
from .dhm import DHMIE
|
||||||
from .dotsub import DotsubIE
|
from .dotsub import DotsubIE
|
||||||
from .douyutv import DouyuTVIE
|
from .douyutv import DouyuTVIE
|
||||||
|
from .dplay import DPlayIE
|
||||||
from .dramafever import (
|
from .dramafever import (
|
||||||
DramaFeverIE,
|
DramaFeverIE,
|
||||||
DramaFeverSeriesIE,
|
DramaFeverSeriesIE,
|
||||||
@@ -195,7 +206,9 @@ from .francetv import (
|
|||||||
from .freesound import FreesoundIE
|
from .freesound import FreesoundIE
|
||||||
from .freespeech import FreespeechIE
|
from .freespeech import FreespeechIE
|
||||||
from .freevideo import FreeVideoIE
|
from .freevideo import FreeVideoIE
|
||||||
|
from .funimation import FunimationIE
|
||||||
from .funnyordie import FunnyOrDieIE
|
from .funnyordie import FunnyOrDieIE
|
||||||
|
from .gameinformer import GameInformerIE
|
||||||
from .gamekings import GamekingsIE
|
from .gamekings import GamekingsIE
|
||||||
from .gameone import (
|
from .gameone import (
|
||||||
GameOneIE,
|
GameOneIE,
|
||||||
@@ -221,7 +234,6 @@ from .goldenmoustache import GoldenMoustacheIE
|
|||||||
from .golem import GolemIE
|
from .golem import GolemIE
|
||||||
from .googleplus import GooglePlusIE
|
from .googleplus import GooglePlusIE
|
||||||
from .googlesearch import GoogleSearchIE
|
from .googlesearch import GoogleSearchIE
|
||||||
from .gorillavid import GorillaVidIE
|
|
||||||
from .goshgay import GoshgayIE
|
from .goshgay import GoshgayIE
|
||||||
from .groupon import GrouponIE
|
from .groupon import GrouponIE
|
||||||
from .hark import HarkIE
|
from .hark import HarkIE
|
||||||
@@ -346,7 +358,6 @@ from .motherless import MotherlessIE
|
|||||||
from .motorsport import MotorsportIE
|
from .motorsport import MotorsportIE
|
||||||
from .movieclips import MovieClipsIE
|
from .movieclips import MovieClipsIE
|
||||||
from .moviezine import MoviezineIE
|
from .moviezine import MoviezineIE
|
||||||
from .movshare import MovShareIE
|
|
||||||
from .mtv import (
|
from .mtv import (
|
||||||
MTVIE,
|
MTVIE,
|
||||||
MTVServicesEmbeddedIE,
|
MTVServicesEmbeddedIE,
|
||||||
@@ -412,14 +423,22 @@ from .noco import NocoIE
|
|||||||
from .normalboots import NormalbootsIE
|
from .normalboots import NormalbootsIE
|
||||||
from .nosvideo import NosVideoIE
|
from .nosvideo import NosVideoIE
|
||||||
from .nova import NovaIE
|
from .nova import NovaIE
|
||||||
from .novamov import NovaMovIE
|
from .novamov import (
|
||||||
|
NovaMovIE,
|
||||||
|
WholeCloudIE,
|
||||||
|
NowVideoIE,
|
||||||
|
VideoWeedIE,
|
||||||
|
CloudTimeIE,
|
||||||
|
)
|
||||||
from .nowness import (
|
from .nowness import (
|
||||||
NownessIE,
|
NownessIE,
|
||||||
NownessPlaylistIE,
|
NownessPlaylistIE,
|
||||||
NownessSeriesIE,
|
NownessSeriesIE,
|
||||||
)
|
)
|
||||||
from .nowtv import NowTVIE
|
from .nowtv import (
|
||||||
from .nowvideo import NowVideoIE
|
NowTVIE,
|
||||||
|
NowTVListIE,
|
||||||
|
)
|
||||||
from .npo import (
|
from .npo import (
|
||||||
NPOIE,
|
NPOIE,
|
||||||
NPOLiveIE,
|
NPOLiveIE,
|
||||||
@@ -456,10 +475,7 @@ from .orf import (
|
|||||||
from .parliamentliveuk import ParliamentLiveUKIE
|
from .parliamentliveuk import ParliamentLiveUKIE
|
||||||
from .patreon import PatreonIE
|
from .patreon import PatreonIE
|
||||||
from .pbs import PBSIE
|
from .pbs import PBSIE
|
||||||
from .periscope import (
|
from .periscope import PeriscopeIE
|
||||||
PeriscopeIE,
|
|
||||||
QuickscopeIE,
|
|
||||||
)
|
|
||||||
from .philharmoniedeparis import PhilharmonieDeParisIE
|
from .philharmoniedeparis import PhilharmonieDeParisIE
|
||||||
from .phoenix import PhoenixIE
|
from .phoenix import PhoenixIE
|
||||||
from .photobucket import PhotobucketIE
|
from .photobucket import PhotobucketIE
|
||||||
@@ -551,6 +567,10 @@ from .shahid import ShahidIE
|
|||||||
from .shared import SharedIE
|
from .shared import SharedIE
|
||||||
from .sharesix import ShareSixIE
|
from .sharesix import ShareSixIE
|
||||||
from .sina import SinaIE
|
from .sina import SinaIE
|
||||||
|
from .skynewsarabia import (
|
||||||
|
SkyNewsArabiaIE,
|
||||||
|
SkyNewsArabiaArticleIE,
|
||||||
|
)
|
||||||
from .slideshare import SlideshareIE
|
from .slideshare import SlideshareIE
|
||||||
from .slutload import SlutloadIE
|
from .slutload import SlutloadIE
|
||||||
from .smotri import (
|
from .smotri import (
|
||||||
@@ -573,7 +593,8 @@ from .soundcloud import (
|
|||||||
SoundcloudIE,
|
SoundcloudIE,
|
||||||
SoundcloudSetIE,
|
SoundcloudSetIE,
|
||||||
SoundcloudUserIE,
|
SoundcloudUserIE,
|
||||||
SoundcloudPlaylistIE
|
SoundcloudPlaylistIE,
|
||||||
|
SoundcloudSearchIE
|
||||||
)
|
)
|
||||||
from .soundgasm import (
|
from .soundgasm import (
|
||||||
SoundgasmIE,
|
SoundgasmIE,
|
||||||
@@ -728,7 +749,6 @@ from .videofyme import VideofyMeIE
|
|||||||
from .videomega import VideoMegaIE
|
from .videomega import VideoMegaIE
|
||||||
from .videopremium import VideoPremiumIE
|
from .videopremium import VideoPremiumIE
|
||||||
from .videott import VideoTtIE
|
from .videott import VideoTtIE
|
||||||
from .videoweed import VideoWeedIE
|
|
||||||
from .vidme import VidmeIE
|
from .vidme import VidmeIE
|
||||||
from .vidzi import VidziIE
|
from .vidzi import VidziIE
|
||||||
from .vier import VierIE, VierVideosIE
|
from .vier import VierIE, VierVideosIE
|
||||||
@@ -786,6 +806,7 @@ from .wrzuta import WrzutaIE
|
|||||||
from .wsj import WSJIE
|
from .wsj import WSJIE
|
||||||
from .xbef import XBefIE
|
from .xbef import XBefIE
|
||||||
from .xboxclips import XboxClipsIE
|
from .xboxclips import XboxClipsIE
|
||||||
|
from .xfileshare import XFileShareIE
|
||||||
from .xhamster import (
|
from .xhamster import (
|
||||||
XHamsterIE,
|
XHamsterIE,
|
||||||
XHamsterEmbedIE,
|
XHamsterEmbedIE,
|
||||||
@@ -829,6 +850,7 @@ from .youtube import (
|
|||||||
YoutubeTruncatedIDIE,
|
YoutubeTruncatedIDIE,
|
||||||
YoutubeTruncatedURLIE,
|
YoutubeTruncatedURLIE,
|
||||||
YoutubeUserIE,
|
YoutubeUserIE,
|
||||||
|
YoutubeUserPlaylistsIE,
|
||||||
YoutubeWatchLaterIE,
|
YoutubeWatchLaterIE,
|
||||||
)
|
)
|
||||||
from .zapiks import ZapiksIE
|
from .zapiks import ZapiksIE
|
||||||
|
70
youtube_dl/extractor/acast.py
Normal file
70
youtube_dl/extractor/acast.py
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
from .common import InfoExtractor
|
||||||
|
from ..compat import compat_str
|
||||||
|
from ..utils import int_or_none
|
||||||
|
|
||||||
|
|
||||||
|
class ACastBaseIE(InfoExtractor):
|
||||||
|
_API_BASE_URL = 'https://www.acast.com/api/'
|
||||||
|
|
||||||
|
|
||||||
|
class ACastIE(ACastBaseIE):
|
||||||
|
IE_NAME = 'acast'
|
||||||
|
_VALID_URL = r'https?://(?:www\.)?acast\.com/(?P<channel>[^/]+)/(?P<id>[^/#?]+)'
|
||||||
|
_TEST = {
|
||||||
|
'url': 'https://www.acast.com/condenasttraveler/-where-are-you-taipei-101-taiwan',
|
||||||
|
'md5': 'ada3de5a1e3a2a381327d749854788bb',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '57de3baa-4bb0-487e-9418-2692c1277a34',
|
||||||
|
'ext': 'mp3',
|
||||||
|
'title': '"Where Are You?": Taipei 101, Taiwan',
|
||||||
|
'timestamp': 1196172000000,
|
||||||
|
'description': 'md5:0c5d8201dfea2b93218ea986c91eee6e',
|
||||||
|
'duration': 211,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
channel, display_id = re.match(self._VALID_URL, url).groups()
|
||||||
|
cast_data = self._download_json(self._API_BASE_URL + 'channels/%s/acasts/%s/playback' % (channel, display_id), display_id)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'id': compat_str(cast_data['id']),
|
||||||
|
'display_id': display_id,
|
||||||
|
'url': cast_data['blings'][0]['audio'],
|
||||||
|
'title': cast_data['name'],
|
||||||
|
'description': cast_data.get('description'),
|
||||||
|
'thumbnail': cast_data.get('image'),
|
||||||
|
'timestamp': int_or_none(cast_data.get('publishingDate')),
|
||||||
|
'duration': int_or_none(cast_data.get('duration')),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ACastChannelIE(ACastBaseIE):
|
||||||
|
IE_NAME = 'acast:channel'
|
||||||
|
_VALID_URL = r'https?://(?:www\.)?acast\.com/(?P<id>[^/#?]+)'
|
||||||
|
_TEST = {
|
||||||
|
'url': 'https://www.acast.com/condenasttraveler',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '50544219-29bb-499e-a083-6087f4cb7797',
|
||||||
|
'title': 'Condé Nast Traveler Podcast',
|
||||||
|
'description': 'md5:98646dee22a5b386626ae31866638fbd',
|
||||||
|
},
|
||||||
|
'playlist_mincount': 20,
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def suitable(cls, url):
|
||||||
|
return False if ACastIE.suitable(url) else super(ACastChannelIE, cls).suitable(url)
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
display_id = self._match_id(url)
|
||||||
|
channel_data = self._download_json(self._API_BASE_URL + 'channels/%s' % display_id, display_id)
|
||||||
|
casts = self._download_json(self._API_BASE_URL + 'channels/%s/acasts' % display_id, display_id)
|
||||||
|
entries = [self.url_result('https://www.acast.com/%s/%s' % (display_id, cast['url']), 'ACast') for cast in casts]
|
||||||
|
|
||||||
|
return self.playlist_result(entries, compat_str(channel_data['id']), channel_data['name'], channel_data.get('description'))
|
@@ -1,23 +1,32 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
|
from ..compat import compat_str
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
parse_duration,
|
parse_duration,
|
||||||
unified_strdate,
|
unified_strdate,
|
||||||
str_to_int,
|
str_to_int,
|
||||||
|
int_or_none,
|
||||||
float_or_none,
|
float_or_none,
|
||||||
ISO639Utils,
|
ISO639Utils,
|
||||||
|
determine_ext,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class AdobeTVIE(InfoExtractor):
|
class AdobeTVBaseIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://tv\.adobe\.com/watch/[^/]+/(?P<id>[^/]+)'
|
_API_BASE_URL = 'http://tv.adobe.com/api/v4/'
|
||||||
|
|
||||||
|
|
||||||
|
class AdobeTVIE(AdobeTVBaseIE):
|
||||||
|
_VALID_URL = r'https?://tv\.adobe\.com/(?:(?P<language>fr|de|es|jp)/)?watch/(?P<show_urlname>[^/]+)/(?P<id>[^/]+)'
|
||||||
|
|
||||||
_TEST = {
|
_TEST = {
|
||||||
'url': 'http://tv.adobe.com/watch/the-complete-picture-with-julieanne-kost/quick-tip-how-to-draw-a-circle-around-an-object-in-photoshop/',
|
'url': 'http://tv.adobe.com/watch/the-complete-picture-with-julieanne-kost/quick-tip-how-to-draw-a-circle-around-an-object-in-photoshop/',
|
||||||
'md5': '9bc5727bcdd55251f35ad311ca74fa1e',
|
'md5': '9bc5727bcdd55251f35ad311ca74fa1e',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'quick-tip-how-to-draw-a-circle-around-an-object-in-photoshop',
|
'id': '10981',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Quick Tip - How to Draw a Circle Around an Object in Photoshop',
|
'title': 'Quick Tip - How to Draw a Circle Around an Object in Photoshop',
|
||||||
'description': 'md5:99ec318dc909d7ba2a1f2b038f7d2311',
|
'description': 'md5:99ec318dc909d7ba2a1f2b038f7d2311',
|
||||||
@@ -29,50 +38,106 @@ class AdobeTVIE(InfoExtractor):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
language, show_urlname, urlname = re.match(self._VALID_URL, url).groups()
|
||||||
webpage = self._download_webpage(url, video_id)
|
if not language:
|
||||||
|
language = 'en'
|
||||||
|
|
||||||
player = self._parse_json(
|
video_data = self._download_json(
|
||||||
self._search_regex(r'html5player:\s*({.+?})\s*\n', webpage, 'player'),
|
self._API_BASE_URL + 'episode/get/?language=%s&show_urlname=%s&urlname=%s&disclosure=standard' % (language, show_urlname, urlname),
|
||||||
video_id)
|
urlname)['data'][0]
|
||||||
|
|
||||||
title = player.get('title') or self._search_regex(
|
|
||||||
r'data-title="([^"]+)"', webpage, 'title')
|
|
||||||
description = self._og_search_description(webpage)
|
|
||||||
thumbnail = self._og_search_thumbnail(webpage)
|
|
||||||
|
|
||||||
upload_date = unified_strdate(
|
|
||||||
self._html_search_meta('datepublished', webpage, 'upload date'))
|
|
||||||
|
|
||||||
duration = parse_duration(
|
|
||||||
self._html_search_meta('duration', webpage, 'duration') or
|
|
||||||
self._search_regex(
|
|
||||||
r'Runtime:\s*(\d{2}:\d{2}:\d{2})',
|
|
||||||
webpage, 'duration', fatal=False))
|
|
||||||
|
|
||||||
view_count = str_to_int(self._search_regex(
|
|
||||||
r'<div class="views">\s*Views?:\s*([\d,.]+)\s*</div>',
|
|
||||||
webpage, 'view count'))
|
|
||||||
|
|
||||||
formats = [{
|
formats = [{
|
||||||
'url': source['src'],
|
'url': source['url'],
|
||||||
'format_id': source.get('quality') or source['src'].split('-')[-1].split('.')[0] or None,
|
'format_id': source.get('quality_level') or source['url'].split('-')[-1].split('.')[0] or None,
|
||||||
'tbr': source.get('bitrate'),
|
'width': int_or_none(source.get('width')),
|
||||||
} for source in player['sources']]
|
'height': int_or_none(source.get('height')),
|
||||||
|
'tbr': int_or_none(source.get('video_data_rate')),
|
||||||
|
} for source in video_data['videos']]
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': compat_str(video_data['id']),
|
||||||
'title': title,
|
'title': video_data['title'],
|
||||||
'description': description,
|
'description': video_data.get('description'),
|
||||||
'thumbnail': thumbnail,
|
'thumbnail': video_data.get('thumbnail'),
|
||||||
'upload_date': upload_date,
|
'upload_date': unified_strdate(video_data.get('start_date')),
|
||||||
'duration': duration,
|
'duration': parse_duration(video_data.get('duration')),
|
||||||
'view_count': view_count,
|
'view_count': str_to_int(video_data.get('playcount')),
|
||||||
'formats': formats,
|
'formats': formats,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class AdobeTVPlaylistBaseIE(AdobeTVBaseIE):
|
||||||
|
def _parse_page_data(self, page_data):
|
||||||
|
return [self.url_result(self._get_element_url(element_data)) for element_data in page_data]
|
||||||
|
|
||||||
|
def _extract_playlist_entries(self, url, display_id):
|
||||||
|
page = self._download_json(url, display_id)
|
||||||
|
entries = self._parse_page_data(page['data'])
|
||||||
|
for page_num in range(2, page['paging']['pages'] + 1):
|
||||||
|
entries.extend(self._parse_page_data(
|
||||||
|
self._download_json(url + '&page=%d' % page_num, display_id)['data']))
|
||||||
|
return entries
|
||||||
|
|
||||||
|
|
||||||
|
class AdobeTVShowIE(AdobeTVPlaylistBaseIE):
|
||||||
|
_VALID_URL = r'https?://tv\.adobe\.com/(?:(?P<language>fr|de|es|jp)/)?show/(?P<id>[^/]+)'
|
||||||
|
|
||||||
|
_TEST = {
|
||||||
|
'url': 'http://tv.adobe.com/show/the-complete-picture-with-julieanne-kost',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '36',
|
||||||
|
'title': 'The Complete Picture with Julieanne Kost',
|
||||||
|
'description': 'md5:fa50867102dcd1aa0ddf2ab039311b27',
|
||||||
|
},
|
||||||
|
'playlist_mincount': 136,
|
||||||
|
}
|
||||||
|
|
||||||
|
def _get_element_url(self, element_data):
|
||||||
|
return element_data['urls'][0]
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
language, show_urlname = re.match(self._VALID_URL, url).groups()
|
||||||
|
if not language:
|
||||||
|
language = 'en'
|
||||||
|
query = 'language=%s&show_urlname=%s' % (language, show_urlname)
|
||||||
|
|
||||||
|
show_data = self._download_json(self._API_BASE_URL + 'show/get/?%s' % query, show_urlname)['data'][0]
|
||||||
|
|
||||||
|
return self.playlist_result(
|
||||||
|
self._extract_playlist_entries(self._API_BASE_URL + 'episode/?%s' % query, show_urlname),
|
||||||
|
compat_str(show_data['id']),
|
||||||
|
show_data['show_name'],
|
||||||
|
show_data['show_description'])
|
||||||
|
|
||||||
|
|
||||||
|
class AdobeTVChannelIE(AdobeTVPlaylistBaseIE):
|
||||||
|
_VALID_URL = r'https?://tv\.adobe\.com/(?:(?P<language>fr|de|es|jp)/)?channel/(?P<id>[^/]+)(?:/(?P<category_urlname>[^/]+))?'
|
||||||
|
|
||||||
|
_TEST = {
|
||||||
|
'url': 'http://tv.adobe.com/channel/development',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'development',
|
||||||
|
},
|
||||||
|
'playlist_mincount': 96,
|
||||||
|
}
|
||||||
|
|
||||||
|
def _get_element_url(self, element_data):
|
||||||
|
return element_data['url']
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
language, channel_urlname, category_urlname = re.match(self._VALID_URL, url).groups()
|
||||||
|
if not language:
|
||||||
|
language = 'en'
|
||||||
|
query = 'language=%s&channel_urlname=%s' % (language, channel_urlname)
|
||||||
|
if category_urlname:
|
||||||
|
query += '&category_urlname=%s' % category_urlname
|
||||||
|
|
||||||
|
return self.playlist_result(
|
||||||
|
self._extract_playlist_entries(self._API_BASE_URL + 'show/?%s' % query, channel_urlname),
|
||||||
|
channel_urlname)
|
||||||
|
|
||||||
|
|
||||||
class AdobeTVVideoIE(InfoExtractor):
|
class AdobeTVVideoIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://video\.tv\.adobe\.com/v/(?P<id>\d+)'
|
_VALID_URL = r'https?://video\.tv\.adobe\.com/v/(?P<id>\d+)'
|
||||||
|
|
||||||
@@ -91,28 +156,25 @@ class AdobeTVVideoIE(InfoExtractor):
|
|||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
|
video_data = self._download_json(url + '?format=json', video_id)
|
||||||
webpage = self._download_webpage(url, video_id)
|
|
||||||
|
|
||||||
player_params = self._parse_json(self._search_regex(
|
|
||||||
r'var\s+bridge\s*=\s*([^;]+);', webpage, 'player parameters'),
|
|
||||||
video_id)
|
|
||||||
|
|
||||||
formats = [{
|
formats = [{
|
||||||
|
'format_id': '%s-%s' % (determine_ext(source['src']), source.get('height')),
|
||||||
'url': source['src'],
|
'url': source['src'],
|
||||||
'width': source.get('width'),
|
'width': int_or_none(source.get('width')),
|
||||||
'height': source.get('height'),
|
'height': int_or_none(source.get('height')),
|
||||||
'tbr': source.get('bitrate'),
|
'tbr': int_or_none(source.get('bitrate')),
|
||||||
} for source in player_params['sources']]
|
} for source in video_data['sources']]
|
||||||
|
self._sort_formats(formats)
|
||||||
|
|
||||||
# For both metadata and downloaded files the duration varies among
|
# For both metadata and downloaded files the duration varies among
|
||||||
# formats. I just pick the max one
|
# formats. I just pick the max one
|
||||||
duration = max(filter(None, [
|
duration = max(filter(None, [
|
||||||
float_or_none(source.get('duration'), scale=1000)
|
float_or_none(source.get('duration'), scale=1000)
|
||||||
for source in player_params['sources']]))
|
for source in video_data['sources']]))
|
||||||
|
|
||||||
subtitles = {}
|
subtitles = {}
|
||||||
for translation in player_params.get('translations', []):
|
for translation in video_data.get('translations', []):
|
||||||
lang_id = translation.get('language_w3c') or ISO639Utils.long2short(translation['language_medium'])
|
lang_id = translation.get('language_w3c') or ISO639Utils.long2short(translation['language_medium'])
|
||||||
if lang_id not in subtitles:
|
if lang_id not in subtitles:
|
||||||
subtitles[lang_id] = []
|
subtitles[lang_id] = []
|
||||||
@@ -124,8 +186,9 @@ class AdobeTVVideoIE(InfoExtractor):
|
|||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
'formats': formats,
|
'formats': formats,
|
||||||
'title': player_params['title'],
|
'title': video_data['title'],
|
||||||
'description': self._og_search_description(webpage),
|
'description': video_data.get('description'),
|
||||||
|
'thumbnail': video_data['video'].get('poster'),
|
||||||
'duration': duration,
|
'duration': duration,
|
||||||
'subtitles': subtitles,
|
'subtitles': subtitles,
|
||||||
}
|
}
|
||||||
|
@@ -15,7 +15,7 @@ class AlJazeeraIE(InfoExtractor):
|
|||||||
'description': 'As a birth attendant advocating for family planning, Remy is on the frontline of Tondo\'s battle with overcrowding.',
|
'description': 'As a birth attendant advocating for family planning, Remy is on the frontline of Tondo\'s battle with overcrowding.',
|
||||||
'uploader': 'Al Jazeera English',
|
'uploader': 'Al Jazeera English',
|
||||||
},
|
},
|
||||||
'add_ie': ['Brightcove'],
|
'add_ie': ['BrightcoveLegacy'],
|
||||||
'skip': 'Not accessible from Travis CI server',
|
'skip': 'Not accessible from Travis CI server',
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,5 +32,5 @@ class AlJazeeraIE(InfoExtractor):
|
|||||||
'playerKey=AQ~~%2CAAAAmtVJIFk~%2CTVGOQ5ZTwJbeMWnq5d_H4MOM57xfzApc'
|
'playerKey=AQ~~%2CAAAAmtVJIFk~%2CTVGOQ5ZTwJbeMWnq5d_H4MOM57xfzApc'
|
||||||
'&%40videoPlayer={0}'.format(brightcove_id)
|
'&%40videoPlayer={0}'.format(brightcove_id)
|
||||||
),
|
),
|
||||||
'ie_key': 'Brightcove',
|
'ie_key': 'BrightcoveLegacy',
|
||||||
}
|
}
|
||||||
|
@@ -7,11 +7,11 @@ from .common import InfoExtractor
|
|||||||
from ..compat import (
|
from ..compat import (
|
||||||
compat_str,
|
compat_str,
|
||||||
compat_urllib_parse,
|
compat_urllib_parse,
|
||||||
compat_urllib_request,
|
|
||||||
)
|
)
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
int_or_none,
|
int_or_none,
|
||||||
float_or_none,
|
float_or_none,
|
||||||
|
sanitized_Request,
|
||||||
xpath_text,
|
xpath_text,
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
)
|
)
|
||||||
@@ -63,7 +63,7 @@ class AtresPlayerIE(InfoExtractor):
|
|||||||
'j_password': password,
|
'j_password': password,
|
||||||
}
|
}
|
||||||
|
|
||||||
request = compat_urllib_request.Request(
|
request = sanitized_Request(
|
||||||
self._LOGIN_URL, compat_urllib_parse.urlencode(login_form).encode('utf-8'))
|
self._LOGIN_URL, compat_urllib_parse.urlencode(login_form).encode('utf-8'))
|
||||||
request.add_header('Content-Type', 'application/x-www-form-urlencoded')
|
request.add_header('Content-Type', 'application/x-www-form-urlencoded')
|
||||||
response = self._download_webpage(
|
response = self._download_webpage(
|
||||||
@@ -94,7 +94,7 @@ class AtresPlayerIE(InfoExtractor):
|
|||||||
|
|
||||||
formats = []
|
formats = []
|
||||||
for fmt in ['windows', 'android_tablet']:
|
for fmt in ['windows', 'android_tablet']:
|
||||||
request = compat_urllib_request.Request(
|
request = sanitized_Request(
|
||||||
self._URL_VIDEO_TEMPLATE.format(fmt, episode_id, timestamp_shifted, token))
|
self._URL_VIDEO_TEMPLATE.format(fmt, episode_id, timestamp_shifted, token))
|
||||||
request.add_header('User-Agent', self._USER_AGENT)
|
request.add_header('User-Agent', self._USER_AGENT)
|
||||||
|
|
||||||
|
80
youtube_dl/extractor/audimedia.py
Normal file
80
youtube_dl/extractor/audimedia.py
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from .common import InfoExtractor
|
||||||
|
from ..utils import (
|
||||||
|
int_or_none,
|
||||||
|
parse_iso8601,
|
||||||
|
sanitized_Request,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class AudiMediaIE(InfoExtractor):
|
||||||
|
_VALID_URL = r'https?://(?:www\.)?audimedia\.tv/(?:en|de)/vid/(?P<id>[^/?#]+)'
|
||||||
|
_TEST = {
|
||||||
|
'url': 'https://audimedia.tv/en/vid/60-seconds-of-audi-sport-104-2015-wec-bahrain-rookie-test',
|
||||||
|
'md5': '79a8b71c46d49042609795ab59779b66',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '1564',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': '60 Seconds of Audi Sport 104/2015 - WEC Bahrain, Rookie Test',
|
||||||
|
'description': 'md5:60e5d30a78ced725f7b8d34370762941',
|
||||||
|
'upload_date': '20151124',
|
||||||
|
'timestamp': 1448354940,
|
||||||
|
'duration': 74022,
|
||||||
|
'view_count': int,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# extracted from https://audimedia.tv/assets/embed/embedded-player.js (dataSourceAuthToken)
|
||||||
|
_AUTH_TOKEN = 'e25b42847dba18c6c8816d5d8ce94c326e06823ebf0859ed164b3ba169be97f2'
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
display_id = self._match_id(url)
|
||||||
|
webpage = self._download_webpage(url, display_id)
|
||||||
|
|
||||||
|
raw_payload = self._search_regex(r'<script[^>]+class="amtv-embed"[^>]+id="([^"]+)"', webpage, 'raw payload')
|
||||||
|
_, stage_mode, video_id, lang = raw_payload.split('-')
|
||||||
|
|
||||||
|
# TODO: handle s and e stage_mode (live streams and ended live streams)
|
||||||
|
if stage_mode not in ('s', 'e'):
|
||||||
|
request = sanitized_Request(
|
||||||
|
'https://audimedia.tv/api/video/v1/videos/%s?embed[]=video_versions&embed[]=thumbnail_image&where[content_language_iso]=%s' % (video_id, lang),
|
||||||
|
headers={'X-Auth-Token': self._AUTH_TOKEN})
|
||||||
|
json_data = self._download_json(request, video_id)['results']
|
||||||
|
formats = []
|
||||||
|
|
||||||
|
stream_url_hls = json_data.get('stream_url_hls')
|
||||||
|
if stream_url_hls:
|
||||||
|
m3u8_formats = self._extract_m3u8_formats(stream_url_hls, video_id, 'mp4', entry_protocol='m3u8_native', m3u8_id='hls', fatal=False)
|
||||||
|
if m3u8_formats:
|
||||||
|
formats.extend(m3u8_formats)
|
||||||
|
|
||||||
|
stream_url_hds = json_data.get('stream_url_hds')
|
||||||
|
if stream_url_hds:
|
||||||
|
f4m_formats = self._extract_f4m_formats(json_data.get('stream_url_hds') + '?hdcore=3.4.0', video_id, -1, f4m_id='hds', fatal=False)
|
||||||
|
if f4m_formats:
|
||||||
|
formats.extend(f4m_formats)
|
||||||
|
|
||||||
|
for video_version in json_data.get('video_versions'):
|
||||||
|
video_version_url = video_version.get('download_url') or video_version.get('stream_url')
|
||||||
|
if not video_version_url:
|
||||||
|
continue
|
||||||
|
formats.append({
|
||||||
|
'url': video_version_url,
|
||||||
|
'width': int_or_none(video_version.get('width')),
|
||||||
|
'height': int_or_none(video_version.get('height')),
|
||||||
|
'abr': int_or_none(video_version.get('audio_bitrate')),
|
||||||
|
'vbr': int_or_none(video_version.get('video_bitrate')),
|
||||||
|
})
|
||||||
|
self._sort_formats(formats)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'id': video_id,
|
||||||
|
'title': json_data['title'],
|
||||||
|
'description': json_data.get('subtitle'),
|
||||||
|
'thumbnail': json_data.get('thumbnail_image', {}).get('file'),
|
||||||
|
'timestamp': parse_iso8601(json_data.get('publication_date')),
|
||||||
|
'duration': int_or_none(json_data.get('duration')),
|
||||||
|
'view_count': int_or_none(json_data.get('view_count')),
|
||||||
|
'formats': formats,
|
||||||
|
}
|
@@ -6,13 +6,13 @@ import itertools
|
|||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
from ..compat import (
|
||||||
compat_urllib_parse,
|
compat_urllib_parse,
|
||||||
compat_urllib_request,
|
|
||||||
compat_str,
|
compat_str,
|
||||||
)
|
)
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
float_or_none,
|
float_or_none,
|
||||||
|
sanitized_Request,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ class BambuserIE(InfoExtractor):
|
|||||||
'pass': password,
|
'pass': password,
|
||||||
}
|
}
|
||||||
|
|
||||||
request = compat_urllib_request.Request(
|
request = sanitized_Request(
|
||||||
self._LOGIN_URL, compat_urllib_parse.urlencode(login_form).encode('utf-8'))
|
self._LOGIN_URL, compat_urllib_parse.urlencode(login_form).encode('utf-8'))
|
||||||
request.add_header('Referer', self._LOGIN_URL)
|
request.add_header('Referer', self._LOGIN_URL)
|
||||||
response = self._download_webpage(
|
response = self._download_webpage(
|
||||||
@@ -126,7 +126,7 @@ class BambuserChannelIE(InfoExtractor):
|
|||||||
'&sort=created&access_mode=0%2C1%2C2&limit={count}'
|
'&sort=created&access_mode=0%2C1%2C2&limit={count}'
|
||||||
'&method=broadcast&format=json&vid_older_than={last}'
|
'&method=broadcast&format=json&vid_older_than={last}'
|
||||||
).format(user=user, count=self._STEP, last=last_id)
|
).format(user=user, count=self._STEP, last=last_id)
|
||||||
req = compat_urllib_request.Request(req_url)
|
req = sanitized_Request(req_url)
|
||||||
# Without setting this header, we wouldn't get any result
|
# Without setting this header, we wouldn't get any result
|
||||||
req.add_header('Referer', 'http://bambuser.com/channel/%s' % user)
|
req.add_header('Referer', 'http://bambuser.com/channel/%s' % user)
|
||||||
data = self._download_json(
|
data = self._download_json(
|
||||||
|
@@ -22,12 +22,13 @@ from ..compat import (
|
|||||||
class BBCCoUkIE(InfoExtractor):
|
class BBCCoUkIE(InfoExtractor):
|
||||||
IE_NAME = 'bbc.co.uk'
|
IE_NAME = 'bbc.co.uk'
|
||||||
IE_DESC = 'BBC iPlayer'
|
IE_DESC = 'BBC iPlayer'
|
||||||
_VALID_URL = r'https?://(?:www\.)?bbc\.co\.uk/(?:(?:programmes/(?!articles/)|iplayer(?:/[^/]+)?/(?:episode/|playlist/))|music/clips[/#])(?P<id>[\da-z]{8})'
|
_ID_REGEX = r'[pb][\da-z]{7}'
|
||||||
|
_VALID_URL = r'https?://(?:www\.)?bbc\.co\.uk/(?:(?:programmes/(?!articles/)|iplayer(?:/[^/]+)?/(?:episode/|playlist/))|music/clips[/#])(?P<id>%s)' % _ID_REGEX
|
||||||
|
|
||||||
_MEDIASELECTOR_URLS = [
|
_MEDIASELECTOR_URLS = [
|
||||||
# Provides HQ HLS streams with even better quality that pc mediaset but fails
|
# Provides HQ HLS streams with even better quality that pc mediaset but fails
|
||||||
# with geolocation in some cases when it's even not geo restricted at all (e.g.
|
# with geolocation in some cases when it's even not geo restricted at all (e.g.
|
||||||
# http://www.bbc.co.uk/programmes/b06bp7lf)
|
# http://www.bbc.co.uk/programmes/b06bp7lf). Also may fail with selectionunavailable.
|
||||||
'http://open.live.bbc.co.uk/mediaselector/5/select/version/2.0/mediaset/iptv-all/vpid/%s',
|
'http://open.live.bbc.co.uk/mediaselector/5/select/version/2.0/mediaset/iptv-all/vpid/%s',
|
||||||
'http://open.live.bbc.co.uk/mediaselector/5/select/version/2.0/mediaset/pc/vpid/%s',
|
'http://open.live.bbc.co.uk/mediaselector/5/select/version/2.0/mediaset/pc/vpid/%s',
|
||||||
]
|
]
|
||||||
@@ -46,9 +47,8 @@ class BBCCoUkIE(InfoExtractor):
|
|||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'b039d07m',
|
'id': 'b039d07m',
|
||||||
'ext': 'flv',
|
'ext': 'flv',
|
||||||
'title': 'Kaleidoscope, Leonard Cohen',
|
'title': 'Leonard Cohen, Kaleidoscope - BBC Radio 4',
|
||||||
'description': 'The Canadian poet and songwriter reflects on his musical career.',
|
'description': 'The Canadian poet and songwriter reflects on his musical career.',
|
||||||
'duration': 1740,
|
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
# rtmp download
|
# rtmp download
|
||||||
@@ -111,7 +111,8 @@ class BBCCoUkIE(InfoExtractor):
|
|||||||
'params': {
|
'params': {
|
||||||
# rtmp download
|
# rtmp download
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
}
|
},
|
||||||
|
'skip': 'Episode is no longer available on BBC iPlayer Radio',
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://www.bbc.co.uk/music/clips/p02frcc3',
|
'url': 'http://www.bbc.co.uk/music/clips/p02frcc3',
|
||||||
'note': 'Audio',
|
'note': 'Audio',
|
||||||
@@ -334,7 +335,7 @@ class BBCCoUkIE(InfoExtractor):
|
|||||||
return self._download_media_selector_url(
|
return self._download_media_selector_url(
|
||||||
mediaselector_url % programme_id, programme_id)
|
mediaselector_url % programme_id, programme_id)
|
||||||
except BBCCoUkIE.MediaSelectionError as e:
|
except BBCCoUkIE.MediaSelectionError as e:
|
||||||
if e.id in ('notukerror', 'geolocation'):
|
if e.id in ('notukerror', 'geolocation', 'selectionunavailable'):
|
||||||
last_exception = e
|
last_exception = e
|
||||||
continue
|
continue
|
||||||
self._raise_extractor_error(e)
|
self._raise_extractor_error(e)
|
||||||
@@ -345,7 +346,7 @@ class BBCCoUkIE(InfoExtractor):
|
|||||||
media_selection = self._download_xml(
|
media_selection = self._download_xml(
|
||||||
url, programme_id, 'Downloading media selection XML')
|
url, programme_id, 'Downloading media selection XML')
|
||||||
except ExtractorError as ee:
|
except ExtractorError as ee:
|
||||||
if isinstance(ee.cause, compat_HTTPError) and ee.cause.code == 403:
|
if isinstance(ee.cause, compat_HTTPError) and ee.cause.code in (403, 404):
|
||||||
media_selection = compat_etree_fromstring(ee.cause.read().decode('utf-8'))
|
media_selection = compat_etree_fromstring(ee.cause.read().decode('utf-8'))
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
@@ -453,6 +454,7 @@ class BBCCoUkIE(InfoExtractor):
|
|||||||
webpage = self._download_webpage(url, group_id, 'Downloading video page')
|
webpage = self._download_webpage(url, group_id, 'Downloading video page')
|
||||||
|
|
||||||
programme_id = None
|
programme_id = None
|
||||||
|
duration = None
|
||||||
|
|
||||||
tviplayer = self._search_regex(
|
tviplayer = self._search_regex(
|
||||||
r'mediator\.bind\(({.+?})\s*,\s*document\.getElementById',
|
r'mediator\.bind\(({.+?})\s*,\s*document\.getElementById',
|
||||||
@@ -465,14 +467,16 @@ class BBCCoUkIE(InfoExtractor):
|
|||||||
|
|
||||||
if not programme_id:
|
if not programme_id:
|
||||||
programme_id = self._search_regex(
|
programme_id = self._search_regex(
|
||||||
r'"vpid"\s*:\s*"([\da-z]{8})"', webpage, 'vpid', fatal=False, default=None)
|
r'"vpid"\s*:\s*"(%s)"' % self._ID_REGEX, webpage, 'vpid', fatal=False, default=None)
|
||||||
|
|
||||||
if programme_id:
|
if programme_id:
|
||||||
formats, subtitles = self._download_media_selector(programme_id)
|
formats, subtitles = self._download_media_selector(programme_id)
|
||||||
title = self._og_search_title(webpage)
|
title = self._og_search_title(webpage)
|
||||||
description = self._search_regex(
|
description = self._search_regex(
|
||||||
r'<p class="[^"]*medium-description[^"]*">([^<]+)</p>',
|
r'<p class="[^"]*medium-description[^"]*">([^<]+)</p>',
|
||||||
webpage, 'description', fatal=False)
|
webpage, 'description', default=None)
|
||||||
|
if not description:
|
||||||
|
description = self._html_search_meta('description', webpage)
|
||||||
else:
|
else:
|
||||||
programme_id, title, description, duration, formats, subtitles = self._download_playlist(group_id)
|
programme_id, title, description, duration, formats, subtitles = self._download_playlist(group_id)
|
||||||
|
|
||||||
@@ -586,6 +590,7 @@ class BBCIE(BBCCoUkIE):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': '''Judge Mindy Glazer: "I'm sorry to see you here... I always wondered what happened to you"''',
|
'title': '''Judge Mindy Glazer: "I'm sorry to see you here... I always wondered what happened to you"''',
|
||||||
'duration': 56,
|
'duration': 56,
|
||||||
|
'description': '''Judge Mindy Glazer: "I'm sorry to see you here... I always wondered what happened to you"''',
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
@@ -728,6 +733,7 @@ class BBCIE(BBCCoUkIE):
|
|||||||
# article with multiple videos embedded with playlist.sxml (e.g.
|
# article with multiple videos embedded with playlist.sxml (e.g.
|
||||||
# http://www.bbc.com/sport/0/football/34475836)
|
# http://www.bbc.com/sport/0/football/34475836)
|
||||||
playlists = re.findall(r'<param[^>]+name="playlist"[^>]+value="([^"]+)"', webpage)
|
playlists = re.findall(r'<param[^>]+name="playlist"[^>]+value="([^"]+)"', webpage)
|
||||||
|
playlists.extend(re.findall(r'data-media-id="([^"]+/playlist\.sxml)"', webpage))
|
||||||
if playlists:
|
if playlists:
|
||||||
entries = [
|
entries = [
|
||||||
self._extract_from_playlist_sxml(playlist_url, playlist_id, timestamp)
|
self._extract_from_playlist_sxml(playlist_url, playlist_id, timestamp)
|
||||||
@@ -780,8 +786,9 @@ class BBCIE(BBCCoUkIE):
|
|||||||
|
|
||||||
# single video story (e.g. http://www.bbc.com/travel/story/20150625-sri-lankas-spicy-secret)
|
# single video story (e.g. http://www.bbc.com/travel/story/20150625-sri-lankas-spicy-secret)
|
||||||
programme_id = self._search_regex(
|
programme_id = self._search_regex(
|
||||||
[r'data-video-player-vpid="([\da-z]{8})"',
|
[r'data-video-player-vpid="(%s)"' % self._ID_REGEX,
|
||||||
r'<param[^>]+name="externalIdentifier"[^>]+value="([\da-z]{8})"'],
|
r'<param[^>]+name="externalIdentifier"[^>]+value="(%s)"' % self._ID_REGEX,
|
||||||
|
r'videoId\s*:\s*["\'](%s)["\']' % self._ID_REGEX],
|
||||||
webpage, 'vpid', default=None)
|
webpage, 'vpid', default=None)
|
||||||
|
|
||||||
if programme_id:
|
if programme_id:
|
||||||
@@ -816,7 +823,7 @@ class BBCIE(BBCCoUkIE):
|
|||||||
|
|
||||||
# Multiple video article (e.g.
|
# Multiple video article (e.g.
|
||||||
# http://www.bbc.co.uk/blogs/adamcurtis/entries/3662a707-0af9-3149-963f-47bea720b460)
|
# http://www.bbc.co.uk/blogs/adamcurtis/entries/3662a707-0af9-3149-963f-47bea720b460)
|
||||||
EMBED_URL = r'https?://(?:www\.)?bbc\.co\.uk/(?:[^/]+/)+[\da-z]{8}(?:\b[^"]+)?'
|
EMBED_URL = r'https?://(?:www\.)?bbc\.co\.uk/(?:[^/]+/)+%s(?:\b[^"]+)?' % self._ID_REGEX
|
||||||
entries = []
|
entries = []
|
||||||
for match in extract_all(r'new\s+SMP\(({.+?})\)'):
|
for match in extract_all(r'new\s+SMP\(({.+?})\)'):
|
||||||
embed_url = match.get('playerSettings', {}).get('externalEmbedUrl')
|
embed_url = match.get('playerSettings', {}).get('externalEmbedUrl')
|
||||||
|
@@ -1,6 +1,11 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
|
from ..compat import (
|
||||||
|
compat_chr,
|
||||||
|
compat_ord,
|
||||||
|
compat_urllib_parse_unquote,
|
||||||
|
)
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
int_or_none,
|
int_or_none,
|
||||||
parse_iso8601,
|
parse_iso8601,
|
||||||
@@ -29,7 +34,38 @@ class BeegIE(InfoExtractor):
|
|||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
|
|
||||||
video = self._download_json(
|
video = self._download_json(
|
||||||
'http://beeg.com/api/v1/video/%s' % video_id, video_id)
|
'http://beeg.com/api/v5/video/%s' % video_id, video_id)
|
||||||
|
|
||||||
|
def split(o, e):
|
||||||
|
def cut(s, x):
|
||||||
|
n.append(s[:x])
|
||||||
|
return s[x:]
|
||||||
|
n = []
|
||||||
|
r = len(o) % e
|
||||||
|
if r > 0:
|
||||||
|
o = cut(o, r)
|
||||||
|
while len(o) > e:
|
||||||
|
o = cut(o, e)
|
||||||
|
n.append(o)
|
||||||
|
return n
|
||||||
|
|
||||||
|
def decrypt_key(key):
|
||||||
|
# Reverse engineered from http://static.beeg.com/cpl/1105.js
|
||||||
|
a = '5ShMcIQlssOd7zChAIOlmeTZDaUxULbJRnywYaiB'
|
||||||
|
e = compat_urllib_parse_unquote(key)
|
||||||
|
o = ''.join([
|
||||||
|
compat_chr(compat_ord(e[n]) - compat_ord(a[n % len(a)]) % 21)
|
||||||
|
for n in range(len(e))])
|
||||||
|
return ''.join(split(o, 3)[::-1])
|
||||||
|
|
||||||
|
def decrypt_url(encrypted_url):
|
||||||
|
encrypted_url = self._proto_relative_url(
|
||||||
|
encrypted_url.replace('{DATA_MARKERS}', ''), 'http:')
|
||||||
|
key = self._search_regex(
|
||||||
|
r'/key=(.*?)%2Cend=', encrypted_url, 'key', default=None)
|
||||||
|
if not key:
|
||||||
|
return encrypted_url
|
||||||
|
return encrypted_url.replace(key, decrypt_key(key))
|
||||||
|
|
||||||
formats = []
|
formats = []
|
||||||
for format_id, video_url in video.items():
|
for format_id, video_url in video.items():
|
||||||
@@ -40,7 +76,7 @@ class BeegIE(InfoExtractor):
|
|||||||
if not height:
|
if not height:
|
||||||
continue
|
continue
|
||||||
formats.append({
|
formats.append({
|
||||||
'url': self._proto_relative_url(video_url.replace('{DATA_MARKERS}', ''), 'http:'),
|
'url': decrypt_url(video_url),
|
||||||
'format_id': format_id,
|
'format_id': format_id,
|
||||||
'height': int(height),
|
'height': int(height),
|
||||||
})
|
})
|
||||||
|
@@ -2,143 +2,109 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import itertools
|
|
||||||
import json
|
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
from ..compat import compat_str
|
||||||
compat_etree_fromstring,
|
|
||||||
)
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
int_or_none,
|
int_or_none,
|
||||||
unified_strdate,
|
unescapeHTML,
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
|
xpath_text,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class BiliBiliIE(InfoExtractor):
|
class BiliBiliIE(InfoExtractor):
|
||||||
_VALID_URL = r'http://www\.bilibili\.(?:tv|com)/video/av(?P<id>[0-9]+)/'
|
_VALID_URL = r'http://www\.bilibili\.(?:tv|com)/video/av(?P<id>\d+)(?:/index_(?P<page_num>\d+).html)?'
|
||||||
|
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://www.bilibili.tv/video/av1074402/',
|
'url': 'http://www.bilibili.tv/video/av1074402/',
|
||||||
'md5': '2c301e4dab317596e837c3e7633e7d86',
|
'md5': '2c301e4dab317596e837c3e7633e7d86',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '1074402_part1',
|
'id': '1554319',
|
||||||
'ext': 'flv',
|
'ext': 'flv',
|
||||||
'title': '【金坷垃】金泡沫',
|
'title': '【金坷垃】金泡沫',
|
||||||
'duration': 308,
|
'duration': 308313,
|
||||||
'upload_date': '20140420',
|
'upload_date': '20140420',
|
||||||
'thumbnail': 're:^https?://.+\.jpg',
|
'thumbnail': 're:^https?://.+\.jpg',
|
||||||
|
'description': 'md5:ce18c2a2d2193f0df2917d270f2e5923',
|
||||||
|
'timestamp': 1397983878,
|
||||||
|
'uploader': '菊子桑',
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://www.bilibili.com/video/av1041170/',
|
'url': 'http://www.bilibili.com/video/av1041170/',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '1041170',
|
'id': '1041170',
|
||||||
'title': '【BD1080P】刀语【诸神&异域】',
|
'title': '【BD1080P】刀语【诸神&异域】',
|
||||||
|
'description': '这是个神奇的故事~每个人不留弹幕不给走哦~切利哦!~',
|
||||||
|
'uploader': '枫叶逝去',
|
||||||
|
'timestamp': 1396501299,
|
||||||
},
|
},
|
||||||
'playlist_count': 9,
|
'playlist_count': 9,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
mobj = re.match(self._VALID_URL, url)
|
||||||
webpage = self._download_webpage(url, video_id)
|
video_id = mobj.group('id')
|
||||||
|
page_num = mobj.group('page_num') or '1'
|
||||||
|
|
||||||
if '(此视频不存在或被删除)' in webpage:
|
view_data = self._download_json(
|
||||||
raise ExtractorError(
|
'http://api.bilibili.com/view?type=json&appkey=8e9fc618fbd41e28&id=%s&page=%s' % (video_id, page_num),
|
||||||
'The video does not exist or was deleted', expected=True)
|
video_id)
|
||||||
|
if 'error' in view_data:
|
||||||
|
raise ExtractorError('%s said: %s' % (self.IE_NAME, view_data['error']), expected=True)
|
||||||
|
|
||||||
if '>你没有权限浏览! 由于版权相关问题 我们不对您所在的地区提供服务<' in webpage:
|
cid = view_data['cid']
|
||||||
raise ExtractorError(
|
title = unescapeHTML(view_data['title'])
|
||||||
'The video is not available in your region due to copyright reasons',
|
|
||||||
expected=True)
|
|
||||||
|
|
||||||
video_code = self._search_regex(
|
doc = self._download_xml(
|
||||||
r'(?s)<div itemprop="video".*?>(.*?)</div>', webpage, 'video code')
|
'http://interface.bilibili.com/v_cdn_play?appkey=8e9fc618fbd41e28&cid=%s' % cid,
|
||||||
|
cid,
|
||||||
|
'Downloading page %s/%s' % (page_num, view_data['pages'])
|
||||||
|
)
|
||||||
|
|
||||||
title = self._html_search_meta(
|
if xpath_text(doc, './result') == 'error':
|
||||||
'media:title', video_code, 'title', fatal=True)
|
raise ExtractorError('%s said: %s' % (self.IE_NAME, xpath_text(doc, './message')), expected=True)
|
||||||
duration_str = self._html_search_meta(
|
|
||||||
'duration', video_code, 'duration')
|
|
||||||
if duration_str is None:
|
|
||||||
duration = None
|
|
||||||
else:
|
|
||||||
duration_mobj = re.match(
|
|
||||||
r'^T(?:(?P<hours>[0-9]+)H)?(?P<minutes>[0-9]+)M(?P<seconds>[0-9]+)S$',
|
|
||||||
duration_str)
|
|
||||||
duration = (
|
|
||||||
int_or_none(duration_mobj.group('hours'), default=0) * 3600 +
|
|
||||||
int(duration_mobj.group('minutes')) * 60 +
|
|
||||||
int(duration_mobj.group('seconds')))
|
|
||||||
upload_date = unified_strdate(self._html_search_meta(
|
|
||||||
'uploadDate', video_code, fatal=False))
|
|
||||||
thumbnail = self._html_search_meta(
|
|
||||||
'thumbnailUrl', video_code, 'thumbnail', fatal=False)
|
|
||||||
|
|
||||||
cid = self._search_regex(r'cid=(\d+)', webpage, 'cid')
|
|
||||||
|
|
||||||
entries = []
|
entries = []
|
||||||
|
|
||||||
lq_page = self._download_webpage(
|
for durl in doc.findall('./durl'):
|
||||||
'http://interface.bilibili.com/v_cdn_play?appkey=1&cid=%s' % cid,
|
size = xpath_text(durl, ['./filesize', './size'])
|
||||||
video_id,
|
|
||||||
note='Downloading LQ video info'
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
err_info = json.loads(lq_page)
|
|
||||||
raise ExtractorError(
|
|
||||||
'BiliBili said: ' + err_info['error_text'], expected=True)
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
lq_doc = compat_etree_fromstring(lq_page)
|
|
||||||
lq_durls = lq_doc.findall('./durl')
|
|
||||||
|
|
||||||
hq_doc = self._download_xml(
|
|
||||||
'http://interface.bilibili.com/playurl?appkey=1&cid=%s' % cid,
|
|
||||||
video_id,
|
|
||||||
note='Downloading HQ video info',
|
|
||||||
fatal=False,
|
|
||||||
)
|
|
||||||
if hq_doc is not False:
|
|
||||||
hq_durls = hq_doc.findall('./durl')
|
|
||||||
assert len(lq_durls) == len(hq_durls)
|
|
||||||
else:
|
|
||||||
hq_durls = itertools.repeat(None)
|
|
||||||
|
|
||||||
i = 1
|
|
||||||
for lq_durl, hq_durl in zip(lq_durls, hq_durls):
|
|
||||||
formats = [{
|
formats = [{
|
||||||
'format_id': 'lq',
|
'url': durl.find('./url').text,
|
||||||
'quality': 1,
|
'filesize': int_or_none(size),
|
||||||
'url': lq_durl.find('./url').text,
|
'ext': 'flv',
|
||||||
'filesize': int_or_none(
|
|
||||||
lq_durl.find('./size'), get_attr='text'),
|
|
||||||
}]
|
}]
|
||||||
if hq_durl is not None:
|
backup_urls = durl.find('./backup_url')
|
||||||
formats.append({
|
if backup_urls is not None:
|
||||||
'format_id': 'hq',
|
for backup_url in backup_urls.findall('./url'):
|
||||||
'quality': 2,
|
formats.append({'url': backup_url.text})
|
||||||
'ext': 'flv',
|
formats.reverse()
|
||||||
'url': hq_durl.find('./url').text,
|
|
||||||
'filesize': int_or_none(
|
|
||||||
hq_durl.find('./size'), get_attr='text'),
|
|
||||||
})
|
|
||||||
self._sort_formats(formats)
|
|
||||||
|
|
||||||
entries.append({
|
entries.append({
|
||||||
'id': '%s_part%d' % (video_id, i),
|
'id': '%s_part%s' % (cid, xpath_text(durl, './order')),
|
||||||
'title': title,
|
'title': title,
|
||||||
|
'duration': int_or_none(xpath_text(durl, './length'), 1000),
|
||||||
'formats': formats,
|
'formats': formats,
|
||||||
'duration': duration,
|
|
||||||
'upload_date': upload_date,
|
|
||||||
'thumbnail': thumbnail,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
i += 1
|
info = {
|
||||||
|
'id': compat_str(cid),
|
||||||
return {
|
'title': title,
|
||||||
'_type': 'multi_video',
|
'description': view_data.get('description'),
|
||||||
'entries': entries,
|
'thumbnail': view_data.get('pic'),
|
||||||
'id': video_id,
|
'uploader': view_data.get('author'),
|
||||||
'title': title
|
'timestamp': int_or_none(view_data.get('created')),
|
||||||
|
'view_count': int_or_none(view_data.get('play')),
|
||||||
|
'duration': int_or_none(xpath_text(doc, './timelength')),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(entries) == 1:
|
||||||
|
entries[0].update(info)
|
||||||
|
return entries[0]
|
||||||
|
else:
|
||||||
|
info.update({
|
||||||
|
'_type': 'multi_video',
|
||||||
|
'id': video_id,
|
||||||
|
'entries': entries,
|
||||||
|
})
|
||||||
|
return info
|
||||||
|
@@ -4,14 +4,12 @@ import re
|
|||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
|
|
||||||
from ..compat import (
|
from ..compat import compat_urlparse
|
||||||
compat_urllib_request,
|
|
||||||
compat_urlparse,
|
|
||||||
)
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
clean_html,
|
clean_html,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
parse_iso8601,
|
parse_iso8601,
|
||||||
|
sanitized_Request,
|
||||||
unescapeHTML,
|
unescapeHTML,
|
||||||
xpath_text,
|
xpath_text,
|
||||||
xpath_with_ns,
|
xpath_with_ns,
|
||||||
@@ -219,7 +217,7 @@ class BlipTVIE(InfoExtractor):
|
|||||||
for lang, url in subtitles_urls.items():
|
for lang, url in subtitles_urls.items():
|
||||||
# For some weird reason, blip.tv serves a video instead of subtitles
|
# For some weird reason, blip.tv serves a video instead of subtitles
|
||||||
# when we request with a common UA
|
# when we request with a common UA
|
||||||
req = compat_urllib_request.Request(url)
|
req = sanitized_Request(url)
|
||||||
req.add_header('User-Agent', 'youtube-dl')
|
req.add_header('User-Agent', 'youtube-dl')
|
||||||
subtitles[lang] = [{
|
subtitles[lang] = [{
|
||||||
# The extension is 'srt' but it's actually an 'ass' file
|
# The extension is 'srt' but it's actually an 'ass' file
|
||||||
|
@@ -6,9 +6,9 @@ from .common import InfoExtractor
|
|||||||
|
|
||||||
|
|
||||||
class BloombergIE(InfoExtractor):
|
class BloombergIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://www\.bloomberg\.com/news/videos/[^/]+/(?P<id>[^/?#]+)'
|
_VALID_URL = r'https?://(?:www\.)?bloomberg\.com/(?:[^/]+/)*(?P<id>[^/?#]+)'
|
||||||
|
|
||||||
_TEST = {
|
_TESTS = [{
|
||||||
'url': 'http://www.bloomberg.com/news/videos/b/aaeae121-5949-481e-a1ce-4562db6f5df2',
|
'url': 'http://www.bloomberg.com/news/videos/b/aaeae121-5949-481e-a1ce-4562db6f5df2',
|
||||||
# The md5 checksum changes
|
# The md5 checksum changes
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
@@ -17,22 +17,39 @@ class BloombergIE(InfoExtractor):
|
|||||||
'title': 'Shah\'s Presentation on Foreign-Exchange Strategies',
|
'title': 'Shah\'s Presentation on Foreign-Exchange Strategies',
|
||||||
'description': 'md5:a8ba0302912d03d246979735c17d2761',
|
'description': 'md5:a8ba0302912d03d246979735c17d2761',
|
||||||
},
|
},
|
||||||
}
|
}, {
|
||||||
|
'url': 'http://www.bloomberg.com/news/articles/2015-11-12/five-strange-things-that-have-been-happening-in-financial-markets',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'http://www.bloomberg.com/politics/videos/2015-11-25/karl-rove-on-jeb-bush-s-struggles-stopping-trump',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
name = self._match_id(url)
|
name = self._match_id(url)
|
||||||
webpage = self._download_webpage(url, name)
|
webpage = self._download_webpage(url, name)
|
||||||
video_id = self._search_regex(r'"bmmrId":"(.+?)"', webpage, 'id')
|
video_id = self._search_regex(
|
||||||
|
r'["\']bmmrId["\']\s*:\s*(["\'])(?P<url>.+?)\1',
|
||||||
|
webpage, 'id', group='url')
|
||||||
title = re.sub(': Video$', '', self._og_search_title(webpage))
|
title = re.sub(': Video$', '', self._og_search_title(webpage))
|
||||||
|
|
||||||
embed_info = self._download_json(
|
embed_info = self._download_json(
|
||||||
'http://www.bloomberg.com/api/embed?id=%s' % video_id, video_id)
|
'http://www.bloomberg.com/api/embed?id=%s' % video_id, video_id)
|
||||||
formats = []
|
formats = []
|
||||||
for stream in embed_info['streams']:
|
for stream in embed_info['streams']:
|
||||||
if stream["muxing_format"] == "TS":
|
stream_url = stream.get('url')
|
||||||
formats.extend(self._extract_m3u8_formats(stream['url'], video_id))
|
if not stream_url:
|
||||||
|
continue
|
||||||
|
if stream['muxing_format'] == 'TS':
|
||||||
|
m3u8_formats = self._extract_m3u8_formats(
|
||||||
|
stream_url, video_id, 'mp4', m3u8_id='hls', fatal=False)
|
||||||
|
if m3u8_formats:
|
||||||
|
formats.extend(m3u8_formats)
|
||||||
else:
|
else:
|
||||||
formats.extend(self._extract_f4m_formats(stream['url'], video_id))
|
f4m_formats = self._extract_f4m_formats(
|
||||||
|
stream_url, video_id, f4m_id='hds', fatal=False)
|
||||||
|
if f4m_formats:
|
||||||
|
formats.extend(f4m_formats)
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@@ -11,7 +11,6 @@ from ..compat import (
|
|||||||
compat_str,
|
compat_str,
|
||||||
compat_urllib_parse,
|
compat_urllib_parse,
|
||||||
compat_urllib_parse_urlparse,
|
compat_urllib_parse_urlparse,
|
||||||
compat_urllib_request,
|
|
||||||
compat_urlparse,
|
compat_urlparse,
|
||||||
compat_xml_parse_error,
|
compat_xml_parse_error,
|
||||||
)
|
)
|
||||||
@@ -20,12 +19,18 @@ from ..utils import (
|
|||||||
ExtractorError,
|
ExtractorError,
|
||||||
find_xpath_attr,
|
find_xpath_attr,
|
||||||
fix_xml_ampersands,
|
fix_xml_ampersands,
|
||||||
|
float_or_none,
|
||||||
|
js_to_json,
|
||||||
|
int_or_none,
|
||||||
|
parse_iso8601,
|
||||||
|
sanitized_Request,
|
||||||
unescapeHTML,
|
unescapeHTML,
|
||||||
unsmuggle_url,
|
unsmuggle_url,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class BrightcoveIE(InfoExtractor):
|
class BrightcoveLegacyIE(InfoExtractor):
|
||||||
|
IE_NAME = 'brightcove:legacy'
|
||||||
_VALID_URL = r'(?:https?://.*brightcove\.com/(services|viewer).*?\?|brightcove:)(?P<query>.*)'
|
_VALID_URL = r'(?:https?://.*brightcove\.com/(services|viewer).*?\?|brightcove:)(?P<query>.*)'
|
||||||
_FEDERATED_URL_TEMPLATE = 'http://c.brightcove.com/services/viewer/htmlFederated?%s'
|
_FEDERATED_URL_TEMPLATE = 'http://c.brightcove.com/services/viewer/htmlFederated?%s'
|
||||||
|
|
||||||
@@ -245,7 +250,7 @@ class BrightcoveIE(InfoExtractor):
|
|||||||
|
|
||||||
def _get_video_info(self, video_id, query_str, query, referer=None):
|
def _get_video_info(self, video_id, query_str, query, referer=None):
|
||||||
request_url = self._FEDERATED_URL_TEMPLATE % query_str
|
request_url = self._FEDERATED_URL_TEMPLATE % query_str
|
||||||
req = compat_urllib_request.Request(request_url)
|
req = sanitized_Request(request_url)
|
||||||
linkBase = query.get('linkBaseURL')
|
linkBase = query.get('linkBaseURL')
|
||||||
if linkBase is not None:
|
if linkBase is not None:
|
||||||
referer = linkBase[0]
|
referer = linkBase[0]
|
||||||
@@ -346,3 +351,172 @@ class BrightcoveIE(InfoExtractor):
|
|||||||
if 'url' not in info and not info.get('formats'):
|
if 'url' not in info and not info.get('formats'):
|
||||||
raise ExtractorError('Unable to extract video url for %s' % info['id'])
|
raise ExtractorError('Unable to extract video url for %s' % info['id'])
|
||||||
return info
|
return info
|
||||||
|
|
||||||
|
|
||||||
|
class BrightcoveNewIE(InfoExtractor):
|
||||||
|
IE_NAME = 'brightcove:new'
|
||||||
|
_VALID_URL = r'https?://players\.brightcove\.net/(?P<account_id>\d+)/(?P<player_id>[^/]+)_(?P<embed>[^/]+)/index\.html\?.*videoId=(?P<video_id>\d+)'
|
||||||
|
_TESTS = [{
|
||||||
|
'url': 'http://players.brightcove.net/929656772001/e41d32dc-ec74-459e-a845-6c69f7b724ea_default/index.html?videoId=4463358922001',
|
||||||
|
'md5': 'c8100925723840d4b0d243f7025703be',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '4463358922001',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Meet the man behind Popcorn Time',
|
||||||
|
'description': 'md5:eac376a4fe366edc70279bfb681aea16',
|
||||||
|
'duration': 165.768,
|
||||||
|
'timestamp': 1441391203,
|
||||||
|
'upload_date': '20150904',
|
||||||
|
'uploader_id': '929656772001',
|
||||||
|
'formats': 'mincount:22',
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
# with rtmp streams
|
||||||
|
'url': 'http://players.brightcove.net/4036320279001/5d112ed9-283f-485f-a7f9-33f42e8bc042_default/index.html?videoId=4279049078001',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '4279049078001',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Titansgrave: Chapter 0',
|
||||||
|
'description': 'Titansgrave: Chapter 0',
|
||||||
|
'duration': 1242.058,
|
||||||
|
'timestamp': 1433556729,
|
||||||
|
'upload_date': '20150606',
|
||||||
|
'uploader_id': '4036320279001',
|
||||||
|
'formats': 'mincount:41',
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
'skip_download': True,
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _extract_urls(webpage):
|
||||||
|
# Reference:
|
||||||
|
# 1. http://docs.brightcove.com/en/video-cloud/brightcove-player/guides/publish-video.html#setvideoiniframe
|
||||||
|
# 2. http://docs.brightcove.com/en/video-cloud/brightcove-player/guides/publish-video.html#setvideousingjavascript)
|
||||||
|
# 3. http://docs.brightcove.com/en/video-cloud/brightcove-player/guides/embed-in-page.html
|
||||||
|
|
||||||
|
entries = []
|
||||||
|
|
||||||
|
# Look for iframe embeds [1]
|
||||||
|
for _, url in re.findall(
|
||||||
|
r'<iframe[^>]+src=(["\'])((?:https?:)//players\.brightcove\.net/\d+/[^/]+/index\.html.+?)\1', webpage):
|
||||||
|
entries.append(url)
|
||||||
|
|
||||||
|
# Look for embed_in_page embeds [2]
|
||||||
|
for video_id, account_id, player_id, embed in re.findall(
|
||||||
|
# According to examples from [3] it's unclear whether video id
|
||||||
|
# may be optional and what to do when it is
|
||||||
|
r'''(?sx)
|
||||||
|
<video[^>]+
|
||||||
|
data-video-id=["\'](\d+)["\'][^>]*>.*?
|
||||||
|
</video>.*?
|
||||||
|
<script[^>]+
|
||||||
|
src=["\'](?:https?:)?//players\.brightcove\.net/
|
||||||
|
(\d+)/([\da-f-]+)_([^/]+)/index\.min\.js
|
||||||
|
''', webpage):
|
||||||
|
entries.append(
|
||||||
|
'http://players.brightcove.net/%s/%s_%s/index.html?videoId=%s'
|
||||||
|
% (account_id, player_id, embed, video_id))
|
||||||
|
|
||||||
|
return entries
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
account_id, player_id, embed, video_id = re.match(self._VALID_URL, url).groups()
|
||||||
|
|
||||||
|
webpage = self._download_webpage(
|
||||||
|
'http://players.brightcove.net/%s/%s_%s/index.min.js'
|
||||||
|
% (account_id, player_id, embed), video_id)
|
||||||
|
|
||||||
|
policy_key = None
|
||||||
|
|
||||||
|
catalog = self._search_regex(
|
||||||
|
r'catalog\(({.+?})\);', webpage, 'catalog', default=None)
|
||||||
|
if catalog:
|
||||||
|
catalog = self._parse_json(
|
||||||
|
js_to_json(catalog), video_id, fatal=False)
|
||||||
|
if catalog:
|
||||||
|
policy_key = catalog.get('policyKey')
|
||||||
|
|
||||||
|
if not policy_key:
|
||||||
|
policy_key = self._search_regex(
|
||||||
|
r'policyKey\s*:\s*(["\'])(?P<pk>.+?)\1',
|
||||||
|
webpage, 'policy key', group='pk')
|
||||||
|
|
||||||
|
req = sanitized_Request(
|
||||||
|
'https://edge.api.brightcove.com/playback/v1/accounts/%s/videos/%s'
|
||||||
|
% (account_id, video_id),
|
||||||
|
headers={'Accept': 'application/json;pk=%s' % policy_key})
|
||||||
|
json_data = self._download_json(req, video_id)
|
||||||
|
|
||||||
|
title = json_data['name']
|
||||||
|
|
||||||
|
formats = []
|
||||||
|
for source in json_data.get('sources', []):
|
||||||
|
source_type = source.get('type')
|
||||||
|
src = source.get('src')
|
||||||
|
if source_type == 'application/x-mpegURL':
|
||||||
|
if not src:
|
||||||
|
continue
|
||||||
|
m3u8_formats = self._extract_m3u8_formats(
|
||||||
|
src, video_id, 'mp4', entry_protocol='m3u8_native',
|
||||||
|
m3u8_id='hls', fatal=False)
|
||||||
|
if m3u8_formats:
|
||||||
|
formats.extend(m3u8_formats)
|
||||||
|
else:
|
||||||
|
streaming_src = source.get('streaming_src')
|
||||||
|
stream_name, app_name = source.get('stream_name'), source.get('app_name')
|
||||||
|
if not src and not streaming_src and (not stream_name or not app_name):
|
||||||
|
continue
|
||||||
|
tbr = float_or_none(source.get('avg_bitrate'), 1000)
|
||||||
|
height = int_or_none(source.get('height'))
|
||||||
|
f = {
|
||||||
|
'tbr': tbr,
|
||||||
|
'width': int_or_none(source.get('width')),
|
||||||
|
'height': height,
|
||||||
|
'filesize': int_or_none(source.get('size')),
|
||||||
|
'container': source.get('container'),
|
||||||
|
'vcodec': source.get('codec'),
|
||||||
|
'ext': source.get('container').lower(),
|
||||||
|
}
|
||||||
|
|
||||||
|
def build_format_id(kind):
|
||||||
|
format_id = kind
|
||||||
|
if tbr:
|
||||||
|
format_id += '-%dk' % int(tbr)
|
||||||
|
if height:
|
||||||
|
format_id += '-%dp' % height
|
||||||
|
return format_id
|
||||||
|
|
||||||
|
if src or streaming_src:
|
||||||
|
f.update({
|
||||||
|
'url': src or streaming_src,
|
||||||
|
'format_id': build_format_id('http' if src else 'http-streaming'),
|
||||||
|
'preference': 2 if src else 1,
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
f.update({
|
||||||
|
'url': app_name,
|
||||||
|
'play_path': stream_name,
|
||||||
|
'format_id': build_format_id('rtmp'),
|
||||||
|
})
|
||||||
|
formats.append(f)
|
||||||
|
self._sort_formats(formats)
|
||||||
|
|
||||||
|
description = json_data.get('description')
|
||||||
|
thumbnail = json_data.get('thumbnail')
|
||||||
|
timestamp = parse_iso8601(json_data.get('published_at'))
|
||||||
|
duration = float_or_none(json_data.get('duration'), 1000)
|
||||||
|
tags = json_data.get('tags', [])
|
||||||
|
|
||||||
|
return {
|
||||||
|
'id': video_id,
|
||||||
|
'title': title,
|
||||||
|
'description': description,
|
||||||
|
'thumbnail': thumbnail,
|
||||||
|
'duration': duration,
|
||||||
|
'timestamp': timestamp,
|
||||||
|
'uploader_id': account_id,
|
||||||
|
'formats': formats,
|
||||||
|
'tags': tags,
|
||||||
|
}
|
||||||
|
@@ -14,9 +14,10 @@ class BYUtvIE(InfoExtractor):
|
|||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'studio-c-season-5-episode-5',
|
'id': 'studio-c-season-5-episode-5',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'description': 'md5:5438d33774b6bdc662f9485a340401cc',
|
'description': 'md5:e07269172baff037f8e8bf9956bc9747',
|
||||||
'title': 'Season 5 Episode 5',
|
'title': 'Season 5 Episode 5',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$'
|
'thumbnail': 're:^https?://.*\.jpg$',
|
||||||
|
'duration': 1486.486,
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
|
@@ -1,6 +1,10 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
|
from ..utils import (
|
||||||
|
sanitized_Request,
|
||||||
|
smuggle_url,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class CBSIE(InfoExtractor):
|
class CBSIE(InfoExtractor):
|
||||||
@@ -46,13 +50,19 @@ class CBSIE(InfoExtractor):
|
|||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
display_id = self._match_id(url)
|
display_id = self._match_id(url)
|
||||||
webpage = self._download_webpage(url, display_id)
|
request = sanitized_Request(url)
|
||||||
|
# Android UA is served with higher quality (720p) streams (see
|
||||||
|
# https://github.com/rg3/youtube-dl/issues/7490)
|
||||||
|
request.add_header('User-Agent', 'Mozilla/5.0 (Linux; Android 4.4; Nexus 5)')
|
||||||
|
webpage = self._download_webpage(request, display_id)
|
||||||
real_id = self._search_regex(
|
real_id = self._search_regex(
|
||||||
[r"video\.settings\.pid\s*=\s*'([^']+)';", r"cbsplayer\.pid\s*=\s*'([^']+)';"],
|
[r"video\.settings\.pid\s*=\s*'([^']+)';", r"cbsplayer\.pid\s*=\s*'([^']+)';"],
|
||||||
webpage, 'real video ID')
|
webpage, 'real video ID')
|
||||||
return {
|
return {
|
||||||
'_type': 'url_transparent',
|
'_type': 'url_transparent',
|
||||||
'ie_key': 'ThePlatform',
|
'ie_key': 'ThePlatform',
|
||||||
'url': 'theplatform:%s' % real_id,
|
'url': smuggle_url(
|
||||||
|
'http://link.theplatform.com/s/dJ5BDC/%s?mbr=true&manifest=m3u' % real_id,
|
||||||
|
{'force_smil_url': True}),
|
||||||
'display_id': display_id,
|
'display_id': display_id,
|
||||||
}
|
}
|
||||||
|
@@ -67,9 +67,12 @@ class CBSNewsIE(InfoExtractor):
|
|||||||
'format_id': format_id,
|
'format_id': format_id,
|
||||||
}
|
}
|
||||||
if uri.startswith('rtmp'):
|
if uri.startswith('rtmp'):
|
||||||
|
play_path = re.sub(
|
||||||
|
r'{slistFilePath}', '',
|
||||||
|
uri.split('<break>')[-1].split('{break}')[-1])
|
||||||
fmt.update({
|
fmt.update({
|
||||||
'app': 'ondemand?auth=cbs',
|
'app': 'ondemand?auth=cbs',
|
||||||
'play_path': 'mp4:' + uri.split('<break>')[-1],
|
'play_path': 'mp4:' + play_path,
|
||||||
'player_url': 'http://www.cbsnews.com/[[IMPORT]]/vidtech.cbsinteractive.com/player/3_3_0/CBSI_PLAYER_HD.swf',
|
'player_url': 'http://www.cbsnews.com/[[IMPORT]]/vidtech.cbsinteractive.com/player/3_3_0/CBSI_PLAYER_HD.swf',
|
||||||
'page_url': 'http://www.cbsnews.com',
|
'page_url': 'http://www.cbsnews.com',
|
||||||
'ext': 'flv',
|
'ext': 'flv',
|
||||||
|
@@ -5,7 +5,6 @@ import re
|
|||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
from ..compat import (
|
||||||
compat_urllib_request,
|
|
||||||
compat_urllib_parse,
|
compat_urllib_parse,
|
||||||
compat_urllib_parse_unquote,
|
compat_urllib_parse_unquote,
|
||||||
compat_urllib_parse_urlparse,
|
compat_urllib_parse_urlparse,
|
||||||
@@ -13,6 +12,7 @@ from ..compat import (
|
|||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
float_or_none,
|
float_or_none,
|
||||||
|
sanitized_Request,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ class CeskaTelevizeIE(InfoExtractor):
|
|||||||
'requestSource': 'iVysilani',
|
'requestSource': 'iVysilani',
|
||||||
}
|
}
|
||||||
|
|
||||||
req = compat_urllib_request.Request(
|
req = sanitized_Request(
|
||||||
'http://www.ceskatelevize.cz/ivysilani/ajax/get-client-playlist',
|
'http://www.ceskatelevize.cz/ivysilani/ajax/get-client-playlist',
|
||||||
data=compat_urllib_parse.urlencode(data))
|
data=compat_urllib_parse.urlencode(data))
|
||||||
|
|
||||||
@@ -115,7 +115,7 @@ class CeskaTelevizeIE(InfoExtractor):
|
|||||||
if playlist_url == 'error_region':
|
if playlist_url == 'error_region':
|
||||||
raise ExtractorError(NOT_AVAILABLE_STRING, expected=True)
|
raise ExtractorError(NOT_AVAILABLE_STRING, expected=True)
|
||||||
|
|
||||||
req = compat_urllib_request.Request(compat_urllib_parse_unquote(playlist_url))
|
req = sanitized_Request(compat_urllib_parse_unquote(playlist_url))
|
||||||
req.add_header('Referer', url)
|
req.add_header('Referer', url)
|
||||||
|
|
||||||
playlist_title = self._og_search_title(webpage)
|
playlist_title = self._og_search_title(webpage)
|
||||||
|
@@ -1,14 +1,9 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import re
|
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
determine_ext,
|
|
||||||
int_or_none,
|
int_or_none,
|
||||||
js_to_json,
|
unified_strdate,
|
||||||
parse_iso8601,
|
|
||||||
remove_end,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -21,48 +16,47 @@ class ClipfishIE(InfoExtractor):
|
|||||||
'id': '3966754',
|
'id': '3966754',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'FIFA 14 - E3 2013 Trailer',
|
'title': 'FIFA 14 - E3 2013 Trailer',
|
||||||
'timestamp': 1370938118,
|
'description': 'Video zu FIFA 14: E3 2013 Trailer',
|
||||||
'upload_date': '20130611',
|
'upload_date': '20130611',
|
||||||
'duration': 82,
|
'duration': 82,
|
||||||
|
'view_count': int,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
|
|
||||||
webpage = self._download_webpage(url, video_id)
|
video_info = self._download_json(
|
||||||
|
'http://www.clipfish.de/devapi/id/%s?format=json&apikey=hbbtv' % video_id,
|
||||||
video_info = self._parse_json(
|
video_id)['items'][0]
|
||||||
js_to_json(self._html_search_regex(
|
|
||||||
'(?s)videoObject\s*=\s*({.+?});', webpage, 'video object')),
|
|
||||||
video_id)
|
|
||||||
|
|
||||||
formats = []
|
formats = []
|
||||||
for video_url in re.findall(r'var\s+videourl\s*=\s*"([^"]+)"', webpage):
|
|
||||||
ext = determine_ext(video_url)
|
|
||||||
if ext == 'm3u8':
|
|
||||||
formats.append({
|
|
||||||
'url': video_url.replace('de.hls.fra.clipfish.de', 'hls.fra.clipfish.de'),
|
|
||||||
'ext': 'mp4',
|
|
||||||
'format_id': 'hls',
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
formats.append({
|
|
||||||
'url': video_url,
|
|
||||||
'format_id': ext,
|
|
||||||
})
|
|
||||||
self._sort_formats(formats)
|
|
||||||
|
|
||||||
title = remove_end(self._og_search_title(webpage), ' - Video')
|
m3u8_url = video_info.get('media_videourl_hls')
|
||||||
thumbnail = self._og_search_thumbnail(webpage)
|
if m3u8_url:
|
||||||
duration = int_or_none(video_info.get('length'))
|
formats.append({
|
||||||
timestamp = parse_iso8601(self._html_search_meta('uploadDate', webpage, 'upload date'))
|
'url': m3u8_url.replace('de.hls.fra.clipfish.de', 'hls.fra.clipfish.de'),
|
||||||
|
'ext': 'mp4',
|
||||||
|
'format_id': 'hls',
|
||||||
|
})
|
||||||
|
|
||||||
|
mp4_url = video_info.get('media_videourl')
|
||||||
|
if mp4_url:
|
||||||
|
formats.append({
|
||||||
|
'url': mp4_url,
|
||||||
|
'format_id': 'mp4',
|
||||||
|
'width': int_or_none(video_info.get('width')),
|
||||||
|
'height': int_or_none(video_info.get('height')),
|
||||||
|
'tbr': int_or_none(video_info.get('bitrate')),
|
||||||
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
'title': title,
|
'title': video_info['title'],
|
||||||
|
'description': video_info.get('descr'),
|
||||||
'formats': formats,
|
'formats': formats,
|
||||||
'thumbnail': thumbnail,
|
'thumbnail': video_info.get('media_content_thumbnail_large') or video_info.get('media_thumbnail'),
|
||||||
'duration': duration,
|
'duration': int_or_none(video_info.get('media_length')),
|
||||||
'timestamp': timestamp,
|
'upload_date': unified_strdate(video_info.get('pubDate')),
|
||||||
|
'view_count': int_or_none(video_info.get('media_views'))
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..utils import determine_ext
|
from ..utils import int_or_none
|
||||||
|
|
||||||
|
|
||||||
_translation_table = {
|
_translation_table = {
|
||||||
@@ -42,31 +42,26 @@ class CliphunterIE(InfoExtractor):
|
|||||||
video_title = self._search_regex(
|
video_title = self._search_regex(
|
||||||
r'mediaTitle = "([^"]+)"', webpage, 'title')
|
r'mediaTitle = "([^"]+)"', webpage, 'title')
|
||||||
|
|
||||||
fmts = {}
|
gexo_files = self._parse_json(
|
||||||
for fmt in ('mp4', 'flv'):
|
self._search_regex(
|
||||||
fmt_list = self._parse_json(self._search_regex(
|
r'var\s+gexoFiles\s*=\s*({.+?});', webpage, 'gexo files'),
|
||||||
r'var %sjson\s*=\s*(\[.*?\]);' % fmt, webpage, '%s formats' % fmt), video_id)
|
video_id)
|
||||||
for f in fmt_list:
|
|
||||||
fmts[f['fname']] = _decode(f['sUrl'])
|
|
||||||
|
|
||||||
qualities = self._parse_json(self._search_regex(
|
|
||||||
r'var player_btns\s*=\s*(.*?);\n', webpage, 'quality info'), video_id)
|
|
||||||
|
|
||||||
formats = []
|
formats = []
|
||||||
for fname, url in fmts.items():
|
for format_id, f in gexo_files.items():
|
||||||
f = {
|
video_url = f.get('url')
|
||||||
'url': url,
|
if not video_url:
|
||||||
}
|
continue
|
||||||
if fname in qualities:
|
fmt = f.get('fmt')
|
||||||
qual = qualities[fname]
|
height = f.get('h')
|
||||||
f.update({
|
format_id = '%s_%sp' % (fmt, height) if fmt and height else format_id
|
||||||
'format_id': '%s_%sp' % (determine_ext(url), qual['h']),
|
formats.append({
|
||||||
'width': qual['w'],
|
'url': _decode(video_url),
|
||||||
'height': qual['h'],
|
'format_id': format_id,
|
||||||
'tbr': qual['br'],
|
'width': int_or_none(f.get('w')),
|
||||||
})
|
'height': int_or_none(height),
|
||||||
formats.append(f)
|
'tbr': int_or_none(f.get('br')),
|
||||||
|
})
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
|
||||||
thumbnail = self._search_regex(
|
thumbnail = self._search_regex(
|
||||||
|
@@ -3,10 +3,10 @@ from __future__ import unicode_literals
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import compat_urllib_request
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
float_or_none,
|
float_or_none,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
|
sanitized_Request,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ class CollegeRamaIE(InfoExtractor):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
request = compat_urllib_request.Request(
|
request = sanitized_Request(
|
||||||
'http://collegerama.tudelft.nl/Mediasite/PlayerService/PlayerService.svc/json/GetPlayerOptions',
|
'http://collegerama.tudelft.nl/Mediasite/PlayerService/PlayerService.svc/json/GetPlayerOptions',
|
||||||
json.dumps(player_options_request))
|
json.dumps(player_options_request))
|
||||||
request.add_header('Content-Type', 'application/json')
|
request.add_header('Content-Type', 'application/json')
|
||||||
|
@@ -19,7 +19,6 @@ from ..compat import (
|
|||||||
compat_urllib_error,
|
compat_urllib_error,
|
||||||
compat_urllib_parse,
|
compat_urllib_parse,
|
||||||
compat_urllib_parse_urlparse,
|
compat_urllib_parse_urlparse,
|
||||||
compat_urllib_request,
|
|
||||||
compat_urlparse,
|
compat_urlparse,
|
||||||
compat_str,
|
compat_str,
|
||||||
compat_etree_fromstring,
|
compat_etree_fromstring,
|
||||||
@@ -37,6 +36,7 @@ from ..utils import (
|
|||||||
int_or_none,
|
int_or_none,
|
||||||
RegexNotFoundError,
|
RegexNotFoundError,
|
||||||
sanitize_filename,
|
sanitize_filename,
|
||||||
|
sanitized_Request,
|
||||||
unescapeHTML,
|
unescapeHTML,
|
||||||
unified_strdate,
|
unified_strdate,
|
||||||
url_basename,
|
url_basename,
|
||||||
@@ -167,7 +167,7 @@ class InfoExtractor(object):
|
|||||||
"ext" will be calculated from URL if missing
|
"ext" will be calculated from URL if missing
|
||||||
automatic_captions: Like 'subtitles', used by the YoutubeIE for
|
automatic_captions: Like 'subtitles', used by the YoutubeIE for
|
||||||
automatically generated captions
|
automatically generated captions
|
||||||
duration: Length of the video in seconds, as an integer.
|
duration: Length of the video in seconds, as an integer or float.
|
||||||
view_count: How many users have watched the video on the platform.
|
view_count: How many users have watched the video on the platform.
|
||||||
like_count: Number of positive ratings of the video
|
like_count: Number of positive ratings of the video
|
||||||
dislike_count: Number of negative ratings of the video
|
dislike_count: Number of negative ratings of the video
|
||||||
@@ -891,6 +891,11 @@ class InfoExtractor(object):
|
|||||||
if not media_nodes:
|
if not media_nodes:
|
||||||
manifest_version = '2.0'
|
manifest_version = '2.0'
|
||||||
media_nodes = manifest.findall('{http://ns.adobe.com/f4m/2.0}media')
|
media_nodes = manifest.findall('{http://ns.adobe.com/f4m/2.0}media')
|
||||||
|
base_url = xpath_text(
|
||||||
|
manifest, ['{http://ns.adobe.com/f4m/1.0}baseURL', '{http://ns.adobe.com/f4m/2.0}baseURL'],
|
||||||
|
'base URL', default=None)
|
||||||
|
if base_url:
|
||||||
|
base_url = base_url.strip()
|
||||||
for i, media_el in enumerate(media_nodes):
|
for i, media_el in enumerate(media_nodes):
|
||||||
if manifest_version == '2.0':
|
if manifest_version == '2.0':
|
||||||
media_url = media_el.attrib.get('href') or media_el.attrib.get('url')
|
media_url = media_el.attrib.get('href') or media_el.attrib.get('url')
|
||||||
@@ -898,7 +903,7 @@ class InfoExtractor(object):
|
|||||||
continue
|
continue
|
||||||
manifest_url = (
|
manifest_url = (
|
||||||
media_url if media_url.startswith('http://') or media_url.startswith('https://')
|
media_url if media_url.startswith('http://') or media_url.startswith('https://')
|
||||||
else ('/'.join(manifest_url.split('/')[:-1]) + '/' + media_url))
|
else ((base_url or '/'.join(manifest_url.split('/')[:-1])) + '/' + media_url))
|
||||||
# If media_url is itself a f4m manifest do the recursive extraction
|
# If media_url is itself a f4m manifest do the recursive extraction
|
||||||
# since bitrates in parent manifest (this one) and media_url manifest
|
# since bitrates in parent manifest (this one) and media_url manifest
|
||||||
# may differ leading to inability to resolve the format by requested
|
# may differ leading to inability to resolve the format by requested
|
||||||
@@ -1280,7 +1285,7 @@ class InfoExtractor(object):
|
|||||||
|
|
||||||
def _get_cookies(self, url):
|
def _get_cookies(self, url):
|
||||||
""" Return a compat_cookies.SimpleCookie with the cookies for the url """
|
""" Return a compat_cookies.SimpleCookie with the cookies for the url """
|
||||||
req = compat_urllib_request.Request(url)
|
req = sanitized_Request(url)
|
||||||
self._downloader.cookiejar.add_cookie_header(req)
|
self._downloader.cookiejar.add_cookie_header(req)
|
||||||
return compat_cookies.SimpleCookie(req.get_header('Cookie'))
|
return compat_cookies.SimpleCookie(req.get_header('Cookie'))
|
||||||
|
|
||||||
|
@@ -23,6 +23,7 @@ from ..utils import (
|
|||||||
int_or_none,
|
int_or_none,
|
||||||
lowercase_escape,
|
lowercase_escape,
|
||||||
remove_end,
|
remove_end,
|
||||||
|
sanitized_Request,
|
||||||
unified_strdate,
|
unified_strdate,
|
||||||
urlencode_postdata,
|
urlencode_postdata,
|
||||||
xpath_text,
|
xpath_text,
|
||||||
@@ -46,7 +47,7 @@ class CrunchyrollBaseIE(InfoExtractor):
|
|||||||
'name': username,
|
'name': username,
|
||||||
'password': password,
|
'password': password,
|
||||||
})
|
})
|
||||||
login_request = compat_urllib_request.Request(login_url, data)
|
login_request = sanitized_Request(login_url, data)
|
||||||
login_request.add_header('Content-Type', 'application/x-www-form-urlencoded')
|
login_request.add_header('Content-Type', 'application/x-www-form-urlencoded')
|
||||||
self._download_webpage(login_request, None, False, 'Wrong login info')
|
self._download_webpage(login_request, None, False, 'Wrong login info')
|
||||||
|
|
||||||
@@ -55,7 +56,7 @@ class CrunchyrollBaseIE(InfoExtractor):
|
|||||||
|
|
||||||
def _download_webpage(self, url_or_request, video_id, note=None, errnote=None, fatal=True, tries=1, timeout=5, encoding=None):
|
def _download_webpage(self, url_or_request, video_id, note=None, errnote=None, fatal=True, tries=1, timeout=5, encoding=None):
|
||||||
request = (url_or_request if isinstance(url_or_request, compat_urllib_request.Request)
|
request = (url_or_request if isinstance(url_or_request, compat_urllib_request.Request)
|
||||||
else compat_urllib_request.Request(url_or_request))
|
else sanitized_Request(url_or_request))
|
||||||
# Accept-Language must be set explicitly to accept any language to avoid issues
|
# Accept-Language must be set explicitly to accept any language to avoid issues
|
||||||
# similar to https://github.com/rg3/youtube-dl/issues/6797.
|
# similar to https://github.com/rg3/youtube-dl/issues/6797.
|
||||||
# Along with IP address Crunchyroll uses Accept-Language to guess whether georestriction
|
# Along with IP address Crunchyroll uses Accept-Language to guess whether georestriction
|
||||||
@@ -307,7 +308,7 @@ Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
|
|||||||
'video_uploader', fatal=False)
|
'video_uploader', fatal=False)
|
||||||
|
|
||||||
playerdata_url = compat_urllib_parse_unquote(self._html_search_regex(r'"config_url":"([^"]+)', webpage, 'playerdata_url'))
|
playerdata_url = compat_urllib_parse_unquote(self._html_search_regex(r'"config_url":"([^"]+)', webpage, 'playerdata_url'))
|
||||||
playerdata_req = compat_urllib_request.Request(playerdata_url)
|
playerdata_req = sanitized_Request(playerdata_url)
|
||||||
playerdata_req.data = compat_urllib_parse.urlencode({'current_page': webpage_url})
|
playerdata_req.data = compat_urllib_parse.urlencode({'current_page': webpage_url})
|
||||||
playerdata_req.add_header('Content-Type', 'application/x-www-form-urlencoded')
|
playerdata_req.add_header('Content-Type', 'application/x-www-form-urlencoded')
|
||||||
playerdata = self._download_webpage(playerdata_req, video_id, note='Downloading media info')
|
playerdata = self._download_webpage(playerdata_req, video_id, note='Downloading media info')
|
||||||
@@ -319,7 +320,7 @@ Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
|
|||||||
for fmt in re.findall(r'showmedia\.([0-9]{3,4})p', webpage):
|
for fmt in re.findall(r'showmedia\.([0-9]{3,4})p', webpage):
|
||||||
stream_quality, stream_format = self._FORMAT_IDS[fmt]
|
stream_quality, stream_format = self._FORMAT_IDS[fmt]
|
||||||
video_format = fmt + 'p'
|
video_format = fmt + 'p'
|
||||||
streamdata_req = compat_urllib_request.Request(
|
streamdata_req = sanitized_Request(
|
||||||
'http://www.crunchyroll.com/xml/?req=RpcApiVideoPlayer_GetStandardConfig&media_id=%s&video_format=%s&video_quality=%s'
|
'http://www.crunchyroll.com/xml/?req=RpcApiVideoPlayer_GetStandardConfig&media_id=%s&video_format=%s&video_quality=%s'
|
||||||
% (stream_id, stream_format, stream_quality),
|
% (stream_id, stream_format, stream_quality),
|
||||||
compat_urllib_parse.urlencode({'current_page': url}).encode('utf-8'))
|
compat_urllib_parse.urlencode({'current_page': url}).encode('utf-8'))
|
||||||
|
@@ -9,6 +9,7 @@ from ..utils import (
|
|||||||
find_xpath_attr,
|
find_xpath_attr,
|
||||||
smuggle_url,
|
smuggle_url,
|
||||||
determine_ext,
|
determine_ext,
|
||||||
|
ExtractorError,
|
||||||
)
|
)
|
||||||
from .senateisvp import SenateISVPIE
|
from .senateisvp import SenateISVPIE
|
||||||
|
|
||||||
@@ -18,33 +19,32 @@ class CSpanIE(InfoExtractor):
|
|||||||
IE_DESC = 'C-SPAN'
|
IE_DESC = 'C-SPAN'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://www.c-span.org/video/?313572-1/HolderonV',
|
'url': 'http://www.c-span.org/video/?313572-1/HolderonV',
|
||||||
'md5': '8e44ce11f0f725527daccc453f553eb0',
|
'md5': '94b29a4f131ff03d23471dd6f60b6a1d',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '315139',
|
'id': '315139',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Attorney General Eric Holder on Voting Rights Act Decision',
|
'title': 'Attorney General Eric Holder on Voting Rights Act Decision',
|
||||||
'description': 'Attorney General Eric Holder spoke to reporters following the Supreme Court decision in Shelby County v. Holder in which the court ruled that the preclearance provisions of the Voting Rights Act could not be enforced until Congress established new guidelines for review.',
|
'description': 'Attorney General Eric Holder speaks to reporters following the Supreme Court decision in [Shelby County v. Holder], in which the court ruled that the preclearance provisions of the Voting Rights Act could not be enforced.',
|
||||||
},
|
},
|
||||||
'skip': 'Regularly fails on travis, for unknown reasons',
|
'skip': 'Regularly fails on travis, for unknown reasons',
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://www.c-span.org/video/?c4486943/cspan-international-health-care-models',
|
'url': 'http://www.c-span.org/video/?c4486943/cspan-international-health-care-models',
|
||||||
# For whatever reason, the served video alternates between
|
'md5': '8e5fbfabe6ad0f89f3012a7943c1287b',
|
||||||
# two different ones
|
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '340723',
|
'id': 'c4486943',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'International Health Care Models',
|
'title': 'CSPAN - International Health Care Models',
|
||||||
'description': 'md5:7a985a2d595dba00af3d9c9f0783c967',
|
'description': 'md5:7a985a2d595dba00af3d9c9f0783c967',
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://www.c-span.org/video/?318608-1/gm-ignition-switch-recall',
|
'url': 'http://www.c-span.org/video/?318608-1/gm-ignition-switch-recall',
|
||||||
'md5': '446562a736c6bf97118e389433ed88d4',
|
'md5': '2ae5051559169baadba13fc35345ae74',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '342759',
|
'id': '342759',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'General Motors Ignition Switch Recall',
|
'title': 'General Motors Ignition Switch Recall',
|
||||||
'duration': 14848,
|
'duration': 14848,
|
||||||
'description': 'md5:70c7c3b8fa63fa60d42772440596034c'
|
'description': 'md5:118081aedd24bf1d3b68b3803344e7f3'
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
# Video from senate.gov
|
# Video from senate.gov
|
||||||
@@ -57,67 +57,77 @@ class CSpanIE(InfoExtractor):
|
|||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
mobj = re.match(self._VALID_URL, url)
|
video_id = self._match_id(url)
|
||||||
page_id = mobj.group('id')
|
webpage = self._download_webpage(url, video_id)
|
||||||
webpage = self._download_webpage(url, page_id)
|
matches = re.search(r'data-(prog|clip)id=\'([0-9]+)\'', webpage)
|
||||||
video_id = self._search_regex(r'progid=\'?([0-9]+)\'?>', webpage, 'video id')
|
if matches:
|
||||||
|
video_type, video_id = matches.groups()
|
||||||
|
if video_type == 'prog':
|
||||||
|
video_type = 'program'
|
||||||
|
else:
|
||||||
|
senate_isvp_url = SenateISVPIE._search_iframe_url(webpage)
|
||||||
|
if senate_isvp_url:
|
||||||
|
title = self._og_search_title(webpage)
|
||||||
|
surl = smuggle_url(senate_isvp_url, {'force_title': title})
|
||||||
|
return self.url_result(surl, 'SenateISVP', video_id, title)
|
||||||
|
|
||||||
description = self._html_search_regex(
|
def get_text_attr(d, attr):
|
||||||
[
|
return d.get(attr, {}).get('#text')
|
||||||
# The full description
|
|
||||||
r'<div class=\'expandable\'>(.*?)<a href=\'#\'',
|
|
||||||
# If the description is small enough the other div is not
|
|
||||||
# present, otherwise this is a stripped version
|
|
||||||
r'<p class=\'initial\'>(.*?)</p>'
|
|
||||||
],
|
|
||||||
webpage, 'description', flags=re.DOTALL, default=None)
|
|
||||||
|
|
||||||
info_url = 'http://c-spanvideo.org/videoLibrary/assets/player/ajax-player.php?os=android&html5=program&id=' + video_id
|
data = self._download_json(
|
||||||
data = self._download_json(info_url, video_id)
|
'http://www.c-span.org/assets/player/ajax-player.php?os=android&html5=%s&id=%s' % (video_type, video_id),
|
||||||
|
video_id)['video']
|
||||||
|
if data['@status'] != 'Success':
|
||||||
|
raise ExtractorError('%s said: %s' % (self.IE_NAME, get_text_attr(data, 'error')), expected=True)
|
||||||
|
|
||||||
doc = self._download_xml(
|
doc = self._download_xml(
|
||||||
'http://www.c-span.org/common/services/flashXml.php?programid=' + video_id,
|
'http://www.c-span.org/common/services/flashXml.php?%sid=%s' % (video_type, video_id),
|
||||||
video_id)
|
video_id)
|
||||||
|
|
||||||
|
description = self._html_search_meta('description', webpage)
|
||||||
|
|
||||||
title = find_xpath_attr(doc, './/string', 'name', 'title').text
|
title = find_xpath_attr(doc, './/string', 'name', 'title').text
|
||||||
thumbnail = find_xpath_attr(doc, './/string', 'name', 'poster').text
|
thumbnail = find_xpath_attr(doc, './/string', 'name', 'poster').text
|
||||||
|
|
||||||
senate_isvp_url = SenateISVPIE._search_iframe_url(webpage)
|
files = data['files']
|
||||||
if senate_isvp_url:
|
capfile = get_text_attr(data, 'capfile')
|
||||||
surl = smuggle_url(senate_isvp_url, {'force_title': title})
|
|
||||||
return self.url_result(surl, 'SenateISVP', video_id, title)
|
|
||||||
|
|
||||||
files = data['video']['files']
|
entries = []
|
||||||
try:
|
for partnum, f in enumerate(files):
|
||||||
capfile = data['video']['capfile']['#text']
|
formats = []
|
||||||
except KeyError:
|
for quality in f['qualities']:
|
||||||
capfile = None
|
formats.append({
|
||||||
|
'format_id': '%s-%sp' % (get_text_attr(quality, 'bitrate'), get_text_attr(quality, 'height')),
|
||||||
entries = [{
|
'url': unescapeHTML(get_text_attr(quality, 'file')),
|
||||||
'id': '%s_%d' % (video_id, partnum + 1),
|
'height': int_or_none(get_text_attr(quality, 'height')),
|
||||||
'title': (
|
'tbr': int_or_none(get_text_attr(quality, 'bitrate')),
|
||||||
title if len(files) == 1 else
|
})
|
||||||
'%s part %d' % (title, partnum + 1)),
|
self._sort_formats(formats)
|
||||||
'url': unescapeHTML(f['path']['#text']),
|
entries.append({
|
||||||
'description': description,
|
'id': '%s_%d' % (video_id, partnum + 1),
|
||||||
'thumbnail': thumbnail,
|
'title': (
|
||||||
'duration': int_or_none(f.get('length', {}).get('#text')),
|
title if len(files) == 1 else
|
||||||
'subtitles': {
|
'%s part %d' % (title, partnum + 1)),
|
||||||
'en': [{
|
'formats': formats,
|
||||||
'url': capfile,
|
'description': description,
|
||||||
'ext': determine_ext(capfile, 'dfxp')
|
'thumbnail': thumbnail,
|
||||||
}],
|
'duration': int_or_none(get_text_attr(f, 'length')),
|
||||||
} if capfile else None,
|
'subtitles': {
|
||||||
} for partnum, f in enumerate(files)]
|
'en': [{
|
||||||
|
'url': capfile,
|
||||||
|
'ext': determine_ext(capfile, 'dfxp')
|
||||||
|
}],
|
||||||
|
} if capfile else None,
|
||||||
|
})
|
||||||
|
|
||||||
if len(entries) == 1:
|
if len(entries) == 1:
|
||||||
entry = dict(entries[0])
|
entry = dict(entries[0])
|
||||||
entry['id'] = video_id
|
entry['id'] = 'c' + video_id if video_type == 'clip' else video_id
|
||||||
return entry
|
return entry
|
||||||
else:
|
else:
|
||||||
return {
|
return {
|
||||||
'_type': 'playlist',
|
'_type': 'playlist',
|
||||||
'entries': entries,
|
'entries': entries,
|
||||||
'title': title,
|
'title': title,
|
||||||
'id': video_id,
|
'id': 'c' + video_id if video_type == 'clip' else video_id,
|
||||||
}
|
}
|
||||||
|
@@ -7,15 +7,13 @@ import itertools
|
|||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
|
|
||||||
from ..compat import (
|
from ..compat import compat_str
|
||||||
compat_str,
|
|
||||||
compat_urllib_request,
|
|
||||||
)
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
determine_ext,
|
determine_ext,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
parse_iso8601,
|
parse_iso8601,
|
||||||
|
sanitized_Request,
|
||||||
str_to_int,
|
str_to_int,
|
||||||
unescapeHTML,
|
unescapeHTML,
|
||||||
)
|
)
|
||||||
@@ -25,7 +23,7 @@ class DailymotionBaseInfoExtractor(InfoExtractor):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def _build_request(url):
|
def _build_request(url):
|
||||||
"""Build a request with the family filter disabled"""
|
"""Build a request with the family filter disabled"""
|
||||||
request = compat_urllib_request.Request(url)
|
request = sanitized_Request(url)
|
||||||
request.add_header('Cookie', 'family_filter=off; ff=off')
|
request.add_header('Cookie', 'family_filter=off; ff=off')
|
||||||
return request
|
return request
|
||||||
|
|
||||||
@@ -101,6 +99,11 @@ class DailymotionIE(DailymotionBaseInfoExtractor):
|
|||||||
{
|
{
|
||||||
'url': 'http://www.dailymotion.com/video/xhza0o',
|
'url': 'http://www.dailymotion.com/video/xhza0o',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
},
|
||||||
|
# with subtitles
|
||||||
|
{
|
||||||
|
'url': 'http://www.dailymotion.com/video/x20su5f_the-power-of-nightmares-1-the-rise-of-the-politics-of-fear-bbc-2004_news',
|
||||||
|
'only_matching': True,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -124,7 +127,9 @@ class DailymotionIE(DailymotionBaseInfoExtractor):
|
|||||||
webpage, 'comment count', fatal=False))
|
webpage, 'comment count', fatal=False))
|
||||||
|
|
||||||
player_v5 = self._search_regex(
|
player_v5 = self._search_regex(
|
||||||
[r'buildPlayer\(({.+?})\);', r'playerV5\s*=\s*dmp\.create\([^,]+?,\s*({.+?})\);'],
|
[r'buildPlayer\(({.+?})\);\n', # See https://github.com/rg3/youtube-dl/issues/7826
|
||||||
|
r'playerV5\s*=\s*dmp\.create\([^,]+?,\s*({.+?})\);',
|
||||||
|
r'buildPlayer\(({.+?})\);'],
|
||||||
webpage, 'player v5', default=None)
|
webpage, 'player v5', default=None)
|
||||||
if player_v5:
|
if player_v5:
|
||||||
player = self._parse_json(player_v5, video_id)
|
player = self._parse_json(player_v5, video_id)
|
||||||
@@ -174,11 +179,13 @@ class DailymotionIE(DailymotionBaseInfoExtractor):
|
|||||||
uploader_id = metadata.get('owner', {}).get('id')
|
uploader_id = metadata.get('owner', {}).get('id')
|
||||||
|
|
||||||
subtitles = {}
|
subtitles = {}
|
||||||
for subtitle_lang, subtitle in metadata.get('subtitles', {}).get('data', {}).items():
|
subtitles_data = metadata.get('subtitles', {}).get('data', {})
|
||||||
subtitles[subtitle_lang] = [{
|
if subtitles_data and isinstance(subtitles_data, dict):
|
||||||
'ext': determine_ext(subtitle_url),
|
for subtitle_lang, subtitle in subtitles_data.items():
|
||||||
'url': subtitle_url,
|
subtitles[subtitle_lang] = [{
|
||||||
} for subtitle_url in subtitle.get('urls', [])]
|
'ext': determine_ext(subtitle_url),
|
||||||
|
'url': subtitle_url,
|
||||||
|
} for subtitle_url in subtitle.get('urls', [])]
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
|
@@ -13,8 +13,8 @@ from ..utils import (
|
|||||||
|
|
||||||
|
|
||||||
class DBTVIE(InfoExtractor):
|
class DBTVIE(InfoExtractor):
|
||||||
_VALID_URL = r'http://dbtv\.no/(?P<id>[0-9]+)#(?P<display_id>.+)'
|
_VALID_URL = r'https?://(?:www\.)?dbtv\.no/(?:(?:lazyplayer|player)/)?(?P<id>[0-9]+)(?:#(?P<display_id>.+))?'
|
||||||
_TEST = {
|
_TESTS = [{
|
||||||
'url': 'http://dbtv.no/3649835190001#Skulle_teste_ut_fornøyelsespark,_men_kollegaen_var_bare_opptatt_av_bikinikroppen',
|
'url': 'http://dbtv.no/3649835190001#Skulle_teste_ut_fornøyelsespark,_men_kollegaen_var_bare_opptatt_av_bikinikroppen',
|
||||||
'md5': 'b89953ed25dacb6edb3ef6c6f430f8bc',
|
'md5': 'b89953ed25dacb6edb3ef6c6f430f8bc',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
@@ -30,12 +30,18 @@ class DBTVIE(InfoExtractor):
|
|||||||
'view_count': int,
|
'view_count': int,
|
||||||
'categories': list,
|
'categories': list,
|
||||||
}
|
}
|
||||||
}
|
}, {
|
||||||
|
'url': 'http://dbtv.no/3649835190001',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'http://www.dbtv.no/lazyplayer/4631135248001',
|
||||||
|
'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')
|
video_id = mobj.group('id')
|
||||||
display_id = mobj.group('display_id')
|
display_id = mobj.group('display_id') or video_id
|
||||||
|
|
||||||
data = self._download_json(
|
data = self._download_json(
|
||||||
'http://api.dbtv.no/discovery/%s' % video_id, display_id)
|
'http://api.dbtv.no/discovery/%s' % video_id, display_id)
|
||||||
|
@@ -2,13 +2,11 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
from ..compat import compat_urllib_parse
|
||||||
compat_urllib_parse,
|
|
||||||
compat_urllib_request,
|
|
||||||
)
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
int_or_none,
|
int_or_none,
|
||||||
parse_iso8601,
|
parse_iso8601,
|
||||||
|
sanitized_Request,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -36,7 +34,7 @@ class DCNIE(InfoExtractor):
|
|||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
|
|
||||||
request = compat_urllib_request.Request(
|
request = sanitized_Request(
|
||||||
'http://admin.mangomolo.com/analytics/index.php/plus/video?id=%s' % video_id,
|
'http://admin.mangomolo.com/analytics/index.php/plus/video?id=%s' % video_id,
|
||||||
headers={'Origin': 'http://www.dcndigital.ae'})
|
headers={'Origin': 'http://www.dcndigital.ae'})
|
||||||
|
|
||||||
|
51
youtube_dl/extractor/dplay.py
Normal file
51
youtube_dl/extractor/dplay.py
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# encoding: utf-8
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
from .common import InfoExtractor
|
||||||
|
from ..utils import int_or_none
|
||||||
|
|
||||||
|
|
||||||
|
class DPlayIE(InfoExtractor):
|
||||||
|
_VALID_URL = r'http://www\.dplay\.se/[^/]+/(?P<id>[^/?#]+)'
|
||||||
|
|
||||||
|
_TEST = {
|
||||||
|
'url': 'http://www.dplay.se/nugammalt-77-handelser-som-format-sverige/season-1-svensken-lar-sig-njuta-av-livet/',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '3172',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'display_id': 'season-1-svensken-lar-sig-njuta-av-livet',
|
||||||
|
'title': 'Svensken lär sig njuta av livet',
|
||||||
|
'duration': 2650,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
display_id = self._match_id(url)
|
||||||
|
webpage = self._download_webpage(url, display_id)
|
||||||
|
video_id = self._search_regex(
|
||||||
|
r'data-video-id="(\d+)"', webpage, 'video id')
|
||||||
|
|
||||||
|
info = self._download_json(
|
||||||
|
'http://www.dplay.se/api/v2/ajax/videos?video_id=' + video_id,
|
||||||
|
video_id)['data'][0]
|
||||||
|
|
||||||
|
self._set_cookie(
|
||||||
|
'secure.dplay.se', 'dsc-geo',
|
||||||
|
'{"countryCode":"NL","expiry":%d}' % ((time.time() + 20 * 60) * 1000))
|
||||||
|
# TODO: consider adding support for 'stream_type=hds', it seems to
|
||||||
|
# require setting some cookies
|
||||||
|
manifest_url = self._download_json(
|
||||||
|
'https://secure.dplay.se/secure/api/v2/user/authorization/stream/%s?stream_type=hls' % video_id,
|
||||||
|
video_id, 'Getting manifest url for hls stream')['hls']
|
||||||
|
formats = self._extract_m3u8_formats(
|
||||||
|
manifest_url, video_id, ext='mp4', entry_protocol='m3u8_native')
|
||||||
|
|
||||||
|
return {
|
||||||
|
'id': video_id,
|
||||||
|
'display_id': display_id,
|
||||||
|
'title': info['title'],
|
||||||
|
'formats': formats,
|
||||||
|
'duration': int_or_none(info.get('video_metadata_length'), scale=1000),
|
||||||
|
}
|
@@ -7,7 +7,6 @@ from .common import InfoExtractor
|
|||||||
from ..compat import (
|
from ..compat import (
|
||||||
compat_HTTPError,
|
compat_HTTPError,
|
||||||
compat_urllib_parse,
|
compat_urllib_parse,
|
||||||
compat_urllib_request,
|
|
||||||
compat_urlparse,
|
compat_urlparse,
|
||||||
)
|
)
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
@@ -16,6 +15,7 @@ from ..utils import (
|
|||||||
determine_ext,
|
determine_ext,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
parse_iso8601,
|
parse_iso8601,
|
||||||
|
sanitized_Request,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ class DramaFeverBaseIE(InfoExtractor):
|
|||||||
'password': password,
|
'password': password,
|
||||||
}
|
}
|
||||||
|
|
||||||
request = compat_urllib_request.Request(
|
request = sanitized_Request(
|
||||||
self._LOGIN_URL, compat_urllib_parse.urlencode(login_form).encode('utf-8'))
|
self._LOGIN_URL, compat_urllib_parse.urlencode(login_form).encode('utf-8'))
|
||||||
response = self._download_webpage(
|
response = self._download_webpage(
|
||||||
request, None, 'Logging in as %s' % username)
|
request, None, 'Logging in as %s' % username)
|
||||||
|
@@ -2,14 +2,17 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import compat_urllib_request
|
from ..utils import (
|
||||||
from ..utils import qualities
|
qualities,
|
||||||
|
sanitized_Request,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class DumpertIE(InfoExtractor):
|
class DumpertIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://(?:www\.)?dumpert\.nl/(?:mediabase|embed)/(?P<id>[0-9]+/[0-9a-zA-Z]+)'
|
_VALID_URL = r'(?P<protocol>https?)://(?:www\.)?dumpert\.nl/(?:mediabase|embed)/(?P<id>[0-9]+/[0-9a-zA-Z]+)'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://www.dumpert.nl/mediabase/6646981/951bc60f/',
|
'url': 'http://www.dumpert.nl/mediabase/6646981/951bc60f/',
|
||||||
'md5': '1b9318d7d5054e7dcb9dc7654f21d643',
|
'md5': '1b9318d7d5054e7dcb9dc7654f21d643',
|
||||||
@@ -26,10 +29,12 @@ class DumpertIE(InfoExtractor):
|
|||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
mobj = re.match(self._VALID_URL, url)
|
||||||
|
video_id = mobj.group('id')
|
||||||
|
protocol = mobj.group('protocol')
|
||||||
|
|
||||||
url = 'https://www.dumpert.nl/mediabase/' + video_id
|
url = '%s://www.dumpert.nl/mediabase/%s' % (protocol, video_id)
|
||||||
req = compat_urllib_request.Request(url)
|
req = sanitized_Request(url)
|
||||||
req.add_header('Cookie', 'nsfw=1; cpc=10')
|
req.add_header('Cookie', 'nsfw=1; cpc=10')
|
||||||
webpage = self._download_webpage(req, video_id)
|
webpage = self._download_webpage(req, video_id)
|
||||||
|
|
||||||
|
@@ -2,11 +2,11 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import compat_urllib_request
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
float_or_none,
|
float_or_none,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
parse_iso8601,
|
parse_iso8601,
|
||||||
|
sanitized_Request,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ class EitbIE(InfoExtractor):
|
|||||||
|
|
||||||
hls_url = media.get('HLS_SURL')
|
hls_url = media.get('HLS_SURL')
|
||||||
if hls_url:
|
if hls_url:
|
||||||
request = compat_urllib_request.Request(
|
request = sanitized_Request(
|
||||||
'http://mam.eitb.eus/mam/REST/ServiceMultiweb/DomainRestrictedSecurity/TokenAuth/',
|
'http://mam.eitb.eus/mam/REST/ServiceMultiweb/DomainRestrictedSecurity/TokenAuth/',
|
||||||
headers={'Referer': url})
|
headers={'Referer': url})
|
||||||
token_data = self._download_json(
|
token_data = self._download_json(
|
||||||
|
@@ -3,13 +3,12 @@ from __future__ import unicode_literals
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import compat_urllib_request
|
|
||||||
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
determine_ext,
|
determine_ext,
|
||||||
clean_html,
|
clean_html,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
float_or_none,
|
float_or_none,
|
||||||
|
sanitized_Request,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -75,7 +74,7 @@ class EscapistIE(InfoExtractor):
|
|||||||
video_id = ims_video['videoID']
|
video_id = ims_video['videoID']
|
||||||
key = ims_video['hash']
|
key = ims_video['hash']
|
||||||
|
|
||||||
config_req = compat_urllib_request.Request(
|
config_req = sanitized_Request(
|
||||||
'http://www.escapistmagazine.com/videos/'
|
'http://www.escapistmagazine.com/videos/'
|
||||||
'vidconfig.php?videoID=%s&hash=%s' % (video_id, key))
|
'vidconfig.php?videoID=%s&hash=%s' % (video_id, key))
|
||||||
config_req.add_header('Referer', url)
|
config_req.add_header('Referer', url)
|
||||||
|
@@ -3,11 +3,9 @@ from __future__ import unicode_literals
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
|
||||||
compat_urllib_request,
|
|
||||||
)
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
|
sanitized_Request,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -42,7 +40,7 @@ class EveryonesMixtapeIE(InfoExtractor):
|
|||||||
playlist_id = mobj.group('id')
|
playlist_id = mobj.group('id')
|
||||||
|
|
||||||
pllist_url = 'http://everyonesmixtape.com/mixtape.php?a=getMixes&u=-1&linked=%s&explore=' % playlist_id
|
pllist_url = 'http://everyonesmixtape.com/mixtape.php?a=getMixes&u=-1&linked=%s&explore=' % playlist_id
|
||||||
pllist_req = compat_urllib_request.Request(pllist_url)
|
pllist_req = sanitized_Request(pllist_url)
|
||||||
pllist_req.add_header('X-Requested-With', 'XMLHttpRequest')
|
pllist_req.add_header('X-Requested-With', 'XMLHttpRequest')
|
||||||
|
|
||||||
playlist_list = self._download_json(
|
playlist_list = self._download_json(
|
||||||
@@ -55,7 +53,7 @@ class EveryonesMixtapeIE(InfoExtractor):
|
|||||||
raise ExtractorError('Playlist id not found')
|
raise ExtractorError('Playlist id not found')
|
||||||
|
|
||||||
pl_url = 'http://everyonesmixtape.com/mixtape.php?a=getMix&id=%s&userId=null&code=' % playlist_no
|
pl_url = 'http://everyonesmixtape.com/mixtape.php?a=getMix&id=%s&userId=null&code=' % playlist_no
|
||||||
pl_req = compat_urllib_request.Request(pl_url)
|
pl_req = sanitized_Request(pl_url)
|
||||||
pl_req.add_header('X-Requested-With', 'XMLHttpRequest')
|
pl_req.add_header('X-Requested-With', 'XMLHttpRequest')
|
||||||
playlist = self._download_json(
|
playlist = self._download_json(
|
||||||
pl_req, playlist_id, note='Downloading playlist info')
|
pl_req, playlist_id, note='Downloading playlist info')
|
||||||
|
@@ -3,9 +3,9 @@ from __future__ import unicode_literals
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import compat_urllib_request
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
int_or_none,
|
int_or_none,
|
||||||
|
sanitized_Request,
|
||||||
str_to_int,
|
str_to_int,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ class ExtremeTubeIE(InfoExtractor):
|
|||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
|
|
||||||
req = compat_urllib_request.Request(url)
|
req = sanitized_Request(url)
|
||||||
req.add_header('Cookie', 'age_verified=1')
|
req.add_header('Cookie', 'age_verified=1')
|
||||||
webpage = self._download_webpage(req, video_id)
|
webpage = self._download_webpage(req, video_id)
|
||||||
|
|
||||||
|
@@ -10,11 +10,11 @@ from ..compat import (
|
|||||||
compat_str,
|
compat_str,
|
||||||
compat_urllib_error,
|
compat_urllib_error,
|
||||||
compat_urllib_parse_unquote,
|
compat_urllib_parse_unquote,
|
||||||
compat_urllib_request,
|
|
||||||
)
|
)
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
limit_length,
|
limit_length,
|
||||||
|
sanitized_Request,
|
||||||
urlencode_postdata,
|
urlencode_postdata,
|
||||||
get_element_by_id,
|
get_element_by_id,
|
||||||
clean_html,
|
clean_html,
|
||||||
@@ -73,7 +73,7 @@ class FacebookIE(InfoExtractor):
|
|||||||
if useremail is None:
|
if useremail is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
login_page_req = compat_urllib_request.Request(self._LOGIN_URL)
|
login_page_req = sanitized_Request(self._LOGIN_URL)
|
||||||
login_page_req.add_header('Cookie', 'locale=en_US')
|
login_page_req.add_header('Cookie', 'locale=en_US')
|
||||||
login_page = self._download_webpage(login_page_req, None,
|
login_page = self._download_webpage(login_page_req, None,
|
||||||
note='Downloading login page',
|
note='Downloading login page',
|
||||||
@@ -94,7 +94,7 @@ class FacebookIE(InfoExtractor):
|
|||||||
'timezone': '-60',
|
'timezone': '-60',
|
||||||
'trynum': '1',
|
'trynum': '1',
|
||||||
}
|
}
|
||||||
request = compat_urllib_request.Request(self._LOGIN_URL, urlencode_postdata(login_form))
|
request = sanitized_Request(self._LOGIN_URL, urlencode_postdata(login_form))
|
||||||
request.add_header('Content-Type', 'application/x-www-form-urlencoded')
|
request.add_header('Content-Type', 'application/x-www-form-urlencoded')
|
||||||
try:
|
try:
|
||||||
login_results = self._download_webpage(request, None,
|
login_results = self._download_webpage(request, None,
|
||||||
@@ -109,7 +109,7 @@ class FacebookIE(InfoExtractor):
|
|||||||
r'name="h"\s+(?:\w+="[^"]+"\s+)*?value="([^"]+)"', login_results, 'h'),
|
r'name="h"\s+(?:\w+="[^"]+"\s+)*?value="([^"]+)"', login_results, 'h'),
|
||||||
'name_action_selected': 'dont_save',
|
'name_action_selected': 'dont_save',
|
||||||
}
|
}
|
||||||
check_req = compat_urllib_request.Request(self._CHECKPOINT_URL, urlencode_postdata(check_form))
|
check_req = sanitized_Request(self._CHECKPOINT_URL, urlencode_postdata(check_form))
|
||||||
check_req.add_header('Content-Type', 'application/x-www-form-urlencoded')
|
check_req.add_header('Content-Type', 'application/x-www-form-urlencoded')
|
||||||
check_response = self._download_webpage(check_req, None,
|
check_response = self._download_webpage(check_req, None,
|
||||||
note='Confirming login')
|
note='Confirming login')
|
||||||
@@ -164,7 +164,7 @@ class FacebookIE(InfoExtractor):
|
|||||||
if not video_title:
|
if not video_title:
|
||||||
video_title = self._html_search_regex(
|
video_title = self._html_search_regex(
|
||||||
r'(?s)<span class="fbPhotosPhotoCaption".*?id="fbPhotoPageCaption"><span class="hasCaption">(.*?)</span>',
|
r'(?s)<span class="fbPhotosPhotoCaption".*?id="fbPhotoPageCaption"><span class="hasCaption">(.*?)</span>',
|
||||||
webpage, 'alternative title', fatal=False)
|
webpage, 'alternative title', default=None)
|
||||||
video_title = limit_length(video_title, 80)
|
video_title = limit_length(video_title, 80)
|
||||||
if not video_title:
|
if not video_title:
|
||||||
video_title = 'Facebook video #%s' % video_id
|
video_title = 'Facebook video #%s' % video_id
|
||||||
|
@@ -12,6 +12,7 @@ from ..compat import (
|
|||||||
from ..utils import (
|
from ..utils import (
|
||||||
encode_dict,
|
encode_dict,
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
|
sanitized_Request,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -36,8 +37,8 @@ class FC2IE(InfoExtractor):
|
|||||||
'params': {
|
'params': {
|
||||||
'username': 'ytdl@yt-dl.org',
|
'username': 'ytdl@yt-dl.org',
|
||||||
'password': '(snip)',
|
'password': '(snip)',
|
||||||
'skip': 'requires actual password'
|
},
|
||||||
}
|
'skip': 'requires actual password',
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://video.fc2.com/en/a/content/20130926eZpARwsF',
|
'url': 'http://video.fc2.com/en/a/content/20130926eZpARwsF',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
@@ -57,7 +58,7 @@ class FC2IE(InfoExtractor):
|
|||||||
}
|
}
|
||||||
|
|
||||||
login_data = compat_urllib_parse.urlencode(encode_dict(login_form_strs)).encode('utf-8')
|
login_data = compat_urllib_parse.urlencode(encode_dict(login_form_strs)).encode('utf-8')
|
||||||
request = compat_urllib_request.Request(
|
request = sanitized_Request(
|
||||||
'https://secure.id.fc2.com/index.php?mode=login&switch_language=en', login_data)
|
'https://secure.id.fc2.com/index.php?mode=login&switch_language=en', login_data)
|
||||||
|
|
||||||
login_results = self._download_webpage(request, None, note='Logging in', errnote='Unable to log in')
|
login_results = self._download_webpage(request, None, note='Logging in', errnote='Unable to log in')
|
||||||
@@ -66,7 +67,7 @@ class FC2IE(InfoExtractor):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
# this is also needed
|
# this is also needed
|
||||||
login_redir = compat_urllib_request.Request('http://id.fc2.com/?mode=redirect&login=done')
|
login_redir = sanitized_Request('http://id.fc2.com/?mode=redirect&login=done')
|
||||||
self._download_webpage(
|
self._download_webpage(
|
||||||
login_redir, None, note='Login redirect', errnote='Login redirect failed')
|
login_redir, None, note='Login redirect', errnote='Login redirect failed')
|
||||||
|
|
||||||
|
@@ -3,10 +3,10 @@ from __future__ import unicode_literals
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import compat_urllib_request
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
find_xpath_attr,
|
find_xpath_attr,
|
||||||
|
sanitized_Request,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ class FlickrIE(InfoExtractor):
|
|||||||
video_id = mobj.group('id')
|
video_id = mobj.group('id')
|
||||||
video_uploader_id = mobj.group('uploader_id')
|
video_uploader_id = mobj.group('uploader_id')
|
||||||
webpage_url = 'http://www.flickr.com/photos/' + video_uploader_id + '/' + video_id
|
webpage_url = 'http://www.flickr.com/photos/' + video_uploader_id + '/' + video_id
|
||||||
req = compat_urllib_request.Request(webpage_url)
|
req = sanitized_Request(webpage_url)
|
||||||
req.add_header(
|
req.add_header(
|
||||||
'User-Agent',
|
'User-Agent',
|
||||||
# it needs a more recent version
|
# it needs a more recent version
|
||||||
|
@@ -3,12 +3,10 @@ from __future__ import unicode_literals
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
|
||||||
compat_urllib_request,
|
|
||||||
)
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
parse_duration,
|
parse_duration,
|
||||||
parse_iso8601,
|
parse_iso8601,
|
||||||
|
sanitized_Request,
|
||||||
str_to_int,
|
str_to_int,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -93,7 +91,7 @@ class FourTubeIE(InfoExtractor):
|
|||||||
b'Content-Type': b'application/x-www-form-urlencoded',
|
b'Content-Type': b'application/x-www-form-urlencoded',
|
||||||
b'Origin': b'http://www.4tube.com',
|
b'Origin': b'http://www.4tube.com',
|
||||||
}
|
}
|
||||||
token_req = compat_urllib_request.Request(token_url, b'{}', headers)
|
token_req = sanitized_Request(token_url, b'{}', headers)
|
||||||
tokens = self._download_json(token_req, video_id)
|
tokens = self._download_json(token_req, video_id)
|
||||||
formats = [{
|
formats = [{
|
||||||
'url': tokens[format]['token'],
|
'url': tokens[format]['token'],
|
||||||
|
193
youtube_dl/extractor/funimation.py
Normal file
193
youtube_dl/extractor/funimation.py
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from .common import InfoExtractor
|
||||||
|
from ..utils import (
|
||||||
|
clean_html,
|
||||||
|
determine_ext,
|
||||||
|
encode_dict,
|
||||||
|
int_or_none,
|
||||||
|
sanitized_Request,
|
||||||
|
ExtractorError,
|
||||||
|
urlencode_postdata
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class FunimationIE(InfoExtractor):
|
||||||
|
_VALID_URL = r'https?://(?:www\.)?funimation\.com/shows/[^/]+/videos/(?:official|promotional)/(?P<id>[^/?#&]+)'
|
||||||
|
|
||||||
|
_NETRC_MACHINE = 'funimation'
|
||||||
|
|
||||||
|
_TESTS = [{
|
||||||
|
'url': 'http://www.funimation.com/shows/air/videos/official/breeze',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '658',
|
||||||
|
'display_id': 'breeze',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Air - 1 - Breeze',
|
||||||
|
'description': 'md5:1769f43cd5fc130ace8fd87232207892',
|
||||||
|
'thumbnail': 're:https?://.*\.jpg',
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
'url': 'http://www.funimation.com/shows/hacksign/videos/official/role-play',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '31128',
|
||||||
|
'display_id': 'role-play',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': '.hack//SIGN - 1 - Role Play',
|
||||||
|
'description': 'md5:b602bdc15eef4c9bbb201bb6e6a4a2dd',
|
||||||
|
'thumbnail': 're:https?://.*\.jpg',
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
'url': 'http://www.funimation.com/shows/attack-on-titan-junior-high/videos/promotional/broadcast-dub-preview',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '9635',
|
||||||
|
'display_id': 'broadcast-dub-preview',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Attack on Titan: Junior High - Broadcast Dub Preview',
|
||||||
|
'description': 'md5:f8ec49c0aff702a7832cd81b8a44f803',
|
||||||
|
'thumbnail': 're:https?://.*\.(?:jpg|png)',
|
||||||
|
},
|
||||||
|
}]
|
||||||
|
|
||||||
|
def _login(self):
|
||||||
|
(username, password) = self._get_login_info()
|
||||||
|
if username is None:
|
||||||
|
return
|
||||||
|
data = urlencode_postdata(encode_dict({
|
||||||
|
'email_field': username,
|
||||||
|
'password_field': password,
|
||||||
|
}))
|
||||||
|
login_request = sanitized_Request('http://www.funimation.com/login', data, headers={
|
||||||
|
'User-Agent': 'Mozilla/5.0 (Windows NT 5.2; WOW64; rv:42.0) Gecko/20100101 Firefox/42.0',
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
})
|
||||||
|
login_page = self._download_webpage(
|
||||||
|
login_request, None, 'Logging in as %s' % username)
|
||||||
|
if any(p in login_page for p in ('funimation.com/logout', '>Log Out<')):
|
||||||
|
return
|
||||||
|
error = self._html_search_regex(
|
||||||
|
r'(?s)<div[^>]+id=["\']errorMessages["\'][^>]*>(.+?)</div>',
|
||||||
|
login_page, 'error messages', default=None)
|
||||||
|
if error:
|
||||||
|
raise ExtractorError('Unable to login: %s' % error, expected=True)
|
||||||
|
raise ExtractorError('Unable to log in')
|
||||||
|
|
||||||
|
def _real_initialize(self):
|
||||||
|
self._login()
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
display_id = self._match_id(url)
|
||||||
|
|
||||||
|
errors = []
|
||||||
|
formats = []
|
||||||
|
|
||||||
|
ERRORS_MAP = {
|
||||||
|
'ERROR_MATURE_CONTENT_LOGGED_IN': 'matureContentLoggedIn',
|
||||||
|
'ERROR_MATURE_CONTENT_LOGGED_OUT': 'matureContentLoggedOut',
|
||||||
|
'ERROR_SUBSCRIPTION_LOGGED_OUT': 'subscriptionLoggedOut',
|
||||||
|
'ERROR_VIDEO_EXPIRED': 'videoExpired',
|
||||||
|
'ERROR_TERRITORY_UNAVAILABLE': 'territoryUnavailable',
|
||||||
|
'SVODBASIC_SUBSCRIPTION_IN_PLAYER': 'basicSubscription',
|
||||||
|
'SVODNON_SUBSCRIPTION_IN_PLAYER': 'nonSubscription',
|
||||||
|
'ERROR_PLAYER_NOT_RESPONDING': 'playerNotResponding',
|
||||||
|
'ERROR_UNABLE_TO_CONNECT_TO_CDN': 'unableToConnectToCDN',
|
||||||
|
'ERROR_STREAM_NOT_FOUND': 'streamNotFound',
|
||||||
|
}
|
||||||
|
|
||||||
|
USER_AGENTS = (
|
||||||
|
# PC UA is served with m3u8 that provides some bonus lower quality formats
|
||||||
|
('pc', 'Mozilla/5.0 (Windows NT 5.2; WOW64; rv:42.0) Gecko/20100101 Firefox/42.0'),
|
||||||
|
# Mobile UA allows to extract direct links and also does not fail when
|
||||||
|
# PC UA fails with hulu error (e.g.
|
||||||
|
# http://www.funimation.com/shows/hacksign/videos/official/role-play)
|
||||||
|
('mobile', 'Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.114 Mobile Safari/537.36'),
|
||||||
|
)
|
||||||
|
|
||||||
|
for kind, user_agent in USER_AGENTS:
|
||||||
|
request = sanitized_Request(url)
|
||||||
|
request.add_header('User-Agent', user_agent)
|
||||||
|
webpage = self._download_webpage(
|
||||||
|
request, display_id, 'Downloading %s webpage' % kind)
|
||||||
|
|
||||||
|
playlist = self._parse_json(
|
||||||
|
self._search_regex(
|
||||||
|
r'var\s+playersData\s*=\s*(\[.+?\]);\n',
|
||||||
|
webpage, 'players data'),
|
||||||
|
display_id)[0]['playlist']
|
||||||
|
|
||||||
|
items = next(item['items'] for item in playlist if item.get('items'))
|
||||||
|
item = next(item for item in items if item.get('itemAK') == display_id)
|
||||||
|
|
||||||
|
error_messages = {}
|
||||||
|
video_error_messages = self._search_regex(
|
||||||
|
r'var\s+videoErrorMessages\s*=\s*({.+?});\n',
|
||||||
|
webpage, 'error messages', default=None)
|
||||||
|
if video_error_messages:
|
||||||
|
error_messages_json = self._parse_json(video_error_messages, display_id, fatal=False)
|
||||||
|
if error_messages_json:
|
||||||
|
for _, error in error_messages_json.items():
|
||||||
|
type_ = error.get('type')
|
||||||
|
description = error.get('description')
|
||||||
|
content = error.get('content')
|
||||||
|
if type_ == 'text' and description and content:
|
||||||
|
error_message = ERRORS_MAP.get(description)
|
||||||
|
if error_message:
|
||||||
|
error_messages[error_message] = content
|
||||||
|
|
||||||
|
for video in item.get('videoSet', []):
|
||||||
|
auth_token = video.get('authToken')
|
||||||
|
if not auth_token:
|
||||||
|
continue
|
||||||
|
funimation_id = video.get('FUNImationID') or video.get('videoId')
|
||||||
|
preference = 1 if video.get('languageMode') == 'dub' else 0
|
||||||
|
if not auth_token.startswith('?'):
|
||||||
|
auth_token = '?%s' % auth_token
|
||||||
|
for quality, height in (('sd', 480), ('hd', 720), ('hd1080', 1080)):
|
||||||
|
format_url = video.get('%sUrl' % quality)
|
||||||
|
if not format_url:
|
||||||
|
continue
|
||||||
|
if not format_url.startswith(('http', '//')):
|
||||||
|
errors.append(format_url)
|
||||||
|
continue
|
||||||
|
if determine_ext(format_url) == 'm3u8':
|
||||||
|
m3u8_formats = self._extract_m3u8_formats(
|
||||||
|
format_url + auth_token, display_id, 'mp4', entry_protocol='m3u8_native',
|
||||||
|
preference=preference, m3u8_id='%s-hls' % funimation_id, fatal=False)
|
||||||
|
if m3u8_formats:
|
||||||
|
formats.extend(m3u8_formats)
|
||||||
|
else:
|
||||||
|
tbr = int_or_none(self._search_regex(
|
||||||
|
r'-(\d+)[Kk]', format_url, 'tbr', default=None))
|
||||||
|
formats.append({
|
||||||
|
'url': format_url + auth_token,
|
||||||
|
'format_id': '%s-http-%dp' % (funimation_id, height),
|
||||||
|
'height': height,
|
||||||
|
'tbr': tbr,
|
||||||
|
'preference': preference,
|
||||||
|
})
|
||||||
|
|
||||||
|
if not formats and errors:
|
||||||
|
raise ExtractorError(
|
||||||
|
'%s returned error: %s'
|
||||||
|
% (self.IE_NAME, clean_html(error_messages.get(errors[0], errors[0]))),
|
||||||
|
expected=True)
|
||||||
|
|
||||||
|
self._sort_formats(formats)
|
||||||
|
|
||||||
|
title = item['title']
|
||||||
|
artist = item.get('artist')
|
||||||
|
if artist:
|
||||||
|
title = '%s - %s' % (artist, title)
|
||||||
|
description = self._og_search_description(webpage) or item.get('description')
|
||||||
|
thumbnail = self._og_search_thumbnail(webpage) or item.get('posterUrl')
|
||||||
|
video_id = item.get('itemId') or display_id
|
||||||
|
|
||||||
|
return {
|
||||||
|
'id': video_id,
|
||||||
|
'display_id': display_id,
|
||||||
|
'title': title,
|
||||||
|
'description': description,
|
||||||
|
'thumbnail': thumbnail,
|
||||||
|
'formats': formats,
|
||||||
|
}
|
@@ -45,11 +45,20 @@ class FunnyOrDieIE(InfoExtractor):
|
|||||||
|
|
||||||
links.sort(key=lambda link: 1 if link[1] == 'mp4' else 0)
|
links.sort(key=lambda link: 1 if link[1] == 'mp4' else 0)
|
||||||
|
|
||||||
bitrates = self._html_search_regex(r'<source src="[^"]+/v,((?:\d+,)+)\.mp4\.csmil', webpage, 'video bitrates')
|
m3u8_url = self._search_regex(
|
||||||
bitrates = [int(b) for b in bitrates.rstrip(',').split(',')]
|
r'<source[^>]+src=(["\'])(?P<url>.+?/master\.m3u8)\1',
|
||||||
bitrates.sort()
|
webpage, 'm3u8 url', default=None, group='url')
|
||||||
|
|
||||||
formats = []
|
formats = []
|
||||||
|
|
||||||
|
m3u8_formats = self._extract_m3u8_formats(
|
||||||
|
m3u8_url, video_id, 'mp4', 'm3u8_native', m3u8_id='hls', fatal=False)
|
||||||
|
if m3u8_formats:
|
||||||
|
formats.extend(m3u8_formats)
|
||||||
|
|
||||||
|
bitrates = [int(bitrate) for bitrate in re.findall(r'[,/]v(\d+)[,/]', m3u8_url)]
|
||||||
|
bitrates.sort()
|
||||||
|
|
||||||
for bitrate in bitrates:
|
for bitrate in bitrates:
|
||||||
for link in links:
|
for link in links:
|
||||||
formats.append({
|
formats.append({
|
||||||
|
43
youtube_dl/extractor/gameinformer.py
Normal file
43
youtube_dl/extractor/gameinformer.py
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from .common import InfoExtractor
|
||||||
|
from ..compat import compat_str
|
||||||
|
from ..utils import int_or_none
|
||||||
|
|
||||||
|
|
||||||
|
class GameInformerIE(InfoExtractor):
|
||||||
|
_VALID_URL = r'https?://(?:www\.)?gameinformer\.com/(?:[^/]+/)*(?P<id>.+)\.aspx'
|
||||||
|
_TEST = {
|
||||||
|
'url': 'http://www.gameinformer.com/b/features/archive/2015/09/26/replay-animal-crossing.aspx',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '4515472681001',
|
||||||
|
'ext': 'm3u8',
|
||||||
|
'title': 'Replay - Animal Crossing',
|
||||||
|
'description': 'md5:2e211891b215c85d061adc7a4dd2d930',
|
||||||
|
'timestamp': 1443457610706,
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
# m3u8 download
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
display_id = self._match_id(url)
|
||||||
|
webpage = self._download_webpage(url, display_id)
|
||||||
|
|
||||||
|
bc_api_url = self._search_regex(r"getVideo\('([^']+)'", webpage, 'brightcove api url')
|
||||||
|
json_data = self._download_json(
|
||||||
|
bc_api_url + '&video_fields=id,name,shortDescription,publishedDate,videoStillURL,length,IOSRenditions',
|
||||||
|
display_id)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'id': compat_str(json_data['id']),
|
||||||
|
'display_id': display_id,
|
||||||
|
'url': json_data['IOSRenditions'][0]['url'],
|
||||||
|
'title': json_data['name'],
|
||||||
|
'description': json_data.get('shortDescription'),
|
||||||
|
'timestamp': int_or_none(json_data.get('publishedDate')),
|
||||||
|
'duration': int_or_none(json_data.get('length')),
|
||||||
|
}
|
@@ -1,19 +1,62 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from .mtv import MTVServicesInfoExtractor
|
from .common import InfoExtractor
|
||||||
|
from ..utils import (
|
||||||
|
int_or_none,
|
||||||
|
parse_age_limit,
|
||||||
|
url_basename,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class GametrailersIE(MTVServicesInfoExtractor):
|
class GametrailersIE(InfoExtractor):
|
||||||
_VALID_URL = r'http://www\.gametrailers\.com/(?P<type>videos|reviews|full-episodes)/(?P<id>.*?)/(?P<title>.*)'
|
_VALID_URL = r'http://www\.gametrailers\.com/videos/view/[^/]+/(?P<id>.+)'
|
||||||
|
|
||||||
_TEST = {
|
_TEST = {
|
||||||
'url': 'http://www.gametrailers.com/videos/zbvr8i/mirror-s-edge-2-e3-2013--debut-trailer',
|
'url': 'http://www.gametrailers.com/videos/view/gametrailers-com/116437-Just-Cause-3-Review',
|
||||||
'md5': '4c8e67681a0ea7ec241e8c09b3ea8cf7',
|
'md5': 'f28c4efa0bdfaf9b760f6507955b6a6a',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '70e9a5d7-cf25-4a10-9104-6f3e7342ae0d',
|
'id': '2983958',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'E3 2013: Debut Trailer',
|
'display_id': '116437-Just-Cause-3-Review',
|
||||||
'description': 'Faith is back! Check out the World Premiere trailer for Mirror\'s Edge 2 straight from the EA Press Conference at E3 2013!',
|
'title': 'Just Cause 3 - Review',
|
||||||
|
'description': 'It\'s a lot of fun to shoot at things and then watch them explode in Just Cause 3, but should there be more to the experience than that?',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
_FEED_URL = 'http://www.gametrailers.com/feeds/mrss'
|
def _real_extract(self, url):
|
||||||
|
display_id = self._match_id(url)
|
||||||
|
webpage = self._download_webpage(url, display_id)
|
||||||
|
title = self._html_search_regex(
|
||||||
|
r'<title>(.+?)\|', webpage, 'title').strip()
|
||||||
|
embed_url = self._proto_relative_url(
|
||||||
|
self._search_regex(
|
||||||
|
r'src=\'(//embed.gametrailers.com/embed/[^\']+)\'', webpage,
|
||||||
|
'embed url'),
|
||||||
|
scheme='http:')
|
||||||
|
video_id = url_basename(embed_url)
|
||||||
|
embed_page = self._download_webpage(embed_url, video_id)
|
||||||
|
embed_vars_json = self._search_regex(
|
||||||
|
r'(?s)var embedVars = (\{.*?\})\s*</script>', embed_page,
|
||||||
|
'embed vars')
|
||||||
|
info = self._parse_json(embed_vars_json, video_id)
|
||||||
|
|
||||||
|
formats = []
|
||||||
|
for media in info['media']:
|
||||||
|
if media['mediaPurpose'] == 'play':
|
||||||
|
formats.append({
|
||||||
|
'url': media['uri'],
|
||||||
|
'height': media['height'],
|
||||||
|
'width:': media['width'],
|
||||||
|
})
|
||||||
|
self._sort_formats(formats)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'id': video_id,
|
||||||
|
'display_id': display_id,
|
||||||
|
'title': title,
|
||||||
|
'formats': formats,
|
||||||
|
'thumbnail': info.get('thumbUri'),
|
||||||
|
'description': self._og_search_description(webpage),
|
||||||
|
'duration': int_or_none(info.get('videoLengthInSeconds')),
|
||||||
|
'age_limit': parse_age_limit(info.get('audienceRating')),
|
||||||
|
}
|
||||||
|
@@ -3,13 +3,11 @@ from __future__ import unicode_literals
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
from ..compat import compat_urllib_parse
|
||||||
compat_urllib_parse,
|
|
||||||
compat_urllib_request,
|
|
||||||
)
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
remove_end,
|
remove_end,
|
||||||
HEADRequest,
|
HEADRequest,
|
||||||
|
sanitized_Request,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -125,7 +123,7 @@ class GDCVaultIE(InfoExtractor):
|
|||||||
'password': password,
|
'password': password,
|
||||||
}
|
}
|
||||||
|
|
||||||
request = compat_urllib_request.Request(login_url, compat_urllib_parse.urlencode(login_form))
|
request = sanitized_Request(login_url, compat_urllib_parse.urlencode(login_form))
|
||||||
request.add_header('Content-Type', 'application/x-www-form-urlencoded')
|
request.add_header('Content-Type', 'application/x-www-form-urlencoded')
|
||||||
self._download_webpage(request, display_id, 'Logging in')
|
self._download_webpage(request, display_id, 'Logging in')
|
||||||
start_page = self._download_webpage(webpage_url, display_id, 'Getting authenticated video page')
|
start_page = self._download_webpage(webpage_url, display_id, 'Getting authenticated video page')
|
||||||
|
@@ -11,7 +11,6 @@ from .youtube import YoutubeIE
|
|||||||
from ..compat import (
|
from ..compat import (
|
||||||
compat_etree_fromstring,
|
compat_etree_fromstring,
|
||||||
compat_urllib_parse_unquote,
|
compat_urllib_parse_unquote,
|
||||||
compat_urllib_request,
|
|
||||||
compat_urlparse,
|
compat_urlparse,
|
||||||
compat_xml_parse_error,
|
compat_xml_parse_error,
|
||||||
)
|
)
|
||||||
@@ -22,6 +21,7 @@ from ..utils import (
|
|||||||
HEADRequest,
|
HEADRequest,
|
||||||
is_html,
|
is_html,
|
||||||
orderedSet,
|
orderedSet,
|
||||||
|
sanitized_Request,
|
||||||
smuggle_url,
|
smuggle_url,
|
||||||
unescapeHTML,
|
unescapeHTML,
|
||||||
unified_strdate,
|
unified_strdate,
|
||||||
@@ -30,7 +30,10 @@ from ..utils import (
|
|||||||
url_basename,
|
url_basename,
|
||||||
xpath_text,
|
xpath_text,
|
||||||
)
|
)
|
||||||
from .brightcove import BrightcoveIE
|
from .brightcove import (
|
||||||
|
BrightcoveLegacyIE,
|
||||||
|
BrightcoveNewIE,
|
||||||
|
)
|
||||||
from .nbc import NBCSportsVPlayerIE
|
from .nbc import NBCSportsVPlayerIE
|
||||||
from .ooyala import OoyalaIE
|
from .ooyala import OoyalaIE
|
||||||
from .rutv import RUTVIE
|
from .rutv import RUTVIE
|
||||||
@@ -51,6 +54,7 @@ from .onionstudios import OnionStudiosIE
|
|||||||
from .snagfilms import SnagFilmsEmbedIE
|
from .snagfilms import SnagFilmsEmbedIE
|
||||||
from .screenwavemedia import ScreenwaveMediaIE
|
from .screenwavemedia import ScreenwaveMediaIE
|
||||||
from .mtv import MTVServicesEmbeddedIE
|
from .mtv import MTVServicesEmbeddedIE
|
||||||
|
from .pladform import PladformIE
|
||||||
|
|
||||||
|
|
||||||
class GenericIE(InfoExtractor):
|
class GenericIE(InfoExtractor):
|
||||||
@@ -275,7 +279,7 @@ class GenericIE(InfoExtractor):
|
|||||||
# it also tests brightcove videos that need to set the 'Referer' in the
|
# it also tests brightcove videos that need to set the 'Referer' in the
|
||||||
# http requests
|
# http requests
|
||||||
{
|
{
|
||||||
'add_ie': ['Brightcove'],
|
'add_ie': ['BrightcoveLegacy'],
|
||||||
'url': 'http://www.bfmtv.com/video/bfmbusiness/cours-bourse/cours-bourse-l-analyse-technique-154522/',
|
'url': 'http://www.bfmtv.com/video/bfmbusiness/cours-bourse/cours-bourse-l-analyse-technique-154522/',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '2765128793001',
|
'id': '2765128793001',
|
||||||
@@ -299,7 +303,7 @@ class GenericIE(InfoExtractor):
|
|||||||
'uploader': 'thestar.com',
|
'uploader': 'thestar.com',
|
||||||
'description': 'Mississauga resident David Farmer is still out of power as a result of the ice storm a month ago. To keep the house warm, Farmer cuts wood from his property for a wood burning stove downstairs.',
|
'description': 'Mississauga resident David Farmer is still out of power as a result of the ice storm a month ago. To keep the house warm, Farmer cuts wood from his property for a wood burning stove downstairs.',
|
||||||
},
|
},
|
||||||
'add_ie': ['Brightcove'],
|
'add_ie': ['BrightcoveLegacy'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'url': 'http://www.championat.com/video/football/v/87/87499.html',
|
'url': 'http://www.championat.com/video/football/v/87/87499.html',
|
||||||
@@ -314,7 +318,7 @@ class GenericIE(InfoExtractor):
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
# https://github.com/rg3/youtube-dl/issues/3541
|
# https://github.com/rg3/youtube-dl/issues/3541
|
||||||
'add_ie': ['Brightcove'],
|
'add_ie': ['BrightcoveLegacy'],
|
||||||
'url': 'http://www.kijk.nl/sbs6/leermijvrouwenkennen/videos/jqMiXKAYan2S/aflevering-1',
|
'url': 'http://www.kijk.nl/sbs6/leermijvrouwenkennen/videos/jqMiXKAYan2S/aflevering-1',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '3866516442001',
|
'id': '3866516442001',
|
||||||
@@ -336,6 +340,7 @@ class GenericIE(InfoExtractor):
|
|||||||
'id': 'BwY2RxaTrTkslxOfcan0UCf0YqyvWysJ',
|
'id': 'BwY2RxaTrTkslxOfcan0UCf0YqyvWysJ',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': '2cc213299525360.mov', # that's what we get
|
'title': '2cc213299525360.mov', # that's what we get
|
||||||
|
'duration': 238.231,
|
||||||
},
|
},
|
||||||
'add_ie': ['Ooyala'],
|
'add_ie': ['Ooyala'],
|
||||||
},
|
},
|
||||||
@@ -347,6 +352,7 @@ class GenericIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': '"Steve Jobs: Man in the Machine" trailer',
|
'title': '"Steve Jobs: Man in the Machine" trailer',
|
||||||
'description': 'The first trailer for the Alex Gibney documentary "Steve Jobs: Man in the Machine."',
|
'description': 'The first trailer for the Alex Gibney documentary "Steve Jobs: Man in the Machine."',
|
||||||
|
'duration': 135.427,
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
@@ -820,6 +826,19 @@ class GenericIE(InfoExtractor):
|
|||||||
'title': 'Os Guinness // Is It Fools Talk? // Unbelievable? Conference 2014',
|
'title': 'Os Guinness // Is It Fools Talk? // Unbelievable? Conference 2014',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
# Kaltura embed protected with referrer
|
||||||
|
{
|
||||||
|
'url': 'http://www.disney.nl/disney-channel/filmpjes/achter-de-schermen#/videoId/violetta-achter-de-schermen-ruggero',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '1_g4fbemnq',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Violetta - Achter De Schermen - Ruggero',
|
||||||
|
'description': 'Achter de schermen met Ruggero',
|
||||||
|
'timestamp': 1435133761,
|
||||||
|
'upload_date': '20150624',
|
||||||
|
'uploader_id': 'echojecka',
|
||||||
|
},
|
||||||
|
},
|
||||||
# Eagle.Platform embed (generic URL)
|
# Eagle.Platform embed (generic URL)
|
||||||
{
|
{
|
||||||
'url': 'http://lenta.ru/news/2015/03/06/navalny/',
|
'url': 'http://lenta.ru/news/2015/03/06/navalny/',
|
||||||
@@ -944,8 +963,9 @@ class GenericIE(InfoExtractor):
|
|||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '50YnY4czr4ms1vJ7yz3xzq0excz_pUMs',
|
'id': '50YnY4czr4ms1vJ7yz3xzq0excz_pUMs',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'description': 'VIDEO: Index/Match versus VLOOKUP.',
|
'description': 'VIDEO: INDEX/MATCH versus VLOOKUP.',
|
||||||
'title': 'This is what separates the Excel masters from the wannabes',
|
'title': 'This is what separates the Excel masters from the wannabes',
|
||||||
|
'duration': 191.933,
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
# m3u8 downloads
|
# m3u8 downloads
|
||||||
@@ -1031,6 +1051,31 @@ class GenericIE(InfoExtractor):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'cinemasnob',
|
'title': 'cinemasnob',
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
# BrightcoveInPageEmbed embed
|
||||||
|
{
|
||||||
|
'url': 'http://www.geekandsundry.com/tabletop-bonus-wils-final-thoughts-on-dread/',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '4238694884001',
|
||||||
|
'ext': 'flv',
|
||||||
|
'title': 'Tabletop: Dread, Last Thoughts',
|
||||||
|
'description': 'Tabletop: Dread, Last Thoughts',
|
||||||
|
'duration': 51690,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
# JWPlayer with M3U8
|
||||||
|
{
|
||||||
|
'url': 'http://ren.tv/novosti/2015-09-25/sluchaynyy-prohozhiy-poymal-avtougonshchika-v-murmanske-video',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'playlist',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Случайный прохожий поймал автоугонщика в Мурманске. ВИДЕО | РЕН ТВ',
|
||||||
|
'uploader': 'ren.tv',
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
# m3u8 downloads
|
||||||
|
'skip_download': True,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1174,7 +1219,7 @@ class GenericIE(InfoExtractor):
|
|||||||
|
|
||||||
full_response = None
|
full_response = None
|
||||||
if head_response is False:
|
if head_response is False:
|
||||||
request = compat_urllib_request.Request(url)
|
request = sanitized_Request(url)
|
||||||
request.add_header('Accept-Encoding', '*')
|
request.add_header('Accept-Encoding', '*')
|
||||||
full_response = self._request_webpage(request, video_id)
|
full_response = self._request_webpage(request, video_id)
|
||||||
head_response = full_response
|
head_response = full_response
|
||||||
@@ -1203,7 +1248,7 @@ class GenericIE(InfoExtractor):
|
|||||||
'%s on generic information extractor.' % ('Forcing' if force else 'Falling back'))
|
'%s on generic information extractor.' % ('Forcing' if force else 'Falling back'))
|
||||||
|
|
||||||
if not full_response:
|
if not full_response:
|
||||||
request = compat_urllib_request.Request(url)
|
request = sanitized_Request(url)
|
||||||
# Some webservers may serve compressed content of rather big size (e.g. gzipped flac)
|
# Some webservers may serve compressed content of rather big size (e.g. gzipped flac)
|
||||||
# making it impossible to download only chunk of the file (yet we need only 512kB to
|
# making it impossible to download only chunk of the file (yet we need only 512kB to
|
||||||
# test whether it's HTML or not). According to youtube-dl default Accept-Encoding
|
# test whether it's HTML or not). According to youtube-dl default Accept-Encoding
|
||||||
@@ -1290,14 +1335,14 @@ class GenericIE(InfoExtractor):
|
|||||||
return self.playlist_result(
|
return self.playlist_result(
|
||||||
urlrs, playlist_id=video_id, playlist_title=video_title)
|
urlrs, playlist_id=video_id, playlist_title=video_title)
|
||||||
|
|
||||||
# Look for BrightCove:
|
# Look for Brightcove Legacy Studio embeds
|
||||||
bc_urls = BrightcoveIE._extract_brightcove_urls(webpage)
|
bc_urls = BrightcoveLegacyIE._extract_brightcove_urls(webpage)
|
||||||
if bc_urls:
|
if bc_urls:
|
||||||
self.to_screen('Brightcove video detected.')
|
self.to_screen('Brightcove video detected.')
|
||||||
entries = [{
|
entries = [{
|
||||||
'_type': 'url',
|
'_type': 'url',
|
||||||
'url': smuggle_url(bc_url, {'Referer': url}),
|
'url': smuggle_url(bc_url, {'Referer': url}),
|
||||||
'ie_key': 'Brightcove'
|
'ie_key': 'BrightcoveLegacy'
|
||||||
} for bc_url in bc_urls]
|
} for bc_url in bc_urls]
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -1307,6 +1352,11 @@ class GenericIE(InfoExtractor):
|
|||||||
'entries': entries,
|
'entries': entries,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Look for Brightcove New Studio embeds
|
||||||
|
bc_urls = BrightcoveNewIE._extract_urls(webpage)
|
||||||
|
if bc_urls:
|
||||||
|
return _playlist_from_matches(bc_urls, ie='BrightcoveNew')
|
||||||
|
|
||||||
# Look for embedded rtl.nl player
|
# Look for embedded rtl.nl player
|
||||||
matches = re.findall(
|
matches = re.findall(
|
||||||
r'<iframe[^>]+?src="((?:https?:)?//(?:www\.)?rtl\.nl/system/videoplayer/[^"]+(?:video_)?embed[^"]+)"',
|
r'<iframe[^>]+?src="((?:https?:)?//(?:www\.)?rtl\.nl/system/videoplayer/[^"]+(?:video_)?embed[^"]+)"',
|
||||||
@@ -1455,7 +1505,7 @@ class GenericIE(InfoExtractor):
|
|||||||
re.search(r'SBN\.VideoLinkset\.ooyala\([\'"](?P<ec>.{32})[\'"]\)', webpage) or
|
re.search(r'SBN\.VideoLinkset\.ooyala\([\'"](?P<ec>.{32})[\'"]\)', webpage) or
|
||||||
re.search(r'data-ooyala-video-id\s*=\s*[\'"](?P<ec>.{32})[\'"]', webpage))
|
re.search(r'data-ooyala-video-id\s*=\s*[\'"](?P<ec>.{32})[\'"]', webpage))
|
||||||
if mobj is not None:
|
if mobj is not None:
|
||||||
return OoyalaIE._build_url_result(mobj.group('ec'))
|
return OoyalaIE._build_url_result(smuggle_url(mobj.group('ec'), {'domain': url}))
|
||||||
|
|
||||||
# Look for multiple Ooyala embeds on SBN network websites
|
# Look for multiple Ooyala embeds on SBN network websites
|
||||||
mobj = re.search(r'SBN\.VideoLinkset\.entryGroup\((\[.*?\])', webpage)
|
mobj = re.search(r'SBN\.VideoLinkset\.entryGroup\((\[.*?\])', webpage)
|
||||||
@@ -1463,7 +1513,7 @@ class GenericIE(InfoExtractor):
|
|||||||
embeds = self._parse_json(mobj.group(1), video_id, fatal=False)
|
embeds = self._parse_json(mobj.group(1), video_id, fatal=False)
|
||||||
if embeds:
|
if embeds:
|
||||||
return _playlist_from_matches(
|
return _playlist_from_matches(
|
||||||
embeds, getter=lambda v: OoyalaIE._url_for_embed_code(v['provider_video_id']), ie='Ooyala')
|
embeds, getter=lambda v: OoyalaIE._url_for_embed_code(smuggle_url(v['provider_video_id'], {'domain': url})), ie='Ooyala')
|
||||||
|
|
||||||
# Look for Aparat videos
|
# Look for Aparat videos
|
||||||
mobj = re.search(r'<iframe .*?src="(http://www\.aparat\.com/video/[^"]+)"', webpage)
|
mobj = re.search(r'<iframe .*?src="(http://www\.aparat\.com/video/[^"]+)"', webpage)
|
||||||
@@ -1675,7 +1725,9 @@ class GenericIE(InfoExtractor):
|
|||||||
mobj = (re.search(r"(?s)kWidget\.(?:thumb)?[Ee]mbed\(\{.*?'wid'\s*:\s*'_?(?P<partner_id>[^']+)',.*?'entry_?[Ii]d'\s*:\s*'(?P<id>[^']+)',", webpage) or
|
mobj = (re.search(r"(?s)kWidget\.(?:thumb)?[Ee]mbed\(\{.*?'wid'\s*:\s*'_?(?P<partner_id>[^']+)',.*?'entry_?[Ii]d'\s*:\s*'(?P<id>[^']+)',", webpage) or
|
||||||
re.search(r'(?s)(?P<q1>["\'])(?:https?:)?//cdnapi(?:sec)?\.kaltura\.com/.*?(?:p|partner_id)/(?P<partner_id>\d+).*?(?P=q1).*?entry_?[Ii]d\s*:\s*(?P<q2>["\'])(?P<id>.+?)(?P=q2)', webpage))
|
re.search(r'(?s)(?P<q1>["\'])(?:https?:)?//cdnapi(?:sec)?\.kaltura\.com/.*?(?:p|partner_id)/(?P<partner_id>\d+).*?(?P=q1).*?entry_?[Ii]d\s*:\s*(?P<q2>["\'])(?P<id>.+?)(?P=q2)', webpage))
|
||||||
if mobj is not None:
|
if mobj is not None:
|
||||||
return self.url_result('kaltura:%(partner_id)s:%(id)s' % mobj.groupdict(), 'Kaltura')
|
return self.url_result(smuggle_url(
|
||||||
|
'kaltura:%(partner_id)s:%(id)s' % mobj.groupdict(),
|
||||||
|
{'source_url': url}), 'Kaltura')
|
||||||
|
|
||||||
# Look for Eagle.Platform embeds
|
# Look for Eagle.Platform embeds
|
||||||
mobj = re.search(
|
mobj = re.search(
|
||||||
@@ -1690,10 +1742,9 @@ class GenericIE(InfoExtractor):
|
|||||||
return self.url_result('eagleplatform:%(host)s:%(id)s' % mobj.groupdict(), 'EaglePlatform')
|
return self.url_result('eagleplatform:%(host)s:%(id)s' % mobj.groupdict(), 'EaglePlatform')
|
||||||
|
|
||||||
# Look for Pladform embeds
|
# Look for Pladform embeds
|
||||||
mobj = re.search(
|
pladform_url = PladformIE._extract_url(webpage)
|
||||||
r'<iframe[^>]+src="(?P<url>https?://out\.pladform\.ru/player\?.+?)"', webpage)
|
if pladform_url:
|
||||||
if mobj is not None:
|
return self.url_result(pladform_url)
|
||||||
return self.url_result(mobj.group('url'), 'Pladform')
|
|
||||||
|
|
||||||
# Look for Playwire embeds
|
# Look for Playwire embeds
|
||||||
mobj = re.search(
|
mobj = re.search(
|
||||||
@@ -1720,7 +1771,7 @@ class GenericIE(InfoExtractor):
|
|||||||
|
|
||||||
# Look for UDN embeds
|
# Look for UDN embeds
|
||||||
mobj = re.search(
|
mobj = re.search(
|
||||||
r'<iframe[^>]+src="(?P<url>%s)"' % UDNEmbedIE._VALID_URL, webpage)
|
r'<iframe[^>]+src="(?P<url>%s)"' % UDNEmbedIE._PROTOCOL_RELATIVE_VALID_URL, webpage)
|
||||||
if mobj is not None:
|
if mobj is not None:
|
||||||
return self.url_result(
|
return self.url_result(
|
||||||
compat_urlparse.urljoin(url, mobj.group('url')), 'UDNEmbed')
|
compat_urlparse.urljoin(url, mobj.group('url')), 'UDNEmbed')
|
||||||
@@ -1840,6 +1891,7 @@ class GenericIE(InfoExtractor):
|
|||||||
|
|
||||||
entries = []
|
entries = []
|
||||||
for video_url in found:
|
for video_url in found:
|
||||||
|
video_url = video_url.replace('\\/', '/')
|
||||||
video_url = compat_urlparse.urljoin(url, video_url)
|
video_url = compat_urlparse.urljoin(url, video_url)
|
||||||
video_id = compat_urllib_parse_unquote(os.path.basename(video_url))
|
video_id = compat_urllib_parse_unquote(os.path.basename(video_url))
|
||||||
|
|
||||||
@@ -1851,25 +1903,24 @@ class GenericIE(InfoExtractor):
|
|||||||
# here's a fun little line of code for you:
|
# here's a fun little line of code for you:
|
||||||
video_id = os.path.splitext(video_id)[0]
|
video_id = os.path.splitext(video_id)[0]
|
||||||
|
|
||||||
|
entry_info_dict = {
|
||||||
|
'id': video_id,
|
||||||
|
'uploader': video_uploader,
|
||||||
|
'title': video_title,
|
||||||
|
'age_limit': age_limit,
|
||||||
|
}
|
||||||
|
|
||||||
ext = determine_ext(video_url)
|
ext = determine_ext(video_url)
|
||||||
if ext == 'smil':
|
if ext == 'smil':
|
||||||
entries.append({
|
entry_info_dict['formats'] = self._extract_smil_formats(video_url, video_id)
|
||||||
'id': video_id,
|
|
||||||
'formats': self._extract_smil_formats(video_url, video_id),
|
|
||||||
'uploader': video_uploader,
|
|
||||||
'title': video_title,
|
|
||||||
'age_limit': age_limit,
|
|
||||||
})
|
|
||||||
elif ext == 'xspf':
|
elif ext == 'xspf':
|
||||||
return self.playlist_result(self._extract_xspf_playlist(video_url, video_id), video_id)
|
return self.playlist_result(self._extract_xspf_playlist(video_url, video_id), video_id)
|
||||||
|
elif ext == 'm3u8':
|
||||||
|
entry_info_dict['formats'] = self._extract_m3u8_formats(video_url, video_id, ext='mp4')
|
||||||
else:
|
else:
|
||||||
entries.append({
|
entry_info_dict['url'] = video_url
|
||||||
'id': video_id,
|
|
||||||
'url': video_url,
|
entries.append(entry_info_dict)
|
||||||
'uploader': video_uploader,
|
|
||||||
'title': video_title,
|
|
||||||
'age_limit': age_limit,
|
|
||||||
})
|
|
||||||
|
|
||||||
if len(entries) == 1:
|
if len(entries) == 1:
|
||||||
return entries[0]
|
return entries[0]
|
||||||
|
@@ -18,6 +18,8 @@ class GrouponIE(InfoExtractor):
|
|||||||
'id': 'tubGNycTo_9Uxg82uESj4i61EYX8nyuf',
|
'id': 'tubGNycTo_9Uxg82uESj4i61EYX8nyuf',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Bikram Yoga Huntington Beach | Orange County',
|
'title': 'Bikram Yoga Huntington Beach | Orange County',
|
||||||
|
'description': 'md5:d41d8cd98f00b204e9800998ecf8427e',
|
||||||
|
'duration': 44.961,
|
||||||
},
|
},
|
||||||
}],
|
}],
|
||||||
'params': {
|
'params': {
|
||||||
|
@@ -4,12 +4,10 @@ from __future__ import unicode_literals
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
from ..compat import compat_urlparse
|
||||||
compat_urllib_request,
|
|
||||||
compat_urlparse,
|
|
||||||
)
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
HEADRequest,
|
HEADRequest,
|
||||||
|
sanitized_Request,
|
||||||
str_to_int,
|
str_to_int,
|
||||||
urlencode_postdata,
|
urlencode_postdata,
|
||||||
urlhandle_detect_ext,
|
urlhandle_detect_ext,
|
||||||
@@ -47,7 +45,7 @@ class HearThisAtIE(InfoExtractor):
|
|||||||
r'intTrackId\s*=\s*(\d+)', webpage, 'track ID')
|
r'intTrackId\s*=\s*(\d+)', webpage, 'track ID')
|
||||||
|
|
||||||
payload = urlencode_postdata({'tracks[]': track_id})
|
payload = urlencode_postdata({'tracks[]': track_id})
|
||||||
req = compat_urllib_request.Request(self._PLAYLIST_URL, payload)
|
req = sanitized_Request(self._PLAYLIST_URL, payload)
|
||||||
req.add_header('Content-type', 'application/x-www-form-urlencoded')
|
req.add_header('Content-type', 'application/x-www-form-urlencoded')
|
||||||
|
|
||||||
track = self._download_json(req, track_id, 'Downloading playlist')[0]
|
track = self._download_json(req, track_id, 'Downloading playlist')[0]
|
||||||
|
@@ -3,13 +3,11 @@ from __future__ import unicode_literals
|
|||||||
import base64
|
import base64
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
from ..compat import compat_urllib_parse
|
||||||
compat_urllib_parse,
|
|
||||||
compat_urllib_request,
|
|
||||||
)
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
HEADRequest,
|
HEADRequest,
|
||||||
|
sanitized_Request,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -41,7 +39,7 @@ class HotNewHipHopIE(InfoExtractor):
|
|||||||
('mediaType', 's'),
|
('mediaType', 's'),
|
||||||
('mediaId', video_id),
|
('mediaId', video_id),
|
||||||
])
|
])
|
||||||
r = compat_urllib_request.Request(
|
r = sanitized_Request(
|
||||||
'http://www.hotnewhiphop.com/ajax/media/getActions/', data=reqdata)
|
'http://www.hotnewhiphop.com/ajax/media/getActions/', data=reqdata)
|
||||||
r.add_header('Content-Type', 'application/x-www-form-urlencoded')
|
r.add_header('Content-Type', 'application/x-www-form-urlencoded')
|
||||||
mkd = self._download_json(
|
mkd = self._download_json(
|
||||||
|
@@ -16,6 +16,7 @@ class HowcastIE(InfoExtractor):
|
|||||||
'description': 'md5:dbe792e5f6f1489027027bf2eba188a3',
|
'description': 'md5:dbe792e5f6f1489027027bf2eba188a3',
|
||||||
'timestamp': 1276081287,
|
'timestamp': 1276081287,
|
||||||
'upload_date': '20100609',
|
'upload_date': '20100609',
|
||||||
|
'duration': 56.823,
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
# m3u8 download
|
# m3u8 download
|
||||||
|
@@ -4,12 +4,10 @@ import json
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
from ..compat import compat_urllib_parse
|
||||||
compat_urllib_parse,
|
|
||||||
compat_urllib_request,
|
|
||||||
)
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
|
sanitized_Request,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -30,15 +28,12 @@ class HypemIE(InfoExtractor):
|
|||||||
track_id = self._match_id(url)
|
track_id = self._match_id(url)
|
||||||
|
|
||||||
data = {'ax': 1, 'ts': time.time()}
|
data = {'ax': 1, 'ts': time.time()}
|
||||||
data_encoded = compat_urllib_parse.urlencode(data)
|
request = sanitized_Request(url + '?' + compat_urllib_parse.urlencode(data))
|
||||||
complete_url = url + "?" + data_encoded
|
|
||||||
request = compat_urllib_request.Request(complete_url)
|
|
||||||
response, urlh = self._download_webpage_handle(
|
response, urlh = self._download_webpage_handle(
|
||||||
request, track_id, 'Downloading webpage with the url')
|
request, track_id, 'Downloading webpage with the url')
|
||||||
cookie = urlh.headers.get('Set-Cookie', '')
|
|
||||||
|
|
||||||
html_tracks = self._html_search_regex(
|
html_tracks = self._html_search_regex(
|
||||||
r'(?ms)<script type="application/json" id="displayList-data">\s*(.*?)\s*</script>',
|
r'(?ms)<script type="application/json" id="displayList-data">(.+?)</script>',
|
||||||
response, 'tracks')
|
response, 'tracks')
|
||||||
try:
|
try:
|
||||||
track_list = json.loads(html_tracks)
|
track_list = json.loads(html_tracks)
|
||||||
@@ -48,15 +43,14 @@ class HypemIE(InfoExtractor):
|
|||||||
|
|
||||||
key = track['key']
|
key = track['key']
|
||||||
track_id = track['id']
|
track_id = track['id']
|
||||||
artist = track['artist']
|
|
||||||
title = track['song']
|
title = track['song']
|
||||||
|
|
||||||
serve_url = "http://hypem.com/serve/source/%s/%s" % (track_id, key)
|
request = sanitized_Request(
|
||||||
request = compat_urllib_request.Request(
|
'http://hypem.com/serve/source/%s/%s' % (track_id, key),
|
||||||
serve_url, '', {'Content-Type': 'application/json'})
|
'', {'Content-Type': 'application/json'})
|
||||||
request.add_header('cookie', cookie)
|
|
||||||
song_data = self._download_json(request, track_id, 'Downloading metadata')
|
song_data = self._download_json(request, track_id, 'Downloading metadata')
|
||||||
final_url = song_data["url"]
|
final_url = song_data['url']
|
||||||
|
artist = track.get('artist')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': track_id,
|
'id': track_id,
|
||||||
|
@@ -3,10 +3,7 @@ from __future__ import unicode_literals
|
|||||||
import base64
|
import base64
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
from ..compat import compat_urllib_parse_unquote
|
||||||
compat_urllib_parse_unquote,
|
|
||||||
compat_urlparse,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class InfoQIE(InfoExtractor):
|
class InfoQIE(InfoExtractor):
|
||||||
@@ -45,9 +42,11 @@ class InfoQIE(InfoExtractor):
|
|||||||
video_filename = playpath.split('/')[-1]
|
video_filename = playpath.split('/')[-1]
|
||||||
video_id, extension = video_filename.split('.')
|
video_id, extension = video_filename.split('.')
|
||||||
|
|
||||||
http_base = self._search_regex(
|
http_video_url = self._search_regex(r'P\.s\s*=\s*\'([^\']+)\'', webpage, 'video URL')
|
||||||
r'EXPRESSINSTALL_SWF\s*=\s*[^"]*"((?:https?:)?//[^/"]+/)', webpage,
|
|
||||||
'HTTP base URL')
|
policy = self._search_regex(r'InfoQConstants.scp\s*=\s*\'([^\']+)\'', webpage, 'policy')
|
||||||
|
signature = self._search_regex(r'InfoQConstants.scs\s*=\s*\'([^\']+)\'', webpage, 'signature')
|
||||||
|
key_pair_id = self._search_regex(r'InfoQConstants.sck\s*=\s*\'([^\']+)\'', webpage, 'key-pair-id')
|
||||||
|
|
||||||
formats = [{
|
formats = [{
|
||||||
'format_id': 'rtmp',
|
'format_id': 'rtmp',
|
||||||
@@ -56,7 +55,11 @@ class InfoQIE(InfoExtractor):
|
|||||||
'play_path': playpath,
|
'play_path': playpath,
|
||||||
}, {
|
}, {
|
||||||
'format_id': 'http',
|
'format_id': 'http',
|
||||||
'url': compat_urlparse.urljoin(url, http_base) + real_id,
|
'url': http_video_url,
|
||||||
|
'http_headers': {
|
||||||
|
'Cookie': 'CloudFront-Policy=%s; CloudFront-Signature=%s; CloudFront-Key-Pair-Id=%s' % (
|
||||||
|
policy, signature, key_pair_id),
|
||||||
|
},
|
||||||
}]
|
}]
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
|
||||||
|
@@ -10,8 +10,8 @@ from ..utils import (
|
|||||||
|
|
||||||
|
|
||||||
class InstagramIE(InfoExtractor):
|
class InstagramIE(InfoExtractor):
|
||||||
_VALID_URL = r'https://instagram\.com/p/(?P<id>[\da-zA-Z]+)'
|
_VALID_URL = r'https?://(?:www\.)?instagram\.com/p/(?P<id>[^/?#&]+)'
|
||||||
_TEST = {
|
_TESTS = [{
|
||||||
'url': 'https://instagram.com/p/aye83DjauH/?foo=bar#abc',
|
'url': 'https://instagram.com/p/aye83DjauH/?foo=bar#abc',
|
||||||
'md5': '0d2da106a9d2631273e192b372806516',
|
'md5': '0d2da106a9d2631273e192b372806516',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
@@ -21,7 +21,10 @@ class InstagramIE(InfoExtractor):
|
|||||||
'title': 'Video by naomipq',
|
'title': 'Video by naomipq',
|
||||||
'description': 'md5:1f17f0ab29bd6fe2bfad705f58de3cb8',
|
'description': 'md5:1f17f0ab29bd6fe2bfad705f58de3cb8',
|
||||||
}
|
}
|
||||||
}
|
}, {
|
||||||
|
'url': 'https://instagram.com/p/-Cmh1cukG2/',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
|
@@ -6,12 +6,10 @@ from random import random
|
|||||||
from math import floor
|
from math import floor
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
|
||||||
compat_urllib_request,
|
|
||||||
)
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
remove_end,
|
remove_end,
|
||||||
|
sanitized_Request,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -61,7 +59,7 @@ class IPrimaIE(InfoExtractor):
|
|||||||
(floor(random() * 1073741824), floor(random() * 1073741824))
|
(floor(random() * 1073741824), floor(random() * 1073741824))
|
||||||
)
|
)
|
||||||
|
|
||||||
req = compat_urllib_request.Request(player_url)
|
req = sanitized_Request(player_url)
|
||||||
req.add_header('Referer', url)
|
req.add_header('Referer', url)
|
||||||
playerpage = self._download_webpage(req, video_id)
|
playerpage = self._download_webpage(req, video_id)
|
||||||
|
|
||||||
|
@@ -205,9 +205,8 @@ class IqiyiIE(InfoExtractor):
|
|||||||
|
|
||||||
def get_enc_key(self, swf_url, video_id):
|
def get_enc_key(self, swf_url, video_id):
|
||||||
# TODO: automatic key extraction
|
# TODO: automatic key extraction
|
||||||
# last update at 2015-10-22 for Zombie::bite
|
# last update at 2015-12-06 for Zombie::bite
|
||||||
# '7223c67061dbea1259d0ceb44f44b6d62288f4f80c972170de5201d2321060270e05'[2:66][0::2]
|
enc_key = '3719f6a1da83ee0aee3488d8802d7696'[::-1]
|
||||||
enc_key = '2c76de15dcb44bd28ff0927d50d31620'
|
|
||||||
return enc_key
|
return enc_key
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
|
@@ -5,11 +5,9 @@ import re
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
|
||||||
compat_urllib_request,
|
|
||||||
)
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
|
sanitized_Request,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -78,7 +76,7 @@ class IviIE(InfoExtractor):
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
request = compat_urllib_request.Request(api_url, json.dumps(data))
|
request = sanitized_Request(api_url, json.dumps(data))
|
||||||
|
|
||||||
video_json_page = self._download_webpage(
|
video_json_page = self._download_webpage(
|
||||||
request, video_id, 'Downloading video JSON')
|
request, video_id, 'Downloading video JSON')
|
||||||
|
@@ -2,12 +2,18 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
import base64
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import compat_urllib_parse
|
from ..compat import (
|
||||||
|
compat_urllib_parse,
|
||||||
|
compat_urlparse,
|
||||||
|
)
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
|
clean_html,
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
|
unsmuggle_url,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -121,31 +127,47 @@ class KalturaIE(InfoExtractor):
|
|||||||
video_id, actions, note='Downloading video info JSON')
|
video_id, actions, note='Downloading video info JSON')
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
|
url, smuggled_data = unsmuggle_url(url, {})
|
||||||
|
|
||||||
mobj = re.match(self._VALID_URL, url)
|
mobj = re.match(self._VALID_URL, url)
|
||||||
partner_id = mobj.group('partner_id_s') or mobj.group('partner_id') or mobj.group('partner_id_html5')
|
partner_id = mobj.group('partner_id_s') or mobj.group('partner_id') or mobj.group('partner_id_html5')
|
||||||
entry_id = mobj.group('id_s') or mobj.group('id') or mobj.group('id_html5')
|
entry_id = mobj.group('id_s') or mobj.group('id') or mobj.group('id_html5')
|
||||||
|
|
||||||
info, source_data = self._get_video_info(entry_id, partner_id)
|
info, source_data = self._get_video_info(entry_id, partner_id)
|
||||||
|
|
||||||
formats = [{
|
source_url = smuggled_data.get('source_url')
|
||||||
'format_id': '%(fileExt)s-%(bitrate)s' % f,
|
if source_url:
|
||||||
'ext': f['fileExt'],
|
referrer = base64.b64encode(
|
||||||
'tbr': f['bitrate'],
|
'://'.join(compat_urlparse.urlparse(source_url)[:2])
|
||||||
'fps': f.get('frameRate'),
|
.encode('utf-8')).decode('utf-8')
|
||||||
'filesize_approx': int_or_none(f.get('size'), invscale=1024),
|
else:
|
||||||
'container': f.get('containerFormat'),
|
referrer = None
|
||||||
'vcodec': f.get('videoCodecId'),
|
|
||||||
'height': f.get('height'),
|
formats = []
|
||||||
'width': f.get('width'),
|
for f in source_data['flavorAssets']:
|
||||||
'url': '%s/flavorId/%s' % (info['dataUrl'], f['id']),
|
video_url = '%s/flavorId/%s' % (info['dataUrl'], f['id'])
|
||||||
} for f in source_data['flavorAssets']]
|
if referrer:
|
||||||
|
video_url += '?referrer=%s' % referrer
|
||||||
|
formats.append({
|
||||||
|
'format_id': '%(fileExt)s-%(bitrate)s' % f,
|
||||||
|
'ext': f.get('fileExt'),
|
||||||
|
'tbr': int_or_none(f['bitrate']),
|
||||||
|
'fps': int_or_none(f.get('frameRate')),
|
||||||
|
'filesize_approx': int_or_none(f.get('size'), invscale=1024),
|
||||||
|
'container': f.get('containerFormat'),
|
||||||
|
'vcodec': f.get('videoCodecId'),
|
||||||
|
'height': int_or_none(f.get('height')),
|
||||||
|
'width': int_or_none(f.get('width')),
|
||||||
|
'url': video_url,
|
||||||
|
})
|
||||||
|
self._check_formats(formats, entry_id)
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': entry_id,
|
'id': entry_id,
|
||||||
'title': info['name'],
|
'title': info['name'],
|
||||||
'formats': formats,
|
'formats': formats,
|
||||||
'description': info.get('description'),
|
'description': clean_html(info.get('description')),
|
||||||
'thumbnail': info.get('thumbnailUrl'),
|
'thumbnail': info.get('thumbnailUrl'),
|
||||||
'duration': info.get('duration'),
|
'duration': info.get('duration'),
|
||||||
'timestamp': info.get('createdAt'),
|
'timestamp': info.get('createdAt'),
|
||||||
|
@@ -1,12 +1,11 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import os
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
from ..utils import (
|
||||||
compat_urllib_parse_urlparse,
|
sanitized_Request,
|
||||||
compat_urllib_request,
|
url_basename,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -14,19 +13,20 @@ class KeezMoviesIE(InfoExtractor):
|
|||||||
_VALID_URL = r'https?://(?:www\.)?keezmovies\.com/video/.+?(?P<id>[0-9]+)(?:[/?&]|$)'
|
_VALID_URL = r'https?://(?:www\.)?keezmovies\.com/video/.+?(?P<id>[0-9]+)(?:[/?&]|$)'
|
||||||
_TEST = {
|
_TEST = {
|
||||||
'url': 'http://www.keezmovies.com/video/petite-asian-lady-mai-playing-in-bathtub-1214711',
|
'url': 'http://www.keezmovies.com/video/petite-asian-lady-mai-playing-in-bathtub-1214711',
|
||||||
'md5': '6e297b7e789329923fcf83abb67c9289',
|
'md5': '1c1e75d22ffa53320f45eeb07bc4cdc0',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '1214711',
|
'id': '1214711',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Petite Asian Lady Mai Playing In Bathtub',
|
'title': 'Petite Asian Lady Mai Playing In Bathtub',
|
||||||
'age_limit': 18,
|
'age_limit': 18,
|
||||||
|
'thumbnail': 're:^https?://.*\.jpg$',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
|
|
||||||
req = compat_urllib_request.Request(url)
|
req = sanitized_Request(url)
|
||||||
req.add_header('Cookie', 'age_verified=1')
|
req.add_header('Cookie', 'age_verified=1')
|
||||||
webpage = self._download_webpage(req, video_id)
|
webpage = self._download_webpage(req, video_id)
|
||||||
|
|
||||||
@@ -38,21 +38,29 @@ class KeezMoviesIE(InfoExtractor):
|
|||||||
|
|
||||||
video_title = self._html_search_regex(
|
video_title = self._html_search_regex(
|
||||||
r'<h1 [^>]*>([^<]+)', webpage, 'title')
|
r'<h1 [^>]*>([^<]+)', webpage, 'title')
|
||||||
video_url = self._html_search_regex(
|
flashvars = self._parse_json(self._search_regex(
|
||||||
r'(?s)html5VideoPlayer = .*?src="([^"]+)"', webpage, 'video URL')
|
r'var\s+flashvars\s*=\s*([^;]+);', webpage, 'flashvars'), video_id)
|
||||||
path = compat_urllib_parse_urlparse(video_url).path
|
|
||||||
extension = os.path.splitext(path)[1][1:]
|
formats = []
|
||||||
format = path.split('/')[4].split('_')[:2]
|
for height in (180, 240, 480):
|
||||||
format = "-".join(format)
|
if flashvars.get('quality_%dp' % height):
|
||||||
|
video_url = flashvars['quality_%dp' % height]
|
||||||
|
a_format = {
|
||||||
|
'url': video_url,
|
||||||
|
'height': height,
|
||||||
|
'format_id': '%dp' % height,
|
||||||
|
}
|
||||||
|
filename_parts = url_basename(video_url).split('_')
|
||||||
|
if len(filename_parts) >= 2 and re.match(r'\d+[Kk]', filename_parts[1]):
|
||||||
|
a_format['tbr'] = int(filename_parts[1][:-1])
|
||||||
|
formats.append(a_format)
|
||||||
|
|
||||||
age_limit = self._rta_search(webpage)
|
age_limit = self._rta_search(webpage)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
'title': video_title,
|
'title': video_title,
|
||||||
'url': video_url,
|
'formats': formats,
|
||||||
'ext': extension,
|
|
||||||
'format': format,
|
|
||||||
'format_id': format,
|
|
||||||
'age_limit': age_limit,
|
'age_limit': age_limit,
|
||||||
|
'thumbnail': flashvars.get('image_url')
|
||||||
}
|
}
|
||||||
|
@@ -8,13 +8,13 @@ import time
|
|||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
from ..compat import (
|
||||||
compat_urllib_parse,
|
compat_urllib_parse,
|
||||||
compat_urllib_request,
|
|
||||||
compat_ord,
|
compat_ord,
|
||||||
)
|
)
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
determine_ext,
|
determine_ext,
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
parse_iso8601,
|
parse_iso8601,
|
||||||
|
sanitized_Request,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
encode_data_uri,
|
encode_data_uri,
|
||||||
)
|
)
|
||||||
@@ -114,7 +114,7 @@ class LetvIE(InfoExtractor):
|
|||||||
'tkey': self.calc_time_key(int(time.time())),
|
'tkey': self.calc_time_key(int(time.time())),
|
||||||
'domain': 'www.letv.com'
|
'domain': 'www.letv.com'
|
||||||
}
|
}
|
||||||
play_json_req = compat_urllib_request.Request(
|
play_json_req = sanitized_Request(
|
||||||
'http://api.letv.com/mms/out/video/playJson?' + compat_urllib_parse.urlencode(params)
|
'http://api.letv.com/mms/out/video/playJson?' + compat_urllib_parse.urlencode(params)
|
||||||
)
|
)
|
||||||
cn_verification_proxy = self._downloader.params.get('cn_verification_proxy')
|
cn_verification_proxy = self._downloader.params.get('cn_verification_proxy')
|
||||||
|
@@ -7,12 +7,12 @@ from .common import InfoExtractor
|
|||||||
from ..compat import (
|
from ..compat import (
|
||||||
compat_str,
|
compat_str,
|
||||||
compat_urllib_parse,
|
compat_urllib_parse,
|
||||||
compat_urllib_request,
|
|
||||||
)
|
)
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
clean_html,
|
clean_html,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
|
sanitized_Request,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ class LyndaBaseIE(InfoExtractor):
|
|||||||
self._login()
|
self._login()
|
||||||
|
|
||||||
def _login(self):
|
def _login(self):
|
||||||
(username, password) = self._get_login_info()
|
username, password = self._get_login_info()
|
||||||
if username is None:
|
if username is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ class LyndaBaseIE(InfoExtractor):
|
|||||||
'remember': 'false',
|
'remember': 'false',
|
||||||
'stayPut': 'false'
|
'stayPut': 'false'
|
||||||
}
|
}
|
||||||
request = compat_urllib_request.Request(
|
request = sanitized_Request(
|
||||||
self._LOGIN_URL, compat_urllib_parse.urlencode(login_form).encode('utf-8'))
|
self._LOGIN_URL, compat_urllib_parse.urlencode(login_form).encode('utf-8'))
|
||||||
login_page = self._download_webpage(
|
login_page = self._download_webpage(
|
||||||
request, None, 'Logging in as %s' % username)
|
request, None, 'Logging in as %s' % username)
|
||||||
@@ -64,7 +64,7 @@ class LyndaBaseIE(InfoExtractor):
|
|||||||
'remember': 'false',
|
'remember': 'false',
|
||||||
'stayPut': 'false',
|
'stayPut': 'false',
|
||||||
}
|
}
|
||||||
request = compat_urllib_request.Request(
|
request = sanitized_Request(
|
||||||
self._LOGIN_URL, compat_urllib_parse.urlencode(confirm_form).encode('utf-8'))
|
self._LOGIN_URL, compat_urllib_parse.urlencode(confirm_form).encode('utf-8'))
|
||||||
login_page = self._download_webpage(
|
login_page = self._download_webpage(
|
||||||
request, None,
|
request, None,
|
||||||
@@ -83,6 +83,10 @@ class LyndaBaseIE(InfoExtractor):
|
|||||||
raise ExtractorError('Unable to log in')
|
raise ExtractorError('Unable to log in')
|
||||||
|
|
||||||
def _logout(self):
|
def _logout(self):
|
||||||
|
username, _ = self._get_login_info()
|
||||||
|
if username is None:
|
||||||
|
return
|
||||||
|
|
||||||
self._download_webpage(
|
self._download_webpage(
|
||||||
'http://www.lynda.com/ajax/logout.aspx', None,
|
'http://www.lynda.com/ajax/logout.aspx', None,
|
||||||
'Logging out', 'Unable to log out', fatal=False)
|
'Logging out', 'Unable to log out', fatal=False)
|
||||||
|
@@ -7,12 +7,12 @@ from ..compat import (
|
|||||||
compat_parse_qs,
|
compat_parse_qs,
|
||||||
compat_urllib_parse,
|
compat_urllib_parse,
|
||||||
compat_urllib_parse_unquote,
|
compat_urllib_parse_unquote,
|
||||||
compat_urllib_request,
|
|
||||||
)
|
)
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
determine_ext,
|
determine_ext,
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
|
sanitized_Request,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -117,7 +117,7 @@ class MetacafeIE(InfoExtractor):
|
|||||||
'filters': '0',
|
'filters': '0',
|
||||||
'submit': "Continue - I'm over 18",
|
'submit': "Continue - I'm over 18",
|
||||||
}
|
}
|
||||||
request = compat_urllib_request.Request(self._FILTER_POST, compat_urllib_parse.urlencode(disclaimer_form))
|
request = sanitized_Request(self._FILTER_POST, compat_urllib_parse.urlencode(disclaimer_form))
|
||||||
request.add_header('Content-Type', 'application/x-www-form-urlencoded')
|
request.add_header('Content-Type', 'application/x-www-form-urlencoded')
|
||||||
self.report_age_confirmation()
|
self.report_age_confirmation()
|
||||||
self._download_webpage(request, None, False, 'Unable to confirm age')
|
self._download_webpage(request, None, False, 'Unable to confirm age')
|
||||||
@@ -142,7 +142,7 @@ class MetacafeIE(InfoExtractor):
|
|||||||
return self.url_result('theplatform:%s' % ext_id, 'ThePlatform')
|
return self.url_result('theplatform:%s' % ext_id, 'ThePlatform')
|
||||||
|
|
||||||
# Retrieve video webpage to extract further information
|
# Retrieve video webpage to extract further information
|
||||||
req = compat_urllib_request.Request('http://www.metacafe.com/watch/%s/' % video_id)
|
req = sanitized_Request('http://www.metacafe.com/watch/%s/' % video_id)
|
||||||
|
|
||||||
# AnyClip videos require the flashversion cookie so that we get the link
|
# AnyClip videos require the flashversion cookie so that we get the link
|
||||||
# to the mp4 file
|
# to the mp4 file
|
||||||
@@ -154,10 +154,10 @@ class MetacafeIE(InfoExtractor):
|
|||||||
# Extract URL, uploader and title from webpage
|
# Extract URL, uploader and title from webpage
|
||||||
self.report_extraction(video_id)
|
self.report_extraction(video_id)
|
||||||
video_url = None
|
video_url = None
|
||||||
mobj = re.search(r'(?m)&mediaURL=([^&]+)', webpage)
|
mobj = re.search(r'(?m)&(?:media|video)URL=([^&]+)', webpage)
|
||||||
if mobj is not None:
|
if mobj is not None:
|
||||||
mediaURL = compat_urllib_parse_unquote(mobj.group(1))
|
mediaURL = compat_urllib_parse_unquote(mobj.group(1))
|
||||||
video_ext = mediaURL[-3:]
|
video_ext = determine_ext(mediaURL)
|
||||||
|
|
||||||
# Extract gdaKey if available
|
# Extract gdaKey if available
|
||||||
mobj = re.search(r'(?m)&gdaKey=(.*?)&', webpage)
|
mobj = re.search(r'(?m)&gdaKey=(.*?)&', webpage)
|
||||||
@@ -229,7 +229,7 @@ class MetacafeIE(InfoExtractor):
|
|||||||
|
|
||||||
age_limit = (
|
age_limit = (
|
||||||
18
|
18
|
||||||
if re.search(r'"contentRating":"restricted"', webpage)
|
if re.search(r'(?:"contentRating":|"rating",)"restricted"', webpage)
|
||||||
else 0)
|
else 0)
|
||||||
|
|
||||||
if isinstance(video_url, list):
|
if isinstance(video_url, list):
|
||||||
|
@@ -2,14 +2,12 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
from ..compat import compat_urllib_parse
|
||||||
compat_urllib_parse,
|
|
||||||
compat_urllib_request,
|
|
||||||
)
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
int_or_none,
|
int_or_none,
|
||||||
parse_duration,
|
parse_duration,
|
||||||
parse_filesize,
|
parse_filesize,
|
||||||
|
sanitized_Request,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -39,7 +37,7 @@ class MinhatecaIE(InfoExtractor):
|
|||||||
('fileId', video_id),
|
('fileId', video_id),
|
||||||
('__RequestVerificationToken', token),
|
('__RequestVerificationToken', token),
|
||||||
]
|
]
|
||||||
req = compat_urllib_request.Request(
|
req = sanitized_Request(
|
||||||
'http://minhateca.com.br/action/License/Download',
|
'http://minhateca.com.br/action/License/Download',
|
||||||
data=compat_urllib_parse.urlencode(token_data))
|
data=compat_urllib_parse.urlencode(token_data))
|
||||||
req.add_header('Content-Type', 'application/x-www-form-urlencoded')
|
req.add_header('Content-Type', 'application/x-www-form-urlencoded')
|
||||||
|
@@ -4,11 +4,11 @@ from __future__ import unicode_literals
|
|||||||
import random
|
import random
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import compat_urllib_request
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
xpath_text,
|
xpath_text,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
|
sanitized_Request,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ class MioMioIE(InfoExtractor):
|
|||||||
'http://www.miomio.tv/mioplayer/mioplayerconfigfiles/xml.php?id=%s&r=%s' % (id, random.randint(100, 999)),
|
'http://www.miomio.tv/mioplayer/mioplayerconfigfiles/xml.php?id=%s&r=%s' % (id, random.randint(100, 999)),
|
||||||
video_id)
|
video_id)
|
||||||
|
|
||||||
vid_config_request = compat_urllib_request.Request(
|
vid_config_request = sanitized_Request(
|
||||||
'http://www.miomio.tv/mioplayer/mioplayerconfigfiles/sina.php?{0}'.format(xml_config),
|
'http://www.miomio.tv/mioplayer/mioplayerconfigfiles/sina.php?{0}'.format(xml_config),
|
||||||
headers=http_headers)
|
headers=http_headers)
|
||||||
|
|
||||||
|
@@ -64,7 +64,8 @@ class MixcloudIE(InfoExtractor):
|
|||||||
|
|
||||||
preview_url = self._search_regex(
|
preview_url = self._search_regex(
|
||||||
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 = re.sub(r'audiocdn(\d+)', r'stream\1', preview_url)
|
||||||
|
song_url = song_url.replace('/previews/', '/c/originals/')
|
||||||
if not self._check_url(song_url, track_id, 'mp3'):
|
if not self._check_url(song_url, track_id, 'mp3'):
|
||||||
song_url = song_url.replace('.mp3', '.m4a').replace('originals/', 'm4a/64/')
|
song_url = song_url.replace('.mp3', '.m4a').replace('originals/', 'm4a/64/')
|
||||||
if not self._check_url(song_url, track_id, 'm4a'):
|
if not self._check_url(song_url, track_id, 'm4a'):
|
||||||
|
@@ -5,13 +5,11 @@ import json
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
from ..compat import compat_urllib_parse
|
||||||
compat_urllib_parse,
|
|
||||||
compat_urllib_request,
|
|
||||||
)
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
|
sanitized_Request,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -80,7 +78,7 @@ class MoeVideoIE(InfoExtractor):
|
|||||||
]
|
]
|
||||||
r_json = json.dumps(r)
|
r_json = json.dumps(r)
|
||||||
post = compat_urllib_parse.urlencode({'r': r_json})
|
post = compat_urllib_parse.urlencode({'r': r_json})
|
||||||
req = compat_urllib_request.Request(self._API_URL, post)
|
req = sanitized_Request(self._API_URL, post)
|
||||||
req.add_header('Content-type', 'application/x-www-form-urlencoded')
|
req.add_header('Content-type', 'application/x-www-form-urlencoded')
|
||||||
|
|
||||||
response = self._download_json(req, video_id)
|
response = self._download_json(req, video_id)
|
||||||
|
@@ -7,8 +7,8 @@ from .common import InfoExtractor
|
|||||||
from ..compat import (
|
from ..compat import (
|
||||||
compat_urllib_parse_unquote,
|
compat_urllib_parse_unquote,
|
||||||
compat_urllib_parse_urlparse,
|
compat_urllib_parse_urlparse,
|
||||||
compat_urllib_request,
|
|
||||||
)
|
)
|
||||||
|
from ..utils import sanitized_Request
|
||||||
|
|
||||||
|
|
||||||
class MofosexIE(InfoExtractor):
|
class MofosexIE(InfoExtractor):
|
||||||
@@ -29,7 +29,7 @@ class MofosexIE(InfoExtractor):
|
|||||||
video_id = mobj.group('id')
|
video_id = mobj.group('id')
|
||||||
url = 'http://www.' + mobj.group('url')
|
url = 'http://www.' + mobj.group('url')
|
||||||
|
|
||||||
req = compat_urllib_request.Request(url)
|
req = sanitized_Request(url)
|
||||||
req.add_header('Cookie', 'age_verified=1')
|
req.add_header('Cookie', 'age_verified=1')
|
||||||
webpage = self._download_webpage(req, video_id)
|
webpage = self._download_webpage(req, video_id)
|
||||||
|
|
||||||
|
@@ -5,13 +5,11 @@ import os.path
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
from ..compat import compat_urllib_parse
|
||||||
compat_urllib_parse,
|
|
||||||
compat_urllib_request,
|
|
||||||
)
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
remove_start,
|
remove_start,
|
||||||
|
sanitized_Request,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -81,7 +79,7 @@ class MonikerIE(InfoExtractor):
|
|||||||
orig_webpage, 'builtin URL', default=None, group='url')
|
orig_webpage, 'builtin URL', default=None, group='url')
|
||||||
|
|
||||||
if builtin_url:
|
if builtin_url:
|
||||||
req = compat_urllib_request.Request(builtin_url)
|
req = sanitized_Request(builtin_url)
|
||||||
req.add_header('Referer', url)
|
req.add_header('Referer', url)
|
||||||
webpage = self._download_webpage(req, video_id, 'Downloading builtin page')
|
webpage = self._download_webpage(req, video_id, 'Downloading builtin page')
|
||||||
title = self._og_search_title(orig_webpage).strip()
|
title = self._og_search_title(orig_webpage).strip()
|
||||||
@@ -94,7 +92,7 @@ class MonikerIE(InfoExtractor):
|
|||||||
headers = {
|
headers = {
|
||||||
b'Content-Type': b'application/x-www-form-urlencoded',
|
b'Content-Type': b'application/x-www-form-urlencoded',
|
||||||
}
|
}
|
||||||
req = compat_urllib_request.Request(url, post, headers)
|
req = sanitized_Request(url, post, headers)
|
||||||
webpage = self._download_webpage(
|
webpage = self._download_webpage(
|
||||||
req, video_id, note='Downloading video page ...')
|
req, video_id, note='Downloading video page ...')
|
||||||
|
|
||||||
|
@@ -3,12 +3,10 @@ from __future__ import unicode_literals
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
from ..compat import compat_urllib_parse
|
||||||
compat_urllib_request,
|
|
||||||
compat_urllib_parse,
|
|
||||||
)
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
|
sanitized_Request,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -59,7 +57,7 @@ class MooshareIE(InfoExtractor):
|
|||||||
'hash': hash_key,
|
'hash': hash_key,
|
||||||
}
|
}
|
||||||
|
|
||||||
request = compat_urllib_request.Request(
|
request = sanitized_Request(
|
||||||
'http://mooshare.biz/%s' % video_id, compat_urllib_parse.urlencode(download_form))
|
'http://mooshare.biz/%s' % video_id, compat_urllib_parse.urlencode(download_form))
|
||||||
request.add_header('Content-Type', 'application/x-www-form-urlencoded')
|
request.add_header('Content-Type', 'application/x-www-form-urlencoded')
|
||||||
|
|
||||||
|
@@ -2,9 +2,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
from ..utils import sanitized_Request
|
||||||
compat_urllib_request,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class MovieClipsIE(InfoExtractor):
|
class MovieClipsIE(InfoExtractor):
|
||||||
@@ -25,7 +23,7 @@ class MovieClipsIE(InfoExtractor):
|
|||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
display_id = self._match_id(url)
|
display_id = self._match_id(url)
|
||||||
|
|
||||||
req = compat_urllib_request.Request(url)
|
req = sanitized_Request(url)
|
||||||
# it doesn't work if it thinks the browser it's too old
|
# it doesn't work if it thinks the browser it's too old
|
||||||
req.add_header('User-Agent', 'Mozilla/5.0 (X11; Linux x86_64; rv:10.0) Gecko/20150101 Firefox/43.0 (Chrome)')
|
req.add_header('User-Agent', 'Mozilla/5.0 (X11; Linux x86_64; rv:10.0) Gecko/20150101 Firefox/43.0 (Chrome)')
|
||||||
webpage = self._download_webpage(req, display_id)
|
webpage = self._download_webpage(req, display_id)
|
||||||
|
@@ -1,27 +0,0 @@
|
|||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from .novamov import NovaMovIE
|
|
||||||
|
|
||||||
|
|
||||||
class MovShareIE(NovaMovIE):
|
|
||||||
IE_NAME = 'movshare'
|
|
||||||
IE_DESC = 'MovShare'
|
|
||||||
|
|
||||||
_VALID_URL = NovaMovIE._VALID_URL_TEMPLATE % {'host': 'movshare\.(?:net|sx|ag)'}
|
|
||||||
|
|
||||||
_HOST = 'www.movshare.net'
|
|
||||||
|
|
||||||
_FILE_DELETED_REGEX = r'>This file no longer exists on our servers.<'
|
|
||||||
_TITLE_REGEX = r'<strong>Title:</strong> ([^<]+)</p>'
|
|
||||||
_DESCRIPTION_REGEX = r'<strong>Description:</strong> ([^<]+)</p>'
|
|
||||||
|
|
||||||
_TEST = {
|
|
||||||
'url': 'http://www.movshare.net/video/559e28be54d96',
|
|
||||||
'md5': 'abd31a2132947262c50429e1d16c1bfd',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '559e28be54d96',
|
|
||||||
'ext': 'flv',
|
|
||||||
'title': 'dissapeared image',
|
|
||||||
'description': 'optical illusion dissapeared image magic illusion',
|
|
||||||
}
|
|
||||||
}
|
|
@@ -5,7 +5,6 @@ import re
|
|||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
from ..compat import (
|
||||||
compat_urllib_parse,
|
compat_urllib_parse,
|
||||||
compat_urllib_request,
|
|
||||||
compat_str,
|
compat_str,
|
||||||
)
|
)
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
@@ -13,6 +12,7 @@ from ..utils import (
|
|||||||
find_xpath_attr,
|
find_xpath_attr,
|
||||||
fix_xml_ampersands,
|
fix_xml_ampersands,
|
||||||
HEADRequest,
|
HEADRequest,
|
||||||
|
sanitized_Request,
|
||||||
unescapeHTML,
|
unescapeHTML,
|
||||||
url_basename,
|
url_basename,
|
||||||
RegexNotFoundError,
|
RegexNotFoundError,
|
||||||
@@ -53,7 +53,7 @@ class MTVServicesInfoExtractor(InfoExtractor):
|
|||||||
|
|
||||||
def _extract_mobile_video_formats(self, mtvn_id):
|
def _extract_mobile_video_formats(self, mtvn_id):
|
||||||
webpage_url = self._MOBILE_TEMPLATE % mtvn_id
|
webpage_url = self._MOBILE_TEMPLATE % mtvn_id
|
||||||
req = compat_urllib_request.Request(webpage_url)
|
req = sanitized_Request(webpage_url)
|
||||||
# Otherwise we get a webpage that would execute some javascript
|
# Otherwise we get a webpage that would execute some javascript
|
||||||
req.add_header('User-Agent', 'curl/7')
|
req.add_header('User-Agent', 'curl/7')
|
||||||
webpage = self._download_webpage(req, mtvn_id,
|
webpage = self._download_webpage(req, mtvn_id,
|
||||||
|
@@ -11,10 +11,10 @@ from ..compat import (
|
|||||||
compat_ord,
|
compat_ord,
|
||||||
compat_urllib_parse,
|
compat_urllib_parse,
|
||||||
compat_urllib_parse_unquote,
|
compat_urllib_parse_unquote,
|
||||||
compat_urllib_request,
|
|
||||||
)
|
)
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
|
sanitized_Request,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ class MyVideoIE(InfoExtractor):
|
|||||||
|
|
||||||
mobj = re.search(r'data-video-service="/service/data/video/%s/config' % video_id, webpage)
|
mobj = re.search(r'data-video-service="/service/data/video/%s/config' % video_id, webpage)
|
||||||
if mobj is not None:
|
if mobj is not None:
|
||||||
request = compat_urllib_request.Request('http://www.myvideo.de/service/data/video/%s/config' % video_id, '')
|
request = sanitized_Request('http://www.myvideo.de/service/data/video/%s/config' % video_id, '')
|
||||||
response = self._download_webpage(request, video_id,
|
response = self._download_webpage(request, video_id,
|
||||||
'Downloading video info')
|
'Downloading video info')
|
||||||
info = json.loads(base64.b64decode(response).decode('utf-8'))
|
info = json.loads(base64.b64decode(response).decode('utf-8'))
|
||||||
|
@@ -1,63 +1,102 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
remove_end,
|
|
||||||
parse_duration,
|
parse_duration,
|
||||||
|
int_or_none,
|
||||||
|
xpath_text,
|
||||||
|
xpath_attr,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class NBAIE(InfoExtractor):
|
class NBAIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://(?:watch\.|www\.)?nba\.com/(?:nba/)?video(?P<id>/[^?]*?)/?(?:/index\.html)?(?:\?.*)?$'
|
_VALID_URL = r'https?://(?:watch\.|www\.)?nba\.com/(?P<path>(?:[^/]+/)?video/(?P<id>[^?]*?))/?(?:/index\.html)?(?:\?.*)?$'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://www.nba.com/video/games/nets/2012/12/04/0021200253-okc-bkn-recap.nba/index.html',
|
'url': 'http://www.nba.com/video/games/nets/2012/12/04/0021200253-okc-bkn-recap.nba/index.html',
|
||||||
'md5': 'c0edcfc37607344e2ff8f13c378c88a4',
|
'md5': '9e7729d3010a9c71506fd1248f74e4f4',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '0021200253-okc-bkn-recap.nba',
|
'id': '0021200253-okc-bkn-recap',
|
||||||
'ext': 'mp4',
|
'ext': 'flv',
|
||||||
'title': 'Thunder vs. Nets',
|
'title': 'Thunder vs. Nets',
|
||||||
'description': 'Kevin Durant scores 32 points and dishes out six assists as the Thunder beat the Nets in Brooklyn.',
|
'description': 'Kevin Durant scores 32 points and dishes out six assists as the Thunder beat the Nets in Brooklyn.',
|
||||||
'duration': 181,
|
'duration': 181,
|
||||||
|
'timestamp': 1354638466,
|
||||||
|
'upload_date': '20121204',
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://www.nba.com/video/games/hornets/2014/12/05/0021400276-nyk-cha-play5.nba/',
|
'url': 'http://www.nba.com/video/games/hornets/2014/12/05/0021400276-nyk-cha-play5.nba/',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://watch.nba.com/nba/video/channels/playoffs/2015/05/20/0041400301-cle-atl-recap.nba',
|
'url': 'http://watch.nba.com/video/channels/playoffs/2015/05/20/0041400301-cle-atl-recap.nba',
|
||||||
|
'md5': 'b2b39b81cf28615ae0c3360a3f9668c4',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '0041400301-cle-atl-recap.nba',
|
'id': '0041400301-cle-atl-recap',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'NBA GAME TIME | Video: Hawks vs. Cavaliers Game 1',
|
'title': 'Hawks vs. Cavaliers Game 1',
|
||||||
'description': 'md5:8094c3498d35a9bd6b1a8c396a071b4d',
|
'description': 'md5:8094c3498d35a9bd6b1a8c396a071b4d',
|
||||||
'duration': 228,
|
'duration': 228,
|
||||||
},
|
'timestamp': 1432134543,
|
||||||
'params': {
|
'upload_date': '20150520',
|
||||||
'skip_download': True,
|
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
path, video_id = re.match(self._VALID_URL, url).groups()
|
||||||
webpage = self._download_webpage(url, video_id)
|
if path.startswith('nba/'):
|
||||||
|
path = path[3:]
|
||||||
|
video_info = self._download_xml('http://www.nba.com/%s.xml' % path, video_id)
|
||||||
|
video_id = xpath_text(video_info, 'slug')
|
||||||
|
title = xpath_text(video_info, 'headline')
|
||||||
|
description = xpath_text(video_info, 'description')
|
||||||
|
duration = parse_duration(xpath_text(video_info, 'length'))
|
||||||
|
timestamp = int_or_none(xpath_attr(video_info, 'dateCreated', 'uts'))
|
||||||
|
|
||||||
video_url = 'http://ht-mobile.cdn.turner.com/nba/big' + video_id + '_nba_1280x720.mp4'
|
thumbnails = []
|
||||||
|
for image in video_info.find('images'):
|
||||||
|
thumbnails.append({
|
||||||
|
'id': image.attrib.get('cut'),
|
||||||
|
'url': image.text,
|
||||||
|
'width': int_or_none(image.attrib.get('width')),
|
||||||
|
'height': int_or_none(image.attrib.get('height')),
|
||||||
|
})
|
||||||
|
|
||||||
shortened_video_id = video_id.rpartition('/')[2]
|
formats = []
|
||||||
title = remove_end(
|
for video_file in video_info.findall('.//file'):
|
||||||
self._og_search_title(webpage, default=shortened_video_id), ' : NBA.com')
|
video_url = video_file.text
|
||||||
|
if video_url.startswith('/'):
|
||||||
description = self._og_search_description(webpage)
|
continue
|
||||||
duration_str = self._html_search_meta(
|
if video_url.endswith('.m3u8'):
|
||||||
'duration', webpage, 'duration', default=None)
|
m3u8_formats = self._extract_m3u8_formats(video_url, video_id, m3u8_id='hls', fatal=False)
|
||||||
if not duration_str:
|
if m3u8_formats:
|
||||||
duration_str = self._html_search_regex(
|
formats.extend(m3u8_formats)
|
||||||
r'Duration:</b>\s*(\d+:\d+)', webpage, 'duration', fatal=False)
|
elif video_url.endswith('.f4m'):
|
||||||
duration = parse_duration(duration_str)
|
f4m_formats = self._extract_f4m_formats(video_url + '?hdcore=3.4.1.1', video_id, f4m_id='hds', fatal=False)
|
||||||
|
if f4m_formats:
|
||||||
|
formats.extend(f4m_formats)
|
||||||
|
else:
|
||||||
|
key = video_file.attrib.get('bitrate')
|
||||||
|
format_info = {
|
||||||
|
'format_id': key,
|
||||||
|
'url': video_url,
|
||||||
|
}
|
||||||
|
mobj = re.search(r'(\d+)x(\d+)(?:_(\d+))?', key)
|
||||||
|
if mobj:
|
||||||
|
format_info.update({
|
||||||
|
'width': int(mobj.group(1)),
|
||||||
|
'height': int(mobj.group(2)),
|
||||||
|
'tbr': int_or_none(mobj.group(3)),
|
||||||
|
})
|
||||||
|
formats.append(format_info)
|
||||||
|
self._sort_formats(formats)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': shortened_video_id,
|
'id': video_id,
|
||||||
'url': video_url,
|
|
||||||
'title': title,
|
'title': title,
|
||||||
'description': description,
|
'description': description,
|
||||||
'duration': duration,
|
'duration': duration,
|
||||||
|
'timestamp': timestamp,
|
||||||
|
'thumbnails': thumbnails,
|
||||||
|
'formats': formats,
|
||||||
}
|
}
|
||||||
|
@@ -11,6 +11,7 @@ from ..utils import (
|
|||||||
ExtractorError,
|
ExtractorError,
|
||||||
find_xpath_attr,
|
find_xpath_attr,
|
||||||
lowercase_escape,
|
lowercase_escape,
|
||||||
|
smuggle_url,
|
||||||
unescapeHTML,
|
unescapeHTML,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -62,12 +63,13 @@ class NBCIE(InfoExtractor):
|
|||||||
theplatform_url = unescapeHTML(lowercase_escape(self._html_search_regex(
|
theplatform_url = unescapeHTML(lowercase_escape(self._html_search_regex(
|
||||||
[
|
[
|
||||||
r'(?:class="video-player video-player-full" data-mpx-url|class="player" src)="(.*?)"',
|
r'(?:class="video-player video-player-full" data-mpx-url|class="player" src)="(.*?)"',
|
||||||
|
r'<iframe[^>]+src="((?:https?:)?//player\.theplatform\.com/[^"]+)"',
|
||||||
r'"embedURL"\s*:\s*"([^"]+)"'
|
r'"embedURL"\s*:\s*"([^"]+)"'
|
||||||
],
|
],
|
||||||
webpage, 'theplatform url').replace('_no_endcard', '').replace('\\/', '/')))
|
webpage, 'theplatform url').replace('_no_endcard', '').replace('\\/', '/')))
|
||||||
if theplatform_url.startswith('//'):
|
if theplatform_url.startswith('//'):
|
||||||
theplatform_url = 'http:' + theplatform_url
|
theplatform_url = 'http:' + theplatform_url
|
||||||
return self.url_result(theplatform_url)
|
return self.url_result(smuggle_url(theplatform_url, {'source_url': url}))
|
||||||
|
|
||||||
|
|
||||||
class NBCSportsVPlayerIE(InfoExtractor):
|
class NBCSportsVPlayerIE(InfoExtractor):
|
||||||
|
@@ -8,11 +8,11 @@ import re
|
|||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
from ..compat import (
|
||||||
compat_urllib_request,
|
|
||||||
compat_urllib_parse,
|
compat_urllib_parse,
|
||||||
compat_str,
|
compat_str,
|
||||||
compat_itertools_count,
|
compat_itertools_count,
|
||||||
)
|
)
|
||||||
|
from ..utils import sanitized_Request
|
||||||
|
|
||||||
|
|
||||||
class NetEaseMusicBaseIE(InfoExtractor):
|
class NetEaseMusicBaseIE(InfoExtractor):
|
||||||
@@ -40,7 +40,7 @@ class NetEaseMusicBaseIE(InfoExtractor):
|
|||||||
if not details:
|
if not details:
|
||||||
continue
|
continue
|
||||||
formats.append({
|
formats.append({
|
||||||
'url': 'http://m1.music.126.net/%s/%s.%s' %
|
'url': 'http://m5.music.126.net/%s/%s.%s' %
|
||||||
(cls._encrypt(details['dfsId']), details['dfsId'],
|
(cls._encrypt(details['dfsId']), details['dfsId'],
|
||||||
details['extension']),
|
details['extension']),
|
||||||
'ext': details.get('extension'),
|
'ext': details.get('extension'),
|
||||||
@@ -56,7 +56,7 @@ class NetEaseMusicBaseIE(InfoExtractor):
|
|||||||
return int(round(ms / 1000.0))
|
return int(round(ms / 1000.0))
|
||||||
|
|
||||||
def query_api(self, endpoint, video_id, note):
|
def query_api(self, endpoint, video_id, note):
|
||||||
req = compat_urllib_request.Request('%s%s' % (self._API_BASE, endpoint))
|
req = sanitized_Request('%s%s' % (self._API_BASE, endpoint))
|
||||||
req.add_header('Referer', self._API_BASE)
|
req.add_header('Referer', self._API_BASE)
|
||||||
return self._download_json(req, video_id, note)
|
return self._download_json(req, video_id, note)
|
||||||
|
|
||||||
|
@@ -1,10 +1,8 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
from ..compat import compat_urllib_parse
|
||||||
compat_urllib_request,
|
from ..utils import sanitized_Request
|
||||||
compat_urllib_parse,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class NFBIE(InfoExtractor):
|
class NFBIE(InfoExtractor):
|
||||||
@@ -40,8 +38,9 @@ class NFBIE(InfoExtractor):
|
|||||||
uploader = self._html_search_regex(r'<em class="director-name" itemprop="name">([^<]+)</em>',
|
uploader = self._html_search_regex(r'<em class="director-name" itemprop="name">([^<]+)</em>',
|
||||||
page, 'director name', fatal=False)
|
page, 'director name', fatal=False)
|
||||||
|
|
||||||
request = compat_urllib_request.Request('https://www.nfb.ca/film/%s/player_config' % video_id,
|
request = sanitized_Request(
|
||||||
compat_urllib_parse.urlencode({'getConfig': 'true'}).encode('ascii'))
|
'https://www.nfb.ca/film/%s/player_config' % video_id,
|
||||||
|
compat_urllib_parse.urlencode({'getConfig': 'true'}).encode('ascii'))
|
||||||
request.add_header('Content-Type', 'application/x-www-form-urlencoded')
|
request.add_header('Content-Type', 'application/x-www-form-urlencoded')
|
||||||
request.add_header('X-NFB-Referer', 'http://www.nfb.ca/medias/flash/NFBVideoPlayer.swf')
|
request.add_header('X-NFB-Referer', 'http://www.nfb.ca/medias/flash/NFBVideoPlayer.swf')
|
||||||
|
|
||||||
|
@@ -8,7 +8,6 @@ import datetime
|
|||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
from ..compat import (
|
||||||
compat_urllib_parse,
|
compat_urllib_parse,
|
||||||
compat_urllib_request,
|
|
||||||
compat_urlparse,
|
compat_urlparse,
|
||||||
)
|
)
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
@@ -17,6 +16,7 @@ from ..utils import (
|
|||||||
int_or_none,
|
int_or_none,
|
||||||
parse_duration,
|
parse_duration,
|
||||||
parse_iso8601,
|
parse_iso8601,
|
||||||
|
sanitized_Request,
|
||||||
xpath_text,
|
xpath_text,
|
||||||
determine_ext,
|
determine_ext,
|
||||||
)
|
)
|
||||||
@@ -102,7 +102,7 @@ class NiconicoIE(InfoExtractor):
|
|||||||
'password': password,
|
'password': password,
|
||||||
}
|
}
|
||||||
login_data = compat_urllib_parse.urlencode(encode_dict(login_form_strs)).encode('utf-8')
|
login_data = compat_urllib_parse.urlencode(encode_dict(login_form_strs)).encode('utf-8')
|
||||||
request = compat_urllib_request.Request(
|
request = sanitized_Request(
|
||||||
'https://secure.nicovideo.jp/secure/login', login_data)
|
'https://secure.nicovideo.jp/secure/login', login_data)
|
||||||
login_results = self._download_webpage(
|
login_results = self._download_webpage(
|
||||||
request, None, note='Logging in', errnote='Unable to log in')
|
request, None, note='Logging in', errnote='Unable to log in')
|
||||||
@@ -145,7 +145,7 @@ class NiconicoIE(InfoExtractor):
|
|||||||
'k': thumb_play_key,
|
'k': thumb_play_key,
|
||||||
'v': video_id
|
'v': video_id
|
||||||
})
|
})
|
||||||
flv_info_request = compat_urllib_request.Request(
|
flv_info_request = sanitized_Request(
|
||||||
'http://ext.nicovideo.jp/thumb_watch', flv_info_data,
|
'http://ext.nicovideo.jp/thumb_watch', flv_info_data,
|
||||||
{'Content-Type': 'application/x-www-form-urlencoded'})
|
{'Content-Type': 'application/x-www-form-urlencoded'})
|
||||||
flv_info_webpage = self._download_webpage(
|
flv_info_webpage = self._download_webpage(
|
||||||
|
@@ -9,7 +9,6 @@ from .common import InfoExtractor
|
|||||||
from ..compat import (
|
from ..compat import (
|
||||||
compat_str,
|
compat_str,
|
||||||
compat_urllib_parse,
|
compat_urllib_parse,
|
||||||
compat_urllib_request,
|
|
||||||
)
|
)
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
clean_html,
|
clean_html,
|
||||||
@@ -17,6 +16,7 @@ from ..utils import (
|
|||||||
int_or_none,
|
int_or_none,
|
||||||
float_or_none,
|
float_or_none,
|
||||||
parse_iso8601,
|
parse_iso8601,
|
||||||
|
sanitized_Request,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ class NocoIE(InfoExtractor):
|
|||||||
'username': username,
|
'username': username,
|
||||||
'password': password,
|
'password': password,
|
||||||
}
|
}
|
||||||
request = compat_urllib_request.Request(self._LOGIN_URL, compat_urllib_parse.urlencode(login_form))
|
request = sanitized_Request(self._LOGIN_URL, compat_urllib_parse.urlencode(login_form))
|
||||||
request.add_header('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8')
|
request.add_header('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8')
|
||||||
|
|
||||||
login = self._download_json(request, None, 'Logging in as %s' % username)
|
login = self._download_json(request, None, 'Logging in as %s' % username)
|
||||||
|
@@ -4,11 +4,9 @@ from __future__ import unicode_literals
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
|
||||||
compat_urllib_request,
|
|
||||||
)
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
|
sanitized_Request,
|
||||||
urlencode_postdata,
|
urlencode_postdata,
|
||||||
xpath_text,
|
xpath_text,
|
||||||
xpath_with_ns,
|
xpath_with_ns,
|
||||||
@@ -41,7 +39,7 @@ class NosVideoIE(InfoExtractor):
|
|||||||
'op': 'download1',
|
'op': 'download1',
|
||||||
'method_free': 'Continue to Video',
|
'method_free': 'Continue to Video',
|
||||||
}
|
}
|
||||||
req = compat_urllib_request.Request(url, urlencode_postdata(fields))
|
req = sanitized_Request(url, urlencode_postdata(fields))
|
||||||
req.add_header('Content-type', 'application/x-www-form-urlencoded')
|
req.add_header('Content-type', 'application/x-www-form-urlencoded')
|
||||||
webpage = self._download_webpage(req, video_id,
|
webpage = self._download_webpage(req, video_id,
|
||||||
'Downloading download page')
|
'Downloading download page')
|
||||||
|
@@ -3,11 +3,13 @@ from __future__ import unicode_literals
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
from ..compat import compat_urlparse
|
||||||
compat_urlparse,
|
|
||||||
)
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
|
NO_DEFAULT,
|
||||||
|
encode_dict,
|
||||||
|
sanitized_Request,
|
||||||
|
urlencode_postdata,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -21,9 +23,10 @@ class NovaMovIE(InfoExtractor):
|
|||||||
_HOST = 'www.novamov.com'
|
_HOST = 'www.novamov.com'
|
||||||
|
|
||||||
_FILE_DELETED_REGEX = r'This file no longer exists on our servers!</h2>'
|
_FILE_DELETED_REGEX = r'This file no longer exists on our servers!</h2>'
|
||||||
_FILEKEY_REGEX = r'flashvars\.filekey="(?P<filekey>[^"]+)";'
|
_FILEKEY_REGEX = r'flashvars\.filekey=(?P<filekey>"?[^"]+"?);'
|
||||||
_TITLE_REGEX = r'(?s)<div class="v_tab blockborder rounded5" id="v_tab1">\s*<h3>([^<]+)</h3>'
|
_TITLE_REGEX = r'(?s)<div class="v_tab blockborder rounded5" id="v_tab1">\s*<h3>([^<]+)</h3>'
|
||||||
_DESCRIPTION_REGEX = r'(?s)<div class="v_tab blockborder rounded5" id="v_tab1">\s*<h3>[^<]+</h3><p>([^<]+)</p>'
|
_DESCRIPTION_REGEX = r'(?s)<div class="v_tab blockborder rounded5" id="v_tab1">\s*<h3>[^<]+</h3><p>([^<]+)</p>'
|
||||||
|
_URL_TEMPLATE = 'http://%s/video/%s'
|
||||||
|
|
||||||
_TEST = {
|
_TEST = {
|
||||||
'url': 'http://www.novamov.com/video/4rurhn9x446jj',
|
'url': 'http://www.novamov.com/video/4rurhn9x446jj',
|
||||||
@@ -37,20 +40,50 @@ class NovaMovIE(InfoExtractor):
|
|||||||
'skip': '"Invalid token" errors abound (in web interface as well as youtube-dl, there is nothing we can do about it.)'
|
'skip': '"Invalid token" errors abound (in web interface as well as youtube-dl, there is nothing we can do about it.)'
|
||||||
}
|
}
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _check_existence(self, webpage, video_id):
|
||||||
mobj = re.match(self._VALID_URL, url)
|
if re.search(self._FILE_DELETED_REGEX, webpage) is not None:
|
||||||
video_id = mobj.group('id')
|
|
||||||
|
|
||||||
page = self._download_webpage(
|
|
||||||
'http://%s/video/%s' % (self._HOST, video_id), video_id, 'Downloading video page')
|
|
||||||
|
|
||||||
if re.search(self._FILE_DELETED_REGEX, page) is not None:
|
|
||||||
raise ExtractorError('Video %s does not exist' % video_id, expected=True)
|
raise ExtractorError('Video %s does not exist' % video_id, expected=True)
|
||||||
|
|
||||||
filekey = self._search_regex(self._FILEKEY_REGEX, page, 'filekey')
|
def _real_extract(self, url):
|
||||||
|
video_id = self._match_id(url)
|
||||||
|
|
||||||
title = self._html_search_regex(self._TITLE_REGEX, page, 'title', fatal=False)
|
url = self._URL_TEMPLATE % (self._HOST, video_id)
|
||||||
description = self._html_search_regex(self._DESCRIPTION_REGEX, page, 'description', default='', fatal=False)
|
|
||||||
|
webpage = self._download_webpage(
|
||||||
|
url, video_id, 'Downloading video page')
|
||||||
|
|
||||||
|
self._check_existence(webpage, video_id)
|
||||||
|
|
||||||
|
def extract_filekey(default=NO_DEFAULT):
|
||||||
|
filekey = self._search_regex(
|
||||||
|
self._FILEKEY_REGEX, webpage, 'filekey', default=default)
|
||||||
|
if filekey is not default and (filekey[0] != '"' or filekey[-1] != '"'):
|
||||||
|
return self._search_regex(
|
||||||
|
r'var\s*%s\s*=\s*"([^"]+)"', webpage, 'filekey', default=default)
|
||||||
|
else:
|
||||||
|
return filekey
|
||||||
|
|
||||||
|
filekey = extract_filekey(default=None)
|
||||||
|
|
||||||
|
if not filekey:
|
||||||
|
fields = self._hidden_inputs(webpage)
|
||||||
|
post_url = self._search_regex(
|
||||||
|
r'<form[^>]+action=(["\'])(?P<url>.+?)\1', webpage,
|
||||||
|
'post url', default=url, group='url')
|
||||||
|
if not post_url.startswith('http'):
|
||||||
|
post_url = compat_urlparse.urljoin(url, post_url)
|
||||||
|
request = sanitized_Request(
|
||||||
|
post_url, urlencode_postdata(encode_dict(fields)))
|
||||||
|
request.add_header('Content-Type', 'application/x-www-form-urlencoded')
|
||||||
|
request.add_header('Referer', post_url)
|
||||||
|
webpage = self._download_webpage(
|
||||||
|
request, video_id, 'Downloading continue to the video page')
|
||||||
|
self._check_existence(webpage, video_id)
|
||||||
|
|
||||||
|
filekey = extract_filekey()
|
||||||
|
|
||||||
|
title = self._html_search_regex(self._TITLE_REGEX, webpage, 'title', fatal=False)
|
||||||
|
description = self._html_search_regex(self._DESCRIPTION_REGEX, webpage, 'description', default='', fatal=False)
|
||||||
|
|
||||||
api_response = self._download_webpage(
|
api_response = self._download_webpage(
|
||||||
'http://%s/api/player.api.php?key=%s&file=%s' % (self._HOST, filekey, video_id), video_id,
|
'http://%s/api/player.api.php?key=%s&file=%s' % (self._HOST, filekey, video_id), video_id,
|
||||||
@@ -69,3 +102,90 @@ class NovaMovIE(InfoExtractor):
|
|||||||
'title': title,
|
'title': title,
|
||||||
'description': description
|
'description': description
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class WholeCloudIE(NovaMovIE):
|
||||||
|
IE_NAME = 'wholecloud'
|
||||||
|
IE_DESC = 'WholeCloud'
|
||||||
|
|
||||||
|
_VALID_URL = NovaMovIE._VALID_URL_TEMPLATE % {'host': '(?:wholecloud\.net|movshare\.(?:net|sx|ag))'}
|
||||||
|
|
||||||
|
_HOST = 'www.wholecloud.net'
|
||||||
|
|
||||||
|
_FILE_DELETED_REGEX = r'>This file no longer exists on our servers.<'
|
||||||
|
_TITLE_REGEX = r'<strong>Title:</strong> ([^<]+)</p>'
|
||||||
|
_DESCRIPTION_REGEX = r'<strong>Description:</strong> ([^<]+)</p>'
|
||||||
|
|
||||||
|
_TEST = {
|
||||||
|
'url': 'http://www.wholecloud.net/video/559e28be54d96',
|
||||||
|
'md5': 'abd31a2132947262c50429e1d16c1bfd',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '559e28be54d96',
|
||||||
|
'ext': 'flv',
|
||||||
|
'title': 'dissapeared image',
|
||||||
|
'description': 'optical illusion dissapeared image magic illusion',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class NowVideoIE(NovaMovIE):
|
||||||
|
IE_NAME = 'nowvideo'
|
||||||
|
IE_DESC = 'NowVideo'
|
||||||
|
|
||||||
|
_VALID_URL = NovaMovIE._VALID_URL_TEMPLATE % {'host': 'nowvideo\.(?:to|ch|ec|sx|eu|at|ag|co|li)'}
|
||||||
|
|
||||||
|
_HOST = 'www.nowvideo.to'
|
||||||
|
|
||||||
|
_FILE_DELETED_REGEX = r'>This file no longer exists on our servers.<'
|
||||||
|
_TITLE_REGEX = r'<h4>([^<]+)</h4>'
|
||||||
|
_DESCRIPTION_REGEX = r'</h4>\s*<p>([^<]+)</p>'
|
||||||
|
|
||||||
|
_TEST = {
|
||||||
|
'url': 'http://www.nowvideo.to/video/0mw0yow7b6dxa',
|
||||||
|
'md5': 'f8fbbc8add72bd95b7850c6a02fc8817',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '0mw0yow7b6dxa',
|
||||||
|
'ext': 'flv',
|
||||||
|
'title': 'youtubedl test video _BaW_jenozKc.mp4',
|
||||||
|
'description': 'Description',
|
||||||
|
},
|
||||||
|
'skip': 'Video 0mw0yow7b6dxa does not exist',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class VideoWeedIE(NovaMovIE):
|
||||||
|
IE_NAME = 'videoweed'
|
||||||
|
IE_DESC = 'VideoWeed'
|
||||||
|
|
||||||
|
_VALID_URL = NovaMovIE._VALID_URL_TEMPLATE % {'host': 'videoweed\.(?:es|com)'}
|
||||||
|
|
||||||
|
_HOST = 'www.videoweed.es'
|
||||||
|
|
||||||
|
_FILE_DELETED_REGEX = r'>This file no longer exists on our servers.<'
|
||||||
|
_TITLE_REGEX = r'<h1 class="text_shadow">([^<]+)</h1>'
|
||||||
|
_URL_TEMPLATE = 'http://%s/file/%s'
|
||||||
|
|
||||||
|
_TEST = {
|
||||||
|
'url': 'http://www.videoweed.es/file/b42178afbea14',
|
||||||
|
'md5': 'abd31a2132947262c50429e1d16c1bfd',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'b42178afbea14',
|
||||||
|
'ext': 'flv',
|
||||||
|
'title': 'optical illusion dissapeared image magic illusion',
|
||||||
|
'description': ''
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CloudTimeIE(NovaMovIE):
|
||||||
|
IE_NAME = 'cloudtime'
|
||||||
|
IE_DESC = 'CloudTime'
|
||||||
|
|
||||||
|
_VALID_URL = NovaMovIE._VALID_URL_TEMPLATE % {'host': 'cloudtime\.to'}
|
||||||
|
|
||||||
|
_HOST = 'www.cloudtime.to'
|
||||||
|
|
||||||
|
_FILE_DELETED_REGEX = r'>This file no longer exists on our servers.<'
|
||||||
|
_TITLE_REGEX = r'<div[^>]+class=["\']video_det["\'][^>]*>\s*<strong>([^<]+)</strong>'
|
||||||
|
|
||||||
|
_TEST = None
|
||||||
|
@@ -1,12 +1,12 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from .brightcove import BrightcoveIE
|
from .brightcove import BrightcoveLegacyIE
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..utils import ExtractorError
|
from ..compat import compat_str
|
||||||
from ..compat import (
|
from ..utils import (
|
||||||
compat_str,
|
ExtractorError,
|
||||||
compat_urllib_request,
|
sanitized_Request,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -22,10 +22,10 @@ class NownessBaseIE(InfoExtractor):
|
|||||||
'http://www.nowness.com/iframe?id=%s' % video_id, video_id,
|
'http://www.nowness.com/iframe?id=%s' % video_id, video_id,
|
||||||
note='Downloading player JavaScript',
|
note='Downloading player JavaScript',
|
||||||
errnote='Unable to download player JavaScript')
|
errnote='Unable to download player JavaScript')
|
||||||
bc_url = BrightcoveIE._extract_brightcove_url(player_code)
|
bc_url = BrightcoveLegacyIE._extract_brightcove_url(player_code)
|
||||||
if bc_url is None:
|
if bc_url is None:
|
||||||
raise ExtractorError('Could not find player definition')
|
raise ExtractorError('Could not find player definition')
|
||||||
return self.url_result(bc_url, 'Brightcove')
|
return self.url_result(bc_url, 'BrightcoveLegacy')
|
||||||
elif source == 'vimeo':
|
elif source == 'vimeo':
|
||||||
return self.url_result('http://vimeo.com/%s' % video_id, 'Vimeo')
|
return self.url_result('http://vimeo.com/%s' % video_id, 'Vimeo')
|
||||||
elif source == 'youtube':
|
elif source == 'youtube':
|
||||||
@@ -37,7 +37,7 @@ class NownessBaseIE(InfoExtractor):
|
|||||||
|
|
||||||
def _api_request(self, url, request_path):
|
def _api_request(self, url, request_path):
|
||||||
display_id = self._match_id(url)
|
display_id = self._match_id(url)
|
||||||
request = compat_urllib_request.Request(
|
request = sanitized_Request(
|
||||||
'http://api.nowness.com/api/' + request_path % display_id,
|
'http://api.nowness.com/api/' + request_path % display_id,
|
||||||
headers={
|
headers={
|
||||||
'X-Nowness-Language': 'zh-cn' if 'cn.nowness.com' in url else 'en-us',
|
'X-Nowness-Language': 'zh-cn' if 'cn.nowness.com' in url else 'en-us',
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import compat_str
|
from ..compat import compat_str
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
@@ -13,8 +15,63 @@ from ..utils import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class NowTVIE(InfoExtractor):
|
class NowTVBaseIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://(?:www\.)?nowtv\.(?:de|at|ch)/(?:rtl|rtl2|rtlnitro|superrtl|ntv|vox)/(?P<id>.+?)/(?:player|preview)'
|
_VIDEO_FIELDS = (
|
||||||
|
'id', 'title', 'free', 'geoblocked', 'articleLong', 'articleShort',
|
||||||
|
'broadcastStartDate', 'seoUrl', 'duration', 'files',
|
||||||
|
'format.defaultImage169Format', 'format.defaultImage169Logo')
|
||||||
|
|
||||||
|
def _extract_video(self, info, display_id=None):
|
||||||
|
video_id = compat_str(info['id'])
|
||||||
|
|
||||||
|
files = info['files']
|
||||||
|
if not files:
|
||||||
|
if info.get('geoblocked', False):
|
||||||
|
raise ExtractorError(
|
||||||
|
'Video %s is not available from your location due to geo restriction' % video_id,
|
||||||
|
expected=True)
|
||||||
|
if not info.get('free', True):
|
||||||
|
raise ExtractorError(
|
||||||
|
'Video %s is not available for free' % video_id, expected=True)
|
||||||
|
|
||||||
|
formats = []
|
||||||
|
for item in files['items']:
|
||||||
|
if determine_ext(item['path']) != 'f4v':
|
||||||
|
continue
|
||||||
|
app, play_path = remove_start(item['path'], '/').split('/', 1)
|
||||||
|
formats.append({
|
||||||
|
'url': 'rtmpe://fms.rtl.de',
|
||||||
|
'app': app,
|
||||||
|
'play_path': 'mp4:%s' % play_path,
|
||||||
|
'ext': 'flv',
|
||||||
|
'page_url': 'http://rtlnow.rtl.de',
|
||||||
|
'player_url': 'http://cdn.static-fra.de/now/vodplayer.swf',
|
||||||
|
'tbr': int_or_none(item.get('bitrate')),
|
||||||
|
})
|
||||||
|
self._sort_formats(formats)
|
||||||
|
|
||||||
|
title = info['title']
|
||||||
|
description = info.get('articleLong') or info.get('articleShort')
|
||||||
|
timestamp = parse_iso8601(info.get('broadcastStartDate'), ' ')
|
||||||
|
duration = parse_duration(info.get('duration'))
|
||||||
|
|
||||||
|
f = info.get('format', {})
|
||||||
|
thumbnail = f.get('defaultImage169Format') or f.get('defaultImage169Logo')
|
||||||
|
|
||||||
|
return {
|
||||||
|
'id': video_id,
|
||||||
|
'display_id': display_id or info.get('seoUrl'),
|
||||||
|
'title': title,
|
||||||
|
'description': description,
|
||||||
|
'thumbnail': thumbnail,
|
||||||
|
'timestamp': timestamp,
|
||||||
|
'duration': duration,
|
||||||
|
'formats': formats,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class NowTVIE(NowTVBaseIE):
|
||||||
|
_VALID_URL = r'https?://(?:www\.)?nowtv\.(?:de|at|ch)/(?:rtl|rtl2|rtlnitro|superrtl|ntv|vox)/(?P<show_id>[^/]+)/(?:(?:list/[^/]+|jahr/\d{4}/\d{1,2})/)?(?P<id>[^/]+)/(?:player|preview)'
|
||||||
|
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
# rtl
|
# rtl
|
||||||
@@ -23,7 +80,7 @@ class NowTVIE(InfoExtractor):
|
|||||||
'id': '203519',
|
'id': '203519',
|
||||||
'display_id': 'bauer-sucht-frau/die-neuen-bauern-und-eine-hochzeit',
|
'display_id': 'bauer-sucht-frau/die-neuen-bauern-und-eine-hochzeit',
|
||||||
'ext': 'flv',
|
'ext': 'flv',
|
||||||
'title': 'Die neuen Bauern und eine Hochzeit',
|
'title': 'Inka Bause stellt die neuen Bauern vor',
|
||||||
'description': 'md5:e234e1ed6d63cf06be5c070442612e7e',
|
'description': 'md5:e234e1ed6d63cf06be5c070442612e7e',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': 're:^https?://.*\.jpg$',
|
||||||
'timestamp': 1432580700,
|
'timestamp': 1432580700,
|
||||||
@@ -133,61 +190,71 @@ class NowTVIE(InfoExtractor):
|
|||||||
}, {
|
}, {
|
||||||
'url': 'http://www.nowtv.de/rtl2/echtzeit/list/aktuell/schnelles-geld-am-ende-der-welt/player',
|
'url': 'http://www.nowtv.de/rtl2/echtzeit/list/aktuell/schnelles-geld-am-ende-der-welt/player',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'http://www.nowtv.de/rtl2/zuhause-im-glueck/jahr/2015/11/eine-erschuetternde-diagnose/player',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
display_id = self._match_id(url)
|
mobj = re.match(self._VALID_URL, url)
|
||||||
display_id_split = display_id.split('/')
|
display_id = '%s/%s' % (mobj.group('show_id'), mobj.group('id'))
|
||||||
if len(display_id) > 2:
|
|
||||||
display_id = '/'.join((display_id_split[0], display_id_split[-1]))
|
|
||||||
|
|
||||||
info = self._download_json(
|
info = self._download_json(
|
||||||
'https://api.nowtv.de/v3/movies/%s?fields=id,title,free,geoblocked,articleLong,articleShort,broadcastStartDate,seoUrl,duration,format,files' % display_id,
|
'https://api.nowtv.de/v3/movies/%s?fields=%s'
|
||||||
display_id)
|
% (display_id, ','.join(self._VIDEO_FIELDS)), display_id)
|
||||||
|
|
||||||
video_id = compat_str(info['id'])
|
return self._extract_video(info, display_id)
|
||||||
|
|
||||||
files = info['files']
|
|
||||||
if not files:
|
|
||||||
if info.get('geoblocked', False):
|
|
||||||
raise ExtractorError(
|
|
||||||
'Video %s is not available from your location due to geo restriction' % video_id,
|
|
||||||
expected=True)
|
|
||||||
if not info.get('free', True):
|
|
||||||
raise ExtractorError(
|
|
||||||
'Video %s is not available for free' % video_id, expected=True)
|
|
||||||
|
|
||||||
formats = []
|
class NowTVListIE(NowTVBaseIE):
|
||||||
for item in files['items']:
|
_VALID_URL = r'https?://(?:www\.)?nowtv\.(?:de|at|ch)/(?:rtl|rtl2|rtlnitro|superrtl|ntv|vox)/(?P<show_id>[^/]+)/list/(?P<id>[^?/#&]+)$'
|
||||||
if determine_ext(item['path']) != 'f4v':
|
|
||||||
continue
|
|
||||||
app, play_path = remove_start(item['path'], '/').split('/', 1)
|
|
||||||
formats.append({
|
|
||||||
'url': 'rtmpe://fms.rtl.de',
|
|
||||||
'app': app,
|
|
||||||
'play_path': 'mp4:%s' % play_path,
|
|
||||||
'ext': 'flv',
|
|
||||||
'page_url': 'http://rtlnow.rtl.de',
|
|
||||||
'player_url': 'http://cdn.static-fra.de/now/vodplayer.swf',
|
|
||||||
'tbr': int_or_none(item.get('bitrate')),
|
|
||||||
})
|
|
||||||
self._sort_formats(formats)
|
|
||||||
|
|
||||||
title = info['title']
|
_SHOW_FIELDS = ('title', )
|
||||||
description = info.get('articleLong') or info.get('articleShort')
|
_SEASON_FIELDS = ('id', 'headline', 'seoheadline', )
|
||||||
timestamp = parse_iso8601(info.get('broadcastStartDate'), ' ')
|
|
||||||
duration = parse_duration(info.get('duration'))
|
|
||||||
|
|
||||||
f = info.get('format', {})
|
_TESTS = [{
|
||||||
thumbnail = f.get('defaultImage169Format') or f.get('defaultImage169Logo')
|
'url': 'http://www.nowtv.at/rtl/stern-tv/list/aktuell',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '17006',
|
||||||
|
'title': 'stern TV - Aktuell',
|
||||||
|
},
|
||||||
|
'playlist_count': 1,
|
||||||
|
}, {
|
||||||
|
'url': 'http://www.nowtv.at/rtl/das-supertalent/list/free-staffel-8',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '20716',
|
||||||
|
'title': 'Das Supertalent - FREE Staffel 8',
|
||||||
|
},
|
||||||
|
'playlist_count': 14,
|
||||||
|
}]
|
||||||
|
|
||||||
return {
|
def _real_extract(self, url):
|
||||||
'id': video_id,
|
mobj = re.match(self._VALID_URL, url)
|
||||||
'display_id': display_id,
|
show_id = mobj.group('show_id')
|
||||||
'title': title,
|
season_id = mobj.group('id')
|
||||||
'description': description,
|
|
||||||
'thumbnail': thumbnail,
|
fields = []
|
||||||
'timestamp': timestamp,
|
fields.extend(self._SHOW_FIELDS)
|
||||||
'duration': duration,
|
fields.extend('formatTabs.%s' % field for field in self._SEASON_FIELDS)
|
||||||
'formats': formats,
|
fields.extend(
|
||||||
}
|
'formatTabs.formatTabPages.container.movies.%s' % field
|
||||||
|
for field in self._VIDEO_FIELDS)
|
||||||
|
|
||||||
|
list_info = self._download_json(
|
||||||
|
'https://api.nowtv.de/v3/formats/seo?fields=%s&name=%s.php'
|
||||||
|
% (','.join(fields), show_id),
|
||||||
|
season_id)
|
||||||
|
|
||||||
|
season = next(
|
||||||
|
season for season in list_info['formatTabs']['items']
|
||||||
|
if season.get('seoheadline') == season_id)
|
||||||
|
|
||||||
|
title = '%s - %s' % (list_info['title'], season['headline'])
|
||||||
|
|
||||||
|
entries = []
|
||||||
|
for container in season['formatTabPages']['items']:
|
||||||
|
for info in ((container.get('container') or {}).get('movies') or {}).get('items') or []:
|
||||||
|
entries.append(self._extract_video(info))
|
||||||
|
|
||||||
|
return self.playlist_result(
|
||||||
|
entries, compat_str(season.get('id') or season_id), title)
|
||||||
|
@@ -1,28 +0,0 @@
|
|||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from .novamov import NovaMovIE
|
|
||||||
|
|
||||||
|
|
||||||
class NowVideoIE(NovaMovIE):
|
|
||||||
IE_NAME = 'nowvideo'
|
|
||||||
IE_DESC = 'NowVideo'
|
|
||||||
|
|
||||||
_VALID_URL = NovaMovIE._VALID_URL_TEMPLATE % {'host': 'nowvideo\.(?:ch|ec|sx|eu|at|ag|co|li)'}
|
|
||||||
|
|
||||||
_HOST = 'www.nowvideo.ch'
|
|
||||||
|
|
||||||
_FILE_DELETED_REGEX = r'>This file no longer exists on our servers.<'
|
|
||||||
_FILEKEY_REGEX = r'var fkzd="([^"]+)";'
|
|
||||||
_TITLE_REGEX = r'<h4>([^<]+)</h4>'
|
|
||||||
_DESCRIPTION_REGEX = r'</h4>\s*<p>([^<]+)</p>'
|
|
||||||
|
|
||||||
_TEST = {
|
|
||||||
'url': 'http://www.nowvideo.ch/video/0mw0yow7b6dxa',
|
|
||||||
'md5': 'f8fbbc8add72bd95b7850c6a02fc8817',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '0mw0yow7b6dxa',
|
|
||||||
'ext': 'flv',
|
|
||||||
'title': 'youtubedl test video _BaW_jenozKc.mp4',
|
|
||||||
'description': 'Description',
|
|
||||||
}
|
|
||||||
}
|
|
@@ -6,6 +6,7 @@ import re
|
|||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import compat_urlparse
|
from ..compat import compat_urlparse
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
|
determine_ext,
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
float_or_none,
|
float_or_none,
|
||||||
parse_duration,
|
parse_duration,
|
||||||
@@ -48,12 +49,22 @@ class NRKIE(InfoExtractor):
|
|||||||
'http://v8.psapi.nrk.no/mediaelement/%s' % video_id,
|
'http://v8.psapi.nrk.no/mediaelement/%s' % video_id,
|
||||||
video_id, 'Downloading media JSON')
|
video_id, 'Downloading media JSON')
|
||||||
|
|
||||||
if data['usageRights']['isGeoBlocked']:
|
media_url = data.get('mediaUrl')
|
||||||
raise ExtractorError(
|
|
||||||
'NRK har ikke rettigheter til å vise dette programmet utenfor Norge',
|
|
||||||
expected=True)
|
|
||||||
|
|
||||||
video_url = data['mediaUrl'] + '?hdcore=3.5.0&plugin=aasp-3.5.0.151.81'
|
if not media_url:
|
||||||
|
if data['usageRights']['isGeoBlocked']:
|
||||||
|
raise ExtractorError(
|
||||||
|
'NRK har ikke rettigheter til å vise dette programmet utenfor Norge',
|
||||||
|
expected=True)
|
||||||
|
|
||||||
|
if determine_ext(media_url) == 'f4m':
|
||||||
|
formats = self._extract_f4m_formats(
|
||||||
|
media_url + '?hdcore=3.5.0&plugin=aasp-3.5.0.151.81', video_id, f4m_id='hds')
|
||||||
|
else:
|
||||||
|
formats = [{
|
||||||
|
'url': media_url,
|
||||||
|
'ext': 'flv',
|
||||||
|
}]
|
||||||
|
|
||||||
duration = parse_duration(data.get('duration'))
|
duration = parse_duration(data.get('duration'))
|
||||||
|
|
||||||
@@ -67,12 +78,11 @@ class NRKIE(InfoExtractor):
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
'url': video_url,
|
|
||||||
'ext': 'flv',
|
|
||||||
'title': data['title'],
|
'title': data['title'],
|
||||||
'description': data['description'],
|
'description': data['description'],
|
||||||
'duration': duration,
|
'duration': duration,
|
||||||
'thumbnail': thumbnail,
|
'thumbnail': thumbnail,
|
||||||
|
'formats': formats,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -3,11 +3,9 @@ from __future__ import unicode_literals
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
|
||||||
compat_urllib_request,
|
|
||||||
)
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
parse_duration,
|
parse_duration,
|
||||||
|
sanitized_Request,
|
||||||
unified_strdate,
|
unified_strdate,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -33,7 +31,7 @@ class NuvidIE(InfoExtractor):
|
|||||||
formats = []
|
formats = []
|
||||||
|
|
||||||
for dwnld_speed, format_id in [(0, '3gp'), (5, 'mp4')]:
|
for dwnld_speed, format_id in [(0, '3gp'), (5, 'mp4')]:
|
||||||
request = compat_urllib_request.Request(
|
request = sanitized_Request(
|
||||||
'http://m.nuvid.com/play/%s' % video_id)
|
'http://m.nuvid.com/play/%s' % video_id)
|
||||||
request.add_header('Cookie', 'skip_download_page=1; dwnld_speed=%d; adv_show=1' % dwnld_speed)
|
request.add_header('Cookie', 'skip_download_page=1; dwnld_speed=%d; adv_show=1' % dwnld_speed)
|
||||||
webpage = self._download_webpage(
|
webpage = self._download_webpage(
|
||||||
|
@@ -1,108 +1,78 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import re
|
import re
|
||||||
import json
|
|
||||||
import base64
|
import base64
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
unescapeHTML,
|
|
||||||
ExtractorError,
|
|
||||||
determine_ext,
|
|
||||||
int_or_none,
|
int_or_none,
|
||||||
|
float_or_none,
|
||||||
|
ExtractorError,
|
||||||
|
unsmuggle_url,
|
||||||
)
|
)
|
||||||
|
from ..compat import compat_urllib_parse
|
||||||
|
|
||||||
|
|
||||||
class OoyalaBaseIE(InfoExtractor):
|
class OoyalaBaseIE(InfoExtractor):
|
||||||
|
|
||||||
def _extract_result(self, info, more_info):
|
def _extract(self, content_tree_url, video_id, domain='example.org'):
|
||||||
embedCode = info['embedCode']
|
content_tree = self._download_json(content_tree_url, video_id)['content_tree']
|
||||||
video_url = info.get('ipad_url') or info['url']
|
metadata = content_tree[list(content_tree)[0]]
|
||||||
|
embed_code = metadata['embed_code']
|
||||||
if determine_ext(video_url) == 'm3u8':
|
pcode = metadata.get('asset_pcode') or embed_code
|
||||||
formats = self._extract_m3u8_formats(video_url, embedCode, ext='mp4')
|
video_info = {
|
||||||
else:
|
'id': embed_code,
|
||||||
formats = [{
|
'title': metadata['title'],
|
||||||
'url': video_url,
|
'description': metadata.get('description'),
|
||||||
'ext': 'mp4',
|
'thumbnail': metadata.get('thumbnail_image') or metadata.get('promo_image'),
|
||||||
}]
|
'duration': float_or_none(metadata.get('duration'), 1000),
|
||||||
|
|
||||||
return {
|
|
||||||
'id': embedCode,
|
|
||||||
'title': unescapeHTML(info['title']),
|
|
||||||
'formats': formats,
|
|
||||||
'description': unescapeHTML(more_info['description']),
|
|
||||||
'thumbnail': more_info['promo'],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def _extract(self, player_url, video_id):
|
urls = []
|
||||||
player = self._download_webpage(player_url, video_id)
|
formats = []
|
||||||
mobile_url = self._search_regex(r'mobile_player_url="(.+?)&device="',
|
for supported_format in ('mp4', 'm3u8', 'hds', 'rtmp'):
|
||||||
player, 'mobile player url')
|
|
||||||
# Looks like some videos are only available for particular devices
|
|
||||||
# (e.g. http://player.ooyala.com/player.js?embedCode=x1b3lqZDq9y_7kMyC2Op5qo-p077tXD0
|
|
||||||
# is only available for ipad)
|
|
||||||
# Working around with fetching URLs for all the devices found starting with 'unknown'
|
|
||||||
# until we succeed or eventually fail for each device.
|
|
||||||
devices = re.findall(r'device\s*=\s*"([^"]+)";', player)
|
|
||||||
devices.remove('unknown')
|
|
||||||
devices.insert(0, 'unknown')
|
|
||||||
for device in devices:
|
|
||||||
mobile_player = self._download_webpage(
|
|
||||||
'%s&device=%s' % (mobile_url, device), video_id,
|
|
||||||
'Downloading mobile player JS for %s device' % device)
|
|
||||||
videos_info = self._search_regex(
|
|
||||||
r'var streams=window.oo_testEnv\?\[\]:eval\("\((\[{.*?}\])\)"\);',
|
|
||||||
mobile_player, 'info', fatal=False, default=None)
|
|
||||||
if videos_info:
|
|
||||||
break
|
|
||||||
|
|
||||||
if not videos_info:
|
|
||||||
formats = []
|
|
||||||
auth_data = self._download_json(
|
auth_data = self._download_json(
|
||||||
'http://player.ooyala.com/sas/player_api/v1/authorization/embed_code/%s/%s?domain=www.example.org&supportedFormats=mp4,webm' % (video_id, video_id),
|
'http://player.ooyala.com/sas/player_api/v1/authorization/embed_code/%s/%s?' % (pcode, embed_code) + compat_urllib_parse.urlencode({'domain': domain, 'supportedFormats': supported_format}),
|
||||||
video_id)
|
video_id, 'Downloading %s JSON' % supported_format)
|
||||||
|
|
||||||
cur_auth_data = auth_data['authorization_data'][video_id]
|
cur_auth_data = auth_data['authorization_data'][embed_code]
|
||||||
|
|
||||||
for stream in cur_auth_data['streams']:
|
if cur_auth_data['authorized']:
|
||||||
formats.append({
|
for stream in cur_auth_data['streams']:
|
||||||
'url': base64.b64decode(stream['url']['data'].encode('ascii')).decode('utf-8'),
|
url = base64.b64decode(stream['url']['data'].encode('ascii')).decode('utf-8')
|
||||||
'ext': stream.get('delivery_type'),
|
if url in urls:
|
||||||
'format': stream.get('video_codec'),
|
continue
|
||||||
'format_id': stream.get('profile'),
|
urls.append(url)
|
||||||
'width': int_or_none(stream.get('width')),
|
delivery_type = stream['delivery_type']
|
||||||
'height': int_or_none(stream.get('height')),
|
if delivery_type == 'hls' or '.m3u8' in url:
|
||||||
'abr': int_or_none(stream.get('audio_bitrate')),
|
m3u8_formats = self._extract_m3u8_formats(url, embed_code, 'mp4', 'm3u8_native', m3u8_id='hls', fatal=False)
|
||||||
'vbr': int_or_none(stream.get('video_bitrate')),
|
if m3u8_formats:
|
||||||
})
|
formats.extend(m3u8_formats)
|
||||||
if formats:
|
elif delivery_type == 'hds' or '.f4m' in url:
|
||||||
return {
|
f4m_formats = self._extract_f4m_formats(url, embed_code, f4m_id='hds', fatal=False)
|
||||||
'id': video_id,
|
if f4m_formats:
|
||||||
'formats': formats,
|
formats.extend(f4m_formats)
|
||||||
'title': 'Ooyala video',
|
elif '.smil' in url:
|
||||||
}
|
smil_formats = self._extract_smil_formats(url, embed_code, fatal=False)
|
||||||
|
if smil_formats:
|
||||||
|
formats.extend(smil_formats)
|
||||||
|
else:
|
||||||
|
formats.append({
|
||||||
|
'url': url,
|
||||||
|
'ext': stream.get('delivery_type'),
|
||||||
|
'vcodec': stream.get('video_codec'),
|
||||||
|
'format_id': delivery_type,
|
||||||
|
'width': int_or_none(stream.get('width')),
|
||||||
|
'height': int_or_none(stream.get('height')),
|
||||||
|
'abr': int_or_none(stream.get('audio_bitrate')),
|
||||||
|
'vbr': int_or_none(stream.get('video_bitrate')),
|
||||||
|
'fps': float_or_none(stream.get('framerate')),
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
raise ExtractorError('%s said: %s' % (self.IE_NAME, cur_auth_data['message']), expected=True)
|
||||||
|
self._sort_formats(formats)
|
||||||
|
|
||||||
if not cur_auth_data['authorized']:
|
video_info['formats'] = formats
|
||||||
raise ExtractorError(cur_auth_data['message'], expected=True)
|
return video_info
|
||||||
|
|
||||||
if not videos_info:
|
|
||||||
raise ExtractorError('Unable to extract info')
|
|
||||||
videos_info = videos_info.replace('\\"', '"')
|
|
||||||
videos_more_info = self._search_regex(
|
|
||||||
r'eval\("\(({.*?\\"promo\\".*?})\)"', mobile_player, 'more info').replace('\\"', '"')
|
|
||||||
videos_info = json.loads(videos_info)
|
|
||||||
videos_more_info = json.loads(videos_more_info)
|
|
||||||
|
|
||||||
if videos_more_info.get('lineup'):
|
|
||||||
videos = [self._extract_result(info, more_info) for (info, more_info) in zip(videos_info, videos_more_info['lineup'])]
|
|
||||||
return {
|
|
||||||
'_type': 'playlist',
|
|
||||||
'id': video_id,
|
|
||||||
'title': unescapeHTML(videos_more_info['title']),
|
|
||||||
'entries': videos,
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
return self._extract_result(videos_info[0], videos_more_info)
|
|
||||||
|
|
||||||
|
|
||||||
class OoyalaIE(OoyalaBaseIE):
|
class OoyalaIE(OoyalaBaseIE):
|
||||||
@@ -117,6 +87,7 @@ class OoyalaIE(OoyalaBaseIE):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Explaining Data Recovery from Hard Drives and SSDs',
|
'title': 'Explaining Data Recovery from Hard Drives and SSDs',
|
||||||
'description': 'How badly damaged does a drive have to be to defeat Russell and his crew? Apparently, smashed to bits.',
|
'description': 'How badly damaged does a drive have to be to defeat Russell and his crew? Apparently, smashed to bits.',
|
||||||
|
'duration': 853.386,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
# Only available for ipad
|
# Only available for ipad
|
||||||
@@ -125,7 +96,7 @@ class OoyalaIE(OoyalaBaseIE):
|
|||||||
'id': 'x1b3lqZDq9y_7kMyC2Op5qo-p077tXD0',
|
'id': 'x1b3lqZDq9y_7kMyC2Op5qo-p077tXD0',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Simulation Overview - Levels of Simulation',
|
'title': 'Simulation Overview - Levels of Simulation',
|
||||||
'description': '',
|
'duration': 194.948,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -136,7 +107,8 @@ class OoyalaIE(OoyalaBaseIE):
|
|||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'FiOG81ZTrvckcchQxmalf4aQj590qTEx',
|
'id': 'FiOG81ZTrvckcchQxmalf4aQj590qTEx',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Ooyala video',
|
'title': 'Divide Tool Path.mp4',
|
||||||
|
'duration': 204.405,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -151,9 +123,11 @@ class OoyalaIE(OoyalaBaseIE):
|
|||||||
ie=cls.ie_key())
|
ie=cls.ie_key())
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
|
url, smuggled_data = unsmuggle_url(url, {})
|
||||||
embed_code = self._match_id(url)
|
embed_code = self._match_id(url)
|
||||||
player_url = 'http://player.ooyala.com/player.js?embedCode=%s' % embed_code
|
domain = smuggled_data.get('domain')
|
||||||
return self._extract(player_url, embed_code)
|
content_tree_url = 'http://player.ooyala.com/player_api/v1/content_tree/embed_code/%s/%s' % (embed_code, embed_code)
|
||||||
|
return self._extract(content_tree_url, embed_code, domain)
|
||||||
|
|
||||||
|
|
||||||
class OoyalaExternalIE(OoyalaBaseIE):
|
class OoyalaExternalIE(OoyalaBaseIE):
|
||||||
@@ -170,7 +144,7 @@ class OoyalaExternalIE(OoyalaBaseIE):
|
|||||||
.*?&pcode=
|
.*?&pcode=
|
||||||
)
|
)
|
||||||
(?P<pcode>.+?)
|
(?P<pcode>.+?)
|
||||||
(&|$)
|
(?:&|$)
|
||||||
'''
|
'''
|
||||||
|
|
||||||
_TEST = {
|
_TEST = {
|
||||||
@@ -179,7 +153,7 @@ class OoyalaExternalIE(OoyalaBaseIE):
|
|||||||
'id': 'FkYWtmazr6Ed8xmvILvKLWjd4QvYZpzG',
|
'id': 'FkYWtmazr6Ed8xmvILvKLWjd4QvYZpzG',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'dm_140128_30for30Shorts___JudgingJewellv2',
|
'title': 'dm_140128_30for30Shorts___JudgingJewellv2',
|
||||||
'description': '',
|
'duration': 1302000,
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
# m3u8 download
|
# m3u8 download
|
||||||
@@ -188,9 +162,6 @@ class OoyalaExternalIE(OoyalaBaseIE):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
mobj = re.match(self._VALID_URL, url)
|
partner_id, video_id, pcode = re.match(self._VALID_URL, url).groups()
|
||||||
partner_id = mobj.group('partner_id')
|
content_tree_url = 'http://player.ooyala.com/player_api/v1/content_tree/external_id/%s/%s:%s' % (pcode, partner_id, video_id)
|
||||||
video_id = mobj.group('id')
|
return self._extract(content_tree_url, video_id)
|
||||||
pcode = mobj.group('pcode')
|
|
||||||
player_url = 'http://player.ooyala.com/player.js?externalId=%s:%s&pcode=%s' % (partner_id, video_id, pcode)
|
|
||||||
return self._extract(player_url, video_id)
|
|
||||||
|
@@ -2,9 +2,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..utils import (
|
from ..utils import js_to_json
|
||||||
js_to_json,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class PatreonIE(InfoExtractor):
|
class PatreonIE(InfoExtractor):
|
||||||
@@ -65,7 +63,7 @@ class PatreonIE(InfoExtractor):
|
|||||||
'password': password,
|
'password': password,
|
||||||
}
|
}
|
||||||
|
|
||||||
request = compat_urllib_request.Request(
|
request = sanitized_Request(
|
||||||
'https://www.patreon.com/processLogin',
|
'https://www.patreon.com/processLogin',
|
||||||
compat_urllib_parse.urlencode(login_form).encode('utf-8')
|
compat_urllib_parse.urlencode(login_form).encode('utf-8')
|
||||||
)
|
)
|
||||||
|
@@ -15,16 +15,181 @@ from ..utils import (
|
|||||||
|
|
||||||
|
|
||||||
class PBSIE(InfoExtractor):
|
class PBSIE(InfoExtractor):
|
||||||
|
_STATIONS = (
|
||||||
|
('video.pbs.org', 'PBS: Public Broadcasting Service'), # http://www.pbs.org/
|
||||||
|
('video.aptv.org', 'APT - Alabama Public Television (WBIQ)'), # http://aptv.org/
|
||||||
|
('video.gpb.org', 'GPB/Georgia Public Broadcasting (WGTV)'), # http://www.gpb.org/
|
||||||
|
('video.mpbonline.org', 'Mississippi Public Broadcasting (WMPN)'), # http://www.mpbonline.org
|
||||||
|
('video.wnpt.org', 'Nashville Public Television (WNPT)'), # http://www.wnpt.org
|
||||||
|
('video.wfsu.org', 'WFSU-TV (WFSU)'), # http://wfsu.org/
|
||||||
|
('video.wsre.org', 'WSRE (WSRE)'), # http://www.wsre.org
|
||||||
|
('video.wtcitv.org', 'WTCI (WTCI)'), # http://www.wtcitv.org
|
||||||
|
('video.pba.org', 'WPBA/Channel 30 (WPBA)'), # http://pba.org/
|
||||||
|
('video.alaskapublic.org', 'Alaska Public Media (KAKM)'), # http://alaskapublic.org/kakm
|
||||||
|
# ('kuac.org', 'KUAC (KUAC)'), # http://kuac.org/kuac-tv/
|
||||||
|
# ('ktoo.org', '360 North (KTOO)'), # http://www.ktoo.org/
|
||||||
|
# ('azpm.org', 'KUAT 6 (KUAT)'), # http://www.azpm.org/
|
||||||
|
('video.azpbs.org', 'Arizona PBS (KAET)'), # http://www.azpbs.org
|
||||||
|
('portal.knme.org', 'KNME-TV/Channel 5 (KNME)'), # http://www.newmexicopbs.org/
|
||||||
|
('video.vegaspbs.org', 'Vegas PBS (KLVX)'), # http://vegaspbs.org/
|
||||||
|
('watch.aetn.org', 'AETN/ARKANSAS ETV NETWORK (KETS)'), # http://www.aetn.org/
|
||||||
|
('video.ket.org', 'KET (WKLE)'), # http://www.ket.org/
|
||||||
|
('video.wkno.org', 'WKNO/Channel 10 (WKNO)'), # http://www.wkno.org/
|
||||||
|
('video.lpb.org', 'LPB/LOUISIANA PUBLIC BROADCASTING (WLPB)'), # http://www.lpb.org/
|
||||||
|
('videos.oeta.tv', 'OETA (KETA)'), # http://www.oeta.tv
|
||||||
|
('video.optv.org', 'Ozarks Public Television (KOZK)'), # http://www.optv.org/
|
||||||
|
('watch.wsiu.org', 'WSIU Public Broadcasting (WSIU)'), # http://www.wsiu.org/
|
||||||
|
('video.keet.org', 'KEET TV (KEET)'), # http://www.keet.org
|
||||||
|
('pbs.kixe.org', 'KIXE/Channel 9 (KIXE)'), # http://kixe.org/
|
||||||
|
('video.kpbs.org', 'KPBS San Diego (KPBS)'), # http://www.kpbs.org/
|
||||||
|
('video.kqed.org', 'KQED (KQED)'), # http://www.kqed.org
|
||||||
|
('vids.kvie.org', 'KVIE Public Television (KVIE)'), # http://www.kvie.org
|
||||||
|
('video.pbssocal.org', 'PBS SoCal/KOCE (KOCE)'), # http://www.pbssocal.org/
|
||||||
|
('video.valleypbs.org', 'ValleyPBS (KVPT)'), # http://www.valleypbs.org/
|
||||||
|
('video.cptv.org', 'CONNECTICUT PUBLIC TELEVISION (WEDH)'), # http://cptv.org
|
||||||
|
('watch.knpb.org', 'KNPB Channel 5 (KNPB)'), # http://www.knpb.org/
|
||||||
|
('video.soptv.org', 'SOPTV (KSYS)'), # http://www.soptv.org
|
||||||
|
# ('klcs.org', 'KLCS/Channel 58 (KLCS)'), # http://www.klcs.org
|
||||||
|
# ('krcb.org', 'KRCB Television & Radio (KRCB)'), # http://www.krcb.org
|
||||||
|
# ('kvcr.org', 'KVCR TV/DT/FM :: Vision for the Future (KVCR)'), # http://kvcr.org
|
||||||
|
('video.rmpbs.org', 'Rocky Mountain PBS (KRMA)'), # http://www.rmpbs.org
|
||||||
|
('video.kenw.org', 'KENW-TV3 (KENW)'), # http://www.kenw.org
|
||||||
|
('video.kued.org', 'KUED Channel 7 (KUED)'), # http://www.kued.org
|
||||||
|
('video.wyomingpbs.org', 'Wyoming PBS (KCWC)'), # http://www.wyomingpbs.org
|
||||||
|
('video.cpt12.org', 'Colorado Public Television / KBDI 12 (KBDI)'), # http://www.cpt12.org/
|
||||||
|
('video.kbyueleven.org', 'KBYU-TV (KBYU)'), # http://www.kbyutv.org/
|
||||||
|
('video.thirteen.org', 'Thirteen/WNET New York (WNET)'), # http://www.thirteen.org
|
||||||
|
('video.wgbh.org', 'WGBH/Channel 2 (WGBH)'), # http://wgbh.org
|
||||||
|
('video.wgby.org', 'WGBY (WGBY)'), # http://www.wgby.org
|
||||||
|
('watch.njtvonline.org', 'NJTV Public Media NJ (WNJT)'), # http://www.njtvonline.org/
|
||||||
|
# ('ripbs.org', 'Rhode Island PBS (WSBE)'), # http://www.ripbs.org/home/
|
||||||
|
('watch.wliw.org', 'WLIW21 (WLIW)'), # http://www.wliw.org/
|
||||||
|
('video.mpt.tv', 'mpt/Maryland Public Television (WMPB)'), # http://www.mpt.org
|
||||||
|
('watch.weta.org', 'WETA Television and Radio (WETA)'), # http://www.weta.org
|
||||||
|
('video.whyy.org', 'WHYY (WHYY)'), # http://www.whyy.org
|
||||||
|
('video.wlvt.org', 'PBS 39 (WLVT)'), # http://www.wlvt.org/
|
||||||
|
('video.wvpt.net', 'WVPT - Your Source for PBS and More! (WVPT)'), # http://www.wvpt.net
|
||||||
|
('video.whut.org', 'Howard University Television (WHUT)'), # http://www.whut.org
|
||||||
|
('video.wedu.org', 'WEDU PBS (WEDU)'), # http://www.wedu.org
|
||||||
|
('video.wgcu.org', 'WGCU Public Media (WGCU)'), # http://www.wgcu.org/
|
||||||
|
# ('wjct.org', 'WJCT Public Broadcasting (WJCT)'), # http://www.wjct.org
|
||||||
|
('video.wpbt2.org', 'WPBT2 (WPBT)'), # http://www.wpbt2.org
|
||||||
|
('video.wucftv.org', 'WUCF TV (WUCF)'), # http://wucftv.org
|
||||||
|
('video.wuft.org', 'WUFT/Channel 5 (WUFT)'), # http://www.wuft.org
|
||||||
|
('watch.wxel.org', 'WXEL/Channel 42 (WXEL)'), # http://www.wxel.org/home/
|
||||||
|
('video.wlrn.org', 'WLRN/Channel 17 (WLRN)'), # http://www.wlrn.org/
|
||||||
|
('video.wusf.usf.edu', 'WUSF Public Broadcasting (WUSF)'), # http://wusf.org/
|
||||||
|
('video.scetv.org', 'ETV (WRLK)'), # http://www.scetv.org
|
||||||
|
('video.unctv.org', 'UNC-TV (WUNC)'), # http://www.unctv.org/
|
||||||
|
# ('pbsguam.org', 'PBS Guam (KGTF)'), # http://www.pbsguam.org/
|
||||||
|
('video.pbshawaii.org', 'PBS Hawaii - Oceanic Cable Channel 10 (KHET)'), # http://www.pbshawaii.org/
|
||||||
|
('video.idahoptv.org', 'Idaho Public Television (KAID)'), # http://idahoptv.org
|
||||||
|
('video.ksps.org', 'KSPS (KSPS)'), # http://www.ksps.org/home/
|
||||||
|
('watch.opb.org', 'OPB (KOPB)'), # http://www.opb.org
|
||||||
|
('watch.nwptv.org', 'KWSU/Channel 10 & KTNW/Channel 31 (KWSU)'), # http://www.kwsu.org
|
||||||
|
('video.will.illinois.edu', 'WILL-TV (WILL)'), # http://will.illinois.edu/
|
||||||
|
('video.networkknowledge.tv', 'Network Knowledge - WSEC/Springfield (WSEC)'), # http://www.wsec.tv
|
||||||
|
('video.wttw.com', 'WTTW11 (WTTW)'), # http://www.wttw.com/
|
||||||
|
# ('wtvp.org', 'WTVP & WTVP.org, Public Media for Central Illinois (WTVP)'), # http://www.wtvp.org/
|
||||||
|
('video.iptv.org', 'Iowa Public Television/IPTV (KDIN)'), # http://www.iptv.org/
|
||||||
|
('video.ninenet.org', 'Nine Network (KETC)'), # http://www.ninenet.org
|
||||||
|
('video.wfwa.org', 'PBS39 Fort Wayne (WFWA)'), # http://wfwa.org/
|
||||||
|
('video.wfyi.org', 'WFYI Indianapolis (WFYI)'), # http://www.wfyi.org
|
||||||
|
('video.mptv.org', 'Milwaukee Public Television (WMVS)'), # http://www.mptv.org
|
||||||
|
('video.wnin.org', 'WNIN (WNIN)'), # http://www.wnin.org/
|
||||||
|
('video.wnit.org', 'WNIT Public Television (WNIT)'), # http://www.wnit.org/
|
||||||
|
('video.wpt.org', 'WPT (WPNE)'), # http://www.wpt.org/
|
||||||
|
('video.wvut.org', 'WVUT/Channel 22 (WVUT)'), # http://wvut.org/
|
||||||
|
('video.weiu.net', 'WEIU/Channel 51 (WEIU)'), # http://www.weiu.net
|
||||||
|
('video.wqpt.org', 'WQPT-TV (WQPT)'), # http://www.wqpt.org
|
||||||
|
('video.wycc.org', 'WYCC PBS Chicago (WYCC)'), # http://www.wycc.org
|
||||||
|
# ('lakeshorepublicmedia.org', 'Lakeshore Public Television (WYIN)'), # http://lakeshorepublicmedia.org/
|
||||||
|
('video.wipb.org', 'WIPB-TV (WIPB)'), # http://wipb.org
|
||||||
|
('video.indianapublicmedia.org', 'WTIU (WTIU)'), # http://indianapublicmedia.org/tv/
|
||||||
|
('watch.cetconnect.org', 'CET (WCET)'), # http://www.cetconnect.org
|
||||||
|
('video.thinktv.org', 'ThinkTVNetwork (WPTD)'), # http://www.thinktv.org
|
||||||
|
('video.wbgu.org', 'WBGU-TV (WBGU)'), # http://wbgu.org
|
||||||
|
('video.wgvu.org', 'WGVU TV (WGVU)'), # http://www.wgvu.org/
|
||||||
|
('video.netnebraska.org', 'NET1 (KUON)'), # http://netnebraska.org
|
||||||
|
('video.pioneer.org', 'Pioneer Public Television (KWCM)'), # http://www.pioneer.org
|
||||||
|
('watch.sdpb.org', 'SDPB Television (KUSD)'), # http://www.sdpb.org
|
||||||
|
('video.tpt.org', 'TPT (KTCA)'), # http://www.tpt.org
|
||||||
|
('watch.ksmq.org', 'KSMQ (KSMQ)'), # http://www.ksmq.org/
|
||||||
|
('watch.kpts.org', 'KPTS/Channel 8 (KPTS)'), # http://www.kpts.org/
|
||||||
|
('watch.ktwu.org', 'KTWU/Channel 11 (KTWU)'), # http://ktwu.org
|
||||||
|
# ('shptv.org', 'Smoky Hills Public Television (KOOD)'), # http://www.shptv.org
|
||||||
|
# ('kcpt.org', 'KCPT Kansas City Public Television (KCPT)'), # http://kcpt.org/
|
||||||
|
# ('blueridgepbs.org', 'Blue Ridge PBS (WBRA)'), # http://www.blueridgepbs.org/
|
||||||
|
('watch.easttennesseepbs.org', 'East Tennessee PBS (WSJK)'), # http://easttennesseepbs.org
|
||||||
|
('video.wcte.tv', 'WCTE-TV (WCTE)'), # http://www.wcte.org
|
||||||
|
('video.wljt.org', 'WLJT, Channel 11 (WLJT)'), # http://wljt.org/
|
||||||
|
('video.wosu.org', 'WOSU TV (WOSU)'), # http://wosu.org/
|
||||||
|
('video.woub.org', 'WOUB/WOUC (WOUB)'), # http://woub.org/tv/index.php?section=5
|
||||||
|
('video.wvpublic.org', 'WVPB (WVPB)'), # http://wvpublic.org/
|
||||||
|
('video.wkyupbs.org', 'WKYU-PBS (WKYU)'), # http://www.wkyupbs.org
|
||||||
|
# ('wyes.org', 'WYES-TV/New Orleans (WYES)'), # http://www.wyes.org
|
||||||
|
('video.kera.org', 'KERA 13 (KERA)'), # http://www.kera.org/
|
||||||
|
('video.mpbn.net', 'MPBN (WCBB)'), # http://www.mpbn.net/
|
||||||
|
('video.mountainlake.org', 'Mountain Lake PBS (WCFE)'), # http://www.mountainlake.org/
|
||||||
|
('video.nhptv.org', 'NHPTV (WENH)'), # http://nhptv.org/
|
||||||
|
('video.vpt.org', 'Vermont PBS (WETK)'), # http://www.vpt.org
|
||||||
|
('video.witf.org', 'witf (WITF)'), # http://www.witf.org
|
||||||
|
('watch.wqed.org', 'WQED Multimedia (WQED)'), # http://www.wqed.org/
|
||||||
|
('video.wmht.org', 'WMHT Educational Telecommunications (WMHT)'), # http://www.wmht.org/home/
|
||||||
|
('video.deltabroadcasting.org', 'Q-TV (WDCQ)'), # http://www.deltabroadcasting.org
|
||||||
|
('video.dptv.org', 'WTVS Detroit Public TV (WTVS)'), # http://www.dptv.org/
|
||||||
|
('video.wcmu.org', 'CMU Public Television (WCMU)'), # http://www.wcmu.org
|
||||||
|
('video.wkar.org', 'WKAR-TV (WKAR)'), # http://wkar.org/
|
||||||
|
('wnmuvideo.nmu.edu', 'WNMU-TV Public TV 13 (WNMU)'), # http://wnmutv.nmu.edu
|
||||||
|
('video.wdse.org', 'WDSE - WRPT (WDSE)'), # http://www.wdse.org/
|
||||||
|
('video.wgte.org', 'WGTE TV (WGTE)'), # http://www.wgte.org
|
||||||
|
('video.lptv.org', 'Lakeland Public Television (KAWE)'), # http://www.lakelandptv.org
|
||||||
|
# ('prairiepublic.org', 'PRAIRIE PUBLIC (KFME)'), # http://www.prairiepublic.org/
|
||||||
|
('video.kmos.org', 'KMOS-TV - Channels 6.1, 6.2 and 6.3 (KMOS)'), # http://www.kmos.org/
|
||||||
|
('watch.montanapbs.org', 'MontanaPBS (KUSM)'), # http://montanapbs.org
|
||||||
|
('video.krwg.org', 'KRWG/Channel 22 (KRWG)'), # http://www.krwg.org
|
||||||
|
('video.kacvtv.org', 'KACV (KACV)'), # http://www.panhandlepbs.org/home/
|
||||||
|
('video.kcostv.org', 'KCOS/Channel 13 (KCOS)'), # www.kcostv.org
|
||||||
|
('video.wcny.org', 'WCNY/Channel 24 (WCNY)'), # http://www.wcny.org
|
||||||
|
('video.wned.org', 'WNED (WNED)'), # http://www.wned.org/
|
||||||
|
('watch.wpbstv.org', 'WPBS (WPBS)'), # http://www.wpbstv.org
|
||||||
|
('video.wskg.org', 'WSKG Public TV (WSKG)'), # http://wskg.org
|
||||||
|
('video.wxxi.org', 'WXXI (WXXI)'), # http://wxxi.org
|
||||||
|
('video.wpsu.org', 'WPSU (WPSU)'), # http://www.wpsu.org
|
||||||
|
# ('wqln.org', 'WQLN/Channel 54 (WQLN)'), # http://www.wqln.org
|
||||||
|
('on-demand.wvia.org', 'WVIA Public Media Studios (WVIA)'), # http://www.wvia.org/
|
||||||
|
('video.wtvi.org', 'WTVI (WTVI)'), # http://www.wtvi.org/
|
||||||
|
# ('whro.org', 'WHRO (WHRO)'), # http://whro.org
|
||||||
|
('video.westernreservepublicmedia.org', 'Western Reserve PBS (WNEO)'), # http://www.WesternReservePublicMedia.org/
|
||||||
|
('video.ideastream.org', 'WVIZ/PBS ideastream (WVIZ)'), # http://www.wviz.org/
|
||||||
|
('video.kcts9.org', 'KCTS 9 (KCTS)'), # http://kcts9.org/
|
||||||
|
('video.basinpbs.org', 'Basin PBS (KPBT)'), # http://www.basinpbs.org
|
||||||
|
('video.houstonpbs.org', 'KUHT / Channel 8 (KUHT)'), # http://www.houstonpublicmedia.org/
|
||||||
|
# ('tamu.edu', 'KAMU - TV (KAMU)'), # http://KAMU.tamu.edu
|
||||||
|
# ('kedt.org', 'KEDT/Channel 16 (KEDT)'), # http://www.kedt.org
|
||||||
|
('video.klrn.org', 'KLRN (KLRN)'), # http://www.klrn.org
|
||||||
|
('video.klru.tv', 'KLRU (KLRU)'), # http://www.klru.org
|
||||||
|
# ('kmbh.org', 'KMBH-TV (KMBH)'), # http://www.kmbh.org
|
||||||
|
# ('knct.org', 'KNCT (KNCT)'), # http://www.knct.org
|
||||||
|
# ('ktxt.org', 'KTTZ-TV (KTXT)'), # http://www.ktxt.org
|
||||||
|
('video.wtjx.org', 'WTJX Channel 12 (WTJX)'), # http://www.wtjx.org/
|
||||||
|
('video.ideastations.org', 'WCVE PBS (WCVE)'), # http://ideastations.org/
|
||||||
|
('video.kbtc.org', 'KBTC Public Television (KBTC)'), # http://kbtc.org
|
||||||
|
)
|
||||||
|
|
||||||
|
IE_NAME = 'pbs'
|
||||||
|
IE_DESC = 'Public Broadcasting Service (PBS) and member stations: %s' % ', '.join(list(zip(*_STATIONS))[1])
|
||||||
|
|
||||||
_VALID_URL = r'''(?x)https?://
|
_VALID_URL = r'''(?x)https?://
|
||||||
(?:
|
(?:
|
||||||
# Direct video URL
|
# Direct video URL
|
||||||
video\.pbs\.org/(?:viralplayer|video)/(?P<id>[0-9]+)/? |
|
(?:%s)/(?:viralplayer|video)/(?P<id>[0-9]+)/? |
|
||||||
# Article with embedded player (or direct video)
|
# Article with embedded player (or direct video)
|
||||||
(?:www\.)?pbs\.org/(?:[^/]+/){2,5}(?P<presumptive_id>[^/]+?)(?:\.html)?/?(?:$|[?\#]) |
|
(?:www\.)?pbs\.org/(?:[^/]+/){2,5}(?P<presumptive_id>[^/]+?)(?:\.html)?/?(?:$|[?\#]) |
|
||||||
# Player
|
# Player
|
||||||
video\.pbs\.org/(?:widget/)?partnerplayer/(?P<player_id>[^/]+)/
|
(?:video|player)\.pbs\.org/(?:widget/)?partnerplayer/(?P<player_id>[^/]+)/
|
||||||
)
|
)
|
||||||
'''
|
''' % '|'.join(re.escape(p) for p in list(zip(*_STATIONS))[0])
|
||||||
|
|
||||||
_TESTS = [
|
_TESTS = [
|
||||||
{
|
{
|
||||||
@@ -170,6 +335,14 @@ class PBSIE(InfoExtractor):
|
|||||||
'params': {
|
'params': {
|
||||||
'skip_download': True, # requires ffmpeg
|
'skip_download': True, # requires ffmpeg
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'url': 'http://player.pbs.org/widget/partnerplayer/2365297708/?start=0&end=0&chapterbar=false&endscreen=false&topbar=true',
|
||||||
|
'only_matching': True,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'url': 'http://watch.knpb.org/video/2365616055/',
|
||||||
|
'only_matching': True,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
_ERRORS = {
|
_ERRORS = {
|
||||||
@@ -200,6 +373,7 @@ class PBSIE(InfoExtractor):
|
|||||||
MEDIA_ID_REGEXES = [
|
MEDIA_ID_REGEXES = [
|
||||||
r"div\s*:\s*'videoembed'\s*,\s*mediaid\s*:\s*'(\d+)'", # frontline video embed
|
r"div\s*:\s*'videoembed'\s*,\s*mediaid\s*:\s*'(\d+)'", # frontline video embed
|
||||||
r'class="coveplayerid">([^<]+)<', # coveplayer
|
r'class="coveplayerid">([^<]+)<', # coveplayer
|
||||||
|
r'<section[^>]+data-coveid="(\d+)"', # coveplayer from http://www.pbs.org/wgbh/frontline/film/real-csi/
|
||||||
r'<input type="hidden" id="pbs_video_id_[0-9]+" value="([0-9]+)"/>', # jwplayer
|
r'<input type="hidden" id="pbs_video_id_[0-9]+" value="([0-9]+)"/>', # jwplayer
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -259,7 +433,7 @@ class PBSIE(InfoExtractor):
|
|||||||
return self.playlist_result(entries, display_id)
|
return self.playlist_result(entries, display_id)
|
||||||
|
|
||||||
info = self._download_json(
|
info = self._download_json(
|
||||||
'http://video.pbs.org/videoInfo/%s?format=json&type=partner' % video_id,
|
'http://player.pbs.org/videoInfo/%s?format=json&type=partner' % video_id,
|
||||||
display_id)
|
display_id)
|
||||||
|
|
||||||
formats = []
|
formats = []
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user