Compare commits
	
		
			952 Commits
		
	
	
		
			2011.09.18
			...
			2013.05.07
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					0f75d25991 | ||
| 
						 | 
					67129e4a15 | ||
| 
						 | 
					dfb9323cf9 | ||
| 
						 | 
					7f5bd09baf | ||
| 
						 | 
					02d5eb935f | ||
| 
						 | 
					94ca71b7cc | ||
| 
						 | 
					b338f1b154 | ||
| 
						 | 
					486f0c9476 | ||
| 
						 | 
					d96680f58d | ||
| 
						 | 
					f8602d3242 | ||
| 
						 | 
					0c021ad171 | ||
| 
						 | 
					086d7b4500 | ||
| 
						 | 
					891629c84a | ||
| 
						 | 
					ea6d901e51 | ||
| 
						 | 
					4539dd30e6 | ||
| 
						 | 
					c43e57242e | ||
| 
						 | 
					db8fd71ca9 | ||
| 
						 | 
					f4f316881d | ||
| 
						 | 
					0e16f09474 | ||
| 
						 | 
					09dd418f53 | ||
| 
						 | 
					decd1d1737 | ||
| 
						 | 
					180e689f7e | ||
| 
						 | 
					7da5556ac2 | ||
| 
						 | 
					f23a03a89b | ||
| 
						 | 
					84e4682f0e | ||
| 
						 | 
					1f99511210 | ||
| 
						 | 
					0d94f2474c | ||
| 
						 | 
					480b6c1e8b | ||
| 
						 | 
					95464f14d1 | ||
| 
						 | 
					c34407d16c | ||
| 
						 | 
					5e34d2ebbf | ||
| 
						 | 
					815dd2ffa8 | ||
| 
						 | 
					ecd5fb49c5 | ||
| 
						 | 
					b86174e7a3 | ||
| 
						 | 
					2e2038dc35 | ||
| 
						 | 
					46bfb42258 | ||
| 
						 | 
					feecf22511 | ||
| 
						 | 
					4c4f15eb78 | ||
| 
						 | 
					104ccdb8b4 | ||
| 
						 | 
					6ccff79594 | ||
| 
						 | 
					aed523ecc1 | ||
| 
						 | 
					d496a75d0a | ||
| 
						 | 
					5c01dd1e73 | ||
| 
						 | 
					11d9224e3b | ||
| 
						 | 
					34c29ba1d7 | ||
| 
						 | 
					6cd657f9f2 | ||
| 
						 | 
					4ae9e55822 | ||
| 
						 | 
					8749b71273 | ||
| 
						 | 
					dbc50fdf82 | ||
| 
						 | 
					b1d2ef9255 | ||
| 
						 | 
					5fb16555af | ||
| 
						 | 
					ba7c775a04 | ||
| 
						 | 
					fe348844d9 | ||
| 
						 | 
					767e00277f | ||
| 
						 | 
					6ce533a220 | ||
| 
						 | 
					08b2ac745a | ||
| 
						 | 
					46a127eecb | ||
| 
						 | 
					fc63faf070 | ||
| 
						 | 
					9665577802 | ||
| 
						 | 
					434aca5b14 | ||
| 
						 | 
					e31852aba9 | ||
| 
						 | 
					37254abc36 | ||
| 
						 | 
					a11ea50319 | ||
| 
						 | 
					81df121dd3 | ||
| 
						 | 
					50f6412eb8 | ||
| 
						 | 
					bf50b0383e | ||
| 
						 | 
					bd55852517 | ||
| 
						 | 
					4c9f7a9988 | ||
| 
						 | 
					aba8df23ed | ||
| 
						 | 
					3820df0106 | ||
| 
						 | 
					e74c504f91 | ||
| 
						 | 
					fa70605db2 | ||
| 
						 | 
					0d173446ff | ||
| 
						 | 
					320e26a0af | ||
| 
						 | 
					a3d689cfb3 | ||
| 
						 | 
					59cc5d9380 | ||
| 
						 | 
					28535652ab | ||
| 
						 | 
					7b670a4483 | ||
| 
						 | 
					69fc019f26 | ||
| 
						 | 
					613bf66939 | ||
| 
						 | 
					9edb0916f4 | ||
| 
						 | 
					f4b659f782 | ||
| 
						 | 
					c70446c7df | ||
| 
						 | 
					c76cb6d548 | ||
| 
						 | 
					71f37e90ef | ||
| 
						 | 
					75b5c590a8 | ||
| 
						 | 
					4469666780 | ||
| 
						 | 
					c15e024141 | ||
| 
						 | 
					8cb94542f4 | ||
| 
						 | 
					c681a03918 | ||
| 
						 | 
					30f2999962 | ||
| 
						 | 
					74e3452b9e | ||
| 
						 | 
					9e1cf0c200 | ||
| 
						 | 
					e11eb11906 | ||
| 
						 | 
					c04bca6f60 | ||
| 
						 | 
					b0936ef423 | ||
| 
						 | 
					41a6eb949a | ||
| 
						 | 
					f17ce13a92 | ||
| 
						 | 
					8c416ad29a | ||
| 
						 | 
					c72938240e | ||
| 
						 | 
					e905b6f80e | ||
| 
						 | 
					6de8f1afb7 | ||
| 
						 | 
					9341212642 | ||
| 
						 | 
					f7a9721e16 | ||
| 
						 | 
					089e843b0f | ||
| 
						 | 
					c8056d866a | ||
| 
						 | 
					49da66e459 | ||
| 
						 | 
					fb6c319904 | ||
| 
						 | 
					5a8d13199c | ||
| 
						 | 
					dce9027045 | ||
| 
						 | 
					feba604e92 | ||
| 
						 | 
					d22f65413a | ||
| 
						 | 
					0599ef8c08 | ||
| 
						 | 
					bfdf469295 | ||
| 
						 | 
					32c96387c1 | ||
| 
						 | 
					c8c5443bb5 | ||
| 
						 | 
					a60b854d90 | ||
| 
						 | 
					b8ad4f02a2 | ||
| 
						 | 
					d281274bf2 | ||
| 
						 | 
					b625bc2c31 | ||
| 
						 | 
					f4381ab88a | ||
| 
						 | 
					744435f2a4 | ||
| 
						 | 
					855703e55e | ||
| 
						 | 
					927c8c4924 | ||
| 
						 | 
					0ba994e9e3 | ||
| 
						 | 
					af9ad45cd4 | ||
| 
						 | 
					e0fee250c3 | ||
| 
						 | 
					72ca05016d | ||
| 
						 | 
					844d1f9fa1 | ||
| 
						 | 
					213c31ae16 | ||
| 
						 | 
					04f3d551a0 | ||
| 
						 | 
					e8600d69fd | ||
| 
						 | 
					b03d65c237 | ||
| 
						 | 
					8743974189 | ||
| 
						 | 
					dc36bc9434 | ||
| 
						 | 
					bce878a7c1 | ||
| 
						 | 
					532d797824 | ||
| 
						 | 
					146c12a2da | ||
| 
						 | 
					d39919c03e | ||
| 
						 | 
					df2dedeefb | ||
| 
						 | 
					adb029ed81 | ||
| 
						 | 
					43ff1a347d | ||
| 
						 | 
					14294236bf | ||
| 
						 | 
					c2b293ba30 | ||
| 
						 | 
					37cd9f522f | ||
| 
						 | 
					f33154cd39 | ||
| 
						 | 
					bafeed9f5d | ||
| 
						 | 
					ef767f9fd5 | ||
| 
						 | 
					bc97f6d60c | ||
| 
						 | 
					90a99c1b5e | ||
| 
						 | 
					f375d4b7de | ||
| 
						 | 
					fa41fbd318 | ||
| 
						 | 
					6a205c8876 | ||
| 
						 | 
					0fb3756409 | ||
| 
						 | 
					fbbdf475b1 | ||
| 
						 | 
					c238be3e3a | ||
| 
						 | 
					1bf2801e6a | ||
| 
						 | 
					c9c8402093 | ||
| 
						 | 
					6060788083 | ||
| 
						 | 
					e3700fc9e4 | ||
| 
						 | 
					b693216d8d | ||
| 
						 | 
					46b9d8295d | ||
| 
						 | 
					7decf8951c | ||
| 
						 | 
					1f46c15262 | ||
| 
						 | 
					0cd358676c | ||
| 
						 | 
					43113d92cc | ||
| 
						 | 
					7eab8dc750 | ||
| 
						 | 
					44e939514e | ||
| 
						 | 
					95506f1235 | ||
| 
						 | 
					a91556fd74 | ||
| 
						 | 
					1447f728b5 | ||
| 
						 | 
					d2c690828a | ||
| 
						 | 
					cfa90f4adc | ||
| 
						 | 
					898280a056 | ||
| 
						 | 
					59b4a2f0e4 | ||
| 
						 | 
					1ee9778405 | ||
| 
						 | 
					db74c11d2b | ||
| 
						 | 
					5011cded16 | ||
| 
						 | 
					f10b2a9c14 | ||
| 
						 | 
					5cb3c0b319 | ||
| 
						 | 
					b9fc428494 | ||
| 
						 | 
					c0ba104674 | ||
| 
						 | 
					2a4093eaf3 | ||
| 
						 | 
					9e62bc4439 | ||
| 
						 | 
					553d097442 | ||
| 
						 | 
					ae608b8076 | ||
| 
						 | 
					c397187061 | ||
| 
						 | 
					e32b06e977 | ||
| 
						 | 
					8c42c506cd | ||
| 
						 | 
					8cc83b8dbe | ||
| 
						 | 
					51af426d89 | ||
| 
						 | 
					08ec0af7c6 | ||
| 
						 | 
					3b221c5406 | ||
| 
						 | 
					3d3423574d | ||
| 
						 | 
					e5edd51de4 | ||
| 
						 | 
					64c78d50cc | ||
| 
						 | 
					b3bcca0844 | ||
| 
						 | 
					61e40c88a9 | ||
| 
						 | 
					40634747f7 | ||
| 
						 | 
					c2e21f2f0d | ||
| 
						 | 
					47dcd621c0 | ||
| 
						 | 
					a0d6fe7b92 | ||
| 
						 | 
					c9fa1cbab6 | ||
| 
						 | 
					8a38a194fb | ||
| 
						 | 
					6ac7f082c4 | ||
| 
						 | 
					f6e6da9525 | ||
| 
						 | 
					597cc8a455 | ||
| 
						 | 
					3370abd509 | ||
| 
						 | 
					631f73978c | ||
| 
						 | 
					e5f30ade10 | ||
| 
						 | 
					6622d22c79 | ||
| 
						 | 
					4e1582f372 | ||
| 
						 | 
					967897fd22 | ||
| 
						 | 
					f918ec7ea2 | ||
| 
						 | 
					a2ae43a55f | ||
| 
						 | 
					7ae153ee9c | ||
| 
						 | 
					f7b567ff84 | ||
| 
						 | 
					f2e237adc8 | ||
| 
						 | 
					2e5457be1d | ||
| 
						 | 
					7f9d41a55e | ||
| 
						 | 
					8207626bbe | ||
| 
						 | 
					df8db1aa21 | ||
| 
						 | 
					691db5ba02 | ||
| 
						 | 
					acb8752f80 | ||
| 
						 | 
					679790eee1 | ||
| 
						 | 
					6bf48bd866 | ||
| 
						 | 
					790d4fcbe1 | ||
| 
						 | 
					89de9eb125 | ||
| 
						 | 
					6324fd1d74 | ||
| 
						 | 
					9e07cf2955 | ||
| 
						 | 
					f03b88b3fb | ||
| 
						 | 
					97d0365f49 | ||
| 
						 | 
					12887875a2 | ||
| 
						 | 
					450e709972 | ||
| 
						 | 
					9befce2b8c | ||
| 
						 | 
					cb99797798 | ||
| 
						 | 
					f82b28146a | ||
| 
						 | 
					4dc72b830c | ||
| 
						 | 
					ea05129ebd | ||
| 
						 | 
					35d217133f | ||
| 
						 | 
					d1b7a24354 | ||
| 
						 | 
					c85538dba1 | ||
| 
						 | 
					60bd48b175 | ||
| 
						 | 
					4be0aa3539 | ||
| 
						 | 
					f636c34481 | ||
| 
						 | 
					3bf79c752e | ||
| 
						 | 
					cdb130b09a | ||
| 
						 | 
					2e5d60b7db | ||
| 
						 | 
					8271226a55 | ||
| 
						 | 
					1013186a17 | ||
| 
						 | 
					7c038b3c32 | ||
| 
						 | 
					c8cd8e5f55 | ||
| 
						 | 
					471cf47796 | ||
| 
						 | 
					d8f64574a4 | ||
| 
						 | 
					e711babbd1 | ||
| 
						 | 
					a72b0f2b6f | ||
| 
						 | 
					434eb6f26b | ||
| 
						 | 
					197080b10b | ||
| 
						 | 
					7796e8c2cb | ||
| 
						 | 
					6d4363368a | ||
| 
						 | 
					414638cd50 | ||
| 
						 | 
					2a9983b78f | ||
| 
						 | 
					b17c974a88 | ||
| 
						 | 
					5717d91ab7 | ||
| 
						 | 
					79eb0287ab | ||
| 
						 | 
					58994225bc | ||
| 
						 | 
					59d4c2fe1b | ||
| 
						 | 
					3a468f2d8b | ||
| 
						 | 
					1ad5d872b9 | ||
| 
						 | 
					355fc8e944 | ||
| 
						 | 
					380a29dbf7 | ||
| 
						 | 
					1528d6642d | ||
| 
						 | 
					7311fef854 | ||
| 
						 | 
					906417c7c5 | ||
| 
						 | 
					6aabe82035 | ||
| 
						 | 
					f0877a445e | ||
| 
						 | 
					da06e2daf8 | ||
| 
						 | 
					d3f5f9f6b9 | ||
| 
						 | 
					bfc6ea7935 | ||
| 
						 | 
					8edc2cf8ca | ||
| 
						 | 
					fb778e66df | ||
| 
						 | 
					3a9918d37f | ||
| 
						 | 
					ccb0cae134 | ||
| 
						 | 
					085c8b75a6 | ||
| 
						 | 
					dbf2ba3d61 | ||
| 
						 | 
					b47bbac393 | ||
| 
						 | 
					229cac754a | ||
| 
						 | 
					0e33684194 | ||
| 
						 | 
					9e982f9e4e | ||
| 
						 | 
					c7a725cfad | ||
| 
						 | 
					450a30cae8 | ||
| 
						 | 
					9cd5e4fce8 | ||
| 
						 | 
					edba5137b8 | ||
| 
						 | 
					233a22960a | ||
| 
						 | 
					3b024e17af | ||
| 
						 | 
					a32b573ccb | ||
| 
						 | 
					ec71c13ab8 | ||
| 
						 | 
					f0bad2b026 | ||
| 
						 | 
					25580f3251 | ||
| 
						 | 
					da4de959df | ||
| 
						 | 
					d0d51a8afa | ||
| 
						 | 
					c67598c3e1 | ||
| 
						 | 
					811d253bc2 | ||
| 
						 | 
					c3a1642ead | ||
| 
						 | 
					ccf65f9dee | ||
| 
						 | 
					b954070d70 | ||
| 
						 | 
					30e9f4496b | ||
| 
						 | 
					271d3fbdaa | ||
| 
						 | 
					6df40dcbe0 | ||
| 
						 | 
					97f194c1fb | ||
| 
						 | 
					4da769ccca | ||
| 
						 | 
					253d96f2e2 | ||
| 
						 | 
					bbc3e2753a | ||
| 
						 | 
					67353612ba | ||
| 
						 | 
					bffbd5f038 | ||
| 
						 | 
					d8bbf2018e | ||
| 
						 | 
					187f491ad2 | ||
| 
						 | 
					335959e778 | ||
| 
						 | 
					3b83bf8f6a | ||
| 
						 | 
					51719893bf | ||
| 
						 | 
					1841f65e64 | ||
| 
						 | 
					bb28998920 | ||
| 
						 | 
					fbc5f99db9 | ||
| 
						 | 
					ca0a0bbeec | ||
| 
						 | 
					6119f78cb9 | ||
| 
						 | 
					539679c7f9 | ||
| 
						 | 
					b642cd44c1 | ||
| 
						 | 
					fffec3b9d9 | ||
| 
						 | 
					3446dfb7cb | ||
| 
						 | 
					db16276b7c | ||
| 
						 | 
					629fcdd135 | ||
| 
						 | 
					64ce2aada8 | ||
| 
						 | 
					565f751967 | ||
| 
						 | 
					6017964580 | ||
| 
						 | 
					1d16b0c3fe | ||
| 
						 | 
					7851b37993 | ||
| 
						 | 
					d81edc573e | ||
| 
						 | 
					ef0c8d5f9f | ||
| 
						 | 
					db30f02b50 | ||
| 
						 | 
					4ba7262467 | ||
| 
						 | 
					67d0c25eab | ||
| 
						 | 
					09f9552b40 | ||
| 
						 | 
					142d38f776 | ||
| 
						 | 
					6dd3471900 | ||
| 
						 | 
					280d67896a | ||
| 
						 | 
					510e6f6dc1 | ||
| 
						 | 
					712e86b999 | ||
| 
						 | 
					74fdba620d | ||
| 
						 | 
					dc1c479a6f | ||
| 
						 | 
					119d536e07 | ||
| 
						 | 
					fa1bf9c653 | ||
| 
						 | 
					814eed0ea1 | ||
| 
						 | 
					0aa3068e9e | ||
| 
						 | 
					db2d6124b1 | ||
| 
						 | 
					039dc61bd2 | ||
| 
						 | 
					4b879984ea | ||
| 
						 | 
					55e286ba55 | ||
| 
						 | 
					9450bfa26e | ||
| 
						 | 
					18be482a6f | ||
| 
						 | 
					ca6710ee41 | ||
| 
						 | 
					9314810243 | ||
| 
						 | 
					7717ae19fa | ||
| 
						 | 
					32635ec685 | ||
| 
						 | 
					caec7618a1 | ||
| 
						 | 
					7e7ab2815c | ||
| 
						 | 
					d7744f2219 | ||
| 
						 | 
					7161829de5 | ||
| 
						 | 
					991ba7fae3 | ||
| 
						 | 
					a7539296ce | ||
| 
						 | 
					258d5850c9 | ||
| 
						 | 
					20759b340a | ||
| 
						 | 
					8e5f761870 | ||
| 
						 | 
					26714799c9 | ||
| 
						 | 
					5e9d042d8f | ||
| 
						 | 
					9cf98a2bcc | ||
| 
						 | 
					f5ebb61495 | ||
| 
						 | 
					431d88dd31 | ||
| 
						 | 
					876f1a86af | ||
| 
						 | 
					01951dda7a | ||
| 
						 | 
					6e3dba168b | ||
| 
						 | 
					d851e895d5 | ||
| 
						 | 
					b962b76f43 | ||
| 
						 | 
					26cf040827 | ||
| 
						 | 
					8e241d1a1a | ||
| 
						 | 
					3a648b209c | ||
| 
						 | 
					c80f0a417a | ||
| 
						 | 
					4fcca4bb18 | ||
| 
						 | 
					511eda8eda | ||
| 
						 | 
					5f9551719c | ||
| 
						 | 
					d830b7c297 | ||
| 
						 | 
					1c256f7047 | ||
| 
						 | 
					a34dd63beb | ||
| 
						 | 
					4aeae91f86 | ||
| 
						 | 
					c073e35b1e | ||
| 
						 | 
					5c892b0ba9 | ||
| 
						 | 
					6985325e01 | ||
| 
						 | 
					911ee27e83 | ||
| 
						 | 
					2069acc6a4 | ||
| 
						 | 
					278986ea0f | ||
| 
						 | 
					6535e9511f | ||
| 
						 | 
					60c7520a51 | ||
| 
						 | 
					deb594a9a0 | ||
| 
						 | 
					e314ba675b | ||
| 
						 | 
					0214ce7c75 | ||
| 
						 | 
					95fedbf86b | ||
| 
						 | 
					b7769a05ec | ||
| 
						 | 
					067f6a3536 | ||
| 
						 | 
					8cad53e84c | ||
| 
						 | 
					d5ed35b664 | ||
| 
						 | 
					f427df17ab | ||
| 
						 | 
					4e38899e97 | ||
| 
						 | 
					cb6ff87fbb | ||
| 
						 | 
					0deac3a2d8 | ||
| 
						 | 
					92e3e18a1d | ||
| 
						 | 
					3bb6165927 | ||
| 
						 | 
					d0d4f277da | ||
| 
						 | 
					99b0a1292b | ||
| 
						 | 
					dc23886a77 | ||
| 
						 | 
					b7298b6e2a | ||
| 
						 | 
					3e6c3f52a9 | ||
| 
						 | 
					0c0074328b | ||
| 
						 | 
					f0648fc18c | ||
| 
						 | 
					a7c0f8602e | ||
| 
						 | 
					21a9c6aaac | ||
| 
						 | 
					162e3c5261 | ||
| 
						 | 
					6b3aef80ce | ||
| 
						 | 
					77c4beab8a | ||
| 
						 | 
					1a2c3c0f3e | ||
| 
						 | 
					0eaf520d77 | ||
| 
						 | 
					056d857571 | ||
| 
						 | 
					69a3883199 | ||
| 
						 | 
					0dcfb234ed | ||
| 
						 | 
					43e8fafd49 | ||
| 
						 | 
					314d506b96 | ||
| 
						 | 
					af42895612 | ||
| 
						 | 
					bfa6389b74 | ||
| 
						 | 
					9b14f51a3e | ||
| 
						 | 
					f4bfd65ff2 | ||
| 
						 | 
					3cc687d486 | ||
| 
						 | 
					cdb3076445 | ||
| 
						 | 
					8a2f13c304 | ||
| 
						 | 
					77bd7968ea | ||
| 
						 | 
					993693aa79 | ||
| 
						 | 
					ce4be3a91d | ||
| 
						 | 
					937021133f | ||
| 
						 | 
					f7b111b7d1 | ||
| 
						 | 
					80d3177e5c | ||
| 
						 | 
					5e5ddcfbcf | ||
| 
						 | 
					5910e210f4 | ||
| 
						 | 
					b375c8b946 | ||
| 
						 | 
					88f6c78b02 | ||
| 
						 | 
					4096b60948 | ||
| 
						 | 
					2ab1c5ed1a | ||
| 
						 | 
					0b40544f29 | ||
| 
						 | 
					187da2c093 | ||
| 
						 | 
					9a2cf56d51 | ||
| 
						 | 
					0be41ec241 | ||
| 
						 | 
					f1171f7c2d | ||
| 
						 | 
					28ca6b5afa | ||
| 
						 | 
					bec102a843 | ||
| 
						 | 
					8f6f40d991 | ||
| 
						 | 
					e2a8ff24a9 | ||
| 
						 | 
					8588a86f9e | ||
| 
						 | 
					5cb9c3129b | ||
| 
						 | 
					4cc3d07426 | ||
| 
						 | 
					5d01a64719 | ||
| 
						 | 
					a276e06080 | ||
| 
						 | 
					fd5ff02042 | ||
| 
						 | 
					2b5b2cb84c | ||
| 
						 | 
					ca6849e65d | ||
| 
						 | 
					1535ac2ae9 | ||
| 
						 | 
					a4680a590f | ||
| 
						 | 
					fedb6816cd | ||
| 
						 | 
					f6152b4b64 | ||
| 
						 | 
					4b618047ce | ||
| 
						 | 
					2c6945be30 | ||
| 
						 | 
					9a6f4429a0 | ||
| 
						 | 
					4c21c56bfe | ||
| 
						 | 
					2a298b72eb | ||
| 
						 | 
					55c0539872 | ||
| 
						 | 
					9789a05c20 | ||
| 
						 | 
					d050de77f9 | ||
| 
						 | 
					95eb771dcd | ||
| 
						 | 
					4fb1acc212 | ||
| 
						 | 
					d3d3199870 | ||
| 
						 | 
					1ca63e3ae3 | ||
| 
						 | 
					59ce201915 | ||
| 
						 | 
					8d5d3a5d00 | ||
| 
						 | 
					37c8fd4842 | ||
| 
						 | 
					3c6ffbaedb | ||
| 
						 | 
					c7287a3caf | ||
| 
						 | 
					5a304a7637 | ||
| 
						 | 
					4c1d273e88 | ||
| 
						 | 
					a9d2f7e894 | ||
| 
						 | 
					682407f2d5 | ||
| 
						 | 
					bdff345529 | ||
| 
						 | 
					23109d6a9c | ||
| 
						 | 
					4bb028f48e | ||
| 
						 | 
					fec89790b1 | ||
| 
						 | 
					a5741a3f5e | ||
| 
						 | 
					863baa16ec | ||
| 
						 | 
					c7214f9a6f | ||
| 
						 | 
					8fd3afd56c | ||
| 
						 | 
					f9b2f2b955 | ||
| 
						 | 
					633b4a5ff6 | ||
| 
						 | 
					b4cd069d5e | ||
| 
						 | 
					0f8d03f81c | ||
| 
						 | 
					077174f4ed | ||
| 
						 | 
					e387eb5aba | ||
| 
						 | 
					4083bf81a0 | ||
| 
						 | 
					796173d08b | ||
| 
						 | 
					e575b6821e | ||
| 
						 | 
					d78be7e331 | ||
| 
						 | 
					15c8d83358 | ||
| 
						 | 
					e91d2338d8 | ||
| 
						 | 
					4b235346d6 | ||
| 
						 | 
					ad348291bb | ||
| 
						 | 
					2f1765c4ea | ||
| 
						 | 
					3c5b63d2d6 | ||
| 
						 | 
					cc51a7d4e0 | ||
| 
						 | 
					8af4ed7b4f | ||
| 
						 | 
					8192ebe1f8 | ||
| 
						 | 
					20ba04267c | ||
| 
						 | 
					743b28ce11 | ||
| 
						 | 
					caaa47d372 | ||
| 
						 | 
					10f100ac8a | ||
| 
						 | 
					8176041605 | ||
| 
						 | 
					87bec4c715 | ||
| 
						 | 
					190e8e27d8 | ||
| 
						 | 
					4efe62a016 | ||
| 
						 | 
					c64de2c980 | ||
| 
						 | 
					6ad98fb3fd | ||
| 
						 | 
					b08e09c370 | ||
| 
						 | 
					cdab8aa389 | ||
| 
						 | 
					3cd69a54b2 | ||
| 
						 | 
					627dcfff39 | ||
| 
						 | 
					df5cff3751 | ||
| 
						 | 
					79ae0a06d5 | ||
| 
						 | 
					2d2fa229ec | ||
| 
						 | 
					5a59fd6392 | ||
| 
						 | 
					0eb0faa26f | ||
| 
						 | 
					32761d863c | ||
| 
						 | 
					799c076384 | ||
| 
						 | 
					f1cb5bcad2 | ||
| 
						 | 
					9e8056d5a7 | ||
| 
						 | 
					c6f3620859 | ||
| 
						 | 
					59ae15a507 | ||
| 
						 | 
					40b35b4aa6 | ||
| 
						 | 
					be0f77d075 | ||
| 
						 | 
					0f00efed4c | ||
| 
						 | 
					e6137fd61d | ||
| 
						 | 
					8cd10ac4ef | ||
| 
						 | 
					64a57846d3 | ||
| 
						 | 
					72f976701a | ||
| 
						 | 
					5bd9cc7a6a | ||
| 
						 | 
					f660c89d51 | ||
| 
						 | 
					73dce4b2e4 | ||
| 
						 | 
					9f37a95941 | ||
| 
						 | 
					a130bc6d02 | ||
| 
						 | 
					348d0a7a18 | ||
| 
						 | 
					03f9daab34 | ||
| 
						 | 
					a8156c1d2e | ||
| 
						 | 
					3e669f369f | ||
| 
						 | 
					da779b4924 | ||
| 
						 | 
					89fb51dd2d | ||
| 
						 | 
					01ba00ca42 | ||
| 
						 | 
					e08bee320e | ||
| 
						 | 
					96731798db | ||
| 
						 | 
					c116339ddb | ||
| 
						 | 
					e643e2c6b7 | ||
| 
						 | 
					c63cc10ffa | ||
| 
						 | 
					dae7c920f6 | ||
| 
						 | 
					f462df021a | ||
| 
						 | 
					1a84d8675b | ||
| 
						 | 
					18ea0cefc3 | ||
| 
						 | 
					c806f804d8 | ||
| 
						 | 
					03c5b0fbd4 | ||
| 
						 | 
					95649b3936 | ||
| 
						 | 
					3aeb78ea4e | ||
| 
						 | 
					dd109dee8e | ||
| 
						 | 
					b514df2034 | ||
| 
						 | 
					0969bdd305 | ||
| 
						 | 
					1a9c655e3b | ||
| 
						 | 
					88db5ef279 | ||
| 
						 | 
					f8d8b39bba | ||
| 
						 | 
					dcd60025f8 | ||
| 
						 | 
					7e4674830e | ||
| 
						 | 
					9ce5d9ee75 | ||
| 
						 | 
					b49e75ff9a | ||
| 
						 | 
					abe7a3ac2a | ||
| 
						 | 
					717b1f72ed | ||
| 
						 | 
					26396311b5 | ||
| 
						 | 
					dffe658bac | ||
| 
						 | 
					33d94a6c99 | ||
| 
						 | 
					4d47921c9e | ||
| 
						 | 
					d94adc2638 | ||
| 
						 | 
					5c5d06d31d | ||
| 
						 | 
					cc872b68a8 | ||
| 
						 | 
					17cb14a336 | ||
| 
						 | 
					877f4c45d3 | ||
| 
						 | 
					02531431f2 | ||
| 
						 | 
					e02066e7ff | ||
| 
						 | 
					c9128b353d | ||
| 
						 | 
					e7c6f1a2dc | ||
| 
						 | 
					1a911e60a4 | ||
| 
						 | 
					46cbda0be4 | ||
| 
						 | 
					fa59f4b6a9 | ||
| 
						 | 
					4a702f3819 | ||
| 
						 | 
					6bac102a4d | ||
| 
						 | 
					958a22b7cf | ||
| 
						 | 
					97cd3afc75 | ||
| 
						 | 
					aa2a94ed81 | ||
| 
						 | 
					c7032546f1 | ||
| 
						 | 
					56781d3d2e | ||
| 
						 | 
					feb22fe5fe | ||
| 
						 | 
					d8dddb7c02 | ||
| 
						 | 
					4408d996fb | ||
| 
						 | 
					ed7516c69d | ||
| 
						 | 
					89af8e9d32 | ||
| 
						 | 
					36a9c0b5ff | ||
| 
						 | 
					9fb3bfb45a | ||
| 
						 | 
					d479e34043 | ||
| 
						 | 
					240089e5df | ||
| 
						 | 
					1c469a9480 | ||
| 
						 | 
					71f36332dd | ||
| 
						 | 
					8179d2ba74 | ||
| 
						 | 
					df4bad3245 | ||
| 
						 | 
					a7b5c8d6a8 | ||
| 
						 | 
					92b91c1878 | ||
| 
						 | 
					7ec1a206ea | ||
| 
						 | 
					51937c0869 | ||
| 
						 | 
					6b50761222 | ||
| 
						 | 
					6571408dc6 | ||
| 
						 | 
					b6fab35b9f | ||
| 
						 | 
					baec15387c | ||
| 
						 | 
					297d7fd9c0 | ||
| 
						 | 
					5002aea371 | ||
| 
						 | 
					5f7ad21633 | ||
| 
						 | 
					089d47f8d5 | ||
| 
						 | 
					74033a662d | ||
| 
						 | 
					fdef722fa1 | ||
| 
						 | 
					110d4f4c91 | ||
| 
						 | 
					0526e4f55a | ||
| 
						 | 
					39973a0236 | ||
| 
						 | 
					5d40a470a2 | ||
| 
						 | 
					4cc391461a | ||
| 
						 | 
					bf95333e5e | ||
| 
						 | 
					b7a34316d2 | ||
| 
						 | 
					74e453bdea | ||
| 
						 | 
					156a59e7a9 | ||
| 
						 | 
					aeca861f22 | ||
| 
						 | 
					42cb53fcfa | ||
| 
						 | 
					fe4d68e196 | ||
| 
						 | 
					25b7fd9c01 | ||
| 
						 | 
					e79e8b7dc4 | ||
| 
						 | 
					965a8b2bc4 | ||
| 
						 | 
					a8ac2f8664 | ||
| 
						 | 
					fb0e99b884 | ||
| 
						 | 
					9c6e9a4532 | ||
| 
						 | 
					67af74992e | ||
| 
						 | 
					103c508ffa | ||
| 
						 | 
					2876773381 | ||
| 
						 | 
					f06eaa873e | ||
| 
						 | 
					ece34e8951 | ||
| 
						 | 
					2262a32dd7 | ||
| 
						 | 
					c6c0e23a32 | ||
| 
						 | 
					02b324a23d | ||
| 
						 | 
					b8005afc20 | ||
| 
						 | 
					073522bc6c | ||
| 
						 | 
					9248cb0549 | ||
| 
						 | 
					6b41b61119 | ||
| 
						 | 
					591bbe9c90 | ||
| 
						 | 
					fc7376016c | ||
| 
						 | 
					97a37c2319 | ||
| 
						 | 
					3afed78a6a | ||
| 
						 | 
					4279a0ca98 | ||
| 
						 | 
					edcc7d2dd3 | ||
| 
						 | 
					7f60b5aa40 | ||
| 
						 | 
					65adb79fb6 | ||
| 
						 | 
					aeeb29a356 | ||
| 
						 | 
					902b2a0a45 | ||
| 
						 | 
					6d9c22cd26 | ||
| 
						 | 
					729baf58b2 | ||
| 
						 | 
					4c9afeca34 | ||
| 
						 | 
					6da7877bf5 | ||
| 
						 | 
					b4e5de51ec | ||
| 
						 | 
					a4b5f22554 | ||
| 
						 | 
					ff08984246 | ||
| 
						 | 
					137c5803c3 | ||
| 
						 | 
					3eec021a1f | ||
| 
						 | 
					5a33b73309 | ||
| 
						 | 
					0b4e98490b | ||
| 
						 | 
					80a846e119 | ||
| 
						 | 
					434d60cd95 | ||
| 
						 | 
					efe8902f0b | ||
| 
						 | 
					44fb345437 | ||
| 
						 | 
					9993976ae4 | ||
| 
						 | 
					b387fb0385 | ||
| 
						 | 
					10daa766a1 | ||
| 
						 | 
					7b107eea51 | ||
| 
						 | 
					646b885cbf | ||
| 
						 | 
					0bfd0b598a | ||
| 
						 | 
					fd873c69a4 | ||
| 
						 | 
					d64db7409b | ||
| 
						 | 
					27fec0e3bd | ||
| 
						 | 
					65f934dc93 | ||
| 
						 | 
					d51d784f85 | ||
| 
						 | 
					aa85963987 | ||
| 
						 | 
					413575f7a5 | ||
| 
						 | 
					b7b4796bf2 | ||
| 
						 | 
					fcbc8c830e | ||
| 
						 | 
					f48ce130c7 | ||
| 
						 | 
					13e69f546c | ||
| 
						 | 
					63ec7b7479 | ||
| 
						 | 
					7b6d7001d8 | ||
| 
						 | 
					39ce6e79e7 | ||
| 
						 | 
					5c961d89df | ||
| 
						 | 
					3c4d6c9eba | ||
| 
						 | 
					349e2e3e21 | ||
| 
						 | 
					551fa9dfbf | ||
| 
						 | 
					ce3674430b | ||
| 
						 | 
					5cdfaeb37b | ||
| 
						 | 
					38612b4edc | ||
| 
						 | 
					6c5b442a9b | ||
| 
						 | 
					5a5523698d | ||
| 
						 | 
					05a2c206be | ||
| 
						 | 
					8ca21983d8 | ||
| 
						 | 
					20326b8b1b | ||
| 
						 | 
					5d534e2fe6 | ||
| 
						 | 
					234e230c87 | ||
| 
						 | 
					34ae0f9d20 | ||
| 
						 | 
					df09e5f9e1 | ||
| 
						 | 
					3af2f7656c | ||
| 
						 | 
					74e716bb64 | ||
| 
						 | 
					85f76ac90b | ||
| 
						 | 
					7f36e39676 | ||
| 
						 | 
					ebe3f89ea4 | ||
| 
						 | 
					b5de8af234 | ||
| 
						 | 
					eb817499b0 | ||
| 
						 | 
					e2af9232b2 | ||
| 
						 | 
					9ca667065e | ||
| 
						 | 
					ae16f68f4a | ||
| 
						 | 
					3cd98c7894 | ||
| 
						 | 
					2866e68838 | ||
| 
						 | 
					be8786a6a4 | ||
| 
						 | 
					0e841bdc54 | ||
| 
						 | 
					225dceb046 | ||
| 
						 | 
					b0d4f95899 | ||
| 
						 | 
					d443aca863 | ||
| 
						 | 
					2ebc6e6a92 | ||
| 
						 | 
					f2ad10a97d | ||
| 
						 | 
					ea46fe2dd4 | ||
| 
						 | 
					202e76cfb0 | ||
| 
						 | 
					3a68d7b467 | ||
| 
						 | 
					795cc5059a | ||
| 
						 | 
					5dc846fad0 | ||
| 
						 | 
					d5c4c4c10e | ||
| 
						 | 
					1ac3e3315e | ||
| 
						 | 
					0e4dc2fc74 | ||
| 
						 | 
					9bb8dc8e42 | ||
| 
						 | 
					154b55dae3 | ||
| 
						 | 
					6de7ef9b8d | ||
| 
						 | 
					392105265c | ||
| 
						 | 
					51661d8600 | ||
| 
						 | 
					b5809a68bf | ||
| 
						 | 
					7733d455c8 | ||
| 
						 | 
					0a98b09bc2 | ||
| 
						 | 
					302efc19ea | ||
| 
						 | 
					55a1fa8a56 | ||
| 
						 | 
					dce1088450 | ||
| 
						 | 
					a171dbfc27 | ||
| 
						 | 
					11a141dec9 | ||
| 
						 | 
					818282710b | ||
| 
						 | 
					7a7c093ab0 | ||
| 
						 | 
					ce7b2a40d0 | ||
| 
						 | 
					cfcec69331 | ||
| 
						 | 
					91645066e2 | ||
| 
						 | 
					dee5d76923 | ||
| 
						 | 
					363a4e1114 | ||
| 
						 | 
					ef0c08cdfe | ||
| 
						 | 
					3210735c49 | ||
| 
						 | 
					aab4fca422 | ||
| 
						 | 
					891d7f2329 | ||
| 
						 | 
					b24676ce88 | ||
| 
						 | 
					cca4828ac9 | ||
| 
						 | 
					bae611f216 | ||
| 
						 | 
					d4e16d3e97 | ||
| 
						 | 
					65dc7d0272 | ||
| 
						 | 
					5404179338 | ||
| 
						 | 
					7df97fb59f | ||
| 
						 | 
					3187e42a23 | ||
| 
						 | 
					f1927d71e4 | ||
| 
						 | 
					eeeb4daabc | ||
| 
						 | 
					3c4fc580bb | ||
| 
						 | 
					17f3c40a31 | ||
| 
						 | 
					505ed3088f | ||
| 
						 | 
					0b976545c7 | ||
| 
						 | 
					a047951477 | ||
| 
						 | 
					6ab92c8b62 | ||
| 
						 | 
					f36cd07685 | ||
| 
						 | 
					668d975039 | ||
| 
						 | 
					9ab3406ddb | ||
| 
						 | 
					1b91a2e2cf | ||
| 
						 | 
					2c288bda42 | ||
| 
						 | 
					0b8c922da9 | ||
| 
						 | 
					3fe294e4ef | ||
| 
						 | 
					921a145592 | ||
| 
						 | 
					0c24eed73a | ||
| 
						 | 
					29ce2c1201 | ||
| 
						 | 
					532c74ae86 | ||
| 
						 | 
					9beb5af82e | ||
| 
						 | 
					9e6dd23876 | ||
| 
						 | 
					7a8501e307 | ||
| 
						 | 
					781cc523af | ||
| 
						 | 
					c6f45d4314 | ||
| 
						 | 
					d11d05d07a | ||
| 
						 | 
					e179aadfdf | ||
| 
						 | 
					d6a9615347 | ||
| 
						 | 
					c6306eb798 | ||
| 
						 | 
					bcfde70d73 | ||
| 
						 | 
					53e893615d | ||
| 
						 | 
					303692b5ed | ||
| 
						 | 
					58ca755f40 | ||
| 
						 | 
					770234afa2 | ||
| 
						 | 
					d77c3dfd02 | ||
| 
						 | 
					c23d8a74dc | ||
| 
						 | 
					74a5ff5f43 | ||
| 
						 | 
					071940680f | ||
| 
						 | 
					69d3b2d824 | ||
| 
						 | 
					d891ff9fd9 | ||
| 
						 | 
					6af22cf0ef | ||
| 
						 | 
					fff24d5e35 | ||
| 
						 | 
					ceba827e9a | ||
| 
						 | 
					a0432a1e80 | ||
| 
						 | 
					cfcf32d038 | ||
| 
						 | 
					a67bdc34fa | ||
| 
						 | 
					b3a653c245 | ||
| 
						 | 
					4a34b7252e | ||
| 
						 | 
					7e45ec57a8 | ||
| 
						 | 
					afbaa80b8b | ||
| 
						 | 
					115d243428 | ||
| 
						 | 
					7151f63a5f | ||
| 
						 | 
					597e7b1805 | ||
| 
						 | 
					2934c2ce43 | ||
| 
						 | 
					0f6e296a8e | ||
| 
						 | 
					9c228928b6 | ||
| 
						 | 
					ff3a2b8eab | ||
| 
						 | 
					c4105fa035 | ||
| 
						 | 
					871dbd3c92 | ||
| 
						 | 
					c9ed14e6d6 | ||
| 
						 | 
					1ad85e5061 | ||
| 
						 | 
					09fbc6c952 | ||
| 
						 | 
					895ec266bb | ||
| 
						 | 
					d85448f3bb | ||
| 
						 | 
					99d46e8c27 | ||
| 
						 | 
					4afdff39d7 | ||
| 
						 | 
					661a807c65 | ||
| 
						 | 
					6d58c4546e | ||
| 
						 | 
					38ffbc0222 | ||
| 
						 | 
					fefb166c52 | ||
| 
						 | 
					dcb3c22e0b | ||
| 
						 | 
					47a53c9e46 | ||
| 
						 | 
					1413cd87eb | ||
| 
						 | 
					c92e184f75 | ||
| 
						 | 
					3906e6ce60 | ||
| 
						 | 
					c7d3c3db0d | ||
| 
						 | 
					d6639d05c2 | ||
| 
						 | 
					633cf7cbad | ||
| 
						 | 
					a5647b79ce | ||
| 
						 | 
					ba5059dd66 | ||
| 
						 | 
					bb8abbbbae | ||
| 
						 | 
					561504fffa | ||
| 
						 | 
					23e6b8adc8 | ||
| 
						 | 
					3e0ea7d07a | ||
| 
						 | 
					94fd3201b2 | ||
| 
						 | 
					0b3f3e1ad9 | ||
| 
						 | 
					a05d2a0c05 | ||
| 
						 | 
					0b14e0b367 | ||
| 
						 | 
					66e8777769 | ||
| 
						 | 
					348486ced4 | ||
| 
						 | 
					f1f300e629 | ||
| 
						 | 
					dd17922afc | ||
| 
						 | 
					40fd4cb86a | ||
| 
						 | 
					9e9b75ae4d | ||
| 
						 | 
					8abf76ddb9 | ||
| 
						 | 
					c95da745bc | ||
| 
						 | 
					0cd235eef6 | ||
| 
						 | 
					77315556f1 | ||
| 
						 | 
					c379c181e0 | ||
| 
						 | 
					31a2ec2d88 | ||
| 
						 | 
					b88a52504e | ||
| 
						 | 
					a95567af99 | ||
| 
						 | 
					849edab8ec | ||
| 
						 | 
					b158a1d946 | ||
| 
						 | 
					fa2672f9fc | ||
| 
						 | 
					28e3614bc0 | ||
| 
						 | 
					208e095f72 | ||
| 
						 | 
					0ae7abe57c | ||
| 
						 | 
					dc0a294a73 | ||
| 
						 | 
					468c99257c | ||
| 
						 | 
					af8e8d63f9 | ||
| 
						 | 
					e092418d8b | ||
| 
						 | 
					e33e3045c6 | ||
| 
						 | 
					cb6568bf21 | ||
| 
						 | 
					235b3ba479 | ||
| 
						 | 
					5b3330e0cf | ||
| 
						 | 
					aab771fbdf | ||
| 
						 | 
					00f95a93f5 | ||
| 
						 | 
					1724e7c461 | ||
| 
						 | 
					3b98a5ddac | ||
| 
						 | 
					8b59cc93d5 | ||
| 
						 | 
					c3e4e7c182 | ||
| 
						 | 
					38348005b3 | ||
| 
						 | 
					208c4b9128 | ||
| 
						 | 
					ec574c2c41 | ||
| 
						 | 
					871be928a8 | ||
| 
						 | 
					b20d4f8626 | ||
| 
						 | 
					073d7a5985 | ||
| 
						 | 
					40306424b1 | ||
| 
						 | 
					ecb3bfe543 | ||
| 
						 | 
					abeac45abe | ||
| 
						 | 
					0fca93ac60 | ||
| 
						 | 
					857e5f329a | ||
| 
						 | 
					053419cd24 | ||
| 
						 | 
					99e207bab0 | ||
| 
						 | 
					0067bbe7a7 | ||
| 
						 | 
					45aa690868 | ||
| 
						 | 
					beb245e92f | ||
| 
						 | 
					c424df0d2f | ||
| 
						 | 
					87929e4b35 | ||
| 
						 | 
					d76736fc5e | ||
| 
						 | 
					0f9b77223e | ||
| 
						 | 
					9f47175a40 | ||
| 
						 | 
					a1a8713aad | ||
| 
						 | 
					6501a06d46 | ||
| 
						 | 
					8d89fbae5a | ||
| 
						 | 
					7a2cf5455c | ||
| 
						 | 
					7125a7ca8b | ||
| 
						 | 
					54d47874f7 | ||
| 
						 | 
					2761012f69 | ||
| 
						 | 
					3de2a1e635 | ||
| 
						 | 
					1eff9ac0c5 | ||
| 
						 | 
					54f329fe93 | ||
| 
						 | 
					9baa2ef53b | ||
| 
						 | 
					6bde5972c3 | ||
| 
						 | 
					36f6cb369b | ||
| 
						 | 
					b845d58b04 | ||
| 
						 | 
					efb113c736 | ||
| 
						 | 
					3ce59dae88 | ||
| 
						 | 
					f0b0caa3fa | ||
| 
						 | 
					58384838c3 | ||
| 
						 | 
					abb870d1ad | 
							
								
								
									
										20
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
*.pyc
 | 
			
		||||
*.pyo
 | 
			
		||||
*~
 | 
			
		||||
*.DS_Store
 | 
			
		||||
wine-py2exe/
 | 
			
		||||
py2exe.log
 | 
			
		||||
*.kate-swp
 | 
			
		||||
build/
 | 
			
		||||
dist/
 | 
			
		||||
MANIFEST
 | 
			
		||||
README.txt
 | 
			
		||||
youtube-dl.1
 | 
			
		||||
youtube-dl.bash-completion
 | 
			
		||||
youtube-dl
 | 
			
		||||
youtube-dl.exe
 | 
			
		||||
youtube-dl.tar.gz
 | 
			
		||||
.coverage
 | 
			
		||||
cover/
 | 
			
		||||
updates_key.pem
 | 
			
		||||
*.egg-info
 | 
			
		||||
							
								
								
									
										15
									
								
								.travis.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								.travis.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
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
									
								
								CHANGELOG
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								CHANGELOG
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
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 +1 @@
 | 
			
		||||
2011.09.18c
 | 
			
		||||
2012.12.99
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										24
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
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
									
								
								MANIFEST.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								MANIFEST.in
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
include README.md
 | 
			
		||||
include test/*.py
 | 
			
		||||
include test/*.json
 | 
			
		||||
include youtube-dl.bash-completion
 | 
			
		||||
include youtube-dl.1
 | 
			
		||||
							
								
								
									
										76
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										76
									
								
								Makefile
									
									
									
									
									
								
							@@ -1,20 +1,68 @@
 | 
			
		||||
default: update
 | 
			
		||||
all: youtube-dl README.md README.txt youtube-dl.1 youtube-dl.bash-completion
 | 
			
		||||
 | 
			
		||||
update: update-readme update-latest
 | 
			
		||||
clean:
 | 
			
		||||
	rm -rf youtube-dl.1 youtube-dl.bash-completion README.txt MANIFEST build/ dist/ .coverage cover/ youtube-dl.tar.gz
 | 
			
		||||
 | 
			
		||||
update-latest:
 | 
			
		||||
	./youtube-dl --version > LATEST_VERSION
 | 
			
		||||
cleanall: clean
 | 
			
		||||
	rm -f youtube-dl youtube-dl.exe
 | 
			
		||||
 | 
			
		||||
update-readme:
 | 
			
		||||
	@options=$$(COLUMNS=80 ./youtube-dl --help | sed -e '1,/.*General Options.*/ d' -e 's/^\W\{2\}\(\w\)/### \1/') && \
 | 
			
		||||
		header=$$(sed -e '/.*## OPTIONS/,$$ d' README.md) && \
 | 
			
		||||
		footer=$$(sed -e '1,/.*## FAQ/ d' README.md) && \
 | 
			
		||||
		echo "$${header}" > README.md && \
 | 
			
		||||
		echo -e '\n## OPTIONS' >> README.md && \
 | 
			
		||||
		echo "$${options}" >> README.md&& \
 | 
			
		||||
		echo -e '\n## FAQ' >> README.md && \
 | 
			
		||||
		echo "$${footer}" >> README.md
 | 
			
		||||
PREFIX=/usr/local
 | 
			
		||||
BINDIR=$(PREFIX)/bin
 | 
			
		||||
MANDIR=$(PREFIX)/man
 | 
			
		||||
SYSCONFDIR=/etc
 | 
			
		||||
PYTHON=/usr/bin/env python
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
.PHONY: default update update-latest update-readme
 | 
			
		||||
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
 | 
			
		||||
	zip --quiet youtube-dl 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
 | 
			
		||||
	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 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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										274
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										274
									
								
								README.md
									
									
									
									
									
								
							@@ -1,84 +1,192 @@
 | 
			
		||||
# youtube-dl
 | 
			
		||||
% YOUTUBE-DL(1)
 | 
			
		||||
 | 
			
		||||
## USAGE
 | 
			
		||||
youtube-dl [options] url [url...]
 | 
			
		||||
# NAME
 | 
			
		||||
youtube-dl
 | 
			
		||||
 | 
			
		||||
## DESCRIPTION
 | 
			
		||||
# 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.x (x being at least 5), 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,
 | 
			
		||||
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
 | 
			
		||||
    -v, --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   download rate limit (e.g. 50k or 44.6m)
 | 
			
		||||
    -R, --retries RETRIES    number of retries (default is 10)
 | 
			
		||||
    --dump-user-agent        display the current browser identification
 | 
			
		||||
    --list-extractors        List all supported extractors and the URLs they
 | 
			
		||||
                             would handle
 | 
			
		||||
# 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)
 | 
			
		||||
## 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
 | 
			
		||||
    -l, --literal            use literal title in file name
 | 
			
		||||
    -A, --auto-number        number downloaded files starting from 00000
 | 
			
		||||
    -o, --output TEMPLATE    output filename template
 | 
			
		||||
    -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 dump cookie jar to
 | 
			
		||||
    --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
 | 
			
		||||
## 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-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
 | 
			
		||||
    --no-progress            do not print progress bar
 | 
			
		||||
    --console-title          display progress in console titlebar
 | 
			
		||||
## 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-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
 | 
			
		||||
    --all-formats            download all available video formats
 | 
			
		||||
    --max-quality FORMAT     highest quality format to download
 | 
			
		||||
## 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                 downloads only the subtitles (no video)
 | 
			
		||||
    --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 LANG          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
 | 
			
		||||
## Authentication Options:
 | 
			
		||||
    -u, --username USERNAME    account username
 | 
			
		||||
    -p, --password PASSWORD    account password
 | 
			
		||||
    -n, --netrc                use .netrc authentication data
 | 
			
		||||
 | 
			
		||||
### Post-processing Options:
 | 
			
		||||
    --extract-audio          convert video files to audio-only files (requires
 | 
			
		||||
                             ffmpeg and ffprobe)
 | 
			
		||||
    --audio-format FORMAT    "best", "aac" or "mp3"; best by default
 | 
			
		||||
    --audio-quality QUALITY  ffmpeg audio bitrate specification, 128k by default
 | 
			
		||||
    -k, --keep-video         keeps the video file on disk after the post-
 | 
			
		||||
                             processing; the video is erased by default
 | 
			
		||||
## 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
 | 
			
		||||
 | 
			
		||||
## FAQ
 | 
			
		||||
# 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?
 | 
			
		||||
 | 
			
		||||
@@ -94,25 +202,49 @@ Once the video is fully downloaded, use any video player, such as [vlc](http://w
 | 
			
		||||
 | 
			
		||||
### 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.
 | 
			
		||||
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`.
 | 
			
		||||
 | 
			
		||||
## COPYRIGHT
 | 
			
		||||
### 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
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								bin/youtube-dl
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										6
									
								
								bin/youtube-dl
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
 | 
			
		||||
import youtube_dl
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    youtube_dl.main()
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								devscripts/SizeOfImage.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								devscripts/SizeOfImage.patch
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								devscripts/SizeOfImage_w.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								devscripts/SizeOfImage_w.patch
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										14
									
								
								devscripts/bash-completion.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								devscripts/bash-completion.in
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
__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
									
								
								devscripts/bash-completion.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										26
									
								
								devscripts/bash-completion.py
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
#!/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
									
								
								devscripts/gh-pages/add-version.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										33
									
								
								devscripts/gh-pages/add-version.py
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
#!/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
									
								
								devscripts/gh-pages/generate-download.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										32
									
								
								devscripts/gh-pages/generate-download.py
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
#!/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
									
								
								devscripts/gh-pages/sign-versions.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										32
									
								
								devscripts/gh-pages/sign-versions.py
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
#!/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
									
								
								devscripts/gh-pages/update-copyright.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										21
									
								
								devscripts/gh-pages/update-copyright.py
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
#!/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
									
								
								devscripts/gh-pages/update-feed.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										57
									
								
								devscripts/gh-pages/update-feed.py
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,57 @@
 | 
			
		||||
#!/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
									
								
								devscripts/make_readme.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										20
									
								
								devscripts/make_readme.py
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
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
									
								
								devscripts/posix-locale.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										6
									
								
								devscripts/posix-locale.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
 | 
			
		||||
# 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
 | 
			
		||||
							
								
								
									
										92
									
								
								devscripts/release.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										92
									
								
								devscripts/release.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,92 @@
 | 
			
		||||
#!/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
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
nosetests --with-coverage --cover-package=youtube_dl --cover-html test --stop || exit 1
 | 
			
		||||
 | 
			
		||||
/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
									
								
								devscripts/transition_helper.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								devscripts/transition_helper.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
			
		||||
#!/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
									
								
								devscripts/transition_helper_exe/setup.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								devscripts/transition_helper_exe/setup.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
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)
 | 
			
		||||
							
								
								
									
										102
									
								
								devscripts/transition_helper_exe/youtube-dl.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								devscripts/transition_helper_exe/youtube-dl.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,102 @@
 | 
			
		||||
#!/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
									
								
								devscripts/wine-py2exe.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										56
									
								
								devscripts/wine-py2exe.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,56 @@
 | 
			
		||||
#!/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
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										78
									
								
								setup.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								setup.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,78 @@
 | 
			
		||||
#!/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'],
 | 
			
		||||
 | 
			
		||||
    # 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
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										44
									
								
								test/parameters.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								test/parameters.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
			
		||||
{
 | 
			
		||||
    "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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										38
									
								
								test/test_all_urls.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								test/test_all_urls.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
#!/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.InfoExtractors import YoutubeIE, YoutubePlaylistIE, YoutubeChannelIE
 | 
			
		||||
 | 
			
		||||
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_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()
 | 
			
		||||
							
								
								
									
										142
									
								
								test/test_download.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								test/test_download.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,142 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
 | 
			
		||||
import errno
 | 
			
		||||
import hashlib
 | 
			
		||||
import io
 | 
			
		||||
import os
 | 
			
		||||
import json
 | 
			
		||||
import unittest
 | 
			
		||||
import sys
 | 
			
		||||
import hashlib
 | 
			
		||||
import socket
 | 
			
		||||
 | 
			
		||||
# Allow direct execution
 | 
			
		||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
 | 
			
		||||
 | 
			
		||||
import youtube_dl.FileDownloader
 | 
			
		||||
import youtube_dl.InfoExtractors
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
class FileDownloader(youtube_dl.FileDownloader):
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        self.to_stderr = self.to_screen
 | 
			
		||||
        self.processed_info_dicts = []
 | 
			
		||||
        return youtube_dl.FileDownloader.__init__(self, *args, **kwargs)
 | 
			
		||||
    def process_info(self, info_dict):
 | 
			
		||||
        self.processed_info_dicts.append(info_dict)
 | 
			
		||||
        return youtube_dl.FileDownloader.process_info(self, 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.InfoExtractors.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', {}))
 | 
			
		||||
 | 
			
		||||
        fd = FileDownloader(params)
 | 
			
		||||
        for ie in youtube_dl.InfoExtractors.gen_extractors():
 | 
			
		||||
            fd.add_info_extractor(ie)
 | 
			
		||||
        finished_hook_called = set()
 | 
			
		||||
        def _hook(status):
 | 
			
		||||
            if status['status'] == 'finished':
 | 
			
		||||
                finished_hook_called.add(status['filename'])
 | 
			
		||||
        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:
 | 
			
		||||
                    fd.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, value) in tc.get('info_dict', {}).items():
 | 
			
		||||
                    self.assertEqual(value, info_dict.get(info_field))
 | 
			
		||||
        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
									
								
								test/test_execution.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								test/test_execution.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
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
									
								
								test/test_utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								test/test_utils.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,116 @@
 | 
			
		||||
#!/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
									
								
								test/test_write_info_json.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								test/test_write_info_json.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,77 @@
 | 
			
		||||
#!/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.FileDownloader
 | 
			
		||||
import youtube_dl.InfoExtractors
 | 
			
		||||
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 FileDownloader(youtube_dl.FileDownloader):
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        youtube_dl.FileDownloader.__init__(self, *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.InfoExtractors.YoutubeIE()
 | 
			
		||||
        fd = FileDownloader(params)
 | 
			
		||||
        fd.add_info_extractor(ie)
 | 
			
		||||
        fd.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()
 | 
			
		||||
							
								
								
									
										109
									
								
								test/test_youtube_lists.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								test/test_youtube_lists.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,109 @@
 | 
			
		||||
#!/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.InfoExtractors import YoutubeUserIE, YoutubePlaylistIE, YoutubeIE, YoutubeChannelIE
 | 
			
		||||
from youtube_dl.utils import *
 | 
			
		||||
from youtube_dl.FileDownloader import FileDownloader
 | 
			
		||||
 | 
			
		||||
PARAMETERS_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "parameters.json")
 | 
			
		||||
with io.open(PARAMETERS_FILE, encoding='utf-8') as pf:
 | 
			
		||||
    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 FakeDownloader(FileDownloader):
 | 
			
		||||
    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 = FakeDownloader()
 | 
			
		||||
        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 = FakeDownloader()
 | 
			
		||||
        ie = YoutubePlaylistIE(dl)
 | 
			
		||||
        result = ie.extract('PLBB231211A4F62143')[0]
 | 
			
		||||
        self.assertEqual(result['title'], 'Team Fortress 2')
 | 
			
		||||
        self.assertTrue(len(result['entries']) > 40)
 | 
			
		||||
 | 
			
		||||
    def test_youtube_playlist_long(self):
 | 
			
		||||
        dl = FakeDownloader()
 | 
			
		||||
        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 = FakeDownloader()
 | 
			
		||||
        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 = FakeDownloader()
 | 
			
		||||
        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 = FakeDownloader()
 | 
			
		||||
        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 = FakeDownloader()
 | 
			
		||||
        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 = FakeDownloader()
 | 
			
		||||
        ie = YoutubeUserIE(dl)
 | 
			
		||||
        result = ie.extract('https://www.youtube.com/user/TheLinuxFoundation')[0]
 | 
			
		||||
        self.assertTrue(len(result['entries']) >= 320)
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    unittest.main()
 | 
			
		||||
							
								
								
									
										100
									
								
								test/test_youtube_subtitles.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								test/test_youtube_subtitles.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,100 @@
 | 
			
		||||
#!/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.InfoExtractors import YoutubeIE
 | 
			
		||||
from youtube_dl.utils import *
 | 
			
		||||
 | 
			
		||||
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 FakeDownloader(object):
 | 
			
		||||
    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 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 = FakeDownloader()
 | 
			
		||||
        DL.params['allsubtitles'] = False
 | 
			
		||||
        DL.params['writesubtitles'] = False
 | 
			
		||||
        DL.params['subtitlesformat'] = 'srt'
 | 
			
		||||
        DL.params['listsubtitles'] = False
 | 
			
		||||
    def test_youtube_no_subtitles(self):
 | 
			
		||||
        DL = FakeDownloader()
 | 
			
		||||
        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 = FakeDownloader()
 | 
			
		||||
        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 = FakeDownloader()
 | 
			
		||||
        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 = FakeDownloader()
 | 
			
		||||
        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 = FakeDownloader()
 | 
			
		||||
        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 = FakeDownloader()
 | 
			
		||||
        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 = FakeDownloader()
 | 
			
		||||
        DL.params['listsubtitles'] = True
 | 
			
		||||
        IE = YoutubeIE(DL)
 | 
			
		||||
        info_dict = IE.extract('QRS8MkLhQmM')
 | 
			
		||||
        self.assertEqual(info_dict, None)
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    unittest.main()
 | 
			
		||||
							
								
								
									
										433
									
								
								test/tests.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										433
									
								
								test/tests.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,433 @@
 | 
			
		||||
[
 | 
			
		||||
  {
 | 
			
		||||
    "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"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "Metacafe",
 | 
			
		||||
    "add_ie": ["Youtube"],
 | 
			
		||||
    "url":  "http://metacafe.com/watch/yt-_aUehQsCQtM/the_electric_company_short_i_pbs_kids_go/",
 | 
			
		||||
    "file":  "_aUehQsCQtM.flv"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "BlipTV",
 | 
			
		||||
    "md5":  "b2d849efcf7ee18917e4b4d9ff37cafe",
 | 
			
		||||
    "url":  "http://blip.tv/cbr/cbr-exclusive-gotham-city-imposters-bats-vs-jokerz-short-3-5796352",
 | 
			
		||||
    "file":  "5779306.m4v"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "XVideos",
 | 
			
		||||
    "md5":  "1d0c835822f0a71a7bf011855db929d0",
 | 
			
		||||
    "url":  "http://www.xvideos.com/video939581/funny_porns_by_s_-1",
 | 
			
		||||
    "file":  "939581.flv"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "YouPorn",
 | 
			
		||||
    "md5": "c37ddbaaa39058c76a7e86c6813423c1",
 | 
			
		||||
    "url": "http://www.youporn.com/watch/505835/sex-ed-is-it-safe-to-masturbate-daily/",
 | 
			
		||||
    "file": "505835.mp4"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "Pornotube",
 | 
			
		||||
    "md5": "374dd6dcedd24234453b295209aa69b6",
 | 
			
		||||
    "url": "http://pornotube.com/c/173/m/1689755/Marilyn-Monroe-Bathing",
 | 
			
		||||
    "file": "1689755.flv"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "YouJizz",
 | 
			
		||||
    "md5": "07e15fa469ba384c7693fd246905547c",
 | 
			
		||||
    "url": "http://www.youjizz.com/videos/zeichentrick-1-2189178.html",
 | 
			
		||||
    "file": "2189178.flv"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "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"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "StanfordOpenClassroom",
 | 
			
		||||
    "md5":  "544a9468546059d4e80d76265b0443b8",
 | 
			
		||||
    "url":  "http://openclassroom.stanford.edu/MainFolder/VideoPage.php?course=PracticalUnix&video=intro-environment&speed=100",
 | 
			
		||||
    "file":  "PracticalUnix_intro-environment.mp4"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "XNXX",
 | 
			
		||||
    "md5":  "0831677e2b4761795f68d417e0b7b445",
 | 
			
		||||
    "url":  "http://video.xnxx.com/video1135332/lida_naked_funny_actress_5_",
 | 
			
		||||
    "file":  "1135332.flv"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "Youku",
 | 
			
		||||
    "url": "http://v.youku.com/v_show/id_XNDgyMDQ2NTQw.html",
 | 
			
		||||
    "file": "XNDgyMDQ2NTQw_part00.flv",
 | 
			
		||||
    "md5": "ffe3f2e435663dc2d1eea34faeff5b5b",
 | 
			
		||||
    "params": { "test": false }
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "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"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "JustinTV",
 | 
			
		||||
    "url": "http://www.twitch.tv/thegamedevhub/b/296128360",
 | 
			
		||||
    "file": "296128360.flv",
 | 
			
		||||
    "md5": "ecaa8a790c22a40770901460af191c9a"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "MyVideo",
 | 
			
		||||
    "url": "http://www.myvideo.de/watch/8229274/bowling_fail_or_win",
 | 
			
		||||
    "file": "8229274.flv",
 | 
			
		||||
    "md5": "2d2753e8130479ba2cb7e0a37002053e"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "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"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "GooglePlus",
 | 
			
		||||
    "url": "https://plus.google.com/u/0/108897254135232129896/posts/ZButuJc6CtH",
 | 
			
		||||
    "file": "ZButuJc6CtH.flv"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "FunnyOrDie",
 | 
			
		||||
    "url": "http://www.funnyordie.com/videos/0732f586d7/heart-shaped-box-literal-video-version",
 | 
			
		||||
    "file": "0732f586d7.mp4",
 | 
			
		||||
    "md5": "f647e9e90064b53b6e046e75d0241fbd"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "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"
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "InfoQ",
 | 
			
		||||
    "url": "http://www.infoq.com/presentations/A-Few-of-My-Favorite-Python-Things",
 | 
			
		||||
    "file": "12-jan-pythonthings.mp4",
 | 
			
		||||
    "info_dict": {
 | 
			
		||||
      "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": {
 | 
			
		||||
        "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"
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "file": "11885680.m4a",
 | 
			
		||||
        "md5": "4eb0a669317cd725f6bbd336a29f923a",
 | 
			
		||||
        "info_dict": {
 | 
			
		||||
          "title": "youtube-dl project as well - youtube-dl test track 4 \"'/\\\u00e4\u21ad"
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "file": "11885682.m4a",
 | 
			
		||||
        "md5": "1893e872e263a2705558d1d319ad19e8",
 | 
			
		||||
        "info_dict": {
 | 
			
		||||
          "title": "PH - youtube-dl test track 5 \"'/\\\u00e4\u21ad"
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "file": "11885683.m4a",
 | 
			
		||||
        "md5": "b673c46f47a216ab1741ae8836af5899",
 | 
			
		||||
        "info_dict": {
 | 
			
		||||
          "title": "PH - youtube-dl test track 6 \"'/\\\u00e4\u21ad"
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "file": "11885684.m4a",
 | 
			
		||||
        "md5": "1d74534e95df54986da7f5abf7d842b7",
 | 
			
		||||
        "info_dict": {
 | 
			
		||||
          "title": "phihag - youtube-dl test track 7 \"'/\\\u00e4\u21ad"
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "file": "11885685.m4a",
 | 
			
		||||
        "md5": "f081f47af8f6ae782ed131d38b9cd1c0",
 | 
			
		||||
        "info_dict": {
 | 
			
		||||
          "title": "phihag - youtube-dl test track 8 \"'/\\\u00e4\u21ad"
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "Keek",
 | 
			
		||||
    "url": "http://www.keek.com/ytdl/keeks/NODfbab",
 | 
			
		||||
    "file": "NODfbab.mp4",
 | 
			
		||||
    "md5": "9b0636f8c0f7614afa4ea5e4c6e57e83",
 | 
			
		||||
    "info_dict": {
 | 
			
		||||
      "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": "7bc087e71d16f18f9b8ab9fa62a8a031",
 | 
			
		||||
    "info_dict": {
 | 
			
		||||
        "title": "Dan Dennett: The illusion of consciousness",
 | 
			
		||||
        "thumbnail": "http://images.ted.com/images/ted/488_389x292.jpg"
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "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": {
 | 
			
		||||
        "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"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "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://birthdayproject2012.tumblr.com/post/17258355236/a-sample-video-from-leeann-if-you-need-an-idea",
 | 
			
		||||
    "file": "17258355236.mp4",
 | 
			
		||||
    "md5": "7c6a514d691b034ccf8567999e9e88a3",
 | 
			
		||||
    "info_dict": {
 | 
			
		||||
        "title": "Calling all Pris! - A sample video from LeeAnn. (If you need an idea..."
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "SoundcloudSet",
 | 
			
		||||
    "url":"https://soundcloud.com/the-concept-band/sets/the-royal-concept-ep",
 | 
			
		||||
    "playlist":[
 | 
			
		||||
      {
 | 
			
		||||
        "file":"30510138.mp3",
 | 
			
		||||
        "md5":"f9136bf103901728f29e419d2c70f55d",
 | 
			
		||||
        "info_dict": {
 | 
			
		||||
          "title":"D-D-Dance"
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "file":"47127625.mp3",
 | 
			
		||||
        "md5":"09b6758a018470570f8fd423c9453dd8",
 | 
			
		||||
        "info_dict": {
 | 
			
		||||
          "title":"The Royal Concept - Gimme Twice"
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "file":"47127627.mp3",
 | 
			
		||||
        "md5":"154abd4e418cea19c3b901f1e1306d9c",
 | 
			
		||||
        "info_dict": {
 | 
			
		||||
          "title":"Goldrushed"
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "file":"47127629.mp3",
 | 
			
		||||
        "md5":"2f5471edc79ad3f33a683153e96a79c1",
 | 
			
		||||
        "info_dict": {
 | 
			
		||||
          "title":"In the End"
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "file":"47127631.mp3",
 | 
			
		||||
        "md5":"f9ba87aa940af7213f98949254f1c6e2",
 | 
			
		||||
        "info_dict": {
 | 
			
		||||
          "title":"Knocked Up"
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "file":"75206121.mp3",
 | 
			
		||||
        "md5":"f9d1fe9406717e302980c30de4af9353",
 | 
			
		||||
        "info_dict": {
 | 
			
		||||
          "title":"World On Fire"
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name":"Bandcamp",
 | 
			
		||||
    "url":"http://youtube-dl.bandcamp.com/track/youtube-dl-test-song",
 | 
			
		||||
    "file":"1812978515.mp3",
 | 
			
		||||
    "md5":"cdeb30cdae1921719a3cbcab696ef53c",
 | 
			
		||||
    "info_dict": {
 | 
			
		||||
      "title":"youtube-dl test song \"'/\\ä↭"
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "RedTube",
 | 
			
		||||
    "url": "http://www.redtube.com/66418",
 | 
			
		||||
    "file": "66418.mp4",
 | 
			
		||||
    "md5": "7b8c22b5e7098a3e1c09709df1126d2d",
 | 
			
		||||
    "info_dict":{
 | 
			
		||||
      "title":"Sucked on a toilet"
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "Photobucket",
 | 
			
		||||
    "url": "http://media.photobucket.com/user/rachaneronas/media/TiredofLinkBuildingTryBacklinkMyDomaincom_zpsc0c3b9fa.mp4.html?filters[term]=search&filters[primary]=videos&filters[secondary]=images&sort=1&o=0",
 | 
			
		||||
    "file": "zpsc0c3b9fa.mp4",
 | 
			
		||||
    "md5": "7dabfb92b0a31f6c16cebc0f8e60ff99",
 | 
			
		||||
    "info_dict":{
 | 
			
		||||
      "title":"Tired of Link Building? Try BacklinkMyDomain.com!"
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "Ina",
 | 
			
		||||
    "url": "www.ina.fr/video/I12055569/francois-hollande-je-crois-que-c-est-clair-video.html",
 | 
			
		||||
    "file": "I12055569.mp4",
 | 
			
		||||
    "md5": "a667021bf2b41f8dc6049479d9bb38a3",
 | 
			
		||||
    "info_dict":{
 | 
			
		||||
      "title":"François Hollande \"Je crois que c'est clair\""
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
]
 | 
			
		||||
							
								
								
									
										3887
									
								
								youtube-dl
									
									
									
									
									
								
							
							
						
						
									
										3887
									
								
								youtube-dl
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								youtube-dl.exe
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								youtube-dl.exe
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										1003
									
								
								youtube_dl/FileDownloader.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1003
									
								
								youtube_dl/FileDownloader.py
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										4219
									
								
								youtube_dl/InfoExtractors.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										4219
									
								
								youtube_dl/InfoExtractors.py
									
									
									
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										237
									
								
								youtube_dl/PostProcessor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										237
									
								
								youtube_dl/PostProcessor.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,237 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
from __future__ import absolute_import
 | 
			
		||||
 | 
			
		||||
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:
 | 
			
		||||
            msg = stderr.strip().split('\n')[-1]
 | 
			
		||||
            raise FFmpegPostProcessorError(msg.decode('utf-8', 'replace'))
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
							
								
								
									
										605
									
								
								youtube_dl/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										605
									
								
								youtube_dl/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,605 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
from __future__ import with_statement
 | 
			
		||||
from __future__ import absolute_import
 | 
			
		||||
 | 
			
		||||
__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',
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
__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 .InfoExtractors import gen_extractors
 | 
			
		||||
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='onlysubtitles',
 | 
			
		||||
            help='downloads only the subtitles (no video)', 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='LANG',
 | 
			
		||||
            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-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:
 | 
			
		||||
            print(u'[debug] Override config: ' + repr(overrideArguments))
 | 
			
		||||
    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:
 | 
			
		||||
            print(u'[debug] System config: ' + repr(systemConf))
 | 
			
		||||
            print(u'[debug] User config: ' + repr(userConf))
 | 
			
		||||
            print(u'[debug] Command-line args: ' + repr(commandLineConf))
 | 
			
		||||
 | 
			
		||||
    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:
 | 
			
		||||
        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:
 | 
			
		||||
        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:
 | 
			
		||||
            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:
 | 
			
		||||
                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:
 | 
			
		||||
        parser.error(u'account username missing')
 | 
			
		||||
    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')
 | 
			
		||||
 | 
			
		||||
    # File downloader
 | 
			
		||||
    fd = FileDownloader({
 | 
			
		||||
        'usenetrc': opts.usenetrc,
 | 
			
		||||
        'username': opts.username,
 | 
			
		||||
        'password': opts.password,
 | 
			
		||||
        'quiet': (opts.quiet or opts.geturl or opts.gettitle or opts.getthumbnail or opts.getdescription or opts.getfilename or opts.getformat),
 | 
			
		||||
        'forceurl': opts.geturl,
 | 
			
		||||
        'forcetitle': opts.gettitle,
 | 
			
		||||
        'forcethumbnail': opts.getthumbnail,
 | 
			
		||||
        'forcedescription': opts.getdescription,
 | 
			
		||||
        'forcefilename': opts.getfilename,
 | 
			
		||||
        'forceformat': opts.getformat,
 | 
			
		||||
        'simulate': opts.simulate,
 | 
			
		||||
        'skip_download': (opts.skip_download or opts.simulate or opts.geturl or opts.gettitle or opts.getthumbnail or opts.getdescription or opts.getfilename or opts.getformat),
 | 
			
		||||
        '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,
 | 
			
		||||
        'onlysubtitles': opts.onlysubtitles,
 | 
			
		||||
        '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:
 | 
			
		||||
        fd.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):
 | 
			
		||||
                fd.to_screen(u'[debug] Git HEAD: ' + out)
 | 
			
		||||
        except:
 | 
			
		||||
            pass
 | 
			
		||||
        fd.to_screen(u'[debug] Python version %s - %s' %(platform.python_version(), platform.platform()))
 | 
			
		||||
        fd.to_screen(u'[debug] Proxy map: ' + str(proxy_handler.proxies))
 | 
			
		||||
 | 
			
		||||
    for extractor in extractors:
 | 
			
		||||
        fd.add_info_extractor(extractor)
 | 
			
		||||
 | 
			
		||||
    # PostProcessors
 | 
			
		||||
    if opts.extractaudio:
 | 
			
		||||
        fd.add_post_processor(FFmpegExtractAudioPP(preferredcodec=opts.audioformat, preferredquality=opts.audioquality, nopostoverwrites=opts.nopostoverwrites))
 | 
			
		||||
    if opts.recodevideo:
 | 
			
		||||
        fd.add_post_processor(FFmpegVideoConvertor(preferedformat=opts.recodevideo))
 | 
			
		||||
 | 
			
		||||
    # Update version
 | 
			
		||||
    if opts.update_self:
 | 
			
		||||
        update_self(fd.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 = fd.download(all_urls)
 | 
			
		||||
    except MaxDownloadsReached:
 | 
			
		||||
        fd.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
									
								
								youtube_dl/__main__.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										18
									
								
								youtube_dl/__main__.py
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
#!/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()
 | 
			
		||||
							
								
								
									
										172
									
								
								youtube_dl/update.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								youtube_dl/update.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,172 @@
 | 
			
		||||
import json
 | 
			
		||||
import traceback
 | 
			
		||||
import hashlib
 | 
			
		||||
from zipimport import zipimporter
 | 
			
		||||
 | 
			
		||||
from .utils import *
 | 
			
		||||
from .version import __version__
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
def update_self(to_screen, verbose, filename):
 | 
			
		||||
    """Update the program file with the latest version from the repository"""
 | 
			
		||||
 | 
			
		||||
    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 isinstance(globals().get('__loader__'), zipimporter) and not hasattr(sys, "frozen"):
 | 
			
		||||
        to_screen(u'It looks like you installed youtube-dl with pip, setup.py or a tarball. Please use that to update.')
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    # Check if there is a new version
 | 
			
		||||
    try:
 | 
			
		||||
        newversion = compat_urllib_request.urlopen(VERSION_URL).read().decode('utf-8').strip()
 | 
			
		||||
    except:
 | 
			
		||||
        if verbose: to_screen(compat_str(traceback.format_exc()))
 | 
			
		||||
        to_screen(u'ERROR: can\'t find the current version. Please try again later.')
 | 
			
		||||
        return
 | 
			
		||||
    if newversion == __version__:
 | 
			
		||||
        to_screen(u'youtube-dl is up-to-date (' + __version__ + ')')
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    # Download and check versions info
 | 
			
		||||
    try:
 | 
			
		||||
        versions_info = compat_urllib_request.urlopen(JSON_URL).read().decode('utf-8')
 | 
			
		||||
        versions_info = json.loads(versions_info)
 | 
			
		||||
    except:
 | 
			
		||||
        if verbose: to_screen(compat_str(traceback.format_exc()))
 | 
			
		||||
        to_screen(u'ERROR: can\'t obtain versions info. Please try again later.')
 | 
			
		||||
        return
 | 
			
		||||
    if not 'signature' in versions_info:
 | 
			
		||||
        to_screen(u'ERROR: the versions file is not signed or corrupted. Aborting.')
 | 
			
		||||
        return
 | 
			
		||||
    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):
 | 
			
		||||
        to_screen(u'ERROR: the versions file signature is invalid. Aborting.')
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    to_screen(u'Updating to version ' + versions_info['latest'] + '...')
 | 
			
		||||
    version = versions_info['versions'][versions_info['latest']]
 | 
			
		||||
 | 
			
		||||
    print_notes(to_screen, versions_info['versions'])
 | 
			
		||||
 | 
			
		||||
    if not os.access(filename, os.W_OK):
 | 
			
		||||
        to_screen(u'ERROR: no write permissions on %s' % filename)
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    # Py2EXE
 | 
			
		||||
    if hasattr(sys, "frozen"):
 | 
			
		||||
        exe = os.path.abspath(filename)
 | 
			
		||||
        directory = os.path.dirname(exe)
 | 
			
		||||
        if not os.access(directory, os.W_OK):
 | 
			
		||||
            to_screen(u'ERROR: no write permissions on %s' % directory)
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            urlh = compat_urllib_request.urlopen(version['exe'][0])
 | 
			
		||||
            newcontent = urlh.read()
 | 
			
		||||
            urlh.close()
 | 
			
		||||
        except (IOError, OSError) as err:
 | 
			
		||||
            if verbose: to_screen(compat_str(traceback.format_exc()))
 | 
			
		||||
            to_screen(u'ERROR: unable to download latest version')
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        newcontent_hash = hashlib.sha256(newcontent).hexdigest()
 | 
			
		||||
        if newcontent_hash != version['exe'][1]:
 | 
			
		||||
            to_screen(u'ERROR: the downloaded file hash does not match. Aborting.')
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            with open(exe + '.new', 'wb') as outf:
 | 
			
		||||
                outf.write(newcontent)
 | 
			
		||||
        except (IOError, OSError) as err:
 | 
			
		||||
            if verbose: to_screen(compat_str(traceback.format_exc()))
 | 
			
		||||
            to_screen(u'ERROR: unable to write the new version')
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        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:
 | 
			
		||||
            if verbose: to_screen(compat_str(traceback.format_exc()))
 | 
			
		||||
            to_screen(u'ERROR: unable to overwrite current version')
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
    # Zip unix package
 | 
			
		||||
    elif isinstance(globals().get('__loader__'), zipimporter):
 | 
			
		||||
        try:
 | 
			
		||||
            urlh = compat_urllib_request.urlopen(version['bin'][0])
 | 
			
		||||
            newcontent = urlh.read()
 | 
			
		||||
            urlh.close()
 | 
			
		||||
        except (IOError, OSError) as err:
 | 
			
		||||
            if verbose: to_screen(compat_str(traceback.format_exc()))
 | 
			
		||||
            to_screen(u'ERROR: unable to download latest version')
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        newcontent_hash = hashlib.sha256(newcontent).hexdigest()
 | 
			
		||||
        if newcontent_hash != version['bin'][1]:
 | 
			
		||||
            to_screen(u'ERROR: the downloaded file hash does not match. Aborting.')
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            with open(filename, 'wb') as outf:
 | 
			
		||||
                outf.write(newcontent)
 | 
			
		||||
        except (IOError, OSError) as err:
 | 
			
		||||
            if verbose: to_screen(compat_str(traceback.format_exc()))
 | 
			
		||||
            to_screen(u'ERROR: unable to overwrite current version')
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
    to_screen(u'Updated youtube-dl. Restart youtube-dl to use the new version.')
 | 
			
		||||
 | 
			
		||||
def get_notes(versions, fromVersion):
 | 
			
		||||
    notes = []
 | 
			
		||||
    for v,vdata in sorted(versions.items()):
 | 
			
		||||
        if v > fromVersion:
 | 
			
		||||
            notes.extend(vdata.get('notes', []))
 | 
			
		||||
    return notes
 | 
			
		||||
 | 
			
		||||
def print_notes(to_screen, versions, fromVersion=__version__):
 | 
			
		||||
    notes = get_notes(versions, fromVersion)
 | 
			
		||||
    if notes:
 | 
			
		||||
        to_screen(u'PLEASE NOTE:')
 | 
			
		||||
        for note in notes:
 | 
			
		||||
            to_screen(note)
 | 
			
		||||
							
								
								
									
										660
									
								
								youtube_dl/utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										660
									
								
								youtube_dl/utils.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,660 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
import gzip
 | 
			
		||||
import io
 | 
			
		||||
import json
 | 
			
		||||
import locale
 | 
			
		||||
import os
 | 
			
		||||
import re
 | 
			
		||||
import sys
 | 
			
		||||
import traceback
 | 
			
		||||
import zlib
 | 
			
		||||
import email.utils
 | 
			
		||||
import json
 | 
			
		||||
import datetime
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    import urllib.request as compat_urllib_request
 | 
			
		||||
except ImportError: # Python 2
 | 
			
		||||
    import urllib2 as compat_urllib_request
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    import urllib.error as compat_urllib_error
 | 
			
		||||
except ImportError: # Python 2
 | 
			
		||||
    import urllib2 as compat_urllib_error
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    import urllib.parse as compat_urllib_parse
 | 
			
		||||
except ImportError: # Python 2
 | 
			
		||||
    import urllib as compat_urllib_parse
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    from urllib.parse import urlparse as compat_urllib_parse_urlparse
 | 
			
		||||
except ImportError: # Python 2
 | 
			
		||||
    from urlparse import urlparse as compat_urllib_parse_urlparse
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    import http.cookiejar as compat_cookiejar
 | 
			
		||||
except ImportError: # Python 2
 | 
			
		||||
    import cookielib as compat_cookiejar
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    import html.entities as compat_html_entities
 | 
			
		||||
except ImportError: # Python 2
 | 
			
		||||
    import htmlentitydefs as compat_html_entities
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    import html.parser as compat_html_parser
 | 
			
		||||
except ImportError: # Python 2
 | 
			
		||||
    import HTMLParser as compat_html_parser
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    import http.client as compat_http_client
 | 
			
		||||
except ImportError: # Python 2
 | 
			
		||||
    import httplib as compat_http_client
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    from subprocess import DEVNULL
 | 
			
		||||
    compat_subprocess_get_DEVNULL = lambda: DEVNULL
 | 
			
		||||
except ImportError:
 | 
			
		||||
    compat_subprocess_get_DEVNULL = lambda: open(os.path.devnull, 'w')
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    from urllib.parse import parse_qs as compat_parse_qs
 | 
			
		||||
except ImportError: # Python 2
 | 
			
		||||
    # HACK: The following is the correct parse_qs implementation from cpython 3's stdlib.
 | 
			
		||||
    # Python 2's version is apparently totally broken
 | 
			
		||||
    def _unquote(string, encoding='utf-8', errors='replace'):
 | 
			
		||||
        if string == '':
 | 
			
		||||
            return string
 | 
			
		||||
        res = string.split('%')
 | 
			
		||||
        if len(res) == 1:
 | 
			
		||||
            return string
 | 
			
		||||
        if encoding is None:
 | 
			
		||||
            encoding = 'utf-8'
 | 
			
		||||
        if errors is None:
 | 
			
		||||
            errors = 'replace'
 | 
			
		||||
        # pct_sequence: contiguous sequence of percent-encoded bytes, decoded
 | 
			
		||||
        pct_sequence = b''
 | 
			
		||||
        string = res[0]
 | 
			
		||||
        for item in res[1:]:
 | 
			
		||||
            try:
 | 
			
		||||
                if not item:
 | 
			
		||||
                    raise ValueError
 | 
			
		||||
                pct_sequence += item[:2].decode('hex')
 | 
			
		||||
                rest = item[2:]
 | 
			
		||||
                if not rest:
 | 
			
		||||
                    # This segment was just a single percent-encoded character.
 | 
			
		||||
                    # May be part of a sequence of code units, so delay decoding.
 | 
			
		||||
                    # (Stored in pct_sequence).
 | 
			
		||||
                    continue
 | 
			
		||||
            except ValueError:
 | 
			
		||||
                rest = '%' + item
 | 
			
		||||
            # Encountered non-percent-encoded characters. Flush the current
 | 
			
		||||
            # pct_sequence.
 | 
			
		||||
            string += pct_sequence.decode(encoding, errors) + rest
 | 
			
		||||
            pct_sequence = b''
 | 
			
		||||
        if pct_sequence:
 | 
			
		||||
            # Flush the final pct_sequence
 | 
			
		||||
            string += pct_sequence.decode(encoding, errors)
 | 
			
		||||
        return string
 | 
			
		||||
 | 
			
		||||
    def _parse_qsl(qs, keep_blank_values=False, strict_parsing=False,
 | 
			
		||||
                encoding='utf-8', errors='replace'):
 | 
			
		||||
        qs, _coerce_result = qs, unicode
 | 
			
		||||
        pairs = [s2 for s1 in qs.split('&') for s2 in s1.split(';')]
 | 
			
		||||
        r = []
 | 
			
		||||
        for name_value in pairs:
 | 
			
		||||
            if not name_value and not strict_parsing:
 | 
			
		||||
                continue
 | 
			
		||||
            nv = name_value.split('=', 1)
 | 
			
		||||
            if len(nv) != 2:
 | 
			
		||||
                if strict_parsing:
 | 
			
		||||
                    raise ValueError("bad query field: %r" % (name_value,))
 | 
			
		||||
                # Handle case of a control-name with no equal sign
 | 
			
		||||
                if keep_blank_values:
 | 
			
		||||
                    nv.append('')
 | 
			
		||||
                else:
 | 
			
		||||
                    continue
 | 
			
		||||
            if len(nv[1]) or keep_blank_values:
 | 
			
		||||
                name = nv[0].replace('+', ' ')
 | 
			
		||||
                name = _unquote(name, encoding=encoding, errors=errors)
 | 
			
		||||
                name = _coerce_result(name)
 | 
			
		||||
                value = nv[1].replace('+', ' ')
 | 
			
		||||
                value = _unquote(value, encoding=encoding, errors=errors)
 | 
			
		||||
                value = _coerce_result(value)
 | 
			
		||||
                r.append((name, value))
 | 
			
		||||
        return r
 | 
			
		||||
 | 
			
		||||
    def compat_parse_qs(qs, keep_blank_values=False, strict_parsing=False,
 | 
			
		||||
                encoding='utf-8', errors='replace'):
 | 
			
		||||
        parsed_result = {}
 | 
			
		||||
        pairs = _parse_qsl(qs, keep_blank_values, strict_parsing,
 | 
			
		||||
                        encoding=encoding, errors=errors)
 | 
			
		||||
        for name, value in pairs:
 | 
			
		||||
            if name in parsed_result:
 | 
			
		||||
                parsed_result[name].append(value)
 | 
			
		||||
            else:
 | 
			
		||||
                parsed_result[name] = [value]
 | 
			
		||||
        return parsed_result
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    compat_str = unicode # Python 2
 | 
			
		||||
except NameError:
 | 
			
		||||
    compat_str = str
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    compat_chr = unichr # Python 2
 | 
			
		||||
except NameError:
 | 
			
		||||
    compat_chr = chr
 | 
			
		||||
 | 
			
		||||
std_headers = {
 | 
			
		||||
    'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:10.0) Gecko/20100101 Firefox/10.0',
 | 
			
		||||
    'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
 | 
			
		||||
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
 | 
			
		||||
    'Accept-Encoding': 'gzip, deflate',
 | 
			
		||||
    'Accept-Language': 'en-us,en;q=0.5',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
def preferredencoding():
 | 
			
		||||
    """Get preferred encoding.
 | 
			
		||||
 | 
			
		||||
    Returns the best encoding scheme for the system, based on
 | 
			
		||||
    locale.getpreferredencoding() and some further tweaks.
 | 
			
		||||
    """
 | 
			
		||||
    try:
 | 
			
		||||
        pref = locale.getpreferredencoding()
 | 
			
		||||
        u'TEST'.encode(pref)
 | 
			
		||||
    except:
 | 
			
		||||
        pref = 'UTF-8'
 | 
			
		||||
 | 
			
		||||
    return pref
 | 
			
		||||
 | 
			
		||||
if sys.version_info < (3,0):
 | 
			
		||||
    def compat_print(s):
 | 
			
		||||
        print(s.encode(preferredencoding(), 'xmlcharrefreplace'))
 | 
			
		||||
else:
 | 
			
		||||
    def compat_print(s):
 | 
			
		||||
        assert type(s) == type(u'')
 | 
			
		||||
        print(s)
 | 
			
		||||
 | 
			
		||||
# In Python 2.x, json.dump expects a bytestream.
 | 
			
		||||
# In Python 3.x, it writes to a character stream
 | 
			
		||||
if sys.version_info < (3,0):
 | 
			
		||||
    def write_json_file(obj, fn):
 | 
			
		||||
        with open(fn, 'wb') as f:
 | 
			
		||||
            json.dump(obj, f)
 | 
			
		||||
else:
 | 
			
		||||
    def write_json_file(obj, fn):
 | 
			
		||||
        with open(fn, 'w', encoding='utf-8') as f:
 | 
			
		||||
            json.dump(obj, f)
 | 
			
		||||
 | 
			
		||||
def htmlentity_transform(matchobj):
 | 
			
		||||
    """Transforms an HTML entity to a character.
 | 
			
		||||
 | 
			
		||||
    This function receives a match object and is intended to be used with
 | 
			
		||||
    the re.sub() function.
 | 
			
		||||
    """
 | 
			
		||||
    entity = matchobj.group(1)
 | 
			
		||||
 | 
			
		||||
    # Known non-numeric HTML entity
 | 
			
		||||
    if entity in compat_html_entities.name2codepoint:
 | 
			
		||||
        return compat_chr(compat_html_entities.name2codepoint[entity])
 | 
			
		||||
 | 
			
		||||
    mobj = re.match(u'(?u)#(x?\\d+)', entity)
 | 
			
		||||
    if mobj is not None:
 | 
			
		||||
        numstr = mobj.group(1)
 | 
			
		||||
        if numstr.startswith(u'x'):
 | 
			
		||||
            base = 16
 | 
			
		||||
            numstr = u'0%s' % numstr
 | 
			
		||||
        else:
 | 
			
		||||
            base = 10
 | 
			
		||||
        return compat_chr(int(numstr, base))
 | 
			
		||||
 | 
			
		||||
    # Unknown entity in name, return its literal representation
 | 
			
		||||
    return (u'&%s;' % entity)
 | 
			
		||||
 | 
			
		||||
compat_html_parser.locatestarttagend = re.compile(r"""<[a-zA-Z][-.a-zA-Z0-9:_]*(?:\s+(?:(?<=['"\s])[^\s/>][^\s/=>]*(?:\s*=+\s*(?:'[^']*'|"[^"]*"|(?!['"])[^>\s]*))?\s*)*)?\s*""", re.VERBOSE) # backport bugfix
 | 
			
		||||
class AttrParser(compat_html_parser.HTMLParser):
 | 
			
		||||
    """Modified HTMLParser that isolates a tag with the specified attribute"""
 | 
			
		||||
    def __init__(self, attribute, value):
 | 
			
		||||
        self.attribute = attribute
 | 
			
		||||
        self.value = value
 | 
			
		||||
        self.result = None
 | 
			
		||||
        self.started = False
 | 
			
		||||
        self.depth = {}
 | 
			
		||||
        self.html = None
 | 
			
		||||
        self.watch_startpos = False
 | 
			
		||||
        self.error_count = 0
 | 
			
		||||
        compat_html_parser.HTMLParser.__init__(self)
 | 
			
		||||
 | 
			
		||||
    def error(self, message):
 | 
			
		||||
        if self.error_count > 10 or self.started:
 | 
			
		||||
            raise compat_html_parser.HTMLParseError(message, self.getpos())
 | 
			
		||||
        self.rawdata = '\n'.join(self.html.split('\n')[self.getpos()[0]:]) # skip one line
 | 
			
		||||
        self.error_count += 1
 | 
			
		||||
        self.goahead(1)
 | 
			
		||||
 | 
			
		||||
    def loads(self, html):
 | 
			
		||||
        self.html = html
 | 
			
		||||
        self.feed(html)
 | 
			
		||||
        self.close()
 | 
			
		||||
 | 
			
		||||
    def handle_starttag(self, tag, attrs):
 | 
			
		||||
        attrs = dict(attrs)
 | 
			
		||||
        if self.started:
 | 
			
		||||
            self.find_startpos(None)
 | 
			
		||||
        if self.attribute in attrs and attrs[self.attribute] == self.value:
 | 
			
		||||
            self.result = [tag]
 | 
			
		||||
            self.started = True
 | 
			
		||||
            self.watch_startpos = True
 | 
			
		||||
        if self.started:
 | 
			
		||||
            if not tag in self.depth: self.depth[tag] = 0
 | 
			
		||||
            self.depth[tag] += 1
 | 
			
		||||
 | 
			
		||||
    def handle_endtag(self, tag):
 | 
			
		||||
        if self.started:
 | 
			
		||||
            if tag in self.depth: self.depth[tag] -= 1
 | 
			
		||||
            if self.depth[self.result[0]] == 0:
 | 
			
		||||
                self.started = False
 | 
			
		||||
                self.result.append(self.getpos())
 | 
			
		||||
 | 
			
		||||
    def find_startpos(self, x):
 | 
			
		||||
        """Needed to put the start position of the result (self.result[1])
 | 
			
		||||
        after the opening tag with the requested id"""
 | 
			
		||||
        if self.watch_startpos:
 | 
			
		||||
            self.watch_startpos = False
 | 
			
		||||
            self.result.append(self.getpos())
 | 
			
		||||
    handle_entityref = handle_charref = handle_data = handle_comment = \
 | 
			
		||||
    handle_decl = handle_pi = unknown_decl = find_startpos
 | 
			
		||||
 | 
			
		||||
    def get_result(self):
 | 
			
		||||
        if self.result is None:
 | 
			
		||||
            return None
 | 
			
		||||
        if len(self.result) != 3:
 | 
			
		||||
            return None
 | 
			
		||||
        lines = self.html.split('\n')
 | 
			
		||||
        lines = lines[self.result[1][0]-1:self.result[2][0]]
 | 
			
		||||
        lines[0] = lines[0][self.result[1][1]:]
 | 
			
		||||
        if len(lines) == 1:
 | 
			
		||||
            lines[-1] = lines[-1][:self.result[2][1]-self.result[1][1]]
 | 
			
		||||
        lines[-1] = lines[-1][:self.result[2][1]]
 | 
			
		||||
        return '\n'.join(lines).strip()
 | 
			
		||||
# Hack for https://github.com/rg3/youtube-dl/issues/662
 | 
			
		||||
if sys.version_info < (2, 7, 3):
 | 
			
		||||
    AttrParser.parse_endtag = (lambda self, i:
 | 
			
		||||
        i + len("</scr'+'ipt>")
 | 
			
		||||
        if self.rawdata[i:].startswith("</scr'+'ipt>")
 | 
			
		||||
        else compat_html_parser.HTMLParser.parse_endtag(self, i))
 | 
			
		||||
 | 
			
		||||
def get_element_by_id(id, html):
 | 
			
		||||
    """Return the content of the tag with the specified ID in the passed HTML document"""
 | 
			
		||||
    return get_element_by_attribute("id", id, html)
 | 
			
		||||
 | 
			
		||||
def get_element_by_attribute(attribute, value, html):
 | 
			
		||||
    """Return the content of the tag with the specified attribute in the passed HTML document"""
 | 
			
		||||
    parser = AttrParser(attribute, value)
 | 
			
		||||
    try:
 | 
			
		||||
        parser.loads(html)
 | 
			
		||||
    except compat_html_parser.HTMLParseError:
 | 
			
		||||
        pass
 | 
			
		||||
    return parser.get_result()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def clean_html(html):
 | 
			
		||||
    """Clean an HTML snippet into a readable string"""
 | 
			
		||||
    # Newline vs <br />
 | 
			
		||||
    html = html.replace('\n', ' ')
 | 
			
		||||
    html = re.sub(r'\s*<\s*br\s*/?\s*>\s*', '\n', html)
 | 
			
		||||
    html = re.sub(r'<\s*/\s*p\s*>\s*<\s*p[^>]*>', '\n', html)
 | 
			
		||||
    # Strip html tags
 | 
			
		||||
    html = re.sub('<.*?>', '', html)
 | 
			
		||||
    # Replace html entities
 | 
			
		||||
    html = unescapeHTML(html)
 | 
			
		||||
    return html.strip()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def sanitize_open(filename, open_mode):
 | 
			
		||||
    """Try to open the given filename, and slightly tweak it if this fails.
 | 
			
		||||
 | 
			
		||||
    Attempts to open the given filename. If this fails, it tries to change
 | 
			
		||||
    the filename slightly, step by step, until it's either able to open it
 | 
			
		||||
    or it fails and raises a final exception, like the standard open()
 | 
			
		||||
    function.
 | 
			
		||||
 | 
			
		||||
    It returns the tuple (stream, definitive_file_name).
 | 
			
		||||
    """
 | 
			
		||||
    try:
 | 
			
		||||
        if filename == u'-':
 | 
			
		||||
            if sys.platform == 'win32':
 | 
			
		||||
                import msvcrt
 | 
			
		||||
                msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
 | 
			
		||||
            return (sys.stdout.buffer if hasattr(sys.stdout, 'buffer') else sys.stdout, filename)
 | 
			
		||||
        stream = open(encodeFilename(filename), open_mode)
 | 
			
		||||
        return (stream, filename)
 | 
			
		||||
    except (IOError, OSError) as err:
 | 
			
		||||
        # In case of error, try to remove win32 forbidden chars
 | 
			
		||||
        filename = re.sub(u'[/<>:"\\|\\\\?\\*]', u'#', filename)
 | 
			
		||||
 | 
			
		||||
        # An exception here should be caught in the caller
 | 
			
		||||
        stream = open(encodeFilename(filename), open_mode)
 | 
			
		||||
        return (stream, filename)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def timeconvert(timestr):
 | 
			
		||||
    """Convert RFC 2822 defined time string into system timestamp"""
 | 
			
		||||
    timestamp = None
 | 
			
		||||
    timetuple = email.utils.parsedate_tz(timestr)
 | 
			
		||||
    if timetuple is not None:
 | 
			
		||||
        timestamp = email.utils.mktime_tz(timetuple)
 | 
			
		||||
    return timestamp
 | 
			
		||||
 | 
			
		||||
def sanitize_filename(s, restricted=False, is_id=False):
 | 
			
		||||
    """Sanitizes a string so it could be used as part of a filename.
 | 
			
		||||
    If restricted is set, use a stricter subset of allowed characters.
 | 
			
		||||
    Set is_id if this is not an arbitrary string, but an ID that should be kept if possible
 | 
			
		||||
    """
 | 
			
		||||
    def replace_insane(char):
 | 
			
		||||
        if char == '?' or ord(char) < 32 or ord(char) == 127:
 | 
			
		||||
            return ''
 | 
			
		||||
        elif char == '"':
 | 
			
		||||
            return '' if restricted else '\''
 | 
			
		||||
        elif char == ':':
 | 
			
		||||
            return '_-' if restricted else ' -'
 | 
			
		||||
        elif char in '\\/|*<>':
 | 
			
		||||
            return '_'
 | 
			
		||||
        if restricted and (char in '!&\'()[]{}$;`^,#' or char.isspace()):
 | 
			
		||||
            return '_'
 | 
			
		||||
        if restricted and ord(char) > 127:
 | 
			
		||||
            return '_'
 | 
			
		||||
        return char
 | 
			
		||||
 | 
			
		||||
    result = u''.join(map(replace_insane, s))
 | 
			
		||||
    if not is_id:
 | 
			
		||||
        while '__' in result:
 | 
			
		||||
            result = result.replace('__', '_')
 | 
			
		||||
        result = result.strip('_')
 | 
			
		||||
        # Common case of "Foreign band name - English song title"
 | 
			
		||||
        if restricted and result.startswith('-_'):
 | 
			
		||||
            result = result[2:]
 | 
			
		||||
        if not result:
 | 
			
		||||
            result = '_'
 | 
			
		||||
    return result
 | 
			
		||||
 | 
			
		||||
def orderedSet(iterable):
 | 
			
		||||
    """ Remove all duplicates from the input iterable """
 | 
			
		||||
    res = []
 | 
			
		||||
    for el in iterable:
 | 
			
		||||
        if el not in res:
 | 
			
		||||
            res.append(el)
 | 
			
		||||
    return res
 | 
			
		||||
 | 
			
		||||
def unescapeHTML(s):
 | 
			
		||||
    """
 | 
			
		||||
    @param s a string
 | 
			
		||||
    """
 | 
			
		||||
    assert type(s) == type(u'')
 | 
			
		||||
 | 
			
		||||
    result = re.sub(u'(?u)&(.+?);', htmlentity_transform, s)
 | 
			
		||||
    return result
 | 
			
		||||
 | 
			
		||||
def encodeFilename(s):
 | 
			
		||||
    """
 | 
			
		||||
    @param s The name of the file
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    assert type(s) == type(u'')
 | 
			
		||||
 | 
			
		||||
    # Python 3 has a Unicode API
 | 
			
		||||
    if sys.version_info >= (3, 0):
 | 
			
		||||
        return s
 | 
			
		||||
 | 
			
		||||
    if sys.platform == 'win32' and sys.getwindowsversion()[0] >= 5:
 | 
			
		||||
        # Pass u'' directly to use Unicode APIs on Windows 2000 and up
 | 
			
		||||
        # (Detecting Windows NT 4 is tricky because 'major >= 4' would
 | 
			
		||||
        # match Windows 9x series as well. Besides, NT 4 is obsolete.)
 | 
			
		||||
        return s
 | 
			
		||||
    else:
 | 
			
		||||
        encoding = sys.getfilesystemencoding()
 | 
			
		||||
        if encoding is None:
 | 
			
		||||
            encoding = 'utf-8'
 | 
			
		||||
        return s.encode(encoding, 'ignore')
 | 
			
		||||
 | 
			
		||||
def decodeOption(optval):
 | 
			
		||||
    if optval is None:
 | 
			
		||||
        return optval
 | 
			
		||||
    if isinstance(optval, bytes):
 | 
			
		||||
        optval = optval.decode(preferredencoding())
 | 
			
		||||
 | 
			
		||||
    assert isinstance(optval, compat_str)
 | 
			
		||||
    return optval
 | 
			
		||||
 | 
			
		||||
def formatSeconds(secs):
 | 
			
		||||
    if secs > 3600:
 | 
			
		||||
        return '%d:%02d:%02d' % (secs // 3600, (secs % 3600) // 60, secs % 60)
 | 
			
		||||
    elif secs > 60:
 | 
			
		||||
        return '%d:%02d' % (secs // 60, secs % 60)
 | 
			
		||||
    else:
 | 
			
		||||
        return '%d' % secs
 | 
			
		||||
 | 
			
		||||
def make_HTTPS_handler(opts):
 | 
			
		||||
    if sys.version_info < (3,2):
 | 
			
		||||
        # Python's 2.x handler is very simplistic
 | 
			
		||||
        return compat_urllib_request.HTTPSHandler()
 | 
			
		||||
    else:
 | 
			
		||||
        import ssl
 | 
			
		||||
        context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
 | 
			
		||||
        context.set_default_verify_paths()
 | 
			
		||||
        
 | 
			
		||||
        context.verify_mode = (ssl.CERT_NONE
 | 
			
		||||
                               if opts.no_check_certificate
 | 
			
		||||
                               else ssl.CERT_REQUIRED)
 | 
			
		||||
        return compat_urllib_request.HTTPSHandler(context=context)
 | 
			
		||||
 | 
			
		||||
class ExtractorError(Exception):
 | 
			
		||||
    """Error during info extraction."""
 | 
			
		||||
    def __init__(self, msg, tb=None):
 | 
			
		||||
        """ tb, if given, is the original traceback (so that it can be printed out). """
 | 
			
		||||
        super(ExtractorError, self).__init__(msg)
 | 
			
		||||
        self.traceback = tb
 | 
			
		||||
        self.exc_info = sys.exc_info()  # preserve original exception
 | 
			
		||||
 | 
			
		||||
    def format_traceback(self):
 | 
			
		||||
        if self.traceback is None:
 | 
			
		||||
            return None
 | 
			
		||||
        return u''.join(traceback.format_tb(self.traceback))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DownloadError(Exception):
 | 
			
		||||
    """Download Error exception.
 | 
			
		||||
 | 
			
		||||
    This exception may be thrown by FileDownloader objects if they are not
 | 
			
		||||
    configured to continue on errors. They will contain the appropriate
 | 
			
		||||
    error message.
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, msg, exc_info=None):
 | 
			
		||||
        """ exc_info, if given, is the original exception that caused the trouble (as returned by sys.exc_info()). """
 | 
			
		||||
        super(DownloadError, self).__init__(msg)
 | 
			
		||||
        self.exc_info = exc_info
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SameFileError(Exception):
 | 
			
		||||
    """Same File exception.
 | 
			
		||||
 | 
			
		||||
    This exception will be thrown by FileDownloader objects if they detect
 | 
			
		||||
    multiple files would have to be downloaded to the same file on disk.
 | 
			
		||||
    """
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PostProcessingError(Exception):
 | 
			
		||||
    """Post Processing exception.
 | 
			
		||||
 | 
			
		||||
    This exception may be raised by PostProcessor's .run() method to
 | 
			
		||||
    indicate an error in the postprocessing task.
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, msg):
 | 
			
		||||
        self.msg = msg
 | 
			
		||||
 | 
			
		||||
class MaxDownloadsReached(Exception):
 | 
			
		||||
    """ --max-downloads limit has been reached. """
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UnavailableVideoError(Exception):
 | 
			
		||||
    """Unavailable Format exception.
 | 
			
		||||
 | 
			
		||||
    This exception will be thrown when a video is requested
 | 
			
		||||
    in a format that is not available for that video.
 | 
			
		||||
    """
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ContentTooShortError(Exception):
 | 
			
		||||
    """Content Too Short exception.
 | 
			
		||||
 | 
			
		||||
    This exception may be raised by FileDownloader objects when a file they
 | 
			
		||||
    download is too small for what the server announced first, indicating
 | 
			
		||||
    the connection was probably interrupted.
 | 
			
		||||
    """
 | 
			
		||||
    # Both in bytes
 | 
			
		||||
    downloaded = None
 | 
			
		||||
    expected = None
 | 
			
		||||
 | 
			
		||||
    def __init__(self, downloaded, expected):
 | 
			
		||||
        self.downloaded = downloaded
 | 
			
		||||
        self.expected = expected
 | 
			
		||||
 | 
			
		||||
class YoutubeDLHandler(compat_urllib_request.HTTPHandler):
 | 
			
		||||
    """Handler for HTTP requests and responses.
 | 
			
		||||
 | 
			
		||||
    This class, when installed with an OpenerDirector, automatically adds
 | 
			
		||||
    the standard headers to every HTTP request and handles gzipped and
 | 
			
		||||
    deflated responses from web servers. If compression is to be avoided in
 | 
			
		||||
    a particular request, the original request in the program code only has
 | 
			
		||||
    to include the HTTP header "Youtubedl-No-Compression", which will be
 | 
			
		||||
    removed before making the real request.
 | 
			
		||||
 | 
			
		||||
    Part of this code was copied from:
 | 
			
		||||
 | 
			
		||||
    http://techknack.net/python-urllib2-handlers/
 | 
			
		||||
 | 
			
		||||
    Andrew Rowls, the author of that code, agreed to release it to the
 | 
			
		||||
    public domain.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def deflate(data):
 | 
			
		||||
        try:
 | 
			
		||||
            return zlib.decompress(data, -zlib.MAX_WBITS)
 | 
			
		||||
        except zlib.error:
 | 
			
		||||
            return zlib.decompress(data)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def addinfourl_wrapper(stream, headers, url, code):
 | 
			
		||||
        if hasattr(compat_urllib_request.addinfourl, 'getcode'):
 | 
			
		||||
            return compat_urllib_request.addinfourl(stream, headers, url, code)
 | 
			
		||||
        ret = compat_urllib_request.addinfourl(stream, headers, url)
 | 
			
		||||
        ret.code = code
 | 
			
		||||
        return ret
 | 
			
		||||
 | 
			
		||||
    def http_request(self, req):
 | 
			
		||||
        for h,v in std_headers.items():
 | 
			
		||||
            if h in req.headers:
 | 
			
		||||
                del req.headers[h]
 | 
			
		||||
            req.add_header(h, v)
 | 
			
		||||
        if 'Youtubedl-no-compression' in req.headers:
 | 
			
		||||
            if 'Accept-encoding' in req.headers:
 | 
			
		||||
                del req.headers['Accept-encoding']
 | 
			
		||||
            del req.headers['Youtubedl-no-compression']
 | 
			
		||||
        if 'Youtubedl-user-agent' in req.headers:
 | 
			
		||||
            if 'User-agent' in req.headers:
 | 
			
		||||
                del req.headers['User-agent']
 | 
			
		||||
            req.headers['User-agent'] = req.headers['Youtubedl-user-agent']
 | 
			
		||||
            del req.headers['Youtubedl-user-agent']
 | 
			
		||||
        return req
 | 
			
		||||
 | 
			
		||||
    def http_response(self, req, resp):
 | 
			
		||||
        old_resp = resp
 | 
			
		||||
        # gzip
 | 
			
		||||
        if resp.headers.get('Content-encoding', '') == 'gzip':
 | 
			
		||||
            gz = gzip.GzipFile(fileobj=io.BytesIO(resp.read()), mode='r')
 | 
			
		||||
            resp = self.addinfourl_wrapper(gz, old_resp.headers, old_resp.url, old_resp.code)
 | 
			
		||||
            resp.msg = old_resp.msg
 | 
			
		||||
        # deflate
 | 
			
		||||
        if resp.headers.get('Content-encoding', '') == 'deflate':
 | 
			
		||||
            gz = io.BytesIO(self.deflate(resp.read()))
 | 
			
		||||
            resp = self.addinfourl_wrapper(gz, old_resp.headers, old_resp.url, old_resp.code)
 | 
			
		||||
            resp.msg = old_resp.msg
 | 
			
		||||
        return resp
 | 
			
		||||
 | 
			
		||||
    https_request = http_request
 | 
			
		||||
    https_response = http_response
 | 
			
		||||
 | 
			
		||||
def unified_strdate(date_str):
 | 
			
		||||
    """Return a string with the date in the format YYYYMMDD"""
 | 
			
		||||
    upload_date = None
 | 
			
		||||
    #Replace commas
 | 
			
		||||
    date_str = date_str.replace(',',' ')
 | 
			
		||||
    # %z (UTC offset) is only supported in python>=3.2
 | 
			
		||||
    date_str = re.sub(r' (\+|-)[\d]*$', '', date_str)
 | 
			
		||||
    format_expressions = ['%d %B %Y', '%B %d %Y', '%b %d %Y', '%Y-%m-%d', '%d/%m/%Y', '%Y/%m/%d %H:%M:%S']
 | 
			
		||||
    for expression in format_expressions:
 | 
			
		||||
        try:
 | 
			
		||||
            upload_date = datetime.datetime.strptime(date_str, expression).strftime('%Y%m%d')
 | 
			
		||||
        except:
 | 
			
		||||
            pass
 | 
			
		||||
    return upload_date
 | 
			
		||||
 | 
			
		||||
def date_from_str(date_str):
 | 
			
		||||
    """
 | 
			
		||||
    Return a datetime object from a string in the format YYYYMMDD or
 | 
			
		||||
    (now|today)[+-][0-9](day|week|month|year)(s)?"""
 | 
			
		||||
    today = datetime.date.today()
 | 
			
		||||
    if date_str == 'now'or date_str == 'today':
 | 
			
		||||
        return today
 | 
			
		||||
    match = re.match('(now|today)(?P<sign>[+-])(?P<time>\d+)(?P<unit>day|week|month|year)(s)?', date_str)
 | 
			
		||||
    if match is not None:
 | 
			
		||||
        sign = match.group('sign')
 | 
			
		||||
        time = int(match.group('time'))
 | 
			
		||||
        if sign == '-':
 | 
			
		||||
            time = -time
 | 
			
		||||
        unit = match.group('unit')
 | 
			
		||||
        #A bad aproximation?
 | 
			
		||||
        if unit == 'month':
 | 
			
		||||
            unit = 'day'
 | 
			
		||||
            time *= 30
 | 
			
		||||
        elif unit == 'year':
 | 
			
		||||
            unit = 'day'
 | 
			
		||||
            time *= 365
 | 
			
		||||
        unit += 's'
 | 
			
		||||
        delta = datetime.timedelta(**{unit: time})
 | 
			
		||||
        return today + delta
 | 
			
		||||
    return datetime.datetime.strptime(date_str, "%Y%m%d").date()
 | 
			
		||||
    
 | 
			
		||||
class DateRange(object):
 | 
			
		||||
    """Represents a time interval between two dates"""
 | 
			
		||||
    def __init__(self, start=None, end=None):
 | 
			
		||||
        """start and end must be strings in the format accepted by date"""
 | 
			
		||||
        if start is not None:
 | 
			
		||||
            self.start = date_from_str(start)
 | 
			
		||||
        else:
 | 
			
		||||
            self.start = datetime.datetime.min.date()
 | 
			
		||||
        if end is not None:
 | 
			
		||||
            self.end = date_from_str(end)
 | 
			
		||||
        else:
 | 
			
		||||
            self.end = datetime.datetime.max.date()
 | 
			
		||||
        if self.start > self.end:
 | 
			
		||||
            raise ValueError('Date range: "%s" , the start date must be before the end date' % self)
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def day(cls, day):
 | 
			
		||||
        """Returns a range that only contains the given day"""
 | 
			
		||||
        return cls(day,day)
 | 
			
		||||
    def __contains__(self, date):
 | 
			
		||||
        """Check if the date is in the range"""
 | 
			
		||||
        if not isinstance(date, datetime.date):
 | 
			
		||||
            date = date_from_str(date)
 | 
			
		||||
        return self.start <= date <= self.end
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return '%s - %s' % ( self.start.isoformat(), self.end.isoformat())
 | 
			
		||||
							
								
								
									
										2
									
								
								youtube_dl/version.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								youtube_dl/version.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
 | 
			
		||||
__version__ = '2013.05.07'
 | 
			
		||||
		Reference in New Issue
	
	Block a user