Compare commits

..

1069 Commits

Author SHA1 Message Date
Sergey M․ 7072c76395 Start moving to ytdl-org 2019-03-09 18:19:40 +07:00
Sergey M․ 366611a433 release 2019.03.09 2019-03-09 02:55:40 +07:00
Sergey M․ 2cdb4cbda8 release 2019.03.01 2019-03-01 01:05:49 +07:00
Sergey M․ b7c4159cf4 release 2019.02.18 2019-02-18 02:13:07 +07:00
Sergey M․ 776ab5d197 release 2019.02.08 2019-02-08 01:14:34 +07:00
Sergey M․ c8c49ed9f2 release 2019.01.30.1 2019-01-30 06:21:19 +07:00
Sergey M․ 8f784279fa release 2019.01.30 2019-01-30 00:10:39 +07:00
Sergey M․ 9efd7102bf release 2019.01.27 2019-01-27 21:30:06 +07:00
Sergey M․ d8ed44d0c5 release 2019.01.24 2019-01-24 01:47:59 +07:00
Sergey M․ f8c035788c release 2019.01.23 2019-01-23 04:49:04 +07:00
Sergey M․ 9ffe8f3881 release 2019.01.17 2019-01-17 10:29:19 +07:00
Sergey M․ dbf36b0f0d release 2019.01.16 2019-01-16 02:25:56 +07:00
Sergey M․ 3d818b9608 release 2019.01.10 2019-01-10 23:33:51 +07:00
Sergey M․ 33479e1148 release 2019.01.02 2019-01-02 23:54:41 +07:00
Sergey M․ 80a8975990 release 2018.12.31 2019-01-01 00:02:02 +07:00
Sergey M․ 025607be72 release 2018.12.17 2018-12-17 05:39:47 +07:00
Sergey M․ 0780a7264f release 2018.12.09 2018-12-09 23:13:41 +07:00
Sergey M․ b58b254a97 release 2018.12.03 2018-12-03 00:12:07 +07:00
Sergey M․ bc966de080 release 2018.11.23 2018-11-23 00:18:34 +07:00
Sergey M․ cbee43ed19 release 2018.11.18 2018-11-18 00:13:43 +07:00
Sergey M․ 882e952aef release 2018.11.07 2018-11-07 01:40:08 +07:00
Sergey M․ 5f154c6cea release 2018.11.03 2018-11-03 02:59:23 +07:00
Sergey M․ eb6f7c190b release 2018.10.29 2018-10-29 00:41:13 +07:00
Sergey M․ c2a2a95eee release 2018.10.05 2018-10-05 02:33:34 +07:00
Sergey M․ 33c2f66cce release 2018.09.26 2018-09-26 12:00:35 +07:00
Sergey M․ 7c9892610d release 2018.09.18 2018-09-18 01:48:40 +07:00
Sergey M․ 14a0b8b228 release 2018.09.10 2018-09-10 02:50:48 +07:00
Sergey M․ d5c28fe83a release 2018.09.08 2018-09-08 03:44:54 +07:00
Sergey M․ c215f09b95 release 2018.09.01 2018-09-01 18:42:18 +07:00
Sergey M․ e95b4e63a0 release 2018.08.28 2018-08-28 03:13:04 +07:00
Sergey M․ 830431d0d8 release 2018.08.22 2018-08-22 02:34:36 +07:00
Sergey M․ e10516b65d release 2018.08.04 2018-08-04 01:25:44 +07:00
Sergey M․ 3651ec647c release 2018.07.29 2018-07-29 07:04:22 +07:00
Sergey M․ 31027fac78 release 2018.07.21 2018-07-21 21:02:27 +07:00
Sergey M․ dd13bb2901 release 2018.07.10 2018-07-10 02:11:28 +07:00
Sergey M․ 1ef6d64f84 release 2018.07.04 2018-07-04 05:01:26 +07:00
Sergey M․ ea0af571a2 release 2018.06.25 2018-06-25 02:28:19 +07:00
Sergey M․ b9f6f5400b release 2018.06.19 2018-06-19 23:18:09 +07:00
Sergey M․ a6871badf8 release 2018.06.18 2018-06-18 01:36:23 +07:00
Sergey M․ 9cb5e51de0 release 2018.06.14 2018-06-14 01:27:01 +07:00
Sergey M․ 76dbdca39f release 2018.06.11 2018-06-11 01:59:24 +07:00
Sergey M․ bef98ad2f4 release 2018.06.04 2018-06-04 02:43:51 +07:00
Sergey M․ 3a8ea8d17a Actualize copyrights 2018-06-04 02:39:30 +07:00
Sergey M․ efe19c0d37 release 2018.06.02 2018-06-02 01:53:07 +07:00
Sergey M․ 7db2ace54b release 2018.05.30 2018-05-30 21:56:13 +07:00
Sergey M․ 610d07eca7 release 2018.05.26 2018-05-26 13:04:23 +07:00
Sergey M․ cff7ad06d7 release 2018.05.18 2018-05-18 00:34:43 +07:00
Sergey M․ 493beb72af release 2018.05.09 2018-05-09 00:38:22 +07:00
Sergey M․ 0f2ff4f2e2 release 2018.05.01 2018-05-01 03:41:20 +07:00
Sergey M․ b9f748bdcb release 2018.04.25 2018-04-25 02:03:29 +07:00
Sergey M․ c94a4a7d3f release 2018.04.16 2018-04-16 01:11:13 +07:00
Sergey M․ 8e6efd6191 release 2018.04.09 2018-04-09 01:21:26 +07:00
Sergey M․ 99b8fd6cd3 release 2018.04.03 2018-04-03 00:28:34 +07:00
Sergey M․ f2411c698b release 2018.03.26.1 2018-03-26 22:33:42 +07:00
Sergey M․ 63e1223703 release 2018.03.26 2018-03-26 22:27:17 +07:00
Sergey M․ 2dd46e74a2 release 2018.03.20 2018-03-20 01:58:01 +07:00
Sergey M․ 2d59dbf140 release 2018.03.14 2018-03-14 22:51:48 +07:00
Sergey M․ 0ea14737af release 2018.03.10 2018-03-10 04:48:00 +07:00
Sergey M․ b8b7dbbaef release 2018.03.03 2018-03-03 01:38:48 +07:00
Sergey M․ 40c0ea163e release 2018.02.26 2018-02-26 04:25:23 +07:00
Sergey M․ 0e9e748a01 release 2018.02.25 2018-02-25 20:53:48 +07:00
Sergey M․ 540d4d0308 release 2018.02.22 2018-02-22 23:52:36 +07:00
Sergey M․ 679abbc99e release 2018.02.11 2018-02-11 22:33:31 +07:00
Sergey M․ c291983979 release 2018.02.08 2018-02-08 23:41:34 +07:00
Sergey M․ 42f75d4d45 release 2018.02.04 2018-02-04 08:04:48 +07:00
Sergey M․ 929045923f release 2018.02.03 2018-02-03 23:46:12 +07:00
Sergey M․ 3fc2784af8 release 2018.01.27 2018-01-27 23:46:12 +07:00
Sergey M․ 2c2dd943e0 release 2018.01.21 2018-01-21 21:27:47 +07:00
Sergey M․ e655f10673 release 2018.01.18 2018-01-18 23:43:45 +07:00
Sergey M․ 49aea39b26 release 2018.01.14 2018-01-14 00:15:39 +07:00
Sergey M․ 55a35ce9a1 release 2018.01.07 2018-01-07 23:54:06 +07:00
Sergey M․ 8457b78f9d release 2017.12.31 2017-12-31 04:32:33 +07:00
Sergey M․ 23e4d4fee3 release 2017.12.28 2017-12-28 23:14:59 +07:00
Sergey M․ 81153fdc24 release 2017.12.23 2017-12-23 21:26:27 +07:00
Sergey M․ f7644f0ffd release 2017.12.14 2017-12-14 05:21:18 +07:00
Sergey M․ 4a79deb63d release 2017.12.10 2017-12-10 23:20:52 +07:00
Sergey M․ 4e748007fc release 2017.12.02 2017-12-02 21:36:37 +07:00
Sergey M․ 0bc87bb732 release 2017.11.26 2017-11-26 21:51:15 +07:00
Sergey M․ 33db337845 release 2017.11.15 2017-11-15 00:21:39 +07:00
Sergey M․ 0f2f505c2f release 2017.11.06 2017-11-06 22:43:04 +07:00
Sergey M․ 71bff58a00 release 2017.10.29 2017-10-29 07:24:53 +07:00
Sergey M․ 2a11c83e32 release 2017.10.20 2017-10-20 23:42:43 +07:00
Sergey M․ f6a68a76d3 release 2017.10.15.1 2017-10-15 06:18:34 +07:00
Sergey M․ d451428eb0 release 2017.10.15 2017-10-15 02:28:59 +07:00
Sergey M․ 004938f463 release 2017.10.12 2017-10-12 01:08:25 +07:00
Sergey M․ d810a3fd73 release 2017.10.07 2017-10-07 05:04:53 +07:00
Sergey M․ 0b4bb07d96 release 2017.10.01 2017-10-01 21:56:35 +07:00
Sergey M․ 7033a5c718 release 2017.09.24 2017-09-24 00:24:55 +07:00
Sergey M․ 19d55d19f1 release 2017.09.15 2017-09-15 21:50:11 +07:00
Sergey M․ a17cf2f21e release 2017.09.11 2017-09-11 03:32:37 +07:00
Sergey M․ 0559e642f3 release 2017.09.10 2017-09-10 22:21:15 +07:00
Sergey M․ 879f4b5a4b release 2017.09.02 2017-09-02 01:10:22 +07:00
Sergey M․ f1c88434fe release 2017.08.27.1 2017-08-27 06:11:35 +07:00
Sergey M․ 329002a07d release 2017.08.27 2017-08-27 04:30:22 +07:00
Sergey M․ 0bd3d4f43b release 2017.08.23 2017-08-23 23:25:09 +07:00
Sergey M․ 0d842f0931 release 2017.08.18 2017-08-18 01:07:28 +07:00
Sergey M․ 7ffb188d0e release 2017.08.13 2017-08-13 09:00:21 +07:00
Sergey M․ 53653526df release 2017.08.09 2017-08-09 23:54:16 +07:00
Sergey M․ 3f7778cff5 release 2017.08.06 2017-08-06 09:07:41 +07:00
Sergey M․ f502243adb release 2017.07.30.1 2017-07-30 20:49:10 +07:00
Sergey M․ 551e77c1cd release 2017.07.23 2017-07-23 01:10:58 +07:00
Sergey M․ 234fc749eb release 2017.07.15 2017-07-15 07:38:20 +07:00
Sergey M․ 3979f9d91b Actualize about page 2017-07-15 07:25:53 +07:00
Sergey M․ 2baebebe4a release 2017.07.09 2017-07-09 20:18:41 +07:00
Sergey M․ d0cd4e653b release 2017.07.02 2017-07-02 20:18:56 +07:00
Sergey M․ 4dcb6bc04a release 2017.06.25 2017-06-25 05:17:55 +07:00
Sergey M․ 78844ace39 release 2017.06.23 2017-06-23 02:15:01 +07:00
Sergey M․ dcfd9d7a45 release 2017.06.18 2017-06-18 00:18:41 +07:00
Sergey M․ dccd806cb3 release 2017.06.12 2017-06-12 02:25:02 +07:00
Sergey M․ 4096a0d66f release 2017.06.05 2017-06-05 00:49:58 +07:00
Sergey M․ 76f8048076 release 2017.05.29 2017-05-29 00:43:56 +07:00
Sergey M․ 2742d23142 release 2017.05.26 2017-05-26 22:47:49 +07:00
Sergey M․ 5f170b1bda release 2017.05.23 2017-05-23 00:19:45 +07:00
Sergey M․ bf10513151 release 2017.05.18.1 2017-05-18 23:01:39 +07:00
Sergey M․ 3b2623455b release 2017.05.18 2017-05-18 22:36:12 +07:00
Sergey M․ 48fb22678c release 2017.05.14 2017-05-14 07:40:21 +07:00
Sergey M․ 28b168333b release 2017.05.09 2017-05-09 04:22:36 +07:00
Sergey M․ 253c51a16e release 2017.05.07 2017-05-07 04:53:26 +07:00
Sergey M․ 38de738253 release 2017.05.01 2017-05-01 01:42:55 +07:00
Sergey M․ 098929c2c6 release 2017.04.28 2017-04-28 18:31:43 +07:00
Sergey M․ b876a9cd72 release 2017.04.26 2017-04-26 00:08:50 +07:00
Sergey M․ 629dc1892f release 2017.04.17 2017-04-17 00:48:58 +07:00
Sergey M․ 4b8c87e5ae release 2017.04.16 2017-04-16 01:51:15 +07:00
Sergey M․ 83a5be5c78 release 2017.04.15 2017-04-15 01:41:40 +07:00
Sergey M․ 4e0652e5fc release 2017.04.14 2017-04-14 00:33:59 +07:00
Sergey M․ 54ebda6963 release 2017.04.11 2017-04-11 02:20:05 +07:00
Sergey M․ a9af16af9d release 2017.04.09 2017-04-09 00:03:53 +07:00
Sergey M․ 59f724af05 release 2017.04.03 2017-04-03 03:56:26 +07:00
Sergey M․ da53aa217d release 2017.04.02 2017-04-02 02:40:50 +07:00
Sergey M․ 9b1a20166b release 2017.03.26 2017-03-26 08:13:15 +07:00
Sergey M․ fb6696ca3a release 2017.03.24 2017-03-24 00:26:02 +07:00
Sergey M․ 112ed9c7ab release 2017.03.22 2017-03-22 02:49:04 +07:00
Sergey M․ 14d7ce7a21 release 2017.03.20 2017-03-20 00:09:38 +07:00
Sergey M․ 1cabc6acc3 release 2017.03.16 2017-03-16 22:44:31 +07:00
Sergey M․ e327cee999 release 2017.03.15 2017-03-15 02:04:31 +07:00
Sergey M․ 366ab6fbd4 release 2017.03.10 2017-03-10 23:29:18 +07:00
Sergey M․ 69d33a207a release 2017.03.07 2017-03-07 04:00:57 +07:00
Sergey M․ af016730ce release 2017.03.06 2017-03-06 04:06:16 +07:00
Sergey M․ 92541af62c release 2017.03.05 2017-03-05 02:25:16 +07:00
Sergey M․ b2066bc974 release 2017.03.02 2017-03-03 00:21:47 +07:00
Sergey M․ dd6c468984 release 2017.02.28 2017-03-01 00:14:41 +07:00
Sergey M․ 0202d38902 release 2017.02.27 2017-02-27 23:30:02 +07:00
Sergey M․ 83b62c9084 release 2017.02.24.1 2017-02-24 22:01:27 +07:00
Sergey M․ a428893aba release 2017.02.24 2017-02-24 06:11:14 +07:00
Sergey M․ ed83a1aefb release 2017.02.22 2017-02-22 23:52:35 +07:00
Sergey M․ e9a4b18b92 release 2017.02.21 2017-02-21 23:50:31 +07:00
Sergey M․ f4b623972c release 2017.02.17 2017-02-18 00:02:14 +07:00
Sergey M․ da22904fb0 release 2017.02.16 2017-02-16 00:11:50 +07:00
Sergey M․ 15751f4b67 release 2017.02.14 2017-02-14 01:11:58 +07:00
Sergey M․ 797a77034c release 2017.02.11 2017-02-11 03:25:44 +07:00
Sergey M․ f339ed8700 release 2017.02.10 2017-02-10 01:29:30 +07:00
Sergey M․ b3b47f32a8 release 2017.02.07 2017-02-07 02:06:54 +07:00
Sergey M․ e48688b313 release 2017.02.04.1 2017-02-04 23:28:16 +07:00
Sergey M․ 0696cdc164 release 2017.02.04 2017-02-04 23:00:00 +07:00
Sergey M․ 4f0a956f84 release 2017.02.01 2017-02-01 03:22:17 +07:00
Sergey M․ 932b2a23f1 release 2017.01.31 2017-01-31 22:26:42 +07:00
Sergey M․ efcd0e8d13 release 2017.01.29 2017-01-29 13:23:27 +07:00
Sergey M․ 0f766d3382 release 2017.01.28 2017-01-28 00:36:01 +07:00
Sergey M․ 9f85dd5863 release 2017.01.25 2017-01-25 23:37:51 +07:00
Sergey M․ c445b0e36c release 2017.01.24 2017-01-24 03:00:59 +07:00
Sergey M․ 92fb683c67 release 2017.01.22 2017-01-22 19:01:16 +07:00
Sergey M․ d549f3d89e release 2017.01.18 2017-01-18 23:11:56 +07:00
Sergey M․ 51feebce2a release 2017.01.16 2017-01-16 23:46:07 +07:00
Sergey M․ 38438fdf67 release 2017.01.14 2017-01-14 07:33:02 +07:00
Sergey M․ 8999c1e5ca release 2017.01.10 2017-01-10 21:31:15 +07:00
Sergey M․ 4b53472d1a release 2017.01.08 2017-01-08 21:00:21 +07:00
Sergey M․ 9655675e93 release 2017.01.05 2017-01-05 04:12:15 +07:00
Sergey M․ 56c3abce9a release 2017.01.02 2017-01-02 23:57:04 +07:00
Sergey M․ b7cf13240d release 2016.12.31 2017-01-01 00:00:44 +07:00
Sergey M․ 8454ae5c66 release 2016.12.22 2016-12-22 22:54:48 +07:00
Sergey M․ 89155cba2f release 2016.12.20 2016-12-20 22:41:33 +07:00
Sergey M․ d6210cc567 release 2016.12.18 2016-12-18 19:52:37 +07:00
Sergey M․ 64368043c2 release 2016.12.15 2016-12-15 21:18:40 +07:00
Sergey M․ 9afe56446e release 2016.12.12 2016-12-12 01:46:48 +07:00
Sergey M․ e58db47e88 release 2016.12.09 2016-12-09 02:48:10 +07:00
Sergey M․ b3fce8f4b0 release 2016.12.01 2016-12-01 00:16:55 +07:00
Sergey M․ bbc77dc77b release 2016.11.27 2016-11-27 20:07:45 +07:00
Sergey M․ 5dd9c9bd8e release 2016.11.22 2016-11-22 22:34:14 +07:00
Sergey M․ 872e22ef7a release 2016.11.18 2016-11-18 00:27:06 +07:00
Sergey M․ b70620141d release 2016.11.14.1 2016-11-14 02:49:51 +07:00
Sergey M․ 5abd12d6db release 2016.11.08.1 2016-11-08 22:32:44 +07:00
Sergey M․ 0d64f775fd release 2016.11.08 2016-11-08 22:13:07 +07:00
Sergey M․ 498a616957 release 2016.11.04 2016-11-04 22:10:57 +07:00
Sergey M․ df811195b3 release 2016.11.02 2016-11-02 02:41:14 +07:00
Sergey M․ 8a4416f196 release 2016.10.31 2016-10-31 01:38:45 +07:00
Sergey M․ 078a5ae930 release 2016.10.26 2016-10-26 19:57:26 +07:00
Sergey M․ 28bfe57d98 release 2016.10.25 2016-10-25 03:21:27 +07:00
Sergey M․ f35f2c3054 release 2016.10.21.1 2016-10-21 05:01:59 +07:00
Sergey M․ d979231c90 release 2016.10.21 2016-10-21 03:14:17 +07:00
Sergey M․ b408c2cee9 release 2016.10.19 2016-10-19 23:58:20 +07:00
Sergey M․ 2a230e0d14 release 2016.10.16 2016-10-16 03:26:09 +07:00
Sergey M․ 23aa1b6d8a release 2016.10.12 2016-10-12 21:32:32 +07:00
Sergey M․ a054e619cc release 2016.10.07 2016-10-07 22:27:15 +07:00
Sergey M․ a2a5e76628 release 2016.10.02 2016-10-02 15:59:38 +07:00
Sergey M․ b7538715d6 release 2016.09.27 2016-09-27 23:13:50 +07:00
Sergey M․ d3a2da730d release 2016.09.24 2016-09-24 02:18:35 +07:00
Sergey M․ 54d490ab70 release 2016.09.19 2016-09-19 03:00:23 +07:00
Sergey M․ c5647fb553 release 2016.09.18 2016-09-18 17:19:22 +07:00
Sergey M․ cd9433026d release 2016.09.15 2016-09-15 23:51:59 +07:00
Sergey M․ 09bb21c5c1 release 2016.09.11.1 2016-09-11 23:36:05 +07:00
Sergey M․ f50f62f3ba release 2016.09.11 2016-09-11 23:22:44 +07:00
Sergey M․ e0217ea0c7 release 2016.09.08 2016-09-08 23:49:54 +07:00
Sergey M․ 9515d34cdf Put donations info into separate paragraph 2016-09-08 23:44:37 +07:00
Philipp Hagemeister f8e043ccff Merge pull request #10590 from prayashm/patch-1
Add donations.html link
2016-09-08 14:55:53 +02:00
Prayash Mohapatra 55ae1a5152 Add donations.html link 2016-09-08 02:17:57 +05:30
Sergey M․ fff5d4c3da release 2016.09.04.1 2016-09-04 21:00:18 +07:00
Sergey M․ ae7c722607 release 2016.09.04 2016-09-04 20:54:02 +07:00
Sergey M․ 7bafd90591 release 2016.09.03 2016-09-03 01:49:22 +07:00
Sergey M․ 384c6af240 release 2016.08.31 2016-08-31 02:41:46 +07:00
Sergey M․ ca3d1c51dc release 2016.08.28 2016-08-28 07:25:37 +07:00
Sergey M․ f18c4ec060 release 2016.08.24.1 2016-08-24 10:13:08 +07:00
Sergey M․ 37f34abf6b release 2016.08.24 2016-08-24 01:41:06 +07:00
Philipp Hagemeister b65bb17fd9 highlight actual fingerprints
It has always been possible to generate keys with the same key ID (it's only 32 Bit, duh). There has now been a public demonstration of just that.
Do what we should have done ages ago by listing the full fingerprint only.
2016-08-22 01:26:41 +02:00
Sergey M․ 33f2f9f683 release 2016.08.22 2016-08-22 04:19:28 +07:00
Sergey M․ 9f87328418 release 2016.08.19 2016-08-19 00:17:23 +07:00
Sergey M․ b46638472a release 2016.08.17 2016-08-17 06:23:32 +07:00
Sergey M․ ddc28e92b9 release 2016.08.13 2016-08-13 23:19:51 +07:00
Sergey M․ 082742d195 release 2016.08.12 2016-08-12 00:24:30 +07:00
Philipp Hagemeister 310ec3b601 download.html.in: Remove outdated warning
No current webbrowser I am aware of has this behavior.
2016-08-09 20:03:20 +02:00
Sergey M․ dc27b9ef78 release 2016.08.10 2016-08-10 00:23:47 +07:00
Sergey M․ 99e1e39110 release 2016.08.07 2016-08-07 21:15:26 +07:00
Sergey M․ 5139edb775 release 2016.08.06 2016-08-06 01:25:42 +07:00
Sergey M․ 74cf4844c8 release 2016.08.01 2016-08-01 23:01:50 +07:00
Sergey M․ 22625eefbc release 2016.07.30 2016-07-30 14:47:34 +07:00
Sergey M․ 9b52314abf release 2016.07.28 2016-07-28 02:44:58 +07:00
Sergey M․ 3f6ea8989c release 2016.07.26.2 2016-07-26 23:59:39 +07:00
Sergey M․ ecd7dd9d08 release 2016.07.24 2016-07-24 11:41:33 +07:00
Sergey M․ c7bd93ab2f Actualize about page 2016-07-24 11:37:23 +07:00
Sergey M․ 10e07d485f release 2016.07.22 2016-07-22 23:13:47 +07:00
Sergey M․ 9d6f60a694 release 2016.07.17 2016-07-17 19:07:37 +07:00
Sergey M․ 3dbb634bf3 Use latest in download URLs 2016-07-17 18:56:36 +07:00
Sergey M․ 3533ba6d46 release 2016.07.16 2016-07-16 02:21:57 +07:00
Sergey M․ 252faa3af8 release 2016.07.13 2016-07-14 00:00:39 +07:00
Philipp Hagemeister 06da7aaca8 download.html: correct design 2016-07-12 00:15:59 +02:00
Philipp Hagemeister 55c68753df download.html: only use SHA256 sum
MD5 and to a lesser degree SHA1 are no longer useful to detect malicious modfication, so don't mention them.
Clarify that the numbers after the exe are hashsums.
Closes #10066
2016-07-12 00:15:17 +02:00
Sergey M․ 11e59ef04e release 2016.07.11 2016-07-11 03:19:00 +07:00
Sergey M․ b6b3380673 release 2016.07.09.2 2016-07-09 22:24:33 +07:00
Sergey M․ 9ebe17e80d release 2016.07.09.1 2016-07-09 10:08:07 +07:00
Sergey M․ 1559154b5a release 2016.07.09 2016-07-09 07:19:01 +07:00
Sergey M․ f84c714975 release 2016.07.07 2016-07-07 01:56:07 +07:00
Sergey M․ e1ae332b66 release 2016.07.06 2016-07-06 00:56:17 +07:00
Sergey M․ 51b27bbd59 release 2016.07.05 2016-07-05 06:35:15 +07:00
Sergey M․ c3eaebf2b3 release 2016.07.03.1 2016-07-03 21:31:18 +07:00
Sergey M․ 24ddfc3d4c release 2016.07.03 2016-07-03 03:21:10 +07:00
Philipp Hagemeister d894820391 Move @FiloSottile's key to historical 2016-07-01 22:12:56 +02:00
Sergey M․ 3ca7a40328 release 2016.07.02 2016-07-02 02:49:58 +07:00
Sergey M․ 7943fc726f release 2016.07.01 2016-07-01 04:01:45 +07:00
Sergey M․ e6c82a724d release 2016.06.30 2016-06-30 23:59:14 +07:00
Sergey M․ a029847fad release 2016.06.27 2016-06-27 23:12:15 +07:00
Sergey M․ 3aa66003a2 release 2016.06.26 2016-06-26 21:17:53 +07:00
Sergey M․ 0d208ed61d release 2016.06.25 2016-06-25 03:04:53 +07:00
Sergey M․ 7d32baaca9 release 2016.06.23.1 2016-06-23 09:44:24 +07:00
Sergey M․ 0b1a935eac release 2016.06.23 2016-06-23 04:31:07 +07:00
Sergey M․ ce64943931 release 2016.06.22 2016-06-22 23:46:10 +07:00
Sergey M․ 772283aa5f release 2016.06.20 2016-06-20 20:52:18 +07:00
Sergey M․ 5d5615f148 release 2016.06.19.1 2016-06-19 03:59:03 +07:00
Sergey M․ d47189bbcc release 2016.06.19 2016-06-19 02:33:32 +07:00
Sergey M․ 658975165e Add link to more sites 2016-06-18 22:12:24 +07:00
Sergey M․ d6d7600e37 Remove 'small' from description (Closes #9814) 2016-06-18 22:07:43 +07:00
Sergey M․ 2b60161e60 release 2016.06.18.1 2016-06-18 06:22:31 +07:00
Sergey M․ d7c814e4f7 release 2016.06.16 2016-06-16 22:52:30 +07:00
Sergey M․ 058c2daacc release 2016.06.14 2016-06-14 02:22:04 +07:00
Sergey M․ 145c9da564 release 2016.06.12 2016-06-12 12:08:33 +07:00
Sergey M․ 4cdb53c6ed release 2016.06.11.3 2016-06-11 08:35:40 +07:00
Sergey M․ 4c6805ceb2 release 2016.06.11.1 2016-06-11 03:02:13 +07:00
Sergey M․ dfb01371c8 release 2016.06.11 2016-06-11 02:44:49 +07:00
Sergey M․ af480e49ed Add -L to curl command line 2016-06-11 02:30:37 +07:00
Sergey M․ 8793251a03 release 2016.06.03 2016-06-03 23:27:48 +07:00
Sergey M․ 3d4970c6c0 release 2016.06.02 2016-06-02 01:21:51 +07:00
Sergey M․ 579ce0021e release 2016.05.30.2 2016-05-30 03:13:19 +07:00
Sergey M․ c063643e0f release 2016.05.30.1 2016-05-30 03:04:42 +07:00
Sergey M․ 1ab2908c7a release 2016.05.30 2016-05-30 01:19:32 +07:00
Sergey M․ d761e8a499 Clarify c runtime dependency 2016-05-30 00:54:48 +07:00
Sergey M․ 16a78ee8a8 Clarify Windows build 2016-05-29 12:42:10 +06:00
Sergey M․ a4084fc999 Add myself to GPG keys list 2016-05-29 12:25:17 +06:00
Philipp Hagemeister 27425e1e20 release 2016.05.21.2 2016-05-21 21:50:48 +02:00
Philipp Hagemeister 9f24d385e1 release 2016.05.16 2016-05-16 17:28:38 +02:00
Philipp Hagemeister 2b2f5d9156 release 2016.05.10 2016-05-10 09:14:08 +02:00
Philipp Hagemeister 4b15902432 release 2016.05.01 2016-05-01 10:30:29 +02:00
Philipp Hagemeister ef9ade0b77 release 2016.04.24 2016-04-24 17:09:44 +02:00
Philipp Hagemeister c11b84913a release 2016.04.19 2016-04-19 03:10:06 +02:00
Philipp Hagemeister 054156ac27 release 2016.04.13 2016-04-13 08:05:08 +02:00
Philipp Hagemeister 4388049201 release 2016.04.06 2016-04-06 15:16:29 +02:00
Philipp Hagemeister 737468e540 release 2016.04.05 2016-04-05 18:39:14 +02:00
Philipp Hagemeister e8c339da56 release 2016.04.01 2016-04-01 09:13:47 +02:00
Philipp Hagemeister 4555a7c83a release 2016.03.27 2016-03-27 17:00:27 +02:00
Philipp Hagemeister 9a71eb490e release 2016.03.26 2016-03-26 09:01:03 +01:00
Philipp Hagemeister 3d547b08c0 release 2016.03.25 2016-03-25 09:30:38 +01:00
Philipp Hagemeister 25982d3f66 release 2016.03.18 2016-03-18 11:46:46 +01:00
Philipp Hagemeister 74afb16e77 release 2016.03.14 2016-03-14 10:29:54 +01:00
Philipp Hagemeister 63e3ee4264 release 2016.03.06 2016-03-06 10:13:35 +01:00
Philipp Hagemeister fe10c160cc release 2016.03.01 2016-03-01 00:11:09 +01:00
Philipp Hagemeister c04b8cca69 release 2016.02.27 2016-02-27 21:41:57 +01:00
Philipp Hagemeister a3c2bdf818 release 2016.02.22 2016-02-22 12:01:27 +01:00
Philipp Hagemeister 8f8a03e36a release 2016.02.13 2016-02-13 08:30:15 +01:00
Philipp Hagemeister c1f884ce73 release 2016.02.10 2016-02-10 16:21:10 +01:00
Philipp Hagemeister 13437d26fa release 2016.02.09.1 2016-02-09 20:23:54 +01:00
Philipp Hagemeister 25fb0a671f release 2016.02.09 2016-02-09 13:01:38 +01:00
Philipp Hagemeister 7befbd830a release 2016.02.05.1 2016-02-05 15:19:14 +01:00
Philipp Hagemeister 56a6bfac2f release 2016.02.05 2016-02-05 11:07:33 +01:00
Philipp Hagemeister 4c13aed2ac release 2016.02.04 2016-02-04 13:40:48 +01:00
Philipp Hagemeister 854d1627da release 2016.02.01 2016-02-01 12:15:32 +01:00
Philipp Hagemeister f960ec7fad release 2016.01.31 2016-01-31 13:03:08 +01:00
Philipp Hagemeister d8a6bb81c0 release 2016.01.29 2016-01-29 12:24:08 +01:00
Philipp Hagemeister 168eb9c372 release 2016.01.27 2016-01-27 09:05:56 +01:00
Philipp Hagemeister 67ad5ea0d7 release 2016.01.23 2016-01-23 12:05:43 +01:00
Philipp Hagemeister 757043ab70 release 2016.01.15 2016-01-15 19:46:44 +01:00
Philipp Hagemeister 196c06f959 release 2016.01.14 2016-01-14 15:47:26 +01:00
Philipp Hagemeister fe8cbe9623 release 2016.01.09 2016-01-09 01:19:28 +01:00
Philipp Hagemeister 46f6a0dcdb release 2016.01.01 2016-01-01 12:21:17 +01:00
Philipp Hagemeister 1b3da21bcd release 2015.12.31 2015-12-31 16:57:33 +01:00
Philipp Hagemeister 7c765c45c9 release 2015.12.29 2015-12-29 11:05:50 +01:00
Philipp Hagemeister 217f29f44d release 2015.12.23 2015-12-23 14:08:19 +01:00
Philipp Hagemeister 411ef9fd0c release 2015.12.21 2015-12-21 11:46:41 +01:00
Philipp Hagemeister c096b6a83c release 2015.12.18 2015-12-18 14:25:26 +01:00
Philipp Hagemeister f5a5e29d2c release 2015.12.13 2015-12-13 10:56:18 +01:00
Philipp Hagemeister 6b71876402 release 2015.12.10 2015-12-10 17:07:19 +01:00
Philipp Hagemeister aaf061ce5e release 2015.12.09 2015-12-09 15:45:26 +01:00
Philipp Hagemeister 71e67c54d3 release 2015.12.06 2015-12-06 18:52:11 +01:00
Philipp Hagemeister 136d318592 release 2015.12.05 2015-12-05 16:09:27 +01:00
Philipp Hagemeister 111d1bdaeb release 2015.11.27.1 2015-11-27 16:43:48 +01:00
Philipp Hagemeister 07b956eeab release 2015.11.24 2015-11-24 07:49:46 +01:00
Philipp Hagemeister 7d94a5677f release 2015.11.23 2015-11-23 18:11:03 +01:00
Philipp Hagemeister 8633e60931 release 2015.11.21 2015-11-21 23:38:43 +01:00
Philipp Hagemeister da83d86935 release 2015.11.19 2015-11-19 15:40:08 +01:00
Philipp Hagemeister 157ddaf5c4 release 2015.11.18 2015-11-18 19:25:23 +01:00
Philipp Hagemeister 71dab9aee6 release 2015.11.15 2015-11-15 22:18:27 +01:00
Philipp Hagemeister ade87212d2 release 2015.11.13 2015-11-13 11:08:24 +01:00
Philipp Hagemeister abf90fe835 release 2015.11.10 2015-11-10 11:44:39 +01:00
Philipp Hagemeister a50ad865c5 release 2015.11.02 2015-11-02 16:24:38 +01:00
Philipp Hagemeister 5a00a85c62 release 2015.11.01 2015-11-01 14:22:57 +01:00
Philipp Hagemeister a158a7168e release 2015.10.24 2015-10-24 00:13:34 +02:00
Philipp Hagemeister ac132aee81 release 2015.10.23 2015-10-23 09:49:49 +02:00
Philipp Hagemeister 0791453d64 release 2015.10.18 2015-10-18 19:48:24 +02:00
Philipp Hagemeister 3dd2d8588b release 2015.10.16 2015-10-16 21:44:35 +02:00
Philipp Hagemeister 4b16f7be5b release 2015.10.13 2015-10-13 01:10:54 +02:00
Philipp Hagemeister e4c56e74a6 release 2015.10.12 2015-10-12 06:56:32 +02:00
Philipp Hagemeister 592d526e3e release 2015.10.09 2015-10-09 09:12:26 +02:00
Philipp Hagemeister f7d5b32507 release 2015.10.06.2 2015-10-06 23:50:03 +02:00
Philipp Hagemeister cde9823276 release 2015.10.06.1 2015-10-06 17:48:41 +02:00
Philipp Hagemeister c7ecb30755 release 2015.10.06 2015-10-06 09:16:37 +02:00
Philipp Hagemeister 7b7a810726 release 2015.09.28 2015-09-28 04:51:58 +02:00
Philipp Hagemeister bf98b012da release 2015.09.22 2015-09-22 22:53:10 +02:00
Philipp Hagemeister fa420d115d release 2015.09.09 2015-09-09 21:28:28 +02:00
Philipp Hagemeister 9caabdca83 release 2015.09.03 2015-09-03 12:35:51 +02:00
Philipp Hagemeister 4027267350 release 2015.08.28 2015-08-28 05:07:35 +02:00
Philipp Hagemeister 40cba3ae9a release 2015.08.23 2015-08-24 00:08:11 +02:00
Philipp Hagemeister 4dcb412869 release 2015.08.16.1 2015-08-16 23:52:09 +02:00
Philipp Hagemeister 9d55e73482 release 2015.08.16 2015-08-16 01:08:18 +02:00
Philipp Hagemeister a4f3acef97 release 2015.08.09 2015-08-09 20:18:19 +02:00
Philipp Hagemeister e74d2bdbc5 release 2015.08.06.1 2015-08-06 23:10:27 +02:00
Philipp Hagemeister d8196bece3 release 2015.07.28 2015-07-28 11:36:28 +02:00
Philipp Hagemeister 416f66a0b8 release 2015.07.21 2015-07-21 17:45:38 +02:00
Philipp Hagemeister a5d13c04d9 release 2015.07.18 2015-07-18 12:07:28 +02:00
Philipp Hagemeister b1a027002f release 2015.07.07 2015-07-07 10:45:03 +02:00
Philipp Hagemeister 87fa98037b release 2015.07.04 2015-07-04 09:38:09 +02:00
Philipp Hagemeister 3bb38bbbc5 release 2015.06.25 2015-06-25 07:46:57 +02:00
Philipp Hagemeister 9004185837 release 2015.06.15 2015-06-15 01:42:07 +02:00
Philipp Hagemeister adb380a355 Merge branch 'gh-pages' of github.com:rg3/youtube-dl into gh-pages 2015-06-04 21:59:19 +02:00
Philipp Hagemeister f1d3e95e46 release 2015.06.04.1 2015-06-04 21:56:50 +02:00
Philipp Hagemeister 9a75356870 release 2015.05.29 2015-05-29 08:14:46 +02:00
Sergey M. 1fc67b04d6 Clarify chmod command access permissions 2015-05-24 18:40:19 +06:00
Philipp Hagemeister 6685d1371f release 2015.05.20 2015-05-20 10:12:55 +02:00
Philipp Hagemeister 7ab18af783 release 2015.05.15 2015-05-15 10:30:06 +02:00
Philipp Hagemeister 36525e582b release 2015.05.10 2015-05-10 01:17:36 +02:00
Philipp Hagemeister 996790ebf8 release 2015.05.04 2015-05-04 15:16:16 +02:00
Philipp Hagemeister e75db9e70d release 2015.05.03 2015-05-03 22:44:47 +02:00
Philipp Hagemeister 70564bedb1 release 2015.04.28 2015-04-28 09:27:42 +02:00
Philipp Hagemeister 9717bdcc3d release 2015.04.26 2015-04-26 22:55:13 +02:00
Philipp Hagemeister 885c2259ec release 2015.04.17 2015-04-17 11:42:06 +02:00
Philipp Hagemeister 0882affb2e release 2015.04.09 2015-04-09 00:38:51 +02:00
Philipp Hagemeister d4317e2562 release 2015.04.03 2015-04-03 10:31:42 +02:00
Philipp Hagemeister 444be07f45 release 2015.03.28 2015-03-28 08:37:41 +01:00
Philipp Hagemeister a0ffcdb14c release 2015.03.24 2015-03-24 16:43:07 +01:00
Philipp Hagemeister a3c4b14e99 release 2015.03.18 2015-03-18 22:12:14 +01:00
Philipp Hagemeister 2548810ae1 release 2015.03.15 2015-03-15 19:42:28 +01:00
Philipp Hagemeister edf21705b7 release 2015.03.09 2015-03-09 03:08:48 +01:00
Philipp Hagemeister cac4d9ca02 release 2015.03.03.1 2015-03-03 14:04:17 +01:00
Philipp Hagemeister 801be27bf7 release 2015.03.03 2015-03-03 00:08:50 +01:00
Philipp Hagemeister c32a9ec29d release 2015.02.28 2015-02-28 21:28:20 +01:00
Philipp Hagemeister 6bcb3bb7a5 release 2015.02.26.2 2015-02-26 09:49:14 +01:00
Philipp Hagemeister 9dd5db7193 release 2015.02.26.1 2015-02-26 01:49:37 +01:00
Philipp Hagemeister 0b2374ea7e release 2015.02.26 2015-02-26 00:45:36 +01:00
Philipp Hagemeister 87ef91e888 release 2015.02.24.2 2015-02-24 16:37:49 +01:00
Philipp Hagemeister 81cb7c3f82 release 2015.02.24.1 2015-02-24 11:41:55 +01:00
Philipp Hagemeister b7fd00d68e release 2015.02.24 2015-02-24 11:32:25 +01:00
Philipp Hagemeister 01c432efcf release 2015.02.23.1 2015-02-23 18:56:21 +01:00
Philipp Hagemeister 540800e022 release 2015.02.23 2015-02-23 16:49:08 +01:00
Philipp Hagemeister b7c2f29c71 release 2015.02.21 2015-02-21 21:33:35 +01:00
Philipp Hagemeister 5f1e535937 release 2015.02.20 2015-02-20 23:29:08 +01:00
Philipp Hagemeister 31316b769c release 2015.02.19.3 2015-02-19 19:32:51 +01:00
Philipp Hagemeister 34779a9f20 release 2015.02.19.2 2015-02-19 01:47:14 +01:00
Philipp Hagemeister 4665572a61 release 2015.02.19.1 2015-02-19 01:08:30 +01:00
Philipp Hagemeister 25547d4e68 release 2015.02.19 2015-02-19 00:37:13 +01:00
Philipp Hagemeister 6d2840b147 release 2015.02.18.1 2015-02-18 11:03:11 +01:00
Philipp Hagemeister 6ea3d86cfe release 2015.02.18 2015-02-18 00:53:44 +01:00
Philipp Hagemeister 114d17529f release 2015.02.17.2 2015-02-17 17:40:26 +01:00
Philipp Hagemeister 597232528d release 2015.02.17 2015-02-17 17:31:00 +01:00
Philipp Hagemeister 4d1b121aa6 release 2015.02.16.1 2015-02-16 15:49:30 +01:00
Philipp Hagemeister 327906b652 release 2015.02.16 2015-02-16 04:55:10 +01:00
Philipp Hagemeister 776a127f38 release 2015.02.11 2015-02-11 19:07:08 +01:00
Philipp Hagemeister 9f0acce278 release 2015.02.10.5 2015-02-10 16:03:50 +01:00
Philipp Hagemeister 08f4c0f0e0 release 2015.02.10.4 2015-02-10 11:32:20 +01:00
Philipp Hagemeister 0103a02725 release 2015.02.10.3 2015-02-10 05:56:38 +01:00
Philipp Hagemeister 79379d02f3 release 2015.02.10.2 2015-02-10 03:36:45 +01:00
Philipp Hagemeister 2d2bd2a8cb release 2015.02.10.1 2015-02-10 01:56:52 +01:00
Philipp Hagemeister bb11b2fb3e release 2015.02.10 2015-02-10 01:24:01 +01:00
Philipp Hagemeister 071bec0e36 release 2015.02.09.3 2015-02-09 16:02:56 +01:00
Philipp Hagemeister 7fbd4db982 release 2015.02.09.2 2015-02-09 14:51:07 +01:00
Philipp Hagemeister 4e5165686d release 2015.02.09.1 2015-02-09 10:54:59 +01:00
Philipp Hagemeister 5525db3f74 release 2015.02.09 2015-02-09 10:33:02 +01:00
Philipp Hagemeister a5d09c457d release 2015.02.06 2015-02-06 14:49:03 +01:00
Philipp Hagemeister 3e88ea52e6 release 2015.02.04 2015-02-04 16:11:52 +01:00
Philipp Hagemeister 40fc4799f7 release 2015.02.03.1 2015-02-03 11:07:13 +01:00
Philipp Hagemeister cff0f7ded9 release 2015.02.03 2015-02-03 00:28:30 +01:00
Philipp Hagemeister 80dc055bf1 release 2015.02.02.5 2015-02-02 23:50:19 +01:00
Philipp Hagemeister e4e7579d0f release 2015.02.02.4 2015-02-02 23:41:52 +01:00
Philipp Hagemeister 683d41296c release 2015.02.02.2 2015-02-02 22:14:05 +01:00
Philipp Hagemeister 6169a90659 release 2015.02.02 2015-02-02 01:56:28 +01:00
Philipp Hagemeister 00ea5a500d release 2015.01.30.2 2015-01-30 04:49:09 +01:00
Philipp Hagemeister eb0846f1f0 release 2015.01.30.1 2015-01-30 04:02:43 +01:00
Philipp Hagemeister 2b11530bdb Replace documentation.html with README.md
This allows us to have all the current documentation in one place.
Closes #4817.
2015-01-30 03:01:19 +01:00
Philipp Hagemeister 2f6ff3c625 release 2015.01.25 2015-01-25 21:56:31 +01:00
Philipp Hagemeister b1b1bb29eb release 2015.01.23.4 2015-01-23 18:59:52 +01:00
Philipp Hagemeister b13efe451f release 2015.01.23.3 2015-01-23 12:21:28 +01:00
Philipp Hagemeister 0eb22525e7 release 2015.01.23.2 2015-01-23 11:24:42 +01:00
Philipp Hagemeister 1a2d700c8a release 2015.01.23.1 2015-01-23 00:35:58 +01:00
Philipp Hagemeister c7eab954b7 release 2015.01.23 2015-01-23 00:10:58 +01:00
Philipp Hagemeister 0fc90fb0ff release 2015.01.22 2015-01-22 13:11:14 +01:00
Philipp Hagemeister fdf7547735 release 2015.01.16 2015-01-16 14:26:02 +01:00
Philipp Hagemeister 03105f6854 release 2015.01.15.1 2015-01-15 22:46:27 +01:00
Philipp Hagemeister 41a49da1af release 2015.01.15 2015-01-15 12:50:30 +01:00
Philipp Hagemeister 3a863e1fdb release 2015.01.11 2015-01-11 17:52:27 +01:00
Philipp Hagemeister ef90868590 release 2015.01.10.2 2015-01-10 21:05:47 +01:00
Philipp Hagemeister 998259644e release 2015.01.10.1 2015-01-10 20:08:51 +01:00
Philipp Hagemeister febd9d6e84 release 2015.01.10 2015-01-10 05:54:30 +01:00
Philipp Hagemeister bb50007287 release 2015.01.09.2 2015-01-10 00:03:35 +01:00
Philipp Hagemeister fbcbaa08d2 release 2015.01.09.1 2015-01-09 21:35:36 +01:00
Philipp Hagemeister 79ae0d9359 release 2015.01.09 2015-01-09 20:24:35 +01:00
Philipp Hagemeister 73d6793ff4 release 2015.01.08 2015-01-08 16:21:40 +01:00
Philipp Hagemeister 2933146728 release 2015.01.07.2 2015-01-07 07:47:14 +01:00
Philipp Hagemeister 459c608b68 release 2015.01.07 2015-01-07 07:27:44 +01:00
Philipp Hagemeister 1b4bdeefe9 release 2015.01.05.1 2015-01-05 22:45:29 +01:00
Philipp Hagemeister 07f924bc45 release 2015.01.05 2015-01-05 18:56:02 +01:00
Philipp Hagemeister 0d17780e87 release 2015.01.04 2015-01-04 03:19:09 +01:00
Philipp Hagemeister d128f35310 Denote Python 3.2 support 2015-01-04 02:52:19 +01:00
Philipp Hagemeister 051e107eff Link to modern FAQ 2015-01-04 02:49:50 +01:00
Philipp Hagemeister 9c4ebf4970 Avoid mixed-content warnings 2015-01-04 02:46:28 +01:00
Philipp Hagemeister b59fb8d81b release 2015.01.03 2015-01-03 18:38:38 +01:00
Philipp Hagemeister d1f2600900 release 2015.01.02 2015-01-02 16:00:32 +01:00
Philipp Hagemeister e9260dbb31 release 2015.01.01 2015-01-01 21:51:17 +01:00
Philipp Hagemeister 868522e4c2 release 2014.12.17.2 2014-12-17 11:42:44 +01:00
Philipp Hagemeister 8149c54acf release 2014.12.17.1 2014-12-17 11:32:32 +01:00
Philipp Hagemeister 01ec54d121 release 2014.12.17 2014-12-17 10:57:14 +01:00
Philipp Hagemeister fa3d853f77 release 2014.12.16.2 2014-12-16 16:53:12 +01:00
Philipp Hagemeister 37d868f0f4 release 2014.12.16.1 2014-12-16 16:08:07 +01:00
Philipp Hagemeister 678d7bff51 release 2014.12.16 2014-12-16 00:33:55 +01:00
Philipp Hagemeister 7b86336325 release 2014.12.15 2014-12-15 01:42:54 +01:00
Philipp Hagemeister b8e8032f4d release 2014.12.14 2014-12-14 00:22:00 +01:00
Philipp Hagemeister 31d0cf05e0 release 2014.12.13.1 2014-12-13 23:55:05 +01:00
Philipp Hagemeister e3a3ac788f release 2014.12.13 2014-12-13 23:17:33 +01:00
Philipp Hagemeister 9bb483f879 release 2014.12.12.7 2014-12-12 18:29:19 +01:00
Philipp Hagemeister a1fa9761da release 2014.12.12.5 2014-12-12 17:48:39 +01:00
Philipp Hagemeister b831f149de release 2014.12.12.4 2014-12-12 17:21:10 +01:00
Philipp Hagemeister 4deb5d6900 release 2014.12.12.3 2014-12-12 16:51:21 +01:00
Philipp Hagemeister c191693e04 release 2014.12.12.2 2014-12-12 15:59:45 +01:00
Philipp Hagemeister 6e834f71b5 release 2014.12.12.1 2014-12-12 03:39:03 +01:00
Philipp Hagemeister 709a4eaca6 release 2014.12.10.3 2014-12-10 15:22:15 +01:00
Philipp Hagemeister 54526d3ff3 release 2014.12.10.2 2014-12-10 14:42:05 +01:00
Philipp Hagemeister 4f929b4877 release 2014.12.10.1 2014-12-10 13:27:56 +01:00
Philipp Hagemeister 2309c33451 release 2014.12.10 2014-12-10 12:26:40 +01:00
Philipp Hagemeister 7be418c432 release 2014.12.06.1 2014-12-06 00:52:04 +01:00
Philipp Hagemeister ac7b35363e release 2014.12.06 2014-12-06 00:47:31 +01:00
Philipp Hagemeister ff3bc15613 release 2014.12.04.2 2014-12-04 17:54:49 +01:00
Philipp Hagemeister eb5bc48718 release 2014.12.04.1 2014-12-04 17:04:49 +01:00
Philipp Hagemeister f57eec1bcb release 2014.12.04 2014-12-04 08:41:10 +01:00
Philipp Hagemeister 45162344b7 release 2014.12.03 2014-12-03 12:19:51 +01:00
Philipp Hagemeister e7250b59e1 release 2014.12.01 2014-12-01 17:33:02 +01:00
Philipp Hagemeister e7453bd649 release 2014.11.27 2014-11-27 16:07:17 +01:00
Philipp Hagemeister 2515aa0ab0 release 2014.11.26.4 2014-11-26 23:02:39 +01:00
Philipp Hagemeister 3e3977a554 release 2014.11.26.3 2014-11-26 22:18:22 +01:00
Philipp Hagemeister 82e4cbf19a release 2014.11.26.1 2014-11-26 22:05:41 +01:00
Philipp Hagemeister c64a67ccfd release 2014.11.26 2014-11-26 10:50:48 +01:00
Philipp Hagemeister 8c8b0e4ffd release 2014.11.25.1 2014-11-25 14:38:09 +01:00
Philipp Hagemeister 8238e31efb release 2014.11.25 2014-11-25 10:03:04 +01:00
Philipp Hagemeister 85ea5698b8 Merge branch 'gh-pages' of github.com:rg3/youtube-dl into gh-pages 2014-11-24 23:54:08 +01:00
Philipp Hagemeister 3742c04ee0 release 2014.11.24 2014-11-24 23:52:36 +01:00
Jaime Marquínez Ferrándiz e1a90e58d1 Merge pull request #4299 from crdx/patch-1
fix typo
2014-11-24 21:41:31 +01:00
Sean S 5e2e5b8a22 fix small typo 2014-11-24 20:06:26 +00:00
Philipp Hagemeister 9ce1f77204 release 2014.11.23.1 2014-11-23 10:57:16 +01:00
Philipp Hagemeister 34edc2b0b8 release 2014.11.23 2014-11-23 10:03:00 +01:00
Philipp Hagemeister d1bbe1e28b release 2014.11.21.1 2014-11-21 23:23:55 +01:00
Philipp Hagemeister 4bdc0e241a release 2014.11.21 2014-11-21 10:49:58 +01:00
Philipp Hagemeister ff764dd711 release 2014.11.20 2014-11-20 08:59:58 +01:00
Philipp Hagemeister 9077c895d9 release 2014.11.16 2014-11-16 00:54:37 +01:00
Philipp Hagemeister d3fd131e01 release 2014.11.15.1 2014-11-15 15:24:58 +01:00
Philipp Hagemeister 593a53026d release 2014.11.15 2014-11-15 11:15:23 +01:00
Philipp Hagemeister 890f6ffcdb release 2014.11.14 2014-11-14 22:39:23 +01:00
Philipp Hagemeister 0e5a26df53 release 2014.11.13.3 2014-11-13 16:31:57 +01:00
Philipp Hagemeister 7c10780356 release 2014.11.13.2 2014-11-13 16:25:36 +01:00
Philipp Hagemeister ed3c5f675b release 2014.11.13.1 2014-11-13 15:50:04 +01:00
Philipp Hagemeister d9089f9e69 release 2014.11.13 2014-11-13 10:03:13 +01:00
Philipp Hagemeister 50215c5106 release 2014.11.12.1 2014-11-12 11:47:33 +01:00
Philipp Hagemeister af596e7e2c release 2014.11.12 2014-11-12 09:17:46 +01:00
Philipp Hagemeister 901ac74b81 release 2014.11.09 2014-11-09 22:37:21 +01:00
Philipp Hagemeister 424f72d298 release 2014.11.04 2014-11-04 23:47:01 +01:00
Philipp Hagemeister a67cbf349c release 2014.11.02.1 2014-11-02 10:34:28 +01:00
Philipp Hagemeister c75740b5da release 2014.10.30 2014-10-30 10:18:45 +01:00
Philipp Hagemeister 1b7b087361 release 2014.10.29 2014-10-29 23:36:12 +01:00
Philipp Hagemeister b2ac1e6bd7 release 2014.10.27 2014-10-27 02:46:47 +01:00
Philipp Hagemeister 08011414c9 release 2014.10.26.2 2014-10-26 21:52:22 +01:00
Philipp Hagemeister e86358349a release 2014.10.26.1 2014-10-26 21:07:45 +01:00
Philipp Hagemeister da3fad69c5 release 2014.10.26 2014-10-26 17:37:09 +01:00
Philipp Hagemeister 9f783b25d1 release 2014.10.25 2014-10-25 00:47:51 +02:00
Philipp Hagemeister 31d51aa70f release 2014.10.24 2014-10-24 14:57:44 +02:00
Philipp Hagemeister 23c6d465ad release 2014.10.23 2014-10-23 20:24:40 +02:00
Philipp Hagemeister 20af98619c release 2014.10.18 2014-10-18 20:25:57 +02:00
Philipp Hagemeister e906eeae09 release 2014.10.15 2014-10-15 12:42:46 +02:00
Philipp Hagemeister 5ceedab0e8 release 2014.10.13 2014-10-13 10:20:12 +02:00
Philipp Hagemeister 7f19047270 release 2014.10.12 2014-10-12 22:31:09 +02:00
Philipp Hagemeister b9c4c1b640 release 2014.10.05.2 2014-10-05 22:19:14 +02:00
Philipp Hagemeister d55df49929 release 2014.10.05 2014-10-05 08:00:01 +02:00
Philipp Hagemeister 507e12d727 release 2014.10.02 2014-10-02 15:38:46 +02:00
Philipp Hagemeister 7b445bb0a0 Document config file location on Windows (#1881) 2014-09-29 21:58:41 +02:00
Philipp Hagemeister 57d91e2c31 release 2014.09.29.2 2014-09-29 04:53:29 +02:00
Philipp Hagemeister f16919ebad release 2014.09.29.1 2014-09-29 02:11:19 +02:00
Philipp Hagemeister 29a75387d7 release 2014.09.28.1 2014-09-28 12:19:10 +02:00
Philipp Hagemeister 757d2fff32 release 2014.09.28 2014-09-28 09:52:47 +02:00
Philipp Hagemeister c07ddd87a1 release 2014.09.25 2014-09-25 02:02:48 +02:00
Philipp Hagemeister 3579a6dbe0 release 2014.09.24.1 2014-09-24 14:19:05 +02:00
Philipp Hagemeister babe46018b release 2014.09.24 2014-09-24 11:16:45 +02:00
Philipp Hagemeister 72166059b3 release 2014.09.22 2014-09-22 13:11:04 +02:00
Philipp Hagemeister 9ce623c24d release 2014.09.19 2014-09-19 10:02:47 +02:00
Philipp Hagemeister 890a1cf917 release 2014.09.18 2014-09-18 18:49:04 +02:00
Philipp Hagemeister 08f60a6db5 release 2014.09.16.1 2014-09-16 23:38:36 +02:00
Philipp Hagemeister 78a7220c20 release 2014.09.16 2014-09-16 10:14:37 +02:00
Philipp Hagemeister b22048e3fc release 2014.09.15.1 2014-09-15 15:31:35 +02:00
Philipp Hagemeister c837fc59b8 release 2014.09.15 2014-09-15 15:16:34 +02:00
Philipp Hagemeister 97c9abd4b0 release 2014.09.14.3 2014-09-14 16:56:12 +02:00
Philipp Hagemeister 1b4bf0f3d1 release 2014.09.12 2014-09-12 08:01:28 +02:00
Philipp Hagemeister 914ff382d0 release 2014.09.10.1 2014-09-10 16:43:10 +02:00
Philipp Hagemeister 84794766aa release 2014.09.10 2014-09-10 12:42:39 +02:00
Philipp Hagemeister df24469c79 release 2014.09.06 2014-09-06 15:40:10 +02:00
Philipp Hagemeister f3a2b50b09 release 2014.09.04.3 2014-09-04 16:38:14 +02:00
Philipp Hagemeister e1f869e5e4 release 2014.09.04.1 2014-09-04 04:55:09 +02:00
Philipp Hagemeister 48edbf72f4 release 2014.09.04 2014-09-04 01:35:38 +02:00
Philipp Hagemeister 96217a7344 release 2014.09.01.2 2014-09-02 00:12:43 +02:00
Philipp Hagemeister d1603ead12 release 2014.09.01.1 2014-09-01 00:30:50 +02:00
Philipp Hagemeister 3236a3c603 release 2014.09.01 2014-09-01 00:10:27 +02:00
Philipp Hagemeister 34be9175a2 release 2014.08.29 2014-08-29 01:10:21 +02:00
Philipp Hagemeister cfe7713497 release 2014.08.28.2 2014-08-28 18:09:04 +02:00
Philipp Hagemeister b1f4e7a747 release 2014.08.28.1 2014-08-28 14:07:25 +02:00
Philipp Hagemeister 132b1532c6 release 2014.08.28 2014-08-28 01:44:00 +02:00
Philipp Hagemeister 12ca402bd7 release 2014.08.27.1 2014-08-27 02:40:40 +02:00
Philipp Hagemeister 65d1813840 release 2014.08.27 2014-08-27 01:47:30 +02:00
Philipp Hagemeister 667ef5ede4 release 2014.08.26 2014-08-26 21:34:32 +02:00
Philipp Hagemeister 62f1cbb88d release 2014.08.25.3 2014-08-25 18:42:51 +02:00
Philipp Hagemeister cf84511178 release 2014.08.25.2 2014-08-25 16:54:37 +02:00
Philipp Hagemeister 67de8f0589 release 2014.08.25 2014-08-25 09:42:26 +02:00
Philipp Hagemeister 778bfd76a6 release 2014.08.24.6 2014-08-24 15:24:02 +02:00
Philipp Hagemeister 365843a199 release 2014.08.24.5 2014-08-24 07:01:38 +02:00
Philipp Hagemeister 0326f6eda5 release 2014.08.24.4 2014-08-24 06:48:32 +02:00
Philipp Hagemeister b095a9bc3a release 2014.08.24.3 2014-08-24 05:37:59 +02:00
Philipp Hagemeister 64bdc95e11 release 2014.08.24.2 2014-08-24 04:55:13 +02:00
Philipp Hagemeister 2ed065729f release 2014.08.24.1 2014-08-24 03:30:56 +02:00
Philipp Hagemeister 60a010f665 release 2014.08.24 2014-08-24 02:49:20 +02:00
Philipp Hagemeister 803d3bd32d release 2014.08.23 2014-08-23 15:28:52 +02:00
Philipp Hagemeister deff9e0884 release 2014.08.22.3 2014-08-22 18:45:37 +02:00
Philipp Hagemeister 0345c8d777 release 2014.08.22.2 2014-08-22 03:19:39 +02:00
Philipp Hagemeister b3ebf44676 release 2014.08.22.1 2014-08-22 03:05:41 +02:00
Philipp Hagemeister df9f4d2baa release 2014.08.21.3 2014-08-21 18:07:19 +02:00
Philipp Hagemeister e073b8fa28 release 2014.08.21.2 2014-08-21 13:14:11 +02:00
Philipp Hagemeister d95287d2ae release 2014.08.21.1 2014-08-21 12:24:57 +02:00
Philipp Hagemeister b24b55c27b release 2014.08.10 2014-08-10 19:51:13 +02:00
Philipp Hagemeister 25beeb8dc4 release 2014.08.05 2014-08-05 17:24:39 +02:00
Philipp Hagemeister af50cac100 release 2014.08.02.1 2014-08-02 18:20:51 +02:00
Philipp Hagemeister 2709569cb2 release 2014.08.02 2014-08-02 12:33:22 +02:00
Philipp Hagemeister d5289eb78f release 2014.07.30 2014-07-30 09:57:28 +02:00
Philipp Hagemeister 289ff5644d release 2014.07.25.1 2014-07-25 10:52:25 +02:00
Philipp Hagemeister b466c5d28f release 2014.07.25 2014-07-25 07:08:57 +02:00
Philipp Hagemeister 423fba294a release 2014.07.24 2014-07-24 11:27:51 +02:00
Philipp Hagemeister d504be6d68 release 2014.07.23.2 2014-07-23 02:29:07 +02:00
Philipp Hagemeister 71bd78a193 release 2014.07.23.1 2014-07-23 01:33:40 +02:00
Philipp Hagemeister d0e59bf20d release 2014.07.23 2014-07-23 01:21:49 +02:00
Philipp Hagemeister ddc86a33dc release 2014.07.22 2014-07-22 17:04:25 +02:00
Philipp Hagemeister a513c01a24 release 2014.07.21 2014-07-21 18:14:01 +02:00
Philipp Hagemeister f6e1f2630d release 2014.07.20.2 2014-07-20 23:26:41 +02:00
Philipp Hagemeister c8855ec895 release 2014.07.20.1 2014-07-20 22:00:12 +02:00
Philipp Hagemeister 25bb51bca3 release 2014.07.20 2014-07-20 18:55:39 +02:00
Philipp Hagemeister 5eda0b4c08 release 2014.07.15 2014-07-15 23:06:12 +02:00
Philipp Hagemeister bd4a929996 fix an HTML error 2014-07-13 17:26:52 +02:00
Philipp Hagemeister db4ee49745 link list of supported sites on main page 2014-07-13 17:26:46 +02:00
Philipp Hagemeister 809804b1b0 Clarify that the youtube-dl exe works without Python (Fixes #3249) 2014-07-13 17:26:24 +02:00
Philipp Hagemeister 453ae702ee release 2014.07.11.3 2014-07-11 17:30:26 +02:00
Philipp Hagemeister 2fbb7eb190 release 2014.07.11.2 2014-07-11 13:38:40 +02:00
Philipp Hagemeister 0a0fc250dc release 2014.07.11.1 2014-07-11 11:58:15 +02:00
Philipp Hagemeister e299dbb8c9 release 2014.07.11 2014-07-11 10:53:48 +02:00
Philipp Hagemeister 1c6cd59a03 release 2014.07.10 2014-07-10 14:57:55 +02:00
Philipp Hagemeister b94fac1143 release 2014.06.26 2014-06-26 17:34:09 +02:00
Philipp Hagemeister 44caf6a435 release 2014.06.25 2014-06-25 21:36:29 +02:00
Philipp Hagemeister d62ea494d9 release 2014.06.24.1 2014-06-24 09:15:30 +02:00
Philipp Hagemeister 6581294d11 release 2014.06.19 2014-06-19 17:18:09 +02:00
Philipp Hagemeister 2cdbb0ea2e Merge branch 'gh-pages' of github.com:rg3/youtube-dl into gh-pages 2014-06-16 10:56:08 +02:00
Philipp Hagemeister 1d083917ef release 2014.06.16 2014-06-16 10:55:14 +02:00
Philipp Hagemeister 7b51ff76aa Merge pull request #3070 from EvanHahn/homebrew-instructions
Add Homebrew install instructions
2014-06-12 20:26:15 +02:00
Evan Hahn 2a1c4c2d37 Add Homebrew install instructions 2014-06-12 10:41:08 -07:00
Philipp Hagemeister c3cdb616f3 release 2014.06.09 2014-06-09 23:21:53 +02:00
Philipp Hagemeister 11aeec39d7 release 2014.06.07 2014-06-07 16:46:01 +02:00
Philipp Hagemeister 864a09fff0 release 2014.06.04 2014-06-04 06:52:39 +02:00
Philipp Hagemeister 8629db7f23 release 2014.06.02 2014-06-02 10:50:31 +02:00
Philipp Hagemeister 389588e362 release 2014.05.31.4 2014-05-31 20:48:00 +02:00
Philipp Hagemeister 20bb589883 release 2014.05.19 2014-05-19 11:57:22 +02:00
Philipp Hagemeister 081cc94583 release 2014.05.16.1 2014-05-16 15:57:29 +02:00
Philipp Hagemeister 3081d368bc release 2014.05.16 2014-05-16 12:20:34 +02:00
Philipp Hagemeister a67fe93cd6 release 2014.05.13 2014-05-13 10:25:12 +02:00
Philipp Hagemeister 9190a734e6 release 2014.05.12 2014-05-12 16:42:31 +02:00
Philipp Hagemeister ee5d951160 release 2014.05.05 2014-05-05 03:17:54 +02:00
Philipp Hagemeister 57f7d997aa release 2014.04.30.1 2014-04-30 10:14:35 +02:00
Philipp Hagemeister 524b6a2fd0 release 2014.04.30 2014-04-30 02:11:34 +02:00
Philipp Hagemeister 3228c709d4 release 2014.04.21.6 2014-04-21 16:20:52 +02:00
Philipp Hagemeister c78dff0119 release 2014.04.21.5 2014-04-21 16:00:19 +02:00
Philipp Hagemeister de13322e71 release 2014.04.21.4 2014-04-21 15:28:05 +02:00
Philipp Hagemeister 14af0080ea release 2014.04.21.3 2014-04-21 12:42:49 +02:00
Philipp Hagemeister 455f4dbd65 release 2014.04.21.2 2014-04-21 07:17:26 +02:00
Philipp Hagemeister db831975a2 release 2014.04.21.1 2014-04-21 06:40:19 +02:00
Philipp Hagemeister 0fb9d3e3f8 release 2014.04.21 2014-04-21 02:49:04 +02:00
Philipp Hagemeister 3612000f81 release 2014.04.19 2014-04-19 12:46:42 +02:00
Philipp Hagemeister 6773911609 release 2014.04.13 2014-04-13 03:27:36 +02:00
Philipp Hagemeister 783436dda9 release 2014.04.11.2 2014-04-11 09:49:02 +02:00
Philipp Hagemeister 685b357631 release 2014.04.11.1 2014-04-11 01:34:16 +02:00
Philipp Hagemeister 030c5d014a release 2014.04.07.4 2014-04-07 23:25:49 +02:00
Philipp Hagemeister 22a41b7d30 release 2014.04.07.3 2014-04-07 22:50:20 +02:00
Philipp Hagemeister 9667720e74 release 2014.04.07.2 2014-04-07 21:44:20 +02:00
Philipp Hagemeister 36152c3da7 release 2014.04.07.1 2014-04-07 15:46:26 +02:00
Philipp Hagemeister 52351c0372 release 2014.04.07 2014-04-07 13:49:05 +02:00
Philipp Hagemeister 9c0e70fd32 release 2014.04.04.7 2014-04-04 23:04:55 +02:00
Philipp Hagemeister e32838d54c release 2014.04.04.6 2014-04-04 22:51:49 +02:00
Philipp Hagemeister 2d2d9d1108 release 2014.04.04.5 2014-04-04 22:32:24 +02:00
Philipp Hagemeister 9c323f4926 release 2014.04.04.4 2014-04-04 22:21:38 +02:00
Philipp Hagemeister 2de3613cd7 release 2014.04.04.2 2014-04-04 02:38:36 +02:00
Philipp Hagemeister bbd7253743 release 2014.04.04.3 2014-04-04 02:15:41 +02:00
Philipp Hagemeister 5e6e2c1ac6 release 2014.04.04.1 2014-04-04 00:31:19 +02:00
Philipp Hagemeister f17a9ace3a release 2014.04.04 2014-04-04 00:10:34 +02:00
Philipp Hagemeister f7156ab56e release 2014.04.03.3 2014-04-03 16:25:04 +02:00
Philipp Hagemeister 693e0ae049 release 2014.04.03.2 2014-04-03 15:31:15 +02:00
Philipp Hagemeister 7b6e6131ad release 2014.04.03.1 2014-04-03 09:06:51 +02:00
Philipp Hagemeister 8e5e8656e0 release 2014.04.03 2014-04-03 06:15:28 +02:00
Philipp Hagemeister 38c43f6644 release 2014.04.02 2014-04-02 14:33:15 +02:00
Philipp Hagemeister e6c9e944ac release 2014.04.01.3 2014-04-01 13:20:58 +02:00
Philipp Hagemeister 7a22d25719 release 2014.04.01.2 2014-04-01 06:04:49 +02:00
Philipp Hagemeister 934e86ff0c release 2014.04.01.1 2014-04-01 00:27:55 +02:00
Philipp Hagemeister 68cba3e46a release 2014.04.01 2014-04-01 00:06:09 +02:00
Philipp Hagemeister 08d4cab829 release 2014.03.30.1 2014-03-30 16:04:42 +02:00
Philipp Hagemeister 2073acb594 release 2014.03.30 2014-03-30 07:28:27 +02:00
Philipp Hagemeister dcd600069e release 2014.03.29 2014-03-29 14:05:10 +01:00
Philipp Hagemeister 15d57b9673 release 2014.03.28 2014-03-28 23:18:09 +01:00
Philipp Hagemeister f79e8b7ab8 release 2014.03.27.1 2014-03-27 02:54:40 +01:00
Philipp Hagemeister c8b64cf1d6 release 2014.03.27 2014-03-27 02:26:02 +01:00
Philipp Hagemeister ea2dadfb5a release 2014.03.25.1 2014-03-25 14:30:40 +01:00
Philipp Hagemeister f3c9b0c286 release 2014.03.25 2014-03-25 04:06:07 +01:00
Philipp Hagemeister 65729455c7 release 2014.03.24.5 2014-03-24 23:26:34 +01:00
Philipp Hagemeister f7cc23f1e9 release 2014.03.24.4 2014-03-24 22:14:37 +01:00
Philipp Hagemeister 41e7dd23d1 release 2014.03.24.3 2014-03-24 17:15:13 +01:00
Philipp Hagemeister 9c7a1bbee9 release 2014.03.24.2 2014-03-24 15:03:47 +01:00
Philipp Hagemeister e76a774494 release 2014.03.24.1 2014-03-24 10:29:08 +01:00
Philipp Hagemeister e72711c9b0 release 2013.03.24.2 2014-03-24 02:28:53 +01:00
Philipp Hagemeister 9566442ef3 release 2013.03.24 2014-03-24 01:46:43 +01:00
Philipp Hagemeister 0e362aee7e release 2014.03.23 2014-03-23 16:09:31 +01:00
Philipp Hagemeister c65f61621c release 2014.03.21.5 2014-03-21 14:55:16 +01:00
Philipp Hagemeister 191c0f4e90 release 2014.03.21.3 2014-03-21 02:12:45 +01:00
Philipp Hagemeister 8b64d68089 release 2014.03.21.2 2014-03-21 01:44:34 +01:00
Philipp Hagemeister bb5a8bca6a release 2014.03.21.1 2014-03-21 01:22:30 +01:00
Philipp Hagemeister 2a403b5d4f release 2014.03.21 2014-03-21 00:44:35 +01:00
Philipp Hagemeister f6cdbe792e release 2014.03.20 2014-03-20 16:38:42 +01:00
Philipp Hagemeister 98328f6693 release 2014.03.18.1 2014-03-18 14:49:12 +01:00
Philipp Hagemeister bab31e5c21 release 2014.03.17 2014-03-17 14:52:02 +01:00
Philipp Hagemeister 02bc59198f release 2014.03.12 2014-03-12 14:54:35 +01:00
Philipp Hagemeister 0a27c5551b release 2014.03.11 2014-03-11 16:53:42 +01:00
Philipp Hagemeister 86da332b5b release 2014.03.10 2014-03-10 13:07:07 +01:00
Philipp Hagemeister 62d8317a73 release 2014.03.07.1 2014-03-07 16:08:43 +01:00
Philipp Hagemeister 41366765df release 2014.03.07 2014-03-07 06:47:07 +01:00
Philipp Hagemeister 6d9061d3e0 release 2014.03.06 2014-03-07 00:05:18 +01:00
Philipp Hagemeister 515403b421 release 2014.03.04.2 2014-03-04 21:02:20 +01:00
Philipp Hagemeister e5eb46a38a release 2014.03.04.1 2014-03-04 03:43:47 +01:00
Philipp Hagemeister 4948cca3fd release 2014.03.04 2014-03-04 03:36:27 +01:00
Philipp Hagemeister 47b5a0247f release 2014.03.03 2014-03-03 13:55:59 +01:00
Philipp Hagemeister bcb0eb70e0 release 2014.02.28 2014-02-28 14:57:09 +01:00
Philipp Hagemeister 3fb6d6a6ee release 2014.02.27.1 2014-02-27 16:14:05 +01:00
Philipp Hagemeister b7403001f3 release 2014.02.27 2014-02-27 07:28:28 +01:00
Philipp Hagemeister d5b8d93710 release 2014.02.26 2014-02-26 00:39:22 +01:00
Philipp Hagemeister 4b5e477e60 release 2014.02.25.1 2014-02-25 11:23:12 +01:00
Philipp Hagemeister c9875e8879 release 2014.02.25 2014-02-25 01:51:41 +01:00
Philipp Hagemeister d602cd1cb5 release 2014.02.24 2014-02-24 09:49:03 +01:00
Philipp Hagemeister de2966d1ea release 2014.02.22.1 2014-02-22 23:21:46 +01:00
Philipp Hagemeister 7eed3e3843 release 2014.02.22 2014-02-22 15:15:26 +01:00
Philipp Hagemeister dd00e42333 release 2014.02.21.1 2014-02-21 18:24:18 +01:00
Philipp Hagemeister 58b677fc73 release 2014.02.21 2014-02-21 12:25:46 +01:00
Philipp Hagemeister b32ae92a4e release 2014.02.20 2014-02-20 13:21:29 +01:00
Philipp Hagemeister 108d3b83e9 release 2014.02.19.1 2014-02-19 01:31:59 +01:00
Philipp Hagemeister 97dd4404b0 release 2014.02.19 2014-02-19 01:23:02 +01:00
Philipp Hagemeister 573bedb2fc release 2014.02.17 2014-02-17 11:43:32 +01:00
Philipp Hagemeister 263ce0864f release 2014.02.13 2014-02-13 19:22:40 +01:00
Philipp Hagemeister 6e4d131e23 release 2014.02.10 2014-02-10 02:06:00 +01:00
Philipp Hagemeister d79a7ae4b9 release 2014.02.08.2 2014-02-08 19:41:53 +01:00
Philipp Hagemeister 3b4af451a4 release 2014.02.08.1 2014-02-08 18:42:35 +01:00
Philipp Hagemeister 37d767ec49 release 2014.02.08 2014-02-08 16:34:54 +01:00
Philipp Hagemeister 59afad8ba4 release 2014.02.06.3 2014-02-07 01:48:36 +01:00
Philipp Hagemeister bdc68eb84a release 2014.02.06.2 2014-02-06 15:50:33 +01:00
Philipp Hagemeister e242fb16d4 release 2014.02.06.1 2014-02-06 11:34:29 +01:00
Philipp Hagemeister d24ff9356e release 2014.02.06 2014-02-06 03:32:58 +01:00
Philipp Hagemeister 9bd7125b93 release 2014.02.05 2014-02-05 21:44:31 +01:00
Philipp Hagemeister 420cefb071 release 2014.02.04.1 2014-02-04 23:38:10 +01:00
Philipp Hagemeister a486999504 release 2014.02.04 2014-02-04 16:36:11 +01:00
Philipp Hagemeister 77d02342d1 release 2014.02.03.1 2014-02-03 15:26:38 +01:00
Philipp Hagemeister f29214b135 release 2014.02.03 2014-02-03 06:59:02 +01:00
Philipp Hagemeister 809028e9fa release 2014.01.30.2 2014-01-30 19:36:46 +01:00
Philipp Hagemeister 1dd2c27354 release 2014.01.30.1 2014-01-30 05:57:09 +01:00
Philipp Hagemeister 7651ced5d1 release 2014.01.30 2014-01-30 05:02:45 +01:00
Philipp Hagemeister cd8b212f5b release 2014.01.29 2014-01-29 11:28:22 +01:00
Philipp Hagemeister c53f5c1332 release 2014.01.28.1 2014-01-28 03:48:33 +01:00
Philipp Hagemeister 30d89fba3a release 2014.01.27.2 2014-01-27 19:25:32 +01:00
Philipp Hagemeister 58efc4e3a8 release 2014.01.27.1 2014-01-27 07:12:51 +01:00
Philipp Hagemeister 2c714450ca release 2014.01.27 2014-01-27 07:09:22 +01:00
Philipp Hagemeister 9c07dd21e8 [donations] Linkify bitcoin 2014-01-27 03:06:21 +01:00
Philipp Hagemeister 27cf21a718 [donations] Mention BTC (#2238) 2014-01-27 03:04:46 +01:00
Philipp Hagemeister 590fe11a01 release 2014.01.23.4 2014-01-24 00:10:58 +01:00
Philipp Hagemeister 66e2e6495b release 2014.01.23.3 2014-01-24 00:00:40 +01:00
Philipp Hagemeister 2999c9fc2b release 2014.01.23.2 2014-01-23 19:15:46 +01:00
Philipp Hagemeister 7362738116 release 2014.01.23.1 2014-01-23 10:41:47 +01:00
Philipp Hagemeister 35c171b552 release 2014.01.23 2014-01-23 00:32:05 +01:00
Philipp Hagemeister 77941a2ec3 release 2014.01.22.5 2014-01-22 22:15:37 +01:00
Philipp Hagemeister 1e1e007962 release 2014.01.22.4 2014-01-22 21:16:54 +01:00
Philipp Hagemeister 3749d754de release 2014.01.22.3 2014-01-22 14:55:38 +01:00
Philipp Hagemeister c079f0aeea release 2014.01.22.2 2014-01-22 14:36:47 +01:00
Philipp Hagemeister d513c0e282 release 2014.01.22.1 2014-01-22 02:26:30 +01:00
Philipp Hagemeister 647c4ff6e2 release 2014.01.22 2014-01-22 00:24:23 +01:00
Philipp Hagemeister a41b958624 release 2014.01.21.1 2014-01-21 18:27:20 +01:00
Philipp Hagemeister 55baadab26 release 2014.01.21 2014-01-21 14:12:26 +01:00
Philipp Hagemeister 4d1261086a release 2014.01.20 2014-01-20 11:52:39 +01:00
Philipp Hagemeister 632cc94b53 release 2014.01.17.2 2014-01-17 04:27:58 +01:00
Philipp Hagemeister f147df731c release 2013.01.17.1 2014-01-17 03:13:08 +01:00
Philipp Hagemeister 32c72b8b45 release 2013.01.17 2014-01-17 02:17:28 +01:00
Philipp Hagemeister 516b8fc6d9 Update format documentation (Closes #2158) 2014-01-17 01:42:52 +01:00
Philipp Hagemeister aeeca4fa5c release 2014.01.08 2014-01-08 23:47:26 +01:00
Philipp Hagemeister 831eb2bee7 release 2014.01.07.5 2014-01-07 10:13:29 +01:00
Philipp Hagemeister fadea0241b release 2014.01.07.3 2014-01-07 08:34:43 +01:00
Philipp Hagemeister 604543e9a1 release 2014.01.07.1 2014-01-07 08:18:36 +01:00
Philipp Hagemeister 7cdf324189 release 2014.01.07 2014-01-07 05:37:53 +01:00
Philipp Hagemeister 1af2a670f8 release 2014.01.06.1 2014-01-06 19:29:39 +01:00
Philipp Hagemeister e4a93bdc2f release 2014.01.06 2014-01-06 17:50:12 +01:00
Philipp Hagemeister 216955eed2 release 2014.01.05.6 2014-01-05 12:13:00 +01:00
Philipp Hagemeister 7e8fcedf9b release 2014.01.05.5 2014-01-05 06:00:47 +01:00
Philipp Hagemeister dcc555a4eb release 2014.01.05.4 2014-01-05 05:45:45 +01:00
Philipp Hagemeister a7b4b8b819 release 2014.01.05.3 2014-01-05 05:34:43 +01:00
Philipp Hagemeister 912c10737a [Atom feed] Make all dates valid 2014-01-05 05:15:50 +01:00
Philipp Hagemeister 898535e6fe Improve Atom feed (#2081) 2014-01-05 05:03:33 +01:00
Philipp Hagemeister 0e4c82eea4 release 2014.01.05.1 2014-01-05 04:52:35 +01:00
Philipp Hagemeister 8b20599d6f release 2014.01.03 2014-01-03 12:18:39 +01:00
Philipp Hagemeister 97c7ea358c release 2013.12.26 2013-12-26 22:00:58 +01:00
Philipp Hagemeister 3b506e4652 release 2013.12.23.4 2013-12-23 05:14:39 +01:00
Philipp Hagemeister d952e4ca00 release 2013.12.23.3 2013-12-23 04:46:57 +01:00
Philipp Hagemeister c817e24689 release 2013.12.23.2 2013-12-23 04:33:44 +01:00
Philipp Hagemeister bfacf9503c release 2013.12.23.1 2013-12-23 04:22:43 +01:00
Philipp Hagemeister 95f310dd4e release 2013.12.23 2013-12-23 03:49:04 +01:00
Philipp Hagemeister 977584c07e release 2013.12.20 2013-12-20 17:11:32 +01:00
Philipp Hagemeister 4bfb57ea3e release 2013.12.17.2 2013-12-17 13:05:32 +01:00
Philipp Hagemeister 9eacff6377 release 2013.12.17.1 2013-12-17 04:16:10 +01:00
Philipp Hagemeister 566ec7305b release 2013.12.17 2013-12-17 02:54:19 +01:00
Philipp Hagemeister 9d69b0e1e8 release 2013.12.16.7 2013-12-16 22:23:23 +01:00
Philipp Hagemeister ef3cf84635 release 2013.12.16.6 2013-12-16 21:49:53 +01:00
Philipp Hagemeister 5c0d5fd588 release 2013.12.16.4 2013-12-16 21:14:07 +01:00
Philipp Hagemeister 21bff54f24 release 2013.12.16.3 2013-12-16 14:46:48 +01:00
Philipp Hagemeister a06fb40105 release 2013.12.16.2 2013-12-16 14:20:46 +01:00
Philipp Hagemeister 5d60fe65c8 release 2013.12.16.1 2013-12-16 06:14:46 +01:00
Philipp Hagemeister 3f60bdf21c release 2013.12.16 2013-12-16 04:48:44 +01:00
Philipp Hagemeister 3f0efb919a release 2013.12.11.2 2013-12-11 09:28:50 +01:00
Philipp Hagemeister 128fd87c1d release 2013.12.11.1 2013-12-11 08:59:01 +01:00
Philipp Hagemeister b2c4d5023c release 2013.12.11 2013-12-11 08:52:00 +01:00
Philipp Hagemeister 12c220af97 release 2013.12.10 2013-12-10 11:58:50 +01:00
Philipp Hagemeister d5bd62fa47 release 2013.12.09.4 2013-12-09 20:11:45 +01:00
Philipp Hagemeister a002dc993a release 2013.12.09.2 2013-12-09 18:37:18 +01:00
Philipp Hagemeister 150fc5cece release 2013.12.09.1 2013-12-09 04:12:20 +01:00
Philipp Hagemeister e0156de904 release 2013.12.09 2013-12-09 03:05:04 +01:00
Philipp Hagemeister 998e24adc9 release 2013.12.08.1 2013-12-08 07:35:56 +01:00
Philipp Hagemeister a0a0fa236e release 2013.12.08 2013-12-08 06:59:04 +01:00
Philipp Hagemeister bad94a7022 release 2013.12.04 2013-12-04 14:21:30 +01:00
Philipp Hagemeister 09f355f73b release 2013.12.03 2013-12-03 13:18:19 +01:00
Philipp Hagemeister a1ce6f914d release 2013.12.02 2013-12-02 15:01:38 +01:00
Philipp Hagemeister abf0865149 release 2013.11.29 2013-11-29 03:38:08 +01:00
Philipp Hagemeister ffa5af300e release 2013.11.28.1 2013-11-28 06:20:08 +01:00
Philipp Hagemeister 79806ab5a3 release 2013.11.28 2013-11-28 05:53:55 +01:00
Philipp Hagemeister 88584f74b5 release 2013.11.26 2013-11-26 10:48:14 +01:00
Philipp Hagemeister 7079c07db2 release 2013.11.25.3 2013-11-25 22:57:46 +01:00
Philipp Hagemeister bd2cf1dd4f release 2013.11.25.2 2013-11-25 15:49:04 +01:00
Philipp Hagemeister 035ee9c090 release 2013.11.25.1 2013-11-25 06:09:46 +01:00
Philipp Hagemeister 94a36fdcea release 2013.11.25 2013-11-25 03:45:35 +01:00
Philipp Hagemeister fcaafe5619 Remove duplicate list from documentation
Users are prone to overread the introductory text and conclude that just because an extractor is missing in this list, youtube-dl does not support the site.
2013-11-24 07:58:32 +01:00
Philipp Hagemeister 2097054c58 release 2013.11.24.1 2013-11-24 07:55:07 +01:00
Philipp Hagemeister aa6e508a39 release 2013.11.24 2013-11-24 07:33:47 +01:00
Philipp Hagemeister 1e4364dad6 release 2013.11.22.2 2013-11-22 23:28:10 +01:00
Philipp Hagemeister 0c0e6a64a3 release 2013.11.22.1 2013-11-22 20:25:49 +01:00
Philipp Hagemeister dde4488dd2 release 2013.11.22 2013-11-22 17:48:53 +01:00
Philipp Hagemeister 1fce91664e release 2013.11.21 2013-11-21 14:02:04 +01:00
Philipp Hagemeister fa79991c9a release 2013.11.20 2013-11-20 07:32:28 +01:00
Philipp Hagemeister aa0740099f release 2013.11.19 2013-11-19 12:51:45 +01:00
Philipp Hagemeister 907ea8357a release 2013.11.18.1 2013-11-18 14:01:34 +01:00
Philipp Hagemeister d71ce54c9d release 2013.11.18 2013-11-18 13:34:28 +01:00
Philipp Hagemeister 6da31d3f46 release 2013.11.17 2013-11-17 22:24:23 +01:00
Philipp Hagemeister 2488c5a094 release 2013.11.15.1 2013-11-15 14:37:46 +01:00
Philipp Hagemeister 0d2799f1ab release 2013.11.15 2013-11-15 02:05:19 +01:00
Philipp Hagemeister a429266549 release 2013.11.13 2013-11-13 11:12:44 +01:00
Philipp Hagemeister c4e7461ac4 release 2013.11.11 2013-11-11 18:39:21 +01:00
Philipp Hagemeister 757e64afc6 release 2013.11.07 2013-11-07 11:09:17 +01:00
Philipp Hagemeister 0a7456c6a7 release 2013.11.06.1 2013-11-06 22:15:41 +01:00
Philipp Hagemeister 149f358828 release 2013.11.06 2013-11-06 14:09:37 +01:00
Philipp Hagemeister aaa19f7d06 release 2013.11.03 2013-11-03 15:53:20 +01:00
Philipp Hagemeister 0daef4a0e4 release 2013.11.02 2013-11-02 11:23:54 +01:00
Philipp Hagemeister e2d49a0b93 release 2013.10.30 2013-10-30 01:18:58 +01:00
Philipp Hagemeister e3e599a555 release 2013.10.29 2013-10-29 06:50:46 +01:00
Philipp Hagemeister f41816778d release 2013.10.28 2013-10-28 11:39:32 +01:00
Philipp Hagemeister 009dc7c5a5 release 2013.10.23.2 2013-10-23 18:43:11 +02:00
Philipp Hagemeister 46fb9e95a0 release 2013.10.23.1 2013-10-23 15:13:57 +02:00
Philipp Hagemeister c7b6aa8db1 release 2013.10.23 2013-10-23 00:10:29 +02:00
Philipp Hagemeister 1b3b408cc4 release 2013.10.22 2013-10-22 23:01:36 +02:00
Philipp Hagemeister e5adce03ae release 2013.10.18.2 2013-10-18 23:25:05 +02:00
Philipp Hagemeister 4478038b58 release 2013.10.18.1 2013-10-18 11:47:32 +02:00
Philipp Hagemeister 275fe0b84a release 2013.10.18 2013-10-18 11:20:38 +02:00
Philipp Hagemeister e703403b01 release 2013.10.17 2013-10-17 02:23:59 +02:00
Philipp Hagemeister 3014883d87 release 2013.10.15 2013-10-15 12:29:40 +02:00
Philipp Hagemeister 16200ac861 release 2013.10.09 2013-10-09 23:53:17 +02:00
Philipp Hagemeister 96bb7f8050 release 2013.10.07 2013-10-07 14:36:58 +02:00
Philipp Hagemeister 61cc6dede0 release 2013.10.06 2013-10-06 07:23:25 +02:00
Philipp Hagemeister 37b3fc7b06 release 2013.10.04 2013-10-04 00:40:16 +02:00
Philipp Hagemeister f793d89f34 release 2013.10.01.1 2013-10-01 14:47:16 +02:00
Philipp Hagemeister df20183f34 release 2013.10.01 2013-10-01 11:46:54 +02:00
Philipp Hagemeister 6e1229457b release 2013.09.29 2013-09-29 14:43:38 +02:00
Philipp Hagemeister ecfaa4b127 release 2013.09.24.2 2013-09-24 21:58:15 +02:00
Philipp Hagemeister c750a62ac5 release 2013.09.24.1 2013-09-24 21:42:50 +02:00
Philipp Hagemeister 8186c26b07 release 2013.09.24 2013-09-24 21:36:22 +02:00
Philipp Hagemeister da6ae4d37a release 2013.09.20.1 2013-09-20 23:02:39 +02:00
Philipp Hagemeister f0f42bac08 release 2013.09.20 2013-09-20 10:29:48 +02:00
Philipp Hagemeister 4ae561e7af release 2013.09.17 2013-09-17 17:09:18 +02:00
Philipp Hagemeister 1535e44625 Update download.html as well 2013-09-16 04:33:41 +02:00
Philipp Hagemeister 1cd889a44e Advertise wget and curl (Closes #1147) 2013-09-16 04:21:00 +02:00
Philipp Hagemeister ee087a70ed release 2013.09.16 2013-09-16 04:14:52 +02:00
Philipp Hagemeister 853c2b9864 release 2013.11.09 2013-09-11 11:37:40 +02:00
Philipp Hagemeister 347cfe3a4b release 2013.09.10 2013-09-10 11:55:57 +02:00
Philipp Hagemeister ed8aa85130 release 2013.09.07 2013-09-08 00:29:39 +02:00
Philipp Hagemeister d08c332b8b release 2013.09.06.1 2013-09-06 11:08:32 +02:00
Philipp Hagemeister 2b9e50990e release 2013.09.06 2013-09-06 10:17:43 +02:00
Philipp Hagemeister 65be4a1276 release 2013.09.05 2013-09-05 22:55:17 +02:00
Philipp Hagemeister 8c0e9c62c9 release 2013.09.04 2013-09-04 14:50:37 +02:00
Philipp Hagemeister 9649cab51f Clarify donations 2013-09-02 13:41:42 +02:00
Philipp Hagemeister 6794888237 Add donations page (Fixes #1344) 2013-09-02 12:35:27 +02:00
Jaime Marquínez Ferrándiz 0f783c4790 Readd yahoo to the list of supposed sites
It has been fixed for a while.
2013-08-31 15:26:16 +02:00
Jaime Marquínez Ferrándiz 23ed3711fc Added the template for the supported sites and the generated page.
Link to that page in documentation.html
2013-08-31 15:09:41 +02:00
Philipp Hagemeister dbd043409c release 2013.08.30 2013-08-30 21:23:11 +02:00
Philipp Hagemeister 3d34010890 release 2013.08.29 2013-08-30 00:06:37 +02:00
Philipp Hagemeister 1a1ebb10a2 release 2013.08.28.1 2013-08-28 19:29:10 +02:00
Philipp Hagemeister e9f671d34f release 2013.08.28 2013-08-27 23:42:12 +02:00
Philipp Hagemeister d4d6cce692 release 2013.08.27 2013-08-27 02:36:41 +02:00
Philipp Hagemeister cd73b9f9df release 2013.08.23 2013-08-23 23:42:40 +02:00
Philipp Hagemeister 721656267b release 2013.08.22 2013-08-22 23:33:47 +02:00
Philipp Hagemeister 0d3e82002d release 2013.08.17 2013-08-17 08:47:02 +02:00
Philipp Hagemeister f42ceac10d release 2013.08.15 2013-08-15 22:53:48 +02:00
Philipp Hagemeister 1415d02075 release 2013.08.14 2013-08-14 10:23:40 +02:00
Philipp Hagemeister bcdbda3abe release 2013.08.09 2013-08-09 15:52:30 +02:00
Philipp Hagemeister cde9507171 release 2013.08.08.1 2013-08-08 20:49:29 +02:00
Philipp Hagemeister 3d3d76e745 release 2013.08.08 2013-08-08 00:52:44 +02:00
Philipp Hagemeister a7d6fa532a release 2013.08.02 2013-08-02 13:37:57 +02:00
Philipp Hagemeister d8fb038cca release 2013.07.31 2013-07-31 10:58:43 +02:00
Philipp Hagemeister 42fe48f81d release 2013.07.25.2 2013-07-25 23:03:17 +02:00
Philipp Hagemeister cc86b1e78c release 2013.07.25.1 2013-07-25 10:11:29 +02:00
Philipp Hagemeister 45059e938c release 2013.07.25 2013-07-25 09:38:41 +02:00
Philipp Hagemeister 80cbf875da release 2013.07.24.2 2013-07-24 21:25:54 +02:00
Philipp Hagemeister b4fc4c0cae release 2013.07.23.1 2013-07-23 18:44:25 +02:00
Philipp Hagemeister 57adf7b86b release 2013.07.23 2013-07-23 15:55:34 +02:00
Philipp Hagemeister 18e616ccf2 release 2013.07.19 2013-07-19 23:49:46 +02:00
Philipp Hagemeister 86cc15eb06 release 2013.07.18 2013-07-18 12:49:56 +02:00
Philipp Hagemeister 8f61efc5d9 release 2013.07.17.1 2013-07-17 11:26:29 +02:00
Philipp Hagemeister 1a21103b9f release 2013.07.17 2013-07-17 01:27:10 +02:00
Philipp Hagemeister 7269f7f6f8 release 2013.07.12 2013-07-12 00:03:14 +02:00
Philipp Hagemeister 582d80020d release 2013.07.11 2013-07-11 22:04:57 +02:00
Philipp Hagemeister f8d6958341 release 2013.07.10 2013-07-10 11:41:37 +02:00
Philipp Hagemeister 1824d80813 release 2013.07.08.1 2013-07-08 02:09:19 +02:00
Philipp Hagemeister 604ea6fcc3 release 2013.07.08 2013-07-08 01:35:29 +02:00
Philipp Hagemeister f31532a09e release 2013.07.07.01 2013-07-07 17:18:02 +02:00
Philipp Hagemeister 36128970bc release 2013.07.05 2013-07-05 15:14:15 +02:00
Philipp Hagemeister 95f17ed689 release 2013.07.04 2013-07-04 18:11:49 +02:00
Philipp Hagemeister 5d23097037 release 2013.07.02 2013-07-02 09:23:11 +02:00
Philipp Hagemeister b4a0cb65d2 release 2013.06.34.4 2013-06-29 20:27:48 +02:00
Philipp Hagemeister f172f50972 release 2013.06.34.3 2013-06-29 17:36:18 +02:00
Philipp Hagemeister ddd7e3903d release 2013.06.34.2 2013-06-27 21:06:56 +02:00
Philipp Hagemeister c052f7300c release 2013.06.34.1 2013-06-27 18:01:54 +02:00
Philipp Hagemeister 375db3649b release 2013.06.34 2013-06-27 13:06:57 +02:00
Philipp Hagemeister 4bcac9eadc release 2013.06.33 2013-06-25 22:45:44 +02:00
Philipp Hagemeister f2392cc419 release 2013.06.32 2013-06-25 21:05:34 +02:00
Philipp Hagemeister 4bba8b41a0 release 2013.06.31 2013-06-25 18:43:38 +02:00
Philipp Hagemeister 1deaa4f150 release 2013.06.30 2013-06-25 12:31:16 +02:00
Philipp Hagemeister e6e5130471 release 2013.06.29 2013-06-24 14:59:54 +02:00
Philipp Hagemeister eb409ed6e1 release 2013.06.28 2013-06-24 12:46:24 +02:00
Philipp Hagemeister 79ce12a211 release 2013.06.27 2013-06-24 10:42:51 +02:00
Philipp Hagemeister 93dad3d78c release 2013.06.26 2013-06-24 01:09:03 +02:00
Philipp Hagemeister 064abef36f release 2013.06.25 2013-06-24 00:23:35 +02:00
Philipp Hagemeister 6356d0fecd Merge branch 'gh-pages' of github.com:rg3/youtube-dl into gh-pages 2013-06-23 23:55:13 +02:00
Philipp Hagemeister 5ac0aa0a56 merge 2013-06-23 23:54:49 +02:00
Philipp Hagemeister 96c4401076 release 2013.06.23 2013-06-23 23:53:53 +02:00
Philipp Hagemeister 97b8fca23b release 2013.06.23 2013-06-23 23:51:30 +02:00
Philipp Hagemeister 4d45501232 Merge pull request #891 from Lx/gh-pages-conf-format
describe permitted .conf file content
2013-06-23 09:42:34 -07:00
Philipp Hagemeister c523528639 document new PGP key 2013-06-21 23:05:33 +02:00
Philipp Hagemeister 0b9da457da release 2013.06.21 2013-06-21 00:41:48 +02:00
Alex Peters 5ea8a2b4ff describe permitted .conf file content
Determined by inspecting the documentation for the Python `shlex`
module:

http://docs.python.org/2/library/shlex.html
2013-06-17 21:55:15 +10:00
Philipp Hagemeister d2b808ce4b release 2013.05.23 2013-05-23 13:42:54 +02:00
Philipp Hagemeister dadcaecdfa release 2013.05.14 2013-05-13 14:55:23 +02:00
Philipp Hagemeister 46d641a918 release 2013.05.10 2013-05-10 02:00:07 +02:00
Philipp Hagemeister 1feb835874 release 2013.05.07 2013-05-05 21:25:13 +02:00
Philipp Hagemeister 4fd46f8015 release 2013.05.05 2013-05-04 12:34:53 +02:00
Philipp Hagemeister 8a42178a6a release 2013.05.04 2013-05-04 07:17:52 +02:00
Philipp Hagemeister bb224c7c78 release 2013.05.01 2013-05-01 14:11:35 +02:00
Philipp Hagemeister ff99565e9b release 2013.04.31 2013-04-30 19:56:24 +02:00
Philipp Hagemeister 532122da0f release 2013.04.30 2013-04-30 18:27:06 +02:00
Philipp Hagemeister abcefe99a1 release 2013.04.28 2013-04-28 16:35:35 +02:00
Philipp Hagemeister 4fd463e86c Disable notes 2013-04-28 16:22:55 +02:00
Philipp Hagemeister 81c8e577ae release 2013.04.27 2013-04-28 16:15:14 +02:00
Philipp Hagemeister bb6dfb9553 Add note about -t by default 2013-04-27 21:59:15 +02:00
Jaime Marquínez Ferrándiz 3bd90071be Document order of preference for format selection (related #798) 2013-04-23 10:29:39 +02:00
Philipp Hagemeister fa6bc20e23 release 2013.04.22 2013-04-22 20:37:04 +02:00
Philipp Hagemeister d8330e5a1d release 2013.04.21 2013-04-21 13:00:36 +02:00
Philipp Hagemeister d429f1174a release 2013.04.18 2013-04-18 06:42:52 +02:00
Filippo Valsorda 543b572f1f Merge pull request #788 from jaimeMF/pip
Add pip installation command
2013-04-17 10:57:50 -07:00
Jaime Marquínez Ferrándiz fda2d312d4 Add link to pypi and a note about the update 2013-04-16 14:47:08 +02:00
Jaime Marquínez Ferrándiz 4a715cbacb Add pip installation command
Closes #786
2013-04-16 14:24:05 +02:00
Philipp Hagemeister de1351917d Merge pull request #787 from jaimeMF/commands_style
Move the style of the commands in download.html to style.css
2013-04-16 05:15:27 -07:00
Jaime Marquínez Ferrándiz 8f273b2c05 Move the style of the commands in download.html to style.css 2013-04-16 14:06:16 +02:00
Philipp Hagemeister f5c68c0d86 release 2013.04.11 2013-04-11 18:49:43 +02:00
Filippo Valsorda 24a170f8fd new GPG key - this new one is signed by the old expiried one - closes #779 2013-04-06 21:55:12 +02:00
Filippo Valsorda c5ba189be4 release 2013.04.03 2013-04-06 20:53:44 +02:00
Ricardo Garcia 66661fc019 Change rg3.github.com to rg3.github.io in atom feed 2013-04-06 10:43:34 +02:00
Ricardo Garcia a87558dc12 Documentation license change (fixes #768) 2013-04-03 20:09:38 +02:00
Philipp Hagemeister 8dfa0b76e6 minor feed improvements 2013-03-29 21:43:48 +01:00
Philipp Hagemeister 18aab9843e Correct feed URL 2013-03-29 21:37:51 +01:00
Philipp Hagemeister 7d8127b4b8 release 2013.03.29 2013-03-29 21:29:41 +01:00
Philipp Hagemeister 2386b04c24 Ignore egg-info 2013-03-29 21:19:05 +01:00
Philipp Hagemeister cd0b44fd5e Add link to RSS feed (#758) 2013-03-29 21:18:48 +01:00
Philipp Hagemeister ad8385f1fc release 2013.02.25 2013-02-25 00:34:21 +01:00
Philipp Hagemeister a908b7b816 release 2013.02.22 2013-02-24 00:55:16 +01:00
Philipp Hagemeister 4d8623ed73 release 2012.02.22 2013-02-22 16:46:14 +01:00
Philipp Hagemeister 30b8dbc74f Add note 2013-02-22 16:39:03 +01:00
Philipp Hagemeister e06c1be12d release 2013.02.19 2013-02-19 00:09:58 +01:00
Philipp Hagemeister 1726ff8b2e release 2013.02.18 2013-02-18 23:40:33 +01:00
Philipp Hagemeister 566a15f553 release 2013.02.02 2013-02-02 14:51:42 +01:00
Philipp Hagemeister 99c81cae13 release 2013.02.01 2013-02-01 18:00:15 +01:00
Philipp Hagemeister aa26b834bb release 2013.01.28 2013-01-27 19:10:13 +01:00
Philipp Hagemeister 2644989c7c Merge pull request #636 from jaimeMF/gh-pages
Fix the path to the configuration file
2013-01-13 10:23:50 -08:00
Jaime Marquínez Ferrándiz 158d35395f Fix the path to the configuration file 2013-01-13 19:10:44 +01:00
Philipp Hagemeister 7fb298e717 release 2013.01.13 2013-01-12 22:23:55 +01:00
Philipp Hagemeister 680adff87b Merge branch 'gh-pages' of /home/phihag/projects/youtube-dl/. into gh-pages
Conflicts:
	download.html
	update/LATEST_VERSION
	update/versions.json
2013-01-12 18:20:20 +01:00
Philipp Hagemeister efad35fb70 release 2013.01.12 2013-01-12 18:15:24 +01:00
Philipp Hagemeister 56e6a59bc9 release 2013.01.11 2013-01-12 18:06:07 +01:00
Philipp Hagemeister 7aa6cb5973 release 2013.01.11 2013-01-11 08:13:12 +01:00
Philipp Hagemeister 45af080919 release 2013.01.08 2013-01-08 10:33:37 +01:00
Philipp Hagemeister ffea5c2832 Delete test_coverage 2013-01-06 23:38:56 +01:00
Philipp Hagemeister 77b66b65bd release 2013.01.06 2013-01-06 23:27:36 +01:00
Philipp Hagemeister 3d710b6438 fix signature 2013-01-03 19:08:58 +01:00
Philipp Hagemeister 0ec40a3f16 remove 2012.12.11 from json (doesn't follow new naming scheme) 2013-01-03 18:53:29 +01:00
Philipp Hagemeister 077eb935fb Github seems to (wrongly) optimize HTML pages now, fix download page 2013-01-03 18:35:54 +01:00
Filippo Valsorda 2f0d49e70a release 2013.01.02 2013-01-02 22:47:06 +01:00
Filippo Valsorda a07549dc0f Updating docs 2012-12-31 18:44:31 +01:00
Filippo Valsorda 377689c9ee New download page template 2012-12-31 18:35:23 +01:00
Filippo Valsorda 38a4d6feac populated the .gitignore as we switch branches often; cover/ will be test_coverage/ 2012-12-31 18:10:52 +01:00
Filippo Valsorda 35a06ec0f4 undo 2012.12.27, never released to the public 2012-12-30 21:00:31 +01:00
Filippo Valsorda c8ff21bd82 moved docs and updates generation scripts from gh-pages branch to master 2012-12-30 20:12:14 +01:00
Filippo Valsorda 0e91665e1a New updates system and release 2012.12.27 2012-12-27 01:59:12 +01:00
Filippo Valsorda 480c972687 Test code coverage data in /cover/ 2012-12-18 12:44:56 +01:00
Philipp Hagemeister 642b5170e3 Update Python version in downloads 2012-12-16 13:43:29 +01:00
Philipp Hagemeister 6065e27a13 Add wget installation instructions 2012-12-16 13:39:45 +01:00
Philipp Hagemeister c860c18297 Build everything with Python 3 2012-12-16 13:33:56 +01:00
Philipp Hagemeister 2840ae3a8f Update homepage 2012-12-11 17:52:56 +01:00
Philipp Hagemeister b51355ef99 Credit @FiloSottile 2012-12-11 17:43:50 +01:00
Philipp Hagemeister 1762b604b1 new release 2012-12-11 17:34:56 +01:00
Philipp Hagemeister 8c64aa48d2 New generate-download for the new build mechanism 2012-12-11 17:34:33 +01:00
Philipp Hagemeister 8c3a45b6e4 Update supported extractors 2012-11-30 08:38:14 +01:00
Philipp Hagemeister dc9e2d8e0c Document CollegeHumor and arte.tv 2012-11-28 17:57:06 +01:00
Philipp Hagemeister e7d727efc2 Drop 2.5 support 2012-11-28 03:30:44 +01:00
Philipp Hagemeister a89c5d20c7 2012.11.29 2012-11-27 18:37:27 +01:00
Philipp Hagemeister 044cb16590 release 2012.11.28 2012-11-27 16:16:51 +01:00
Philipp Hagemeister 81db2d8040 Document --restrict-filenames, remove stitle 2012-11-27 16:06:59 +01:00
Philipp Hagemeister 2811949f4c Update doc for 2012.11.27 2012-11-27 00:26:05 +01:00
Philipp Hagemeister 3a2c78a2da Document configuration options 2012-11-26 23:23:33 +01:00
Philipp Hagemeister 23cf656bd1 Automatically update copyright (Closes #549) 2012-11-26 11:19:40 +01:00
Philipp Hagemeister 41ff5333f7 ignore temporary files 2012-11-26 11:18:31 +01:00
Philipp Hagemeister 571d094858 Clarify required Python version (#549) 2012-11-26 11:18:08 +01:00
Philipp Hagemeister 96679b26cf release 2012.11.17 2012-11-26 04:05:23 +01:00
Philipp Hagemeister b7d1a2247e Python 2.6 instead of 2.5 in about (Closes #516) 2012-11-07 01:49:35 +01:00
Philipp Hagemeister 9746c518a5 update docs 2012-10-10 19:33:08 +02:00
Philipp Hagemeister 959fa33ef9 release youtube-dl 2012.10.09 2012-10-10 19:27:50 +02:00
Philipp Hagemeister a44c76cdf4 release release 2012.09.27 2012-09-28 13:27:54 +02:00
Philipp Hagemeister 6a3ff26a01 Add recent breakages to FAQ (#433) 2012-09-27 23:29:40 +02:00
Philipp Hagemeister 60a1c34662 Drop 2.5 compatibility 2012-09-27 15:33:20 +02:00
Philipp Hagemeister 49bd2af54f Clarify 402 2012-02-28 02:00:00 +01:00
Philipp Hagemeister a5999b83fd release 2012.02.27 2012-02-27 20:20:08 +01:00
Philipp Hagemeister 82eedd7d83 update docs for 2012.02.26 2012-02-27 00:43:20 +01:00
Philipp Hagemeister 759320387a Use stitle instead of title in doc 2012-01-12 20:15:54 +01:00
Philipp Hagemeister 0be94d4c6a release 2012.01.08b docs 2012-01-08 17:24:46 +01:00
Philipp Hagemeister 6bd03617e6 docs for 2012.01.05 2012-01-05 11:10:14 +01:00
Philipp Hagemeister a3d06cfe6d doc for 2011.12.18 2011-12-17 01:35:45 +01:00
Philipp Hagemeister cd891850ec Release 2011.12.15 2011-12-15 20:33:49 +01:00
Philipp Hagemeister cdee681a2c Release 2011.12.08 2011-12-08 21:41:43 +01:00
Philipp Hagemeister db2c6281eb Release 2011.11.23: Update webpage 2011-11-23 10:39:50 +01:00
Philipp Hagemeister 06a129c9dd Release 2011.11.22 2011-11-22 15:39:04 +01:00
Philipp Hagemeister 5a938b5d04 +Makefile 2011-11-22 15:38:32 +01:00
Philipp Hagemeister b0e67d2404 Release 2011.11.21 2011-11-21 21:54:04 +01:00
Philipp Hagemeister 92961845c3 docs for 2011.10.19 2011-10-19 00:40:55 +02:00
Philipp Hagemeister f9c3398449 release 2011.09.30 2011-09-30 09:08:53 +02:00
Philipp Hagemeister bffe76ba4b Remove empty <tr>s in index 2011-09-28 09:55:54 +02:00
Philipp Hagemeister 0d29baa7c6 Make documentation valid HTML 2011-09-28 09:53:50 +02:00
Philipp Hagemeister dd9d3efcfb Make FAQ valid HTML 2011-09-28 09:52:01 +02:00
Philipp Hagemeister 9014805f26 Fix stray </a> in FAQ 2011-09-28 09:50:04 +02:00
Philipp Hagemeister f973da57a4 Switch to HTML5 doctype 2011-09-28 09:49:19 +02:00
Philipp Hagemeister a073eb792f Update documentation and FAQ 2011-09-28 09:47:54 +02:00
Philipp Hagemeister f202278fe1 Close <ul> in first FAQ question 2011-09-28 09:29:19 +02:00
Philipp Hagemeister 3328f34eb3 Update homepage for 2011.09.27 2011-09-27 21:44:59 +02:00
Philipp Hagemeister 50d7a239ae Raise correct args in exception 2011-09-27 21:44:02 +02:00
Philipp Hagemeister 81eb98e4bd Update docs 2011-09-27 21:17:50 +02:00
Philipp Hagemeister 78ef538449 Updated bug reporting instructions in the FAQ 2011-09-27 20:58:37 +02:00
Philipp Hagemeister 6e3ed6385c Download regeneration works with python 2.5 2011-09-27 20:50:03 +02:00
Ricardo Garcia a2dafa7316 Add creative commons license to webpages 2011-09-27 20:17:34 +02:00
Philipp Hagemeister 86e5e08789 release 2011.09.18c 2011-09-17 00:59:45 +02:00
Philipp Hagemeister e397cf5b47 2011.09.18b 2011-09-16 22:33:54 +02:00
Philipp Hagemeister 3fd01c88c5 Update documentation for 2011.09.18 2011-09-15 19:29:53 +02:00
Philipp Hagemeister d2cbb552b7 Update documentation for 2011.09.17 2011-09-15 19:25:07 +02:00
Philipp Hagemeister 9c358a8982 Release 2011.09.16 2011-09-15 18:49:20 +02:00
Philipp Hagemeister 7923514ea0 Release 2011.09.15 2011-09-14 22:56:50 +02:00
Philipp Hagemeister 004759c8f2 Add a prominent Develop link in the documentation (Closes #155) 2011-09-14 00:42:45 +02:00
Philipp Hagemeister 95f029a5d8 Release 2011.09.14 2011-09-14 00:02:16 +02:00
Philipp Hagemeister ac47cfe369 Update generate-download 2011-09-13 23:06:09 +02:00
Philipp Hagemeister 463fe92d26 Release new version 2011-09-13 23:05:58 +02:00
Ricardo Garcia ba8a90b0bf Regenerate download page 2011-08-04 19:16:25 +02:00
Ricardo Garcia 3a6c68881a Regenerate download page 2011-03-29 20:33:22 +02:00
Ricardo Garcia a290358c2f Regenerate download page 2011-02-26 00:48:41 +01:00
Ricardo Garcia 17faaa4548 Regenerate download page 2011-02-25 21:54:55 +01:00
Ricardo Garcia 3ea19089f9 Regenerate download page 2011-02-25 20:13:32 +01:00
Ricardo Garcia 9077650490 Add Gergely Imreh to the author list 2011-02-20 18:03:52 +01:00
Ricardo Garcia 2ecbfb4cbc Properly update Python version requirement in the documentation 2011-01-30 13:14:45 +01:00
Ricardo Garcia 3519974510 Regenerate download page 2011-01-30 13:12:45 +01:00
Ricardo Garcia e76e4dc8b1 Add Paweł Paprota to the list of authors in the documentation 2011-01-30 13:12:10 +01:00
Ricardo Garcia 01e013b5e3 Mention support for YouTube.com user videos in the documentation 2011-01-30 13:10:22 +01:00
Ricardo Garcia 64ea994344 Update documentation to reflect Python 2.5 requirement 2011-01-30 12:56:52 +01:00
Ricardo Garcia a3e9208c01 Add Witold Baryluk to the author list 2011-01-21 18:19:43 +01:00
Ricardo Garcia ec0d2e46c5 Add link to the project page from the about page 2011-01-07 10:43:13 +01:00
Ricardo Garcia 1b3dbefb04 Modify pages increasing the copyright year 2011-01-03 10:54:47 +01:00
Ricardo Garcia c3003bb4db Add question about option -g to the FAQ 2011-01-03 10:54:07 +01:00
Ricardo Garcia 6c7d5d8d14 Regenerate download page 2010-12-09 19:58:30 +01:00
Ricardo Garcia 35c0ad4587 Regenerate download page 2010-12-09 19:38:15 +01:00
Ricardo Garcia 941383109a Document new DepositFiles InfoExtractor 2010-12-08 10:59:59 +01:00
Ricardo Garcia c95ac4f062 Regenerate download page 2010-11-19 19:42:22 +01:00
Ricardo Garcia 0732b1f39f Document change from "ord" to "autonumber" in template 2010-11-19 19:38:36 +01:00
Ricardo Garcia 8d78373ea0 Document new upload_date template parameter 2010-11-19 19:38:07 +01:00
Ricardo Garcia 17082ad9db Put the main buttons ordered vertically 2010-11-19 18:24:03 +01:00
Ricardo Garcia 725d763c48 Reorganize website 2010-11-19 18:19:41 +01:00
Ricardo Garcia a993428ac9 Document rtmpdump's optional requirement in the main page 2010-11-06 18:46:50 +01:00
Ricardo Garcia 7136abc8f3 Improve documentation on "SyntaxError" to take Python 3.x into account 2010-11-06 18:45:10 +01:00
Ricardo Garcia 790622091e Renamed "generate-home" to "generate-index" for consistency 2010-11-06 11:29:10 +01:00
Ricardo Garcia db03ff92a6 Changed a few style aspects to make the page look better under IE 2010-11-06 11:29:10 +01:00
Ricardo Garcia b294d9fa38 Use SSL in URLs where possible 2010-11-06 11:28:56 +01:00
Ricardo Garcia 5de4c645e2 Make web pages valid HTML and remove a few glitches 2010-11-02 21:28:56 +01:00
Ricardo Garcia 05703c8f9c Minor changes to stylesheet 2010-11-01 11:28:23 +01:00
Ricardo Garcia f37b138766 Add copyright notice at the bottom of every page 2010-11-01 08:23:57 +01:00
Ricardo Garcia 5c7359365f Initial pages commit, using the wiki contents 2010-10-31 23:50:48 +01:00
124 changed files with 27509 additions and 10652 deletions
+3 -3
View File
@@ -1,6 +1,7 @@
updates_key.pem
*~
*.pyc
*.pyo
*~
*.DS_Store
wine-py2exe/
py2exe.log
@@ -16,5 +17,4 @@ youtube-dl.exe
youtube-dl.tar.gz
.coverage
cover/
updates_key.pem
*.egg-info
youtube_dl.egg-info/
-15
View File
@@ -1,15 +0,0 @@
language: python
python:
- "2.6"
- "2.7"
- "3.3"
script: nosetests test --verbose
notifications:
email:
- filippo.valsorda@gmail.com
- phihag@phihag.de
- jaime.marquinez.ferrandiz+travis@gmail.com
# irc:
# channels:
# - "irc.freenode.org#youtube-dl"
# skip_join: true
-14
View File
@@ -1,14 +0,0 @@
2013.01.02 Codename: GIULIA
* Add support for ComedyCentral clips <nto>
* Corrected Vimeo description fetching <Nick Daniels>
* Added the --no-post-overwrites argument <Barbu Paul - Gheorghe>
* --verbose offers more environment info
* New info_dict field: uploader_id
* New updates system, with signature checking
* New IEs: NBA, JustinTV, FunnyOrDie, TweetReel, Steam, Ustream
* Fixed IEs: BlipTv
* Fixed for Python 3 IEs: Xvideo, Youku, XNXX, Dailymotion, Vimeo, InfoQ
* Simplified IEs and test code
* Various (Python 3 and other) fixes
* Revamped and expanded tests
-1
View File
@@ -1 +0,0 @@
2012.12.99
-24
View File
@@ -1,24 +0,0 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
-5
View File
@@ -1,5 +0,0 @@
include README.md
include test/*.py
include test/*.json
include youtube-dl.bash-completion
include youtube-dl.1
-78
View File
@@ -1,78 +0,0 @@
all: youtube-dl README.md README.txt youtube-dl.1 youtube-dl.bash-completion
clean:
rm -rf youtube-dl.1 youtube-dl.bash-completion README.txt MANIFEST build/ dist/ .coverage cover/ youtube-dl.tar.gz
cleanall: clean
rm -f youtube-dl youtube-dl.exe
PREFIX=/usr/local
BINDIR=$(PREFIX)/bin
MANDIR=$(PREFIX)/man
PYTHON=/usr/bin/env python
# set SYSCONFDIR to /etc if PREFIX=/usr or PREFIX=/usr/local
ifeq ($(PREFIX),/usr)
SYSCONFDIR=/etc
else
ifeq ($(PREFIX),/usr/local)
SYSCONFDIR=/etc
else
SYSCONFDIR=$(PREFIX)/etc
endif
endif
install: youtube-dl youtube-dl.1 youtube-dl.bash-completion
install -d $(DESTDIR)$(BINDIR)
install -m 755 youtube-dl $(DESTDIR)$(BINDIR)
install -d $(DESTDIR)$(MANDIR)/man1
install -m 644 youtube-dl.1 $(DESTDIR)$(MANDIR)/man1
install -d $(DESTDIR)$(SYSCONFDIR)/bash_completion.d
install -m 644 youtube-dl.bash-completion $(DESTDIR)$(SYSCONFDIR)/bash_completion.d/youtube-dl
test:
#nosetests --with-coverage --cover-package=youtube_dl --cover-html --verbose --processes 4 test
nosetests --verbose test
tar: youtube-dl.tar.gz
.PHONY: all clean install test tar bash-completion pypi-files
pypi-files: youtube-dl.bash-completion README.txt youtube-dl.1
youtube-dl: youtube_dl/*.py youtube_dl/*/*.py
zip --quiet youtube-dl youtube_dl/*.py youtube_dl/*/*.py
zip --quiet --junk-paths youtube-dl youtube_dl/__main__.py
echo '#!$(PYTHON)' > youtube-dl
cat youtube-dl.zip >> youtube-dl
rm youtube-dl.zip
chmod a+x youtube-dl
README.md: youtube_dl/*.py youtube_dl/*/*.py
COLUMNS=80 python -m youtube_dl --help | python devscripts/make_readme.py
README.txt: README.md
pandoc -f markdown -t plain README.md -o README.txt
youtube-dl.1: README.md
pandoc -s -f markdown -t man README.md -o youtube-dl.1
youtube-dl.bash-completion: youtube_dl/*.py youtube_dl/*/*.py devscripts/bash-completion.in
python devscripts/bash-completion.py
bash-completion: youtube-dl.bash-completion
youtube-dl.tar.gz: youtube-dl README.md README.txt youtube-dl.1 youtube-dl.bash-completion
@tar -czf youtube-dl.tar.gz --transform "s|^|youtube-dl/|" --owner 0 --group 0 \
--exclude '*.DS_Store' \
--exclude '*.kate-swp' \
--exclude '*.pyc' \
--exclude '*.pyo' \
--exclude '*~' \
--exclude '__pycache' \
--exclude '.git' \
-- \
bin devscripts test youtube_dl \
CHANGELOG LICENSE README.md README.txt \
Makefile MANIFEST.in youtube-dl.1 youtube-dl.bash-completion setup.py \
youtube-dl
-251
View File
@@ -1,251 +0,0 @@
% YOUTUBE-DL(1)
# NAME
youtube-dl - download videos from youtube.com or other video platforms
# SYNOPSIS
**youtube-dl** [OPTIONS] URL [URL...]
# DESCRIPTION
**youtube-dl** is a small command-line program to download videos from
YouTube.com and a few more sites. It requires the Python interpreter, version
2.6, 2.7, or 3.3+, and it is not platform specific. It should work on
your Unix box, on Windows or on Mac OS X. It is released to the public domain,
which means you can modify it, redistribute it or use it however you like.
# OPTIONS
-h, --help print this help text and exit
--version print program version and exit
-U, --update update this program to latest version
-i, --ignore-errors continue on download errors
-r, --rate-limit LIMIT maximum download rate (e.g. 50k or 44.6m)
-R, --retries RETRIES number of retries (default is 10)
--buffer-size SIZE size of download buffer (e.g. 1024 or 16k)
(default is 1024)
--no-resize-buffer do not automatically adjust the buffer size. By
default, the buffer size is automatically resized
from an initial value of SIZE.
--dump-user-agent display the current browser identification
--user-agent UA specify a custom user agent
--referer REF specify a custom referer, use if the video access
is restricted to one domain
--list-extractors List all supported extractors and the URLs they
would handle
--proxy URL Use the specified HTTP/HTTPS proxy
--no-check-certificate Suppress HTTPS certificate validation.
## Video Selection:
--playlist-start NUMBER playlist video to start at (default is 1)
--playlist-end NUMBER playlist video to end at (default is last)
--match-title REGEX download only matching titles (regex or caseless
sub-string)
--reject-title REGEX skip download for matching titles (regex or
caseless sub-string)
--max-downloads NUMBER Abort after downloading NUMBER files
--min-filesize SIZE Do not download any videos smaller than SIZE
(e.g. 50k or 44.6m)
--max-filesize SIZE Do not download any videos larger than SIZE (e.g.
50k or 44.6m)
--date DATE download only videos uploaded in this date
--datebefore DATE download only videos uploaded before this date
--dateafter DATE download only videos uploaded after this date
## Filesystem Options:
-t, --title use title in file name (default)
--id use only video ID in file name
-l, --literal [deprecated] alias of --title
-A, --auto-number number downloaded files starting from 00000
-o, --output TEMPLATE output filename template. Use %(title)s to get
the title, %(uploader)s for the uploader name,
%(uploader_id)s for the uploader nickname if
different, %(autonumber)s to get an automatically
incremented number, %(ext)s for the filename
extension, %(upload_date)s for the upload date
(YYYYMMDD), %(extractor)s for the provider
(youtube, metacafe, etc), %(id)s for the video id
, %(playlist)s for the playlist the video is in,
%(playlist_index)s for the position in the
playlist and %% for a literal percent. Use - to
output to stdout. Can also be used to download to
a different directory, for example with -o '/my/d
ownloads/%(uploader)s/%(title)s-%(id)s.%(ext)s' .
--autonumber-size NUMBER Specifies the number of digits in %(autonumber)s
when it is present in output filename template or
--autonumber option is given
--restrict-filenames Restrict filenames to only ASCII characters, and
avoid "&" and spaces in filenames
-a, --batch-file FILE file containing URLs to download ('-' for stdin)
-w, --no-overwrites do not overwrite files
-c, --continue resume partially downloaded files
--no-continue do not resume partially downloaded files (restart
from beginning)
--cookies FILE file to read cookies from and dump cookie jar in
--no-part do not use .part files
--no-mtime do not use the Last-modified header to set the
file modification time
--write-description write video description to a .description file
--write-info-json write video metadata to a .info.json file
--write-thumbnail write thumbnail image to disk
## Verbosity / Simulation Options:
-q, --quiet activates quiet mode
-s, --simulate do not download the video and do not write
anything to disk
--skip-download do not download the video
-g, --get-url simulate, quiet but print URL
-e, --get-title simulate, quiet but print title
--get-id simulate, quiet but print id
--get-thumbnail simulate, quiet but print thumbnail URL
--get-description simulate, quiet but print video description
--get-filename simulate, quiet but print output filename
--get-format simulate, quiet but print output format
--newline output progress bar as new lines
--no-progress do not print progress bar
--console-title display progress in console titlebar
-v, --verbose print various debugging information
--dump-intermediate-pages print downloaded pages to debug problems(very
verbose)
## Video Format Options:
-f, --format FORMAT video format code, specifiy the order of
preference using slashes: "-f 22/17/18"
--all-formats download all available video formats
--prefer-free-formats prefer free video formats unless a specific one
is requested
--max-quality FORMAT highest quality format to download
-F, --list-formats list all available formats (currently youtube
only)
--write-sub write subtitle file (currently youtube only)
--only-sub [deprecated] alias of --skip-download
--all-subs downloads all the available subtitles of the
video (currently youtube only)
--list-subs lists all available subtitles for the video
(currently youtube only)
--sub-format FORMAT subtitle format [srt/sbv] (default=srt)
(currently youtube only)
--sub-lang LANG language of the subtitles to download (optional)
use IETF language tags like 'en'
## Authentication Options:
-u, --username USERNAME account username
-p, --password PASSWORD account password
-n, --netrc use .netrc authentication data
## Post-processing Options:
-x, --extract-audio convert video files to audio-only files (requires
ffmpeg or avconv and ffprobe or avprobe)
--audio-format FORMAT "best", "aac", "vorbis", "mp3", "m4a", "opus", or
"wav"; best by default
--audio-quality QUALITY ffmpeg/avconv audio quality specification, insert
a value between 0 (better) and 9 (worse) for VBR
or a specific bitrate like 128K (default 5)
--recode-video FORMAT Encode the video to another format if necessary
(currently supported: mp4|flv|ogg|webm)
-k, --keep-video keeps the video file on disk after the post-
processing; the video is erased by default
--no-post-overwrites do not overwrite post-processed files; the post-
processed files are overwritten by default
# CONFIGURATION
You can configure youtube-dl by placing default arguments (such as `--extract-audio --no-mtime` to always extract the audio and not copy the mtime) into `/etc/youtube-dl.conf` and/or `~/.config/youtube-dl.conf`.
# OUTPUT TEMPLATE
The `-o` option allows users to indicate a template for the output file names. The basic usage is not to set any template arguments when downloading a single file, like in `youtube-dl -o funny_video.flv "http://some/video"`. However, it may contain special sequences that will be replaced when downloading each video. The special sequences have the format `%(NAME)s`. To clarify, that is a percent symbol followed by a name in parenthesis, followed by a lowercase S. Allowed names are:
- `id`: The sequence will be replaced by the video identifier.
- `url`: The sequence will be replaced by the video URL.
- `uploader`: The sequence will be replaced by the nickname of the person who uploaded the video.
- `upload_date`: The sequence will be replaced by the upload date in YYYYMMDD format.
- `title`: The sequence will be replaced by the video title.
- `ext`: The sequence will be replaced by the appropriate extension (like flv or mp4).
- `epoch`: The sequence will be replaced by the Unix epoch when creating the file.
- `autonumber`: The sequence will be replaced by a five-digit number that will be increased with each download, starting at zero.
- `playlist`: The name or the id of the playlist that contains the video.
- `playlist_index`: The index of the video in the playlist, a five-digit number.
The current default template is `%(id)s.%(ext)s`, but that will be switchted to `%(title)s-%(id)s.%(ext)s` (which can be requested with `-t` at the moment).
In some cases, you don't want special characters such as 中, spaces, or &, such as when transferring the downloaded filename to a Windows system or the filename through an 8bit-unsafe channel. In these cases, add the `--restrict-filenames` flag to get a shorter title:
$ youtube-dl --get-filename -o "%(title)s.%(ext)s" BaW_jenozKc
youtube-dl test video ''_ä↭𝕐.mp4 # All kinds of weird characters
$ youtube-dl --get-filename -o "%(title)s.%(ext)s" BaW_jenozKc --restrict-filenames
youtube-dl_test_video_.mp4 # A simple file name
# VIDEO SELECTION
Videos can be filtered by their upload date using the options `--date`, `--datebefore` or `--dateafter`, they accept dates in two formats:
- Absolute dates: Dates in the format `YYYYMMDD`.
- Relative dates: Dates in the format `(now|today)[+-][0-9](day|week|month|year)(s)?`
Examples:
$ youtube-dl --dateafter now-6months #will only download the videos uploaded in the last 6 months
$ youtube-dl --date 19700101 #will only download the videos uploaded in January 1, 1970
$ youtube-dl --dateafter 20000101 --datebefore 20100101 #will only download the videos uploaded between 2000 and 2010
# FAQ
### Can you please put the -b option back?
Most people asking this question are not aware that youtube-dl now defaults to downloading the highest available quality as reported by YouTube, which will be 1080p or 720p in some cases, so you no longer need the -b option. For some specific videos, maybe YouTube does not report them to be available in a specific high quality format you''re interested in. In that case, simply request it with the -f option and youtube-dl will try to download it.
### I get HTTP error 402 when trying to download a video. What's this?
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.
### 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/).
### The links provided by youtube-dl -g are not working anymore
The URLs youtube-dl outputs require the downloader to have the correct cookies. Use the `--cookies` option to write the required cookies into a file, and advise your downloader to read cookies from that file. Some sites also require a common user agent to be used, use `--dump-user-agent` to see the one in use by youtube-dl.
### ERROR: no fmt_url_map or conn information found in video info
youtube has switched to a new video info format in July 2011 which is not supported by old versions of youtube-dl. You can update youtube-dl with `sudo youtube-dl --update`.
### ERROR: unable to download video ###
youtube requires an additional signature since September 2012 which is not supported by old versions of youtube-dl. You can update youtube-dl with `sudo youtube-dl --update`.
### SyntaxError: Non-ASCII character ###
The error
File "youtube-dl", line 2
SyntaxError: Non-ASCII character '\x93' ...
means you're using an outdated version of Python. Please update to Python 2.6 or 2.7.
### What is this binary file? Where has the code gone?
Since June 2012 (#342) youtube-dl is packed as an executable zipfile, simply unzip it (might need renaming to `youtube-dl.zip` first on some systems) or clone the git repository, as laid out above. If you modify the code, you can run it by executing the `__main__.py` file. To recompile the executable, run `make youtube-dl`.
### The exe throws a *Runtime error from Visual C++*
To run the exe you need to install first the [Microsoft Visual C++ 2008 Redistributable Package](http://www.microsoft.com/en-us/download/details.aspx?id=29).
# COPYRIGHT
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.
# BUGS
Bugs and suggestions should be reported at: <https://github.com/rg3/youtube-dl/issues>
Please include:
* Your exact command line, like `youtube-dl -t "http://www.youtube.com/watch?v=uHlDtZ6Oc3s&feature=channel_video_title"`. A common mistake is not to escape the `&`. Putting URLs in quotes should solve this problem.
* If possible re-run the command with `--verbose`, and include the full output, it is really helpful to us.
* The output of `youtube-dl --version`
* The output of `python --version`
* The name and version of your Operating System ("Ubuntu 11.04 x64" or "Windows 7 x64" is usually enough).
For discussions, join us in the irc channel #youtube-dl on freenode.
+50
View File
@@ -0,0 +1,50 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>About youtube-dl</title>
<link rel="stylesheet" href="style.css" type="text/css">
</head>
<body>
<table class="heading"><tr>
<td class="title"><a href="index.html">youtube-dl</a></td>
<td class="subtitle">About the program</td>
</tr></table>
<h1>What is it?</h1>
<p><em>youtube-dl</em> is a command-line program to download videos from YouTube.com and a few <a href="supportedsites.html" style="color: blue; text-decoration: underline;">more sites</a>. It requires the <a href="http://www.python.org/">Python interpreter</a>, version 2.6, 2.7, or 3.2+, and it is not platform specific. It should work in your Unix box, in Windows or in Mac OS X. It is released to the public domain, which means you can modify it, redistribute it or use it however you like. The project is currently being developed at <a href="https://github.com/ytdl-org/youtube-dl/"><strong>GitHub</strong></a>.</p>
<h1>Authors</h1>
<p>Core team:</p>
<ul>
<li><a href="https://github.com/yan12125">Yen Chi Hsuan</a>: core developer.</li>
<li><a href="https://github.com/remitamine">Remita Amine</a>: core developer.</li>
<li><a href="https://github.com/dstftw">Sergey M.</a>: core developer, maintainer since 2016.</li>
</ul>
<p>Core team (inactive):</p>
<ul>
<li><a href="https://github.com/rg3">Ricardo Garcia Gonzalez</a>: original author, program core.</li>
<li><a href="https://github.com/phihag">Philipp Hagemeister</a>: core developer, maintainer since 2011.</li>
<li><a href="https://github.com/FiloSottile">Filippo Valsorda</a>: core developer, Windows build, testing.</li>
<li><a href="https://github.com/jaimeMF">Jaime Marquínez Ferrándiz</a>: core developer.</li>
<li><a href="https://github.com/naglis">Naglis Jonaitis</a>: core developer.</li>
<li><a href="https://github.com/pulpe">pulpe</a>: core developer.</li>
</ul>
<p>Many <a href="https://github.com/ytdl-org/youtube-dl/blob/master/AUTHORS">other people</a> contributing patches, code, ideas and kind messages. <a href="https://github.com/ytdl-org/youtube-dl/graphs/contributors">Too many</a> to be listed here. You know who you are. Thank you very much.</p>
<div class="note">
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/deed.en_US">
<img alt="Creative Commons License" style="border-width:0"
src="https://i.creativecommons.org/l/by-sa/3.0/80x15.png" /></a><br />
Copyright © 2006-2011 Ricardo Garcia Gonzalez<br />
Copyright © 2011-2019 youtube-dl developers
</div>
</body>
</html>
-6
View File
@@ -1,6 +0,0 @@
#!/usr/bin/env python
import youtube_dl
if __name__ == '__main__':
youtube_dl.main()
Binary file not shown.
Binary file not shown.
-14
View File
@@ -1,14 +0,0 @@
__youtube-dl()
{
local cur prev opts
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
opts="{{flags}}"
if [[ ${cur} == * ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
fi
}
complete -F __youtube-dl youtube-dl
-26
View File
@@ -1,26 +0,0 @@
#!/usr/bin/env python
import os
from os.path import dirname as dirn
import sys
sys.path.append(dirn(dirn((os.path.abspath(__file__)))))
import youtube_dl
BASH_COMPLETION_FILE = "youtube-dl.bash-completion"
BASH_COMPLETION_TEMPLATE = "devscripts/bash-completion.in"
def build_completion(opt_parser):
opts_flag = []
for group in opt_parser.option_groups:
for option in group.option_list:
#for every long flag
opts_flag.append(option.get_opt_string())
with open(BASH_COMPLETION_TEMPLATE) as f:
template = f.read()
with open(BASH_COMPLETION_FILE, "w") as f:
#just using the special char
filled_template = template.replace("{{flags}}", " ".join(opts_flag))
f.write(filled_template)
parser = youtube_dl.parseOpts()[0]
build_completion(parser)
-33
View File
@@ -1,33 +0,0 @@
#!/usr/bin/env python3
import json
import sys
import hashlib
import urllib.request
if len(sys.argv) <= 1:
print('Specify the version number as parameter')
sys.exit()
version = sys.argv[1]
with open('update/LATEST_VERSION', 'w') as f:
f.write(version)
versions_info = json.load(open('update/versions.json'))
if 'signature' in versions_info:
del versions_info['signature']
new_version = {}
filenames = {'bin': 'youtube-dl', 'exe': 'youtube-dl.exe', 'tar': 'youtube-dl-%s.tar.gz' % version}
for key, filename in filenames.items():
print('Downloading and checksumming %s...' %filename)
url = 'http://youtube-dl.org/downloads/%s/%s' % (version, filename)
data = urllib.request.urlopen(url).read()
sha256sum = hashlib.sha256(data).hexdigest()
new_version[key] = (url, sha256sum)
versions_info['versions'][version] = new_version
versions_info['latest'] = version
json.dump(versions_info, open('update/versions.json', 'w'), indent=4, sort_keys=True)
-32
View File
@@ -1,32 +0,0 @@
#!/usr/bin/env python3
import hashlib
import shutil
import subprocess
import tempfile
import urllib.request
import json
versions_info = json.load(open('update/versions.json'))
version = versions_info['latest']
URL = versions_info['versions'][version]['bin'][0]
data = urllib.request.urlopen(URL).read()
# Read template page
with open('download.html.in', 'r', encoding='utf-8') as tmplf:
template = tmplf.read()
md5sum = hashlib.md5(data).hexdigest()
sha1sum = hashlib.sha1(data).hexdigest()
sha256sum = hashlib.sha256(data).hexdigest()
template = template.replace('@PROGRAM_VERSION@', version)
template = template.replace('@PROGRAM_URL@', URL)
template = template.replace('@PROGRAM_MD5SUM@', md5sum)
template = template.replace('@PROGRAM_SHA1SUM@', sha1sum)
template = template.replace('@PROGRAM_SHA256SUM@', sha256sum)
template = template.replace('@EXE_URL@', versions_info['versions'][version]['exe'][0])
template = template.replace('@EXE_SHA256SUM@', versions_info['versions'][version]['exe'][1])
template = template.replace('@TAR_URL@', versions_info['versions'][version]['tar'][0])
template = template.replace('@TAR_SHA256SUM@', versions_info['versions'][version]['tar'][1])
with open('download.html', 'w', encoding='utf-8') as dlf:
dlf.write(template)
-32
View File
@@ -1,32 +0,0 @@
#!/usr/bin/env python3
import rsa
import json
from binascii import hexlify
try:
input = raw_input
except NameError:
pass
versions_info = json.load(open('update/versions.json'))
if 'signature' in versions_info:
del versions_info['signature']
print('Enter the PKCS1 private key, followed by a blank line:')
privkey = b''
while True:
try:
line = input()
except EOFError:
break
if line == '':
break
privkey += line.encode('ascii') + b'\n'
privkey = rsa.PrivateKey.load_pkcs1(privkey)
signature = hexlify(rsa.pkcs1.sign(json.dumps(versions_info, sort_keys=True).encode('utf-8'), privkey, 'SHA-256')).decode()
print('signature: ' + signature)
versions_info['signature'] = signature
json.dump(versions_info, open('update/versions.json', 'w'), indent=4, sort_keys=True)
-21
View File
@@ -1,21 +0,0 @@
#!/usr/bin/env python
# coding: utf-8
from __future__ import with_statement
import datetime
import glob
import io # For Python 2 compatibilty
import os
import re
year = str(datetime.datetime.now().year)
for fn in glob.glob('*.html*'):
with io.open(fn, encoding='utf-8') as f:
content = f.read()
newc = re.sub(u'(?P<copyright>Copyright © 2006-)(?P<year>[0-9]{4})', u'Copyright © 2006-' + year, content)
if content != newc:
tmpFn = fn + '.part'
with io.open(tmpFn, 'wt', encoding='utf-8') as outf:
outf.write(newc)
os.rename(tmpFn, fn)
-57
View File
@@ -1,57 +0,0 @@
#!/usr/bin/env python3
import datetime
import textwrap
import json
atom_template=textwrap.dedent("""\
<?xml version='1.0' encoding='utf-8'?>
<atom:feed xmlns:atom="http://www.w3.org/2005/Atom">
<atom:title>youtube-dl releases</atom:title>
<atom:id>youtube-dl-updates-feed</atom:id>
<atom:updated>@TIMESTAMP@</atom:updated>
@ENTRIES@
</atom:feed>""")
entry_template=textwrap.dedent("""
<atom:entry>
<atom:id>youtube-dl-@VERSION@</atom:id>
<atom:title>New version @VERSION@</atom:title>
<atom:link href="http://rg3.github.io/youtube-dl" />
<atom:content type="xhtml">
<div xmlns="http://www.w3.org/1999/xhtml">
Downloads available at <a href="http://youtube-dl.org/downloads/@VERSION@/">http://youtube-dl.org/downloads/@VERSION@/</a>
</div>
</atom:content>
<atom:author>
<atom:name>The youtube-dl maintainers</atom:name>
</atom:author>
<atom:updated>@TIMESTAMP@</atom:updated>
</atom:entry>
""")
now = datetime.datetime.now()
now_iso = now.isoformat()
atom_template = atom_template.replace('@TIMESTAMP@',now_iso)
entries=[]
versions_info = json.load(open('update/versions.json'))
versions = list(versions_info['versions'].keys())
versions.sort()
for v in versions:
entry = entry_template.replace('@TIMESTAMP@',v.replace('.','-'))
entry = entry.replace('@VERSION@',v)
entries.append(entry)
entries_str = textwrap.indent(''.join(entries), '\t')
atom_template = atom_template.replace('@ENTRIES@', entries_str)
with open('update/releases.atom','w',encoding='utf-8') as atom_file:
atom_file.write(atom_template)
-20
View File
@@ -1,20 +0,0 @@
import sys
import re
README_FILE = 'README.md'
helptext = sys.stdin.read()
with open(README_FILE) as f:
oldreadme = f.read()
header = oldreadme[:oldreadme.index('# OPTIONS')]
footer = oldreadme[oldreadme.index('# CONFIGURATION'):]
options = helptext[helptext.index(' General Options:')+19:]
options = re.sub(r'^ (\w.+)$', r'## \1', options, flags=re.M)
options = '# OPTIONS\n' + options + '\n'
with open(README_FILE, 'w') as f:
f.write(header)
f.write(options)
f.write(footer)
-6
View File
@@ -1,6 +0,0 @@
# source this file in your shell to get a POSIX locale (which will break many programs, but that's kind of the point)
export LC_ALL=POSIX
export LANG=POSIX
export LANGUAGE=POSIX
-102
View File
@@ -1,102 +0,0 @@
#!/bin/bash
# IMPORTANT: the following assumptions are made
# * the GH repo is on the origin remote
# * the gh-pages branch is named so locally
# * the git config user.signingkey is properly set
# You will need
# pip install coverage nose rsa
# TODO
# release notes
# make hash on local files
set -e
skip_tests=false
if [ "$1" = '--skip-test' ]; then
skip_tests=true
shift
fi
if [ -z "$1" ]; then echo "ERROR: specify version number like this: $0 1994.09.06"; exit 1; fi
version="$1"
if [ ! -z "`git tag | grep "$version"`" ]; then echo 'ERROR: version already present'; exit 1; fi
if [ ! -z "`git status --porcelain | grep -v CHANGELOG`" ]; then echo 'ERROR: the working directory is not clean; commit or stash changes'; exit 1; fi
if [ ! -f "updates_key.pem" ]; then echo 'ERROR: updates_key.pem missing'; exit 1; fi
/bin/echo -e "\n### First of all, testing..."
make cleanall
if $skip_tests ; then
echo 'SKIPPING TESTS'
else
nosetests --verbose --with-coverage --cover-package=youtube_dl --cover-html test --stop || exit 1
fi
/bin/echo -e "\n### Changing version in version.py..."
sed -i "s/__version__ = '.*'/__version__ = '$version'/" youtube_dl/version.py
/bin/echo -e "\n### Committing CHANGELOG README.md and youtube_dl/version.py..."
make README.md
git add CHANGELOG README.md youtube_dl/version.py
git commit -m "release $version"
/bin/echo -e "\n### Now tagging, signing and pushing..."
git tag -s -m "Release $version" "$version"
git show "$version"
read -p "Is it good, can I push? (y/n) " -n 1
if [[ ! $REPLY =~ ^[Yy]$ ]]; then exit 1; fi
echo
MASTER=$(git rev-parse --abbrev-ref HEAD)
git push origin $MASTER:master
git push origin "$version"
/bin/echo -e "\n### OK, now it is time to build the binaries..."
REV=$(git rev-parse HEAD)
make youtube-dl youtube-dl.tar.gz
wget "http://jeromelaheurte.net:8142/download/rg3/youtube-dl/youtube-dl.exe?rev=$REV" -O youtube-dl.exe || \
wget "http://jeromelaheurte.net:8142/build/rg3/youtube-dl/youtube-dl.exe?rev=$REV" -O youtube-dl.exe
mkdir -p "build/$version"
mv youtube-dl youtube-dl.exe "build/$version"
mv youtube-dl.tar.gz "build/$version/youtube-dl-$version.tar.gz"
RELEASE_FILES="youtube-dl youtube-dl.exe youtube-dl-$version.tar.gz"
(cd build/$version/ && md5sum $RELEASE_FILES > MD5SUMS)
(cd build/$version/ && sha1sum $RELEASE_FILES > SHA1SUMS)
(cd build/$version/ && sha256sum $RELEASE_FILES > SHA2-256SUMS)
(cd build/$version/ && sha512sum $RELEASE_FILES > SHA2-512SUMS)
git checkout HEAD -- youtube-dl youtube-dl.exe
/bin/echo -e "\n### Signing and uploading the new binaries to youtube-dl.org..."
for f in $RELEASE_FILES; do gpg --detach-sig "build/$version/$f"; done
scp -r "build/$version" ytdl@youtube-dl.org:html/downloads/
/bin/echo -e "\n### Now switching to gh-pages..."
git clone --branch gh-pages --single-branch . build/gh-pages
ROOT=$(pwd)
(
set -e
ORIGIN_URL=$(git config --get remote.origin.url)
cd build/gh-pages
"$ROOT/devscripts/gh-pages/add-version.py" $version
"$ROOT/devscripts/gh-pages/update-feed.py"
"$ROOT/devscripts/gh-pages/sign-versions.py" < "$ROOT/updates_key.pem"
"$ROOT/devscripts/gh-pages/generate-download.py"
"$ROOT/devscripts/gh-pages/update-copyright.py"
git add *.html *.html.in update
git commit -m "release $version"
git show HEAD
read -p "Is it good, can I push? (y/n) " -n 1
if [[ ! $REPLY =~ ^[Yy]$ ]]; then exit 1; fi
echo
git push "$ROOT" gh-pages
git push "$ORIGIN_URL" gh-pages
)
rm -rf build
make pypi-files
echo "Uploading to PyPi ..."
python setup.py sdist upload
make clean
/bin/echo -e "\n### DONE!"
-40
View File
@@ -1,40 +0,0 @@
#!/usr/bin/env python
import sys, os
try:
import urllib.request as compat_urllib_request
except ImportError: # Python 2
import urllib2 as compat_urllib_request
sys.stderr.write(u'Hi! We changed distribution method and now youtube-dl needs to update itself one more time.\n')
sys.stderr.write(u'This will only happen once. Simply press enter to go on. Sorry for the trouble!\n')
sys.stderr.write(u'The new location of the binaries is https://github.com/rg3/youtube-dl/downloads, not the git repository.\n\n')
try:
raw_input()
except NameError: # Python 3
input()
filename = sys.argv[0]
API_URL = "https://api.github.com/repos/rg3/youtube-dl/downloads"
BIN_URL = "https://github.com/downloads/rg3/youtube-dl/youtube-dl"
if not os.access(filename, os.W_OK):
sys.exit('ERROR: no write permissions on %s' % filename)
try:
urlh = compat_urllib_request.urlopen(BIN_URL)
newcontent = urlh.read()
urlh.close()
except (IOError, OSError) as err:
sys.exit('ERROR: unable to download latest version')
try:
with open(filename, 'wb') as outf:
outf.write(newcontent)
except (IOError, OSError) as err:
sys.exit('ERROR: unable to overwrite current version')
sys.stderr.write(u'Done! Now you can run youtube-dl.\n')
-12
View File
@@ -1,12 +0,0 @@
from distutils.core import setup
import py2exe
py2exe_options = {
"bundle_files": 1,
"compressed": 1,
"optimize": 2,
"dist_dir": '.',
"dll_excludes": ['w9xpopen.exe']
}
setup(console=['youtube-dl.py'], options={ "py2exe": py2exe_options }, zipfile=None)
@@ -1,102 +0,0 @@
#!/usr/bin/env python
import sys, os
import urllib2
import json, hashlib
def rsa_verify(message, signature, key):
from struct import pack
from hashlib import sha256
from sys import version_info
def b(x):
if version_info[0] == 2: return x
else: return x.encode('latin1')
assert(type(message) == type(b('')))
block_size = 0
n = key[0]
while n:
block_size += 1
n >>= 8
signature = pow(int(signature, 16), key[1], key[0])
raw_bytes = []
while signature:
raw_bytes.insert(0, pack("B", signature & 0xFF))
signature >>= 8
signature = (block_size - len(raw_bytes)) * b('\x00') + b('').join(raw_bytes)
if signature[0:2] != b('\x00\x01'): return False
signature = signature[2:]
if not b('\x00') in signature: return False
signature = signature[signature.index(b('\x00'))+1:]
if not signature.startswith(b('\x30\x31\x30\x0D\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20')): return False
signature = signature[19:]
if signature != sha256(message).digest(): return False
return True
sys.stderr.write(u'Hi! We changed distribution method and now youtube-dl needs to update itself one more time.\n')
sys.stderr.write(u'This will only happen once. Simply press enter to go on. Sorry for the trouble!\n')
sys.stderr.write(u'From now on, get the binaries from http://rg3.github.com/youtube-dl/download.html, not from the git repository.\n\n')
raw_input()
filename = sys.argv[0]
UPDATE_URL = "http://rg3.github.io/youtube-dl/update/"
VERSION_URL = UPDATE_URL + 'LATEST_VERSION'
JSON_URL = UPDATE_URL + 'versions.json'
UPDATES_RSA_KEY = (0x9d60ee4d8f805312fdb15a62f87b95bd66177b91df176765d13514a0f1754bcd2057295c5b6f1d35daa6742c3ffc9a82d3e118861c207995a8031e151d863c9927e304576bc80692bc8e094896fcf11b66f3e29e04e3a71e9a11558558acea1840aec37fc396fb6b65dc81a1c4144e03bd1c011de62e3f1357b327d08426fe93, 65537)
if not os.access(filename, os.W_OK):
sys.exit('ERROR: no write permissions on %s' % filename)
exe = os.path.abspath(filename)
directory = os.path.dirname(exe)
if not os.access(directory, os.W_OK):
sys.exit('ERROR: no write permissions on %s' % directory)
try:
versions_info = urllib2.urlopen(JSON_URL).read().decode('utf-8')
versions_info = json.loads(versions_info)
except:
sys.exit(u'ERROR: can\'t obtain versions info. Please try again later.')
if not 'signature' in versions_info:
sys.exit(u'ERROR: the versions file is not signed or corrupted. Aborting.')
signature = versions_info['signature']
del versions_info['signature']
if not rsa_verify(json.dumps(versions_info, sort_keys=True), signature, UPDATES_RSA_KEY):
sys.exit(u'ERROR: the versions file signature is invalid. Aborting.')
version = versions_info['versions'][versions_info['latest']]
try:
urlh = urllib2.urlopen(version['exe'][0])
newcontent = urlh.read()
urlh.close()
except (IOError, OSError) as err:
sys.exit('ERROR: unable to download latest version')
newcontent_hash = hashlib.sha256(newcontent).hexdigest()
if newcontent_hash != version['exe'][1]:
sys.exit(u'ERROR: the downloaded file hash does not match. Aborting.')
try:
with open(exe + '.new', 'wb') as outf:
outf.write(newcontent)
except (IOError, OSError) as err:
sys.exit(u'ERROR: unable to write the new version')
try:
bat = os.path.join(directory, 'youtube-dl-updater.bat')
b = open(bat, 'w')
b.write("""
echo Updating youtube-dl...
ping 127.0.0.1 -n 5 -w 1000 > NUL
move /Y "%s.new" "%s"
del "%s"
\n""" %(exe, exe, bat))
b.close()
os.startfile(bat)
except (IOError, OSError) as err:
sys.exit('ERROR: unable to overwrite current version')
sys.stderr.write(u'Done! Now you can run youtube-dl.\n')
-56
View File
@@ -1,56 +0,0 @@
#!/bin/bash
# Run with as parameter a setup.py that works in the current directory
# e.g. no os.chdir()
# It will run twice, the first time will crash
set -e
SCRIPT_DIR="$( cd "$( dirname "$0" )" && pwd )"
if [ ! -d wine-py2exe ]; then
sudo apt-get install wine1.3 axel bsdiff
mkdir wine-py2exe
cd wine-py2exe
export WINEPREFIX=`pwd`
axel -a "http://www.python.org/ftp/python/2.7/python-2.7.msi"
axel -a "http://downloads.sourceforge.net/project/py2exe/py2exe/0.6.9/py2exe-0.6.9.win32-py2.7.exe"
#axel -a "http://winetricks.org/winetricks"
# http://appdb.winehq.org/objectManager.php?sClass=version&iId=21957
echo "Follow python setup on screen"
wine msiexec /i python-2.7.msi
echo "Follow py2exe setup on screen"
wine py2exe-0.6.9.win32-py2.7.exe
#echo "Follow Microsoft Visual C++ 2008 Redistributable Package setup on screen"
#bash winetricks vcrun2008
rm py2exe-0.6.9.win32-py2.7.exe
rm python-2.7.msi
#rm winetricks
# http://bugs.winehq.org/show_bug.cgi?id=3591
mv drive_c/Python27/Lib/site-packages/py2exe/run.exe drive_c/Python27/Lib/site-packages/py2exe/run.exe.backup
bspatch drive_c/Python27/Lib/site-packages/py2exe/run.exe.backup drive_c/Python27/Lib/site-packages/py2exe/run.exe "$SCRIPT_DIR/SizeOfImage.patch"
mv drive_c/Python27/Lib/site-packages/py2exe/run_w.exe drive_c/Python27/Lib/site-packages/py2exe/run_w.exe.backup
bspatch drive_c/Python27/Lib/site-packages/py2exe/run_w.exe.backup drive_c/Python27/Lib/site-packages/py2exe/run_w.exe "$SCRIPT_DIR/SizeOfImage_w.patch"
cd -
else
export WINEPREFIX="$( cd wine-py2exe && pwd )"
fi
wine "C:\\Python27\\python.exe" "$1" py2exe > "py2exe.log" 2>&1 || true
echo '# Copying python27.dll' >> "py2exe.log"
cp "$WINEPREFIX/drive_c/windows/system32/python27.dll" build/bdist.win32/winexe/bundle-2.7/
wine "C:\\Python27\\python.exe" "$1" py2exe >> "py2exe.log" 2>&1
+7
View File
@@ -0,0 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<meta http-equiv="refresh" content="0; url=https://github.com/ytdl-org/youtube-dl/blob/master/README.md#readme">
</head>
</html>
+43
View File
@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>youtube-dl: Donations</title>
<link rel="stylesheet" href="style.css" type="text/css">
</head>
<body>
<table class="heading"><tr>
<td class="title"><a href="index.html">youtube-dl</a></td>
<td class="subtitle">Donations</td>
</tr></table>
<p>You can donate via Bitcoin at <a href="bitcoin:17EEDY6MuqLQ3jrTeyYaaUXbc6dFaZEYw7">17EEDY6MuqLQ3jrTeyYaaUXbc6dFaZEYw7</a> .</p>
<p>You can donate via PayPal here:</p>
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
<input type="hidden" name="cmd" value="_s-xclick">
<input type="hidden" name="hosted_button_id" value="QC9DQRCVET9NQ">
<input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
</form>
<p>We also appreciate wire transfers to our <a href="https://uberspace.de/">hosting provider</a>:</p>
<table>
<tr><th>account holder</th><td>Jonas Pasche</td></tr>
<tr><th>account number</th><td>200003978 (IBAN: DE35550501200200003978)</td></tr>
<tr><th>bank</th><td>Sparkasse Mainz</td></tr>
<tr><th>(German) bank code</th><td>55050120 (BIC: MALADE51MNZ)</td></tr>
<tr><th>Note (important!)</th><td>Uberspace ytdl</td></tr>
</table>
<div class="note">
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/deed.en_US">
<img alt="Creative Commons License" style="border-width:0"
src="https://i.creativecommons.org/l/by-sa/3.0/80x15.png" /></a><br />
Copyright © 2006-2011 Ricardo Garcia Gonzalez<br />
Copyright © 2011-2019 youtube-dl developers
</div>
</body>
</html>
+77
View File
@@ -0,0 +1,77 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>youtube-dl: Download Page</title>
<link rel="stylesheet" href="style.css" type="text/css">
</head>
<body>
<table class="heading"><tr>
<td class="title"><a href="index.html">youtube-dl</a></td>
<td class="subtitle">Download Page</td>
</tr></table>
<p>Remember <em>youtube-dl</em> requires <a href="http://www.python.org/">Python</a> version 2.6, 2.7, or 3.2+ to work except for Windows exe.</p>
<p><a href="https://yt-dl.org/downloads/2019.03.09/youtube-dl.exe">Windows exe</a> requires <a href="https://www.microsoft.com/en-US/download/details.aspx?id=5555">Microsoft Visual C++ 2010 Redistributable Package (x86)</a> and does not require Python that is already embedded into the binary.</p>
<h2><a href="https://yt-dl.org/downloads/2019.03.09/youtube-dl">2019.03.09</a> (<a href="https://yt-dl.org/downloads/2019.03.09/youtube-dl.sig">sig</a>)</h2>
<p><strong>SHA256</strong>: 2ced5ab54ffe28586121e57af7cb2e3b38ef596d9932479407040ed43a7f9f7c</p>
<p>
<a href="https://yt-dl.org/downloads/2019.03.09/youtube-dl.exe">Windows exe</a> (<a href="https://yt-dl.org/downloads/2019.03.09/youtube-dl.exe.sig">sig</a> - SHA256 f6e4884e5cebf0cb90b7e45075ead3faf6b22a3c14b292ab5b9c5acf2d5c18be)<br>
<a href="https://yt-dl.org/downloads/2019.03.09/youtube-dl-2019.03.09.tar.gz">Full source + docs + binary tarball</a> (<a href="https://yt-dl.org/downloads/2019.03.09/youtube-dl-2019.03.09.tar.gz.sig">sig</a> - SHA256 0ebedd327392ef42c18cadfec50c450c0320c7abdc3cd04d162b8cfc6bae86bc)
</p>
<p>To install it right away for all UNIX users (Linux, OS X, etc.), type:
<code class="commands">sudo curl -L https://yt-dl.org/downloads/latest/youtube-dl -o /usr/local/bin/youtube-dl<br/>
sudo chmod a+rx /usr/local/bin/youtube-dl</code>
</p>
<p>If you do not have curl, you can alternatively use a recent wget:
<code class="commands">sudo wget https://yt-dl.org/downloads/latest/youtube-dl -O /usr/local/bin/youtube-dl<br/>
sudo chmod a+rx /usr/local/bin/youtube-dl</code>
</p>
<p>You can also use pip:
<code class="commands">sudo pip install --upgrade youtube_dl</code>
</p>
<p>
This command will update youtube-dl if you have already installed it.
See the <a href="https://pypi.python.org/pypi/youtube_dl">pypi page</a> for more information.
</p>
<p>You can use Homebrew if you have it:
<code class="commands">brew install youtube-dl</code>
</p>
<p>To check the signature, type:
<code class="commands">sudo wget https://yt-dl.org/downloads/latest/youtube-dl.sig -O youtube-dl.sig<br/>gpg --verify youtube-dl.sig /usr/local/bin/youtube-dl<br/>rm youtube-dl.sig</code>
</p>
<p>The following GPG keys will be used to sign the binaries and the git tags:</p>
<ul>
<li>Philipp Hagemeister <code class="fingerprint">7D33 D762 FD6C 3513 0481 347F DB4B 54CB A482 6A18</code></li>
<li>Sergey M. <code class="fingerprint">ED7F 5BF4 6B3B BED8 1C87 368E 2C39 3E0F 18A9 236D</code></li>
</ul>
<p>Older releases are also signed with one of:
<ul>
<li>Philipp Hagemeister <code class="fingerprint">0600 E1DB 6FB5 3A5D 95D8 FC0D F5EA B582 FAFB 085C</code> (until 2013-06-01)</li>
</li>
<li>Filippo Valsorda <code class="fingerprint">428D F5D6 3EF0 7494 BB45 5AC0 EBF0 1804 BCF0 5F6B</code> (until 2014)</li>
</p>
<div class="note">
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/deed.en_US">
<img alt="Creative Commons License" style="border-width:0"
src="https://i.creativecommons.org/l/by-sa/3.0/80x15.png" /></a><br />
Copyright © 2006-2011 Ricardo Garcia Gonzalez<br />
Copyright © 2011-2019 youtube-dl developers
</div>
</body>
</html>
+77
View File
@@ -0,0 +1,77 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>youtube-dl: Download Page</title>
<link rel="stylesheet" href="style.css" type="text/css">
</head>
<body>
<table class="heading"><tr>
<td class="title"><a href="index.html">youtube-dl</a></td>
<td class="subtitle">Download Page</td>
</tr></table>
<p>Remember <em>youtube-dl</em> requires <a href="http://www.python.org/">Python</a> version 2.6, 2.7, or 3.2+ to work except for Windows exe.</p>
<p><a href="@EXE_URL@">Windows exe</a> requires <a href="https://www.microsoft.com/en-US/download/details.aspx?id=5555">Microsoft Visual C++ 2010 Redistributable Package (x86)</a> and does not require Python that is already embedded into the binary.</p>
<h2><a href="@PROGRAM_URL@">@PROGRAM_VERSION@</a> (<a href="@PROGRAM_URL@.sig">sig</a>)</h2>
<p><strong>SHA256</strong>: @PROGRAM_SHA256SUM@</p>
<p>
<a href="@EXE_URL@">Windows exe</a> (<a href="@EXE_URL@.sig">sig</a> - SHA256 @EXE_SHA256SUM@)<br>
<a href="@TAR_URL@">Full source + docs + binary tarball</a> (<a href="@TAR_URL@.sig">sig</a> - SHA256 @TAR_SHA256SUM@)
</p>
<p>To install it right away for all UNIX users (Linux, OS X, etc.), type:
<code class="commands">sudo curl -L https://yt-dl.org/downloads/latest/youtube-dl -o /usr/local/bin/youtube-dl<br/>
sudo chmod a+rx /usr/local/bin/youtube-dl</code>
</p>
<p>If you do not have curl, you can alternatively use a recent wget:
<code class="commands">sudo wget https://yt-dl.org/downloads/latest/youtube-dl -O /usr/local/bin/youtube-dl<br/>
sudo chmod a+rx /usr/local/bin/youtube-dl</code>
</p>
<p>You can also use pip:
<code class="commands">sudo pip install --upgrade youtube_dl</code>
</p>
<p>
This command will update youtube-dl if you have already installed it.
See the <a href="https://pypi.python.org/pypi/youtube_dl">pypi page</a> for more information.
</p>
<p>You can use Homebrew if you have it:
<code class="commands">brew install youtube-dl</code>
</p>
<p>To check the signature, type:
<code class="commands">sudo wget https://yt-dl.org/downloads/latest/youtube-dl.sig -O youtube-dl.sig<br/>gpg --verify youtube-dl.sig /usr/local/bin/youtube-dl<br/>rm youtube-dl.sig</code>
</p>
<p>The following GPG keys will be used to sign the binaries and the git tags:</p>
<ul>
<li>Philipp Hagemeister <code class="fingerprint">7D33 D762 FD6C 3513 0481 347F DB4B 54CB A482 6A18</code></li>
<li>Sergey M. <code class="fingerprint">ED7F 5BF4 6B3B BED8 1C87 368E 2C39 3E0F 18A9 236D</code></li>
</ul>
<p>Older releases are also signed with one of:
<ul>
<li>Philipp Hagemeister <code class="fingerprint">0600 E1DB 6FB5 3A5D 95D8 FC0D F5EA B582 FAFB 085C</code> (until 2013-06-01)</li>
</li>
<li>Filippo Valsorda <code class="fingerprint">428D F5D6 3EF0 7494 BB45 5AC0 EBF0 1804 BCF0 5F6B</code> (until 2014)</li>
</p>
<div class="note">
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/deed.en_US">
<img alt="Creative Commons License" style="border-width:0"
src="https://i.creativecommons.org/l/by-sa/3.0/80x15.png" /></a><br />
Copyright © 2006-2011 Ricardo Garcia Gonzalez<br />
Copyright © 2011-2019 youtube-dl developers
</div>
</body>
</html>
+7
View File
@@ -0,0 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<meta http-equiv="refresh" content="0; url=https://github.com/ytdl-org/youtube-dl/blob/master/README.md#faq">
</head>
</html>
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 435 B

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 356 B

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 425 B

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 331 B

+39
View File
@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
<title>youtube-dl</title>
<link rel="stylesheet" href="style.css" type="text/css" />
<link rel="alternate" type="application/atom+xml" title="youtube-dl releases" href="update/releases.atom" />
</head>
<body>
<table class="heading"><tr>
<td class="title"><a href="index.html">youtube-dl</a></td>
<td class="subtitle">Download videos from YouTube
(and <a href="supportedsites.html" style="color: blue; text-decoration: underline;">more sites</a>)</td>
</tr></table>
<p><em>youtube-dl</em> is a command-line program to download videos from YouTube.com and a few <a href="supportedsites.html" style="color: blue; text-decoration: underline;">more sites</a>. It requires the <a href="http://www.python.org/">Python interpreter</a> (2.6, 2.7, or 3.2+), and it is not platform specific. We also provide a <a href="https://yt-dl.org/latest/youtube-dl.exe">Windows executable</a> that includes Python. youtube-dl should work in your Unix box, in Windows or in Mac OS X. It is released to the public domain, which means you can modify it, redistribute it or use it however you like.</p>
<table border="0" id="rgb">
<tr><td><a class="button" id="r" href="https://github.com/ytdl-org/youtube-dl/blob/master/README.md#readme">Documentation</a></td></tr>
<tr><td><a class="button" id="g" href="download.html">Download</a></td></tr>
<tr><td><a class="button" id="main-support" href="https://github.com/ytdl-org/youtube-dl/issues/new">Support</a></td></tr>
<tr><td><a class="button" id="y" href="https://github.com/ytdl-org/youtube-dl/">Develop</a></td></tr>
<tr><td><a class="button" id="b" href="about.html">About</a></td></tr>
</table>
<p>You can also contact us on the irc channel <a href="irc://chat.freenode.net/#youtube-dl">#youtube-dl</a> (<a href="http://webchat.freenode.net/?randomnick=1&amp;channels=youtube-dl">webchat</a>) on freenode.</p>
<p>If you like this project, you may donate <a href="donations.html">here</a>.</p>
<div class="note">
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/deed.en_US">
<img alt="Creative Commons License" style="border-width:0"
src="https://i.creativecommons.org/l/by-sa/3.0/80x15.png" /></a><br />
Copyright © 2006-2011 Ricardo Garcia Gonzalez<br />
Copyright © 2011-2019 youtube-dl developers
</div>
</body>
</html>
-79
View File
@@ -1,79 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function
import pkg_resources
import sys
try:
from setuptools import setup
except ImportError:
from distutils.core import setup
try:
import py2exe
"""This will create an exe that needs Microsoft Visual C++ 2008 Redistributable Package"""
except ImportError:
if len(sys.argv) >= 2 and sys.argv[1] == 'py2exe':
print("Cannot import py2exe", file=sys.stderr)
exit(1)
py2exe_options = {
"bundle_files": 1,
"compressed": 1,
"optimize": 2,
"dist_dir": '.',
"dll_excludes": ['w9xpopen.exe'],
}
py2exe_console = [{
"script": "./youtube_dl/__main__.py",
"dest_base": "youtube-dl",
}]
py2exe_params = {
'console': py2exe_console,
'options': { "py2exe": py2exe_options },
'zipfile': None
}
if len(sys.argv) >= 2 and sys.argv[1] == 'py2exe':
params = py2exe_params
else:
params = {
'scripts': ['bin/youtube-dl'],
'data_files': [('etc/bash_completion.d', ['youtube-dl.bash-completion']), # Installing system-wide would require sudo...
('share/doc/youtube_dl', ['README.txt']),
('share/man/man1/', ['youtube-dl.1'])]
}
# Get the version from youtube_dl/version.py without importing the package
exec(compile(open('youtube_dl/version.py').read(), 'youtube_dl/version.py', 'exec'))
setup(
name = 'youtube_dl',
version = __version__,
description = 'YouTube video downloader',
long_description = 'Small command-line program to download videos from YouTube.com and other video sites.',
url = 'https://github.com/rg3/youtube-dl',
author = 'Ricardo Garcia',
maintainer = 'Philipp Hagemeister',
maintainer_email = 'phihag@phihag.de',
packages = ['youtube_dl', 'youtube_dl.extractor'],
# Provokes warning on most systems (why?!)
#test_suite = 'nose.collector',
#test_requires = ['nosetest'],
classifiers = [
"Topic :: Multimedia :: Video",
"Development Status :: 5 - Production/Stable",
"Environment :: Console",
"License :: Public Domain",
"Programming Language :: Python :: 2.6",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.3"
],
**params
)
+156
View File
@@ -0,0 +1,156 @@
body {
font-family: sans-serif;
margin-left: 10%;
margin-right: 10%;
margin-top: 2ex;
margin-bottom: 3ex;
background-color: white;
color: black;
/*background-color: #fff1db;*/
background-color: white;
/*
background-image: url("gradient.png");
background-repeat: repeat-x;
*/
/*
background-image: url("gradient2.png");
background-repeat: repeat-y;
*/
/*
background-image: url("gradient3.png");
background-repeat: repeat-x;
*/
/*
background-image: url("gradient4.png");
background-repeat: repeat-y;
*/
background-image: url("gradient5.png");
background-repeat: repeat-x;
}
.heading {
border: 0;
color: black;
font-size: xx-large;
font-weight: bold;
padding-bottom: 1ex;
border-bottom: 1px solid black;
margin-bottom: 2ex;
width: 100%;
}
.heading tr {
border: 0;
}
.heading td {
border: 0;
}
.heading a {
text-decoration: none;
color: black;
}
.title {
text-align: left;
}
.subtitle {
text-align: right;
}
.toc {
padding-left: 2ex;
border: 1px solid #aaaaaa;
background-color: white;
padding-bottom: 1ex;
border-radius: 10px;
-moz-border-radius: 10px;
}
.toc ul {
margin: 0; list-style-type: none;
}
hr {
margin-top: 3ex;
margin-bottom: 3ex;
width: 50%;
}
.note {
margin-top: 10ex;
text-align: center;
font-size: x-small;
}
h1 {
font-size: x-large;
margin-top: 2ex;
color: black;
margin-left: 2%;
margin-right: 2%;
}
h2 {
font-size: large;
margin-left: 5%;
margin-right: 5%;
}
p {
margin-left: 5%;
margin-right: 5%;
}
ul {
margin-left: 5%;
margin-right: 5%;
}
li {
margin-left: 3%;
margin-top: 0.5ex;
margin-bottom: 0.5ex;
}
tt {
padding-left: 0.5ex;
padding-right: 0.5ex;
background: #dddddd;
}
#rgb {
width: 33%;
margin: 3ex auto;
}
.button {
color: white;
font-weight: bold;
font-size: x-large;
text-decoration: none;
text-align: center;
display: block;
padding: 2ex;
border-radius: 10px;
-moz-border-radius: 10px;
}
#r {
background-color: #884444;
border: 2px solid #880000;
}
#g {
background-color: #448844;
border: 2px solid #006600;
}
#b {
background-color: #444488;
border: 2px solid #000088;
}
#y {
background-color: #888844;
border: 2px solid #666600;
}
#main-support {
background-color: #448888;
border: 2px solid #008888;
}
code.commands {
display:block;
margin-top: 0.4em;
padding: 0.7em;
background: #ccc;
background: rgba(200, 200, 200, 0.4);
white-space: pre;
}
code.fingerprint {
font-weight: bold;
font-size: 140%;
}
+1191
View File
File diff suppressed because it is too large Load Diff
+28
View File
@@ -0,0 +1,28 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>youtube-dl: Supported sites</title>
<link rel="stylesheet" href="style.css" type="text/css">
</head>
<body>
<table class="heading"><tr>
<td class="title"><a href="index.html">youtube-dl</a></td>
<td class="subtitle">Supported sites</td>
</tr></table>
<p>Here's is the list of all the supported sites, ordered alphabetically:</p>
<ul>
@SITES@
</ul>
<div class="note">
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/deed.en_US">
<img alt="Creative Commons License" style="border-width:0"
src="https://i.creativecommons.org/l/by-sa/3.0/80x15.png" /></a><br />
Copyright © 2006-2011 Ricardo Garcia Gonzalez<br />
Copyright © 2011-2019 youtube-dl developers
</div>
</body>
</html>
-44
View File
@@ -1,44 +0,0 @@
{
"consoletitle": false,
"continuedl": true,
"forcedescription": false,
"forcefilename": false,
"forceformat": false,
"forcethumbnail": false,
"forcetitle": false,
"forceurl": false,
"format": null,
"format_limit": null,
"ignoreerrors": false,
"listformats": null,
"logtostderr": false,
"matchtitle": null,
"max_downloads": null,
"nooverwrites": false,
"nopart": false,
"noprogress": false,
"outtmpl": "%(id)s.%(ext)s",
"password": null,
"playlistend": -1,
"playliststart": 1,
"prefer_free_formats": false,
"quiet": false,
"ratelimit": null,
"rejecttitle": null,
"retries": 10,
"simulate": false,
"skip_download": false,
"subtitleslang": null,
"subtitlesformat": "srt",
"test": true,
"updatetime": true,
"usenetrc": false,
"username": null,
"verbose": true,
"writedescription": false,
"writeinfojson": true,
"writesubtitles": false,
"onlysubtitles": false,
"allsubtitles": false,
"listssubtitles": false
}
-54
View File
@@ -1,54 +0,0 @@
#!/usr/bin/env python
import sys
import unittest
# Allow direct execution
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from youtube_dl.extractor import YoutubeIE, YoutubePlaylistIE, YoutubeChannelIE, JustinTVIE
class TestAllURLsMatching(unittest.TestCase):
def test_youtube_playlist_matching(self):
self.assertTrue(YoutubePlaylistIE.suitable(u'ECUl4u3cNGP61MdtwGTqZA0MreSaDybji8'))
self.assertTrue(YoutubePlaylistIE.suitable(u'UUBABnxM4Ar9ten8Mdjj1j0Q')) #585
self.assertTrue(YoutubePlaylistIE.suitable(u'PL63F0C78739B09958'))
self.assertTrue(YoutubePlaylistIE.suitable(u'https://www.youtube.com/playlist?list=UUBABnxM4Ar9ten8Mdjj1j0Q'))
self.assertTrue(YoutubePlaylistIE.suitable(u'https://www.youtube.com/course?list=ECUl4u3cNGP61MdtwGTqZA0MreSaDybji8'))
self.assertTrue(YoutubePlaylistIE.suitable(u'https://www.youtube.com/playlist?list=PLwP_SiAcdui0KVebT0mU9Apz359a4ubsC'))
self.assertTrue(YoutubePlaylistIE.suitable(u'https://www.youtube.com/watch?v=AV6J6_AeFEQ&playnext=1&list=PL4023E734DA416012')) #668
self.assertFalse(YoutubePlaylistIE.suitable(u'PLtS2H6bU1M'))
def test_youtube_matching(self):
self.assertTrue(YoutubeIE.suitable(u'PLtS2H6bU1M'))
self.assertFalse(YoutubeIE.suitable(u'https://www.youtube.com/watch?v=AV6J6_AeFEQ&playnext=1&list=PL4023E734DA416012')) #668
def test_youtube_channel_matching(self):
self.assertTrue(YoutubeChannelIE.suitable('https://www.youtube.com/channel/HCtnHdj3df7iM'))
self.assertTrue(YoutubeChannelIE.suitable('https://www.youtube.com/channel/HCtnHdj3df7iM?feature=gb_ch_rec'))
self.assertTrue(YoutubeChannelIE.suitable('https://www.youtube.com/channel/HCtnHdj3df7iM/videos'))
def test_justin_tv_channelid_matching(self):
self.assertTrue(JustinTVIE.suitable(u"justin.tv/vanillatv"))
self.assertTrue(JustinTVIE.suitable(u"twitch.tv/vanillatv"))
self.assertTrue(JustinTVIE.suitable(u"www.justin.tv/vanillatv"))
self.assertTrue(JustinTVIE.suitable(u"www.twitch.tv/vanillatv"))
self.assertTrue(JustinTVIE.suitable(u"http://www.justin.tv/vanillatv"))
self.assertTrue(JustinTVIE.suitable(u"http://www.twitch.tv/vanillatv"))
self.assertTrue(JustinTVIE.suitable(u"http://www.justin.tv/vanillatv/"))
self.assertTrue(JustinTVIE.suitable(u"http://www.twitch.tv/vanillatv/"))
def test_justintv_videoid_matching(self):
self.assertTrue(JustinTVIE.suitable(u"http://www.twitch.tv/vanillatv/b/328087483"))
def test_justin_tv_chapterid_matching(self):
self.assertTrue(JustinTVIE.suitable(u"http://www.twitch.tv/tsm_theoddone/c/2349361"))
def test_youtube_extract(self):
self.assertEqual(YoutubeIE()._extract_id('http://www.youtube.com/watch?&v=BaW_jenozKc'), 'BaW_jenozKc')
self.assertEqual(YoutubeIE()._extract_id('https://www.youtube.com/watch?&v=BaW_jenozKc'), 'BaW_jenozKc')
self.assertEqual(YoutubeIE()._extract_id('https://www.youtube.com/watch?feature=player_embedded&v=BaW_jenozKc'), 'BaW_jenozKc')
if __name__ == '__main__':
unittest.main()
-164
View File
@@ -1,164 +0,0 @@
#!/usr/bin/env python
import errno
import hashlib
import io
import os
import json
import unittest
import sys
import socket
import binascii
# Allow direct execution
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import youtube_dl.YoutubeDL
import youtube_dl.extractor
from youtube_dl.utils import *
DEF_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'tests.json')
PARAMETERS_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "parameters.json")
RETRIES = 3
# General configuration (from __init__, not very elegant...)
jar = compat_cookiejar.CookieJar()
cookie_processor = compat_urllib_request.HTTPCookieProcessor(jar)
proxy_handler = compat_urllib_request.ProxyHandler()
opener = compat_urllib_request.build_opener(proxy_handler, cookie_processor, YoutubeDLHandler())
compat_urllib_request.install_opener(opener)
socket.setdefaulttimeout(10)
def _try_rm(filename):
""" Remove a file if it exists """
try:
os.remove(filename)
except OSError as ose:
if ose.errno != errno.ENOENT:
raise
md5 = lambda s: hashlib.md5(s.encode('utf-8')).hexdigest()
class YoutubeDL(youtube_dl.YoutubeDL):
def __init__(self, *args, **kwargs):
self.to_stderr = self.to_screen
self.processed_info_dicts = []
super(YoutubeDL, self).__init__(*args, **kwargs)
def report_warning(self, message):
# Don't accept warnings during tests
raise ExtractorError(message)
def process_info(self, info_dict):
self.processed_info_dicts.append(info_dict)
return super(YoutubeDL, self).process_info(info_dict)
def _file_md5(fn):
with open(fn, 'rb') as f:
return hashlib.md5(f.read()).hexdigest()
with io.open(DEF_FILE, encoding='utf-8') as deff:
defs = json.load(deff)
with io.open(PARAMETERS_FILE, encoding='utf-8') as pf:
parameters = json.load(pf)
class TestDownload(unittest.TestCase):
maxDiff = None
def setUp(self):
self.parameters = parameters
self.defs = defs
### Dynamically generate tests
def generator(test_case):
def test_template(self):
ie = youtube_dl.extractor.get_info_extractor(test_case['name'])
if not ie._WORKING:
print('Skipping: IE marked as not _WORKING')
return
if 'playlist' not in test_case and not test_case['file']:
print('Skipping: No output file specified')
return
if 'skip' in test_case:
print('Skipping: {0}'.format(test_case['skip']))
return
params = self.parameters.copy()
params.update(test_case.get('params', {}))
ydl = YoutubeDL(params)
for ie in youtube_dl.extractor.gen_extractors():
ydl.add_info_extractor(ie)
finished_hook_called = set()
def _hook(status):
if status['status'] == 'finished':
finished_hook_called.add(status['filename'])
ydl.fd.add_progress_hook(_hook)
test_cases = test_case.get('playlist', [test_case])
for tc in test_cases:
_try_rm(tc['file'])
_try_rm(tc['file'] + '.part')
_try_rm(tc['file'] + '.info.json')
try:
for retry in range(1, RETRIES + 1):
try:
ydl.download([test_case['url']])
except (DownloadError, ExtractorError) as err:
if retry == RETRIES: raise
# Check if the exception is not a network related one
if not err.exc_info[0] in (compat_urllib_error.URLError, socket.timeout, UnavailableVideoError):
raise
print('Retrying: {0} failed tries\n\n##########\n\n'.format(retry))
else:
break
for tc in test_cases:
if not test_case.get('params', {}).get('skip_download', False):
self.assertTrue(os.path.exists(tc['file']), msg='Missing file ' + tc['file'])
self.assertTrue(tc['file'] in finished_hook_called)
self.assertTrue(os.path.exists(tc['file'] + '.info.json'))
if 'md5' in tc:
md5_for_file = _file_md5(tc['file'])
self.assertEqual(md5_for_file, tc['md5'])
with io.open(tc['file'] + '.info.json', encoding='utf-8') as infof:
info_dict = json.load(infof)
for (info_field, expected) in tc.get('info_dict', {}).items():
if isinstance(expected, compat_str) and expected.startswith('md5:'):
self.assertEqual(expected, 'md5:' + md5(info_dict.get(info_field)))
else:
got = info_dict.get(info_field)
self.assertEqual(
expected, got,
u'invalid value for field %s, expected %r, got %r' % (info_field, expected, got))
# If checkable fields are missing from the test case, print the info_dict
test_info_dict = dict((key, value if not isinstance(value, compat_str) or len(value) < 250 else 'md5:' + md5(value))
for key, value in info_dict.items()
if value and key in ('title', 'description', 'uploader', 'upload_date', 'uploader_id', 'location'))
if not all(key in tc.get('info_dict', {}).keys() for key in test_info_dict.keys()):
sys.stderr.write(u'\n"info_dict": ' + json.dumps(test_info_dict, ensure_ascii=False, indent=2) + u'\n')
# Check for the presence of mandatory fields
for key in ('id', 'url', 'title', 'ext'):
self.assertTrue(key in info_dict.keys() and info_dict[key])
finally:
for tc in test_cases:
_try_rm(tc['file'])
_try_rm(tc['file'] + '.part')
_try_rm(tc['file'] + '.info.json')
return test_template
### And add them to TestDownload
for test_case in defs:
test_method = generator(test_case)
test_method.__name__ = "test_{0}".format(test_case["name"])
setattr(TestDownload, test_method.__name__, test_method)
del test_method
if __name__ == '__main__':
unittest.main()
-26
View File
@@ -1,26 +0,0 @@
import unittest
import sys
import os
import subprocess
rootDir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
try:
_DEV_NULL = subprocess.DEVNULL
except AttributeError:
_DEV_NULL = open(os.devnull, 'wb')
class TestExecution(unittest.TestCase):
def test_import(self):
subprocess.check_call([sys.executable, '-c', 'import youtube_dl'], cwd=rootDir)
def test_module_exec(self):
if sys.version_info >= (2,7): # Python 2.6 doesn't support package execution
subprocess.check_call([sys.executable, '-m', 'youtube_dl', '--version'], cwd=rootDir, stdout=_DEV_NULL)
def test_main_exec(self):
subprocess.check_call([sys.executable, 'youtube_dl/__main__.py', '--version'], cwd=rootDir, stdout=_DEV_NULL)
if __name__ == '__main__':
unittest.main()
-116
View File
@@ -1,116 +0,0 @@
#!/usr/bin/env python
# Various small unit tests
import sys
import unittest
# Allow direct execution
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
#from youtube_dl.utils import htmlentity_transform
from youtube_dl.utils import timeconvert
from youtube_dl.utils import sanitize_filename
from youtube_dl.utils import unescapeHTML
from youtube_dl.utils import orderedSet
from youtube_dl.utils import DateRange
from youtube_dl.utils import unified_strdate
if sys.version_info < (3, 0):
_compat_str = lambda b: b.decode('unicode-escape')
else:
_compat_str = lambda s: s
class TestUtil(unittest.TestCase):
def test_timeconvert(self):
self.assertTrue(timeconvert('') is None)
self.assertTrue(timeconvert('bougrg') is None)
def test_sanitize_filename(self):
self.assertEqual(sanitize_filename('abc'), 'abc')
self.assertEqual(sanitize_filename('abc_d-e'), 'abc_d-e')
self.assertEqual(sanitize_filename('123'), '123')
self.assertEqual('abc_de', sanitize_filename('abc/de'))
self.assertFalse('/' in sanitize_filename('abc/de///'))
self.assertEqual('abc_de', sanitize_filename('abc/<>\\*|de'))
self.assertEqual('xxx', sanitize_filename('xxx/<>\\*|'))
self.assertEqual('yes no', sanitize_filename('yes? no'))
self.assertEqual('this - that', sanitize_filename('this: that'))
self.assertEqual(sanitize_filename('AT&T'), 'AT&T')
aumlaut = _compat_str('\xe4')
self.assertEqual(sanitize_filename(aumlaut), aumlaut)
tests = _compat_str('\u043a\u0438\u0440\u0438\u043b\u043b\u0438\u0446\u0430')
self.assertEqual(sanitize_filename(tests), tests)
forbidden = '"\0\\/'
for fc in forbidden:
for fbc in forbidden:
self.assertTrue(fbc not in sanitize_filename(fc))
def test_sanitize_filename_restricted(self):
self.assertEqual(sanitize_filename('abc', restricted=True), 'abc')
self.assertEqual(sanitize_filename('abc_d-e', restricted=True), 'abc_d-e')
self.assertEqual(sanitize_filename('123', restricted=True), '123')
self.assertEqual('abc_de', sanitize_filename('abc/de', restricted=True))
self.assertFalse('/' in sanitize_filename('abc/de///', restricted=True))
self.assertEqual('abc_de', sanitize_filename('abc/<>\\*|de', restricted=True))
self.assertEqual('xxx', sanitize_filename('xxx/<>\\*|', restricted=True))
self.assertEqual('yes_no', sanitize_filename('yes? no', restricted=True))
self.assertEqual('this_-_that', sanitize_filename('this: that', restricted=True))
tests = _compat_str('a\xe4b\u4e2d\u56fd\u7684c')
self.assertEqual(sanitize_filename(tests, restricted=True), 'a_b_c')
self.assertTrue(sanitize_filename(_compat_str('\xf6'), restricted=True) != '') # No empty filename
forbidden = '"\0\\/&!: \'\t\n()[]{}$;`^,#'
for fc in forbidden:
for fbc in forbidden:
self.assertTrue(fbc not in sanitize_filename(fc, restricted=True))
# Handle a common case more neatly
self.assertEqual(sanitize_filename(_compat_str('\u5927\u58f0\u5e26 - Song'), restricted=True), 'Song')
self.assertEqual(sanitize_filename(_compat_str('\u603b\u7edf: Speech'), restricted=True), 'Speech')
# .. but make sure the file name is never empty
self.assertTrue(sanitize_filename('-', restricted=True) != '')
self.assertTrue(sanitize_filename(':', restricted=True) != '')
def test_sanitize_ids(self):
self.assertEqual(sanitize_filename('_n_cd26wFpw', is_id=True), '_n_cd26wFpw')
self.assertEqual(sanitize_filename('_BD_eEpuzXw', is_id=True), '_BD_eEpuzXw')
self.assertEqual(sanitize_filename('N0Y__7-UOdI', is_id=True), 'N0Y__7-UOdI')
def test_ordered_set(self):
self.assertEqual(orderedSet([1, 1, 2, 3, 4, 4, 5, 6, 7, 3, 5]), [1, 2, 3, 4, 5, 6, 7])
self.assertEqual(orderedSet([]), [])
self.assertEqual(orderedSet([1]), [1])
#keep the list ordered
self.assertEqual(orderedSet([135, 1, 1, 1]), [135, 1])
def test_unescape_html(self):
self.assertEqual(unescapeHTML(_compat_str('%20;')), _compat_str('%20;'))
def test_daterange(self):
_20century = DateRange("19000101","20000101")
self.assertFalse("17890714" in _20century)
_ac = DateRange("00010101")
self.assertTrue("19690721" in _ac)
_firstmilenium = DateRange(end="10000101")
self.assertTrue("07110427" in _firstmilenium)
def test_unified_dates(self):
self.assertEqual(unified_strdate('December 21, 2010'), '20101221')
self.assertEqual(unified_strdate('8/7/2009'), '20090708')
self.assertEqual(unified_strdate('Dec 14, 2012'), '20121214')
self.assertEqual(unified_strdate('2012/10/11 01:56:38 +0000'), '20121011')
if __name__ == '__main__':
unittest.main()
-77
View File
@@ -1,77 +0,0 @@
#!/usr/bin/env python
# coding: utf-8
import json
import os
import sys
import unittest
# Allow direct execution
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import youtube_dl.YoutubeDL
import youtube_dl.extractor
from youtube_dl.utils import *
PARAMETERS_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "parameters.json")
# General configuration (from __init__, not very elegant...)
jar = compat_cookiejar.CookieJar()
cookie_processor = compat_urllib_request.HTTPCookieProcessor(jar)
proxy_handler = compat_urllib_request.ProxyHandler()
opener = compat_urllib_request.build_opener(proxy_handler, cookie_processor, YoutubeDLHandler())
compat_urllib_request.install_opener(opener)
class YoutubeDL(youtube_dl.YoutubeDL):
def __init__(self, *args, **kwargs):
super(YoutubeDL, self).__init__(*args, **kwargs)
self.to_stderr = self.to_screen
with io.open(PARAMETERS_FILE, encoding='utf-8') as pf:
params = json.load(pf)
params['writeinfojson'] = True
params['skip_download'] = True
params['writedescription'] = True
TEST_ID = 'BaW_jenozKc'
INFO_JSON_FILE = TEST_ID + '.mp4.info.json'
DESCRIPTION_FILE = TEST_ID + '.mp4.description'
EXPECTED_DESCRIPTION = u'''test chars: "'/\ä↭𝕐
This is a test video for youtube-dl.
For more information, contact phihag@phihag.de .'''
class TestInfoJSON(unittest.TestCase):
def setUp(self):
# Clear old files
self.tearDown()
def test_info_json(self):
ie = youtube_dl.extractor.YoutubeIE()
ydl = YoutubeDL(params)
ydl.add_info_extractor(ie)
ydl.download([TEST_ID])
self.assertTrue(os.path.exists(INFO_JSON_FILE))
with io.open(INFO_JSON_FILE, 'r', encoding='utf-8') as jsonf:
jd = json.load(jsonf)
self.assertEqual(jd['upload_date'], u'20121002')
self.assertEqual(jd['description'], EXPECTED_DESCRIPTION)
self.assertEqual(jd['id'], TEST_ID)
self.assertEqual(jd['extractor'], 'youtube')
self.assertEqual(jd['title'], u'''youtube-dl test video "'/\ä↭𝕐''')
self.assertEqual(jd['uploader'], 'Philipp Hagemeister')
self.assertTrue(os.path.exists(DESCRIPTION_FILE))
with io.open(DESCRIPTION_FILE, 'r', encoding='utf-8') as descf:
descr = descf.read()
self.assertEqual(descr, EXPECTED_DESCRIPTION)
def tearDown(self):
if os.path.exists(INFO_JSON_FILE):
os.remove(INFO_JSON_FILE)
if os.path.exists(DESCRIPTION_FILE):
os.remove(DESCRIPTION_FILE)
if __name__ == '__main__':
unittest.main()
-114
View File
@@ -1,114 +0,0 @@
#!/usr/bin/env python
import sys
import unittest
import json
# Allow direct execution
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from youtube_dl.extractor import YoutubeUserIE, YoutubePlaylistIE, YoutubeIE, YoutubeChannelIE
from youtube_dl.utils import *
from youtube_dl import YoutubeDL
PARAMETERS_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "parameters.json")
with io.open(PARAMETERS_FILE, encoding='utf-8') as pf:
parameters = json.load(pf)
# General configuration (from __init__, not very elegant...)
jar = compat_cookiejar.CookieJar()
cookie_processor = compat_urllib_request.HTTPCookieProcessor(jar)
proxy_handler = compat_urllib_request.ProxyHandler()
opener = compat_urllib_request.build_opener(proxy_handler, cookie_processor, YoutubeDLHandler())
compat_urllib_request.install_opener(opener)
class FakeYDL(YoutubeDL):
def __init__(self):
self.result = []
self.params = parameters
def to_screen(self, s):
print(s)
def trouble(self, s, tb=None):
raise Exception(s)
def extract_info(self, url):
self.result.append(url)
return url
class TestYoutubeLists(unittest.TestCase):
def assertIsPlaylist(self,info):
"""Make sure the info has '_type' set to 'playlist'"""
self.assertEqual(info['_type'], 'playlist')
def test_youtube_playlist(self):
dl = FakeYDL()
ie = YoutubePlaylistIE(dl)
result = ie.extract('https://www.youtube.com/playlist?list=PLwiyx1dc3P2JR9N8gQaQN_BCvlSlap7re')[0]
self.assertIsPlaylist(result)
self.assertEqual(result['title'], 'ytdl test PL')
ytie_results = [YoutubeIE()._extract_id(url['url']) for url in result['entries']]
self.assertEqual(ytie_results, [ 'bV9L5Ht9LgY', 'FXxLjLQi3Fg', 'tU3Bgo5qJZE'])
def test_issue_673(self):
dl = FakeYDL()
ie = YoutubePlaylistIE(dl)
result = ie.extract('PLBB231211A4F62143')[0]
self.assertTrue(len(result['entries']) > 25)
def test_youtube_playlist_long(self):
dl = FakeYDL()
ie = YoutubePlaylistIE(dl)
result = ie.extract('https://www.youtube.com/playlist?list=UUBABnxM4Ar9ten8Mdjj1j0Q')[0]
self.assertIsPlaylist(result)
self.assertTrue(len(result['entries']) >= 799)
def test_youtube_playlist_with_deleted(self):
#651
dl = FakeYDL()
ie = YoutubePlaylistIE(dl)
result = ie.extract('https://www.youtube.com/playlist?list=PLwP_SiAcdui0KVebT0mU9Apz359a4ubsC')[0]
ytie_results = [YoutubeIE()._extract_id(url['url']) for url in result['entries']]
self.assertFalse('pElCt5oNDuI' in ytie_results)
self.assertFalse('KdPEApIVdWM' in ytie_results)
def test_youtube_playlist_empty(self):
dl = FakeYDL()
ie = YoutubePlaylistIE(dl)
result = ie.extract('https://www.youtube.com/playlist?list=PLtPgu7CB4gbZDA7i_euNxn75ISqxwZPYx')[0]
self.assertIsPlaylist(result)
self.assertEqual(len(result['entries']), 0)
def test_youtube_course(self):
dl = FakeYDL()
ie = YoutubePlaylistIE(dl)
# TODO find a > 100 (paginating?) videos course
result = ie.extract('https://www.youtube.com/course?list=ECUl4u3cNGP61MdtwGTqZA0MreSaDybji8')[0]
entries = result['entries']
self.assertEqual(YoutubeIE()._extract_id(entries[0]['url']), 'j9WZyLZCBzs')
self.assertEqual(len(entries), 25)
self.assertEqual(YoutubeIE()._extract_id(entries[-1]['url']), 'rYefUsYuEp0')
def test_youtube_channel(self):
dl = FakeYDL()
ie = YoutubeChannelIE(dl)
#test paginated channel
result = ie.extract('https://www.youtube.com/channel/UCKfVa3S1e4PHvxWcwyMMg8w')[0]
self.assertTrue(len(result['entries']) > 90)
#test autogenerated channel
result = ie.extract('https://www.youtube.com/channel/HCtnHdj3df7iM/videos')[0]
self.assertTrue(len(result['entries']) >= 18)
def test_youtube_user(self):
dl = FakeYDL()
ie = YoutubeUserIE(dl)
result = ie.extract('https://www.youtube.com/user/TheLinuxFoundation')[0]
self.assertTrue(len(result['entries']) >= 320)
def test_youtube_safe_search(self):
dl = FakeYDL()
ie = YoutubePlaylistIE(dl)
result = ie.extract('PLtPgu7CB4gbY9oDN3drwC3cMbJggS7dKl')[0]
self.assertEqual(len(result['entries']), 2)
if __name__ == '__main__':
unittest.main()
-111
View File
@@ -1,111 +0,0 @@
#!/usr/bin/env python
import sys
import unittest
import json
import io
import hashlib
# Allow direct execution
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from youtube_dl.extractor import YoutubeIE
from youtube_dl.utils import *
from youtube_dl import YoutubeDL
PARAMETERS_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "parameters.json")
with io.open(PARAMETERS_FILE, encoding='utf-8') as pf:
parameters = json.load(pf)
# General configuration (from __init__, not very elegant...)
jar = compat_cookiejar.CookieJar()
cookie_processor = compat_urllib_request.HTTPCookieProcessor(jar)
proxy_handler = compat_urllib_request.ProxyHandler()
opener = compat_urllib_request.build_opener(proxy_handler, cookie_processor, YoutubeDLHandler())
compat_urllib_request.install_opener(opener)
class FakeYDL(YoutubeDL):
def __init__(self):
self.result = []
# Different instances of the downloader can't share the same dictionary
# some test set the "sublang" parameter, which would break the md5 checks.
self.params = dict(parameters)
def to_screen(self, s):
print(s)
def trouble(self, s, tb=None):
raise Exception(s)
def download(self, x):
self.result.append(x)
md5 = lambda s: hashlib.md5(s.encode('utf-8')).hexdigest()
class TestYoutubeSubtitles(unittest.TestCase):
def setUp(self):
DL = FakeYDL()
DL.params['allsubtitles'] = False
DL.params['writesubtitles'] = False
DL.params['subtitlesformat'] = 'srt'
DL.params['listsubtitles'] = False
def test_youtube_no_subtitles(self):
DL = FakeYDL()
DL.params['writesubtitles'] = False
IE = YoutubeIE(DL)
info_dict = IE.extract('QRS8MkLhQmM')
subtitles = info_dict[0]['subtitles']
self.assertEqual(subtitles, None)
def test_youtube_subtitles(self):
DL = FakeYDL()
DL.params['writesubtitles'] = True
IE = YoutubeIE(DL)
info_dict = IE.extract('QRS8MkLhQmM')
sub = info_dict[0]['subtitles'][0]
self.assertEqual(md5(sub[2]), '4cd9278a35ba2305f47354ee13472260')
def test_youtube_subtitles_it(self):
DL = FakeYDL()
DL.params['writesubtitles'] = True
DL.params['subtitleslang'] = 'it'
IE = YoutubeIE(DL)
info_dict = IE.extract('QRS8MkLhQmM')
sub = info_dict[0]['subtitles'][0]
self.assertEqual(md5(sub[2]), '164a51f16f260476a05b50fe4c2f161d')
def test_youtube_onlysubtitles(self):
DL = FakeYDL()
DL.params['writesubtitles'] = True
DL.params['onlysubtitles'] = True
IE = YoutubeIE(DL)
info_dict = IE.extract('QRS8MkLhQmM')
sub = info_dict[0]['subtitles'][0]
self.assertEqual(md5(sub[2]), '4cd9278a35ba2305f47354ee13472260')
def test_youtube_allsubtitles(self):
DL = FakeYDL()
DL.params['allsubtitles'] = True
IE = YoutubeIE(DL)
info_dict = IE.extract('QRS8MkLhQmM')
subtitles = info_dict[0]['subtitles']
self.assertEqual(len(subtitles), 13)
def test_youtube_subtitles_format(self):
DL = FakeYDL()
DL.params['writesubtitles'] = True
DL.params['subtitlesformat'] = 'sbv'
IE = YoutubeIE(DL)
info_dict = IE.extract('QRS8MkLhQmM')
sub = info_dict[0]['subtitles'][0]
self.assertEqual(md5(sub[2]), '13aeaa0c245a8bed9a451cb643e3ad8b')
def test_youtube_list_subtitles(self):
DL = FakeYDL()
DL.params['listsubtitles'] = True
IE = YoutubeIE(DL)
info_dict = IE.extract('QRS8MkLhQmM')
self.assertEqual(info_dict, None)
def test_youtube_automatic_captions(self):
DL = FakeYDL()
DL.params['writesubtitles'] = True
DL.params['subtitleslang'] = 'it'
IE = YoutubeIE(DL)
info_dict = IE.extract('8YoUxe5ncPo')
sub = info_dict[0]['subtitles'][0]
self.assertTrue(sub[2] is not None)
if __name__ == '__main__':
unittest.main()
-664
View File
@@ -1,664 +0,0 @@
[
{
"name": "Youtube",
"url": "http://www.youtube.com/watch?v=BaW_jenozKc",
"file": "BaW_jenozKc.mp4",
"info_dict": {
"title": "youtube-dl test video \"'/\\ä↭𝕐",
"uploader": "Philipp Hagemeister",
"uploader_id": "phihag",
"upload_date": "20121002",
"description": "test chars: \"'/\\ä↭𝕐\n\nThis is a test video for youtube-dl.\n\nFor more information, contact phihag@phihag.de ."
}
},
{
"name": "Dailymotion",
"md5": "392c4b85a60a90dc4792da41ce3144eb",
"url": "http://www.dailymotion.com/video/x33vw9_tutoriel-de-youtubeur-dl-des-video_tech",
"file": "x33vw9.mp4",
"info_dict": {
"uploader": "Alex and Van .",
"title": "Tutoriel de Youtubeur\"DL DES VIDEO DE YOUTUBE\""
}
},
{
"name": "Metacafe",
"add_ie": ["Youtube"],
"url": "http://metacafe.com/watch/yt-_aUehQsCQtM/the_electric_company_short_i_pbs_kids_go/",
"file": "_aUehQsCQtM.flv",
"info_dict": {
"upload_date": "20090102",
"title": "The Electric Company | \"Short I\" | PBS KIDS GO!",
"description": "md5:2439a8ef6d5a70e380c22f5ad323e5a8",
"uploader": "PBS",
"uploader_id": "PBS"
}
},
{
"name": "BlipTV",
"md5": "b2d849efcf7ee18917e4b4d9ff37cafe",
"url": "http://blip.tv/cbr/cbr-exclusive-gotham-city-imposters-bats-vs-jokerz-short-3-5796352",
"file": "5779306.m4v",
"info_dict": {
"upload_date": "20111205",
"description": "md5:9bc31f227219cde65e47eeec8d2dc596",
"uploader": "Comic Book Resources - CBR TV",
"title": "CBR EXCLUSIVE: \"Gotham City Imposters\" Bats VS Jokerz Short 3"
}
},
{
"name": "XVideos",
"md5": "1d0c835822f0a71a7bf011855db929d0",
"url": "http://www.xvideos.com/video939581/funny_porns_by_s_-1",
"file": "939581.flv",
"info_dict": {
"title": "Funny Porns By >>>>S<<<<<< -1"
}
},
{
"name": "YouPorn",
"md5": "c37ddbaaa39058c76a7e86c6813423c1",
"url": "http://www.youporn.com/watch/505835/sex-ed-is-it-safe-to-masturbate-daily/",
"file": "505835.mp4",
"info_dict": {
"upload_date": "20101221",
"description": "Love & Sex Answers: http://bit.ly/DanAndJenn -- Is It Unhealthy To Masturbate Daily?",
"uploader": "Ask Dan And Jennifer",
"title": "Sex Ed: Is It Safe To Masturbate Daily?"
}
},
{
"name": "Pornotube",
"md5": "374dd6dcedd24234453b295209aa69b6",
"url": "http://pornotube.com/c/173/m/1689755/Marilyn-Monroe-Bathing",
"file": "1689755.flv",
"info_dict": {
"upload_date": "20090708",
"title": "Marilyn-Monroe-Bathing"
}
},
{
"name": "YouJizz",
"md5": "07e15fa469ba384c7693fd246905547c",
"url": "http://www.youjizz.com/videos/zeichentrick-1-2189178.html",
"file": "2189178.flv",
"info_dict": {
"title": "Zeichentrick 1"
}
},
{
"name": "Vimeo",
"md5": "8879b6cc097e987f02484baf890129e5",
"url": "http://vimeo.com/56015672",
"file": "56015672.mp4",
"info_dict": {
"title": "youtube-dl test video - ★ \" ' 幸 / \\ ä ↭ 𝕐",
"uploader": "Filippo Valsorda",
"uploader_id": "user7108434",
"upload_date": "20121220",
"description": "This is a test case for youtube-dl.\nFor more information, see github.com/rg3/youtube-dl\nTest chars: ★ \" ' 幸 / \\ ä ↭ 𝕐"
}
},
{
"name": "Soundcloud",
"md5": "ebef0a451b909710ed1d7787dddbf0d7",
"url": "http://soundcloud.com/ethmusic/lostin-powers-she-so-heavy",
"file": "62986583.mp3",
"info_dict": {
"upload_date": "20121011",
"description": "No Downloads untill we record the finished version this weekend, i was too pumped n i had to post it , earl is prolly gonna b hella p.o'd",
"uploader": "E.T. ExTerrestrial Music",
"title": "Lostin Powers - She so Heavy (SneakPreview) Adrian Ackers Blueprint 1"
}
},
{
"name": "StanfordOpenClassroom",
"md5": "544a9468546059d4e80d76265b0443b8",
"url": "http://openclassroom.stanford.edu/MainFolder/VideoPage.php?course=PracticalUnix&video=intro-environment&speed=100",
"file": "PracticalUnix_intro-environment.mp4",
"info_dict": {
"title": "Intro Environment"
}
},
{
"name": "XNXX",
"md5": "0831677e2b4761795f68d417e0b7b445",
"url": "http://video.xnxx.com/video1135332/lida_naked_funny_actress_5_",
"file": "1135332.flv",
"info_dict": {
"title": "lida » Naked Funny Actress (5)"
}
},
{
"name": "Youku",
"url": "http://v.youku.com/v_show/id_XNDgyMDQ2NTQw.html",
"file": "XNDgyMDQ2NTQw_part00.flv",
"md5": "ffe3f2e435663dc2d1eea34faeff5b5b",
"params": { "test": false },
"info_dict": {
"title": "youtube-dl test video \"'/\\ä↭𝕐"
}
},
{
"name": "NBA",
"url": "http://www.nba.com/video/games/nets/2012/12/04/0021200253-okc-bkn-recap.nba/index.html",
"file": "0021200253-okc-bkn-recap.nba.mp4",
"md5": "c0edcfc37607344e2ff8f13c378c88a4",
"info_dict": {
"description": "Kevin Durant scores 32 points and dishes out six assists as the Thunder beat the Nets in Brooklyn.",
"title": "Thunder vs. Nets"
}
},
{
"name": "JustinTV",
"url": "http://www.twitch.tv/thegamedevhub/b/296128360",
"file": "296128360.flv",
"md5": "ecaa8a790c22a40770901460af191c9a",
"info_dict": {
"upload_date": "20110927",
"uploader_id": 25114803,
"uploader": "thegamedevhub",
"title": "Beginner Series - Scripting With Python Pt.1"
}
},
{
"name": "MyVideo",
"url": "http://www.myvideo.de/watch/8229274/bowling_fail_or_win",
"file": "8229274.flv",
"md5": "2d2753e8130479ba2cb7e0a37002053e",
"info_dict": {
"title": "bowling-fail-or-win"
}
},
{
"name": "Escapist",
"url": "http://www.escapistmagazine.com/videos/view/the-escapist-presents/6618-Breaking-Down-Baldurs-Gate",
"file": "6618-Breaking-Down-Baldurs-Gate.mp4",
"md5": "c6793dbda81388f4264c1ba18684a74d",
"info_dict": {
"description": "Baldur's Gate: Original, Modded or Enhanced Edition? I'll break down what you can expect from the new Baldur's Gate: Enhanced Edition.",
"uploader": "the-escapist-presents",
"title": "Breaking Down Baldur's Gate"
}
},
{
"name": "GooglePlus",
"url": "https://plus.google.com/u/0/108897254135232129896/posts/ZButuJc6CtH",
"file": "ZButuJc6CtH.flv",
"info_dict": {
"upload_date": "20120613",
"uploader": "井上ヨシマサ",
"title": "嘆きの天使 降臨"
}
},
{
"name": "FunnyOrDie",
"url": "http://www.funnyordie.com/videos/0732f586d7/heart-shaped-box-literal-video-version",
"file": "0732f586d7.mp4",
"md5": "f647e9e90064b53b6e046e75d0241fbd",
"info_dict": {
"description": "Lyrics changed to match the video. Spoken cameo by Obscurus Lupa (from ThatGuyWithTheGlasses.com). Based on a concept by Dustin McLean (DustFilms.com). Performed, edited, and written by David A. Scott.",
"title": "Heart-Shaped Box: Literal Video Version"
}
},
{
"name": "Steam",
"url": "http://store.steampowered.com/video/105600/",
"playlist": [
{
"file": "81300.flv",
"md5": "f870007cee7065d7c76b88f0a45ecc07",
"info_dict": {
"title": "Terraria 1.1 Trailer"
}
},
{
"file": "80859.flv",
"md5": "61aaf31a5c5c3041afb58fb83cbb5751",
"info_dict": {
"title": "Terraria Trailer"
}
}
]
},
{
"name": "Ustream",
"url": "http://www.ustream.tv/recorded/20274954",
"file": "20274954.flv",
"md5": "088f151799e8f572f84eb62f17d73e5c",
"info_dict": {
"title": "Young Americans for Liberty February 7, 2012 2:28 AM",
"uploader": "Young Americans for Liberty"
}
},
{
"name": "InfoQ",
"url": "http://www.infoq.com/presentations/A-Few-of-My-Favorite-Python-Things",
"file": "12-jan-pythonthings.mp4",
"info_dict": {
"description": "Mike Pirnat presents some tips and tricks, standard libraries and third party packages that make programming in Python a richer experience.",
"title": "A Few of My Favorite [Python] Things"
},
"params": {
"skip_download": true
}
},
{
"name": "ComedyCentral",
"url": "http://www.thedailyshow.com/watch/thu-december-13-2012/kristen-stewart",
"file": "422212.mp4",
"md5": "4e2f5cb088a83cd8cdb7756132f9739d",
"info_dict": {
"upload_date": "20121214",
"description": "Kristen Stewart",
"uploader": "thedailyshow",
"title": "thedailyshow-kristen-stewart part 1"
}
},
{
"name": "RBMARadio",
"url": "http://www.rbmaradio.com/shows/ford-lopatin-live-at-primavera-sound-2011",
"file": "ford-lopatin-live-at-primavera-sound-2011.mp3",
"md5": "6bc6f9bcb18994b4c983bc3bf4384d95",
"info_dict": {
"title": "Live at Primavera Sound 2011",
"description": "Joel Ford and Daniel \u2019Oneohtrix Point Never\u2019 Lopatin fly their midified pop extravaganza to Spain. Live at Primavera Sound 2011.",
"uploader": "Ford & Lopatin",
"uploader_id": "ford-lopatin",
"location": "Spain"
}
},
{
"name": "Facebook",
"url": "https://www.facebook.com/photo.php?v=120708114770723",
"file": "120708114770723.mp4",
"md5": "48975a41ccc4b7a581abd68651c1a5a8",
"info_dict": {
"title": "PEOPLE ARE AWESOME 2013",
"duration": 279
}
},
{
"name": "EightTracks",
"url": "http://8tracks.com/ytdl/youtube-dl-test-tracks-a",
"playlist": [
{
"file": "11885610.m4a",
"md5": "96ce57f24389fc8734ce47f4c1abcc55",
"info_dict": {
"title": "youtue-dl project<>\"' - youtube-dl test track 1 \"'/\\\u00e4\u21ad",
"uploader_id": "ytdl"
}
},
{
"file": "11885608.m4a",
"md5": "4ab26f05c1f7291ea460a3920be8021f",
"info_dict": {
"title": "youtube-dl project - youtube-dl test track 2 \"'/\\\u00e4\u21ad",
"uploader_id": "ytdl"
}
},
{
"file": "11885679.m4a",
"md5": "d30b5b5f74217410f4689605c35d1fd7",
"info_dict": {
"title": "youtube-dl project as well - youtube-dl test track 3 \"'/\\\u00e4\u21ad",
"uploader_id": "ytdl"
}
},
{
"file": "11885680.m4a",
"md5": "4eb0a669317cd725f6bbd336a29f923a",
"info_dict": {
"title": "youtube-dl project as well - youtube-dl test track 4 \"'/\\\u00e4\u21ad",
"uploader_id": "ytdl"
}
},
{
"file": "11885682.m4a",
"md5": "1893e872e263a2705558d1d319ad19e8",
"info_dict": {
"title": "PH - youtube-dl test track 5 \"'/\\\u00e4\u21ad",
"uploader_id": "ytdl"
}
},
{
"file": "11885683.m4a",
"md5": "b673c46f47a216ab1741ae8836af5899",
"info_dict": {
"title": "PH - youtube-dl test track 6 \"'/\\\u00e4\u21ad",
"uploader_id": "ytdl"
}
},
{
"file": "11885684.m4a",
"md5": "1d74534e95df54986da7f5abf7d842b7",
"info_dict": {
"title": "phihag - youtube-dl test track 7 \"'/\\\u00e4\u21ad",
"uploader_id": "ytdl"
}
},
{
"file": "11885685.m4a",
"md5": "f081f47af8f6ae782ed131d38b9cd1c0",
"info_dict": {
"title": "phihag - youtube-dl test track 8 \"'/\\\u00e4\u21ad",
"uploader_id": "ytdl"
}
}
]
},
{
"name": "Keek",
"url": "http://www.keek.com/ytdl/keeks/NODfbab",
"file": "NODfbab.mp4",
"md5": "9b0636f8c0f7614afa4ea5e4c6e57e83",
"info_dict": {
"uploader": "ytdl",
"title": "test chars: \"'/\\ä<>This is a test video for youtube-dl.For more information, contact phihag@phihag.de ."
}
},
{
"name": "TED",
"url": "http://www.ted.com/talks/dan_dennett_on_our_consciousness.html",
"file": "102.mp4",
"md5": "8cd9dfa41ee000ce658fd48fb5d89a61",
"info_dict": {
"title": "Dan Dennett: The illusion of consciousness",
"description": "md5:c6fa72e6eedbd938c9caf6b2702f5922"
}
},
{
"name": "MySpass",
"url": "http://www.myspass.de/myspass/shows/tvshows/absolute-mehrheit/Absolute-Mehrheit-vom-17022013-Die-Highlights-Teil-2--/11741/",
"file": "11741.mp4",
"md5": "0b49f4844a068f8b33f4b7c88405862b",
"info_dict": {
"description": "Wer kann in die Fußstapfen von Wolfgang Kubicki treten und die Mehrheit der Zuschauer hinter sich versammeln? Wird vielleicht sogar die Absolute Mehrheit geknackt und der Jackpot von 200.000 Euro mit nach Hause genommen?",
"title": "Absolute Mehrheit vom 17.02.2013 - Die Highlights, Teil 2"
}
},
{
"name": "Generic",
"url": "http://www.hodiho.fr/2013/02/regis-plante-sa-jeep.html",
"file": "13601338388002.mp4",
"md5": "85b90ccc9d73b4acd9138d3af4c27f89",
"info_dict": {
"uploader": "www.hodiho.fr",
"title": "Régis plante sa Jeep"
}
},
{
"name": "Spiegel",
"url": "http://www.spiegel.de/video/vulkan-tungurahua-in-ecuador-ist-wieder-aktiv-video-1259285.html",
"file": "1259285.mp4",
"md5": "2c2754212136f35fb4b19767d242f66e",
"info_dict": {
"title": "Vulkanausbruch in Ecuador: Der \"Feuerschlund\" ist wieder aktiv"
}
},
{
"name": "LiveLeak",
"md5": "0813c2430bea7a46bf13acf3406992f4",
"url": "http://www.liveleak.com/view?i=757_1364311680",
"file": "757_1364311680.mp4",
"info_dict": {
"title": "Most unlucky car accident",
"description": "extremely bad day for this guy..!",
"uploader": "ljfriel2"
}
},
{
"name": "WorldStarHipHop",
"url": "http://www.worldstarhiphop.com/videos/video.php?v=wshh6a7q1ny0G34ZwuIO",
"file": "wshh6a7q1ny0G34ZwuIO.mp4",
"md5": "9d04de741161603bf7071bbf4e883186",
"info_dict": {
"title": "Video: KO Of The Week: MMA Fighter Gets Knocked Out By Swift Head Kick!"
}
},
{
"name": "ARD",
"url": "http://www.ardmediathek.de/das-erste/tagesschau-in-100-sek?documentId=14077640",
"file": "14077640.mp4",
"md5": "6ca8824255460c787376353f9e20bbd8",
"info_dict": {
"title": "11.04.2013 09:23 Uhr - Tagesschau in 100 Sekunden"
},
"skip": "Requires rtmpdump"
},
{
"name": "Tumblr",
"url": "http://resigno.tumblr.com/post/53364321212/e-de-extrema-importancia-que-esse-video-seja",
"file": "53364321212.mp4",
"md5": "0716d3dd51baf68a28b40fdf1251494e",
"info_dict": {
"title": "Rafael Lemos | Tumblr"
}
},
{
"name": "SoundcloudSet",
"url":"https://soundcloud.com/the-concept-band/sets/the-royal-concept-ep",
"playlist":[
{
"file":"30510138.mp3",
"md5":"f9136bf103901728f29e419d2c70f55d",
"info_dict": {
"upload_date": "20111213",
"description": "The Royal Concept from Stockholm\r\nFilip / Povel / David / Magnus\r\nwww.royalconceptband.com",
"uploader": "The Royal Concept",
"title": "D-D-Dance"
}
},
{
"file":"47127625.mp3",
"md5":"09b6758a018470570f8fd423c9453dd8",
"info_dict": {
"upload_date": "20120521",
"description": "The Royal Concept from Stockholm\r\nFilip / Povel / David / Magnus\r\nwww.royalconceptband.com",
"uploader": "The Royal Concept",
"title": "The Royal Concept - Gimme Twice"
}
},
{
"file":"47127627.mp3",
"md5":"154abd4e418cea19c3b901f1e1306d9c",
"info_dict": {
"upload_date": "20120521",
"uploader": "The Royal Concept",
"title": "Goldrushed"
}
},
{
"file":"47127629.mp3",
"md5":"2f5471edc79ad3f33a683153e96a79c1",
"info_dict": {
"upload_date": "20120521",
"description": "The Royal Concept from Stockholm\r\nFilip / Povel / David / Magnus\r\nwww.royalconceptband.com",
"uploader": "The Royal Concept",
"title": "In the End"
}
},
{
"file":"47127631.mp3",
"md5":"f9ba87aa940af7213f98949254f1c6e2",
"info_dict": {
"upload_date": "20120521",
"description": "The Royal Concept from Stockholm\r\nFilip / David / Povel / Magnus\r\nwww.theroyalconceptband.com",
"uploader": "The Royal Concept",
"title": "Knocked Up"
}
},
{
"file":"75206121.mp3",
"md5":"f9d1fe9406717e302980c30de4af9353",
"info_dict": {
"upload_date": "20130116",
"description": "The unreleased track World on Fire premiered on the CW's hit show Arrow (8pm/7pm central). \r\nAs a gift to our fans we would like to offer you a free download of the track! ",
"uploader": "The Royal Concept",
"title": "World On Fire"
}
}
]
},
{
"name":"Bandcamp",
"url":"http://youtube-dl.bandcamp.com/track/youtube-dl-test-song",
"file":"1812978515.mp3",
"md5":"cdeb30cdae1921719a3cbcab696ef53c",
"info_dict": {
"title":"youtube-dl test song \"'/\\ä↭"
},
"skip": "There is a limit of 200 free downloads / month for the test song"
},
{
"name": "RedTube",
"url": "http://www.redtube.com/66418",
"file": "66418.mp4",
"md5": "7b8c22b5e7098a3e1c09709df1126d2d",
"info_dict":{
"title":"Sucked on a toilet"
}
},
{
"name": "Photobucket",
"url": "http://media.photobucket.com/user/rachaneronas/media/TiredofLinkBuildingTryBacklinkMyDomaincom_zpsc0c3b9fa.mp4.html?filters[term]=search&filters[primary]=videos&filters[secondary]=images&sort=1&o=0",
"file": "zpsc0c3b9fa.mp4",
"md5": "7dabfb92b0a31f6c16cebc0f8e60ff99",
"info_dict": {
"upload_date": "20130504",
"uploader": "rachaneronas",
"title": "Tired of Link Building? Try BacklinkMyDomain.com!"
}
},
{
"name": "Ina",
"url": "www.ina.fr/video/I12055569/francois-hollande-je-crois-que-c-est-clair-video.html",
"file": "I12055569.mp4",
"md5": "a667021bf2b41f8dc6049479d9bb38a3",
"info_dict":{
"title":"François Hollande \"Je crois que c'est clair\""
}
},
{
"name": "Yahoo",
"url": "http://screen.yahoo.com/julian-smith-travis-legg-watch-214727115.html",
"file": "214727115.flv",
"md5": "2e717f169c1be93d84d3794a00d4a325",
"info_dict": {
"title": "Julian Smith & Travis Legg Watch Julian Smith"
},
"skip": "Requires rtmpdump"
},
{
"name": "Howcast",
"url": "http://www.howcast.com/videos/390161-How-to-Tie-a-Square-Knot-Properly",
"file": "390161.mp4",
"md5": "1d7ba54e2c9d7dc6935ef39e00529138",
"info_dict":{
"title":"How to Tie a Square Knot Properly",
"description":"The square knot, also known as the reef knot, is one of the oldest, most basic knots to tie, and can be used in many different ways. Here's the proper way to tie a square knot."
}
},
{
"name": "Vine",
"url": "https://vine.co/v/b9KOOWX7HUx",
"file": "b9KOOWX7HUx.mp4",
"md5": "2f36fed6235b16da96ce9b4dc890940d",
"info_dict":{
"title": "Chicken.",
"uploader": "Jack Dorsey"
}
},
{
"name": "Flickr",
"url": "http://www.flickr.com/photos/forestwander-nature-pictures/5645318632/in/photostream/",
"file": "5645318632.mp4",
"md5": "6fdc01adbc89d72fc9c4f15b4a4ba87b",
"info_dict":{
"title": "Dark Hollow Waterfalls",
"uploader_id": "forestwander-nature-pictures",
"description": "Waterfalls in the Springtime at Dark Hollow Waterfalls. These are located just off of Skyline Drive in Virginia. They are only about 6/10 of a mile hike but it is a pretty steep hill and a good climb back up."
}
},
{
"name": "Teamcoco",
"url": "http://teamcoco.com/video/louis-ck-interview-george-w-bush",
"file": "19705.mp4",
"md5": "27b6f7527da5acf534b15f21b032656e",
"info_dict":{
"title": "Louis C.K. Interview Pt. 1 11/3/11",
"description": "Louis C.K. got starstruck by George W. Bush, so what? Part one."
}
},
{
"name": "XHamster",
"url": "http://xhamster.com/movies/1509445/femaleagent_shy_beauty_takes_the_bait.html",
"file": "1509445.flv",
"md5": "9f48e0e8d58e3076bb236ff412ab62fa",
"info_dict": {
"upload_date": "20121014",
"uploader_id": "Ruseful2011",
"title": "FemaleAgent Shy beauty takes the bait"
}
},
{
"name": "Hypem",
"url": "http://hypem.com/track/1v6ga/BODYWORK+-+TAME",
"file": "1v6ga.mp3",
"md5": "b9cc91b5af8995e9f0c1cee04c575828",
"info_dict":{
"title":"Tame"
}
},
{
"name": "Vbox7",
"url": "http://vbox7.com/play:249bb972c2",
"file": "249bb972c2.flv",
"md5": "9c70d6d956f888bdc08c124acc120cfe",
"info_dict":{
"title":"Смях! Чудо - чист за секунди - Скрита камера"
}
},
{
"name": "Gametrailers",
"url": "http://www.gametrailers.com/videos/zbvr8i/mirror-s-edge-2-e3-2013--debut-trailer",
"file": "zbvr8i.flv",
"md5": "c3edbc995ab4081976e16779bd96a878",
"info_dict": {
"title": "E3 2013: Debut Trailer"
},
"skip": "Requires rtmpdump"
},
{
"name": "Statigram",
"url": "http://statigr.am/p/484091715184808010_284179915",
"file": "484091715184808010_284179915.mp4",
"md5": "deda4ff333abe2e118740321e992605b",
"info_dict": {
"uploader_id": "videoseconds",
"title": "Instagram photo by @videoseconds (Videos)"
}
},
{
"name": "Break",
"url": "http://www.break.com/video/when-girls-act-like-guys-2468056",
"file": "2468056.mp4",
"md5": "a3513fb1547fba4fb6cfac1bffc6c46b",
"info_dict": {
"title": "When Girls Act Like D-Bags"
}
},
{
"name": "Vevo",
"url": "http://www.vevo.com/watch/hurts/somebody-to-die-for/GB1101300280",
"file": "GB1101300280.mp4",
"md5": "06bea460acb744eab74a9d7dcb4bfd61",
"info_dict": {
"title": "Somebody To Die For",
"upload_date": "20130624",
"uploader": "Hurts"
}
}
]
+1
View File
@@ -0,0 +1 @@
2019.03.09
+13358
View File
File diff suppressed because it is too large Load Diff
+12472
View File
File diff suppressed because it is too large Load Diff
-89
View File
@@ -1,89 +0,0 @@
#!/usr/bin/env python
import sys, os
import json, hashlib
try:
import urllib.request as compat_urllib_request
except ImportError: # Python 2
import urllib2 as compat_urllib_request
def rsa_verify(message, signature, key):
from struct import pack
from hashlib import sha256
from sys import version_info
def b(x):
if version_info[0] == 2: return x
else: return x.encode('latin1')
assert(type(message) == type(b('')))
block_size = 0
n = key[0]
while n:
block_size += 1
n >>= 8
signature = pow(int(signature, 16), key[1], key[0])
raw_bytes = []
while signature:
raw_bytes.insert(0, pack("B", signature & 0xFF))
signature >>= 8
signature = (block_size - len(raw_bytes)) * b('\x00') + b('').join(raw_bytes)
if signature[0:2] != b('\x00\x01'): return False
signature = signature[2:]
if not b('\x00') in signature: return False
signature = signature[signature.index(b('\x00'))+1:]
if not signature.startswith(b('\x30\x31\x30\x0D\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20')): return False
signature = signature[19:]
if signature != sha256(message).digest(): return False
return True
sys.stderr.write(u'Hi! We changed distribution method and now youtube-dl needs to update itself one more time.\n')
sys.stderr.write(u'This will only happen once. Simply press enter to go on. Sorry for the trouble!\n')
sys.stderr.write(u'From now on, get the binaries from http://rg3.github.io/youtube-dl/download.html, not from the git repository.\n\n')
try:
raw_input()
except NameError: # Python 3
input()
filename = sys.argv[0]
UPDATE_URL = "http://rg3.github.io/youtube-dl/update/"
VERSION_URL = UPDATE_URL + 'LATEST_VERSION'
JSON_URL = UPDATE_URL + 'versions.json'
UPDATES_RSA_KEY = (0x9d60ee4d8f805312fdb15a62f87b95bd66177b91df176765d13514a0f1754bcd2057295c5b6f1d35daa6742c3ffc9a82d3e118861c207995a8031e151d863c9927e304576bc80692bc8e094896fcf11b66f3e29e04e3a71e9a11558558acea1840aec37fc396fb6b65dc81a1c4144e03bd1c011de62e3f1357b327d08426fe93, 65537)
if not os.access(filename, os.W_OK):
sys.exit('ERROR: no write permissions on %s' % filename)
try:
versions_info = compat_urllib_request.urlopen(JSON_URL).read().decode('utf-8')
versions_info = json.loads(versions_info)
except:
sys.exit(u'ERROR: can\'t obtain versions info. Please try again later.')
if not 'signature' in versions_info:
sys.exit(u'ERROR: the versions file is not signed or corrupted. Aborting.')
signature = versions_info['signature']
del versions_info['signature']
if not rsa_verify(json.dumps(versions_info, sort_keys=True).encode('utf-8'), signature, UPDATES_RSA_KEY):
sys.exit(u'ERROR: the versions file signature is invalid. Aborting.')
version = versions_info['versions'][versions_info['latest']]
try:
urlh = compat_urllib_request.urlopen(version['bin'][0])
newcontent = urlh.read()
urlh.close()
except (IOError, OSError) as err:
sys.exit('ERROR: unable to download latest version')
newcontent_hash = hashlib.sha256(newcontent).hexdigest()
if newcontent_hash != version['bin'][1]:
sys.exit(u'ERROR: the downloaded file hash does not match. Aborting.')
try:
with open(filename, 'wb') as outf:
outf.write(newcontent)
except (IOError, OSError) as err:
sys.exit('ERROR: unable to overwrite current version')
sys.stderr.write(u'Done! Now you can run youtube-dl.\n')
BIN
View File
Binary file not shown.
-544
View File
@@ -1,544 +0,0 @@
import math
import os
import re
import subprocess
import sys
import time
import traceback
if os.name == 'nt':
import ctypes
from .utils import *
class FileDownloader(object):
"""File Downloader class.
File downloader objects are the ones responsible of downloading the
actual video file and writing it to disk.
File downloaders accept a lot of parameters. In order not to saturate
the object constructor with arguments, it receives a dictionary of
options instead.
Available options:
verbose: Print additional info to stdout.
quiet: Do not print messages to stdout.
ratelimit: Download speed limit, in bytes/sec.
retries: Number of times to retry for HTTP error 5xx
buffersize: Size of download buffer in bytes.
noresizebuffer: Do not automatically resize the download buffer.
continuedl: Try to continue downloads if possible.
noprogress: Do not print the progress bar.
logtostderr: Log messages to stderr instead of stdout.
consoletitle: Display progress in console window's titlebar.
nopart: Do not use temporary .part files.
updatetime: Use the Last-modified header to set output file timestamps.
test: Download only first bytes to test the downloader.
min_filesize: Skip files smaller than this size
max_filesize: Skip files larger than this size
"""
params = None
def __init__(self, ydl, params):
"""Create a FileDownloader object with the given options."""
self.ydl = ydl
self._progress_hooks = []
self.params = params
@staticmethod
def format_bytes(bytes):
if bytes is None:
return 'N/A'
if type(bytes) is str:
bytes = float(bytes)
if bytes == 0.0:
exponent = 0
else:
exponent = int(math.log(bytes, 1024.0))
suffix = ['B','KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB'][exponent]
converted = float(bytes) / float(1024 ** exponent)
return '%.2f%s' % (converted, suffix)
@staticmethod
def calc_percent(byte_counter, data_len):
if data_len is None:
return '---.-%'
return '%6s' % ('%3.1f%%' % (float(byte_counter) / float(data_len) * 100.0))
@staticmethod
def calc_eta(start, now, total, current):
if total is None:
return '--:--'
dif = now - start
if current == 0 or dif < 0.001: # One millisecond
return '--:--'
rate = float(current) / dif
eta = int((float(total) - float(current)) / rate)
(eta_mins, eta_secs) = divmod(eta, 60)
if eta_mins > 99:
return '--:--'
return '%02d:%02d' % (eta_mins, eta_secs)
@staticmethod
def calc_speed(start, now, bytes):
dif = now - start
if bytes == 0 or dif < 0.001: # One millisecond
return '%10s' % '---b/s'
return '%10s' % ('%s/s' % FileDownloader.format_bytes(float(bytes) / dif))
@staticmethod
def best_block_size(elapsed_time, bytes):
new_min = max(bytes / 2.0, 1.0)
new_max = min(max(bytes * 2.0, 1.0), 4194304) # Do not surpass 4 MB
if elapsed_time < 0.001:
return int(new_max)
rate = bytes / elapsed_time
if rate > new_max:
return int(new_max)
if rate < new_min:
return int(new_min)
return int(rate)
@staticmethod
def parse_bytes(bytestr):
"""Parse a string indicating a byte quantity into an integer."""
matchobj = re.match(r'(?i)^(\d+(?:\.\d+)?)([kMGTPEZY]?)$', bytestr)
if matchobj is None:
return None
number = float(matchobj.group(1))
multiplier = 1024.0 ** 'bkmgtpezy'.index(matchobj.group(2).lower())
return int(round(number * multiplier))
def to_screen(self, *args, **kargs):
self.ydl.to_screen(*args, **kargs)
def to_stderr(self, message):
self.ydl.to_screen(message)
def to_cons_title(self, message):
"""Set console/terminal window title to message."""
if not self.params.get('consoletitle', False):
return
if os.name == 'nt' and ctypes.windll.kernel32.GetConsoleWindow():
# c_wchar_p() might not be necessary if `message` is
# already of type unicode()
ctypes.windll.kernel32.SetConsoleTitleW(ctypes.c_wchar_p(message))
elif 'TERM' in os.environ:
self.to_screen('\033]0;%s\007' % message, skip_eol=True)
def trouble(self, *args, **kargs):
self.ydl.trouble(*args, **kargs)
def report_warning(self, *args, **kargs):
self.ydl.report_warning(*args, **kargs)
def report_error(self, *args, **kargs):
self.ydl.error(*args, **kargs)
def slow_down(self, start_time, byte_counter):
"""Sleep if the download speed is over the rate limit."""
rate_limit = self.params.get('ratelimit', None)
if rate_limit is None or byte_counter == 0:
return
now = time.time()
elapsed = now - start_time
if elapsed <= 0.0:
return
speed = float(byte_counter) / elapsed
if speed > rate_limit:
time.sleep((byte_counter - rate_limit * (now - start_time)) / rate_limit)
def temp_name(self, filename):
"""Returns a temporary filename for the given filename."""
if self.params.get('nopart', False) or filename == u'-' or \
(os.path.exists(encodeFilename(filename)) and not os.path.isfile(encodeFilename(filename))):
return filename
return filename + u'.part'
def undo_temp_name(self, filename):
if filename.endswith(u'.part'):
return filename[:-len(u'.part')]
return filename
def try_rename(self, old_filename, new_filename):
try:
if old_filename == new_filename:
return
os.rename(encodeFilename(old_filename), encodeFilename(new_filename))
except (IOError, OSError) as err:
self.report_error(u'unable to rename file')
def try_utime(self, filename, last_modified_hdr):
"""Try to set the last-modified time of the given file."""
if last_modified_hdr is None:
return
if not os.path.isfile(encodeFilename(filename)):
return
timestr = last_modified_hdr
if timestr is None:
return
filetime = timeconvert(timestr)
if filetime is None:
return filetime
# Ignore obviously invalid dates
if filetime == 0:
return
try:
os.utime(filename, (time.time(), filetime))
except:
pass
return filetime
def report_destination(self, filename):
"""Report destination filename."""
self.to_screen(u'[download] Destination: ' + filename)
def report_progress(self, percent_str, data_len_str, speed_str, eta_str):
"""Report download progress."""
if self.params.get('noprogress', False):
return
clear_line = (u'\x1b[K' if sys.stderr.isatty() and os.name != 'nt' else u'')
if self.params.get('progress_with_newline', False):
self.to_screen(u'[download] %s of %s at %s ETA %s' %
(percent_str, data_len_str, speed_str, eta_str))
else:
self.to_screen(u'\r%s[download] %s of %s at %s ETA %s' %
(clear_line, percent_str, data_len_str, speed_str, eta_str), skip_eol=True)
self.to_cons_title(u'youtube-dl - %s of %s at %s ETA %s' %
(percent_str.strip(), data_len_str.strip(), speed_str.strip(), eta_str.strip()))
def report_resuming_byte(self, resume_len):
"""Report attempt to resume at given byte."""
self.to_screen(u'[download] Resuming download at byte %s' % resume_len)
def report_retry(self, count, retries):
"""Report retry in case of HTTP error 5xx"""
self.to_screen(u'[download] Got server HTTP error. Retrying (attempt %d of %d)...' % (count, retries))
def report_file_already_downloaded(self, file_name):
"""Report file has already been fully downloaded."""
try:
self.to_screen(u'[download] %s has already been downloaded' % file_name)
except (UnicodeEncodeError) as err:
self.to_screen(u'[download] The file has already been downloaded')
def report_unable_to_resume(self):
"""Report it was impossible to resume download."""
self.to_screen(u'[download] Unable to resume')
def report_finish(self):
"""Report download finished."""
if self.params.get('noprogress', False):
self.to_screen(u'[download] Download completed')
else:
self.to_screen(u'')
def _download_with_rtmpdump(self, filename, url, player_url, page_url, play_path, tc_url):
self.report_destination(filename)
tmpfilename = self.temp_name(filename)
# Check for rtmpdump first
try:
subprocess.call(['rtmpdump', '-h'], stdout=(open(os.path.devnull, 'w')), stderr=subprocess.STDOUT)
except (OSError, IOError):
self.report_error(u'RTMP download detected but "rtmpdump" could not be run')
return False
verbosity_option = '--verbose' if self.params.get('verbose', False) else '--quiet'
# Download using rtmpdump. rtmpdump returns exit code 2 when
# the connection was interrumpted and resuming appears to be
# possible. This is part of rtmpdump's normal usage, AFAIK.
basic_args = ['rtmpdump', verbosity_option, '-r', url, '-o', tmpfilename]
if player_url is not None:
basic_args += ['--swfVfy', player_url]
if page_url is not None:
basic_args += ['--pageUrl', page_url]
if play_path is not None:
basic_args += ['--playpath', play_path]
if tc_url is not None:
basic_args += ['--tcUrl', url]
args = basic_args + [[], ['--resume', '--skip', '1']][self.params.get('continuedl', False)]
if self.params.get('verbose', False):
try:
import pipes
shell_quote = lambda args: ' '.join(map(pipes.quote, args))
except ImportError:
shell_quote = repr
self.to_screen(u'[debug] rtmpdump command line: ' + shell_quote(args))
retval = subprocess.call(args)
while retval == 2 or retval == 1:
prevsize = os.path.getsize(encodeFilename(tmpfilename))
self.to_screen(u'\r[rtmpdump] %s bytes' % prevsize, skip_eol=True)
time.sleep(5.0) # This seems to be needed
retval = subprocess.call(basic_args + ['-e'] + [[], ['-k', '1']][retval == 1])
cursize = os.path.getsize(encodeFilename(tmpfilename))
if prevsize == cursize and retval == 1:
break
# Some rtmp streams seem abort after ~ 99.8%. Don't complain for those
if prevsize == cursize and retval == 2 and cursize > 1024:
self.to_screen(u'\r[rtmpdump] Could not download the whole video. This can happen for some advertisements.')
retval = 0
break
if retval == 0:
fsize = os.path.getsize(encodeFilename(tmpfilename))
self.to_screen(u'\r[rtmpdump] %s bytes' % fsize)
self.try_rename(tmpfilename, filename)
self._hook_progress({
'downloaded_bytes': fsize,
'total_bytes': fsize,
'filename': filename,
'status': 'finished',
})
return True
else:
self.to_stderr(u"\n")
self.report_error(u'rtmpdump exited with code %d' % retval)
return False
def _download_with_mplayer(self, filename, url):
self.report_destination(filename)
tmpfilename = self.temp_name(filename)
args = ['mplayer', '-really-quiet', '-vo', 'null', '-vc', 'dummy', '-dumpstream', '-dumpfile', tmpfilename, url]
# Check for mplayer first
try:
subprocess.call(['mplayer', '-h'], stdout=(open(os.path.devnull, 'w')), stderr=subprocess.STDOUT)
except (OSError, IOError):
self.report_error(u'MMS or RTSP download detected but "%s" could not be run' % args[0] )
return False
# Download using mplayer.
retval = subprocess.call(args)
if retval == 0:
fsize = os.path.getsize(encodeFilename(tmpfilename))
self.to_screen(u'\r[%s] %s bytes' % (args[0], fsize))
self.try_rename(tmpfilename, filename)
self._hook_progress({
'downloaded_bytes': fsize,
'total_bytes': fsize,
'filename': filename,
'status': 'finished',
})
return True
else:
self.to_stderr(u"\n")
self.report_error(u'mplayer exited with code %d' % retval)
return False
def _do_download(self, filename, info_dict):
url = info_dict['url']
# Check file already present
if self.params.get('continuedl', False) and os.path.isfile(encodeFilename(filename)) and not self.params.get('nopart', False):
self.report_file_already_downloaded(filename)
self._hook_progress({
'filename': filename,
'status': 'finished',
})
return True
# Attempt to download using rtmpdump
if url.startswith('rtmp'):
return self._download_with_rtmpdump(filename, url,
info_dict.get('player_url', None),
info_dict.get('page_url', None),
info_dict.get('play_path', None),
info_dict.get('tc_url', None))
# Attempt to download using mplayer
if url.startswith('mms') or url.startswith('rtsp'):
return self._download_with_mplayer(filename, url)
tmpfilename = self.temp_name(filename)
stream = None
# Do not include the Accept-Encoding header
headers = {'Youtubedl-no-compression': 'True'}
if 'user_agent' in info_dict:
headers['Youtubedl-user-agent'] = info_dict['user_agent']
basic_request = compat_urllib_request.Request(url, None, headers)
request = compat_urllib_request.Request(url, None, headers)
if self.params.get('test', False):
request.add_header('Range','bytes=0-10240')
# Establish possible resume length
if os.path.isfile(encodeFilename(tmpfilename)):
resume_len = os.path.getsize(encodeFilename(tmpfilename))
else:
resume_len = 0
open_mode = 'wb'
if resume_len != 0:
if self.params.get('continuedl', False):
self.report_resuming_byte(resume_len)
request.add_header('Range','bytes=%d-' % resume_len)
open_mode = 'ab'
else:
resume_len = 0
count = 0
retries = self.params.get('retries', 0)
while count <= retries:
# Establish connection
try:
if count == 0 and 'urlhandle' in info_dict:
data = info_dict['urlhandle']
data = compat_urllib_request.urlopen(request)
break
except (compat_urllib_error.HTTPError, ) as err:
if (err.code < 500 or err.code >= 600) and err.code != 416:
# Unexpected HTTP error
raise
elif err.code == 416:
# Unable to resume (requested range not satisfiable)
try:
# Open the connection again without the range header
data = compat_urllib_request.urlopen(basic_request)
content_length = data.info()['Content-Length']
except (compat_urllib_error.HTTPError, ) as err:
if err.code < 500 or err.code >= 600:
raise
else:
# Examine the reported length
if (content_length is not None and
(resume_len - 100 < int(content_length) < resume_len + 100)):
# The file had already been fully downloaded.
# Explanation to the above condition: in issue #175 it was revealed that
# YouTube sometimes adds or removes a few bytes from the end of the file,
# changing the file size slightly and causing problems for some users. So
# I decided to implement a suggested change and consider the file
# completely downloaded if the file size differs less than 100 bytes from
# the one in the hard drive.
self.report_file_already_downloaded(filename)
self.try_rename(tmpfilename, filename)
self._hook_progress({
'filename': filename,
'status': 'finished',
})
return True
else:
# The length does not match, we start the download over
self.report_unable_to_resume()
open_mode = 'wb'
break
# Retry
count += 1
if count <= retries:
self.report_retry(count, retries)
if count > retries:
self.report_error(u'giving up after %s retries' % retries)
return False
data_len = data.info().get('Content-length', None)
if data_len is not None:
data_len = int(data_len) + resume_len
min_data_len = self.params.get("min_filesize", None)
max_data_len = self.params.get("max_filesize", None)
if min_data_len is not None and data_len < min_data_len:
self.to_screen(u'\r[download] File is smaller than min-filesize (%s bytes < %s bytes). Aborting.' % (data_len, min_data_len))
return False
if max_data_len is not None and data_len > max_data_len:
self.to_screen(u'\r[download] File is larger than max-filesize (%s bytes > %s bytes). Aborting.' % (data_len, max_data_len))
return False
data_len_str = self.format_bytes(data_len)
byte_counter = 0 + resume_len
block_size = self.params.get('buffersize', 1024)
start = time.time()
while True:
# Download and write
before = time.time()
data_block = data.read(block_size)
after = time.time()
if len(data_block) == 0:
break
byte_counter += len(data_block)
# Open file just in time
if stream is None:
try:
(stream, tmpfilename) = sanitize_open(tmpfilename, open_mode)
assert stream is not None
filename = self.undo_temp_name(tmpfilename)
self.report_destination(filename)
except (OSError, IOError) as err:
self.report_error(u'unable to open for writing: %s' % str(err))
return False
try:
stream.write(data_block)
except (IOError, OSError) as err:
self.to_stderr(u"\n")
self.report_error(u'unable to write data: %s' % str(err))
return False
if not self.params.get('noresizebuffer', False):
block_size = self.best_block_size(after - before, len(data_block))
# Progress message
speed_str = self.calc_speed(start, time.time(), byte_counter - resume_len)
if data_len is None:
self.report_progress('Unknown %', data_len_str, speed_str, 'Unknown ETA')
else:
percent_str = self.calc_percent(byte_counter, data_len)
eta_str = self.calc_eta(start, time.time(), data_len - resume_len, byte_counter - resume_len)
self.report_progress(percent_str, data_len_str, speed_str, eta_str)
self._hook_progress({
'downloaded_bytes': byte_counter,
'total_bytes': data_len,
'tmpfilename': tmpfilename,
'filename': filename,
'status': 'downloading',
})
# Apply rate limit
self.slow_down(start, byte_counter - resume_len)
if stream is None:
self.to_stderr(u"\n")
self.report_error(u'Did not get any data blocks')
return False
stream.close()
self.report_finish()
if data_len is not None and byte_counter != data_len:
raise ContentTooShortError(byte_counter, int(data_len))
self.try_rename(tmpfilename, filename)
# Update file modification time
if self.params.get('updatetime', True):
info_dict['filetime'] = self.try_utime(filename, data.info().get('last-modified', None))
self._hook_progress({
'downloaded_bytes': byte_counter,
'total_bytes': byte_counter,
'filename': filename,
'status': 'finished',
})
return True
def _hook_progress(self, status):
for ph in self._progress_hooks:
ph(status)
def add_progress_hook(self, ph):
""" ph gets called on download progress, with a dictionary with the entries
* filename: The final filename
* status: One of "downloading" and "finished"
It can also have some of the following entries:
* downloaded_bytes: Bytes on disks
* total_bytes: Total bytes, None if unknown
* tmpfilename: The filename we're currently writing to
Hooks are guaranteed to be called at least once (with status "finished")
if the download is successful.
"""
self._progress_hooks.append(ph)
-4
View File
@@ -1,4 +0,0 @@
# Legacy file for backwards compatibility, use youtube_dl.extractor instead!
from .extractor.common import InfoExtractor, SearchInfoExtractor
from .extractor import gen_extractors, get_info_extractor
-233
View File
@@ -1,233 +0,0 @@
import os
import subprocess
import sys
import time
from .utils import *
class PostProcessor(object):
"""Post Processor class.
PostProcessor objects can be added to downloaders with their
add_post_processor() method. When the downloader has finished a
successful download, it will take its internal chain of PostProcessors
and start calling the run() method on each one of them, first with
an initial argument and then with the returned value of the previous
PostProcessor.
The chain will be stopped if one of them ever returns None or the end
of the chain is reached.
PostProcessor objects follow a "mutual registration" process similar
to InfoExtractor objects.
"""
_downloader = None
def __init__(self, downloader=None):
self._downloader = downloader
def set_downloader(self, downloader):
"""Sets the downloader for this PP."""
self._downloader = downloader
def run(self, information):
"""Run the PostProcessor.
The "information" argument is a dictionary like the ones
composed by InfoExtractors. The only difference is that this
one has an extra field called "filepath" that points to the
downloaded file.
This method returns a tuple, the first element of which describes
whether the original file should be kept (i.e. not deleted - None for
no preference), and the second of which is the updated information.
In addition, this method may raise a PostProcessingError
exception if post processing fails.
"""
return None, information # by default, keep file and do nothing
class FFmpegPostProcessorError(PostProcessingError):
pass
class AudioConversionError(PostProcessingError):
pass
class FFmpegPostProcessor(PostProcessor):
def __init__(self,downloader=None):
PostProcessor.__init__(self, downloader)
self._exes = self.detect_executables()
@staticmethod
def detect_executables():
def executable(exe):
try:
subprocess.Popen([exe, '-version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
except OSError:
return False
return exe
programs = ['avprobe', 'avconv', 'ffmpeg', 'ffprobe']
return dict((program, executable(program)) for program in programs)
def run_ffmpeg(self, path, out_path, opts):
if not self._exes['ffmpeg'] and not self._exes['avconv']:
raise FFmpegPostProcessorError(u'ffmpeg or avconv not found. Please install one.')
cmd = ([self._exes['avconv'] or self._exes['ffmpeg'], '-y', '-i', encodeFilename(path)]
+ opts +
[encodeFilename(self._ffmpeg_filename_argument(out_path))])
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout,stderr = p.communicate()
if p.returncode != 0:
stderr = stderr.decode('utf-8', 'replace')
msg = stderr.strip().split('\n')[-1]
raise FFmpegPostProcessorError(msg)
def _ffmpeg_filename_argument(self, fn):
# ffmpeg broke --, see https://ffmpeg.org/trac/ffmpeg/ticket/2127 for details
if fn.startswith(u'-'):
return u'./' + fn
return fn
class FFmpegExtractAudioPP(FFmpegPostProcessor):
def __init__(self, downloader=None, preferredcodec=None, preferredquality=None, nopostoverwrites=False):
FFmpegPostProcessor.__init__(self, downloader)
if preferredcodec is None:
preferredcodec = 'best'
self._preferredcodec = preferredcodec
self._preferredquality = preferredquality
self._nopostoverwrites = nopostoverwrites
def get_audio_codec(self, path):
if not self._exes['ffprobe'] and not self._exes['avprobe']: return None
try:
cmd = [self._exes['avprobe'] or self._exes['ffprobe'], '-show_streams', encodeFilename(self._ffmpeg_filename_argument(path))]
handle = subprocess.Popen(cmd, stderr=compat_subprocess_get_DEVNULL(), stdout=subprocess.PIPE)
output = handle.communicate()[0]
if handle.wait() != 0:
return None
except (IOError, OSError):
return None
audio_codec = None
for line in output.decode('ascii', 'ignore').split('\n'):
if line.startswith('codec_name='):
audio_codec = line.split('=')[1].strip()
elif line.strip() == 'codec_type=audio' and audio_codec is not None:
return audio_codec
return None
def run_ffmpeg(self, path, out_path, codec, more_opts):
if not self._exes['ffmpeg'] and not self._exes['avconv']:
raise AudioConversionError('ffmpeg or avconv not found. Please install one.')
if codec is None:
acodec_opts = []
else:
acodec_opts = ['-acodec', codec]
opts = ['-vn'] + acodec_opts + more_opts
try:
FFmpegPostProcessor.run_ffmpeg(self, path, out_path, opts)
except FFmpegPostProcessorError as err:
raise AudioConversionError(err.message)
def run(self, information):
path = information['filepath']
filecodec = self.get_audio_codec(path)
if filecodec is None:
raise PostProcessingError(u'WARNING: unable to obtain file audio codec with ffprobe')
more_opts = []
if self._preferredcodec == 'best' or self._preferredcodec == filecodec or (self._preferredcodec == 'm4a' and filecodec == 'aac'):
if filecodec == 'aac' and self._preferredcodec in ['m4a', 'best']:
# Lossless, but in another container
acodec = 'copy'
extension = 'm4a'
more_opts = [self._exes['avconv'] and '-bsf:a' or '-absf', 'aac_adtstoasc']
elif filecodec in ['aac', 'mp3', 'vorbis', 'opus']:
# Lossless if possible
acodec = 'copy'
extension = filecodec
if filecodec == 'aac':
more_opts = ['-f', 'adts']
if filecodec == 'vorbis':
extension = 'ogg'
else:
# MP3 otherwise.
acodec = 'libmp3lame'
extension = 'mp3'
more_opts = []
if self._preferredquality is not None:
if int(self._preferredquality) < 10:
more_opts += [self._exes['avconv'] and '-q:a' or '-aq', self._preferredquality]
else:
more_opts += [self._exes['avconv'] and '-b:a' or '-ab', self._preferredquality + 'k']
else:
# We convert the audio (lossy)
acodec = {'mp3': 'libmp3lame', 'aac': 'aac', 'm4a': 'aac', 'opus': 'opus', 'vorbis': 'libvorbis', 'wav': None}[self._preferredcodec]
extension = self._preferredcodec
more_opts = []
if self._preferredquality is not None:
if int(self._preferredquality) < 10:
more_opts += [self._exes['avconv'] and '-q:a' or '-aq', self._preferredquality]
else:
more_opts += [self._exes['avconv'] and '-b:a' or '-ab', self._preferredquality + 'k']
if self._preferredcodec == 'aac':
more_opts += ['-f', 'adts']
if self._preferredcodec == 'm4a':
more_opts += [self._exes['avconv'] and '-bsf:a' or '-absf', 'aac_adtstoasc']
if self._preferredcodec == 'vorbis':
extension = 'ogg'
if self._preferredcodec == 'wav':
extension = 'wav'
more_opts += ['-f', 'wav']
prefix, sep, ext = path.rpartition(u'.') # not os.path.splitext, since the latter does not work on unicode in all setups
new_path = prefix + sep + extension
# If we download foo.mp3 and convert it to... foo.mp3, then don't delete foo.mp3, silly.
if new_path == path:
self._nopostoverwrites = True
try:
if self._nopostoverwrites and os.path.exists(encodeFilename(new_path)):
self._downloader.to_screen(u'[youtube] Post-process file %s exists, skipping' % new_path)
else:
self._downloader.to_screen(u'[' + (self._exes['avconv'] and 'avconv' or 'ffmpeg') + '] Destination: ' + new_path)
self.run_ffmpeg(path, new_path, acodec, more_opts)
except:
etype,e,tb = sys.exc_info()
if isinstance(e, AudioConversionError):
msg = u'audio conversion failed: ' + e.message
else:
msg = u'error running ' + (self._exes['avconv'] and 'avconv' or 'ffmpeg')
raise PostProcessingError(msg)
# Try to update the date time for extracted audio file.
if information.get('filetime') is not None:
try:
os.utime(encodeFilename(new_path), (time.time(), information['filetime']))
except:
self._downloader.to_stderr(u'WARNING: Cannot update utime of audio file')
information['filepath'] = new_path
return self._nopostoverwrites,information
class FFmpegVideoConvertor(FFmpegPostProcessor):
def __init__(self, downloader=None,preferedformat=None):
super(FFmpegVideoConvertor, self).__init__(downloader)
self._preferedformat=preferedformat
def run(self, information):
path = information['filepath']
prefix, sep, ext = path.rpartition(u'.')
outpath = prefix + sep + self._preferedformat
if information['ext'] == self._preferedformat:
self._downloader.to_screen(u'[ffmpeg] Not converting video file %s - already is in target format %s' % (path, self._preferedformat))
return True,information
self._downloader.to_screen(u'['+'ffmpeg'+'] Converting video from %s to %s, Destination: ' % (information['ext'], self._preferedformat) +outpath)
self.run_ffmpeg(path, outpath, [])
information['filepath'] = outpath
information['format'] = self._preferedformat
information['ext'] = self._preferedformat
return False,information
-595
View File
@@ -1,595 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import absolute_import
import io
import os
import re
import shutil
import socket
import sys
import time
import traceback
from .utils import *
from .extractor import get_info_extractor
from .FileDownloader import FileDownloader
class YoutubeDL(object):
"""YoutubeDL class.
YoutubeDL objects are the ones responsible of downloading the
actual video file and writing it to disk if the user has requested
it, among some other tasks. In most cases there should be one per
program. As, given a video URL, the downloader doesn't know how to
extract all the needed information, task that InfoExtractors do, it
has to pass the URL to one of them.
For this, YoutubeDL objects have a method that allows
InfoExtractors to be registered in a given order. When it is passed
a URL, the YoutubeDL object handles it to the first InfoExtractor it
finds that reports being able to handle it. The InfoExtractor extracts
all the information about the video or videos the URL refers to, and
YoutubeDL process the extracted information, possibly using a File
Downloader to download the video.
YoutubeDL objects accept a lot of parameters. In order not to saturate
the object constructor with arguments, it receives a dictionary of
options instead. These options are available through the params
attribute for the InfoExtractors to use. The YoutubeDL also
registers itself as the downloader in charge for the InfoExtractors
that are added to it, so this is a "mutual registration".
Available options:
username: Username for authentication purposes.
password: Password for authentication purposes.
usenetrc: Use netrc for authentication instead.
verbose: Print additional info to stdout.
quiet: Do not print messages to stdout.
forceurl: Force printing final URL.
forcetitle: Force printing title.
forceid: Force printing ID.
forcethumbnail: Force printing thumbnail URL.
forcedescription: Force printing description.
forcefilename: Force printing final filename.
simulate: Do not download the video files.
format: Video format code.
format_limit: Highest quality format to try.
outtmpl: Template for output names.
restrictfilenames: Do not allow "&" and spaces in file names
ignoreerrors: Do not stop on download errors.
nooverwrites: Prevent overwriting files.
playliststart: Playlist item to start at.
playlistend: Playlist item to end at.
matchtitle: Download only matching titles.
rejecttitle: Reject downloads for matching titles.
logtostderr: Log messages to stderr instead of stdout.
writedescription: Write the video description to a .description file
writeinfojson: Write the video description to a .info.json file
writethumbnail: Write the thumbnail image to a file
writesubtitles: Write the video subtitles to a file
allsubtitles: Downloads all the subtitles of the video
listsubtitles: Lists all available subtitles for the video
subtitlesformat: Subtitle format [sbv/srt] (default=srt)
subtitleslang: Language of the subtitles to download
keepvideo: Keep the video file after post-processing
daterange: A DateRange object, download only if the upload_date is in the range.
skip_download: Skip the actual download of the video file
The following parameters are not used by YoutubeDL itself, they are used by
the FileDownloader:
nopart, updatetime, buffersize, ratelimit, min_filesize, max_filesize, test,
noresizebuffer, retries, continuedl, noprogress, consoletitle
"""
params = None
_ies = []
_pps = []
_download_retcode = None
_num_downloads = None
_screen_file = None
def __init__(self, params):
"""Create a FileDownloader object with the given options."""
self._ies = []
self._pps = []
self._progress_hooks = []
self._download_retcode = 0
self._num_downloads = 0
self._screen_file = [sys.stdout, sys.stderr][params.get('logtostderr', False)]
self.params = params
self.fd = FileDownloader(self, self.params)
if '%(stitle)s' in self.params['outtmpl']:
self.report_warning(u'%(stitle)s is deprecated. Use the %(title)s and the --restrict-filenames flag(which also secures %(uploader)s et al) instead.')
def add_info_extractor(self, ie):
"""Add an InfoExtractor object to the end of the list."""
self._ies.append(ie)
ie.set_downloader(self)
def add_post_processor(self, pp):
"""Add a PostProcessor object to the end of the chain."""
self._pps.append(pp)
pp.set_downloader(self)
def to_screen(self, message, skip_eol=False):
"""Print message to stdout if not in quiet mode."""
assert type(message) == type(u'')
if not self.params.get('quiet', False):
terminator = [u'\n', u''][skip_eol]
output = message + terminator
if 'b' in getattr(self._screen_file, 'mode', '') or sys.version_info[0] < 3: # Python 2 lies about the mode of sys.stdout/sys.stderr
output = output.encode(preferredencoding(), 'ignore')
self._screen_file.write(output)
self._screen_file.flush()
def to_stderr(self, message):
"""Print message to stderr."""
assert type(message) == type(u'')
output = message + u'\n'
if 'b' in getattr(self._screen_file, 'mode', '') or sys.version_info[0] < 3: # Python 2 lies about the mode of sys.stdout/sys.stderr
output = output.encode(preferredencoding())
sys.stderr.write(output)
def fixed_template(self):
"""Checks if the output template is fixed."""
return (re.search(u'(?u)%\\(.+?\\)s', self.params['outtmpl']) is None)
def trouble(self, message=None, tb=None):
"""Determine action to take when a download problem appears.
Depending on if the downloader has been configured to ignore
download errors or not, this method may throw an exception or
not when errors are found, after printing the message.
tb, if given, is additional traceback information.
"""
if message is not None:
self.to_stderr(message)
if self.params.get('verbose'):
if tb is None:
if sys.exc_info()[0]: # if .trouble has been called from an except block
tb = u''
if hasattr(sys.exc_info()[1], 'exc_info') and sys.exc_info()[1].exc_info[0]:
tb += u''.join(traceback.format_exception(*sys.exc_info()[1].exc_info))
tb += compat_str(traceback.format_exc())
else:
tb_data = traceback.format_list(traceback.extract_stack())
tb = u''.join(tb_data)
self.to_stderr(tb)
if not self.params.get('ignoreerrors', False):
if sys.exc_info()[0] and hasattr(sys.exc_info()[1], 'exc_info') and sys.exc_info()[1].exc_info[0]:
exc_info = sys.exc_info()[1].exc_info
else:
exc_info = sys.exc_info()
raise DownloadError(message, exc_info)
self._download_retcode = 1
def report_warning(self, message):
'''
Print the message to stderr, it will be prefixed with 'WARNING:'
If stderr is a tty file the 'WARNING:' will be colored
'''
if sys.stderr.isatty() and os.name != 'nt':
_msg_header=u'\033[0;33mWARNING:\033[0m'
else:
_msg_header=u'WARNING:'
warning_message=u'%s %s' % (_msg_header,message)
self.to_stderr(warning_message)
def report_error(self, message, tb=None):
'''
Do the same as trouble, but prefixes the message with 'ERROR:', colored
in red if stderr is a tty file.
'''
if sys.stderr.isatty() and os.name != 'nt':
_msg_header = u'\033[0;31mERROR:\033[0m'
else:
_msg_header = u'ERROR:'
error_message = u'%s %s' % (_msg_header, message)
self.trouble(error_message, tb)
def slow_down(self, start_time, byte_counter):
"""Sleep if the download speed is over the rate limit."""
rate_limit = self.params.get('ratelimit', None)
if rate_limit is None or byte_counter == 0:
return
now = time.time()
elapsed = now - start_time
if elapsed <= 0.0:
return
speed = float(byte_counter) / elapsed
if speed > rate_limit:
time.sleep((byte_counter - rate_limit * (now - start_time)) / rate_limit)
def report_writedescription(self, descfn):
""" Report that the description file is being written """
self.to_screen(u'[info] Writing video description to: ' + descfn)
def report_writesubtitles(self, sub_filename):
""" Report that the subtitles file is being written """
self.to_screen(u'[info] Writing video subtitles to: ' + sub_filename)
def report_writeinfojson(self, infofn):
""" Report that the metadata file has been written """
self.to_screen(u'[info] Video description metadata as JSON to: ' + infofn)
def report_file_already_downloaded(self, file_name):
"""Report file has already been fully downloaded."""
try:
self.to_screen(u'[download] %s has already been downloaded' % file_name)
except (UnicodeEncodeError) as err:
self.to_screen(u'[download] The file has already been downloaded')
def increment_downloads(self):
"""Increment the ordinal that assigns a number to each file."""
self._num_downloads += 1
def prepare_filename(self, info_dict):
"""Generate the output filename."""
try:
template_dict = dict(info_dict)
template_dict['epoch'] = int(time.time())
autonumber_size = self.params.get('autonumber_size')
if autonumber_size is None:
autonumber_size = 5
autonumber_templ = u'%0' + str(autonumber_size) + u'd'
template_dict['autonumber'] = autonumber_templ % self._num_downloads
if template_dict['playlist_index'] is not None:
template_dict['playlist_index'] = u'%05d' % template_dict['playlist_index']
sanitize = lambda k,v: sanitize_filename(
u'NA' if v is None else compat_str(v),
restricted=self.params.get('restrictfilenames'),
is_id=(k==u'id'))
template_dict = dict((k, sanitize(k, v)) for k,v in template_dict.items())
filename = self.params['outtmpl'] % template_dict
return filename
except KeyError as err:
self.report_error(u'Erroneous output template')
return None
except ValueError as err:
self.report_error(u'Insufficient system charset ' + repr(preferredencoding()))
return None
def _match_entry(self, info_dict):
""" Returns None iff the file should be downloaded """
title = info_dict['title']
matchtitle = self.params.get('matchtitle', False)
if matchtitle:
if not re.search(matchtitle, title, re.IGNORECASE):
return u'[download] "' + title + '" title did not match pattern "' + matchtitle + '"'
rejecttitle = self.params.get('rejecttitle', False)
if rejecttitle:
if re.search(rejecttitle, title, re.IGNORECASE):
return u'"' + title + '" title matched reject pattern "' + rejecttitle + '"'
date = info_dict.get('upload_date', None)
if date is not None:
dateRange = self.params.get('daterange', DateRange())
if date not in dateRange:
return u'[download] %s upload date is not in range %s' % (date_from_str(date).isoformat(), dateRange)
return None
def extract_info(self, url, download=True, ie_key=None, extra_info={}):
'''
Returns a list with a dictionary for each video we find.
If 'download', also downloads the videos.
extra_info is a dict containing the extra values to add to each result
'''
if ie_key:
ie = get_info_extractor(ie_key)()
ie.set_downloader(self)
ies = [ie]
else:
ies = self._ies
for ie in ies:
if not ie.suitable(url):
continue
if not ie.working():
self.report_warning(u'The program functionality for this site has been marked as broken, '
u'and will probably not work.')
try:
ie_result = ie.extract(url)
if ie_result is None: # Finished already (backwards compatibility; listformats and friends should be moved here)
break
if isinstance(ie_result, list):
# Backwards compatibility: old IE result format
for result in ie_result:
result.update(extra_info)
ie_result = {
'_type': 'compat_list',
'entries': ie_result,
}
else:
ie_result.update(extra_info)
if 'extractor' not in ie_result:
ie_result['extractor'] = ie.IE_NAME
return self.process_ie_result(ie_result, download=download)
except ExtractorError as de: # An error we somewhat expected
self.report_error(compat_str(de), de.format_traceback())
break
except Exception as e:
if self.params.get('ignoreerrors', False):
self.report_error(compat_str(e), tb=compat_str(traceback.format_exc()))
break
else:
raise
else:
self.report_error(u'no suitable InfoExtractor: %s' % url)
def process_ie_result(self, ie_result, download=True, extra_info={}):
"""
Take the result of the ie(may be modified) and resolve all unresolved
references (URLs, playlist items).
It will also download the videos if 'download'.
Returns the resolved ie_result.
"""
result_type = ie_result.get('_type', 'video') # If not given we suppose it's a video, support the default old system
if result_type == 'video':
if 'playlist' not in ie_result:
# It isn't part of a playlist
ie_result['playlist'] = None
ie_result['playlist_index'] = None
if download:
self.process_info(ie_result)
return ie_result
elif result_type == 'url':
# We have to add extra_info to the results because it may be
# contained in a playlist
return self.extract_info(ie_result['url'],
download,
ie_key=ie_result.get('ie_key'),
extra_info=extra_info)
elif result_type == 'playlist':
# We process each entry in the playlist
playlist = ie_result.get('title', None) or ie_result.get('id', None)
self.to_screen(u'[download] Downloading playlist: %s' % playlist)
playlist_results = []
n_all_entries = len(ie_result['entries'])
playliststart = self.params.get('playliststart', 1) - 1
playlistend = self.params.get('playlistend', -1)
if playlistend == -1:
entries = ie_result['entries'][playliststart:]
else:
entries = ie_result['entries'][playliststart:playlistend]
n_entries = len(entries)
self.to_screen(u"[%s] playlist '%s': Collected %d video ids (downloading %d of them)" %
(ie_result['extractor'], playlist, n_all_entries, n_entries))
for i,entry in enumerate(entries,1):
self.to_screen(u'[download] Downloading video #%s of %s' %(i, n_entries))
extra = {
'playlist': playlist,
'playlist_index': i + playliststart,
}
if not 'extractor' in entry:
# We set the extractor, if it's an url it will be set then to
# the new extractor, but if it's already a video we must make
# sure it's present: see issue #877
entry['extractor'] = ie_result['extractor']
entry_result = self.process_ie_result(entry,
download=download,
extra_info=extra)
playlist_results.append(entry_result)
ie_result['entries'] = playlist_results
return ie_result
elif result_type == 'compat_list':
def _fixup(r):
r.setdefault('extractor', ie_result['extractor'])
return r
ie_result['entries'] = [
self.process_ie_result(_fixup(r), download=download)
for r in ie_result['entries']
]
return ie_result
else:
raise Exception('Invalid result type: %s' % result_type)
def process_info(self, info_dict):
"""Process a single resolved IE result."""
assert info_dict.get('_type', 'video') == 'video'
#We increment the download the download count here to match the previous behaviour.
self.increment_downloads()
info_dict['fulltitle'] = info_dict['title']
if len(info_dict['title']) > 200:
info_dict['title'] = info_dict['title'][:197] + u'...'
# Keep for backwards compatibility
info_dict['stitle'] = info_dict['title']
if not 'format' in info_dict:
info_dict['format'] = info_dict['ext']
reason = self._match_entry(info_dict)
if reason is not None:
self.to_screen(u'[download] ' + reason)
return
max_downloads = self.params.get('max_downloads')
if max_downloads is not None:
if self._num_downloads > int(max_downloads):
raise MaxDownloadsReached()
filename = self.prepare_filename(info_dict)
# Forced printings
if self.params.get('forcetitle', False):
compat_print(info_dict['title'])
if self.params.get('forceid', False):
compat_print(info_dict['id'])
if self.params.get('forceurl', False):
compat_print(info_dict['url'])
if self.params.get('forcethumbnail', False) and 'thumbnail' in info_dict:
compat_print(info_dict['thumbnail'])
if self.params.get('forcedescription', False) and 'description' in info_dict:
compat_print(info_dict['description'])
if self.params.get('forcefilename', False) and filename is not None:
compat_print(filename)
if self.params.get('forceformat', False):
compat_print(info_dict['format'])
# Do nothing else if in simulate mode
if self.params.get('simulate', False):
return
if filename is None:
return
try:
dn = os.path.dirname(encodeFilename(filename))
if dn != '' and not os.path.exists(dn):
os.makedirs(dn)
except (OSError, IOError) as err:
self.report_error(u'unable to create directory ' + compat_str(err))
return
if self.params.get('writedescription', False):
try:
descfn = filename + u'.description'
self.report_writedescription(descfn)
with io.open(encodeFilename(descfn), 'w', encoding='utf-8') as descfile:
descfile.write(info_dict['description'])
except (OSError, IOError):
self.report_error(u'Cannot write description file ' + descfn)
return
if self.params.get('writesubtitles', False) and 'subtitles' in info_dict and info_dict['subtitles']:
# subtitles download errors are already managed as troubles in relevant IE
# that way it will silently go on when used with unsupporting IE
subtitle = info_dict['subtitles'][0]
(sub_error, sub_lang, sub) = subtitle
sub_format = self.params.get('subtitlesformat')
if sub_error:
self.report_warning("Some error while getting the subtitles")
else:
try:
sub_filename = filename.rsplit('.', 1)[0] + u'.' + sub_lang + u'.' + sub_format
self.report_writesubtitles(sub_filename)
with io.open(encodeFilename(sub_filename), 'w', encoding='utf-8') as subfile:
subfile.write(sub)
except (OSError, IOError):
self.report_error(u'Cannot write subtitles file ' + descfn)
return
if self.params.get('allsubtitles', False) and 'subtitles' in info_dict and info_dict['subtitles']:
subtitles = info_dict['subtitles']
sub_format = self.params.get('subtitlesformat')
for subtitle in subtitles:
(sub_error, sub_lang, sub) = subtitle
if sub_error:
self.report_warning("Some error while getting the subtitles")
else:
try:
sub_filename = filename.rsplit('.', 1)[0] + u'.' + sub_lang + u'.' + sub_format
self.report_writesubtitles(sub_filename)
with io.open(encodeFilename(sub_filename), 'w', encoding='utf-8') as subfile:
subfile.write(sub)
except (OSError, IOError):
self.report_error(u'Cannot write subtitles file ' + descfn)
return
if self.params.get('writeinfojson', False):
infofn = filename + u'.info.json'
self.report_writeinfojson(infofn)
try:
json_info_dict = dict((k, v) for k,v in info_dict.items() if not k in ['urlhandle'])
write_json_file(json_info_dict, encodeFilename(infofn))
except (OSError, IOError):
self.report_error(u'Cannot write metadata to JSON file ' + infofn)
return
if self.params.get('writethumbnail', False):
if 'thumbnail' in info_dict:
thumb_format = info_dict['thumbnail'].rpartition(u'/')[2].rpartition(u'.')[2]
if not thumb_format:
thumb_format = 'jpg'
thumb_filename = filename.rpartition('.')[0] + u'.' + thumb_format
self.to_screen(u'[%s] %s: Downloading thumbnail ...' %
(info_dict['extractor'], info_dict['id']))
uf = compat_urllib_request.urlopen(info_dict['thumbnail'])
with open(thumb_filename, 'wb') as thumbf:
shutil.copyfileobj(uf, thumbf)
self.to_screen(u'[%s] %s: Writing thumbnail to: %s' %
(info_dict['extractor'], info_dict['id'], thumb_filename))
if not self.params.get('skip_download', False):
if self.params.get('nooverwrites', False) and os.path.exists(encodeFilename(filename)):
success = True
else:
try:
success = self.fd._do_download(filename, info_dict)
except (OSError, IOError) as err:
raise UnavailableVideoError()
except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
self.report_error(u'unable to download video data: %s' % str(err))
return
except (ContentTooShortError, ) as err:
self.report_error(u'content too short (expected %s bytes and served %s)' % (err.expected, err.downloaded))
return
if success:
try:
self.post_process(filename, info_dict)
except (PostProcessingError) as err:
self.report_error(u'postprocessing: %s' % str(err))
return
def download(self, url_list):
"""Download a given list of URLs."""
if len(url_list) > 1 and self.fixed_template():
raise SameFileError(self.params['outtmpl'])
for url in url_list:
try:
#It also downloads the videos
videos = self.extract_info(url)
except UnavailableVideoError:
self.report_error(u'unable to download video')
except MaxDownloadsReached:
self.to_screen(u'[info] Maximum number of downloaded files reached.')
raise
return self._download_retcode
def post_process(self, filename, ie_info):
"""Run all the postprocessors on the given file."""
info = dict(ie_info)
info['filepath'] = filename
keep_video = None
for pp in self._pps:
try:
keep_video_wish,new_info = pp.run(info)
if keep_video_wish is not None:
if keep_video_wish:
keep_video = keep_video_wish
elif keep_video is None:
# No clear decision yet, let IE decide
keep_video = keep_video_wish
except PostProcessingError as e:
self.to_stderr(u'ERROR: ' + e.msg)
if keep_video is False and not self.params.get('keepvideo', False):
try:
self.to_screen(u'Deleting original file %s (pass -k to keep)' % filename)
os.remove(encodeFilename(filename))
except (IOError, OSError):
self.report_warning(u'Unable to remove downloaded video file')
-610
View File
@@ -1,610 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__authors__ = (
'Ricardo Garcia Gonzalez',
'Danny Colligan',
'Benjamin Johnson',
'Vasyl\' Vavrychuk',
'Witold Baryluk',
'Paweł Paprota',
'Gergely Imreh',
'Rogério Brito',
'Philipp Hagemeister',
'Sören Schulze',
'Kevin Ngo',
'Ori Avtalion',
'shizeeg',
'Filippo Valsorda',
'Christian Albrecht',
'Dave Vasilevsky',
'Jaime Marquínez Ferrándiz',
'Jeff Crouse',
'Osama Khalid',
'Michael Walter',
'M. Yasoob Ullah Khalid',
'Julien Fraichard',
'Johny Mo Swag',
)
__license__ = 'Public Domain'
import codecs
import getpass
import optparse
import os
import re
import shlex
import socket
import subprocess
import sys
import warnings
import platform
from .utils import *
from .update import update_self
from .version import __version__
from .FileDownloader import *
from .extractor import gen_extractors
from .YoutubeDL import YoutubeDL
from .PostProcessor import *
def parseOpts(overrideArguments=None):
def _readOptions(filename_bytes):
try:
optionf = open(filename_bytes)
except IOError:
return [] # silently skip if file is not present
try:
res = []
for l in optionf:
res += shlex.split(l, comments=True)
finally:
optionf.close()
return res
def _format_option_string(option):
''' ('-o', '--option') -> -o, --format METAVAR'''
opts = []
if option._short_opts:
opts.append(option._short_opts[0])
if option._long_opts:
opts.append(option._long_opts[0])
if len(opts) > 1:
opts.insert(1, ', ')
if option.takes_value(): opts.append(' %s' % option.metavar)
return "".join(opts)
def _find_term_columns():
columns = os.environ.get('COLUMNS', None)
if columns:
return int(columns)
try:
sp = subprocess.Popen(['stty', 'size'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out,err = sp.communicate()
return int(out.split()[1])
except:
pass
return None
max_width = 80
max_help_position = 80
# No need to wrap help messages if we're on a wide console
columns = _find_term_columns()
if columns: max_width = columns
fmt = optparse.IndentedHelpFormatter(width=max_width, max_help_position=max_help_position)
fmt.format_option_strings = _format_option_string
kw = {
'version' : __version__,
'formatter' : fmt,
'usage' : '%prog [options] url [url...]',
'conflict_handler' : 'resolve',
}
parser = optparse.OptionParser(**kw)
# option groups
general = optparse.OptionGroup(parser, 'General Options')
selection = optparse.OptionGroup(parser, 'Video Selection')
authentication = optparse.OptionGroup(parser, 'Authentication Options')
video_format = optparse.OptionGroup(parser, 'Video Format Options')
postproc = optparse.OptionGroup(parser, 'Post-processing Options')
filesystem = optparse.OptionGroup(parser, 'Filesystem Options')
verbosity = optparse.OptionGroup(parser, 'Verbosity / Simulation Options')
general.add_option('-h', '--help',
action='help', help='print this help text and exit')
general.add_option('-v', '--version',
action='version', help='print program version and exit')
general.add_option('-U', '--update',
action='store_true', dest='update_self', help='update this program to latest version')
general.add_option('-i', '--ignore-errors',
action='store_true', dest='ignoreerrors', help='continue on download errors', default=False)
general.add_option('-r', '--rate-limit',
dest='ratelimit', metavar='LIMIT', help='maximum download rate (e.g. 50k or 44.6m)')
general.add_option('-R', '--retries',
dest='retries', metavar='RETRIES', help='number of retries (default is %default)', default=10)
general.add_option('--buffer-size',
dest='buffersize', metavar='SIZE', help='size of download buffer (e.g. 1024 or 16k) (default is %default)', default="1024")
general.add_option('--no-resize-buffer',
action='store_true', dest='noresizebuffer',
help='do not automatically adjust the buffer size. By default, the buffer size is automatically resized from an initial value of SIZE.', default=False)
general.add_option('--dump-user-agent',
action='store_true', dest='dump_user_agent',
help='display the current browser identification', default=False)
general.add_option('--user-agent',
dest='user_agent', help='specify a custom user agent', metavar='UA')
general.add_option('--referer',
dest='referer', help='specify a custom referer, use if the video access is restricted to one domain',
metavar='REF', default=None)
general.add_option('--list-extractors',
action='store_true', dest='list_extractors',
help='List all supported extractors and the URLs they would handle', default=False)
general.add_option('--proxy', dest='proxy', default=None, help='Use the specified HTTP/HTTPS proxy', metavar='URL')
general.add_option('--no-check-certificate', action='store_true', dest='no_check_certificate', default=False, help='Suppress HTTPS certificate validation.')
general.add_option('--test', action='store_true', dest='test', default=False, help=optparse.SUPPRESS_HELP)
selection.add_option('--playlist-start',
dest='playliststart', metavar='NUMBER', help='playlist video to start at (default is %default)', default=1)
selection.add_option('--playlist-end',
dest='playlistend', metavar='NUMBER', help='playlist video to end at (default is last)', default=-1)
selection.add_option('--match-title', dest='matchtitle', metavar='REGEX',help='download only matching titles (regex or caseless sub-string)')
selection.add_option('--reject-title', dest='rejecttitle', metavar='REGEX',help='skip download for matching titles (regex or caseless sub-string)')
selection.add_option('--max-downloads', metavar='NUMBER', dest='max_downloads', help='Abort after downloading NUMBER files', default=None)
selection.add_option('--min-filesize', metavar='SIZE', dest='min_filesize', help="Do not download any videos smaller than SIZE (e.g. 50k or 44.6m)", default=None)
selection.add_option('--max-filesize', metavar='SIZE', dest='max_filesize', help="Do not download any videos larger than SIZE (e.g. 50k or 44.6m)", default=None)
selection.add_option('--date', metavar='DATE', dest='date', help='download only videos uploaded in this date', default=None)
selection.add_option('--datebefore', metavar='DATE', dest='datebefore', help='download only videos uploaded before this date', default=None)
selection.add_option('--dateafter', metavar='DATE', dest='dateafter', help='download only videos uploaded after this date', default=None)
authentication.add_option('-u', '--username',
dest='username', metavar='USERNAME', help='account username')
authentication.add_option('-p', '--password',
dest='password', metavar='PASSWORD', help='account password')
authentication.add_option('-n', '--netrc',
action='store_true', dest='usenetrc', help='use .netrc authentication data', default=False)
video_format.add_option('-f', '--format',
action='store', dest='format', metavar='FORMAT',
help='video format code, specifiy the order of preference using slashes: "-f 22/17/18"')
video_format.add_option('--all-formats',
action='store_const', dest='format', help='download all available video formats', const='all')
video_format.add_option('--prefer-free-formats',
action='store_true', dest='prefer_free_formats', default=False, help='prefer free video formats unless a specific one is requested')
video_format.add_option('--max-quality',
action='store', dest='format_limit', metavar='FORMAT', help='highest quality format to download')
video_format.add_option('-F', '--list-formats',
action='store_true', dest='listformats', help='list all available formats (currently youtube only)')
video_format.add_option('--write-sub', '--write-srt',
action='store_true', dest='writesubtitles',
help='write subtitle file (currently youtube only)', default=False)
video_format.add_option('--only-sub',
action='store_true', dest='skip_download',
help='[deprecated] alias of --skip-download', default=False)
video_format.add_option('--all-subs',
action='store_true', dest='allsubtitles',
help='downloads all the available subtitles of the video (currently youtube only)', default=False)
video_format.add_option('--list-subs',
action='store_true', dest='listsubtitles',
help='lists all available subtitles for the video (currently youtube only)', default=False)
video_format.add_option('--sub-format',
action='store', dest='subtitlesformat', metavar='FORMAT',
help='subtitle format [srt/sbv] (default=srt) (currently youtube only)', default='srt')
video_format.add_option('--sub-lang', '--srt-lang',
action='store', dest='subtitleslang', metavar='LANG',
help='language of the subtitles to download (optional) use IETF language tags like \'en\'')
verbosity.add_option('-q', '--quiet',
action='store_true', dest='quiet', help='activates quiet mode', default=False)
verbosity.add_option('-s', '--simulate',
action='store_true', dest='simulate', help='do not download the video and do not write anything to disk', default=False)
verbosity.add_option('--skip-download',
action='store_true', dest='skip_download', help='do not download the video', default=False)
verbosity.add_option('-g', '--get-url',
action='store_true', dest='geturl', help='simulate, quiet but print URL', default=False)
verbosity.add_option('-e', '--get-title',
action='store_true', dest='gettitle', help='simulate, quiet but print title', default=False)
verbosity.add_option('--get-id',
action='store_true', dest='getid', help='simulate, quiet but print id', default=False)
verbosity.add_option('--get-thumbnail',
action='store_true', dest='getthumbnail',
help='simulate, quiet but print thumbnail URL', default=False)
verbosity.add_option('--get-description',
action='store_true', dest='getdescription',
help='simulate, quiet but print video description', default=False)
verbosity.add_option('--get-filename',
action='store_true', dest='getfilename',
help='simulate, quiet but print output filename', default=False)
verbosity.add_option('--get-format',
action='store_true', dest='getformat',
help='simulate, quiet but print output format', default=False)
verbosity.add_option('--newline',
action='store_true', dest='progress_with_newline', help='output progress bar as new lines', default=False)
verbosity.add_option('--no-progress',
action='store_true', dest='noprogress', help='do not print progress bar', default=False)
verbosity.add_option('--console-title',
action='store_true', dest='consoletitle',
help='display progress in console titlebar', default=False)
verbosity.add_option('-v', '--verbose',
action='store_true', dest='verbose', help='print various debugging information', default=False)
verbosity.add_option('--dump-intermediate-pages',
action='store_true', dest='dump_intermediate_pages', default=False,
help='print downloaded pages to debug problems(very verbose)')
filesystem.add_option('-t', '--title',
action='store_true', dest='usetitle', help='use title in file name (default)', default=False)
filesystem.add_option('--id',
action='store_true', dest='useid', help='use only video ID in file name', default=False)
filesystem.add_option('-l', '--literal',
action='store_true', dest='usetitle', help='[deprecated] alias of --title', default=False)
filesystem.add_option('-A', '--auto-number',
action='store_true', dest='autonumber',
help='number downloaded files starting from 00000', default=False)
filesystem.add_option('-o', '--output',
dest='outtmpl', metavar='TEMPLATE',
help=('output filename template. Use %(title)s to get the title, '
'%(uploader)s for the uploader name, %(uploader_id)s for the uploader nickname if different, '
'%(autonumber)s to get an automatically incremented number, '
'%(ext)s for the filename extension, %(upload_date)s for the upload date (YYYYMMDD), '
'%(extractor)s for the provider (youtube, metacafe, etc), '
'%(id)s for the video id , %(playlist)s for the playlist the video is in, '
'%(playlist_index)s for the position in the playlist and %% for a literal percent. '
'Use - to output to stdout. Can also be used to download to a different directory, '
'for example with -o \'/my/downloads/%(uploader)s/%(title)s-%(id)s.%(ext)s\' .'))
filesystem.add_option('--autonumber-size',
dest='autonumber_size', metavar='NUMBER',
help='Specifies the number of digits in %(autonumber)s when it is present in output filename template or --autonumber option is given')
filesystem.add_option('--restrict-filenames',
action='store_true', dest='restrictfilenames',
help='Restrict filenames to only ASCII characters, and avoid "&" and spaces in filenames', default=False)
filesystem.add_option('-a', '--batch-file',
dest='batchfile', metavar='FILE', help='file containing URLs to download (\'-\' for stdin)')
filesystem.add_option('-w', '--no-overwrites',
action='store_true', dest='nooverwrites', help='do not overwrite files', default=False)
filesystem.add_option('-c', '--continue',
action='store_true', dest='continue_dl', help='resume partially downloaded files', default=True)
filesystem.add_option('--no-continue',
action='store_false', dest='continue_dl',
help='do not resume partially downloaded files (restart from beginning)')
filesystem.add_option('--cookies',
dest='cookiefile', metavar='FILE', help='file to read cookies from and dump cookie jar in')
filesystem.add_option('--no-part',
action='store_true', dest='nopart', help='do not use .part files', default=False)
filesystem.add_option('--no-mtime',
action='store_false', dest='updatetime',
help='do not use the Last-modified header to set the file modification time', default=True)
filesystem.add_option('--write-description',
action='store_true', dest='writedescription',
help='write video description to a .description file', default=False)
filesystem.add_option('--write-info-json',
action='store_true', dest='writeinfojson',
help='write video metadata to a .info.json file', default=False)
filesystem.add_option('--write-thumbnail',
action='store_true', dest='writethumbnail',
help='write thumbnail image to disk', default=False)
postproc.add_option('-x', '--extract-audio', action='store_true', dest='extractaudio', default=False,
help='convert video files to audio-only files (requires ffmpeg or avconv and ffprobe or avprobe)')
postproc.add_option('--audio-format', metavar='FORMAT', dest='audioformat', default='best',
help='"best", "aac", "vorbis", "mp3", "m4a", "opus", or "wav"; best by default')
postproc.add_option('--audio-quality', metavar='QUALITY', dest='audioquality', default='5',
help='ffmpeg/avconv audio quality specification, insert a value between 0 (better) and 9 (worse) for VBR or a specific bitrate like 128K (default 5)')
postproc.add_option('--recode-video', metavar='FORMAT', dest='recodevideo', default=None,
help='Encode the video to another format if necessary (currently supported: mp4|flv|ogg|webm)')
postproc.add_option('-k', '--keep-video', action='store_true', dest='keepvideo', default=False,
help='keeps the video file on disk after the post-processing; the video is erased by default')
postproc.add_option('--no-post-overwrites', action='store_true', dest='nopostoverwrites', default=False,
help='do not overwrite post-processed files; the post-processed files are overwritten by default')
parser.add_option_group(general)
parser.add_option_group(selection)
parser.add_option_group(filesystem)
parser.add_option_group(verbosity)
parser.add_option_group(video_format)
parser.add_option_group(authentication)
parser.add_option_group(postproc)
if overrideArguments is not None:
opts, args = parser.parse_args(overrideArguments)
if opts.verbose:
sys.stderr.write(u'[debug] Override config: ' + repr(overrideArguments) + '\n')
else:
xdg_config_home = os.environ.get('XDG_CONFIG_HOME')
if xdg_config_home:
userConfFile = os.path.join(xdg_config_home, 'youtube-dl.conf')
else:
userConfFile = os.path.join(os.path.expanduser('~'), '.config', 'youtube-dl.conf')
systemConf = _readOptions('/etc/youtube-dl.conf')
userConf = _readOptions(userConfFile)
commandLineConf = sys.argv[1:]
argv = systemConf + userConf + commandLineConf
opts, args = parser.parse_args(argv)
if opts.verbose:
sys.stderr.write(u'[debug] System config: ' + repr(systemConf) + '\n')
sys.stderr.write(u'[debug] User config: ' + repr(userConf) + '\n')
sys.stderr.write(u'[debug] Command-line args: ' + repr(commandLineConf) + '\n')
return parser, opts, args
def _real_main(argv=None):
# Compatibility fixes for Windows
if sys.platform == 'win32':
# https://github.com/rg3/youtube-dl/issues/820
codecs.register(lambda name: codecs.lookup('utf-8') if name == 'cp65001' else None)
parser, opts, args = parseOpts(argv)
# Open appropriate CookieJar
if opts.cookiefile is None:
jar = compat_cookiejar.CookieJar()
else:
try:
jar = compat_cookiejar.MozillaCookieJar(opts.cookiefile)
if os.access(opts.cookiefile, os.R_OK):
jar.load()
except (IOError, OSError) as err:
if opts.verbose:
traceback.print_exc()
sys.stderr.write(u'ERROR: unable to open cookie file\n')
sys.exit(101)
# Set user agent
if opts.user_agent is not None:
std_headers['User-Agent'] = opts.user_agent
# Set referer
if opts.referer is not None:
std_headers['Referer'] = opts.referer
# Dump user agent
if opts.dump_user_agent:
compat_print(std_headers['User-Agent'])
sys.exit(0)
# Batch file verification
batchurls = []
if opts.batchfile is not None:
try:
if opts.batchfile == '-':
batchfd = sys.stdin
else:
batchfd = open(opts.batchfile, 'r')
batchurls = batchfd.readlines()
batchurls = [x.strip() for x in batchurls]
batchurls = [x for x in batchurls if len(x) > 0 and not re.search(r'^[#/;]', x)]
except IOError:
sys.exit(u'ERROR: batch file could not be read')
all_urls = batchurls + args
all_urls = [url.strip() for url in all_urls]
# General configuration
cookie_processor = compat_urllib_request.HTTPCookieProcessor(jar)
if opts.proxy is not None:
if opts.proxy == '':
proxies = {}
else:
proxies = {'http': opts.proxy, 'https': opts.proxy}
else:
proxies = compat_urllib_request.getproxies()
# Set HTTPS proxy to HTTP one if given (https://github.com/rg3/youtube-dl/issues/805)
if 'http' in proxies and 'https' not in proxies:
proxies['https'] = proxies['http']
proxy_handler = compat_urllib_request.ProxyHandler(proxies)
https_handler = make_HTTPS_handler(opts)
opener = compat_urllib_request.build_opener(https_handler, proxy_handler, cookie_processor, YoutubeDLHandler())
compat_urllib_request.install_opener(opener)
socket.setdefaulttimeout(300) # 5 minutes should be enough (famous last words)
extractors = gen_extractors()
if opts.list_extractors:
for ie in extractors:
compat_print(ie.IE_NAME + (' (CURRENTLY BROKEN)' if not ie._WORKING else ''))
matchedUrls = [url for url in all_urls if ie.suitable(url)]
all_urls = [url for url in all_urls if url not in matchedUrls]
for mu in matchedUrls:
compat_print(u' ' + mu)
sys.exit(0)
# Conflicting, missing and erroneous options
if opts.usenetrc and (opts.username is not None or opts.password is not None):
parser.error(u'using .netrc conflicts with giving username/password')
if opts.password is not None and opts.username is None:
sys.stderr.write(u'WARNING: account username missing\n')
if opts.outtmpl is not None and (opts.usetitle or opts.autonumber or opts.useid):
parser.error(u'using output template conflicts with using title, video ID or auto number')
if opts.usetitle and opts.useid:
parser.error(u'using title conflicts with using video ID')
if opts.username is not None and opts.password is None:
opts.password = getpass.getpass(u'Type account password and press return:')
if opts.ratelimit is not None:
numeric_limit = FileDownloader.parse_bytes(opts.ratelimit)
if numeric_limit is None:
parser.error(u'invalid rate limit specified')
opts.ratelimit = numeric_limit
if opts.min_filesize is not None:
numeric_limit = FileDownloader.parse_bytes(opts.min_filesize)
if numeric_limit is None:
parser.error(u'invalid min_filesize specified')
opts.min_filesize = numeric_limit
if opts.max_filesize is not None:
numeric_limit = FileDownloader.parse_bytes(opts.max_filesize)
if numeric_limit is None:
parser.error(u'invalid max_filesize specified')
opts.max_filesize = numeric_limit
if opts.retries is not None:
try:
opts.retries = int(opts.retries)
except (TypeError, ValueError) as err:
parser.error(u'invalid retry count specified')
if opts.buffersize is not None:
numeric_buffersize = FileDownloader.parse_bytes(opts.buffersize)
if numeric_buffersize is None:
parser.error(u'invalid buffer size specified')
opts.buffersize = numeric_buffersize
try:
opts.playliststart = int(opts.playliststart)
if opts.playliststart <= 0:
raise ValueError(u'Playlist start must be positive')
except (TypeError, ValueError) as err:
parser.error(u'invalid playlist start number specified')
try:
opts.playlistend = int(opts.playlistend)
if opts.playlistend != -1 and (opts.playlistend <= 0 or opts.playlistend < opts.playliststart):
raise ValueError(u'Playlist end must be greater than playlist start')
except (TypeError, ValueError) as err:
parser.error(u'invalid playlist end number specified')
if opts.extractaudio:
if opts.audioformat not in ['best', 'aac', 'mp3', 'm4a', 'opus', 'vorbis', 'wav']:
parser.error(u'invalid audio format specified')
if opts.audioquality:
opts.audioquality = opts.audioquality.strip('k').strip('K')
if not opts.audioquality.isdigit():
parser.error(u'invalid audio quality specified')
if opts.recodevideo is not None:
if opts.recodevideo not in ['mp4', 'flv', 'webm', 'ogg']:
parser.error(u'invalid video recode format specified')
if opts.date is not None:
date = DateRange.day(opts.date)
else:
date = DateRange(opts.dateafter, opts.datebefore)
if sys.version_info < (3,):
# In Python 2, sys.argv is a bytestring (also note http://bugs.python.org/issue2128 for Windows systems)
if opts.outtmpl is not None:
opts.outtmpl = opts.outtmpl.decode(preferredencoding())
outtmpl =((opts.outtmpl is not None and opts.outtmpl)
or (opts.format == '-1' and opts.usetitle and u'%(title)s-%(id)s-%(format)s.%(ext)s')
or (opts.format == '-1' and u'%(id)s-%(format)s.%(ext)s')
or (opts.usetitle and opts.autonumber and u'%(autonumber)s-%(title)s-%(id)s.%(ext)s')
or (opts.usetitle and u'%(title)s-%(id)s.%(ext)s')
or (opts.useid and u'%(id)s.%(ext)s')
or (opts.autonumber and u'%(autonumber)s-%(id)s.%(ext)s')
or u'%(title)s-%(id)s.%(ext)s')
# YoutubeDL
ydl = YoutubeDL({
'usenetrc': opts.usenetrc,
'username': opts.username,
'password': opts.password,
'quiet': (opts.quiet or opts.geturl or opts.gettitle or opts.getid or opts.getthumbnail or opts.getdescription or opts.getfilename or opts.getformat),
'forceurl': opts.geturl,
'forcetitle': opts.gettitle,
'forceid': opts.getid,
'forcethumbnail': opts.getthumbnail,
'forcedescription': opts.getdescription,
'forcefilename': opts.getfilename,
'forceformat': opts.getformat,
'simulate': opts.simulate,
'skip_download': (opts.skip_download or opts.simulate or opts.geturl or opts.gettitle or opts.getid or opts.getthumbnail or opts.getdescription or opts.getfilename or opts.getformat),
'format': opts.format,
'format_limit': opts.format_limit,
'listformats': opts.listformats,
'outtmpl': outtmpl,
'autonumber_size': opts.autonumber_size,
'restrictfilenames': opts.restrictfilenames,
'ignoreerrors': opts.ignoreerrors,
'ratelimit': opts.ratelimit,
'nooverwrites': opts.nooverwrites,
'retries': opts.retries,
'buffersize': opts.buffersize,
'noresizebuffer': opts.noresizebuffer,
'continuedl': opts.continue_dl,
'noprogress': opts.noprogress,
'progress_with_newline': opts.progress_with_newline,
'playliststart': opts.playliststart,
'playlistend': opts.playlistend,
'logtostderr': opts.outtmpl == '-',
'consoletitle': opts.consoletitle,
'nopart': opts.nopart,
'updatetime': opts.updatetime,
'writedescription': opts.writedescription,
'writeinfojson': opts.writeinfojson,
'writethumbnail': opts.writethumbnail,
'writesubtitles': opts.writesubtitles,
'allsubtitles': opts.allsubtitles,
'listsubtitles': opts.listsubtitles,
'subtitlesformat': opts.subtitlesformat,
'subtitleslang': opts.subtitleslang,
'matchtitle': decodeOption(opts.matchtitle),
'rejecttitle': decodeOption(opts.rejecttitle),
'max_downloads': opts.max_downloads,
'prefer_free_formats': opts.prefer_free_formats,
'verbose': opts.verbose,
'dump_intermediate_pages': opts.dump_intermediate_pages,
'test': opts.test,
'keepvideo': opts.keepvideo,
'min_filesize': opts.min_filesize,
'max_filesize': opts.max_filesize,
'daterange': date,
})
if opts.verbose:
ydl.to_screen(u'[debug] youtube-dl version ' + __version__)
try:
sp = subprocess.Popen(['git', 'rev-parse', '--short', 'HEAD'], stdout=subprocess.PIPE, stderr=subprocess.PIPE,
cwd=os.path.dirname(os.path.abspath(__file__)))
out, err = sp.communicate()
out = out.decode().strip()
if re.match('[0-9a-f]+', out):
ydl.to_screen(u'[debug] Git HEAD: ' + out)
except:
pass
ydl.to_screen(u'[debug] Python version %s - %s' %(platform.python_version(), platform.platform()))
ydl.to_screen(u'[debug] Proxy map: ' + str(proxy_handler.proxies))
for extractor in extractors:
ydl.add_info_extractor(extractor)
# PostProcessors
if opts.extractaudio:
ydl.add_post_processor(FFmpegExtractAudioPP(preferredcodec=opts.audioformat, preferredquality=opts.audioquality, nopostoverwrites=opts.nopostoverwrites))
if opts.recodevideo:
ydl.add_post_processor(FFmpegVideoConvertor(preferedformat=opts.recodevideo))
# Update version
if opts.update_self:
update_self(ydl.to_screen, opts.verbose, sys.argv[0])
# Maybe do nothing
if len(all_urls) < 1:
if not opts.update_self:
parser.error(u'you must provide at least one URL')
else:
sys.exit()
try:
retcode = ydl.download(all_urls)
except MaxDownloadsReached:
ydl.to_screen(u'--max-download limit reached, aborting.')
retcode = 101
# Dump cookie jar if requested
if opts.cookiefile is not None:
try:
jar.save()
except (IOError, OSError) as err:
sys.exit(u'ERROR: unable to save cookie jar')
sys.exit(retcode)
def main(argv=None):
try:
_real_main(argv)
except DownloadError:
sys.exit(1)
except SameFileError:
sys.exit(u'ERROR: fixed output name but more than one file to download')
except KeyboardInterrupt:
sys.exit(u'\nERROR: Interrupted by user')
-18
View File
@@ -1,18 +0,0 @@
#!/usr/bin/env python
# Execute with
# $ python youtube_dl/__main__.py (2.6+)
# $ python -m youtube_dl (2.7+)
import sys
if __package__ is None and not hasattr(sys, "frozen"):
# direct call of __main__.py
import os.path
path = os.path.realpath(os.path.abspath(__file__))
sys.path.append(os.path.dirname(os.path.dirname(path)))
import youtube_dl
if __name__ == '__main__':
youtube_dl.main()
-135
View File
@@ -1,135 +0,0 @@
from .ard import ARDIE
from .arte import ArteTvIE
from .bandcamp import BandcampIE
from .bliptv import BlipTVIE, BlipTVUserIE
from .breakcom import BreakIE
from .comedycentral import ComedyCentralIE
from .collegehumor import CollegeHumorIE
from .dailymotion import DailymotionIE
from .depositfiles import DepositFilesIE
from .eighttracks import EightTracksIE
from .escapist import EscapistIE
from .facebook import FacebookIE
from .flickr import FlickrIE
from .funnyordie import FunnyOrDieIE
from .gametrailers import GametrailersIE
from .generic import GenericIE
from .googleplus import GooglePlusIE
from .googlesearch import GoogleSearchIE
from .howcast import HowcastIE
from .hypem import HypemIE
from .ina import InaIE
from .infoq import InfoQIE
from .justintv import JustinTVIE
from .keek import KeekIE
from .liveleak import LiveLeakIE
from .metacafe import MetacafeIE
from .mixcloud import MixcloudIE
from .mtv import MTVIE
from .myspass import MySpassIE
from .myvideo import MyVideoIE
from .nba import NBAIE
from .statigram import StatigramIE
from .photobucket import PhotobucketIE
from .pornotube import PornotubeIE
from .rbmaradio import RBMARadioIE
from .redtube import RedTubeIE
from .soundcloud import SoundcloudIE, SoundcloudSetIE
from .spiegel import SpiegelIE
from .stanfordoc import StanfordOpenClassroomIE
from .steam import SteamIE
from .teamcoco import TeamcocoIE
from .ted import TEDIE
from .tumblr import TumblrIE
from .ustream import UstreamIE
from .vbox7 import Vbox7IE
from .vevo import VevoIE
from .vimeo import VimeoIE
from .vine import VineIE
from .worldstarhiphop import WorldStarHipHopIE
from .xnxx import XNXXIE
from .xhamster import XHamsterIE
from .xvideos import XVideosIE
from .yahoo import YahooIE, YahooSearchIE
from .youjizz import YouJizzIE
from .youku import YoukuIE
from .youporn import YouPornIE
from .youtube import YoutubeIE, YoutubePlaylistIE, YoutubeSearchIE, YoutubeUserIE, YoutubeChannelIE
from .zdf import ZDFIE
def gen_extractors():
""" Return a list of an instance of every supported extractor.
The order does matter; the first extractor matched is the one handling the URL.
"""
return [
YoutubePlaylistIE(),
YoutubeChannelIE(),
YoutubeUserIE(),
YoutubeSearchIE(),
YoutubeIE(),
MetacafeIE(),
DailymotionIE(),
GoogleSearchIE(),
PhotobucketIE(),
YahooIE(),
YahooSearchIE(),
DepositFilesIE(),
FacebookIE(),
BlipTVIE(),
BlipTVUserIE(),
VimeoIE(),
MyVideoIE(),
ComedyCentralIE(),
EscapistIE(),
CollegeHumorIE(),
XVideosIE(),
SoundcloudSetIE(),
SoundcloudIE(),
InfoQIE(),
MixcloudIE(),
StanfordOpenClassroomIE(),
MTVIE(),
YoukuIE(),
XNXXIE(),
YouJizzIE(),
PornotubeIE(),
YouPornIE(),
GooglePlusIE(),
ArteTvIE(),
NBAIE(),
WorldStarHipHopIE(),
JustinTVIE(),
FunnyOrDieIE(),
SteamIE(),
UstreamIE(),
RBMARadioIE(),
EightTracksIE(),
KeekIE(),
TEDIE(),
MySpassIE(),
SpiegelIE(),
LiveLeakIE(),
ARDIE(),
ZDFIE(),
TumblrIE(),
BandcampIE(),
RedTubeIE(),
InaIE(),
HowcastIE(),
VineIE(),
FlickrIE(),
TeamcocoIE(),
XHamsterIE(),
HypemIE(),
Vbox7IE(),
GametrailersIE(),
StatigramIE(),
BreakIE(),
VevoIE(),
GenericIE()
]
def get_info_extractor(ie_name):
"""Returns the info extractor class with the given ie_name"""
return globals()[ie_name+'IE']
-45
View File
@@ -1,45 +0,0 @@
import re
from .common import InfoExtractor
from ..utils import (
ExtractorError,
)
class ARDIE(InfoExtractor):
_VALID_URL = r'^(?:https?://)?(?:(?:www\.)?ardmediathek\.de|mediathek\.daserste\.de)/(?:.*/)(?P<video_id>[^/\?]+)(?:\?.*)?'
_TITLE = r'<h1(?: class="boxTopHeadline")?>(?P<title>.*)</h1>'
_MEDIA_STREAM = r'mediaCollection\.addMediaStream\((?P<media_type>\d+), (?P<quality>\d+), "(?P<rtmp_url>[^"]*)", "(?P<video_url>[^"]*)", "[^"]*"\)'
def _real_extract(self, url):
# determine video id from url
m = re.match(self._VALID_URL, url)
numid = re.search(r'documentId=([0-9]+)', url)
if numid:
video_id = numid.group(1)
else:
video_id = m.group('video_id')
# determine title and media streams from webpage
html = self._download_webpage(url, video_id)
title = re.search(self._TITLE, html).group('title')
streams = [m.groupdict() for m in re.finditer(self._MEDIA_STREAM, html)]
if not streams:
assert '"fsk"' in html
raise ExtractorError(u'This video is only available after 8:00 pm')
# choose default media type and highest quality for now
stream = max([s for s in streams if int(s["media_type"]) == 0],
key=lambda s: int(s["quality"]))
# there's two possibilities: RTMP stream or HTTP download
info = {'id': video_id, 'title': title, 'ext': 'mp4'}
if stream['rtmp_url']:
self.to_screen(u'RTMP download detected')
assert stream['video_url'].startswith('mp4:')
info["url"] = stream["rtmp_url"]
info["play_path"] = stream['video_url']
else:
assert stream["video_url"].endswith('.mp4')
info["url"] = stream["video_url"]
return [info]
-136
View File
@@ -1,136 +0,0 @@
import re
import socket
from .common import InfoExtractor
from ..utils import (
compat_http_client,
compat_str,
compat_urllib_error,
compat_urllib_parse,
compat_urllib_request,
ExtractorError,
unified_strdate,
)
class ArteTvIE(InfoExtractor):
"""arte.tv information extractor."""
_VALID_URL = r'(?:http://)?videos\.arte\.tv/(?:fr|de)/videos/.*'
_LIVE_URL = r'index-[0-9]+\.html$'
IE_NAME = u'arte.tv'
def fetch_webpage(self, url):
request = compat_urllib_request.Request(url)
try:
self.report_download_webpage(url)
webpage = compat_urllib_request.urlopen(request).read()
except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
raise ExtractorError(u'Unable to retrieve video webpage: %s' % compat_str(err))
except ValueError as err:
raise ExtractorError(u'Invalid URL: %s' % url)
return webpage
def grep_webpage(self, url, regex, regexFlags, matchTuples):
page = self.fetch_webpage(url)
mobj = re.search(regex, page, regexFlags)
info = {}
if mobj is None:
raise ExtractorError(u'Invalid URL: %s' % url)
for (i, key, err) in matchTuples:
if mobj.group(i) is None:
raise ExtractorError(err)
else:
info[key] = mobj.group(i)
return info
# TODO implement Live Stream
# def extractLiveStream(self, url):
# video_lang = url.split('/')[-4]
# info = self.grep_webpage(
# url,
# r'src="(.*?/videothek_js.*?\.js)',
# 0,
# [
# (1, 'url', u'Invalid URL: %s' % url)
# ]
# )
# http_host = url.split('/')[2]
# next_url = 'http://%s%s' % (http_host, compat_urllib_parse.unquote(info.get('url')))
# info = self.grep_webpage(
# next_url,
# r'(s_artestras_scst_geoFRDE_' + video_lang + '.*?)\'.*?' +
# '(http://.*?\.swf).*?' +
# '(rtmp://.*?)\'',
# re.DOTALL,
# [
# (1, 'path', u'could not extract video path: %s' % url),
# (2, 'player', u'could not extract video player: %s' % url),
# (3, 'url', u'could not extract video url: %s' % url)
# ]
# )
# video_url = u'%s/%s' % (info.get('url'), info.get('path'))
def extractPlus7Stream(self, url):
video_lang = url.split('/')[-3]
info = self.grep_webpage(
url,
r'param name="movie".*?videorefFileUrl=(http[^\'"&]*)',
0,
[
(1, 'url', u'Invalid URL: %s' % url)
]
)
next_url = compat_urllib_parse.unquote(info.get('url'))
info = self.grep_webpage(
next_url,
r'<video lang="%s" ref="(http[^\'"&]*)' % video_lang,
0,
[
(1, 'url', u'Could not find <video> tag: %s' % url)
]
)
next_url = compat_urllib_parse.unquote(info.get('url'))
info = self.grep_webpage(
next_url,
r'<video id="(.*?)".*?>.*?' +
'<name>(.*?)</name>.*?' +
'<dateVideo>(.*?)</dateVideo>.*?' +
'<url quality="hd">(.*?)</url>',
re.DOTALL,
[
(1, 'id', u'could not extract video id: %s' % url),
(2, 'title', u'could not extract video title: %s' % url),
(3, 'date', u'could not extract video date: %s' % url),
(4, 'url', u'could not extract video url: %s' % url)
]
)
return {
'id': info.get('id'),
'url': compat_urllib_parse.unquote(info.get('url')),
'uploader': u'arte.tv',
'upload_date': unified_strdate(info.get('date')),
'title': info.get('title').decode('utf-8'),
'ext': u'mp4',
'format': u'NA',
'player_url': None,
}
def _real_extract(self, url):
video_id = url.split('/')[-1]
self.report_extraction(video_id)
if re.search(self._LIVE_URL, video_id) is not None:
raise ExtractorError(u'Arte live streams are not yet supported, sorry')
# self.extractLiveStream(url)
# return
else:
info = self.extractPlus7Stream(url)
return [info]
-54
View File
@@ -1,54 +0,0 @@
import json
import re
from .common import InfoExtractor
from ..utils import (
ExtractorError,
)
class BandcampIE(InfoExtractor):
_VALID_URL = r'http://.*?\.bandcamp\.com/track/(?P<title>.*)'
def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url)
title = mobj.group('title')
webpage = self._download_webpage(url, title)
# We get the link to the free download page
m_download = re.search(r'freeDownloadPage: "(.*?)"', webpage)
if m_download is None:
raise ExtractorError(u'No free songs found')
download_link = m_download.group(1)
id = re.search(r'var TralbumData = {(.*?)id: (?P<id>\d*?)$',
webpage, re.MULTILINE|re.DOTALL).group('id')
download_webpage = self._download_webpage(download_link, id,
'Downloading free downloads page')
# We get the dictionary of the track from some javascrip code
info = re.search(r'items: (.*?),$',
download_webpage, re.MULTILINE).group(1)
info = json.loads(info)[0]
# We pick mp3-320 for now, until format selection can be easily implemented.
mp3_info = info[u'downloads'][u'mp3-320']
# If we try to use this url it says the link has expired
initial_url = mp3_info[u'url']
re_url = r'(?P<server>http://(.*?)\.bandcamp\.com)/download/track\?enc=mp3-320&fsig=(?P<fsig>.*?)&id=(?P<id>.*?)&ts=(?P<ts>.*)$'
m_url = re.match(re_url, initial_url)
#We build the url we will use to get the final track url
# This url is build in Bandcamp in the script download_bunde_*.js
request_url = '%s/statdownload/track?enc=mp3-320&fsig=%s&id=%s&ts=%s&.rand=665028774616&.vrs=1' % (m_url.group('server'), m_url.group('fsig'), id, m_url.group('ts'))
final_url_webpage = self._download_webpage(request_url, id, 'Requesting download url')
# If we could correctly generate the .rand field the url would be
#in the "download_url" key
final_url = re.search(r'"retry_url":"(.*?)"', final_url_webpage).group(1)
track_info = {'id':id,
'title' : info[u'title'],
'ext' : 'mp3',
'url' : final_url,
'thumbnail' : info[u'thumb_url'],
'uploader' : info[u'artist']
}
return [track_info]
-177
View File
@@ -1,177 +0,0 @@
import datetime
import json
import os
import re
import socket
from .common import InfoExtractor
from ..utils import (
compat_http_client,
compat_parse_qs,
compat_str,
compat_urllib_error,
compat_urllib_parse_urlparse,
compat_urllib_request,
ExtractorError,
unescapeHTML,
)
class BlipTVIE(InfoExtractor):
"""Information extractor for blip.tv"""
_VALID_URL = r'^(?:https?://)?(?:\w+\.)?blip\.tv/((.+/)|(play/)|(api\.swf#))(.+)$'
_URL_EXT = r'^.*\.([a-z0-9]+)$'
IE_NAME = u'blip.tv'
def report_direct_download(self, title):
"""Report information extraction."""
self.to_screen(u'%s: Direct download detected' % title)
def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url)
if mobj is None:
raise ExtractorError(u'Invalid URL: %s' % url)
# See https://github.com/rg3/youtube-dl/issues/857
api_mobj = re.match(r'http://a\.blip\.tv/api\.swf#(?P<video_id>[\d\w]+)', url)
if api_mobj is not None:
url = 'http://blip.tv/play/g_%s' % api_mobj.group('video_id')
urlp = compat_urllib_parse_urlparse(url)
if urlp.path.startswith('/play/'):
request = compat_urllib_request.Request(url)
response = compat_urllib_request.urlopen(request)
redirecturl = response.geturl()
rurlp = compat_urllib_parse_urlparse(redirecturl)
file_id = compat_parse_qs(rurlp.fragment)['file'][0].rpartition('/')[2]
url = 'http://blip.tv/a/a-' + file_id
return self._real_extract(url)
if '?' in url:
cchar = '&'
else:
cchar = '?'
json_url = url + cchar + 'skin=json&version=2&no_wrap=1'
request = compat_urllib_request.Request(json_url)
request.add_header('User-Agent', 'iTunes/10.6.1')
self.report_extraction(mobj.group(1))
info = None
try:
urlh = compat_urllib_request.urlopen(request)
if urlh.headers.get('Content-Type', '').startswith('video/'): # Direct download
basename = url.split('/')[-1]
title,ext = os.path.splitext(basename)
title = title.decode('UTF-8')
ext = ext.replace('.', '')
self.report_direct_download(title)
info = {
'id': title,
'url': url,
'uploader': None,
'upload_date': None,
'title': title,
'ext': ext,
'urlhandle': urlh
}
except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
raise ExtractorError(u'ERROR: unable to download video info webpage: %s' % compat_str(err))
if info is None: # Regular URL
try:
json_code_bytes = urlh.read()
json_code = json_code_bytes.decode('utf-8')
except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
raise ExtractorError(u'Unable to read video info webpage: %s' % compat_str(err))
try:
json_data = json.loads(json_code)
if 'Post' in json_data:
data = json_data['Post']
else:
data = json_data
upload_date = datetime.datetime.strptime(data['datestamp'], '%m-%d-%y %H:%M%p').strftime('%Y%m%d')
video_url = data['media']['url']
umobj = re.match(self._URL_EXT, video_url)
if umobj is None:
raise ValueError('Can not determine filename extension')
ext = umobj.group(1)
info = {
'id': data['item_id'],
'url': video_url,
'uploader': data['display_name'],
'upload_date': upload_date,
'title': data['title'],
'ext': ext,
'format': data['media']['mimeType'],
'thumbnail': data['thumbnailUrl'],
'description': data['description'],
'player_url': data['embedUrl'],
'user_agent': 'iTunes/10.6.1',
}
except (ValueError,KeyError) as err:
raise ExtractorError(u'Unable to parse video information: %s' % repr(err))
return [info]
class BlipTVUserIE(InfoExtractor):
"""Information Extractor for blip.tv users."""
_VALID_URL = r'(?:(?:(?:https?://)?(?:\w+\.)?blip\.tv/)|bliptvuser:)([^/]+)/*$'
_PAGE_SIZE = 12
IE_NAME = u'blip.tv:user'
def _real_extract(self, url):
# Extract username
mobj = re.match(self._VALID_URL, url)
if mobj is None:
raise ExtractorError(u'Invalid URL: %s' % url)
username = mobj.group(1)
page_base = 'http://m.blip.tv/pr/show_get_full_episode_list?users_id=%s&lite=0&esi=1'
page = self._download_webpage(url, username, u'Downloading user page')
mobj = re.search(r'data-users-id="([^"]+)"', page)
page_base = page_base % mobj.group(1)
# Download video ids using BlipTV Ajax calls. Result size per
# query is limited (currently to 12 videos) so we need to query
# page by page until there are no video ids - it means we got
# all of them.
video_ids = []
pagenum = 1
while True:
url = page_base + "&page=" + str(pagenum)
page = self._download_webpage(url, username,
u'Downloading video ids from page %d' % pagenum)
# Extract video identifiers
ids_in_page = []
for mobj in re.finditer(r'href="/([^"]+)"', page):
if mobj.group(1) not in ids_in_page:
ids_in_page.append(unescapeHTML(mobj.group(1)))
video_ids.extend(ids_in_page)
# A little optimization - if current page is not
# "full", ie. does not contain PAGE_SIZE video ids then
# we can assume that this page is the last one - there
# are no more ids on further pages - no need to query
# again.
if len(ids_in_page) < self._PAGE_SIZE:
break
pagenum += 1
urls = [u'http://blip.tv/%s' % video_id for video_id in video_ids]
url_entries = [self.url_result(url, 'BlipTV') for url in urls]
return [self.playlist_result(url_entries, playlist_title = username)]
-25
View File
@@ -1,25 +0,0 @@
import re
from .common import InfoExtractor
class BreakIE(InfoExtractor):
_VALID_URL = r'(?:http://)?(?:www\.)?break\.com/video/([^/]+)'
def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url)
video_id = mobj.group(1).split("-")[-1]
webpage = self._download_webpage(url, video_id)
video_url = re.search(r"videoPath: '(.+?)',",webpage).group(1)
key = re.search(r"icon: '(.+?)',",webpage).group(1)
final_url = str(video_url)+"?"+str(key)
thumbnail_url = re.search(r"thumbnailURL: '(.+?)'",webpage).group(1)
title = re.search(r"sVidTitle: '(.+)',",webpage).group(1)
ext = video_url.split('.')[-1]
return [{
'id': video_id,
'url': final_url,
'ext': ext,
'title': title,
'thumbnail': thumbnail_url,
}]
-74
View File
@@ -1,74 +0,0 @@
import re
import socket
import xml.etree.ElementTree
from .common import InfoExtractor
from ..utils import (
compat_http_client,
compat_str,
compat_urllib_error,
compat_urllib_parse_urlparse,
compat_urllib_request,
ExtractorError,
)
class CollegeHumorIE(InfoExtractor):
_WORKING = False
_VALID_URL = r'^(?:https?://)?(?:www\.)?collegehumor\.com/video/(?P<videoid>[0-9]+)/(?P<shorttitle>.*)$'
def report_manifest(self, video_id):
"""Report information extraction."""
self.to_screen(u'%s: Downloading XML manifest' % video_id)
def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url)
if mobj is None:
raise ExtractorError(u'Invalid URL: %s' % url)
video_id = mobj.group('videoid')
info = {
'id': video_id,
'uploader': None,
'upload_date': None,
}
self.report_extraction(video_id)
xmlUrl = 'http://www.collegehumor.com/moogaloop/video/' + video_id
try:
metaXml = compat_urllib_request.urlopen(xmlUrl).read()
except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
raise ExtractorError(u'Unable to download video info XML: %s' % compat_str(err))
mdoc = xml.etree.ElementTree.fromstring(metaXml)
try:
videoNode = mdoc.findall('./video')[0]
info['description'] = videoNode.findall('./description')[0].text
info['title'] = videoNode.findall('./caption')[0].text
info['thumbnail'] = videoNode.findall('./thumbnail')[0].text
manifest_url = videoNode.findall('./file')[0].text
except IndexError:
raise ExtractorError(u'Invalid metadata XML file')
manifest_url += '?hdcore=2.10.3'
self.report_manifest(video_id)
try:
manifestXml = compat_urllib_request.urlopen(manifest_url).read()
except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
raise ExtractorError(u'Unable to download video info XML: %s' % compat_str(err))
adoc = xml.etree.ElementTree.fromstring(manifestXml)
try:
media_node = adoc.findall('./{http://ns.adobe.com/f4m/1.0}media')[0]
node_id = media_node.attrib['url']
video_id = adoc.findall('./{http://ns.adobe.com/f4m/1.0}id')[0].text
except IndexError as err:
raise ExtractorError(u'Invalid manifest file')
url_pr = compat_urllib_parse_urlparse(manifest_url)
url = url_pr.scheme + '://' + url_pr.netloc + '/z' + video_id[:-2] + '/' + node_id + 'Seg1-Frag1'
info['url'] = url
info['ext'] = 'f4f'
return [info]
-179
View File
@@ -1,179 +0,0 @@
import re
import xml.etree.ElementTree
from .common import InfoExtractor
from ..utils import (
compat_str,
compat_urllib_parse,
ExtractorError,
unified_strdate,
)
class ComedyCentralIE(InfoExtractor):
"""Information extractor for The Daily Show and Colbert Report """
# urls can be abbreviations like :thedailyshow or :colbert
# urls for episodes like:
# or urls for clips like: http://www.thedailyshow.com/watch/mon-december-10-2012/any-given-gun-day
# or: http://www.colbertnation.com/the-colbert-report-videos/421667/november-29-2012/moon-shattering-news
# or: http://www.colbertnation.com/the-colbert-report-collections/422008/festival-of-lights/79524
_VALID_URL = r"""^(:(?P<shortname>tds|thedailyshow|cr|colbert|colbertnation|colbertreport)
|(https?://)?(www\.)?
(?P<showname>thedailyshow|colbertnation)\.com/
(full-episodes/(?P<episode>.*)|
(?P<clip>
(the-colbert-report-(videos|collections)/(?P<clipID>[0-9]+)/[^/]*/(?P<cntitle>.*?))
|(watch/(?P<date>[^/]*)/(?P<tdstitle>.*)))))
$"""
_available_formats = ['3500', '2200', '1700', '1200', '750', '400']
_video_extensions = {
'3500': 'mp4',
'2200': 'mp4',
'1700': 'mp4',
'1200': 'mp4',
'750': 'mp4',
'400': 'mp4',
}
_video_dimensions = {
'3500': '1280x720',
'2200': '960x540',
'1700': '768x432',
'1200': '640x360',
'750': '512x288',
'400': '384x216',
}
@classmethod
def suitable(cls, url):
"""Receives a URL and returns True if suitable for this IE."""
return re.match(cls._VALID_URL, url, re.VERBOSE) is not None
def _print_formats(self, formats):
print('Available formats:')
for x in formats:
print('%s\t:\t%s\t[%s]' %(x, self._video_extensions.get(x, 'mp4'), self._video_dimensions.get(x, '???')))
def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url, re.VERBOSE)
if mobj is None:
raise ExtractorError(u'Invalid URL: %s' % url)
if mobj.group('shortname'):
if mobj.group('shortname') in ('tds', 'thedailyshow'):
url = u'http://www.thedailyshow.com/full-episodes/'
else:
url = u'http://www.colbertnation.com/full-episodes/'
mobj = re.match(self._VALID_URL, url, re.VERBOSE)
assert mobj is not None
if mobj.group('clip'):
if mobj.group('showname') == 'thedailyshow':
epTitle = mobj.group('tdstitle')
else:
epTitle = mobj.group('cntitle')
dlNewest = False
else:
dlNewest = not mobj.group('episode')
if dlNewest:
epTitle = mobj.group('showname')
else:
epTitle = mobj.group('episode')
self.report_extraction(epTitle)
webpage,htmlHandle = self._download_webpage_handle(url, epTitle)
if dlNewest:
url = htmlHandle.geturl()
mobj = re.match(self._VALID_URL, url, re.VERBOSE)
if mobj is None:
raise ExtractorError(u'Invalid redirected URL: ' + url)
if mobj.group('episode') == '':
raise ExtractorError(u'Redirected URL is still not specific: ' + url)
epTitle = mobj.group('episode')
mMovieParams = re.findall('(?:<param name="movie" value="|var url = ")(http://media.mtvnservices.com/([^"]*(?:episode|video).*?:.*?))"', webpage)
if len(mMovieParams) == 0:
# The Colbert Report embeds the information in a without
# a URL prefix; so extract the alternate reference
# and then add the URL prefix manually.
altMovieParams = re.findall('data-mgid="([^"]*(?:episode|video).*?:.*?)"', webpage)
if len(altMovieParams) == 0:
raise ExtractorError(u'unable to find Flash URL in webpage ' + url)
else:
mMovieParams = [("http://media.mtvnservices.com/" + altMovieParams[0], altMovieParams[0])]
uri = mMovieParams[0][1]
indexUrl = 'http://shadow.comedycentral.com/feeds/video_player/mrss/?' + compat_urllib_parse.urlencode({'uri': uri})
indexXml = self._download_webpage(indexUrl, epTitle,
u'Downloading show index',
u'unable to download episode index')
results = []
idoc = xml.etree.ElementTree.fromstring(indexXml)
itemEls = idoc.findall('.//item')
for partNum,itemEl in enumerate(itemEls):
mediaId = itemEl.findall('./guid')[0].text
shortMediaId = mediaId.split(':')[-1]
showId = mediaId.split(':')[-2].replace('.com', '')
officialTitle = itemEl.findall('./title')[0].text
officialDate = unified_strdate(itemEl.findall('./pubDate')[0].text)
configUrl = ('http://www.comedycentral.com/global/feeds/entertainment/media/mediaGenEntertainment.jhtml?' +
compat_urllib_parse.urlencode({'uri': mediaId}))
configXml = self._download_webpage(configUrl, epTitle,
u'Downloading configuration for %s' % shortMediaId)
cdoc = xml.etree.ElementTree.fromstring(configXml)
turls = []
for rendition in cdoc.findall('.//rendition'):
finfo = (rendition.attrib['bitrate'], rendition.findall('./src')[0].text)
turls.append(finfo)
if len(turls) == 0:
self._downloader.report_error(u'unable to download ' + mediaId + ': No videos found')
continue
if self._downloader.params.get('listformats', None):
self._print_formats([i[0] for i in turls])
return
# For now, just pick the highest bitrate
format,rtmp_video_url = turls[-1]
# Get the format arg from the arg stream
req_format = self._downloader.params.get('format', None)
# Select format if we can find one
for f,v in turls:
if f == req_format:
format, rtmp_video_url = f, v
break
m = re.match(r'^rtmpe?://.*?/(?P<finalid>gsp.comedystor/.*)$', rtmp_video_url)
if not m:
raise ExtractorError(u'Cannot transform RTMP url')
base = 'http://mtvnmobile.vo.llnwd.net/kip0/_pxn=1+_pxI0=Ripod-h264+_pxL0=undefined+_pxM0=+_pxK=18639+_pxE=mp4/44620/mtvnorigin/'
video_url = base + m.group('finalid')
effTitle = showId + u'-' + epTitle + u' part ' + compat_str(partNum+1)
info = {
'id': shortMediaId,
'url': video_url,
'uploader': showId,
'upload_date': officialDate,
'title': effTitle,
'ext': 'mp4',
'format': format,
'thumbnail': None,
'description': officialTitle,
}
results.append(info)
return results
-264
View File
@@ -1,264 +0,0 @@
import base64
import os
import re
import socket
import sys
from ..utils import (
compat_http_client,
compat_urllib_error,
compat_urllib_request,
compat_str,
clean_html,
compiled_regex_type,
ExtractorError,
)
class InfoExtractor(object):
"""Information Extractor class.
Information extractors are the classes that, given a URL, extract
information about the video (or videos) the URL refers to. This
information includes the real video URL, the video title, author and
others. The information is stored in a dictionary which is then
passed to the FileDownloader. The FileDownloader processes this
information possibly downloading the video to the file system, among
other possible outcomes.
The dictionaries must include the following fields:
id: Video identifier.
url: Final video URL.
title: Video title, unescaped.
ext: Video filename extension.
The following fields are optional:
format: The video format, defaults to ext (used for --get-format)
thumbnail: Full URL to a video thumbnail image.
description: One-line video description.
uploader: Full name of the video uploader.
upload_date: Video upload date (YYYYMMDD).
uploader_id: Nickname or id of the video uploader.
location: Physical location of the video.
player_url: SWF Player URL (used for rtmpdump).
subtitles: The subtitle file contents.
urlhandle: [internal] The urlHandle to be used to download the file,
like returned by urllib.request.urlopen
The fields should all be Unicode strings.
Subclasses of this one should re-define the _real_initialize() and
_real_extract() methods and define a _VALID_URL regexp.
Probably, they should also be added to the list of extractors.
_real_extract() must return a *list* of information dictionaries as
described above.
Finally, the _WORKING attribute should be set to False for broken IEs
in order to warn the users and skip the tests.
"""
_ready = False
_downloader = None
_WORKING = True
def __init__(self, downloader=None):
"""Constructor. Receives an optional downloader."""
self._ready = False
self.set_downloader(downloader)
@classmethod
def suitable(cls, url):
"""Receives a URL and returns True if suitable for this IE."""
return re.match(cls._VALID_URL, url) is not None
@classmethod
def working(cls):
"""Getter method for _WORKING."""
return cls._WORKING
def initialize(self):
"""Initializes an instance (authentication, etc)."""
if not self._ready:
self._real_initialize()
self._ready = True
def extract(self, url):
"""Extracts URL information and returns it in list of dicts."""
self.initialize()
return self._real_extract(url)
def set_downloader(self, downloader):
"""Sets the downloader for this IE."""
self._downloader = downloader
def _real_initialize(self):
"""Real initialization process. Redefine in subclasses."""
pass
def _real_extract(self, url):
"""Real extraction process. Redefine in subclasses."""
pass
@property
def IE_NAME(self):
return type(self).__name__[:-2]
def _request_webpage(self, url_or_request, video_id, note=None, errnote=None):
""" Returns the response handle """
if note is None:
self.report_download_webpage(video_id)
elif note is not False:
self.to_screen(u'%s: %s' % (video_id, note))
try:
return compat_urllib_request.urlopen(url_or_request)
except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
if errnote is None:
errnote = u'Unable to download webpage'
raise ExtractorError(u'%s: %s' % (errnote, compat_str(err)), sys.exc_info()[2])
def _download_webpage_handle(self, url_or_request, video_id, note=None, errnote=None):
""" Returns a tuple (page content as string, URL handle) """
urlh = self._request_webpage(url_or_request, video_id, note, errnote)
content_type = urlh.headers.get('Content-Type', '')
m = re.match(r'[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+\s*;\s*charset=(.+)', content_type)
if m:
encoding = m.group(1)
else:
encoding = 'utf-8'
webpage_bytes = urlh.read()
if self._downloader.params.get('dump_intermediate_pages', False):
try:
url = url_or_request.get_full_url()
except AttributeError:
url = url_or_request
self.to_screen(u'Dumping request to ' + url)
dump = base64.b64encode(webpage_bytes).decode('ascii')
self._downloader.to_screen(dump)
content = webpage_bytes.decode(encoding, 'replace')
return (content, urlh)
def _download_webpage(self, url_or_request, video_id, note=None, errnote=None):
""" Returns the data of the page as a string """
return self._download_webpage_handle(url_or_request, video_id, note, errnote)[0]
def to_screen(self, msg):
"""Print msg to screen, prefixing it with '[ie_name]'"""
self._downloader.to_screen(u'[%s] %s' % (self.IE_NAME, msg))
def report_extraction(self, id_or_name):
"""Report information extraction."""
self.to_screen(u'%s: Extracting information' % id_or_name)
def report_download_webpage(self, video_id):
"""Report webpage download."""
self.to_screen(u'%s: Downloading webpage' % video_id)
def report_age_confirmation(self):
"""Report attempt to confirm age."""
self.to_screen(u'Confirming age')
#Methods for following #608
#They set the correct value of the '_type' key
def video_result(self, video_info):
"""Returns a video"""
video_info['_type'] = 'video'
return video_info
def url_result(self, url, ie=None):
"""Returns a url that points to a page that should be processed"""
#TODO: ie should be the class used for getting the info
video_info = {'_type': 'url',
'url': url,
'ie_key': ie}
return video_info
def playlist_result(self, entries, playlist_id=None, playlist_title=None):
"""Returns a playlist"""
video_info = {'_type': 'playlist',
'entries': entries}
if playlist_id:
video_info['id'] = playlist_id
if playlist_title:
video_info['title'] = playlist_title
return video_info
def _search_regex(self, pattern, string, name, default=None, fatal=True, flags=0):
"""
Perform a regex search on the given string, using a single or a list of
patterns returning the first matching group.
In case of failure return a default value or raise a WARNING or a
ExtractorError, depending on fatal, specifying the field name.
"""
if isinstance(pattern, (str, compat_str, compiled_regex_type)):
mobj = re.search(pattern, string, flags)
else:
for p in pattern:
mobj = re.search(p, string, flags)
if mobj: break
if sys.stderr.isatty() and os.name != 'nt':
_name = u'\033[0;34m%s\033[0m' % name
else:
_name = name
if mobj:
# return the first matching group
return next(g for g in mobj.groups() if g is not None)
elif default is not None:
return default
elif fatal:
raise ExtractorError(u'Unable to extract %s' % _name)
else:
self._downloader.report_warning(u'unable to extract %s; '
u'please report this issue on GitHub.' % _name)
return None
def _html_search_regex(self, pattern, string, name, default=None, fatal=True, flags=0):
"""
Like _search_regex, but strips HTML tags and unescapes entities.
"""
res = self._search_regex(pattern, string, name, default, fatal, flags)
if res:
return clean_html(res).strip()
else:
return res
class SearchInfoExtractor(InfoExtractor):
"""
Base class for paged search queries extractors.
They accept urls in the format _SEARCH_KEY(|all|[0-9]):{query}
Instances should define _SEARCH_KEY and _MAX_RESULTS.
"""
@classmethod
def _make_valid_url(cls):
return r'%s(?P<prefix>|[1-9][0-9]*|all):(?P<query>[\s\S]+)' % cls._SEARCH_KEY
@classmethod
def suitable(cls, url):
return re.match(cls._make_valid_url(), url) is not None
def _real_extract(self, query):
mobj = re.match(self._make_valid_url(), query)
if mobj is None:
raise ExtractorError(u'Invalid search query "%s"' % query)
prefix = mobj.group('prefix')
query = mobj.group('query')
if prefix == '':
return self._get_n_results(query, 1)
elif prefix == 'all':
return self._get_n_results(query, self._MAX_RESULTS)
else:
n = int(prefix)
if n <= 0:
raise ExtractorError(u'invalid download number %s for query "%s"' % (n, query))
elif n > self._MAX_RESULTS:
self._downloader.report_warning(u'%s returns max %i results (you requested %i)' % (self._SEARCH_KEY, self._MAX_RESULTS, n))
n = self._MAX_RESULTS
return self._get_n_results(query, n)
def _get_n_results(self, query, n):
"""Get a specified number of results for a query"""
raise NotImplementedError("This method must be implemented by sublclasses")
-77
View File
@@ -1,77 +0,0 @@
import re
from .common import InfoExtractor
from ..utils import (
compat_urllib_request,
compat_urllib_parse,
ExtractorError,
unescapeHTML,
)
class DailymotionIE(InfoExtractor):
"""Information Extractor for Dailymotion"""
_VALID_URL = r'(?i)(?:https?://)?(?:www\.)?dailymotion\.[a-z]{2,3}/video/([^/]+)'
IE_NAME = u'dailymotion'
def _real_extract(self, url):
# Extract id and simplified title from URL
mobj = re.match(self._VALID_URL, url)
video_id = mobj.group(1).split('_')[0].split('?')[0]
video_extension = 'mp4'
# Retrieve video webpage to extract further information
request = compat_urllib_request.Request(url)
request.add_header('Cookie', 'family_filter=off')
webpage = self._download_webpage(request, video_id)
# Extract URL, uploader and title from webpage
self.report_extraction(video_id)
mobj = re.search(r'\s*var flashvars = (.*)', webpage)
if mobj is None:
raise ExtractorError(u'Unable to extract media URL')
flashvars = compat_urllib_parse.unquote(mobj.group(1))
for key in ['hd1080URL', 'hd720URL', 'hqURL', 'sdURL', 'ldURL', 'video_url']:
if key in flashvars:
max_quality = key
self.to_screen(u'Using %s' % key)
break
else:
raise ExtractorError(u'Unable to extract video URL')
mobj = re.search(r'"' + max_quality + r'":"(.+?)"', flashvars)
if mobj is None:
raise ExtractorError(u'Unable to extract video URL')
video_url = compat_urllib_parse.unquote(mobj.group(1)).replace('\\/', '/')
# TODO: support choosing qualities
mobj = re.search(r'<meta property="og:title" content="(?P<title>[^"]*)" />', webpage)
if mobj is None:
raise ExtractorError(u'Unable to extract title')
video_title = unescapeHTML(mobj.group('title'))
video_uploader = None
video_uploader = self._search_regex([r'(?im)<span class="owner[^\"]+?">[^<]+?<a [^>]+?>([^<]+?)</a>',
# Looking for official user
r'<(?:span|a) .*?rel="author".*?>([^<]+?)</'],
webpage, 'video uploader')
video_upload_date = None
mobj = re.search(r'<div class="[^"]*uploaded_cont[^"]*" title="[^"]*">([0-9]{2})-([0-9]{2})-([0-9]{4})</div>', webpage)
if mobj is not None:
video_upload_date = mobj.group(3) + mobj.group(2) + mobj.group(1)
return [{
'id': video_id,
'url': video_url,
'uploader': video_uploader,
'upload_date': video_upload_date,
'title': video_title,
'ext': video_extension,
}]
-60
View File
@@ -1,60 +0,0 @@
import re
import os
import socket
from .common import InfoExtractor
from ..utils import (
compat_http_client,
compat_str,
compat_urllib_error,
compat_urllib_parse,
compat_urllib_request,
ExtractorError,
)
class DepositFilesIE(InfoExtractor):
"""Information extractor for depositfiles.com"""
_VALID_URL = r'(?:http://)?(?:\w+\.)?depositfiles\.com/(?:../(?#locale))?files/(.+)'
def _real_extract(self, url):
file_id = url.split('/')[-1]
# Rebuild url in english locale
url = 'http://depositfiles.com/en/files/' + file_id
# Retrieve file webpage with 'Free download' button pressed
free_download_indication = { 'gateway_result' : '1' }
request = compat_urllib_request.Request(url, compat_urllib_parse.urlencode(free_download_indication))
try:
self.report_download_webpage(file_id)
webpage = compat_urllib_request.urlopen(request).read()
except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
raise ExtractorError(u'Unable to retrieve file webpage: %s' % compat_str(err))
# Search for the real file URL
mobj = re.search(r'<form action="(http://fileshare.+?)"', webpage)
if (mobj is None) or (mobj.group(1) is None):
# Try to figure out reason of the error.
mobj = re.search(r'<strong>(Attention.*?)</strong>', webpage, re.DOTALL)
if (mobj is not None) and (mobj.group(1) is not None):
restriction_message = re.sub('\s+', ' ', mobj.group(1)).strip()
raise ExtractorError(u'%s' % restriction_message)
else:
raise ExtractorError(u'Unable to extract download URL from: %s' % url)
file_url = mobj.group(1)
file_extension = os.path.splitext(file_url)[1][1:]
# Search for file title
file_title = self._search_regex(r'<b title="(.*?)">', webpage, u'title')
return [{
'id': file_id.decode('utf-8'),
'url': file_url.decode('utf-8'),
'uploader': None,
'upload_date': None,
'title': file_title,
'ext': file_extension.decode('utf-8'),
}]
-51
View File
@@ -1,51 +0,0 @@
import itertools
import json
import random
import re
from .common import InfoExtractor
from ..utils import (
ExtractorError,
)
class EightTracksIE(InfoExtractor):
IE_NAME = '8tracks'
_VALID_URL = r'https?://8tracks.com/(?P<user>[^/]+)/(?P<id>[^/#]+)(?:#.*)?$'
def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url)
if mobj is None:
raise ExtractorError(u'Invalid URL: %s' % url)
playlist_id = mobj.group('id')
webpage = self._download_webpage(url, playlist_id)
json_like = self._search_regex(r"PAGE.mix = (.*?);\n", webpage, u'trax information', flags=re.DOTALL)
data = json.loads(json_like)
session = str(random.randint(0, 1000000000))
mix_id = data['id']
track_count = data['tracks_count']
first_url = 'http://8tracks.com/sets/%s/play?player=sm&mix_id=%s&format=jsonh' % (session, mix_id)
next_url = first_url
res = []
for i in itertools.count():
api_json = self._download_webpage(next_url, playlist_id,
note=u'Downloading song information %s/%s' % (str(i+1), track_count),
errnote=u'Failed to download song information')
api_data = json.loads(api_json)
track_data = api_data[u'set']['track']
info = {
'id': track_data['id'],
'url': track_data['track_file_stream_url'],
'title': track_data['performer'] + u' - ' + track_data['name'],
'raw_title': track_data['name'],
'uploader_id': data['user']['login'],
'ext': 'm4a',
}
res.append(info)
if api_data['set']['at_last_track']:
break
next_url = 'http://8tracks.com/sets/%s/next?player=sm&mix_id=%s&format=jsonh&track_id=%s' % (session, mix_id, track_data['id'])
return res
-68
View File
@@ -1,68 +0,0 @@
import json
import re
from .common import InfoExtractor
from ..utils import (
compat_str,
compat_urllib_parse,
ExtractorError,
)
class EscapistIE(InfoExtractor):
_VALID_URL = r'^(https?://)?(www\.)?escapistmagazine\.com/videos/view/(?P<showname>[^/]+)/(?P<episode>[^/?]+)[/?]?.*$'
def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url)
if mobj is None:
raise ExtractorError(u'Invalid URL: %s' % url)
showName = mobj.group('showname')
videoId = mobj.group('episode')
self.report_extraction(videoId)
webpage = self._download_webpage(url, videoId)
videoDesc = self._html_search_regex('<meta name="description" content="([^"]*)"',
webpage, u'description', fatal=False)
imgUrl = self._html_search_regex('<meta property="og:image" content="([^"]*)"',
webpage, u'thumbnail', fatal=False)
playerUrl = self._html_search_regex('<meta property="og:video" content="([^"]*)"',
webpage, u'player url')
title = self._html_search_regex('<meta name="title" content="([^"]*)"',
webpage, u'player url').split(' : ')[-1]
configUrl = self._search_regex('config=(.*)$', playerUrl, u'config url')
configUrl = compat_urllib_parse.unquote(configUrl)
configJSON = self._download_webpage(configUrl, videoId,
u'Downloading configuration',
u'unable to download configuration')
# Technically, it's JavaScript, not JSON
configJSON = configJSON.replace("'", '"')
try:
config = json.loads(configJSON)
except (ValueError,) as err:
raise ExtractorError(u'Invalid JSON in configuration file: ' + compat_str(err))
playlist = config['playlist']
videoUrl = playlist[1]['url']
info = {
'id': videoId,
'url': videoUrl,
'uploader': showName,
'upload_date': None,
'title': title,
'ext': 'mp4',
'thumbnail': imgUrl,
'description': videoDesc,
'player_url': playerUrl,
}
return [info]
-111
View File
@@ -1,111 +0,0 @@
import json
import netrc
import re
import socket
from .common import InfoExtractor
from ..utils import (
compat_http_client,
compat_str,
compat_urllib_error,
compat_urllib_parse,
compat_urllib_request,
ExtractorError,
)
class FacebookIE(InfoExtractor):
"""Information Extractor for Facebook"""
_VALID_URL = r'^(?:https?://)?(?:\w+\.)?facebook\.com/(?:video/video|photo)\.php\?(?:.*?)v=(?P<ID>\d+)(?:.*)'
_LOGIN_URL = 'https://login.facebook.com/login.php?m&next=http%3A%2F%2Fm.facebook.com%2Fhome.php&'
_NETRC_MACHINE = 'facebook'
IE_NAME = u'facebook'
def report_login(self):
"""Report attempt to log in."""
self.to_screen(u'Logging in')
def _real_initialize(self):
if self._downloader is None:
return
useremail = None
password = None
downloader_params = self._downloader.params
# Attempt to use provided username and password or .netrc data
if downloader_params.get('username', None) is not None:
useremail = downloader_params['username']
password = downloader_params['password']
elif downloader_params.get('usenetrc', False):
try:
info = netrc.netrc().authenticators(self._NETRC_MACHINE)
if info is not None:
useremail = info[0]
password = info[2]
else:
raise netrc.NetrcParseError('No authenticators for %s' % self._NETRC_MACHINE)
except (IOError, netrc.NetrcParseError) as err:
self._downloader.report_warning(u'parsing .netrc: %s' % compat_str(err))
return
if useremail is None:
return
# Log in
login_form = {
'email': useremail,
'pass': password,
'login': 'Log+In'
}
request = compat_urllib_request.Request(self._LOGIN_URL, compat_urllib_parse.urlencode(login_form))
try:
self.report_login()
login_results = compat_urllib_request.urlopen(request).read()
if re.search(r'<form(.*)name="login"(.*)</form>', login_results) is not None:
self._downloader.report_warning(u'unable to log in: bad username/password, or exceded login rate limit (~3/min). Check credentials or wait.')
return
except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
self._downloader.report_warning(u'unable to log in: %s' % compat_str(err))
return
def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url)
if mobj is None:
raise ExtractorError(u'Invalid URL: %s' % url)
video_id = mobj.group('ID')
url = 'https://www.facebook.com/video/video.php?v=%s' % video_id
webpage = self._download_webpage(url, video_id)
BEFORE = '{swf.addParam(param[0], param[1]);});\n'
AFTER = '.forEach(function(variable) {swf.addVariable(variable[0], variable[1]);});'
m = re.search(re.escape(BEFORE) + '(.*?)' + re.escape(AFTER), webpage)
if not m:
raise ExtractorError(u'Cannot parse data')
data = dict(json.loads(m.group(1)))
params_raw = compat_urllib_parse.unquote(data['params'])
params = json.loads(params_raw)
video_data = params['video_data'][0]
video_url = video_data.get('hd_src')
if not video_url:
video_url = video_data['sd_src']
if not video_url:
raise ExtractorError(u'Cannot find video URL')
video_duration = int(video_data['video_duration'])
thumbnail = video_data['thumbnail_src']
video_title = self._html_search_regex('<h2 class="uiHeaderTitle">([^<]+)</h2>',
webpage, u'title')
info = {
'id': video_id,
'title': video_title,
'url': video_url,
'ext': 'mp4',
'duration': video_duration,
'thumbnail': thumbnail,
}
return [info]
-57
View File
@@ -1,57 +0,0 @@
import re
from .common import InfoExtractor
from ..utils import (
ExtractorError,
unescapeHTML,
)
class FlickrIE(InfoExtractor):
"""Information Extractor for Flickr videos"""
_VALID_URL = r'(?:https?://)?(?:www\.)?flickr\.com/photos/(?P<uploader_id>[\w\-_@]+)/(?P<id>\d+).*'
def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url)
video_id = mobj.group('id')
video_uploader_id = mobj.group('uploader_id')
webpage_url = 'http://www.flickr.com/photos/' + video_uploader_id + '/' + video_id
webpage = self._download_webpage(webpage_url, video_id)
secret = self._search_regex(r"photo_secret: '(\w+)'", webpage, u'secret')
first_url = 'https://secure.flickr.com/apps/video/video_mtl_xml.gne?v=x&photo_id=' + video_id + '&secret=' + secret + '&bitrate=700&target=_self'
first_xml = self._download_webpage(first_url, video_id, 'Downloading first data webpage')
node_id = self._html_search_regex(r'<Item id="id">(\d+-\d+)</Item>',
first_xml, u'node_id')
second_url = 'https://secure.flickr.com/video_playlist.gne?node_id=' + node_id + '&tech=flash&mode=playlist&bitrate=700&secret=' + secret + '&rd=video.yahoo.com&noad=1'
second_xml = self._download_webpage(second_url, video_id, 'Downloading second data webpage')
self.report_extraction(video_id)
mobj = re.search(r'<STREAM APP="(.+?)" FULLPATH="(.+?)"', second_xml)
if mobj is None:
raise ExtractorError(u'Unable to extract video url')
video_url = mobj.group(1) + unescapeHTML(mobj.group(2))
video_title = self._html_search_regex(r'<meta property="og:title" content=(?:"([^"]+)"|\'([^\']+)\')',
webpage, u'video title')
video_description = self._html_search_regex(r'<meta property="og:description" content=(?:"([^"]+)"|\'([^\']+)\')',
webpage, u'description', fatal=False)
thumbnail = self._html_search_regex(r'<meta property="og:image" content=(?:"([^"]+)"|\'([^\']+)\')',
webpage, u'thumbnail', fatal=False)
return [{
'id': video_id,
'url': video_url,
'ext': 'mp4',
'title': video_title,
'description': video_description,
'thumbnail': thumbnail,
'uploader_id': video_uploader_id,
}]
-31
View File
@@ -1,31 +0,0 @@
import re
from .common import InfoExtractor
class FunnyOrDieIE(InfoExtractor):
_VALID_URL = r'^(?:https?://)?(?:www\.)?funnyordie\.com/videos/(?P<id>[0-9a-f]+)/.*$'
def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url)
video_id = mobj.group('id')
webpage = self._download_webpage(url, video_id)
video_url = self._html_search_regex(r'<video[^>]*>\s*<source[^>]*>\s*<source src="(?P<url>[^"]+)"',
webpage, u'video URL', flags=re.DOTALL)
title = self._html_search_regex((r"<h1 class='player_page_h1'.*?>(?P<title>.*?)</h1>",
r'<title>(?P<title>[^<]+?)</title>'), webpage, 'title', flags=re.DOTALL)
video_description = self._html_search_regex(r'<meta property="og:description" content="(?P<desc>.*?)"',
webpage, u'description', fatal=False, flags=re.DOTALL)
info = {
'id': video_id,
'url': video_url,
'ext': 'mp4',
'title': title,
'description': video_description,
}
return [info]
-59
View File
@@ -1,59 +0,0 @@
import re
from .common import InfoExtractor
from ..utils import (
compat_urllib_parse,
ExtractorError,
)
class GametrailersIE(InfoExtractor):
_VALID_URL = r'http://www.gametrailers.com/(?P<type>videos|reviews|full-episodes)/(?P<id>.*?)/(?P<title>.*)'
def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url)
if mobj is None:
raise ExtractorError(u'Invalid URL: %s' % url)
video_id = mobj.group('id')
video_type = mobj.group('type')
webpage = self._download_webpage(url, video_id)
if video_type == 'full-episodes':
mgid_re = r'data-video="(?P<mgid>mgid:.*?)"'
else:
mgid_re = r'data-contentId=\'(?P<mgid>mgid:.*?)\''
mgid = self._search_regex(mgid_re, webpage, u'mgid')
data = compat_urllib_parse.urlencode({'uri': mgid, 'acceptMethods': 'fms'})
info_page = self._download_webpage('http://www.gametrailers.com/feeds/mrss?' + data,
video_id, u'Downloading video info')
links_webpage = self._download_webpage('http://www.gametrailers.com/feeds/mediagen/?' + data,
video_id, u'Downloading video urls info')
self.report_extraction(video_id)
info_re = r'''<title><!\[CDATA\[(?P<title>.*?)\]\]></title>.*
<description><!\[CDATA\[(?P<description>.*?)\]\]></description>.*
<image>.*
<url>(?P<thumb>.*?)</url>.*
</image>'''
m_info = re.search(info_re, info_page, re.VERBOSE|re.DOTALL)
if m_info is None:
raise ExtractorError(u'Unable to extract video info')
video_title = m_info.group('title')
video_description = m_info.group('description')
video_thumb = m_info.group('thumb')
m_urls = list(re.finditer(r'<src>(?P<url>.*)</src>', links_webpage))
if m_urls is None or len(m_urls) == 0:
raise ExtractorError(u'Unable to extract video url')
# They are sorted from worst to best quality
video_url = m_urls[-1].group('url')
return {'url': video_url,
'id': video_id,
'title': video_title,
# Videos are actually flv not mp4
'ext': 'flv',
'thumbnail': video_thumb,
'description': video_description,
}
-151
View File
@@ -1,151 +0,0 @@
import os
import re
from .common import InfoExtractor
from ..utils import (
compat_urllib_error,
compat_urllib_parse,
compat_urllib_request,
ExtractorError,
)
class GenericIE(InfoExtractor):
"""Generic last-resort information extractor."""
_VALID_URL = r'.*'
IE_NAME = u'generic'
def report_download_webpage(self, video_id):
"""Report webpage download."""
if not self._downloader.params.get('test', False):
self._downloader.report_warning(u'Falling back on generic information extractor.')
super(GenericIE, self).report_download_webpage(video_id)
def report_following_redirect(self, new_url):
"""Report information extraction."""
self._downloader.to_screen(u'[redirect] Following redirect to %s' % new_url)
def _test_redirect(self, url):
"""Check if it is a redirect, like url shorteners, in case return the new url."""
class HeadRequest(compat_urllib_request.Request):
def get_method(self):
return "HEAD"
class HEADRedirectHandler(compat_urllib_request.HTTPRedirectHandler):
"""
Subclass the HTTPRedirectHandler to make it use our
HeadRequest also on the redirected URL
"""
def redirect_request(self, req, fp, code, msg, headers, newurl):
if code in (301, 302, 303, 307):
newurl = newurl.replace(' ', '%20')
newheaders = dict((k,v) for k,v in req.headers.items()
if k.lower() not in ("content-length", "content-type"))
return HeadRequest(newurl,
headers=newheaders,
origin_req_host=req.get_origin_req_host(),
unverifiable=True)
else:
raise compat_urllib_error.HTTPError(req.get_full_url(), code, msg, headers, fp)
class HTTPMethodFallback(compat_urllib_request.BaseHandler):
"""
Fallback to GET if HEAD is not allowed (405 HTTP error)
"""
def http_error_405(self, req, fp, code, msg, headers):
fp.read()
fp.close()
newheaders = dict((k,v) for k,v in req.headers.items()
if k.lower() not in ("content-length", "content-type"))
return self.parent.open(compat_urllib_request.Request(req.get_full_url(),
headers=newheaders,
origin_req_host=req.get_origin_req_host(),
unverifiable=True))
# Build our opener
opener = compat_urllib_request.OpenerDirector()
for handler in [compat_urllib_request.HTTPHandler, compat_urllib_request.HTTPDefaultErrorHandler,
HTTPMethodFallback, HEADRedirectHandler,
compat_urllib_request.HTTPErrorProcessor, compat_urllib_request.HTTPSHandler]:
opener.add_handler(handler())
response = opener.open(HeadRequest(url))
if response is None:
raise ExtractorError(u'Invalid URL protocol')
new_url = response.geturl()
if url == new_url:
return False
self.report_following_redirect(new_url)
return new_url
def _real_extract(self, url):
new_url = self._test_redirect(url)
if new_url: return [self.url_result(new_url)]
video_id = url.split('/')[-1]
try:
webpage = self._download_webpage(url, video_id)
except ValueError:
# since this is the last-resort InfoExtractor, if
# this error is thrown, it'll be thrown here
raise ExtractorError(u'Invalid URL: %s' % url)
self.report_extraction(video_id)
# Start with something easy: JW Player in SWFObject
mobj = re.search(r'flashvars: [\'"](?:.*&)?file=(http[^\'"&]*)', webpage)
if mobj is None:
# Broaden the search a little bit
mobj = re.search(r'[^A-Za-z0-9]?(?:file|source)=(http[^\'"&]*)', webpage)
if mobj is None:
# Broaden the search a little bit: JWPlayer JS loader
mobj = re.search(r'[^A-Za-z0-9]?file:\s*["\'](http[^\'"&]*)', webpage)
if mobj is None:
# Try to find twitter cards info
mobj = re.search(r'<meta (?:property|name)="twitter:player:stream" (?:content|value)="(.+?)"', webpage)
if mobj is None:
# We look for Open Graph info:
# We have to match any number spaces between elements, some sites try to align them (eg.: statigr.am)
m_video_type = re.search(r'<meta.*?property="og:video:type".*?content="video/(.*?)"', webpage)
# We only look in og:video if the MIME type is a video, don't try if it's a Flash player:
if m_video_type is not None:
mobj = re.search(r'<meta.*?property="og:video".*?content="(.*?)"', webpage)
if mobj is None:
raise ExtractorError(u'Invalid URL: %s' % url)
# It's possible that one of the regexes
# matched, but returned an empty group:
if mobj.group(1) is None:
raise ExtractorError(u'Invalid URL: %s' % url)
video_url = compat_urllib_parse.unquote(mobj.group(1))
video_id = os.path.basename(video_url)
# here's a fun little line of code for you:
video_extension = os.path.splitext(video_id)[1][1:]
video_id = os.path.splitext(video_id)[0]
# it's tempting to parse this further, but you would
# have to take into account all the variations like
# Video Title - Site Name
# Site Name | Video Title
# Video Title - Tagline | Site Name
# and so on and so forth; it's just not practical
video_title = self._html_search_regex(r'<title>(.*)</title>',
webpage, u'video title')
# video uploader is domain name
video_uploader = self._search_regex(r'(?:https?://)?([^/]*)/.*',
url, u'video uploader')
return [{
'id': video_id,
'url': video_url,
'uploader': video_uploader,
'upload_date': None,
'title': video_title,
'ext': video_extension,
}]
-82
View File
@@ -1,82 +0,0 @@
import datetime
import re
from .common import InfoExtractor
from ..utils import (
ExtractorError,
)
class GooglePlusIE(InfoExtractor):
"""Information extractor for plus.google.com."""
_VALID_URL = r'(?:https://)?plus\.google\.com/(?:[^/]+/)*?posts/(\w+)'
IE_NAME = u'plus.google'
def _real_extract(self, url):
# Extract id from URL
mobj = re.match(self._VALID_URL, url)
if mobj is None:
raise ExtractorError(u'Invalid URL: %s' % url)
post_url = mobj.group(0)
video_id = mobj.group(1)
video_extension = 'flv'
# Step 1, Retrieve post webpage to extract further information
webpage = self._download_webpage(post_url, video_id, u'Downloading entry webpage')
self.report_extraction(video_id)
# Extract update date
upload_date = self._html_search_regex('title="Timestamp">(.*?)</a>',
webpage, u'upload date', fatal=False)
if upload_date:
# Convert timestring to a format suitable for filename
upload_date = datetime.datetime.strptime(upload_date, "%Y-%m-%d")
upload_date = upload_date.strftime('%Y%m%d')
# Extract uploader
uploader = self._html_search_regex(r'rel\="author".*?>(.*?)</a>',
webpage, u'uploader', fatal=False)
# Extract title
# Get the first line for title
video_title = self._html_search_regex(r'<meta name\=\"Description\" content\=\"(.*?)[\n<"]',
webpage, 'title', default=u'NA')
# Step 2, Stimulate clicking the image box to launch video
video_page = self._search_regex('"(https\://plus\.google\.com/photos/.*?)",,"image/jpeg","video"\]',
webpage, u'video page URL')
webpage = self._download_webpage(video_page, video_id, u'Downloading video page')
# Extract video links on video page
"""Extract video links of all sizes"""
pattern = '\d+,\d+,(\d+),"(http\://redirector\.googlevideo\.com.*?)"'
mobj = re.findall(pattern, webpage)
if len(mobj) == 0:
raise ExtractorError(u'Unable to extract video links')
# Sort in resolution
links = sorted(mobj)
# Choose the lowest of the sort, i.e. highest resolution
video_url = links[-1]
# Only get the url. The resolution part in the tuple has no use anymore
video_url = video_url[-1]
# Treat escaped \u0026 style hex
try:
video_url = video_url.decode("unicode_escape")
except AttributeError: # Python 3
video_url = bytes(video_url, 'ascii').decode('unicode-escape')
return [{
'id': video_id,
'url': video_url,
'uploader': uploader,
'upload_date': upload_date,
'title': video_title,
'ext': video_extension,
}]
-39
View File
@@ -1,39 +0,0 @@
import itertools
import re
from .common import SearchInfoExtractor
from ..utils import (
compat_urllib_parse,
)
class GoogleSearchIE(SearchInfoExtractor):
"""Information Extractor for Google Video search queries."""
_MORE_PAGES_INDICATOR = r'id="pnnext" class="pn"'
_MAX_RESULTS = 1000
IE_NAME = u'video.google:search'
_SEARCH_KEY = 'gvsearch'
def _get_n_results(self, query, n):
"""Get a specified number of results for a query"""
res = {
'_type': 'playlist',
'id': query,
'entries': []
}
for pagenum in itertools.count(1):
result_url = u'http://www.google.com/search?tbm=vid&q=%s&start=%s&hl=en' % (compat_urllib_parse.quote_plus(query), pagenum*10)
webpage = self._download_webpage(result_url, u'gvsearch:' + query,
note='Downloading result page ' + str(pagenum))
for mobj in re.finditer(r'<h3 class="r"><a href="([^"]+)"', webpage):
e = {
'_type': 'url',
'url': mobj.group(1)
}
res['entries'].append(e)
if (pagenum * 10 > n) or not re.search(self._MORE_PAGES_INDICATOR, webpage):
return res
-37
View File
@@ -1,37 +0,0 @@
import re
from .common import InfoExtractor
class HowcastIE(InfoExtractor):
_VALID_URL = r'(?:https?://)?(?:www\.)?howcast\.com/videos/(?P<id>\d+)'
def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url)
video_id = mobj.group('id')
webpage_url = 'http://www.howcast.com/videos/' + video_id
webpage = self._download_webpage(webpage_url, video_id)
self.report_extraction(video_id)
video_url = self._search_regex(r'\'?file\'?: "(http://mobile-media\.howcast\.com/[0-9]+\.mp4)',
webpage, u'video URL')
video_title = self._html_search_regex(r'<meta content=(?:"([^"]+)"|\'([^\']+)\') property=\'og:title\'',
webpage, u'title')
video_description = self._html_search_regex(r'<meta content=(?:"([^"]+)"|\'([^\']+)\') name=\'description\'',
webpage, u'description', fatal=False)
thumbnail = self._html_search_regex(r'<meta content=\'(.+?)\' property=\'og:image\'',
webpage, u'thumbnail', fatal=False)
return [{
'id': video_id,
'url': video_url,
'ext': 'mp4',
'title': video_title,
'description': video_description,
'thumbnail': thumbnail,
}]
-63
View File
@@ -1,63 +0,0 @@
import json
import re
import time
from .common import InfoExtractor
from ..utils import (
compat_str,
compat_urllib_parse,
compat_urllib_request,
ExtractorError,
)
class HypemIE(InfoExtractor):
"""Information Extractor for hypem"""
_VALID_URL = r'(?:http://)?(?:www\.)?hypem\.com/track/([^/]+)/([^/]+)'
def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url)
if mobj is None:
raise ExtractorError(u'Invalid URL: %s' % url)
track_id = mobj.group(1)
data = { 'ax': 1, 'ts': time.time() }
data_encoded = compat_urllib_parse.urlencode(data)
complete_url = url + "?" + data_encoded
request = compat_urllib_request.Request(complete_url)
response, urlh = self._download_webpage_handle(request, track_id, u'Downloading webpage with the url')
cookie = urlh.headers.get('Set-Cookie', '')
self.report_extraction(track_id)
html_tracks = self._html_search_regex(r'<script type="application/json" id="displayList-data">(.*?)</script>',
response, u'tracks', flags=re.MULTILINE|re.DOTALL).strip()
try:
track_list = json.loads(html_tracks)
track = track_list[u'tracks'][0]
except ValueError:
raise ExtractorError(u'Hypemachine contained invalid JSON.')
key = track[u"key"]
track_id = track[u"id"]
artist = track[u"artist"]
title = track[u"song"]
serve_url = "http://hypem.com/serve/source/%s/%s" % (compat_str(track_id), compat_str(key))
request = compat_urllib_request.Request(serve_url, "" , {'Content-Type': 'application/json'})
request.add_header('cookie', cookie)
song_data_json = self._download_webpage(request, track_id, u'Downloading metadata')
try:
song_data = json.loads(song_data_json)
except ValueError:
raise ExtractorError(u'Hypemachine contained invalid JSON.')
final_url = song_data[u"url"]
return [{
'id': track_id,
'url': final_url,
'ext': "mp3",
'title': title,
'artist': artist,
}]
-31
View File
@@ -1,31 +0,0 @@
import re
from .common import InfoExtractor
class InaIE(InfoExtractor):
"""Information Extractor for Ina.fr"""
_VALID_URL = r'(?:http://)?(?:www\.)?ina\.fr/video/(?P<id>I[0-9]+)/.*'
def _real_extract(self,url):
mobj = re.match(self._VALID_URL, url)
video_id = mobj.group('id')
mrss_url='http://player.ina.fr/notices/%s.mrss' % video_id
video_extension = 'mp4'
webpage = self._download_webpage(mrss_url, video_id)
self.report_extraction(video_id)
video_url = self._html_search_regex(r'<media:player url="(?P<mp4url>http://mp4.ina.fr/[^"]+\.mp4)',
webpage, u'video URL')
video_title = self._search_regex(r'<title><!\[CDATA\[(?P<titre>.*?)]]></title>',
webpage, u'title')
return [{
'id': video_id,
'url': video_url,
'ext': video_extension,
'title': video_title,
}]
-50
View File
@@ -1,50 +0,0 @@
import base64
import re
from .common import InfoExtractor
from ..utils import (
compat_urllib_parse,
ExtractorError,
)
class InfoQIE(InfoExtractor):
_VALID_URL = r'^(?:https?://)?(?:www\.)?infoq\.com/[^/]+/[^/]+$'
def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url)
webpage = self._download_webpage(url, video_id=url)
self.report_extraction(url)
# Extract video URL
mobj = re.search(r"jsclassref ?= ?'([^']*)'", webpage)
if mobj is None:
raise ExtractorError(u'Unable to extract video url')
real_id = compat_urllib_parse.unquote(base64.b64decode(mobj.group(1).encode('ascii')).decode('utf-8'))
video_url = 'rtmpe://video.infoq.com/cfx/st/' + real_id
# Extract title
video_title = self._search_regex(r'contentTitle = "(.*?)";',
webpage, u'title')
# Extract description
video_description = self._html_search_regex(r'<meta name="description" content="(.*)"(?:\s*/)?>',
webpage, u'description', fatal=False)
video_filename = video_url.split('/')[-1]
video_id, extension = video_filename.split('.')
info = {
'id': video_id,
'url': video_url,
'uploader': None,
'upload_date': None,
'title': video_title,
'ext': extension, # Extension is always(?) mp4, but seems to be flv
'thumbnail': None,
'description': video_description,
}
return [info]
-144
View File
@@ -1,144 +0,0 @@
import json
import os
import re
import xml.etree.ElementTree
from .common import InfoExtractor
from ..utils import (
ExtractorError,
formatSeconds,
)
class JustinTVIE(InfoExtractor):
"""Information extractor for justin.tv and twitch.tv"""
# TODO: One broadcast may be split into multiple videos. The key
# 'broadcast_id' is the same for all parts, and 'broadcast_part'
# starts at 1 and increases. Can we treat all parts as one video?
_VALID_URL = r"""(?x)^(?:http://)?(?:www\.)?(?:twitch|justin)\.tv/
(?:
(?P<channelid>[^/]+)|
(?:(?:[^/]+)/b/(?P<videoid>[^/]+))|
(?:(?:[^/]+)/c/(?P<chapterid>[^/]+))
)
/?(?:\#.*)?$
"""
_JUSTIN_PAGE_LIMIT = 100
IE_NAME = u'justin.tv'
def report_download_page(self, channel, offset):
"""Report attempt to download a single page of videos."""
self.to_screen(u'%s: Downloading video information from %d to %d' %
(channel, offset, offset + self._JUSTIN_PAGE_LIMIT))
# Return count of items, list of *valid* items
def _parse_page(self, url, video_id):
info_json = self._download_webpage(url, video_id,
u'Downloading video info JSON',
u'unable to download video info JSON')
response = json.loads(info_json)
if type(response) != list:
error_text = response.get('error', 'unknown error')
raise ExtractorError(u'Justin.tv API: %s' % error_text)
info = []
for clip in response:
video_url = clip['video_file_url']
if video_url:
video_extension = os.path.splitext(video_url)[1][1:]
video_date = re.sub('-', '', clip['start_time'][:10])
video_uploader_id = clip.get('user_id', clip.get('channel_id'))
video_id = clip['id']
video_title = clip.get('title', video_id)
info.append({
'id': video_id,
'url': video_url,
'title': video_title,
'uploader': clip.get('channel_name', video_uploader_id),
'uploader_id': video_uploader_id,
'upload_date': video_date,
'ext': video_extension,
})
return (len(response), info)
def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url)
if mobj is None:
raise ExtractorError(u'invalid URL: %s' % url)
api_base = 'http://api.justin.tv'
paged = False
if mobj.group('channelid'):
paged = True
video_id = mobj.group('channelid')
api = api_base + '/channel/archives/%s.json' % video_id
elif mobj.group('chapterid'):
chapter_id = mobj.group('chapterid')
webpage = self._download_webpage(url, chapter_id)
m = re.search(r'PP\.archive_id = "([0-9]+)";', webpage)
if not m:
raise ExtractorError(u'Cannot find archive of a chapter')
archive_id = m.group(1)
api = api_base + '/broadcast/by_chapter/%s.xml' % chapter_id
chapter_info_xml = self._download_webpage(api, chapter_id,
note=u'Downloading chapter information',
errnote=u'Chapter information download failed')
doc = xml.etree.ElementTree.fromstring(chapter_info_xml)
for a in doc.findall('.//archive'):
if archive_id == a.find('./id').text:
break
else:
raise ExtractorError(u'Could not find chapter in chapter information')
video_url = a.find('./video_file_url').text
video_ext = video_url.rpartition('.')[2] or u'flv'
chapter_api_url = u'https://api.twitch.tv/kraken/videos/c' + chapter_id
chapter_info_json = self._download_webpage(chapter_api_url, u'c' + chapter_id,
note='Downloading chapter metadata',
errnote='Download of chapter metadata failed')
chapter_info = json.loads(chapter_info_json)
bracket_start = int(doc.find('.//bracket_start').text)
bracket_end = int(doc.find('.//bracket_end').text)
# TODO determine start (and probably fix up file)
# youtube-dl -v http://www.twitch.tv/firmbelief/c/1757457
#video_url += u'?start=' + TODO:start_timestamp
# bracket_start is 13290, but we want 51670615
self._downloader.report_warning(u'Chapter detected, but we can just download the whole file. '
u'Chapter starts at %s and ends at %s' % (formatSeconds(bracket_start), formatSeconds(bracket_end)))
info = {
'id': u'c' + chapter_id,
'url': video_url,
'ext': video_ext,
'title': chapter_info['title'],
'thumbnail': chapter_info['preview'],
'description': chapter_info['description'],
'uploader': chapter_info['channel']['display_name'],
'uploader_id': chapter_info['channel']['name'],
}
return [info]
else:
video_id = mobj.group('videoid')
api = api_base + '/broadcast/by_archive/%s.json' % video_id
self.report_extraction(video_id)
info = []
offset = 0
limit = self._JUSTIN_PAGE_LIMIT
while True:
if paged:
self.report_download_page(video_id, offset)
page_url = api + ('?offset=%d&limit=%d' % (offset, limit))
page_count, page_info = self._parse_page(page_url, video_id)
info.extend(page_info)
if not paged or page_count != limit:
break
offset += limit
return info
-32
View File
@@ -1,32 +0,0 @@
import re
from .common import InfoExtractor
class KeekIE(InfoExtractor):
_VALID_URL = r'http://(?:www\.)?keek\.com/(?:!|\w+/keeks/)(?P<videoID>\w+)'
IE_NAME = u'keek'
def _real_extract(self, url):
m = re.match(self._VALID_URL, url)
video_id = m.group('videoID')
video_url = u'http://cdn.keek.com/keek/video/%s' % video_id
thumbnail = u'http://cdn.keek.com/keek/thumbnail/%s/w100/h75' % video_id
webpage = self._download_webpage(url, video_id)
video_title = self._html_search_regex(r'<meta property="og:title" content="(?P<title>.*?)"',
webpage, u'title')
uploader = self._html_search_regex(r'<div class="user-name-and-bio">[\S\s]+?<h2>(?P<uploader>.+?)</h2>',
webpage, u'uploader', fatal=False)
info = {
'id': video_id,
'url': video_url,
'ext': 'mp4',
'title': video_title,
'thumbnail': thumbnail,
'uploader': uploader
}
return [info]
-44
View File
@@ -1,44 +0,0 @@
import re
from .common import InfoExtractor
from ..utils import (
ExtractorError,
)
class LiveLeakIE(InfoExtractor):
_VALID_URL = r'^(?:http?://)?(?:\w+\.)?liveleak\.com/view\?(?:.*?)i=(?P<video_id>[\w_]+)(?:.*)'
IE_NAME = u'liveleak'
def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url)
if mobj is None:
raise ExtractorError(u'Invalid URL: %s' % url)
video_id = mobj.group('video_id')
webpage = self._download_webpage(url, video_id)
video_url = self._search_regex(r'file: "(.*?)",',
webpage, u'video URL')
video_title = self._html_search_regex(r'<meta property="og:title" content="(?P<title>.*?)"',
webpage, u'title').replace('LiveLeak.com -', '').strip()
video_description = self._html_search_regex(r'<meta property="og:description" content="(?P<desc>.*?)"',
webpage, u'description', fatal=False)
video_uploader = self._html_search_regex(r'By:.*?(\w+)</a>',
webpage, u'uploader', fatal=False)
info = {
'id': video_id,
'url': video_url,
'ext': 'mp4',
'title': video_title,
'description': video_description,
'uploader': video_uploader
}
return [info]
-110
View File
@@ -1,110 +0,0 @@
import re
import socket
from .common import InfoExtractor
from ..utils import (
compat_http_client,
compat_parse_qs,
compat_urllib_error,
compat_urllib_parse,
compat_urllib_request,
compat_str,
ExtractorError,
)
class MetacafeIE(InfoExtractor):
"""Information Extractor for metacafe.com."""
_VALID_URL = r'(?:http://)?(?:www\.)?metacafe\.com/watch/([^/]+)/([^/]+)/.*'
_DISCLAIMER = 'http://www.metacafe.com/family_filter/'
_FILTER_POST = 'http://www.metacafe.com/f/index.php?inputType=filter&controllerGroup=user'
IE_NAME = u'metacafe'
def report_disclaimer(self):
"""Report disclaimer retrieval."""
self.to_screen(u'Retrieving disclaimer')
def _real_initialize(self):
# Retrieve disclaimer
request = compat_urllib_request.Request(self._DISCLAIMER)
try:
self.report_disclaimer()
compat_urllib_request.urlopen(request).read()
except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
raise ExtractorError(u'Unable to retrieve disclaimer: %s' % compat_str(err))
# Confirm age
disclaimer_form = {
'filters': '0',
'submit': "Continue - I'm over 18",
}
request = compat_urllib_request.Request(self._FILTER_POST, compat_urllib_parse.urlencode(disclaimer_form))
try:
self.report_age_confirmation()
compat_urllib_request.urlopen(request).read()
except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
raise ExtractorError(u'Unable to confirm age: %s' % compat_str(err))
def _real_extract(self, url):
# Extract id and simplified title from URL
mobj = re.match(self._VALID_URL, url)
if mobj is None:
raise ExtractorError(u'Invalid URL: %s' % url)
video_id = mobj.group(1)
# Check if video comes from YouTube
mobj2 = re.match(r'^yt-(.*)$', video_id)
if mobj2 is not None:
return [self.url_result('http://www.youtube.com/watch?v=%s' % mobj2.group(1), 'Youtube')]
# Retrieve video webpage to extract further information
webpage = self._download_webpage('http://www.metacafe.com/watch/%s/' % video_id, video_id)
# Extract URL, uploader and title from webpage
self.report_extraction(video_id)
mobj = re.search(r'(?m)&mediaURL=([^&]+)', webpage)
if mobj is not None:
mediaURL = compat_urllib_parse.unquote(mobj.group(1))
video_extension = mediaURL[-3:]
# Extract gdaKey if available
mobj = re.search(r'(?m)&gdaKey=(.*?)&', webpage)
if mobj is None:
video_url = mediaURL
else:
gdaKey = mobj.group(1)
video_url = '%s?__gda__=%s' % (mediaURL, gdaKey)
else:
mobj = re.search(r' name="flashvars" value="(.*?)"', webpage)
if mobj is None:
raise ExtractorError(u'Unable to extract media URL')
vardict = compat_parse_qs(mobj.group(1))
if 'mediaData' not in vardict:
raise ExtractorError(u'Unable to extract media URL')
mobj = re.search(r'"mediaURL":"(?P<mediaURL>http.*?)",(.*?)"key":"(?P<key>.*?)"', vardict['mediaData'][0])
if mobj is None:
raise ExtractorError(u'Unable to extract media URL')
mediaURL = mobj.group('mediaURL').replace('\\/', '/')
video_extension = mediaURL[-3:]
video_url = '%s?__gda__=%s' % (mediaURL, mobj.group('key'))
mobj = re.search(r'(?im)<title>(.*) - Video</title>', webpage)
if mobj is None:
raise ExtractorError(u'Unable to extract title')
video_title = mobj.group(1).decode('utf-8')
mobj = re.search(r'submitter=(.*?);', webpage)
if mobj is None:
raise ExtractorError(u'Unable to extract uploader nickname')
video_uploader = mobj.group(1)
return [{
'id': video_id.decode('utf-8'),
'url': video_url.decode('utf-8'),
'uploader': video_uploader.decode('utf-8'),
'upload_date': None,
'title': video_title,
'ext': video_extension.decode('utf-8'),
}]
-115
View File
@@ -1,115 +0,0 @@
import json
import re
import socket
from .common import InfoExtractor
from ..utils import (
compat_http_client,
compat_str,
compat_urllib_error,
compat_urllib_request,
ExtractorError,
)
class MixcloudIE(InfoExtractor):
_WORKING = False # New API, but it seems good http://www.mixcloud.com/developers/documentation/
_VALID_URL = r'^(?:https?://)?(?:www\.)?mixcloud\.com/([\w\d-]+)/([\w\d-]+)'
IE_NAME = u'mixcloud'
def report_download_json(self, file_id):
"""Report JSON download."""
self.to_screen(u'Downloading json')
def get_urls(self, jsonData, fmt, bitrate='best'):
"""Get urls from 'audio_formats' section in json"""
try:
bitrate_list = jsonData[fmt]
if bitrate is None or bitrate == 'best' or bitrate not in bitrate_list:
bitrate = max(bitrate_list) # select highest
url_list = jsonData[fmt][bitrate]
except TypeError: # we have no bitrate info.
url_list = jsonData[fmt]
return url_list
def check_urls(self, url_list):
"""Returns 1st active url from list"""
for url in url_list:
try:
compat_urllib_request.urlopen(url)
return url
except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error):
url = None
return None
def _print_formats(self, formats):
print('Available formats:')
for fmt in formats.keys():
for b in formats[fmt]:
try:
ext = formats[fmt][b][0]
print('%s\t%s\t[%s]' % (fmt, b, ext.split('.')[-1]))
except TypeError: # we have no bitrate info
ext = formats[fmt][0]
print('%s\t%s\t[%s]' % (fmt, '??', ext.split('.')[-1]))
break
def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url)
if mobj is None:
raise ExtractorError(u'Invalid URL: %s' % url)
# extract uploader & filename from url
uploader = mobj.group(1).decode('utf-8')
file_id = uploader + "-" + mobj.group(2).decode('utf-8')
# construct API request
file_url = 'http://www.mixcloud.com/api/1/cloudcast/' + '/'.join(url.split('/')[-3:-1]) + '.json'
# retrieve .json file with links to files
request = compat_urllib_request.Request(file_url)
try:
self.report_download_json(file_url)
jsonData = compat_urllib_request.urlopen(request).read()
except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
raise ExtractorError(u'Unable to retrieve file: %s' % compat_str(err))
# parse JSON
json_data = json.loads(jsonData)
player_url = json_data['player_swf_url']
formats = dict(json_data['audio_formats'])
req_format = self._downloader.params.get('format', None)
if self._downloader.params.get('listformats', None):
self._print_formats(formats)
return
if req_format is None or req_format == 'best':
for format_param in formats.keys():
url_list = self.get_urls(formats, format_param)
# check urls
file_url = self.check_urls(url_list)
if file_url is not None:
break # got it!
else:
if req_format not in formats:
raise ExtractorError(u'Format is not available')
url_list = self.get_urls(formats, req_format)
file_url = self.check_urls(url_list)
format_param = req_format
return [{
'id': file_id.decode('utf-8'),
'url': file_url.decode('utf-8'),
'uploader': uploader.decode('utf-8'),
'upload_date': None,
'title': json_data['name'],
'ext': file_url.split('.')[-1].decode('utf-8'),
'format': (format_param is None and u'NA' or format_param.decode('utf-8')),
'thumbnail': json_data['thumbnail_url'],
'description': json_data['description'],
'player_url': player_url.decode('utf-8'),
}]
-80
View File
@@ -1,80 +0,0 @@
import re
import socket
import xml.etree.ElementTree
from .common import InfoExtractor
from ..utils import (
compat_http_client,
compat_str,
compat_urllib_error,
compat_urllib_request,
ExtractorError,
)
class MTVIE(InfoExtractor):
_VALID_URL = r'^(?P<proto>https?://)?(?:www\.)?mtv\.com/videos/[^/]+/(?P<videoid>[0-9]+)/[^/]+$'
_WORKING = False
def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url)
if mobj is None:
raise ExtractorError(u'Invalid URL: %s' % url)
if not mobj.group('proto'):
url = 'http://' + url
video_id = mobj.group('videoid')
webpage = self._download_webpage(url, video_id)
# Some videos come from Vevo.com
m_vevo = re.search(r'isVevoVideo = true;.*?vevoVideoId = "(.*?)";',
webpage, re.DOTALL)
if m_vevo:
vevo_id = m_vevo.group(1);
self.to_screen(u'Vevo video detected: %s' % vevo_id)
return self.url_result('vevo:%s' % vevo_id, ie='Vevo')
#song_name = self._html_search_regex(r'<meta name="mtv_vt" content="([^"]+)"/>',
# webpage, u'song name', fatal=False)
video_title = self._html_search_regex(r'<meta name="mtv_an" content="([^"]+)"/>',
webpage, u'title')
mtvn_uri = self._html_search_regex(r'<meta name="mtvn_uri" content="([^"]+)"/>',
webpage, u'mtvn_uri', fatal=False)
content_id = self._search_regex(r'MTVN.Player.defaultPlaylistId = ([0-9]+);',
webpage, u'content id', fatal=False)
videogen_url = 'http://www.mtv.com/player/includes/mediaGen.jhtml?uri=' + mtvn_uri + '&id=' + content_id + '&vid=' + video_id + '&ref=www.mtvn.com&viewUri=' + mtvn_uri
self.report_extraction(video_id)
request = compat_urllib_request.Request(videogen_url)
try:
metadataXml = compat_urllib_request.urlopen(request).read()
except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
raise ExtractorError(u'Unable to download video metadata: %s' % compat_str(err))
mdoc = xml.etree.ElementTree.fromstring(metadataXml)
renditions = mdoc.findall('.//rendition')
# For now, always pick the highest quality.
rendition = renditions[-1]
try:
_,_,ext = rendition.attrib['type'].partition('/')
format = ext + '-' + rendition.attrib['width'] + 'x' + rendition.attrib['height'] + '_' + rendition.attrib['bitrate']
video_url = rendition.find('./src').text
except KeyError:
raise ExtractorError('Invalid rendition field.')
info = {
'id': video_id,
'url': video_url,
'upload_date': None,
'title': video_title,
'ext': ext,
'format': format,
}
return [info]
-64
View File
@@ -1,64 +0,0 @@
import os.path
import xml.etree.ElementTree
from .common import InfoExtractor
from ..utils import (
compat_urllib_parse_urlparse,
ExtractorError,
)
class MySpassIE(InfoExtractor):
_VALID_URL = r'http://www.myspass.de/.*'
def _real_extract(self, url):
META_DATA_URL_TEMPLATE = 'http://www.myspass.de/myspass/includes/apps/video/getvideometadataxml.php?id=%s'
# video id is the last path element of the URL
# usually there is a trailing slash, so also try the second but last
url_path = compat_urllib_parse_urlparse(url).path
url_parent_path, video_id = os.path.split(url_path)
if not video_id:
_, video_id = os.path.split(url_parent_path)
# get metadata
metadata_url = META_DATA_URL_TEMPLATE % video_id
metadata_text = self._download_webpage(metadata_url, video_id)
metadata = xml.etree.ElementTree.fromstring(metadata_text.encode('utf-8'))
# extract values from metadata
url_flv_el = metadata.find('url_flv')
if url_flv_el is None:
raise ExtractorError(u'Unable to extract download url')
video_url = url_flv_el.text
extension = os.path.splitext(video_url)[1][1:]
title_el = metadata.find('title')
if title_el is None:
raise ExtractorError(u'Unable to extract title')
title = title_el.text
format_id_el = metadata.find('format_id')
if format_id_el is None:
format = 'mp4'
else:
format = format_id_el.text
description_el = metadata.find('description')
if description_el is not None:
description = description_el.text
else:
description = None
imagePreview_el = metadata.find('imagePreview')
if imagePreview_el is not None:
thumbnail = imagePreview_el.text
else:
thumbnail = None
info = {
'id': video_id,
'url': video_url,
'title': title,
'ext': extension,
'format': format,
'thumbnail': thumbnail,
'description': description
}
return [info]
-164
View File
@@ -1,164 +0,0 @@
import binascii
import base64
import hashlib
import re
from .common import InfoExtractor
from ..utils import (
compat_ord,
compat_urllib_parse,
ExtractorError,
)
class MyVideoIE(InfoExtractor):
"""Information Extractor for myvideo.de."""
_VALID_URL = r'(?:http://)?(?:www\.)?myvideo\.de/watch/([0-9]+)/([^?/]+).*'
IE_NAME = u'myvideo'
# Original Code from: https://github.com/dersphere/plugin.video.myvideo_de.git
# Released into the Public Domain by Tristan Fischer on 2013-05-19
# https://github.com/rg3/youtube-dl/pull/842
def __rc4crypt(self,data, key):
x = 0
box = list(range(256))
for i in list(range(256)):
x = (x + box[i] + compat_ord(key[i % len(key)])) % 256
box[i], box[x] = box[x], box[i]
x = 0
y = 0
out = ''
for char in data:
x = (x + 1) % 256
y = (y + box[x]) % 256
box[x], box[y] = box[y], box[x]
out += chr(compat_ord(char) ^ box[(box[x] + box[y]) % 256])
return out
def __md5(self,s):
return hashlib.md5(s).hexdigest().encode()
def _real_extract(self,url):
mobj = re.match(self._VALID_URL, url)
if mobj is None:
raise ExtractorError(u'invalid URL: %s' % url)
video_id = mobj.group(1)
GK = (
b'WXpnME1EZGhNRGhpTTJNM01XVmhOREU0WldNNVpHTTJOakpt'
b'TW1FMU5tVTBNR05pWkRaa05XRXhNVFJoWVRVd1ptSXhaVEV3'
b'TnpsbA0KTVRkbU1tSTRNdz09'
)
# Get video webpage
webpage_url = 'http://www.myvideo.de/watch/%s' % video_id
webpage = self._download_webpage(webpage_url, video_id)
mobj = re.search('source src=\'(.+?)[.]([^.]+)\'', webpage)
if mobj is not None:
self.report_extraction(video_id)
video_url = mobj.group(1) + '.flv'
video_title = self._html_search_regex('<title>([^<]+)</title>',
webpage, u'title')
video_ext = self._search_regex('[.](.+?)$', video_url, u'extension')
return [{
'id': video_id,
'url': video_url,
'uploader': None,
'upload_date': None,
'title': video_title,
'ext': video_ext,
}]
# try encxml
mobj = re.search('var flashvars={(.+?)}', webpage)
if mobj is None:
raise ExtractorError(u'Unable to extract video')
params = {}
encxml = ''
sec = mobj.group(1)
for (a, b) in re.findall('(.+?):\'(.+?)\',?', sec):
if not a == '_encxml':
params[a] = b
else:
encxml = compat_urllib_parse.unquote(b)
if not params.get('domain'):
params['domain'] = 'www.myvideo.de'
xmldata_url = '%s?%s' % (encxml, compat_urllib_parse.urlencode(params))
if 'flash_playertype=MTV' in xmldata_url:
self._downloader.report_warning(u'avoiding MTV player')
xmldata_url = (
'http://www.myvideo.de/dynamic/get_player_video_xml.php'
'?flash_playertype=D&ID=%s&_countlimit=4&autorun=yes'
) % video_id
# get enc data
enc_data = self._download_webpage(xmldata_url, video_id).split('=')[1]
enc_data_b = binascii.unhexlify(enc_data)
sk = self.__md5(
base64.b64decode(base64.b64decode(GK)) +
self.__md5(
str(video_id).encode('utf-8')
)
)
dec_data = self.__rc4crypt(enc_data_b, sk)
# extracting infos
self.report_extraction(video_id)
video_url = None
mobj = re.search('connectionurl=\'(.*?)\'', dec_data)
if mobj:
video_url = compat_urllib_parse.unquote(mobj.group(1))
if 'myvideo2flash' in video_url:
self._downloader.report_warning(u'forcing RTMPT ...')
video_url = video_url.replace('rtmpe://', 'rtmpt://')
if not video_url:
# extract non rtmp videos
mobj = re.search('path=\'(http.*?)\' source=\'(.*?)\'', dec_data)
if mobj is None:
raise ExtractorError(u'unable to extract url')
video_url = compat_urllib_parse.unquote(mobj.group(1)) + compat_urllib_parse.unquote(mobj.group(2))
video_file = self._search_regex('source=\'(.*?)\'', dec_data, u'video file')
video_file = compat_urllib_parse.unquote(video_file)
if not video_file.endswith('f4m'):
ppath, prefix = video_file.split('.')
video_playpath = '%s:%s' % (prefix, ppath)
video_hls_playlist = ''
else:
video_playpath = ''
video_hls_playlist = (
video_file
).replace('.f4m', '.m3u8')
video_swfobj = self._search_regex('swfobject.embedSWF\(\'(.+?)\'', webpage, u'swfobj')
video_swfobj = compat_urllib_parse.unquote(video_swfobj)
video_title = self._html_search_regex("<h1(?: class='globalHd')?>(.*?)</h1>",
webpage, u'title')
return [{
'id': video_id,
'url': video_url,
'tc_url': video_url,
'uploader': None,
'upload_date': None,
'title': video_title,
'ext': u'flv',
'play_path': video_playpath,
'video_file': video_file,
'video_hls_playlist': video_hls_playlist,
'player_url': video_swfobj,
}]
-40
View File
@@ -1,40 +0,0 @@
import re
from .common import InfoExtractor
from ..utils import (
ExtractorError,
)
class NBAIE(InfoExtractor):
_VALID_URL = r'^(?:https?://)?(?:watch\.|www\.)?nba\.com/(?:nba/)?video(/[^?]*?)(?:/index\.html)?(?:\?.*)?$'
def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url)
if mobj is None:
raise ExtractorError(u'Invalid URL: %s' % url)
video_id = mobj.group(1)
webpage = self._download_webpage(url, video_id)
video_url = u'http://ht-mobile.cdn.turner.com/nba/big' + video_id + '_nba_1280x720.mp4'
shortened_video_id = video_id.rpartition('/')[2]
title = self._html_search_regex(r'<meta property="og:title" content="(.*?)"',
webpage, 'title', default=shortened_video_id).replace('NBA.com: ', '')
# It isn't there in the HTML it returns to us
# uploader_date = self._html_search_regex(r'<b>Date:</b> (.*?)</div>', webpage, 'upload_date', fatal=False)
description = self._html_search_regex(r'<meta name="description" (?:content|value)="(.*?)" />', webpage, 'description', fatal=False)
info = {
'id': shortened_video_id,
'url': video_url,
'ext': 'mp4',
'title': title,
# 'uploader_date': uploader_date,
'description': description,
}
return [info]
-66
View File
@@ -1,66 +0,0 @@
import datetime
import json
import re
from .common import InfoExtractor
from ..utils import (
ExtractorError,
)
class PhotobucketIE(InfoExtractor):
"""Information extractor for photobucket.com."""
# TODO: the original _VALID_URL was:
# r'(?:http://)?(?:[a-z0-9]+\.)?photobucket\.com/.*[\?\&]current=(.*\.flv)'
# Check if it's necessary to keep the old extracion process
_VALID_URL = r'(?:http://)?(?:[a-z0-9]+\.)?photobucket\.com/.*(([\?\&]current=)|_)(?P<id>.*)\.(?P<ext>(flv)|(mp4))'
IE_NAME = u'photobucket'
def _real_extract(self, url):
# Extract id from URL
mobj = re.match(self._VALID_URL, url)
if mobj is None:
raise ExtractorError(u'Invalid URL: %s' % url)
video_id = mobj.group('id')
video_extension = mobj.group('ext')
# Retrieve video webpage to extract further information
webpage = self._download_webpage(url, video_id)
# Extract URL, uploader, and title from webpage
self.report_extraction(video_id)
# We try first by looking the javascript code:
mobj = re.search(r'Pb\.Data\.Shared\.put\(Pb\.Data\.Shared\.MEDIA, (?P<json>.*?)\);', webpage)
if mobj is not None:
info = json.loads(mobj.group('json'))
return [{
'id': video_id,
'url': info[u'downloadUrl'],
'uploader': info[u'username'],
'upload_date': datetime.date.fromtimestamp(info[u'creationDate']).strftime('%Y%m%d'),
'title': info[u'title'],
'ext': video_extension,
'thumbnail': info[u'thumbUrl'],
}]
# We try looking in other parts of the webpage
video_url = self._search_regex(r'<link rel="video_src" href=".*\?file=([^"]+)" />',
webpage, u'video URL')
mobj = re.search(r'<title>(.*) video by (.*) - Photobucket</title>', webpage)
if mobj is None:
raise ExtractorError(u'Unable to extract title')
video_title = mobj.group(1).decode('utf-8')
video_uploader = mobj.group(2).decode('utf-8')
return [{
'id': video_id.decode('utf-8'),
'url': video_url.decode('utf-8'),
'uploader': video_uploader,
'upload_date': None,
'title': video_title,
'ext': video_extension.decode('utf-8'),
}]
-41
View File
@@ -1,41 +0,0 @@
import re
from .common import InfoExtractor
from ..utils import (
compat_urllib_parse,
unified_strdate,
)
class PornotubeIE(InfoExtractor):
_VALID_URL = r'^(?:https?://)?(?:\w+\.)?pornotube\.com(/c/(?P<channel>[0-9]+))?(/m/(?P<videoid>[0-9]+))(/(?P<title>.+))$'
def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url)
video_id = mobj.group('videoid')
video_title = mobj.group('title')
# Get webpage content
webpage = self._download_webpage(url, video_id)
# Get the video URL
VIDEO_URL_RE = r'url: "(?P<url>http://video[0-9].pornotube.com/.+\.flv)",'
video_url = self._search_regex(VIDEO_URL_RE, webpage, u'video url')
video_url = compat_urllib_parse.unquote(video_url)
#Get the uploaded date
VIDEO_UPLOADED_RE = r'<div class="video_added_by">Added (?P<date>[0-9\/]+) by'
upload_date = self._html_search_regex(VIDEO_UPLOADED_RE, webpage, u'upload date', fatal=False)
if upload_date: upload_date = unified_strdate(upload_date)
info = {'id': video_id,
'url': video_url,
'uploader': None,
'upload_date': upload_date,
'title': video_title,
'ext': 'flv',
'format': 'flv'}
return [info]
-44
View File
@@ -1,44 +0,0 @@
import json
import re
from .common import InfoExtractor
from ..utils import (
compat_urllib_parse_urlparse,
ExtractorError,
)
class RBMARadioIE(InfoExtractor):
_VALID_URL = r'https?://(?:www\.)?rbmaradio\.com/shows/(?P<videoID>[^/]+)$'
def _real_extract(self, url):
m = re.match(self._VALID_URL, url)
video_id = m.group('videoID')
webpage = self._download_webpage(url, video_id)
json_data = self._search_regex(r'window\.gon.*?gon\.show=(.+?);$',
webpage, u'json data', flags=re.MULTILINE)
try:
data = json.loads(json_data)
except ValueError as e:
raise ExtractorError(u'Invalid JSON: ' + str(e))
video_url = data['akamai_url'] + '&cbr=256'
url_parts = compat_urllib_parse_urlparse(video_url)
video_ext = url_parts.path.rpartition('.')[2]
info = {
'id': video_id,
'url': video_url,
'ext': video_ext,
'title': data['title'],
'description': data.get('teaser_text'),
'location': data.get('country_of_origin'),
'uploader': data.get('host', {}).get('name'),
'uploader_id': data.get('host', {}).get('slug'),
'thumbnail': data.get('image', {}).get('large_url_2x'),
'duration': data.get('duration'),
}
return [info]
-29
View File
@@ -1,29 +0,0 @@
import re
from .common import InfoExtractor
class RedTubeIE(InfoExtractor):
_VALID_URL = r'(?:http://)?(?:www\.)?redtube\.com/(?P<id>[0-9]+)'
def _real_extract(self,url):
mobj = re.match(self._VALID_URL, url)
video_id = mobj.group('id')
video_extension = 'mp4'
webpage = self._download_webpage(url, video_id)
self.report_extraction(video_id)
video_url = self._html_search_regex(r'<source src="(.+?)" type="video/mp4">',
webpage, u'video URL')
video_title = self._html_search_regex('<h1 class="videoTitle slidePanelMovable">(.+?)</h1>',
webpage, u'title')
return [{
'id': video_id,
'url': video_url,
'ext': video_extension,
'title': video_title,
}]
-129
View File
@@ -1,129 +0,0 @@
import json
import re
from .common import InfoExtractor
from ..utils import (
compat_str,
ExtractorError,
unified_strdate,
)
class SoundcloudIE(InfoExtractor):
"""Information extractor for soundcloud.com
To access the media, the uid of the song and a stream token
must be extracted from the page source and the script must make
a request to media.soundcloud.com/crossdomain.xml. Then
the media can be grabbed by requesting from an url composed
of the stream token and uid
"""
_VALID_URL = r'^(?:https?://)?(?:www\.)?soundcloud\.com/([\w\d-]+)/([\w\d-]+)'
IE_NAME = u'soundcloud'
def report_resolve(self, video_id):
"""Report information extraction."""
self.to_screen(u'%s: Resolving id' % video_id)
def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url)
if mobj is None:
raise ExtractorError(u'Invalid URL: %s' % url)
# extract uploader (which is in the url)
uploader = mobj.group(1)
# extract simple title (uploader + slug of song title)
slug_title = mobj.group(2)
full_title = '%s/%s' % (uploader, slug_title)
self.report_resolve(full_title)
url = 'http://soundcloud.com/%s/%s' % (uploader, slug_title)
resolv_url = 'http://api.soundcloud.com/resolve.json?url=' + url + '&client_id=b45b1aa10f1ac2941910a7f0d10f8e28'
info_json = self._download_webpage(resolv_url, full_title, u'Downloading info JSON')
info = json.loads(info_json)
video_id = info['id']
self.report_extraction(full_title)
streams_url = 'https://api.sndcdn.com/i1/tracks/' + str(video_id) + '/streams?client_id=b45b1aa10f1ac2941910a7f0d10f8e28'
stream_json = self._download_webpage(streams_url, full_title,
u'Downloading stream definitions',
u'unable to download stream definitions')
streams = json.loads(stream_json)
mediaURL = streams['http_mp3_128_url']
upload_date = unified_strdate(info['created_at'])
return [{
'id': info['id'],
'url': mediaURL,
'uploader': info['user']['username'],
'upload_date': upload_date,
'title': info['title'],
'ext': u'mp3',
'description': info['description'],
}]
class SoundcloudSetIE(InfoExtractor):
"""Information extractor for soundcloud.com sets
To access the media, the uid of the song and a stream token
must be extracted from the page source and the script must make
a request to media.soundcloud.com/crossdomain.xml. Then
the media can be grabbed by requesting from an url composed
of the stream token and uid
"""
_VALID_URL = r'^(?:https?://)?(?:www\.)?soundcloud\.com/([\w\d-]+)/sets/([\w\d-]+)'
IE_NAME = u'soundcloud:set'
def report_resolve(self, video_id):
"""Report information extraction."""
self.to_screen(u'%s: Resolving id' % video_id)
def _real_extract(self, url):
mobj = re.match(self._VALID_URL, url)
if mobj is None:
raise ExtractorError(u'Invalid URL: %s' % url)
# extract uploader (which is in the url)
uploader = mobj.group(1)
# extract simple title (uploader + slug of song title)
slug_title = mobj.group(2)
full_title = '%s/sets/%s' % (uploader, slug_title)
self.report_resolve(full_title)
url = 'http://soundcloud.com/%s/sets/%s' % (uploader, slug_title)
resolv_url = 'http://api.soundcloud.com/resolve.json?url=' + url + '&client_id=b45b1aa10f1ac2941910a7f0d10f8e28'
info_json = self._download_webpage(resolv_url, full_title)
videos = []
info = json.loads(info_json)
if 'errors' in info:
for err in info['errors']:
self._downloader.report_error(u'unable to download video webpage: %s' % compat_str(err['error_message']))
return
self.report_extraction(full_title)
for track in info['tracks']:
video_id = track['id']
streams_url = 'https://api.sndcdn.com/i1/tracks/' + str(video_id) + '/streams?client_id=b45b1aa10f1ac2941910a7f0d10f8e28'
stream_json = self._download_webpage(streams_url, video_id, u'Downloading track info JSON')
self.report_extraction(video_id)
streams = json.loads(stream_json)
mediaURL = streams['http_mp3_128_url']
videos.append({
'id': video_id,
'url': mediaURL,
'uploader': track['user']['username'],
'upload_date': unified_strdate(track['created_at']),
'title': track['title'],
'ext': u'mp3',
'description': track['description'],
})
return videos
-37
View File
@@ -1,37 +0,0 @@
import re
import xml.etree.ElementTree
from .common import InfoExtractor
class SpiegelIE(InfoExtractor):
_VALID_URL = r'https?://(?:www\.)?spiegel\.de/video/[^/]*-(?P<videoID>[0-9]+)(?:\.html)?(?:#.*)?$'
def _real_extract(self, url):
m = re.match(self._VALID_URL, url)
video_id = m.group('videoID')
webpage = self._download_webpage(url, video_id)
video_title = self._html_search_regex(r'<div class="module-title">(.*?)</div>',
webpage, u'title')
xml_url = u'http://video2.spiegel.de/flash/' + video_id + u'.xml'
xml_code = self._download_webpage(xml_url, video_id,
note=u'Downloading XML', errnote=u'Failed to download XML')
idoc = xml.etree.ElementTree.fromstring(xml_code)
last_type = idoc[-1]
filename = last_type.findall('./filename')[0].text
duration = float(last_type.findall('./duration')[0].text)
video_url = 'http://video2.spiegel.de/flash/' + filename
video_ext = filename.rpartition('.')[2]
info = {
'id': video_id,
'url': video_url,
'ext': video_ext,
'title': video_title,
'duration': duration,
}
return [info]

Some files were not shown because too many files have changed in this diff Show More