Compare commits
	
		
			793 Commits
		
	
	
		
			2008.09.20
			...
			2012.11.29
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					88db5ef279 | ||
| 
						 | 
					f8d8b39bba | ||
| 
						 | 
					dcd60025f8 | ||
| 
						 | 
					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 | ||
| 
						 | 
					74033a662d | ||
| 
						 | 
					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 | ||
| 
						 | 
					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 | ||
| 
						 | 
					daa982bc01 | ||
| 
						 | 
					767414a292 | ||
| 
						 | 
					7b417b388a | ||
| 
						 | 
					44424ceee9 | ||
| 
						 | 
					08a5b7f800 | ||
| 
						 | 
					1cde6f1d52 | ||
| 
						 | 
					2d8acd8039 | ||
| 
						 | 
					67035ede49 | ||
| 
						 | 
					eb6c37da43 | ||
| 
						 | 
					2736595628 | ||
| 
						 | 
					7b1a2bbe17 | ||
| 
						 | 
					c25303c3d5 | ||
| 
						 | 
					cc025e1226 | ||
| 
						 | 
					eca1b76f01 | ||
| 
						 | 
					366cbfb04a | ||
| 
						 | 
					18bb3d1e35 | ||
| 
						 | 
					10e7194db1 | ||
| 
						 | 
					ef357c4bf2 | ||
| 
						 | 
					5260e68f64 | ||
| 
						 | 
					6a1ca41e17 | ||
| 
						 | 
					c99dcbd2d6 | ||
| 
						 | 
					da0db53a75 | ||
| 
						 | 
					c52b01f326 | ||
| 
						 | 
					36597dc40f | ||
| 
						 | 
					9b4556c469 | ||
| 
						 | 
					f3098c4d8a | ||
| 
						 | 
					bdb3f7a769 | ||
| 
						 | 
					afb5b55de6 | ||
| 
						 | 
					c23cec29a3 | ||
| 
						 | 
					e5b9fac281 | ||
| 
						 | 
					08c1d0d3bc | ||
| 
						 | 
					20e91e8375 | ||
| 
						 | 
					f9c6878714 | ||
| 
						 | 
					8c5dc3ad40 | ||
| 
						 | 
					1d2e86aed9 | ||
| 
						 | 
					a2f7e3a5bb | ||
| 
						 | 
					f2a3a3522c | ||
| 
						 | 
					b487ef0833 | ||
| 
						 | 
					d0922f29a3 | ||
| 
						 | 
					b90bcbe79e | ||
| 
						 | 
					8236e85178 | ||
| 
						 | 
					803abae206 | ||
| 
						 | 
					50bdd8a9e7 | ||
| 
						 | 
					34554a7ad4 | ||
| 
						 | 
					93e1659586 | ||
| 
						 | 
					b576abb457 | ||
| 
						 | 
					f166bccc8f | ||
| 
						 | 
					5a2ba45e09 | ||
| 
						 | 
					e133e1213f | ||
| 
						 | 
					454d6691d8 | ||
| 
						 | 
					d793aebaed | ||
| 
						 | 
					5991ddfd7a | ||
| 
						 | 
					a88bc6bbd3 | ||
| 
						 | 
					46c8c43266 | ||
| 
						 | 
					fedf9f3902 | ||
| 
						 | 
					0f862ea18c | ||
| 
						 | 
					c8e30044b8 | ||
| 
						 | 
					cec3a53cbd | ||
| 
						 | 
					6fc5b0bb17 | ||
| 
						 | 
					9b0a8bc198 | ||
| 
						 | 
					e5e74ffb97 | ||
| 
						 | 
					eb99a7ee5f | ||
| 
						 | 
					50891fece7 | ||
| 
						 | 
					ef53099e35 | ||
| 
						 | 
					c0a10ca8dc | ||
| 
						 | 
					8f88eb1fa7 | ||
| 
						 | 
					447b1d7170 | ||
| 
						 | 
					dbddab2799 | ||
| 
						 | 
					802622ac1c | ||
| 
						 | 
					e0e56865a0 | ||
| 
						 | 
					eb11aaccbb | ||
| 
						 | 
					d207e7cf88 | ||
| 
						 | 
					36cf7bccde | ||
| 
						 | 
					5fd5ce0838 | ||
| 
						 | 
					6ae796b1ee | ||
| 
						 | 
					9c3e23fb64 | ||
| 
						 | 
					5f9f2b7396 | ||
| 
						 | 
					4618f3da74 | ||
| 
						 | 
					eb0387a848 | ||
| 
						 | 
					fe6dc08b79 | ||
| 
						 | 
					4f2a5e06da | ||
| 
						 | 
					2c8d32de33 | ||
| 
						 | 
					2b70537d7b | ||
| 
						 | 
					6a4f0a114d | ||
| 
						 | 
					5adcaa4385 | ||
| 
						 | 
					51c8e53ffe | ||
| 
						 | 
					4f9f96f646 | ||
| 
						 | 
					5fb3df4aff | ||
| 
						 | 
					7a9054ec79 | ||
| 
						 | 
					2770590d5a | ||
| 
						 | 
					e9cb9c2811 | ||
| 
						 | 
					1cab2c6dcf | ||
| 
						 | 
					86e709d3de | ||
| 
						 | 
					8519c32d25 | ||
| 
						 | 
					f3dc18d874 | ||
| 
						 | 
					1293ce58ac | ||
| 
						 | 
					0a3c8b6291 | ||
| 
						 | 
					134cff47ab | ||
| 
						 | 
					f137bef973 | ||
| 
						 | 
					2bf94b3116 | ||
| 
						 | 
					6bcd846b52 | ||
| 
						 | 
					2fb47e073a | ||
| 
						 | 
					05b4029662 | ||
| 
						 | 
					33d507f1fe | ||
| 
						 | 
					c44b9ee95e | ||
| 
						 | 
					8126094cf1 | ||
| 
						 | 
					0ac22e4f5a | ||
| 
						 | 
					c31b124d7a | ||
| 
						 | 
					47b8dab29e | ||
| 
						 | 
					91e6a3855b | ||
| 
						 | 
					5623100e43 | ||
| 
						 | 
					6eb08fbf8b | ||
| 
						 | 
					437d76c19a | ||
| 
						 | 
					2152ee8601 | ||
| 
						 | 
					a1cab7cead | ||
| 
						 | 
					8b95c38707 | ||
| 
						 | 
					c6b55a8d48 | ||
| 
						 | 
					aded78d9e2 | ||
| 
						 | 
					7745f5d881 | ||
| 
						 | 
					18b7f87409 | ||
| 
						 | 
					62a29bbf7b | ||
| 
						 | 
					2fc31a4872 | ||
| 
						 | 
					44c636df89 | ||
| 
						 | 
					1e055db69c | ||
| 
						 | 
					0ecedbdb03 | ||
| 
						 | 
					43c0a396a2 | ||
| 
						 | 
					00f3977f77 | ||
| 
						 | 
					e26005adea | ||
| 
						 | 
					4b0d9eed45 | ||
| 
						 | 
					3efa45c3a2 | ||
| 
						 | 
					2727dbf78d | ||
| 
						 | 
					e3f7e05c27 | ||
| 
						 | 
					da54ed4412 | ||
| 
						 | 
					d8edbf3a93 | ||
| 
						 | 
					a62db07f58 | ||
| 
						 | 
					b58faab5e7 | ||
| 
						 | 
					854cad639e | ||
| 
						 | 
					cb25a0e30c | ||
| 
						 | 
					377086af3d | ||
| 
						 | 
					820eedcb50 | ||
| 
						 | 
					da273188f3 | ||
| 
						 | 
					1bd9258272 | ||
| 
						 | 
					c076845454 | ||
| 
						 | 
					afd233c05c | ||
| 
						 | 
					3072fab115 | ||
| 
						 | 
					87cbd21323 | ||
| 
						 | 
					3b84a43076 | ||
| 
						 | 
					2c8bedd12c | ||
| 
						 | 
					1a3fe4212f | ||
| 
						 | 
					c4cfbdf5a5 | ||
| 
						 | 
					ef9f8451c8 | ||
| 
						 | 
					9f5f960213 | ||
| 
						 | 
					a4a590b5b1 | ||
| 
						 | 
					7f69fd3b39 | ||
| 
						 | 
					a7e5259c33 | ||
| 
						 | 
					7cc3c6fd62 | ||
| 
						 | 
					d119b54df6 | ||
| 
						 | 
					8cc98b2358 | ||
| 
						 | 
					f24c674b04 | ||
| 
						 | 
					58b53721af | ||
| 
						 | 
					f74e22ae28 | ||
| 
						 | 
					16c73c2e51 | ||
| 
						 | 
					5776c3295b | ||
| 
						 | 
					9e0dd8692e | ||
| 
						 | 
					5aba6ea4fe | ||
| 
						 | 
					c5a088d341 | ||
| 
						 | 
					92743d423a | ||
| 
						 | 
					9e1ee3364a | ||
| 
						 | 
					e0edf1e041 | ||
| 
						 | 
					6025795d95 | ||
| 
						 | 
					e30189021d | ||
| 
						 | 
					09bd408c28 | ||
| 
						 | 
					9f7963468b | ||
| 
						 | 
					b940c84a24 | ||
| 
						 | 
					0f7099a59b | ||
| 
						 | 
					c02d8e4040 | ||
| 
						 | 
					0f6b00b587 | ||
| 
						 | 
					7b531c0be6 | ||
| 
						 | 
					0d14e225fa | ||
| 
						 | 
					0fe64c04f8 | ||
| 
						 | 
					0d8d9877ad | ||
| 
						 | 
					8cc42e7c1a | ||
| 
						 | 
					1987c2325a | ||
| 
						 | 
					aac3fe0f4a | ||
| 
						 | 
					3fb2c487c0 | ||
| 
						 | 
					d3975459d1 | ||
| 
						 | 
					ccbd296bee | ||
| 
						 | 
					e7cf18cb6b | ||
| 
						 | 
					09cc744c90 | ||
| 
						 | 
					a57ed21f6d | ||
| 
						 | 
					975a91d0ac | ||
| 
						 | 
					b905e5f583 | ||
| 
						 | 
					ef4f4544a2 | ||
| 
						 | 
					5c1327931a | ||
| 
						 | 
					106d091e80 | ||
| 
						 | 
					f83ae7816b | ||
| 
						 | 
					f148ea4473 | ||
| 
						 | 
					7d950ca1d6 | ||
| 
						 | 
					d157d2597a | ||
| 
						 | 
					e567ef93d8 | ||
| 
						 | 
					27179cfdba | ||
| 
						 | 
					6f0ff3bab9 | ||
| 
						 | 
					a9806fd83d | ||
| 
						 | 
					62cf7aaf9a | ||
| 
						 | 
					a1f03c7b06 | ||
| 
						 | 
					f8dc441430 | ||
| 
						 | 
					010ebaf783 | ||
| 
						 | 
					138b11f36e | ||
| 
						 | 
					05df0c1d4a | ||
| 
						 | 
					b04bb07c94 | ||
| 
						 | 
					b620a5f811 | ||
| 
						 | 
					b3a27b5217 | ||
| 
						 | 
					5e596cac0a | ||
| 
						 | 
					1e47d226e1 | ||
| 
						 | 
					817e8f523f | ||
| 
						 | 
					8cc4434116 | ||
| 
						 | 
					893a13df55 | ||
| 
						 | 
					c34e358456 | ||
| 
						 | 
					a6a61601de | ||
| 
						 | 
					e0c982c8d0 | ||
| 
						 | 
					331ce0a05d | ||
| 
						 | 
					80066952bc | ||
| 
						 | 
					e08878f498 | ||
| 
						 | 
					a949a3ae6b | ||
| 
						 | 
					7df4635faf | ||
| 
						 | 
					f79007e542 | ||
| 
						 | 
					ac249f421f | ||
| 
						 | 
					e86e9474bf | ||
| 
						 | 
					bbd4bb037a | ||
| 
						 | 
					5c44af1875 | ||
| 
						 | 
					33407be7d6 | ||
| 
						 | 
					8e686771af | ||
| 
						 | 
					2933532c5b | ||
| 
						 | 
					6b57e8c5ac | ||
| 
						 | 
					c6c555cf8a | ||
| 
						 | 
					db7e31b853 | ||
| 
						 | 
					d67e097462 | ||
| 
						 | 
					38ed13444a | ||
| 
						 | 
					8a9f53bebf | ||
| 
						 | 
					80cc23304f | ||
| 
						 | 
					813962f85a | ||
| 
						 | 
					109626fcc0 | ||
| 
						 | 
					204c9398ab | ||
| 
						 | 
					2962317dea | ||
| 
						 | 
					268fb2bdd8 | ||
| 
						 | 
					101e0d1e91 | ||
| 
						 | 
					f95f29fd25 | ||
| 
						 | 
					06f34701fe | ||
| 
						 | 
					5ce7d172d7 | ||
| 
						 | 
					2e3a32e4ac | ||
| 
						 | 
					8190e3631b | ||
| 
						 | 
					e4db6fd042 | ||
| 
						 | 
					497cd3e68e | ||
| 
						 | 
					460d8acbaa | ||
| 
						 | 
					9bf7fa5213 | ||
| 
						 | 
					73f4e7afba | ||
| 
						 | 
					9715661c19 | ||
| 
						 | 
					14912efbb7 | ||
| 
						 | 
					96942e6224 | ||
| 
						 | 
					df372a655f | ||
| 
						 | 
					9e9647d9a1 | ||
| 
						 | 
					8da0080d36 | ||
| 
						 | 
					57edaa5bac | ||
| 
						 | 
					823fcda12a | ||
| 
						 | 
					f2413e6793 | ||
| 
						 | 
					c833bb97dc | ||
| 
						 | 
					7e2dd306fe | ||
| 
						 | 
					dea147f78e | ||
| 
						 | 
					08cf5cb80b | ||
| 
						 | 
					4135fa4585 | ||
| 
						 | 
					fd8ede223e | ||
| 
						 | 
					2b06c33d19 | ||
| 
						 | 
					ca6a11fa59 | ||
| 
						 | 
					de3ed1f84a | ||
| 
						 | 
					0b59bf4a5e | ||
| 
						 | 
					896a6ea9e2 | ||
| 
						 | 
					7031008c98 | ||
| 
						 | 
					e616ec0ca6 | ||
| 
						 | 
					2a7353b87a | ||
| 
						 | 
					787f2a5d95 | ||
| 
						 | 
					42e3546fb5 | ||
| 
						 | 
					0228ee9788 | ||
| 
						 | 
					131efd1ae0 | ||
| 
						 | 
					2bebb386b8 | ||
| 
						 | 
					7e58d56888 | ||
| 
						 | 
					554bbdc48c | ||
| 
						 | 
					37dfa1e0df | ||
| 
						 | 
					4dd63be193 | ||
| 
						 | 
					7d8d06122d | ||
| 
						 | 
					9177ce4d8c | ||
| 
						 | 
					ce5cafea40 | ||
| 
						 | 
					ae3fc475eb | ||
| 
						 | 
					d063db3810 | ||
| 
						 | 
					6194531831 | ||
| 
						 | 
					2ed1ddd0a0 | ||
| 
						 | 
					eaf4a7288d | ||
| 
						 | 
					6ba562b0e4 | ||
| 
						 | 
					131bc7651a | ||
| 
						 | 
					5caacaddc6 | ||
| 
						 | 
					79f193e5d8 | ||
| 
						 | 
					44e16fa17f | ||
| 
						 | 
					d983524781 | ||
| 
						 | 
					1392f3f52c | ||
| 
						 | 
					43ab0ca432 | ||
| 
						 | 
					31cbdaafd4 | ||
| 
						 | 
					bd3cdf6dc4 | ||
| 
						 | 
					8cc468de75 | ||
| 
						 | 
					31bcb48001 | ||
| 
						 | 
					c201ebc915 | ||
| 
						 | 
					ce9c6a3097 | ||
| 
						 | 
					4cfeb46544 | ||
| 
						 | 
					490fd7aea7 | ||
| 
						 | 
					c05fc6a345 | ||
| 
						 | 
					91bce611c7 | ||
| 
						 | 
					1c1821f8eb | ||
| 
						 | 
					60f8049d05 | ||
| 
						 | 
					49c0028a7a | ||
| 
						 | 
					f1b4bee09d | ||
| 
						 | 
					a04e80a481 | ||
| 
						 | 
					fe788f2c6f | ||
| 
						 | 
					75a4cf3c97 | ||
| 
						 | 
					0487b407a1 | ||
| 
						 | 
					a692ca7c49 | ||
| 
						 | 
					9c457d2a20 | ||
| 
						 | 
					c39c05cdd7 | ||
| 
						 | 
					29f0756805 | ||
| 
						 | 
					d9bc015b3c | ||
| 
						 | 
					4bec29ef4b | ||
| 
						 | 
					ab1f697827 | ||
| 
						 | 
					583c714fde | ||
| 
						 | 
					850ab76560 | ||
| 
						 | 
					f5a5bec351 | ||
| 
						 | 
					f94b636c3e | ||
| 
						 | 
					0833f1eb83 | ||
| 
						 | 
					ad0525b3e6 | ||
| 
						 | 
					30edbf89e4 | ||
| 
						 | 
					eae2666cb4 | ||
| 
						 | 
					2a04438c7c | ||
| 
						 | 
					dd24ff44ab | ||
| 
						 | 
					304a4d85ea | ||
| 
						 | 
					d899774377 | ||
| 
						 | 
					fade05990c | ||
| 
						 | 
					e5b1604882 | ||
| 
						 | 
					0c8beb43f2 | ||
| 
						 | 
					71b7300e63 | ||
| 
						 | 
					8497c36d5a | ||
| 
						 | 
					110cd3462e | ||
| 
						 | 
					18963a36b0 | ||
| 
						 | 
					df1ceb1fd9 | ||
| 
						 | 
					7eb0e89742 | ||
| 
						 | 
					8b07dec5f6 | ||
| 
						 | 
					113e5266cc | ||
| 
						 | 
					55e7c75e12 | ||
| 
						 | 
					ff21a710ae | ||
| 
						 | 
					7374795552 | ||
| 
						 | 
					0cd61126fc | ||
| 
						 | 
					e1f18b8a84 | ||
| 
						 | 
					6a0015a7e0 | ||
| 
						 | 
					7db85b2c70 | ||
| 
						 | 
					f76c2df64e | ||
| 
						 | 
					daa88ccc2e | ||
| 
						 | 
					eb5d184157 | ||
| 
						 | 
					5745bfdcdc | ||
| 
						 | 
					320becd692 | ||
| 
						 | 
					968aa88438 | ||
| 
						 | 
					cbfff4db63 | ||
| 
						 | 
					781daeabdb | ||
| 
						 | 
					705804f5d1 | ||
| 
						 | 
					1d50e3d153 | ||
| 
						 | 
					d69a1c9189 | ||
| 
						 | 
					488f619471 | ||
| 
						 | 
					097ba9472b | ||
| 
						 | 
					554f3e284c | ||
| 
						 | 
					cab60d710a | ||
| 
						 | 
					152edc0d4c | ||
| 
						 | 
					b74c859d0f | ||
| 
						 | 
					0e54320009 | ||
| 
						 | 
					43f35682e9 | ||
| 
						 | 
					ad274509aa | ||
| 
						 | 
					d09744d055 | ||
| 
						 | 
					1c76e23e0f | ||
| 
						 | 
					42bcd27d3b | ||
| 
						 | 
					2740c509b3 | ||
| 
						 | 
					7b7759f5a4 | ||
| 
						 | 
					8d2c83eda5 | ||
| 
						 | 
					2f11508ada | ||
| 
						 | 
					b65740e474 | ||
| 
						 | 
					a825f0ca83 | ||
| 
						 | 
					27c3383e2d | ||
| 
						 | 
					dbccb6cd84 | ||
| 
						 | 
					98164eb3b9 | ||
| 
						 | 
					2851b2ca18 | ||
| 
						 | 
					6f21f68629 | ||
| 
						 | 
					147753eb33 | ||
| 
						 | 
					3aaf887e98 | ||
| 
						 | 
					9bf386d74b | ||
| 
						 | 
					2f4d18a9f7 | ||
| 
						 | 
					b0eddb2eb4 | ||
| 
						 | 
					9cee6d9035 | ||
| 
						 | 
					c8619e0163 | ||
| 
						 | 
					257453b92b | ||
| 
						 | 
					fd9288c315 | ||
| 
						 | 
					1db4ff6054 | ||
| 
						 | 
					763826cf2c | ||
| 
						 | 
					af6a92f4c9 | ||
| 
						 | 
					f995f7127c | ||
| 
						 | 
					e54930cf71 | ||
| 
						 | 
					c6b311c524 | ||
| 
						 | 
					79e75f66c8 | ||
| 
						 | 
					053e77d6ed | ||
| 
						 | 
					d0a9affb46 | ||
| 
						 | 
					76800042fd | ||
| 
						 | 
					7ab2043c9c | ||
| 
						 | 
					3e703dd1cd | ||
| 
						 | 
					cc10940385 | ||
| 
						 | 
					5121ef2071 | ||
| 
						 | 
					fd20984889 | ||
| 
						 | 
					111ae3695c | ||
| 
						 | 
					0beeff4b3e | ||
| 
						 | 
					64a6f26c5d | ||
| 
						 | 
					a9633f1457 | ||
| 
						 | 
					a20e4c2f96 | ||
| 
						 | 
					d1536018a8 | ||
| 
						 | 
					25af2bce3a | ||
| 
						 | 
					d1580ed990 | ||
| 
						 | 
					eb0d2909a8 | ||
| 
						 | 
					ba72f8a5d1 | ||
| 
						 | 
					c6fd0bb806 | ||
| 
						 | 
					72ac78b8b0 | ||
| 
						 | 
					240b737ebd | ||
| 
						 | 
					27d98b6e25 | ||
| 
						 | 
					5487aea5d8 | ||
| 
						 | 
					9ca4851a00 | ||
| 
						 | 
					1e9daf2a48 | 
							
								
								
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
*.pyc
 | 
			
		||||
*.pyo
 | 
			
		||||
*~
 | 
			
		||||
wine-py2exe/
 | 
			
		||||
py2exe.log
 | 
			
		||||
*.kate-swp
 | 
			
		||||
							
								
								
									
										9
									
								
								.travis.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								.travis.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
language: python
 | 
			
		||||
#specify the python version
 | 
			
		||||
python:
 | 
			
		||||
  - "2.6"
 | 
			
		||||
  - "2.7"
 | 
			
		||||
#command to install the setup
 | 
			
		||||
install:
 | 
			
		||||
# command to run tests
 | 
			
		||||
script: nosetests test --nocapture
 | 
			
		||||
							
								
								
									
										1
									
								
								LATEST_VERSION
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								LATEST_VERSION
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
2012.11.29
 | 
			
		||||
							
								
								
									
										57
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
			
		||||
all: youtube-dl README.md youtube-dl.1 youtube-dl.bash-completion LATEST_VERSION
 | 
			
		||||
# TODO: re-add youtube-dl.exe, and make sure it's 1. safe and 2. doesn't need sudo
 | 
			
		||||
 | 
			
		||||
clean:
 | 
			
		||||
	rm -f youtube-dl youtube-dl.exe youtube-dl.1 LATEST_VERSION
 | 
			
		||||
 | 
			
		||||
PREFIX=/usr/local
 | 
			
		||||
BINDIR=$(PREFIX)/bin
 | 
			
		||||
MANDIR=$(PREFIX)/man
 | 
			
		||||
SYSCONFDIR=/etc
 | 
			
		||||
 | 
			
		||||
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:
 | 
			
		||||
	nosetests2 --nocapture test
 | 
			
		||||
 | 
			
		||||
.PHONY: all clean install test README.md youtube-dl.bash-completion
 | 
			
		||||
# TODO un-phony README.md and youtube-dl.bash_completion by reading from .in files and generating from them
 | 
			
		||||
 | 
			
		||||
youtube-dl: youtube_dl/*.py
 | 
			
		||||
	zip --quiet --junk-paths youtube-dl youtube_dl/*.py
 | 
			
		||||
	echo '#!/usr/bin/env python' > youtube-dl
 | 
			
		||||
	cat youtube-dl.zip >> youtube-dl
 | 
			
		||||
	rm youtube-dl.zip
 | 
			
		||||
	chmod a+x youtube-dl
 | 
			
		||||
 | 
			
		||||
youtube-dl.exe: youtube_dl/*.py
 | 
			
		||||
	bash devscripts/wine-py2exe.sh build_exe.py
 | 
			
		||||
 | 
			
		||||
README.md: youtube_dl/*.py
 | 
			
		||||
	@options=$$(COLUMNS=80 python -m 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,/.*# CONFIGURATION/ d' README.md) && \
 | 
			
		||||
		echo "$${header}" > README.md && \
 | 
			
		||||
		echo >> README.md && \
 | 
			
		||||
		echo '# OPTIONS' >> README.md && \
 | 
			
		||||
		echo "$${options}" >> README.md&& \
 | 
			
		||||
		echo >> README.md && \
 | 
			
		||||
		echo '# CONFIGURATION' >> README.md && \
 | 
			
		||||
		echo "$${footer}" >> README.md
 | 
			
		||||
 | 
			
		||||
youtube-dl.1: README.md
 | 
			
		||||
	pandoc -s -w man README.md -o youtube-dl.1
 | 
			
		||||
 | 
			
		||||
youtube-dl.bash-completion: README.md
 | 
			
		||||
	@options=`egrep -o '(--[a-z-]+) ' README.md | sort -u | xargs echo` && \
 | 
			
		||||
		content=`sed "s/opts=\"[^\"]*\"/opts=\"$${options}\"/g" youtube-dl.bash-completion` && \
 | 
			
		||||
		echo "$${content}" > youtube-dl.bash-completion
 | 
			
		||||
 | 
			
		||||
LATEST_VERSION: youtube_dl/__init__.py
 | 
			
		||||
	python -m youtube_dl --version > LATEST_VERSION
 | 
			
		||||
							
								
								
									
										204
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										204
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,204 @@
 | 
			
		||||
% YOUTUBE-DL(1)
 | 
			
		||||
 | 
			
		||||
# NAME
 | 
			
		||||
youtube-dl
 | 
			
		||||
 | 
			
		||||
# 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 6), and it is not platform specific. It should work in
 | 
			
		||||
your Unix box, in Windows or in Mac OS X. It is released to the public domain,
 | 
			
		||||
which means you can modify it, redistribute it or use it however you like.
 | 
			
		||||
 | 
			
		||||
# 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   download rate limit (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
 | 
			
		||||
    --list-extractors        List all supported extractors and the URLs they
 | 
			
		||||
                             would handle
 | 
			
		||||
 | 
			
		||||
## 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
 | 
			
		||||
 | 
			
		||||
## Filesystem Options:
 | 
			
		||||
    -t, --title              use title in file name
 | 
			
		||||
    --id                     use 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,
 | 
			
		||||
                             %(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 and %% for a literal
 | 
			
		||||
                             percent. Use - to output to stdout.
 | 
			
		||||
    --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
 | 
			
		||||
 | 
			
		||||
## 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
 | 
			
		||||
    -v, --verbose            print various debugging information
 | 
			
		||||
 | 
			
		||||
## Video Format Options:
 | 
			
		||||
    -f, --format FORMAT      video format code
 | 
			
		||||
    --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-srt              write video closed captions to a .srt file
 | 
			
		||||
                             (currently youtube only)
 | 
			
		||||
    --srt-lang LANG          language of the closed captions to download
 | 
			
		||||
                             (optional) use IETF language tags like 'en'
 | 
			
		||||
 | 
			
		||||
## Authentication Options:
 | 
			
		||||
    -u, --username USERNAME  account username
 | 
			
		||||
    -p, --password PASSWORD  account password
 | 
			
		||||
    -n, --netrc              use .netrc authentication data
 | 
			
		||||
 | 
			
		||||
## Post-processing Options:
 | 
			
		||||
    -x, --extract-audio      convert video files to audio-only files (requires
 | 
			
		||||
                             ffmpeg or avconv and ffprobe or avprobe)
 | 
			
		||||
    --audio-format FORMAT    "best", "aac", "vorbis", "mp3", "m4a", 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)
 | 
			
		||||
    -k, --keep-video         keeps the video file on disk after the post-
 | 
			
		||||
                             processing; the video is erased by default
 | 
			
		||||
 | 
			
		||||
# CONFIGURATION
 | 
			
		||||
 | 
			
		||||
You can configure youtube-dl by placing default arguments (such as `--extract-audio --no-mtime` to always extract the audio and not copy the mtime) into `/etc/youtube-dl.conf` and/or `~/.local/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.
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
# FAQ
 | 
			
		||||
 | 
			
		||||
### Can you please put the -b option back?
 | 
			
		||||
 | 
			
		||||
Most people asking this question are not aware that youtube-dl now defaults to downloading the highest available quality as reported by YouTube, which will be 1080p or 720p in some cases, so you no longer need the -b option. For some specific videos, maybe YouTube does not report them to be available in a specific high quality format you''re interested in. In that case, simply request it with the -f option and youtube-dl will try to download it.
 | 
			
		||||
 | 
			
		||||
### I get HTTP error 402 when trying to download a video. What's this?
 | 
			
		||||
 | 
			
		||||
Apparently YouTube requires you to pass a CAPTCHA test if you download too much. We''re [considering to provide a way to let you solve the CAPTCHA](https://github.com/rg3/youtube-dl/issues/154), but at the moment, your best course of action is pointing a webbrowser to the youtube URL, solving the CAPTCHA, and restart youtube-dl.
 | 
			
		||||
 | 
			
		||||
### I have downloaded a video but how can I play it?
 | 
			
		||||
 | 
			
		||||
Once the video is fully downloaded, use any video player, such as [vlc](http://www.videolan.org) or [mplayer](http://www.mplayerhq.hu/).
 | 
			
		||||
 | 
			
		||||
### The links provided by youtube-dl -g are not working anymore
 | 
			
		||||
 | 
			
		||||
The URLs youtube-dl outputs require the downloader to have the correct cookies. Use the `--cookies` option to write the required cookies into a file, and advise your downloader to read cookies from that file. Some sites also require a common user agent to be used, use `--dump-user-agent` to see the one in use by youtube-dl.
 | 
			
		||||
 | 
			
		||||
### ERROR: no fmt_url_map or conn information found in video info
 | 
			
		||||
 | 
			
		||||
youtube has switched to a new video info format in July 2011 which is not supported by old versions of youtube-dl. You can update youtube-dl with `sudo youtube-dl --update`.
 | 
			
		||||
 | 
			
		||||
### ERROR: unable to download video ###
 | 
			
		||||
 | 
			
		||||
youtube requires an additional signature since September 2012 which is not supported by old versions of youtube-dl. You can update youtube-dl with `sudo youtube-dl --update`.
 | 
			
		||||
 | 
			
		||||
### SyntaxError: Non-ASCII character ###
 | 
			
		||||
 | 
			
		||||
The error
 | 
			
		||||
 | 
			
		||||
    File "youtube-dl", line 2
 | 
			
		||||
    SyntaxError: Non-ASCII character '\x93' ...
 | 
			
		||||
 | 
			
		||||
means you're using an outdated version of Python. Please update to Python 2.6 or 2.7.
 | 
			
		||||
 | 
			
		||||
To run youtube-dl under Python 2.5, you'll have to manually check it out like this:
 | 
			
		||||
 | 
			
		||||
	git clone git://github.com/rg3/youtube-dl.git
 | 
			
		||||
	cd youtube-dl
 | 
			
		||||
	python -m youtube_dl --help
 | 
			
		||||
 | 
			
		||||
Please note that Python 2.5 is not supported anymore.
 | 
			
		||||
 | 
			
		||||
### What is this binary file? Where has the code gone?
 | 
			
		||||
 | 
			
		||||
Since June 2012 (#342) youtube-dl is packed as an executable zipfile, simply unzip it (might need renaming to `youtube-dl.zip` first on some systems) or clone the git repository, as laid out above. If you modify the code, you can run it by executing the `__main__.py` file. To recompile the executable, run `make youtube-dl`.
 | 
			
		||||
 | 
			
		||||
### The exe throws a *Runtime error from Visual C++*
 | 
			
		||||
 | 
			
		||||
To run the exe you need to install first the [Microsoft Visual C++ 2008 Redistributable Package](http://www.microsoft.com/en-us/download/details.aspx?id=29).
 | 
			
		||||
 | 
			
		||||
# COPYRIGHT
 | 
			
		||||
 | 
			
		||||
youtube-dl is released into the public domain by the copyright holders.
 | 
			
		||||
 | 
			
		||||
This README file was originally written by Daniel Bolton (<https://github.com/dbbolton>) and is likewise released into the public domain.
 | 
			
		||||
 | 
			
		||||
# BUGS
 | 
			
		||||
 | 
			
		||||
Bugs and suggestions should be reported at: <https://github.com/rg3/youtube-dl/issues>
 | 
			
		||||
 | 
			
		||||
Please include:
 | 
			
		||||
 | 
			
		||||
* Your exact command line, like `youtube-dl -t "http://www.youtube.com/watch?v=uHlDtZ6Oc3s&feature=channel_video_title"`. A common mistake is not to escape the `&`. Putting URLs in quotes should solve this problem.
 | 
			
		||||
* 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).
 | 
			
		||||
							
								
								
									
										48
									
								
								build_exe.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								build_exe.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
			
		||||
from distutils.core import setup
 | 
			
		||||
import py2exe
 | 
			
		||||
import sys, os
 | 
			
		||||
 | 
			
		||||
"""This will create an exe that needs Microsoft Visual C++ 2008 Redistributable Package"""
 | 
			
		||||
 | 
			
		||||
# If run without args, build executables
 | 
			
		||||
if len(sys.argv) == 1:
 | 
			
		||||
    sys.argv.append("py2exe")
 | 
			
		||||
 | 
			
		||||
# os.chdir(os.path.dirname(os.path.abspath(sys.argv[0]))) # conflict with wine-py2exe.sh
 | 
			
		||||
sys.path.append('./youtube_dl')
 | 
			
		||||
 | 
			
		||||
options = {
 | 
			
		||||
    "bundle_files": 1,
 | 
			
		||||
    "compressed": 1,
 | 
			
		||||
    "optimize": 2,
 | 
			
		||||
    "dist_dir": '.',
 | 
			
		||||
    "dll_excludes": ['w9xpopen.exe']
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
console = [{
 | 
			
		||||
    "script":"./youtube_dl/__main__.py",
 | 
			
		||||
    "dest_base": "youtube-dl",
 | 
			
		||||
}]
 | 
			
		||||
 | 
			
		||||
init_file = open('./youtube_dl/__init__.py')
 | 
			
		||||
for line in init_file.readlines():
 | 
			
		||||
    if line.startswith('__version__'):
 | 
			
		||||
        version = line[11:].strip(" ='\n")
 | 
			
		||||
        break
 | 
			
		||||
else:
 | 
			
		||||
    version = ''
 | 
			
		||||
 | 
			
		||||
setup(name='youtube-dl',
 | 
			
		||||
      version=version,
 | 
			
		||||
      description='Small command-line program to download videos from YouTube.com and other video sites',
 | 
			
		||||
      url='https://github.com/rg3/youtube-dl',
 | 
			
		||||
      packages=['youtube_dl'],
 | 
			
		||||
      
 | 
			
		||||
      console = console,
 | 
			
		||||
      options = {"py2exe": options},
 | 
			
		||||
      zipfile = None,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
import shutil
 | 
			
		||||
shutil.rmtree("build")
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										
											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.
										
									
								
							
							
								
								
									
										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
 | 
			
		||||
							
								
								
									
										11
									
								
								devscripts/release.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										11
									
								
								devscripts/release.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
#!/bin/sh
 | 
			
		||||
 | 
			
		||||
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`" ]; then echo 'ERROR: the working directory is not clean; commit or stash changes'; exit 1; fi
 | 
			
		||||
sed -i "s/__version__ = '.*'/__version__ = '$version'/" youtube_dl/__init__.py
 | 
			
		||||
make all
 | 
			
		||||
git add -A
 | 
			
		||||
git commit -m "release $version"
 | 
			
		||||
git tag -m "Release $version" "$version"
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
 | 
			
		||||
@@ -1,15 +0,0 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
import hashlib
 | 
			
		||||
import subprocess
 | 
			
		||||
 | 
			
		||||
template = file('index.html.in', 'r').read()
 | 
			
		||||
version = subprocess.Popen(['./youtube-dl', '--version'], stdout=subprocess.PIPE).communicate()[0].strip()
 | 
			
		||||
data = file('youtube-dl', 'rb').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_MD5SUM@', md5sum)
 | 
			
		||||
template = template.replace('@PROGRAM_SHA1SUM@', sha1sum)
 | 
			
		||||
template = template.replace('@PROGRAM_SHA256SUM@', sha256sum)
 | 
			
		||||
file('index.html', 'w').write(template)
 | 
			
		||||
							
								
								
									
										212
									
								
								index.html.in
									
									
									
									
									
								
							
							
						
						
									
										212
									
								
								index.html.in
									
									
									
									
									
								
							@@ -1,212 +0,0 @@
 | 
			
		||||
<!DOCTYPE html 
 | 
			
		||||
     PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 | 
			
		||||
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 | 
			
		||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
 | 
			
		||||
<head>
 | 
			
		||||
	<meta http-equiv="Content-type" content="text/html; charset=UTF-8" />
 | 
			
		||||
	<title>youtube-dl: Download videos from YouTube.com</title>
 | 
			
		||||
	<style type="text/css"><!--
 | 
			
		||||
		body {
 | 
			
		||||
			font-family: sans-serif;
 | 
			
		||||
			font-size: small;
 | 
			
		||||
		}
 | 
			
		||||
		h1 {
 | 
			
		||||
			text-align: center;
 | 
			
		||||
			text-decoration: underline;
 | 
			
		||||
			color: #006699;
 | 
			
		||||
		}
 | 
			
		||||
		h2 {
 | 
			
		||||
			color: #006699;
 | 
			
		||||
		}
 | 
			
		||||
		p {
 | 
			
		||||
			text-align: justify;
 | 
			
		||||
			margin-left: 5%;
 | 
			
		||||
			margin-right: 5%;
 | 
			
		||||
		}
 | 
			
		||||
		ul {
 | 
			
		||||
			margin-left: 5%;
 | 
			
		||||
			margin-right: 5%;
 | 
			
		||||
			list-style-type: square;
 | 
			
		||||
		}
 | 
			
		||||
		li {
 | 
			
		||||
			margin-bottom: 0.5ex;
 | 
			
		||||
		}
 | 
			
		||||
		.smallnote {
 | 
			
		||||
			font-size: x-small;
 | 
			
		||||
			text-align: center;
 | 
			
		||||
		}
 | 
			
		||||
		--></style>
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
<h1>youtube-dl: Download videos from YouTube.com</h1>
 | 
			
		||||
 | 
			
		||||
<p class="smallnote">(and more...)</p>
 | 
			
		||||
 | 
			
		||||
<h2>What is it?</h2>
 | 
			
		||||
 | 
			
		||||
<p><em>youtube-dl</em> is a small command-line program to download videos
 | 
			
		||||
from YouTube.com. It requires the <a href="http://www.python.org/">Python
 | 
			
		||||
interpreter</a>, version 2.4 or later, and it's not platform specific.
 | 
			
		||||
It should work in your Unix box, in Windows or in Mac OS X. The latest version
 | 
			
		||||
is <strong>@PROGRAM_VERSION@</strong>. It's released to the public domain,
 | 
			
		||||
which means you can modify it, redistribute it or use it however you like.</p>
 | 
			
		||||
 | 
			
		||||
<p>I'll try to keep it updated if YouTube.com changes the way you access
 | 
			
		||||
their videos. After all, it's a simple and short program. However, I can't
 | 
			
		||||
guarantee anything. If you detect it stops working, check for new versions
 | 
			
		||||
and/or inform me about the problem, indicating the program version you
 | 
			
		||||
are using. If the program stops working and I can't solve the problem but
 | 
			
		||||
you have a solution, I'd like to know it. If that happens and you feel you
 | 
			
		||||
can maintain the program yourself, tell me. My contact information is
 | 
			
		||||
at <a href="http://freshmeat.net/~rg3/">freshmeat.net</a>.</p>
 | 
			
		||||
 | 
			
		||||
<p>Thanks for all the feedback received so far. I'm glad people find my
 | 
			
		||||
program useful.</p>
 | 
			
		||||
 | 
			
		||||
<h2>Usage instructions</h2>
 | 
			
		||||
 | 
			
		||||
<p>In Windows, once you have installed the Python interpreter, save the
 | 
			
		||||
program with the <em>.py</em> extension and put it somewhere in the PATH.
 | 
			
		||||
Try to follow the
 | 
			
		||||
<a href="http://rg03.wordpress.com/youtube-dl-under-windows-xp/">guide to
 | 
			
		||||
install youtube-dl under Windows XP</a>.</p>
 | 
			
		||||
 | 
			
		||||
<p>In Unix, download it, give it execution permission and copy it to one
 | 
			
		||||
of the PATH directories (typically, <em>/usr/local/bin</em>).</p>
 | 
			
		||||
 | 
			
		||||
<p>After that, you should be able to call it from the command line as
 | 
			
		||||
<em>youtube-dl</em> or <em>youtube-dl.py</em>. I will use <em>youtube-dl</em>
 | 
			
		||||
in the following examples. Usage instructions are easy. Use <em>youtube-dl</em>
 | 
			
		||||
followed by a video URL or identifier. Example: <em>youtube-dl
 | 
			
		||||
"http://www.youtube.com/watch?v=foobar"</em>. The video will be saved
 | 
			
		||||
to the file <em>foobar.flv</em> in that example. As YouTube.com
 | 
			
		||||
videos are in Flash Video format, their extension should be <em>flv</em>.
 | 
			
		||||
In Linux and other unices, video players using a recent version of
 | 
			
		||||
<em>ffmpeg</em> can play them. That includes MPlayer, VLC, etc. Those two
 | 
			
		||||
work under Windows and other platforms, but you could also get a
 | 
			
		||||
specific FLV player of your taste.</p>
 | 
			
		||||
 | 
			
		||||
<p>If you try to run the program and you receive an error message containing the
 | 
			
		||||
keyword <em>SyntaxError</em> near the end, it means your Python interpreter
 | 
			
		||||
is too old.</p>
 | 
			
		||||
 | 
			
		||||
<h2>More usage tips</h2>
 | 
			
		||||
 | 
			
		||||
<ul>
 | 
			
		||||
 | 
			
		||||
<li>You can change the file name of the video using the -o option, like in
 | 
			
		||||
<em>youtube-dl -o vid.flv "http://www.youtube.com/watch?v=foobar"</em>.
 | 
			
		||||
Read the <em>Output template</em> section for more details on this.</li>
 | 
			
		||||
 | 
			
		||||
<li>Some videos require an account to be downloaded, mostly because they're
 | 
			
		||||
flagged as mature content. You can pass the program a username and password
 | 
			
		||||
for a YouTube.com account with the -u and -p options, like <em>youtube-dl
 | 
			
		||||
-u myusername -p mypassword "http://www.youtube.com/watch?v=foobar"</em>.</li>
 | 
			
		||||
 | 
			
		||||
<li>The account data can also be read from the user .netrc file by indicating
 | 
			
		||||
the -n or --netrc option. The machine name is <em>youtube</em> in that
 | 
			
		||||
case.</li>
 | 
			
		||||
 | 
			
		||||
<li>The <em>simulate mode</em> (activated with -s or --simulate) can be used
 | 
			
		||||
to just get the real video URL and use it with a download manager if you
 | 
			
		||||
prefer that option.</li>
 | 
			
		||||
 | 
			
		||||
<li>The <em>quiet mode</em> (activated with -q or --quiet) can be used to
 | 
			
		||||
supress all output messages. This allows, in systems featuring /dev/stdout
 | 
			
		||||
and other similar special files, outputting the video data to standard output
 | 
			
		||||
in order to pipe it to another program without interferences.</li>
 | 
			
		||||
 | 
			
		||||
<li>The program can be told to simply print the final video URL to standard
 | 
			
		||||
output using the -g or --get-url option.</li>
 | 
			
		||||
 | 
			
		||||
<li>In a similar line, the -e or --get-title option tells the program to print
 | 
			
		||||
the video title.</li>
 | 
			
		||||
 | 
			
		||||
<li>The default filename is <em>video_id.flv</em>. But you can also use the
 | 
			
		||||
video title in the filename with the -t or --title option, or preserve the
 | 
			
		||||
literal title in the filename with the -l or --literal option.</li>
 | 
			
		||||
 | 
			
		||||
<li>You can make the program append <em>&fmt=something</em> to the URL
 | 
			
		||||
by using the -f or --format option. This makes it possible to download high
 | 
			
		||||
quality versions of the videos when available.</li>
 | 
			
		||||
 | 
			
		||||
<li><em>youtube-dl</em> can attempt to download the best quality version of
 | 
			
		||||
a video by using the -b or --best-quality option.</li>
 | 
			
		||||
 | 
			
		||||
<li><em>youtube-dl</em> can attempt to download the mobile quality version of
 | 
			
		||||
a video by using the -m or --mobile-version option.</li>
 | 
			
		||||
 | 
			
		||||
<li>Normally, the program will stop on the first error, but you can tell it
 | 
			
		||||
to attempt to download every video with the -i or --ignore-errors option.</li>
 | 
			
		||||
 | 
			
		||||
<li><em>youtube-dl</em> honors the <em>http_proxy</em> environment variable
 | 
			
		||||
if you want to use a proxy. Set it to something like
 | 
			
		||||
<em>http://proxy.example.com:8080</em>, and do not leave the <em>http://</em>
 | 
			
		||||
prefix out.</li>
 | 
			
		||||
 | 
			
		||||
<li>You can get the program version by calling it as <em>youtube-dl
 | 
			
		||||
-v</em> or <em>youtube-dl --version</em>.</li>
 | 
			
		||||
 | 
			
		||||
<li>For usage instructions, use <em>youtube-dl -h</em> or <em>youtube-dl
 | 
			
		||||
--help.</em></li>
 | 
			
		||||
 | 
			
		||||
<li>You can cancel the program at any time pressing Ctrl+C. It may print
 | 
			
		||||
some error lines saying something about <em>KeyboardInterrupt</em>.
 | 
			
		||||
That's ok.</li>
 | 
			
		||||
 | 
			
		||||
</ul>
 | 
			
		||||
 | 
			
		||||
<h2>Download it</h2>
 | 
			
		||||
 | 
			
		||||
<p>Note that if you directly click on these hyperlinks, your web browser will
 | 
			
		||||
most likely display the program contents. It's usually better to
 | 
			
		||||
right-click on it and choose the appropriate option, normally called <em>Save
 | 
			
		||||
Target As</em> or <em>Save Link As</em>, depending on the web browser you
 | 
			
		||||
are using.</p>
 | 
			
		||||
 | 
			
		||||
<p><a href="youtube-dl">@PROGRAM_VERSION@</a></p>
 | 
			
		||||
<ul>
 | 
			
		||||
        <li><strong>MD5</strong>: @PROGRAM_MD5SUM@</li>
 | 
			
		||||
        <li><strong>SHA1</strong>: @PROGRAM_SHA1SUM@</li>
 | 
			
		||||
        <li><strong>SHA256</strong>: @PROGRAM_SHA256SUM@</li>
 | 
			
		||||
</ul>
 | 
			
		||||
 | 
			
		||||
<h2>Output template</h2>
 | 
			
		||||
 | 
			
		||||
<p>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 <em>youtube-dl -o funny_video.flv 'http://some/video'</em>.
 | 
			
		||||
However, it may contain special sequences that will be replaced when
 | 
			
		||||
downloading each video. The special sequences have the format
 | 
			
		||||
<strong>%(NAME)s</strong>. To clarify, that's a percent symbol followed by a
 | 
			
		||||
name in parenthesis, followed by a lowercase S. Allowed names are:</p>
 | 
			
		||||
 | 
			
		||||
<ul>
 | 
			
		||||
<li><em>id</em>: The sequence will be replaced by the video identifier.</li>
 | 
			
		||||
<li><em>url</em>: The sequence will be replaced by the video URL.</li>
 | 
			
		||||
<li><em>uploader</em>: The sequence will be replaced by the nickname of the
 | 
			
		||||
person who uploaded the video.</li>
 | 
			
		||||
<li><em>title</em>: The sequence will be replaced by the literal video
 | 
			
		||||
title.</li>
 | 
			
		||||
<li><em>stitle</em>: The sequence will be replaced by a simplified video
 | 
			
		||||
title.</li>
 | 
			
		||||
<li><em>ext</em>: The sequence will be replaced by the appropriate
 | 
			
		||||
extension.</li>
 | 
			
		||||
</ul>
 | 
			
		||||
 | 
			
		||||
<p>As you may have guessed, the default template is <em>%(id)s.%(ext)s</em>.
 | 
			
		||||
When some command line options are used, it's replaced by other templates like
 | 
			
		||||
<em>%(title)s-%(id)s.%(ext)s</em>. You can specify your own.</p>
 | 
			
		||||
 | 
			
		||||
<h2>Authors</h2>
 | 
			
		||||
 | 
			
		||||
<ul>
 | 
			
		||||
<li>Ricardo Garcia Gonzalez: program core, YouTube.com InfoExtractor,
 | 
			
		||||
metacafe.com InfoExtractor and YouTube playlist InfoExtractor.</li>
 | 
			
		||||
<li>Many other people contributing patches, code, ideas and kind messages. Too
 | 
			
		||||
many to be listed here. You know who you are. Thank you very much.</li>
 | 
			
		||||
</ul>
 | 
			
		||||
 | 
			
		||||
<p class="smallnote">Copyright © 2006-2007 Ricardo Garcia Gonzalez</p>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										1
									
								
								test/parameters.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								test/parameters.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
{"username": null, "listformats": null, "skip_download": false, "usenetrc": false, "max_downloads": null, "noprogress": false, "forcethumbnail": false, "forceformat": false, "format_limit": null, "ratelimit": null, "nooverwrites": false, "forceurl": false, "writeinfojson": false, "simulate": false, "playliststart": 1, "continuedl": true, "password": null, "prefer_free_formats": false, "nopart": false, "retries": 10, "updatetime": true, "consoletitle": false, "verbose": true, "forcefilename": false, "ignoreerrors": false, "logtostderr": false, "format": null, "subtitleslang": null, "quiet": false, "outtmpl": "%(id)s.%(ext)s", "rejecttitle": null, "playlistend": -1, "writedescription": false, "forcetitle": false, "forcedescription": false, "writesubtitles": false, "matchtitle": null}
 | 
			
		||||
							
								
								
									
										198
									
								
								test/test_download.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								test/test_download.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,198 @@
 | 
			
		||||
#!/usr/bin/env python2
 | 
			
		||||
import unittest
 | 
			
		||||
import hashlib
 | 
			
		||||
import os
 | 
			
		||||
import json
 | 
			
		||||
 | 
			
		||||
from youtube_dl.FileDownloader import FileDownloader
 | 
			
		||||
from youtube_dl.InfoExtractors  import YoutubeIE, DailymotionIE
 | 
			
		||||
from youtube_dl.InfoExtractors import  MetacafeIE, BlipTVIE
 | 
			
		||||
from youtube_dl.InfoExtractors import  XVideosIE, VimeoIE
 | 
			
		||||
from youtube_dl.InfoExtractors import  SoundcloudIE, StanfordOpenClassroomIE
 | 
			
		||||
from youtube_dl.InfoExtractors import  CollegeHumorIE, XNXXIE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DownloadTest(unittest.TestCase):
 | 
			
		||||
	PARAMETERS_FILE = "test/parameters.json"
 | 
			
		||||
	#calculated with md5sum:
 | 
			
		||||
	#md5sum (GNU coreutils) 8.19
 | 
			
		||||
 | 
			
		||||
	YOUTUBE_SIZE = 1993883
 | 
			
		||||
	YOUTUBE_URL = "http://www.youtube.com/watch?v=BaW_jenozKc"
 | 
			
		||||
	YOUTUBE_FILE = "BaW_jenozKc.mp4"
 | 
			
		||||
 | 
			
		||||
	DAILYMOTION_MD5 = "d363a50e9eb4f22ce90d08d15695bb47"
 | 
			
		||||
	DAILYMOTION_URL = "http://www.dailymotion.com/video/x33vw9_tutoriel-de-youtubeur-dl-des-video_tech"
 | 
			
		||||
	DAILYMOTION_FILE = "x33vw9.mp4"
 | 
			
		||||
 | 
			
		||||
	METACAFE_SIZE = 5754305
 | 
			
		||||
	METACAFE_URL = "http://www.metacafe.com/watch/yt-_aUehQsCQtM/the_electric_company_short_i_pbs_kids_go/"
 | 
			
		||||
	METACAFE_FILE = "_aUehQsCQtM.flv"
 | 
			
		||||
 | 
			
		||||
	BLIP_MD5 = "93c24d2f4e0782af13b8a7606ea97ba7"
 | 
			
		||||
	BLIP_URL = "http://blip.tv/cbr/cbr-exclusive-gotham-city-imposters-bats-vs-jokerz-short-3-5796352"
 | 
			
		||||
	BLIP_FILE = "5779306.m4v"
 | 
			
		||||
 | 
			
		||||
	XVIDEO_MD5 = "1ab4dedc01f771cb2a65e91caa801aaf"
 | 
			
		||||
	XVIDEO_URL = "http://www.xvideos.com/video939581/funny_porns_by_s_-1"
 | 
			
		||||
	XVIDEO_FILE = "939581.flv"
 | 
			
		||||
 | 
			
		||||
	VIMEO_MD5 = "1ab4dedc01f771cb2a65e91caa801aaf"
 | 
			
		||||
	VIMEO_URL = "http://vimeo.com/14160053"
 | 
			
		||||
	VIMEO_FILE = ""
 | 
			
		||||
 | 
			
		||||
	VIMEO2_MD5 = ""
 | 
			
		||||
	VIMEO2_URL = "http://player.vimeo.com/video/47019590"
 | 
			
		||||
	VIMEO2_FILE = ""
 | 
			
		||||
 | 
			
		||||
	SOUNDCLOUD_MD5 = "ce3775768ebb6432fa8495d446a078ed"
 | 
			
		||||
	SOUNDCLOUD_URL = "http://soundcloud.com/ethmusic/lostin-powers-she-so-heavy"
 | 
			
		||||
	SOUNDCLOUD_FILE = "n6FLbx6ZzMiu.mp3"
 | 
			
		||||
 | 
			
		||||
	STANDFORD_MD5 = "22c8206291368c4e2c9c1a307f0ea0f4"
 | 
			
		||||
	STANDFORD_URL = "http://openclassroom.stanford.edu/MainFolder/VideoPage.php?course=PracticalUnix&video=intro-environment&speed=100"
 | 
			
		||||
	STANDFORD_FILE = "PracticalUnix_intro-environment.mp4"
 | 
			
		||||
 | 
			
		||||
	COLLEGEHUMOR_MD5 = ""
 | 
			
		||||
	COLLEGEHUMOR_URL = "http://www.collegehumor.com/video/6830834/mitt-romney-style-gangnam-style-parody"
 | 
			
		||||
	COLLEGEHUMOR_FILE = ""
 | 
			
		||||
 | 
			
		||||
	XNXX_MD5 = "5f0469c8d1dfd1bc38c8e6deb5e0a21d"
 | 
			
		||||
	XNXX_URL = "http://video.xnxx.com/video1135332/lida_naked_funny_actress_5_"
 | 
			
		||||
	XNXX_FILE = "1135332.flv"
 | 
			
		||||
 | 
			
		||||
	def test_youtube(self):
 | 
			
		||||
		#let's download a file from youtube
 | 
			
		||||
		with open(DownloadTest.PARAMETERS_FILE) as f:
 | 
			
		||||
			fd = FileDownloader(json.load(f))
 | 
			
		||||
		fd.add_info_extractor(YoutubeIE())
 | 
			
		||||
		fd.download([DownloadTest.YOUTUBE_URL])
 | 
			
		||||
		self.assertTrue(os.path.exists(DownloadTest.YOUTUBE_FILE))
 | 
			
		||||
		self.assertEqual(os.path.getsize(DownloadTest.YOUTUBE_FILE), DownloadTest.YOUTUBE_SIZE)
 | 
			
		||||
 | 
			
		||||
	def test_dailymotion(self):
 | 
			
		||||
		with open(DownloadTest.PARAMETERS_FILE) as f:
 | 
			
		||||
			fd = FileDownloader(json.load(f))
 | 
			
		||||
		fd.add_info_extractor(DailymotionIE())
 | 
			
		||||
		fd.download([DownloadTest.DAILYMOTION_URL])
 | 
			
		||||
		self.assertTrue(os.path.exists(DownloadTest.DAILYMOTION_FILE))
 | 
			
		||||
		md5_down_file = md5_for_file(DownloadTest.DAILYMOTION_FILE)
 | 
			
		||||
		self.assertEqual(md5_down_file, DownloadTest.DAILYMOTION_MD5)
 | 
			
		||||
 | 
			
		||||
	def test_metacafe(self):
 | 
			
		||||
		#this emulate a skip,to be 2.6 compatible
 | 
			
		||||
		with open(DownloadTest.PARAMETERS_FILE) as f:
 | 
			
		||||
			fd = FileDownloader(json.load(f))
 | 
			
		||||
		fd.add_info_extractor(MetacafeIE())
 | 
			
		||||
		fd.add_info_extractor(YoutubeIE())
 | 
			
		||||
		fd.download([DownloadTest.METACAFE_URL])
 | 
			
		||||
		self.assertTrue(os.path.exists(DownloadTest.METACAFE_FILE))
 | 
			
		||||
		self.assertEqual(os.path.getsize(DownloadTest.METACAFE_FILE), DownloadTest.METACAFE_SIZE)
 | 
			
		||||
 | 
			
		||||
	def test_blip(self):
 | 
			
		||||
		with open(DownloadTest.PARAMETERS_FILE) as f:
 | 
			
		||||
			fd = FileDownloader(json.load(f))
 | 
			
		||||
		fd.add_info_extractor(BlipTVIE())
 | 
			
		||||
		fd.download([DownloadTest.BLIP_URL])
 | 
			
		||||
		self.assertTrue(os.path.exists(DownloadTest.BLIP_FILE))
 | 
			
		||||
		md5_down_file = md5_for_file(DownloadTest.BLIP_FILE)
 | 
			
		||||
		self.assertEqual(md5_down_file, DownloadTest.BLIP_MD5)
 | 
			
		||||
 | 
			
		||||
	def test_xvideo(self):
 | 
			
		||||
		with open(DownloadTest.PARAMETERS_FILE) as f:
 | 
			
		||||
			fd = FileDownloader(json.load(f))
 | 
			
		||||
		fd.add_info_extractor(XVideosIE())
 | 
			
		||||
		fd.download([DownloadTest.XVIDEO_URL])
 | 
			
		||||
		self.assertTrue(os.path.exists(DownloadTest.XVIDEO_FILE))
 | 
			
		||||
		md5_down_file = md5_for_file(DownloadTest.XVIDEO_FILE)
 | 
			
		||||
		self.assertEqual(md5_down_file, DownloadTest.XVIDEO_MD5)
 | 
			
		||||
 | 
			
		||||
	def test_vimeo(self):
 | 
			
		||||
		#skipped for the moment produce an error
 | 
			
		||||
		return
 | 
			
		||||
		with open(DownloadTest.PARAMETERS_FILE) as f:
 | 
			
		||||
			fd = FileDownloader(json.load(f))
 | 
			
		||||
		fd.add_info_extractor(VimeoIE())
 | 
			
		||||
		fd.download([DownloadTest.VIMEO_URL])
 | 
			
		||||
		self.assertTrue(os.path.exists(DownloadTest.VIMEO_FILE))
 | 
			
		||||
		md5_down_file = md5_for_file(DownloadTest.VIMEO_FILE)
 | 
			
		||||
		self.assertEqual(md5_down_file, DownloadTest.VIMEO_MD5)
 | 
			
		||||
 | 
			
		||||
	def test_vimeo2(self):
 | 
			
		||||
		#skipped for the moment produce an error
 | 
			
		||||
		return
 | 
			
		||||
		with open(DownloadTest.PARAMETERS_FILE) as f:
 | 
			
		||||
			fd = FileDownloader(json.load(f))
 | 
			
		||||
		fd.add_info_extractor(VimeoIE())
 | 
			
		||||
		fd.download([DownloadTest.VIMEO2_URL])
 | 
			
		||||
		self.assertTrue(os.path.exists(DownloadTest.VIMEO2_FILE))
 | 
			
		||||
		md5_down_file = md5_for_file(DownloadTest.VIMEO2_FILE)
 | 
			
		||||
		self.assertEqual(md5_down_file, DownloadTest.VIMEO2_MD5)
 | 
			
		||||
 | 
			
		||||
	def test_soundcloud(self):
 | 
			
		||||
		with open(DownloadTest.PARAMETERS_FILE) as f:
 | 
			
		||||
			fd = FileDownloader(json.load(f))
 | 
			
		||||
		fd.add_info_extractor(SoundcloudIE())
 | 
			
		||||
		fd.download([DownloadTest.SOUNDCLOUD_URL])
 | 
			
		||||
		self.assertTrue(os.path.exists(DownloadTest.SOUNDCLOUD_FILE))
 | 
			
		||||
		md5_down_file = md5_for_file(DownloadTest.SOUNDCLOUD_FILE)
 | 
			
		||||
		self.assertEqual(md5_down_file, DownloadTest.SOUNDCLOUD_MD5)
 | 
			
		||||
 | 
			
		||||
	def test_standford(self):
 | 
			
		||||
		with open(DownloadTest.PARAMETERS_FILE) as f:
 | 
			
		||||
			fd = FileDownloader(json.load(f))
 | 
			
		||||
		fd.add_info_extractor(StanfordOpenClassroomIE())
 | 
			
		||||
		fd.download([DownloadTest.STANDFORD_URL])
 | 
			
		||||
		self.assertTrue(os.path.exists(DownloadTest.STANDFORD_FILE))
 | 
			
		||||
		md5_down_file = md5_for_file(DownloadTest.STANDFORD_FILE)
 | 
			
		||||
		self.assertEqual(md5_down_file, DownloadTest.STANDFORD_MD5)
 | 
			
		||||
 | 
			
		||||
	def test_collegehumor(self):
 | 
			
		||||
		with open(DownloadTest.PARAMETERS_FILE) as f:
 | 
			
		||||
			fd = FileDownloader(json.load(f))
 | 
			
		||||
		fd.add_info_extractor(CollegeHumorIE())
 | 
			
		||||
		fd.download([DownloadTest.COLLEGEHUMOR_URL])
 | 
			
		||||
		self.assertTrue(os.path.exists(DownloadTest.COLLEGEHUMOR_FILE))
 | 
			
		||||
		md5_down_file = md5_for_file(DownloadTest.COLLEGEHUMOR_FILE)
 | 
			
		||||
		self.assertEqual(md5_down_file, DownloadTest.COLLEGEHUMOR_MD5)
 | 
			
		||||
 | 
			
		||||
	def test_xnxx(self):
 | 
			
		||||
		with open(DownloadTest.PARAMETERS_FILE) as f:
 | 
			
		||||
			fd = FileDownloader(json.load(f))
 | 
			
		||||
		fd.add_info_extractor(XNXXIE())
 | 
			
		||||
		fd.download([DownloadTest.XNXX_URL])
 | 
			
		||||
		self.assertTrue(os.path.exists(DownloadTest.XNXX_FILE))
 | 
			
		||||
		md5_down_file = md5_for_file(DownloadTest.XNXX_FILE)
 | 
			
		||||
		self.assertEqual(md5_down_file, DownloadTest.XNXX_MD5)
 | 
			
		||||
 | 
			
		||||
	def tearDown(self):
 | 
			
		||||
		if os.path.exists(DownloadTest.YOUTUBE_FILE):
 | 
			
		||||
			os.remove(DownloadTest.YOUTUBE_FILE)
 | 
			
		||||
		if os.path.exists(DownloadTest.DAILYMOTION_FILE):
 | 
			
		||||
			os.remove(DownloadTest.DAILYMOTION_FILE)
 | 
			
		||||
		if os.path.exists(DownloadTest.METACAFE_FILE):
 | 
			
		||||
			os.remove(DownloadTest.METACAFE_FILE)
 | 
			
		||||
		if os.path.exists(DownloadTest.BLIP_FILE):
 | 
			
		||||
			os.remove(DownloadTest.BLIP_FILE)
 | 
			
		||||
		if os.path.exists(DownloadTest.XVIDEO_FILE):
 | 
			
		||||
			os.remove(DownloadTest.XVIDEO_FILE)
 | 
			
		||||
		if os.path.exists(DownloadTest.VIMEO_FILE):
 | 
			
		||||
			os.remove(DownloadTest.VIMEO_FILE)
 | 
			
		||||
		if os.path.exists(DownloadTest.SOUNDCLOUD_FILE):
 | 
			
		||||
			os.remove(DownloadTest.SOUNDCLOUD_FILE)
 | 
			
		||||
		if os.path.exists(DownloadTest.STANDFORD_FILE):
 | 
			
		||||
			os.remove(DownloadTest.STANDFORD_FILE)
 | 
			
		||||
		if os.path.exists(DownloadTest.COLLEGEHUMOR_FILE):
 | 
			
		||||
			os.remove(DownloadTest.COLLEGEHUMOR_FILE)
 | 
			
		||||
		if os.path.exists(DownloadTest.XNXX_FILE):
 | 
			
		||||
			os.remove(DownloadTest.XNXX_FILE)
 | 
			
		||||
 | 
			
		||||
def md5_for_file(filename, block_size=2**20):
 | 
			
		||||
	with open(filename) as f:
 | 
			
		||||
		md5 = hashlib.md5()
 | 
			
		||||
		while True:
 | 
			
		||||
			data = f.read(block_size)
 | 
			
		||||
			if not data:
 | 
			
		||||
				break
 | 
			
		||||
			md5.update(data)
 | 
			
		||||
			return md5.hexdigest()
 | 
			
		||||
							
								
								
									
										79
									
								
								test/test_utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								test/test_utils.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,79 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
# Various small unit tests
 | 
			
		||||
 | 
			
		||||
import unittest
 | 
			
		||||
 | 
			
		||||
#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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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(u'abc'), u'abc')
 | 
			
		||||
		self.assertEqual(sanitize_filename(u'abc_d-e'), u'abc_d-e')
 | 
			
		||||
 | 
			
		||||
		self.assertEqual(sanitize_filename(u'123'), u'123')
 | 
			
		||||
 | 
			
		||||
		self.assertEqual(u'abc_de', sanitize_filename(u'abc/de'))
 | 
			
		||||
		self.assertFalse(u'/' in sanitize_filename(u'abc/de///'))
 | 
			
		||||
 | 
			
		||||
		self.assertEqual(u'abc_de', sanitize_filename(u'abc/<>\\*|de'))
 | 
			
		||||
		self.assertEqual(u'xxx', sanitize_filename(u'xxx/<>\\*|'))
 | 
			
		||||
		self.assertEqual(u'yes no', sanitize_filename(u'yes? no'))
 | 
			
		||||
		self.assertEqual(u'this - that', sanitize_filename(u'this: that'))
 | 
			
		||||
 | 
			
		||||
		self.assertEqual(sanitize_filename(u'AT&T'), u'AT&T')
 | 
			
		||||
		self.assertEqual(sanitize_filename(u'ä'), u'ä')
 | 
			
		||||
		self.assertEqual(sanitize_filename(u'кириллица'), u'кириллица')
 | 
			
		||||
 | 
			
		||||
		forbidden = u'"\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(u'abc', restricted=True), u'abc')
 | 
			
		||||
		self.assertEqual(sanitize_filename(u'abc_d-e', restricted=True), u'abc_d-e')
 | 
			
		||||
 | 
			
		||||
		self.assertEqual(sanitize_filename(u'123', restricted=True), u'123')
 | 
			
		||||
 | 
			
		||||
		self.assertEqual(u'abc_de', sanitize_filename(u'abc/de', restricted=True))
 | 
			
		||||
		self.assertFalse(u'/' in sanitize_filename(u'abc/de///', restricted=True))
 | 
			
		||||
 | 
			
		||||
		self.assertEqual(u'abc_de', sanitize_filename(u'abc/<>\\*|de', restricted=True))
 | 
			
		||||
		self.assertEqual(u'xxx', sanitize_filename(u'xxx/<>\\*|', restricted=True))
 | 
			
		||||
		self.assertEqual(u'yes_no', sanitize_filename(u'yes? no', restricted=True))
 | 
			
		||||
		self.assertEqual(u'this_-_that', sanitize_filename(u'this: that', restricted=True))
 | 
			
		||||
 | 
			
		||||
		self.assertEqual(sanitize_filename(u'aäb中国的c', restricted=True), u'a_b_c')
 | 
			
		||||
		self.assertTrue(sanitize_filename(u'ö', restricted=True) != u'') # No empty filename
 | 
			
		||||
 | 
			
		||||
		forbidden = u'"\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(u'大声带 - Song', restricted=True), u'Song')
 | 
			
		||||
		self.assertEqual(sanitize_filename(u'总统: Speech', restricted=True), u'Speech')
 | 
			
		||||
		# .. but make sure the file name is never empty
 | 
			
		||||
		self.assertTrue(sanitize_filename(u'-', restricted=True) != u'')
 | 
			
		||||
		self.assertTrue(sanitize_filename(u':', restricted=True) != u'')
 | 
			
		||||
 | 
			
		||||
	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(u"%20;"), u"%20;")
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								youtube-dl
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								youtube-dl
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										306
									
								
								youtube-dl.1
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										306
									
								
								youtube-dl.1
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,306 @@
 | 
			
		||||
.TH YOUTUBE-DL 1 "" 
 | 
			
		||||
.SH NAME
 | 
			
		||||
.PP
 | 
			
		||||
youtube-dl
 | 
			
		||||
.SH SYNOPSIS
 | 
			
		||||
.PP
 | 
			
		||||
\f[B]youtube-dl\f[] [OPTIONS] URL [URL...]
 | 
			
		||||
.SH DESCRIPTION
 | 
			
		||||
.PP
 | 
			
		||||
\f[B]youtube-dl\f[] 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 6),
 | 
			
		||||
and it is not platform specific.
 | 
			
		||||
It should work in your Unix box, in Windows or in Mac OS X.
 | 
			
		||||
It is released to the public domain, which means you can modify it,
 | 
			
		||||
redistribute it or use it however you like.
 | 
			
		||||
.SH OPTIONS
 | 
			
		||||
.IP
 | 
			
		||||
.nf
 | 
			
		||||
\f[C]
 | 
			
		||||
-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\ \ \ download\ rate\ limit\ (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
 | 
			
		||||
--list-extractors\ \ \ \ \ \ \ \ List\ all\ supported\ extractors\ and\ the\ URLs\ they
 | 
			
		||||
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ would\ handle
 | 
			
		||||
\f[]
 | 
			
		||||
.fi
 | 
			
		||||
.SS Video Selection:
 | 
			
		||||
.IP
 | 
			
		||||
.nf
 | 
			
		||||
\f[C]
 | 
			
		||||
--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
 | 
			
		||||
\f[]
 | 
			
		||||
.fi
 | 
			
		||||
.SS Filesystem Options:
 | 
			
		||||
.IP
 | 
			
		||||
.nf
 | 
			
		||||
\f[C]
 | 
			
		||||
-t,\ --title\ \ \ \ \ \ \ \ \ \ \ \ \ \ use\ title\ in\ file\ name
 | 
			
		||||
--id\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ use\ 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,
 | 
			
		||||
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ %(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\ and\ %%\ for\ a\ literal
 | 
			
		||||
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ percent.\ Use\ -\ to\ output\ to\ stdout.
 | 
			
		||||
--restrict-filenames\ \ \ \ \ Restrict\ filenames\ to\ only\ ASCII\ characters,\ and
 | 
			
		||||
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ avoid\ "&"\ and\ spaces\ in\ filenames
 | 
			
		||||
-a,\ --batch-file\ FILE\ \ \ \ file\ containing\ URLs\ to\ download\ (\[aq]-\[aq]\ 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
 | 
			
		||||
\f[]
 | 
			
		||||
.fi
 | 
			
		||||
.SS Verbosity / Simulation Options:
 | 
			
		||||
.IP
 | 
			
		||||
.nf
 | 
			
		||||
\f[C]
 | 
			
		||||
-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
 | 
			
		||||
-v,\ --verbose\ \ \ \ \ \ \ \ \ \ \ \ print\ various\ debugging\ information
 | 
			
		||||
\f[]
 | 
			
		||||
.fi
 | 
			
		||||
.SS Video Format Options:
 | 
			
		||||
.IP
 | 
			
		||||
.nf
 | 
			
		||||
\f[C]
 | 
			
		||||
-f,\ --format\ FORMAT\ \ \ \ \ \ video\ format\ code
 | 
			
		||||
--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-srt\ \ \ \ \ \ \ \ \ \ \ \ \ \ write\ video\ closed\ captions\ to\ a\ .srt\ file
 | 
			
		||||
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ (currently\ youtube\ only)
 | 
			
		||||
--srt-lang\ LANG\ \ \ \ \ \ \ \ \ \ language\ of\ the\ closed\ captions\ to\ download
 | 
			
		||||
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ (optional)\ use\ IETF\ language\ tags\ like\ \[aq]en\[aq]
 | 
			
		||||
\f[]
 | 
			
		||||
.fi
 | 
			
		||||
.SS Authentication Options:
 | 
			
		||||
.IP
 | 
			
		||||
.nf
 | 
			
		||||
\f[C]
 | 
			
		||||
-u,\ --username\ USERNAME\ \ account\ username
 | 
			
		||||
-p,\ --password\ PASSWORD\ \ account\ password
 | 
			
		||||
-n,\ --netrc\ \ \ \ \ \ \ \ \ \ \ \ \ \ use\ .netrc\ authentication\ data
 | 
			
		||||
\f[]
 | 
			
		||||
.fi
 | 
			
		||||
.SS Post-processing Options:
 | 
			
		||||
.IP
 | 
			
		||||
.nf
 | 
			
		||||
\f[C]
 | 
			
		||||
-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",\ 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)
 | 
			
		||||
-k,\ --keep-video\ \ \ \ \ \ \ \ \ keeps\ the\ video\ file\ on\ disk\ after\ the\ post-
 | 
			
		||||
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ processing;\ the\ video\ is\ erased\ by\ default
 | 
			
		||||
\f[]
 | 
			
		||||
.fi
 | 
			
		||||
.SH CONFIGURATION
 | 
			
		||||
.PP
 | 
			
		||||
You can configure youtube-dl by placing default arguments (such as
 | 
			
		||||
\f[C]--extract-audio\ --no-mtime\f[] to always extract the audio and not
 | 
			
		||||
copy the mtime) into \f[C]/etc/youtube-dl.conf\f[] and/or
 | 
			
		||||
\f[C]~/.local/config/youtube-dl.conf\f[].
 | 
			
		||||
.SH OUTPUT TEMPLATE
 | 
			
		||||
.PP
 | 
			
		||||
The \f[C]-o\f[] 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
 | 
			
		||||
\f[C]youtube-dl\ -o\ funny_video.flv\ "http://some/video"\f[].
 | 
			
		||||
However, it may contain special sequences that will be replaced when
 | 
			
		||||
downloading each video.
 | 
			
		||||
The special sequences have the format \f[C]%(NAME)s\f[].
 | 
			
		||||
To clarify, that is a percent symbol followed by a name in parenthesis,
 | 
			
		||||
followed by a lowercase S.
 | 
			
		||||
Allowed names are:
 | 
			
		||||
.IP \[bu] 2
 | 
			
		||||
\f[C]id\f[]: The sequence will be replaced by the video identifier.
 | 
			
		||||
.IP \[bu] 2
 | 
			
		||||
\f[C]url\f[]: The sequence will be replaced by the video URL.
 | 
			
		||||
.IP \[bu] 2
 | 
			
		||||
\f[C]uploader\f[]: The sequence will be replaced by the nickname of the
 | 
			
		||||
person who uploaded the video.
 | 
			
		||||
.IP \[bu] 2
 | 
			
		||||
\f[C]upload_date\f[]: The sequence will be replaced by the upload date
 | 
			
		||||
in YYYYMMDD format.
 | 
			
		||||
.IP \[bu] 2
 | 
			
		||||
\f[C]title\f[]: The sequence will be replaced by the video title.
 | 
			
		||||
.IP \[bu] 2
 | 
			
		||||
\f[C]ext\f[]: The sequence will be replaced by the appropriate extension
 | 
			
		||||
(like flv or mp4).
 | 
			
		||||
.IP \[bu] 2
 | 
			
		||||
\f[C]epoch\f[]: The sequence will be replaced by the Unix epoch when
 | 
			
		||||
creating the file.
 | 
			
		||||
.IP \[bu] 2
 | 
			
		||||
\f[C]autonumber\f[]: The sequence will be replaced by a five-digit
 | 
			
		||||
number that will be increased with each download, starting at zero.
 | 
			
		||||
.PP
 | 
			
		||||
The current default template is \f[C]%(id)s.%(ext)s\f[], but that will
 | 
			
		||||
be switchted to \f[C]%(title)s-%(id)s.%(ext)s\f[] (which can be
 | 
			
		||||
requested with \f[C]-t\f[] at the moment).
 | 
			
		||||
.PP
 | 
			
		||||
In some cases, you don\[aq]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 \f[C]--restrict-filenames\f[] flag to get a
 | 
			
		||||
shorter title:
 | 
			
		||||
.IP
 | 
			
		||||
.nf
 | 
			
		||||
\f[C]
 | 
			
		||||
$\ youtube-dl\ --get-filename\ -o\ "%(title)s.%(ext)s"\ BaW_jenozKc
 | 
			
		||||
youtube-dl\ test\ video\ \[aq]\[aq]_ä↭𝕐.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
 | 
			
		||||
\f[]
 | 
			
		||||
.fi
 | 
			
		||||
.SH FAQ
 | 
			
		||||
.SS Can you please put the -b option back?
 | 
			
		||||
.PP
 | 
			
		||||
Most people asking this question are not aware that youtube-dl now
 | 
			
		||||
defaults to downloading the highest available quality as reported by
 | 
			
		||||
YouTube, which will be 1080p or 720p in some cases, so you no longer
 | 
			
		||||
need the -b option.
 | 
			
		||||
For some specific videos, maybe YouTube does not report them to be
 | 
			
		||||
available in a specific high quality format you\[aq]\[aq]re interested
 | 
			
		||||
in.
 | 
			
		||||
In that case, simply request it with the -f option and youtube-dl will
 | 
			
		||||
try to download it.
 | 
			
		||||
.SS I get HTTP error 402 when trying to download a video. What\[aq]s
 | 
			
		||||
this?
 | 
			
		||||
.PP
 | 
			
		||||
Apparently YouTube requires you to pass a CAPTCHA test if you download
 | 
			
		||||
too much.
 | 
			
		||||
We\[aq]\[aq]re considering to provide a way to let you solve the
 | 
			
		||||
CAPTCHA (https://github.com/rg3/youtube-dl/issues/154), but at the
 | 
			
		||||
moment, your best course of action is pointing a webbrowser to the
 | 
			
		||||
youtube URL, solving the CAPTCHA, and restart youtube-dl.
 | 
			
		||||
.SS I have downloaded a video but how can I play it?
 | 
			
		||||
.PP
 | 
			
		||||
Once the video is fully downloaded, use any video player, such as
 | 
			
		||||
vlc (http://www.videolan.org) or mplayer (http://www.mplayerhq.hu/).
 | 
			
		||||
.SS The links provided by youtube-dl -g are not working anymore
 | 
			
		||||
.PP
 | 
			
		||||
The URLs youtube-dl outputs require the downloader to have the correct
 | 
			
		||||
cookies.
 | 
			
		||||
Use the \f[C]--cookies\f[] 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
 | 
			
		||||
\f[C]--dump-user-agent\f[] to see the one in use by youtube-dl.
 | 
			
		||||
.SS ERROR: no fmt_url_map or conn information found in video info
 | 
			
		||||
.PP
 | 
			
		||||
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 \f[C]sudo\ youtube-dl\ --update\f[].
 | 
			
		||||
.SS ERROR: unable to download video
 | 
			
		||||
.PP
 | 
			
		||||
youtube requires an additional signature since September 2012 which is
 | 
			
		||||
not supported by old versions of youtube-dl.
 | 
			
		||||
You can update youtube-dl with \f[C]sudo\ youtube-dl\ --update\f[].
 | 
			
		||||
.SS SyntaxError: Non-ASCII character
 | 
			
		||||
.PP
 | 
			
		||||
The error
 | 
			
		||||
.IP
 | 
			
		||||
.nf
 | 
			
		||||
\f[C]
 | 
			
		||||
File\ "youtube-dl",\ line\ 2
 | 
			
		||||
SyntaxError:\ Non-ASCII\ character\ \[aq]\\x93\[aq]\ ...
 | 
			
		||||
\f[]
 | 
			
		||||
.fi
 | 
			
		||||
.PP
 | 
			
		||||
means you\[aq]re using an outdated version of Python.
 | 
			
		||||
Please update to Python 2.6 or 2.7.
 | 
			
		||||
.PP
 | 
			
		||||
To run youtube-dl under Python 2.5, you\[aq]ll have to manually check it
 | 
			
		||||
out like this:
 | 
			
		||||
.IP
 | 
			
		||||
.nf
 | 
			
		||||
\f[C]
 | 
			
		||||
git\ clone\ git://github.com/rg3/youtube-dl.git
 | 
			
		||||
cd\ youtube-dl
 | 
			
		||||
python\ -m\ youtube_dl\ --help
 | 
			
		||||
\f[]
 | 
			
		||||
.fi
 | 
			
		||||
.PP
 | 
			
		||||
Please note that Python 2.5 is not supported anymore.
 | 
			
		||||
.SS What is this binary file? Where has the code gone?
 | 
			
		||||
.PP
 | 
			
		||||
Since June 2012 (#342) youtube-dl is packed as an executable zipfile,
 | 
			
		||||
simply unzip it (might need renaming to \f[C]youtube-dl.zip\f[] 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
 | 
			
		||||
\f[C]__main__.py\f[] file.
 | 
			
		||||
To recompile the executable, run \f[C]make\ youtube-dl\f[].
 | 
			
		||||
.SS The exe throws a \f[I]Runtime error from Visual C++\f[]
 | 
			
		||||
.PP
 | 
			
		||||
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).
 | 
			
		||||
.SH COPYRIGHT
 | 
			
		||||
.PP
 | 
			
		||||
youtube-dl is released into the public domain by the copyright holders.
 | 
			
		||||
.PP
 | 
			
		||||
This README file was originally written by Daniel Bolton
 | 
			
		||||
(<https://github.com/dbbolton>) and is likewise released into the public
 | 
			
		||||
domain.
 | 
			
		||||
.SH BUGS
 | 
			
		||||
.PP
 | 
			
		||||
Bugs and suggestions should be reported at:
 | 
			
		||||
<https://github.com/rg3/youtube-dl/issues>
 | 
			
		||||
.PP
 | 
			
		||||
Please include:
 | 
			
		||||
.IP \[bu] 2
 | 
			
		||||
Your exact command line, like
 | 
			
		||||
\f[C]youtube-dl\ -t\ "http://www.youtube.com/watch?v=uHlDtZ6Oc3s&feature=channel_video_title"\f[].
 | 
			
		||||
A common mistake is not to escape the \f[C]&\f[].
 | 
			
		||||
Putting URLs in quotes should solve this problem.
 | 
			
		||||
.IP \[bu] 2
 | 
			
		||||
The output of \f[C]youtube-dl\ --version\f[]
 | 
			
		||||
.IP \[bu] 2
 | 
			
		||||
The output of \f[C]python\ --version\f[]
 | 
			
		||||
.IP \[bu] 2
 | 
			
		||||
The name and version of your Operating System ("Ubuntu 11.04 x64" or
 | 
			
		||||
"Windows 7 x64" is usually enough).
 | 
			
		||||
							
								
								
									
										14
									
								
								youtube-dl.bash-completion
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								youtube-dl.bash-completion
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
__youtube-dl()
 | 
			
		||||
{
 | 
			
		||||
    local cur prev opts
 | 
			
		||||
    COMPREPLY=()
 | 
			
		||||
    cur="${COMP_WORDS[COMP_CWORD]}"
 | 
			
		||||
    opts="--all-formats --audio-format --audio-quality --auto-number --batch-file --buffer-size --console-title --continue --cookies --dump-user-agent --extract-audio --format --get-description --get-filename --get-format --get-thumbnail --get-title --get-url --help --id --ignore-errors --keep-video --list-extractors --list-formats --literal --match-title --max-downloads --max-quality --netrc --no-continue --no-mtime --no-overwrites --no-part --no-progress --no-resize-buffer --output --password --playlist-end --playlist-start --prefer-free-formats --quiet --rate-limit --reject-title --restrict-filenames --retries --simulate --skip-download --srt-lang --title --update --user-agent --username --verbose --version --write-description --write-info-json --write-srt"
 | 
			
		||||
 | 
			
		||||
    if [[ ${cur} == * ]] ; then
 | 
			
		||||
        COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
 | 
			
		||||
        return 0
 | 
			
		||||
    fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
complete -F __youtube-dl youtube-dl
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								youtube-dl.exe
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								youtube-dl.exe
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										704
									
								
								youtube_dl/FileDownloader.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										704
									
								
								youtube_dl/FileDownloader.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,704 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
import httplib
 | 
			
		||||
import math
 | 
			
		||||
import os
 | 
			
		||||
import re
 | 
			
		||||
import socket
 | 
			
		||||
import subprocess
 | 
			
		||||
import sys
 | 
			
		||||
import time
 | 
			
		||||
import urllib2
 | 
			
		||||
 | 
			
		||||
if os.name == 'nt':
 | 
			
		||||
	import ctypes
 | 
			
		||||
 | 
			
		||||
from utils import *
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FileDownloader(object):
 | 
			
		||||
	"""File Downloader class.
 | 
			
		||||
 | 
			
		||||
	File downloader objects are the ones responsible of downloading the
 | 
			
		||||
	actual video file and writing it to disk if the user has requested
 | 
			
		||||
	it, among some other tasks. In most cases there should be one per
 | 
			
		||||
	program. As, given a video URL, the downloader doesn't know how to
 | 
			
		||||
	extract all the needed information, task that InfoExtractors do, it
 | 
			
		||||
	has to pass the URL to one of them.
 | 
			
		||||
 | 
			
		||||
	For this, file downloader objects have a method that allows
 | 
			
		||||
	InfoExtractors to be registered in a given order. When it is passed
 | 
			
		||||
	a URL, the file downloader handles it to the first InfoExtractor it
 | 
			
		||||
	finds that reports being able to handle it. The InfoExtractor extracts
 | 
			
		||||
	all the information about the video or videos the URL refers to, and
 | 
			
		||||
	asks the FileDownloader to process the video information, possibly
 | 
			
		||||
	downloading the video.
 | 
			
		||||
 | 
			
		||||
	File downloaders accept a lot of parameters. In order not to saturate
 | 
			
		||||
	the object constructor with arguments, it receives a dictionary of
 | 
			
		||||
	options instead. These options are available through the params
 | 
			
		||||
	attribute for the InfoExtractors to use. The FileDownloader also
 | 
			
		||||
	registers itself as the downloader in charge for the InfoExtractors
 | 
			
		||||
	that are added to it, so this is a "mutual registration".
 | 
			
		||||
 | 
			
		||||
	Available options:
 | 
			
		||||
 | 
			
		||||
	username:          Username for authentication purposes.
 | 
			
		||||
	password:          Password for authentication purposes.
 | 
			
		||||
	usenetrc:          Use netrc for authentication instead.
 | 
			
		||||
	quiet:             Do not print messages to stdout.
 | 
			
		||||
	forceurl:          Force printing final URL.
 | 
			
		||||
	forcetitle:        Force printing title.
 | 
			
		||||
	forcethumbnail:    Force printing thumbnail URL.
 | 
			
		||||
	forcedescription:  Force printing description.
 | 
			
		||||
	forcefilename:     Force printing final filename.
 | 
			
		||||
	simulate:          Do not download the video files.
 | 
			
		||||
	format:            Video format code.
 | 
			
		||||
	format_limit:      Highest quality format to try.
 | 
			
		||||
	outtmpl:           Template for output names.
 | 
			
		||||
	restrictfilenames: Do not allow "&" and spaces in file names
 | 
			
		||||
	ignoreerrors:      Do not stop on download errors.
 | 
			
		||||
	ratelimit:         Download speed limit, in bytes/sec.
 | 
			
		||||
	nooverwrites:      Prevent overwriting files.
 | 
			
		||||
	retries:           Number of times to retry for HTTP error 5xx
 | 
			
		||||
	buffersize:        Size of download buffer in bytes.
 | 
			
		||||
	noresizebuffer:    Do not automatically resize the download buffer.
 | 
			
		||||
	continuedl:        Try to continue downloads if possible.
 | 
			
		||||
	noprogress:        Do not print the progress bar.
 | 
			
		||||
	playliststart:     Playlist item to start at.
 | 
			
		||||
	playlistend:       Playlist item to end at.
 | 
			
		||||
	matchtitle:        Download only matching titles.
 | 
			
		||||
	rejecttitle:       Reject downloads for matching titles.
 | 
			
		||||
	logtostderr:       Log messages to stderr instead of stdout.
 | 
			
		||||
	consoletitle:      Display progress in console window's titlebar.
 | 
			
		||||
	nopart:            Do not use temporary .part files.
 | 
			
		||||
	updatetime:        Use the Last-modified header to set output file timestamps.
 | 
			
		||||
	writedescription:  Write the video description to a .description file
 | 
			
		||||
	writeinfojson:     Write the video description to a .info.json file
 | 
			
		||||
	writesubtitles:    Write the video subtitles to a .srt file
 | 
			
		||||
	subtitleslang:     Language of the subtitles to download
 | 
			
		||||
	"""
 | 
			
		||||
 | 
			
		||||
	params = None
 | 
			
		||||
	_ies = []
 | 
			
		||||
	_pps = []
 | 
			
		||||
	_download_retcode = None
 | 
			
		||||
	_num_downloads = None
 | 
			
		||||
	_screen_file = None
 | 
			
		||||
 | 
			
		||||
	def __init__(self, params):
 | 
			
		||||
		"""Create a FileDownloader object with the given options."""
 | 
			
		||||
		self._ies = []
 | 
			
		||||
		self._pps = []
 | 
			
		||||
		self._download_retcode = 0
 | 
			
		||||
		self._num_downloads = 0
 | 
			
		||||
		self._screen_file = [sys.stdout, sys.stderr][params.get('logtostderr', False)]
 | 
			
		||||
		self.params = params
 | 
			
		||||
 | 
			
		||||
		if '%(stitle)s' in self.params['outtmpl']:
 | 
			
		||||
			self.to_stderr(u'WARNING: %(stitle)s is deprecated. Use the %(title)s and the --restrict-filenames flag(which also secures %(uploader)s et al) instead.')
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def format_bytes(bytes):
 | 
			
		||||
		if bytes is None:
 | 
			
		||||
			return 'N/A'
 | 
			
		||||
		if type(bytes) is str:
 | 
			
		||||
			bytes = float(bytes)
 | 
			
		||||
		if bytes == 0.0:
 | 
			
		||||
			exponent = 0
 | 
			
		||||
		else:
 | 
			
		||||
			exponent = long(math.log(bytes, 1024.0))
 | 
			
		||||
		suffix = 'bkMGTPEZY'[exponent]
 | 
			
		||||
		converted = float(bytes) / float(1024 ** exponent)
 | 
			
		||||
		return '%.2f%s' % (converted, suffix)
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def calc_percent(byte_counter, data_len):
 | 
			
		||||
		if data_len is None:
 | 
			
		||||
			return '---.-%'
 | 
			
		||||
		return '%6s' % ('%3.1f%%' % (float(byte_counter) / float(data_len) * 100.0))
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def calc_eta(start, now, total, current):
 | 
			
		||||
		if total is None:
 | 
			
		||||
			return '--:--'
 | 
			
		||||
		dif = now - start
 | 
			
		||||
		if current == 0 or dif < 0.001: # One millisecond
 | 
			
		||||
			return '--:--'
 | 
			
		||||
		rate = float(current) / dif
 | 
			
		||||
		eta = long((float(total) - float(current)) / rate)
 | 
			
		||||
		(eta_mins, eta_secs) = divmod(eta, 60)
 | 
			
		||||
		if eta_mins > 99:
 | 
			
		||||
			return '--:--'
 | 
			
		||||
		return '%02d:%02d' % (eta_mins, eta_secs)
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def calc_speed(start, now, bytes):
 | 
			
		||||
		dif = now - start
 | 
			
		||||
		if bytes == 0 or dif < 0.001: # One millisecond
 | 
			
		||||
			return '%10s' % '---b/s'
 | 
			
		||||
		return '%10s' % ('%s/s' % FileDownloader.format_bytes(float(bytes) / dif))
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def best_block_size(elapsed_time, bytes):
 | 
			
		||||
		new_min = max(bytes / 2.0, 1.0)
 | 
			
		||||
		new_max = min(max(bytes * 2.0, 1.0), 4194304) # Do not surpass 4 MB
 | 
			
		||||
		if elapsed_time < 0.001:
 | 
			
		||||
			return int(new_max)
 | 
			
		||||
		rate = bytes / elapsed_time
 | 
			
		||||
		if rate > new_max:
 | 
			
		||||
			return int(new_max)
 | 
			
		||||
		if rate < new_min:
 | 
			
		||||
			return int(new_min)
 | 
			
		||||
		return int(rate)
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def parse_bytes(bytestr):
 | 
			
		||||
		"""Parse a string indicating a byte quantity into an integer."""
 | 
			
		||||
		matchobj = re.match(r'(?i)^(\d+(?:\.\d+)?)([kMGTPEZY]?)$', bytestr)
 | 
			
		||||
		if matchobj is None:
 | 
			
		||||
			return None
 | 
			
		||||
		number = float(matchobj.group(1))
 | 
			
		||||
		multiplier = 1024.0 ** 'bkmgtpezy'.index(matchobj.group(2).lower())
 | 
			
		||||
		return int(round(number * multiplier))
 | 
			
		||||
 | 
			
		||||
	def add_info_extractor(self, ie):
 | 
			
		||||
		"""Add an InfoExtractor object to the end of the list."""
 | 
			
		||||
		self._ies.append(ie)
 | 
			
		||||
		ie.set_downloader(self)
 | 
			
		||||
 | 
			
		||||
	def add_post_processor(self, pp):
 | 
			
		||||
		"""Add a PostProcessor object to the end of the chain."""
 | 
			
		||||
		self._pps.append(pp)
 | 
			
		||||
		pp.set_downloader(self)
 | 
			
		||||
 | 
			
		||||
	def to_screen(self, message, skip_eol=False):
 | 
			
		||||
		"""Print message to stdout if not in quiet mode."""
 | 
			
		||||
		assert type(message) == type(u'')
 | 
			
		||||
		if not self.params.get('quiet', False):
 | 
			
		||||
			terminator = [u'\n', u''][skip_eol]
 | 
			
		||||
			output = message + terminator
 | 
			
		||||
			if 'b' not in self._screen_file.mode or sys.version_info[0] < 3: # Python 2 lies about the mode of sys.stdout/sys.stderr
 | 
			
		||||
				output = output.encode(preferredencoding(), 'ignore')
 | 
			
		||||
			self._screen_file.write(output)
 | 
			
		||||
			self._screen_file.flush()
 | 
			
		||||
 | 
			
		||||
	def to_stderr(self, message):
 | 
			
		||||
		"""Print message to stderr."""
 | 
			
		||||
		assert type(message) == type(u'')
 | 
			
		||||
		sys.stderr.write((message + u'\n').encode(preferredencoding()))
 | 
			
		||||
 | 
			
		||||
	def to_cons_title(self, message):
 | 
			
		||||
		"""Set console/terminal window title to message."""
 | 
			
		||||
		if not self.params.get('consoletitle', False):
 | 
			
		||||
			return
 | 
			
		||||
		if os.name == 'nt' and ctypes.windll.kernel32.GetConsoleWindow():
 | 
			
		||||
			# c_wchar_p() might not be necessary if `message` is
 | 
			
		||||
			# already of type unicode()
 | 
			
		||||
			ctypes.windll.kernel32.SetConsoleTitleW(ctypes.c_wchar_p(message))
 | 
			
		||||
		elif 'TERM' in os.environ:
 | 
			
		||||
			sys.stderr.write('\033]0;%s\007' % message.encode(preferredencoding()))
 | 
			
		||||
 | 
			
		||||
	def fixed_template(self):
 | 
			
		||||
		"""Checks if the output template is fixed."""
 | 
			
		||||
		return (re.search(ur'(?u)%\(.+?\)s', self.params['outtmpl']) is None)
 | 
			
		||||
 | 
			
		||||
	def trouble(self, message=None):
 | 
			
		||||
		"""Determine action to take when a download problem appears.
 | 
			
		||||
 | 
			
		||||
		Depending on if the downloader has been configured to ignore
 | 
			
		||||
		download errors or not, this method may throw an exception or
 | 
			
		||||
		not when errors are found, after printing the message.
 | 
			
		||||
		"""
 | 
			
		||||
		if message is not None:
 | 
			
		||||
			self.to_stderr(message)
 | 
			
		||||
		if not self.params.get('ignoreerrors', False):
 | 
			
		||||
			raise DownloadError(message)
 | 
			
		||||
		self._download_retcode = 1
 | 
			
		||||
 | 
			
		||||
	def slow_down(self, start_time, byte_counter):
 | 
			
		||||
		"""Sleep if the download speed is over the rate limit."""
 | 
			
		||||
		rate_limit = self.params.get('ratelimit', None)
 | 
			
		||||
		if rate_limit is None or byte_counter == 0:
 | 
			
		||||
			return
 | 
			
		||||
		now = time.time()
 | 
			
		||||
		elapsed = now - start_time
 | 
			
		||||
		if elapsed <= 0.0:
 | 
			
		||||
			return
 | 
			
		||||
		speed = float(byte_counter) / elapsed
 | 
			
		||||
		if speed > rate_limit:
 | 
			
		||||
			time.sleep((byte_counter - rate_limit * (now - start_time)) / rate_limit)
 | 
			
		||||
 | 
			
		||||
	def temp_name(self, filename):
 | 
			
		||||
		"""Returns a temporary filename for the given filename."""
 | 
			
		||||
		if self.params.get('nopart', False) or filename == u'-' or \
 | 
			
		||||
				(os.path.exists(encodeFilename(filename)) and not os.path.isfile(encodeFilename(filename))):
 | 
			
		||||
			return filename
 | 
			
		||||
		return filename + u'.part'
 | 
			
		||||
 | 
			
		||||
	def undo_temp_name(self, filename):
 | 
			
		||||
		if filename.endswith(u'.part'):
 | 
			
		||||
			return filename[:-len(u'.part')]
 | 
			
		||||
		return filename
 | 
			
		||||
 | 
			
		||||
	def try_rename(self, old_filename, new_filename):
 | 
			
		||||
		try:
 | 
			
		||||
			if old_filename == new_filename:
 | 
			
		||||
				return
 | 
			
		||||
			os.rename(encodeFilename(old_filename), encodeFilename(new_filename))
 | 
			
		||||
		except (IOError, OSError), err:
 | 
			
		||||
			self.trouble(u'ERROR: unable to rename file')
 | 
			
		||||
 | 
			
		||||
	def try_utime(self, filename, last_modified_hdr):
 | 
			
		||||
		"""Try to set the last-modified time of the given file."""
 | 
			
		||||
		if last_modified_hdr is None:
 | 
			
		||||
			return
 | 
			
		||||
		if not os.path.isfile(encodeFilename(filename)):
 | 
			
		||||
			return
 | 
			
		||||
		timestr = last_modified_hdr
 | 
			
		||||
		if timestr is None:
 | 
			
		||||
			return
 | 
			
		||||
		filetime = timeconvert(timestr)
 | 
			
		||||
		if filetime is None:
 | 
			
		||||
			return filetime
 | 
			
		||||
		try:
 | 
			
		||||
			os.utime(filename, (time.time(), filetime))
 | 
			
		||||
		except:
 | 
			
		||||
			pass
 | 
			
		||||
		return filetime
 | 
			
		||||
 | 
			
		||||
	def report_writedescription(self, descfn):
 | 
			
		||||
		""" Report that the description file is being written """
 | 
			
		||||
		self.to_screen(u'[info] Writing video description to: ' + descfn)
 | 
			
		||||
 | 
			
		||||
	def report_writesubtitles(self, srtfn):
 | 
			
		||||
		""" Report that the subtitles file is being written """
 | 
			
		||||
		self.to_screen(u'[info] Writing video subtitles to: ' + srtfn)
 | 
			
		||||
 | 
			
		||||
	def report_writeinfojson(self, infofn):
 | 
			
		||||
		""" Report that the metadata file has been written """
 | 
			
		||||
		self.to_screen(u'[info] Video description metadata as JSON to: ' + infofn)
 | 
			
		||||
 | 
			
		||||
	def report_destination(self, filename):
 | 
			
		||||
		"""Report destination filename."""
 | 
			
		||||
		self.to_screen(u'[download] Destination: ' + filename)
 | 
			
		||||
 | 
			
		||||
	def report_progress(self, percent_str, data_len_str, speed_str, eta_str):
 | 
			
		||||
		"""Report download progress."""
 | 
			
		||||
		if self.params.get('noprogress', False):
 | 
			
		||||
			return
 | 
			
		||||
		self.to_screen(u'\r[download] %s of %s at %s ETA %s' %
 | 
			
		||||
				(percent_str, data_len_str, speed_str, eta_str), skip_eol=True)
 | 
			
		||||
		self.to_cons_title(u'youtube-dl - %s of %s at %s ETA %s' %
 | 
			
		||||
				(percent_str.strip(), data_len_str.strip(), speed_str.strip(), eta_str.strip()))
 | 
			
		||||
 | 
			
		||||
	def report_resuming_byte(self, resume_len):
 | 
			
		||||
		"""Report attempt to resume at given byte."""
 | 
			
		||||
		self.to_screen(u'[download] Resuming download at byte %s' % resume_len)
 | 
			
		||||
 | 
			
		||||
	def report_retry(self, count, retries):
 | 
			
		||||
		"""Report retry in case of HTTP error 5xx"""
 | 
			
		||||
		self.to_screen(u'[download] Got server HTTP error. Retrying (attempt %d of %d)...' % (count, retries))
 | 
			
		||||
 | 
			
		||||
	def report_file_already_downloaded(self, file_name):
 | 
			
		||||
		"""Report file has already been fully downloaded."""
 | 
			
		||||
		try:
 | 
			
		||||
			self.to_screen(u'[download] %s has already been downloaded' % file_name)
 | 
			
		||||
		except (UnicodeEncodeError), err:
 | 
			
		||||
			self.to_screen(u'[download] The file has already been downloaded')
 | 
			
		||||
 | 
			
		||||
	def report_unable_to_resume(self):
 | 
			
		||||
		"""Report it was impossible to resume download."""
 | 
			
		||||
		self.to_screen(u'[download] Unable to resume')
 | 
			
		||||
 | 
			
		||||
	def report_finish(self):
 | 
			
		||||
		"""Report download finished."""
 | 
			
		||||
		if self.params.get('noprogress', False):
 | 
			
		||||
			self.to_screen(u'[download] Download completed')
 | 
			
		||||
		else:
 | 
			
		||||
			self.to_screen(u'')
 | 
			
		||||
 | 
			
		||||
	def increment_downloads(self):
 | 
			
		||||
		"""Increment the ordinal that assigns a number to each file."""
 | 
			
		||||
		self._num_downloads += 1
 | 
			
		||||
 | 
			
		||||
	def prepare_filename(self, info_dict):
 | 
			
		||||
		"""Generate the output filename."""
 | 
			
		||||
		try:
 | 
			
		||||
			template_dict = dict(info_dict)
 | 
			
		||||
			template_dict['epoch'] = int(time.time())
 | 
			
		||||
			template_dict['autonumber'] = u'%05d' % self._num_downloads
 | 
			
		||||
 | 
			
		||||
			template_dict = dict((k, sanitize_filename(compat_str(v), self.params.get('restrictfilenames'))) for k,v in template_dict.items())
 | 
			
		||||
			filename = self.params['outtmpl'] % template_dict
 | 
			
		||||
			return filename
 | 
			
		||||
		except (ValueError, KeyError), err:
 | 
			
		||||
			self.trouble(u'ERROR: invalid system charset or erroneous output template')
 | 
			
		||||
			return None
 | 
			
		||||
 | 
			
		||||
	def _match_entry(self, info_dict):
 | 
			
		||||
		""" Returns None iff the file should be downloaded """
 | 
			
		||||
 | 
			
		||||
		title = info_dict['title']
 | 
			
		||||
		matchtitle = self.params.get('matchtitle', False)
 | 
			
		||||
		if matchtitle:
 | 
			
		||||
			matchtitle = matchtitle.decode('utf8')
 | 
			
		||||
			if not re.search(matchtitle, title, re.IGNORECASE):
 | 
			
		||||
				return u'[download] "' + title + '" title did not match pattern "' + matchtitle + '"'
 | 
			
		||||
		rejecttitle = self.params.get('rejecttitle', False)
 | 
			
		||||
		if rejecttitle:
 | 
			
		||||
			rejecttitle = rejecttitle.decode('utf8')
 | 
			
		||||
			if re.search(rejecttitle, title, re.IGNORECASE):
 | 
			
		||||
				return u'"' + title + '" title matched reject pattern "' + rejecttitle + '"'
 | 
			
		||||
		return None
 | 
			
		||||
 | 
			
		||||
	def process_info(self, info_dict):
 | 
			
		||||
		"""Process a single dictionary returned by an InfoExtractor."""
 | 
			
		||||
 | 
			
		||||
		# Keep for backwards compatibility
 | 
			
		||||
		info_dict['stitle'] = info_dict['title']
 | 
			
		||||
 | 
			
		||||
		reason = self._match_entry(info_dict)
 | 
			
		||||
		if reason is not None:
 | 
			
		||||
			self.to_screen(u'[download] ' + reason)
 | 
			
		||||
			return
 | 
			
		||||
 | 
			
		||||
		max_downloads = self.params.get('max_downloads')
 | 
			
		||||
		if max_downloads is not None:
 | 
			
		||||
			if self._num_downloads > int(max_downloads):
 | 
			
		||||
				raise MaxDownloadsReached()
 | 
			
		||||
 | 
			
		||||
		filename = self.prepare_filename(info_dict)
 | 
			
		||||
 | 
			
		||||
		# Forced printings
 | 
			
		||||
		if self.params.get('forcetitle', False):
 | 
			
		||||
			print(info_dict['title'].encode(preferredencoding(), 'xmlcharrefreplace'))
 | 
			
		||||
		if self.params.get('forceurl', False):
 | 
			
		||||
			print(info_dict['url'].encode(preferredencoding(), 'xmlcharrefreplace'))
 | 
			
		||||
		if self.params.get('forcethumbnail', False) and 'thumbnail' in info_dict:
 | 
			
		||||
			print(info_dict['thumbnail'].encode(preferredencoding(), 'xmlcharrefreplace'))
 | 
			
		||||
		if self.params.get('forcedescription', False) and 'description' in info_dict:
 | 
			
		||||
			print(info_dict['description'].encode(preferredencoding(), 'xmlcharrefreplace'))
 | 
			
		||||
		if self.params.get('forcefilename', False) and filename is not None:
 | 
			
		||||
			print(filename.encode(preferredencoding(), 'xmlcharrefreplace'))
 | 
			
		||||
		if self.params.get('forceformat', False):
 | 
			
		||||
			print(info_dict['format'].encode(preferredencoding(), 'xmlcharrefreplace'))
 | 
			
		||||
 | 
			
		||||
		# Do nothing else if in simulate mode
 | 
			
		||||
		if self.params.get('simulate', False):
 | 
			
		||||
			return
 | 
			
		||||
 | 
			
		||||
		if filename is None:
 | 
			
		||||
			return
 | 
			
		||||
 | 
			
		||||
		try:
 | 
			
		||||
			dn = os.path.dirname(encodeFilename(filename))
 | 
			
		||||
			if dn != '' and not os.path.exists(dn): # dn is already encoded
 | 
			
		||||
				os.makedirs(dn)
 | 
			
		||||
		except (OSError, IOError), err:
 | 
			
		||||
			self.trouble(u'ERROR: unable to create directory ' + unicode(err))
 | 
			
		||||
			return
 | 
			
		||||
 | 
			
		||||
		if self.params.get('writedescription', False):
 | 
			
		||||
			try:
 | 
			
		||||
				descfn = filename + u'.description'
 | 
			
		||||
				self.report_writedescription(descfn)
 | 
			
		||||
				descfile = open(encodeFilename(descfn), 'wb')
 | 
			
		||||
				try:
 | 
			
		||||
					descfile.write(info_dict['description'].encode('utf-8'))
 | 
			
		||||
				finally:
 | 
			
		||||
					descfile.close()
 | 
			
		||||
			except (OSError, IOError):
 | 
			
		||||
				self.trouble(u'ERROR: Cannot write description file ' + descfn)
 | 
			
		||||
				return
 | 
			
		||||
 | 
			
		||||
		if self.params.get('writesubtitles', False) and 'subtitles' in info_dict and info_dict['subtitles']:
 | 
			
		||||
			# subtitles download errors are already managed as troubles in relevant IE
 | 
			
		||||
			# that way it will silently go on when used with unsupporting IE
 | 
			
		||||
			try:
 | 
			
		||||
				srtfn = filename.rsplit('.', 1)[0] + u'.srt'
 | 
			
		||||
				self.report_writesubtitles(srtfn)
 | 
			
		||||
				srtfile = open(encodeFilename(srtfn), 'wb')
 | 
			
		||||
				try:
 | 
			
		||||
					srtfile.write(info_dict['subtitles'].encode('utf-8'))
 | 
			
		||||
				finally:
 | 
			
		||||
					srtfile.close()
 | 
			
		||||
			except (OSError, IOError):
 | 
			
		||||
				self.trouble(u'ERROR: Cannot write subtitles file ' + descfn)
 | 
			
		||||
				return
 | 
			
		||||
 | 
			
		||||
		if self.params.get('writeinfojson', False):
 | 
			
		||||
			infofn = filename + u'.info.json'
 | 
			
		||||
			self.report_writeinfojson(infofn)
 | 
			
		||||
			try:
 | 
			
		||||
				json.dump
 | 
			
		||||
			except (NameError,AttributeError):
 | 
			
		||||
				self.trouble(u'ERROR: No JSON encoder found. Update to Python 2.6+, setup a json module, or leave out --write-info-json.')
 | 
			
		||||
				return
 | 
			
		||||
			try:
 | 
			
		||||
				infof = open(encodeFilename(infofn), 'wb')
 | 
			
		||||
				try:
 | 
			
		||||
					json_info_dict = dict((k,v) for k,v in info_dict.iteritems() if not k in ('urlhandle',))
 | 
			
		||||
					json.dump(json_info_dict, infof)
 | 
			
		||||
				finally:
 | 
			
		||||
					infof.close()
 | 
			
		||||
			except (OSError, IOError):
 | 
			
		||||
				self.trouble(u'ERROR: Cannot write metadata to JSON file ' + infofn)
 | 
			
		||||
				return
 | 
			
		||||
 | 
			
		||||
		if not self.params.get('skip_download', False):
 | 
			
		||||
			if self.params.get('nooverwrites', False) and os.path.exists(encodeFilename(filename)):
 | 
			
		||||
				success = True
 | 
			
		||||
			else:
 | 
			
		||||
				try:
 | 
			
		||||
					success = self._do_download(filename, info_dict)
 | 
			
		||||
				except (OSError, IOError), err:
 | 
			
		||||
					raise UnavailableVideoError
 | 
			
		||||
				except (urllib2.URLError, httplib.HTTPException, socket.error), err:
 | 
			
		||||
					self.trouble(u'ERROR: unable to download video data: %s' % str(err))
 | 
			
		||||
					return
 | 
			
		||||
				except (ContentTooShortError, ), err:
 | 
			
		||||
					self.trouble(u'ERROR: content too short (expected %s bytes and served %s)' % (err.expected, err.downloaded))
 | 
			
		||||
					return
 | 
			
		||||
 | 
			
		||||
			if success:
 | 
			
		||||
				try:
 | 
			
		||||
					self.post_process(filename, info_dict)
 | 
			
		||||
				except (PostProcessingError), err:
 | 
			
		||||
					self.trouble(u'ERROR: postprocessing: %s' % str(err))
 | 
			
		||||
					return
 | 
			
		||||
 | 
			
		||||
	def download(self, url_list):
 | 
			
		||||
		"""Download a given list of URLs."""
 | 
			
		||||
		if len(url_list) > 1 and self.fixed_template():
 | 
			
		||||
			raise SameFileError(self.params['outtmpl'])
 | 
			
		||||
 | 
			
		||||
		for url in url_list:
 | 
			
		||||
			suitable_found = False
 | 
			
		||||
			for ie in self._ies:
 | 
			
		||||
				# Go to next InfoExtractor if not suitable
 | 
			
		||||
				if not ie.suitable(url):
 | 
			
		||||
					continue
 | 
			
		||||
 | 
			
		||||
				# Suitable InfoExtractor found
 | 
			
		||||
				suitable_found = True
 | 
			
		||||
 | 
			
		||||
				# Extract information from URL and process it
 | 
			
		||||
				videos = ie.extract(url)
 | 
			
		||||
				for video in videos or []:
 | 
			
		||||
					video['extractor'] = ie.IE_NAME
 | 
			
		||||
					try:
 | 
			
		||||
						self.increment_downloads()
 | 
			
		||||
						self.process_info(video)
 | 
			
		||||
					except UnavailableVideoError:
 | 
			
		||||
						self.trouble(u'\nERROR: unable to download video')
 | 
			
		||||
 | 
			
		||||
				# Suitable InfoExtractor had been found; go to next URL
 | 
			
		||||
				break
 | 
			
		||||
 | 
			
		||||
			if not suitable_found:
 | 
			
		||||
				self.trouble(u'ERROR: no suitable InfoExtractor: %s' % url)
 | 
			
		||||
 | 
			
		||||
		return self._download_retcode
 | 
			
		||||
 | 
			
		||||
	def post_process(self, filename, ie_info):
 | 
			
		||||
		"""Run the postprocessing chain on the given file."""
 | 
			
		||||
		info = dict(ie_info)
 | 
			
		||||
		info['filepath'] = filename
 | 
			
		||||
		for pp in self._pps:
 | 
			
		||||
			info = pp.run(info)
 | 
			
		||||
			if info is None:
 | 
			
		||||
				break
 | 
			
		||||
 | 
			
		||||
	def _download_with_rtmpdump(self, filename, url, player_url):
 | 
			
		||||
		self.report_destination(filename)
 | 
			
		||||
		tmpfilename = self.temp_name(filename)
 | 
			
		||||
 | 
			
		||||
		# Check for rtmpdump first
 | 
			
		||||
		try:
 | 
			
		||||
			subprocess.call(['rtmpdump', '-h'], stdout=(file(os.path.devnull, 'w')), stderr=subprocess.STDOUT)
 | 
			
		||||
		except (OSError, IOError):
 | 
			
		||||
			self.trouble(u'ERROR: RTMP download detected but "rtmpdump" could not be run')
 | 
			
		||||
			return False
 | 
			
		||||
 | 
			
		||||
		# Download using rtmpdump. rtmpdump returns exit code 2 when
 | 
			
		||||
		# the connection was interrumpted and resuming appears to be
 | 
			
		||||
		# possible. This is part of rtmpdump's normal usage, AFAIK.
 | 
			
		||||
		basic_args = ['rtmpdump', '-q'] + [[], ['-W', player_url]][player_url is not None] + ['-r', url, '-o', tmpfilename]
 | 
			
		||||
		args = basic_args + [[], ['-e', '-k', '1']][self.params.get('continuedl', False)]
 | 
			
		||||
		if self.params.get('verbose', False):
 | 
			
		||||
			try:
 | 
			
		||||
				import pipes
 | 
			
		||||
				shell_quote = lambda args: ' '.join(map(pipes.quote, args))
 | 
			
		||||
			except ImportError:
 | 
			
		||||
				shell_quote = repr
 | 
			
		||||
			self.to_screen(u'[debug] rtmpdump command line: ' + shell_quote(args))
 | 
			
		||||
		retval = subprocess.call(args)
 | 
			
		||||
		while retval == 2 or retval == 1:
 | 
			
		||||
			prevsize = os.path.getsize(encodeFilename(tmpfilename))
 | 
			
		||||
			self.to_screen(u'\r[rtmpdump] %s bytes' % prevsize, skip_eol=True)
 | 
			
		||||
			time.sleep(5.0) # This seems to be needed
 | 
			
		||||
			retval = subprocess.call(basic_args + ['-e'] + [[], ['-k', '1']][retval == 1])
 | 
			
		||||
			cursize = os.path.getsize(encodeFilename(tmpfilename))
 | 
			
		||||
			if prevsize == cursize and retval == 1:
 | 
			
		||||
				break
 | 
			
		||||
			 # Some rtmp streams seem abort after ~ 99.8%. Don't complain for those
 | 
			
		||||
			if prevsize == cursize and retval == 2 and cursize > 1024:
 | 
			
		||||
				self.to_screen(u'\r[rtmpdump] Could not download the whole video. This can happen for some advertisements.')
 | 
			
		||||
				retval = 0
 | 
			
		||||
				break
 | 
			
		||||
		if retval == 0:
 | 
			
		||||
			self.to_screen(u'\r[rtmpdump] %s bytes' % os.path.getsize(encodeFilename(tmpfilename)))
 | 
			
		||||
			self.try_rename(tmpfilename, filename)
 | 
			
		||||
			return True
 | 
			
		||||
		else:
 | 
			
		||||
			self.trouble(u'\nERROR: rtmpdump exited with code %d' % retval)
 | 
			
		||||
			return False
 | 
			
		||||
 | 
			
		||||
	def _do_download(self, filename, info_dict):
 | 
			
		||||
		url = info_dict['url']
 | 
			
		||||
		player_url = info_dict.get('player_url', None)
 | 
			
		||||
 | 
			
		||||
		# Check file already present
 | 
			
		||||
		if self.params.get('continuedl', False) and os.path.isfile(encodeFilename(filename)) and not self.params.get('nopart', False):
 | 
			
		||||
			self.report_file_already_downloaded(filename)
 | 
			
		||||
			return True
 | 
			
		||||
 | 
			
		||||
		# Attempt to download using rtmpdump
 | 
			
		||||
		if url.startswith('rtmp'):
 | 
			
		||||
			return self._download_with_rtmpdump(filename, url, player_url)
 | 
			
		||||
 | 
			
		||||
		tmpfilename = self.temp_name(filename)
 | 
			
		||||
		stream = None
 | 
			
		||||
 | 
			
		||||
		# Do not include the Accept-Encoding header
 | 
			
		||||
		headers = {'Youtubedl-no-compression': 'True'}
 | 
			
		||||
		basic_request = urllib2.Request(url, None, headers)
 | 
			
		||||
		request = urllib2.Request(url, None, headers)
 | 
			
		||||
 | 
			
		||||
		# Establish possible resume length
 | 
			
		||||
		if os.path.isfile(encodeFilename(tmpfilename)):
 | 
			
		||||
			resume_len = os.path.getsize(encodeFilename(tmpfilename))
 | 
			
		||||
		else:
 | 
			
		||||
			resume_len = 0
 | 
			
		||||
 | 
			
		||||
		open_mode = 'wb'
 | 
			
		||||
		if resume_len != 0:
 | 
			
		||||
			if self.params.get('continuedl', False):
 | 
			
		||||
				self.report_resuming_byte(resume_len)
 | 
			
		||||
				request.add_header('Range','bytes=%d-' % resume_len)
 | 
			
		||||
				open_mode = 'ab'
 | 
			
		||||
			else:
 | 
			
		||||
				resume_len = 0
 | 
			
		||||
 | 
			
		||||
		count = 0
 | 
			
		||||
		retries = self.params.get('retries', 0)
 | 
			
		||||
		while count <= retries:
 | 
			
		||||
			# Establish connection
 | 
			
		||||
			try:
 | 
			
		||||
				if count == 0 and 'urlhandle' in info_dict:
 | 
			
		||||
					data = info_dict['urlhandle']
 | 
			
		||||
				data = urllib2.urlopen(request)
 | 
			
		||||
				break
 | 
			
		||||
			except (urllib2.HTTPError, ), err:
 | 
			
		||||
				if (err.code < 500 or err.code >= 600) and err.code != 416:
 | 
			
		||||
					# Unexpected HTTP error
 | 
			
		||||
					raise
 | 
			
		||||
				elif err.code == 416:
 | 
			
		||||
					# Unable to resume (requested range not satisfiable)
 | 
			
		||||
					try:
 | 
			
		||||
						# Open the connection again without the range header
 | 
			
		||||
						data = urllib2.urlopen(basic_request)
 | 
			
		||||
						content_length = data.info()['Content-Length']
 | 
			
		||||
					except (urllib2.HTTPError, ), err:
 | 
			
		||||
						if err.code < 500 or err.code >= 600:
 | 
			
		||||
							raise
 | 
			
		||||
					else:
 | 
			
		||||
						# Examine the reported length
 | 
			
		||||
						if (content_length is not None and
 | 
			
		||||
								(resume_len - 100 < long(content_length) < resume_len + 100)):
 | 
			
		||||
							# The file had already been fully downloaded.
 | 
			
		||||
							# Explanation to the above condition: in issue #175 it was revealed that
 | 
			
		||||
							# YouTube sometimes adds or removes a few bytes from the end of the file,
 | 
			
		||||
							# changing the file size slightly and causing problems for some users. So
 | 
			
		||||
							# I decided to implement a suggested change and consider the file
 | 
			
		||||
							# completely downloaded if the file size differs less than 100 bytes from
 | 
			
		||||
							# the one in the hard drive.
 | 
			
		||||
							self.report_file_already_downloaded(filename)
 | 
			
		||||
							self.try_rename(tmpfilename, filename)
 | 
			
		||||
							return True
 | 
			
		||||
						else:
 | 
			
		||||
							# The length does not match, we start the download over
 | 
			
		||||
							self.report_unable_to_resume()
 | 
			
		||||
							open_mode = 'wb'
 | 
			
		||||
							break
 | 
			
		||||
			# Retry
 | 
			
		||||
			count += 1
 | 
			
		||||
			if count <= retries:
 | 
			
		||||
				self.report_retry(count, retries)
 | 
			
		||||
 | 
			
		||||
		if count > retries:
 | 
			
		||||
			self.trouble(u'ERROR: giving up after %s retries' % retries)
 | 
			
		||||
			return False
 | 
			
		||||
 | 
			
		||||
		data_len = data.info().get('Content-length', None)
 | 
			
		||||
		if data_len is not None:
 | 
			
		||||
			data_len = long(data_len) + resume_len
 | 
			
		||||
		data_len_str = self.format_bytes(data_len)
 | 
			
		||||
		byte_counter = 0 + resume_len
 | 
			
		||||
		block_size = self.params.get('buffersize', 1024)
 | 
			
		||||
		start = time.time()
 | 
			
		||||
		while True:
 | 
			
		||||
			# Download and write
 | 
			
		||||
			before = time.time()
 | 
			
		||||
			data_block = data.read(block_size)
 | 
			
		||||
			after = time.time()
 | 
			
		||||
			if len(data_block) == 0:
 | 
			
		||||
				break
 | 
			
		||||
			byte_counter += len(data_block)
 | 
			
		||||
 | 
			
		||||
			# Open file just in time
 | 
			
		||||
			if stream is None:
 | 
			
		||||
				try:
 | 
			
		||||
					(stream, tmpfilename) = sanitize_open(tmpfilename, open_mode)
 | 
			
		||||
					assert stream is not None
 | 
			
		||||
					filename = self.undo_temp_name(tmpfilename)
 | 
			
		||||
					self.report_destination(filename)
 | 
			
		||||
				except (OSError, IOError), err:
 | 
			
		||||
					self.trouble(u'ERROR: unable to open for writing: %s' % str(err))
 | 
			
		||||
					return False
 | 
			
		||||
			try:
 | 
			
		||||
				stream.write(data_block)
 | 
			
		||||
			except (IOError, OSError), err:
 | 
			
		||||
				self.trouble(u'\nERROR: unable to write data: %s' % str(err))
 | 
			
		||||
				return False
 | 
			
		||||
			if not self.params.get('noresizebuffer', False):
 | 
			
		||||
				block_size = self.best_block_size(after - before, len(data_block))
 | 
			
		||||
 | 
			
		||||
			# Progress message
 | 
			
		||||
			speed_str = self.calc_speed(start, time.time(), byte_counter - resume_len)
 | 
			
		||||
			if data_len is None:
 | 
			
		||||
				self.report_progress('Unknown %', data_len_str, speed_str, 'Unknown ETA')
 | 
			
		||||
			else:
 | 
			
		||||
				percent_str = self.calc_percent(byte_counter, data_len)
 | 
			
		||||
				eta_str = self.calc_eta(start, time.time(), data_len - resume_len, byte_counter - resume_len)
 | 
			
		||||
				self.report_progress(percent_str, data_len_str, speed_str, eta_str)
 | 
			
		||||
 | 
			
		||||
			# Apply rate limit
 | 
			
		||||
			self.slow_down(start, byte_counter - resume_len)
 | 
			
		||||
 | 
			
		||||
		if stream is None:
 | 
			
		||||
			self.trouble(u'\nERROR: Did not get any data blocks')
 | 
			
		||||
			return False
 | 
			
		||||
		stream.close()
 | 
			
		||||
		self.report_finish()
 | 
			
		||||
		if data_len is not None and byte_counter != data_len:
 | 
			
		||||
			raise ContentTooShortError(byte_counter, long(data_len))
 | 
			
		||||
		self.try_rename(tmpfilename, filename)
 | 
			
		||||
 | 
			
		||||
		# Update file modification time
 | 
			
		||||
		if self.params.get('updatetime', True):
 | 
			
		||||
			info_dict['filetime'] = self.try_utime(filename, data.info().get('last-modified', None))
 | 
			
		||||
 | 
			
		||||
		return True
 | 
			
		||||
							
								
								
									
										3574
									
								
								youtube_dl/InfoExtractors.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3574
									
								
								youtube_dl/InfoExtractors.py
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										198
									
								
								youtube_dl/PostProcessor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								youtube_dl/PostProcessor.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,198 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
		When this method returns None, the postprocessing chain is
 | 
			
		||||
		stopped. However, this method may return an information
 | 
			
		||||
		dictionary that will be passed to the next postprocessing
 | 
			
		||||
		object in the chain. It can be the one it received after
 | 
			
		||||
		changing some fields.
 | 
			
		||||
 | 
			
		||||
		In addition, this method may raise a PostProcessingError
 | 
			
		||||
		exception that will be taken into account by the downloader
 | 
			
		||||
		it was called from.
 | 
			
		||||
		"""
 | 
			
		||||
		return information # by default, do nothing
 | 
			
		||||
 | 
			
		||||
class AudioConversionError(BaseException):
 | 
			
		||||
	def __init__(self, message):
 | 
			
		||||
		self.message = message
 | 
			
		||||
 | 
			
		||||
class FFmpegExtractAudioPP(PostProcessor):
 | 
			
		||||
	def __init__(self, downloader=None, preferredcodec=None, preferredquality=None, keepvideo=False):
 | 
			
		||||
		PostProcessor.__init__(self, downloader)
 | 
			
		||||
		if preferredcodec is None:
 | 
			
		||||
			preferredcodec = 'best'
 | 
			
		||||
		self._preferredcodec = preferredcodec
 | 
			
		||||
		self._preferredquality = preferredquality
 | 
			
		||||
		self._keepvideo = keepvideo
 | 
			
		||||
		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 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(path)]
 | 
			
		||||
			handle = subprocess.Popen(cmd, stderr=file(os.path.devnull, 'w'), 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.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]
 | 
			
		||||
		cmd = ([self._exes['avconv'] or self._exes['ffmpeg'], '-y', '-i', encodeFilename(path), '-vn']
 | 
			
		||||
			   + acodec_opts + more_opts +
 | 
			
		||||
			   ['--', encodeFilename(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 AudioConversionError(msg)
 | 
			
		||||
 | 
			
		||||
	def run(self, information):
 | 
			
		||||
		path = information['filepath']
 | 
			
		||||
 | 
			
		||||
		filecodec = self.get_audio_codec(path)
 | 
			
		||||
		if filecodec is None:
 | 
			
		||||
			self._downloader.to_stderr(u'WARNING: unable to obtain file audio codec with ffprobe')
 | 
			
		||||
			return None
 | 
			
		||||
 | 
			
		||||
		more_opts = []
 | 
			
		||||
		if self._preferredcodec == 'best' or self._preferredcodec == filecodec or (self._preferredcodec == 'm4a' and filecodec == 'aac'):
 | 
			
		||||
			if self._preferredcodec == 'm4a' and filecodec == 'aac':
 | 
			
		||||
				# Lossless, but in another container
 | 
			
		||||
				acodec = 'copy'
 | 
			
		||||
				extension = self._preferredcodec
 | 
			
		||||
				more_opts = [self._exes['avconv'] and '-bsf:a' or '-absf', 'aac_adtstoasc']
 | 
			
		||||
			elif filecodec in ['aac', 'mp3', 'vorbis']:
 | 
			
		||||
				# 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', '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
 | 
			
		||||
		self._downloader.to_screen(u'[' + (self._exes['avconv'] and 'avconv' or 'ffmpeg') + '] Destination: ' + new_path)
 | 
			
		||||
		try:
 | 
			
		||||
			self.run_ffmpeg(path, new_path, acodec, more_opts)
 | 
			
		||||
		except:
 | 
			
		||||
			etype,e,tb = sys.exc_info()
 | 
			
		||||
			if isinstance(e, AudioConversionError):
 | 
			
		||||
				self._downloader.to_stderr(u'ERROR: audio conversion failed: ' + e.message)
 | 
			
		||||
			else:
 | 
			
		||||
				self._downloader.to_stderr(u'ERROR: error running ' + (self._exes['avconv'] and 'avconv' or 'ffmpeg'))
 | 
			
		||||
			return None
 | 
			
		||||
 | 
			
		||||
 		# 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')
 | 
			
		||||
 | 
			
		||||
		if not self._keepvideo:
 | 
			
		||||
			try:
 | 
			
		||||
				os.remove(encodeFilename(path))
 | 
			
		||||
			except (IOError, OSError):
 | 
			
		||||
				self._downloader.to_stderr(u'WARNING: Unable to remove downloaded video file')
 | 
			
		||||
				return None
 | 
			
		||||
 | 
			
		||||
		information['filepath'] = new_path
 | 
			
		||||
		return information
 | 
			
		||||
							
								
								
									
										572
									
								
								youtube_dl/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										572
									
								
								youtube_dl/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,572 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
from __future__ import with_statement
 | 
			
		||||
 | 
			
		||||
__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',
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
__license__ = 'Public Domain'
 | 
			
		||||
__version__ = '2012.11.29'
 | 
			
		||||
 | 
			
		||||
UPDATE_URL = 'https://raw.github.com/rg3/youtube-dl/master/youtube-dl'
 | 
			
		||||
UPDATE_URL_VERSION = 'https://raw.github.com/rg3/youtube-dl/master/LATEST_VERSION'
 | 
			
		||||
UPDATE_URL_EXE = 'https://raw.github.com/rg3/youtube-dl/master/youtube-dl.exe'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import cookielib
 | 
			
		||||
import getpass
 | 
			
		||||
import optparse
 | 
			
		||||
import os
 | 
			
		||||
import re
 | 
			
		||||
import shlex
 | 
			
		||||
import socket
 | 
			
		||||
import subprocess
 | 
			
		||||
import sys
 | 
			
		||||
import urllib2
 | 
			
		||||
import warnings
 | 
			
		||||
 | 
			
		||||
from utils import *
 | 
			
		||||
from FileDownloader import *
 | 
			
		||||
from InfoExtractors import *
 | 
			
		||||
from PostProcessor import *
 | 
			
		||||
 | 
			
		||||
def updateSelf(downloader, filename):
 | 
			
		||||
	''' Update the program file with the latest version from the repository '''
 | 
			
		||||
	# Note: downloader only used for options
 | 
			
		||||
 | 
			
		||||
	if not os.access(filename, os.W_OK):
 | 
			
		||||
		sys.exit('ERROR: no write permissions on %s' % filename)
 | 
			
		||||
 | 
			
		||||
	downloader.to_screen(u'Updating to latest version...')
 | 
			
		||||
 | 
			
		||||
	urlv = urllib2.urlopen(UPDATE_URL_VERSION)
 | 
			
		||||
	newversion = urlv.read().strip()
 | 
			
		||||
	if newversion == __version__:
 | 
			
		||||
		downloader.to_screen(u'youtube-dl is up-to-date (' + __version__ + ')')
 | 
			
		||||
		return
 | 
			
		||||
	urlv.close()
 | 
			
		||||
 | 
			
		||||
	if hasattr(sys, "frozen"): #py2exe
 | 
			
		||||
		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:
 | 
			
		||||
			urlh = urllib2.urlopen(UPDATE_URL_EXE)
 | 
			
		||||
			newcontent = urlh.read()
 | 
			
		||||
			urlh.close()
 | 
			
		||||
			with open(exe + '.new', 'wb') as outf:
 | 
			
		||||
				outf.write(newcontent)
 | 
			
		||||
		except (IOError, OSError), err:
 | 
			
		||||
			sys.exit('ERROR: unable to download latest 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), err:
 | 
			
		||||
			sys.exit('ERROR: unable to overwrite current version')
 | 
			
		||||
 | 
			
		||||
	else:
 | 
			
		||||
		try:
 | 
			
		||||
			urlh = urllib2.urlopen(UPDATE_URL)
 | 
			
		||||
			newcontent = urlh.read()
 | 
			
		||||
			urlh.close()
 | 
			
		||||
		except (IOError, OSError), err:
 | 
			
		||||
			sys.exit('ERROR: unable to download latest version')
 | 
			
		||||
 | 
			
		||||
		try:
 | 
			
		||||
			with open(filename, 'wb') as outf:
 | 
			
		||||
				outf.write(newcontent)
 | 
			
		||||
		except (IOError, OSError), err:
 | 
			
		||||
			sys.exit('ERROR: unable to overwrite current version')
 | 
			
		||||
 | 
			
		||||
	downloader.to_screen(u'Updated youtube-dl. Restart youtube-dl to use the new version.')
 | 
			
		||||
 | 
			
		||||
def parseOpts():
 | 
			
		||||
	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='download rate limit (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('--list-extractors',
 | 
			
		||||
			action='store_true', dest='list_extractors',
 | 
			
		||||
			help='List all supported extractors and the URLs they would handle', default=False)
 | 
			
		||||
 | 
			
		||||
	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)
 | 
			
		||||
 | 
			
		||||
	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')
 | 
			
		||||
	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-srt',
 | 
			
		||||
			action='store_true', dest='writesubtitles',
 | 
			
		||||
			help='write video closed captions to a .srt file (currently youtube only)', default=False)
 | 
			
		||||
	video_format.add_option('--srt-lang',
 | 
			
		||||
			action='store', dest='subtitleslang', metavar='LANG',
 | 
			
		||||
			help='language of the closed captions 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('--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)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	filesystem.add_option('-t', '--title',
 | 
			
		||||
			action='store_true', dest='usetitle', help='use title in file name', default=False)
 | 
			
		||||
	filesystem.add_option('--id',
 | 
			
		||||
			action='store_true', dest='useid', help='use 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, %(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 and %% for a literal percent. Use - to output to stdout.')
 | 
			
		||||
	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)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	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", 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('-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')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	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)
 | 
			
		||||
 | 
			
		||||
	xdg_config_home = os.environ.get('XDG_CONFIG_HOME')
 | 
			
		||||
	if xdg_config_home:
 | 
			
		||||
		userConf = os.path.join(xdg_config_home, 'youtube-dl.conf')
 | 
			
		||||
	else:
 | 
			
		||||
		userConf = os.path.join(os.path.expanduser('~'), '.config', 'youtube-dl.conf')
 | 
			
		||||
	argv = _readOptions('/etc/youtube-dl.conf') + _readOptions(userConf) + sys.argv[1:]
 | 
			
		||||
	opts, args = parser.parse_args(argv)
 | 
			
		||||
 | 
			
		||||
	return parser, opts, args
 | 
			
		||||
 | 
			
		||||
def gen_extractors():
 | 
			
		||||
	""" Return a list of an instance of every supported extractor.
 | 
			
		||||
	The order does matter; the first extractor matched is the one handling the URL.
 | 
			
		||||
	"""
 | 
			
		||||
	return [
 | 
			
		||||
		YoutubePlaylistIE(),
 | 
			
		||||
		YoutubeChannelIE(),
 | 
			
		||||
		YoutubeUserIE(),
 | 
			
		||||
		YoutubeSearchIE(),
 | 
			
		||||
		YoutubeIE(),
 | 
			
		||||
		MetacafeIE(),
 | 
			
		||||
		DailymotionIE(),
 | 
			
		||||
		GoogleIE(),
 | 
			
		||||
		GoogleSearchIE(),
 | 
			
		||||
		PhotobucketIE(),
 | 
			
		||||
		YahooIE(),
 | 
			
		||||
		YahooSearchIE(),
 | 
			
		||||
		DepositFilesIE(),
 | 
			
		||||
		FacebookIE(),
 | 
			
		||||
		BlipTVUserIE(),
 | 
			
		||||
		BlipTVIE(),
 | 
			
		||||
		VimeoIE(),
 | 
			
		||||
		MyVideoIE(),
 | 
			
		||||
		ComedyCentralIE(),
 | 
			
		||||
		EscapistIE(),
 | 
			
		||||
		CollegeHumorIE(),
 | 
			
		||||
		XVideosIE(),
 | 
			
		||||
		SoundcloudIE(),
 | 
			
		||||
		InfoQIE(),
 | 
			
		||||
		MixcloudIE(),
 | 
			
		||||
		StanfordOpenClassroomIE(),
 | 
			
		||||
		MTVIE(),
 | 
			
		||||
		YoukuIE(),
 | 
			
		||||
		XNXXIE(),
 | 
			
		||||
		GooglePlusIE(),
 | 
			
		||||
		ArteTvIE(),
 | 
			
		||||
		GenericIE()
 | 
			
		||||
	]
 | 
			
		||||
 | 
			
		||||
def _real_main():
 | 
			
		||||
	parser, opts, args = parseOpts()
 | 
			
		||||
 | 
			
		||||
	# Open appropriate CookieJar
 | 
			
		||||
	if opts.cookiefile is None:
 | 
			
		||||
		jar = cookielib.CookieJar()
 | 
			
		||||
	else:
 | 
			
		||||
		try:
 | 
			
		||||
			jar = cookielib.MozillaCookieJar(opts.cookiefile)
 | 
			
		||||
			if os.path.isfile(opts.cookiefile) and os.access(opts.cookiefile, os.R_OK):
 | 
			
		||||
				jar.load()
 | 
			
		||||
		except (IOError, OSError), err:
 | 
			
		||||
			sys.exit(u'ERROR: unable to open cookie file')
 | 
			
		||||
	# Set user agent
 | 
			
		||||
	if opts.user_agent is not None:
 | 
			
		||||
		std_headers['User-Agent'] = opts.user_agent
 | 
			
		||||
 | 
			
		||||
	# 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 = map(lambda url: url.strip(), all_urls)
 | 
			
		||||
 | 
			
		||||
	# General configuration
 | 
			
		||||
	cookie_processor = urllib2.HTTPCookieProcessor(jar)
 | 
			
		||||
	proxy_handler = urllib2.ProxyHandler()
 | 
			
		||||
	opener = urllib2.build_opener(proxy_handler, cookie_processor, YoutubeDLHandler())
 | 
			
		||||
	urllib2.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)
 | 
			
		||||
			matchedUrls = filter(lambda url: ie.suitable(url), all_urls)
 | 
			
		||||
			all_urls = filter(lambda url: url not in matchedUrls, all_urls)
 | 
			
		||||
			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.retries is not None:
 | 
			
		||||
		try:
 | 
			
		||||
			opts.retries = long(opts.retries)
 | 
			
		||||
		except (TypeError, ValueError), 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), 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), err:
 | 
			
		||||
		parser.error(u'invalid playlist end number specified')
 | 
			
		||||
	if opts.extractaudio:
 | 
			
		||||
		if opts.audioformat not in ['best', 'aac', 'mp3', 'vorbis', 'm4a', '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')
 | 
			
		||||
 | 
			
		||||
	# 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': ((opts.outtmpl is not None and opts.outtmpl.decode(preferredencoding()))
 | 
			
		||||
			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'%(id)s.%(ext)s'),
 | 
			
		||||
		'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,
 | 
			
		||||
		'playliststart': opts.playliststart,
 | 
			
		||||
		'playlistend': opts.playlistend,
 | 
			
		||||
		'logtostderr': opts.outtmpl == '-',
 | 
			
		||||
		'consoletitle': opts.consoletitle,
 | 
			
		||||
		'nopart': opts.nopart,
 | 
			
		||||
		'updatetime': opts.updatetime,
 | 
			
		||||
		'writedescription': opts.writedescription,
 | 
			
		||||
		'writeinfojson': opts.writeinfojson,
 | 
			
		||||
		'writesubtitles': opts.writesubtitles,
 | 
			
		||||
		'subtitleslang': opts.subtitleslang,
 | 
			
		||||
		'matchtitle': opts.matchtitle,
 | 
			
		||||
		'rejecttitle': opts.rejecttitle,
 | 
			
		||||
		'max_downloads': opts.max_downloads,
 | 
			
		||||
		'prefer_free_formats': opts.prefer_free_formats,
 | 
			
		||||
		'verbose': opts.verbose,
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
	if opts.verbose:
 | 
			
		||||
		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, keepvideo=opts.keepvideo))
 | 
			
		||||
 | 
			
		||||
	# Update version
 | 
			
		||||
	if opts.update_self:
 | 
			
		||||
		updateSelf(fd, 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), err:
 | 
			
		||||
			sys.exit(u'ERROR: unable to save cookie jar')
 | 
			
		||||
 | 
			
		||||
	sys.exit(retcode)
 | 
			
		||||
 | 
			
		||||
def main():
 | 
			
		||||
	try:
 | 
			
		||||
		_real_main()
 | 
			
		||||
	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')
 | 
			
		||||
							
								
								
									
										7
									
								
								youtube_dl/__main__.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										7
									
								
								youtube_dl/__main__.py
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
import __init__
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
	__init__.main()
 | 
			
		||||
							
								
								
									
										380
									
								
								youtube_dl/utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										380
									
								
								youtube_dl/utils.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,380 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
import gzip
 | 
			
		||||
import htmlentitydefs
 | 
			
		||||
import HTMLParser
 | 
			
		||||
import locale
 | 
			
		||||
import os
 | 
			
		||||
import re
 | 
			
		||||
import sys
 | 
			
		||||
import zlib
 | 
			
		||||
import urllib2
 | 
			
		||||
import email.utils
 | 
			
		||||
import json
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
	import cStringIO as StringIO
 | 
			
		||||
except ImportError:
 | 
			
		||||
	import StringIO
 | 
			
		||||
 | 
			
		||||
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',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    compat_str = unicode # Python 2
 | 
			
		||||
except NameError:
 | 
			
		||||
    compat_str = str
 | 
			
		||||
 | 
			
		||||
def preferredencoding():
 | 
			
		||||
	"""Get preferred encoding.
 | 
			
		||||
 | 
			
		||||
	Returns the best encoding scheme for the system, based on
 | 
			
		||||
	locale.getpreferredencoding() and some further tweaks.
 | 
			
		||||
	"""
 | 
			
		||||
	def yield_preferredencoding():
 | 
			
		||||
		try:
 | 
			
		||||
			pref = locale.getpreferredencoding()
 | 
			
		||||
			u'TEST'.encode(pref)
 | 
			
		||||
		except:
 | 
			
		||||
			pref = 'UTF-8'
 | 
			
		||||
		while True:
 | 
			
		||||
			yield pref
 | 
			
		||||
	return yield_preferredencoding().next()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def htmlentity_transform(matchobj):
 | 
			
		||||
	"""Transforms an HTML entity to a Unicode 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 htmlentitydefs.name2codepoint:
 | 
			
		||||
		return unichr(htmlentitydefs.name2codepoint[entity])
 | 
			
		||||
 | 
			
		||||
	# Unicode character
 | 
			
		||||
	mobj = re.match(ur'(?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 unichr(long(numstr, base))
 | 
			
		||||
 | 
			
		||||
	# Unknown entity in name, return its literal representation
 | 
			
		||||
	return (u'&%s;' % entity)
 | 
			
		||||
 | 
			
		||||
HTMLParser.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 IDParser(HTMLParser.HTMLParser):
 | 
			
		||||
	"""Modified HTMLParser that isolates a tag with the specified id"""
 | 
			
		||||
	def __init__(self, id):
 | 
			
		||||
		self.id = id
 | 
			
		||||
		self.result = None
 | 
			
		||||
		self.started = False
 | 
			
		||||
		self.depth = {}
 | 
			
		||||
		self.html = None
 | 
			
		||||
		self.watch_startpos = False
 | 
			
		||||
		self.error_count = 0
 | 
			
		||||
		HTMLParser.HTMLParser.__init__(self)
 | 
			
		||||
 | 
			
		||||
	def error(self, message):
 | 
			
		||||
		if self.error_count > 10 or self.started:
 | 
			
		||||
			raise HTMLParser.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 'id' in attrs and attrs['id'] == self.id:
 | 
			
		||||
			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 == 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()
 | 
			
		||||
 | 
			
		||||
def get_element_by_id(id, html):
 | 
			
		||||
	"""Return the content of the tag with the specified id in the passed HTML document"""
 | 
			
		||||
	parser = IDParser(id)
 | 
			
		||||
	try:
 | 
			
		||||
		parser.loads(html)
 | 
			
		||||
	except HTMLParser.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('\s*<\s*br\s*/?\s*>\s*', '\n', html)
 | 
			
		||||
	# Strip html tags
 | 
			
		||||
	html = re.sub('<.*?>', '', html)
 | 
			
		||||
	# Replace html entities
 | 
			
		||||
	html = unescapeHTML(html)
 | 
			
		||||
	return html
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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, filename)
 | 
			
		||||
		stream = open(encodeFilename(filename), open_mode)
 | 
			
		||||
		return (stream, filename)
 | 
			
		||||
	except (IOError, OSError), err:
 | 
			
		||||
		# In case of error, try to remove win32 forbidden chars
 | 
			
		||||
		filename = re.sub(ur'[/<>:"\|\?\*]', 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):
 | 
			
		||||
	"""Sanitizes a string so it could be used as part of a filename.
 | 
			
		||||
	If restricted is set, use a stricter subset of allowed characters.
 | 
			
		||||
	"""
 | 
			
		||||
	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))
 | 
			
		||||
	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 (of type unicode)
 | 
			
		||||
	"""
 | 
			
		||||
	assert type(s) == type(u'')
 | 
			
		||||
 | 
			
		||||
	result = re.sub(ur'(?u)&(.+?);', htmlentity_transform, s)
 | 
			
		||||
	return result
 | 
			
		||||
 | 
			
		||||
def encodeFilename(s):
 | 
			
		||||
	"""
 | 
			
		||||
	@param s The name of the file (of type unicode)
 | 
			
		||||
	"""
 | 
			
		||||
 | 
			
		||||
	assert type(s) == type(u'')
 | 
			
		||||
 | 
			
		||||
	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:
 | 
			
		||||
		return s.encode(sys.getfilesystemencoding(), 'ignore')
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
	"""
 | 
			
		||||
	pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
	"""
 | 
			
		||||
	pass
 | 
			
		||||
 | 
			
		||||
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 Trouble(Exception):
 | 
			
		||||
	"""Trouble helper exception
 | 
			
		||||
 | 
			
		||||
	This is an exception to be handled with
 | 
			
		||||
	FileDownloader.trouble
 | 
			
		||||
	"""
 | 
			
		||||
 | 
			
		||||
class YoutubeDLHandler(urllib2.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(urllib2.addinfourl, 'getcode'):
 | 
			
		||||
			return urllib2.addinfourl(stream, headers, url, code)
 | 
			
		||||
		ret = urllib2.addinfourl(stream, headers, url)
 | 
			
		||||
		ret.code = code
 | 
			
		||||
		return ret
 | 
			
		||||
 | 
			
		||||
	def http_request(self, req):
 | 
			
		||||
		for h in std_headers:
 | 
			
		||||
			if h in req.headers:
 | 
			
		||||
				del req.headers[h]
 | 
			
		||||
			req.add_header(h, std_headers[h])
 | 
			
		||||
		if 'Youtubedl-no-compression' in req.headers:
 | 
			
		||||
			if 'Accept-encoding' in req.headers:
 | 
			
		||||
				del req.headers['Accept-encoding']
 | 
			
		||||
			del req.headers['Youtubedl-no-compression']
 | 
			
		||||
		return req
 | 
			
		||||
 | 
			
		||||
	def http_response(self, req, resp):
 | 
			
		||||
		old_resp = resp
 | 
			
		||||
		# gzip
 | 
			
		||||
		if resp.headers.get('Content-encoding', '') == 'gzip':
 | 
			
		||||
			gz = gzip.GzipFile(fileobj=StringIO.StringIO(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 = StringIO.StringIO(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
 | 
			
		||||
		Reference in New Issue
	
	Block a user