use pipenv, added contrib
Browse files- .envrc +24 -0
- Makefile +1 -0
- Pipfile +28 -0
- Pipfile.lock +371 -0
- requirements.txt → pytube/contrib/__init__.py +0 -0
- pytube/{playlist.py → contrib/playlist.py} +27 -35
- setup.cfg +2 -3
- tests/requirements.txt +0 -10
- tests/test_playlist.py +26 -18
.envrc
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# TODO: This function is only necessary until
|
2 |
+
# https://github.com/direnv/direnv/pull/314 gets accepted
|
3 |
+
# -------------------- 8< ---------------------------------
|
4 |
+
layout_pipenv() {
|
5 |
+
if [[ ! -f Pipfile ]]; then
|
6 |
+
echo 'No Pipfile found. Use `pipenv` to create a Pipfile first.' >&2
|
7 |
+
exit 2
|
8 |
+
fi
|
9 |
+
|
10 |
+
local VENV=$(pipenv --bare --venv 2>/dev/null)
|
11 |
+
if [[ -z $VENV || ! -d $VENV ]]; then
|
12 |
+
pipenv install --dev
|
13 |
+
fi
|
14 |
+
|
15 |
+
export VIRTUAL_ENV=$(pipenv --venv)
|
16 |
+
export PIPENV_ACTIVE=1
|
17 |
+
PATH_add "$VIRTUAL_ENV/bin"
|
18 |
+
}
|
19 |
+
# -------------------- 8< ---------------------------------
|
20 |
+
|
21 |
+
layout pipenv
|
22 |
+
|
23 |
+
# Will add node_modules/.bin to the $PATH
|
24 |
+
layout node
|
Makefile
CHANGED
@@ -12,6 +12,7 @@ clean-build:
|
|
12 |
rm -fr .eggs/
|
13 |
find . -name '*.egg-info' -exec rm -fr {} +
|
14 |
find . -name '*.egg' -exec rm -f {} +
|
|
|
15 |
|
16 |
clean-pyc:
|
17 |
find . -name '*.pyc' -exec rm -f {} +
|
|
|
12 |
rm -fr .eggs/
|
13 |
find . -name '*.egg-info' -exec rm -fr {} +
|
14 |
find . -name '*.egg' -exec rm -f {} +
|
15 |
+
find . -name '*.DS_Store' -exec rm -f {} +
|
16 |
|
17 |
clean-pyc:
|
18 |
find . -name '*.pyc' -exec rm -f {} +
|
Pipfile
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[[source]]
|
2 |
+
|
3 |
+
url = "https://pypi.python.org/simple"
|
4 |
+
verify_ssl = true
|
5 |
+
name = "pypi"
|
6 |
+
|
7 |
+
|
8 |
+
[packages]
|
9 |
+
|
10 |
+
|
11 |
+
|
12 |
+
[dev-packages]
|
13 |
+
|
14 |
+
bumpversion = "*"
|
15 |
+
coveralls = "*"
|
16 |
+
"flake8" = "*"
|
17 |
+
mock = "*"
|
18 |
+
pre-commit = "*"
|
19 |
+
pytest = "*"
|
20 |
+
pytest-cov = "*"
|
21 |
+
pytest-mock = "*"
|
22 |
+
sphinx = "*"
|
23 |
+
sphinx-rtd-theme = "*"
|
24 |
+
|
25 |
+
|
26 |
+
[requires]
|
27 |
+
|
28 |
+
python_version = "2.7"
|
Pipfile.lock
ADDED
@@ -0,0 +1,371 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"_meta": {
|
3 |
+
"hash": {
|
4 |
+
"sha256": "cfd5870db2e1ead08c85c866c3b50a92b4a08be4df3737e2f090f061389b8812"
|
5 |
+
},
|
6 |
+
"host-environment-markers": {
|
7 |
+
"implementation_name": "cpython",
|
8 |
+
"implementation_version": "0",
|
9 |
+
"os_name": "posix",
|
10 |
+
"platform_machine": "x86_64",
|
11 |
+
"platform_python_implementation": "CPython",
|
12 |
+
"platform_release": "17.4.0",
|
13 |
+
"platform_system": "Darwin",
|
14 |
+
"platform_version": "Darwin Kernel Version 17.4.0: Sun Dec 17 09:19:54 PST 2017; root:xnu-4570.41.2~1/RELEASE_X86_64",
|
15 |
+
"python_full_version": "2.7.14",
|
16 |
+
"python_version": "2.7",
|
17 |
+
"sys_platform": "darwin"
|
18 |
+
},
|
19 |
+
"pipfile-spec": 6,
|
20 |
+
"requires": {
|
21 |
+
"python_version": "2.7"
|
22 |
+
},
|
23 |
+
"sources": [
|
24 |
+
{
|
25 |
+
"name": "pypi",
|
26 |
+
"url": "https://pypi.python.org/simple",
|
27 |
+
"verify_ssl": true
|
28 |
+
}
|
29 |
+
]
|
30 |
+
},
|
31 |
+
"default": {},
|
32 |
+
"develop": {
|
33 |
+
"alabaster": {
|
34 |
+
"hashes": [
|
35 |
+
"sha256:2eef172f44e8d301d25aff8068fddd65f767a3f04b5f15b0f4922f113aa1c732",
|
36 |
+
"sha256:37cdcb9e9954ed60912ebc1ca12a9d12178c26637abdf124e3cde2341c257fe0"
|
37 |
+
],
|
38 |
+
"version": "==0.7.10"
|
39 |
+
},
|
40 |
+
"aspy.yaml": {
|
41 |
+
"hashes": [
|
42 |
+
"sha256:be70cc0ccd1ee1d30f589f2403792eb2ffa7546470af0a17179541b13d8374df",
|
43 |
+
"sha256:6215f44900ff65f27dbd00a36b06a7926276436ed377320cfd4febd69bbd4a94"
|
44 |
+
],
|
45 |
+
"version": "==1.0.0"
|
46 |
+
},
|
47 |
+
"attrs": {
|
48 |
+
"hashes": [
|
49 |
+
"sha256:a17a9573a6f475c99b551c0e0a812707ddda1ec9653bed04c13841404ed6f450",
|
50 |
+
"sha256:1c7960ccfd6a005cd9f7ba884e6316b5e430a3f1a6c37c5f87d8b43f83b54ec9"
|
51 |
+
],
|
52 |
+
"version": "==17.4.0"
|
53 |
+
},
|
54 |
+
"babel": {
|
55 |
+
"hashes": [
|
56 |
+
"sha256:ad209a68d7162c4cff4b29cdebe3dec4cef75492df501b0049a9433c96ce6f80",
|
57 |
+
"sha256:8ce4cb6fdd4393edd323227cba3a077bceb2a6ce5201c902c65e730046f41f14"
|
58 |
+
],
|
59 |
+
"version": "==2.5.3"
|
60 |
+
},
|
61 |
+
"bumpversion": {
|
62 |
+
"hashes": [
|
63 |
+
"sha256:6753d9ff3552013e2130f7bc03c1007e24473b4835952679653fb132367bdd57",
|
64 |
+
"sha256:6744c873dd7aafc24453d8b6a1a0d6d109faf63cd0cd19cb78fd46e74932c77e"
|
65 |
+
],
|
66 |
+
"version": "==0.5.3"
|
67 |
+
},
|
68 |
+
"cached-property": {
|
69 |
+
"hashes": [
|
70 |
+
"sha256:fe045921fe75c873064028e9fbbe06121114ccf613227f4ba284fa7d4c9ff27f",
|
71 |
+
"sha256:6562f0be134957547421dda11640e8cadfa7c23238fc4e0821ab69efdb1095f3"
|
72 |
+
],
|
73 |
+
"version": "==1.3.1"
|
74 |
+
},
|
75 |
+
"certifi": {
|
76 |
+
"hashes": [
|
77 |
+
"sha256:14131608ad2fd56836d33a71ee60fa1c82bc9d2c8d98b7bdbc631fe1b3cd1296",
|
78 |
+
"sha256:edbc3f203427eef571f79a7692bb160a2b0f7ccaa31953e99bd17e307cf63f7d"
|
79 |
+
],
|
80 |
+
"version": "==2018.1.18"
|
81 |
+
},
|
82 |
+
"chardet": {
|
83 |
+
"hashes": [
|
84 |
+
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691",
|
85 |
+
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"
|
86 |
+
],
|
87 |
+
"version": "==3.0.4"
|
88 |
+
},
|
89 |
+
"coverage": {
|
90 |
+
"hashes": [
|
91 |
+
"sha256:464d85d6959497cc4adfa9f0d36fca809e2ca7ec5f4625f548317892cac6ed7c",
|
92 |
+
"sha256:e958ab5b6a7f3b88289a25c95d031f2b62bc73219141c09d261fd97f244c124c",
|
93 |
+
"sha256:67288f8834a0a64c1af66286b22fd325b5524ceaa153a51c3e2e30f7e8b3f826",
|
94 |
+
"sha256:cfb6b7035c6605e2a87abe7d84ea35a107e6c432014a3f1ca243ab57a558fbcd",
|
95 |
+
"sha256:c86a12b3dc004bcbe97a3849354bd1f93eb6fb69b0e4eb58831fd7adba7740ec",
|
96 |
+
"sha256:8ddcf308f894d01a1a0ae01283d19b613751815b7190113266a0b7f9d076e86d",
|
97 |
+
"sha256:adab01e4c63a01bdf036f57f0114497994aa2195d8659d12a3d69673c3f27939",
|
98 |
+
"sha256:54d73fe68a7ac9c847af69a234a7461bbaf3cad95f258317d4584d14dd53f679",
|
99 |
+
"sha256:a0d98c71d026c1757c7393a99d24c6e42091ff41e20e68238b17e145252c2d0a",
|
100 |
+
"sha256:464e0eda175c7fe2dc730d9d02acde5b8a8518d9417413dee6ca187d1f65ef89",
|
101 |
+
"sha256:2890cb40464686c0c1dccc1223664bbc34d85af053bc5dbcd71ea13959e264f2",
|
102 |
+
"sha256:0f2315c793b1360f80a9119fff76efb7b4e5ab5062651dff515e681719f29689",
|
103 |
+
"sha256:85c028e959312285225040cdac5ad3db6189e958a234f09ae6b4ba5f539c842d",
|
104 |
+
"sha256:da6585339fc8a25086003a2b2c0167438b8ab0cd0ccae468d22ed603e414bba1",
|
105 |
+
"sha256:e837865a7b20c01a8a2f904c05fba36e8406b146649ff9174cbddf32e217b777",
|
106 |
+
"sha256:b718efb33097c7651a60a03b4b38b14776f92194bc0e9e598ce05ddaef7c70e7",
|
107 |
+
"sha256:7413f078fbba267de44814584593a729f88fc37f2d938263844b7f4daf1e36ec",
|
108 |
+
"sha256:47ad00a0c025f87a7528cc13d013c54e4691ae8730430e49ec9c7ace7e0e1fba",
|
109 |
+
"sha256:95f9f5072afeb2204401401cbd0ab978a9f86ef1ebc5cd267ba431cfa581cc4d",
|
110 |
+
"sha256:ca8827a5dad1176a8da6bf5396fd07e66549d1bc842047b76cdf69e196597a80",
|
111 |
+
"sha256:c68164c4f49cfc2e66ca1ded62e4a1092a6bd4b2c65222059b867700ad19151c",
|
112 |
+
"sha256:61e0bcf15aa0385e15d1fe4a86022a6b813d08c785855e3fab56ba6d7ac3dd21",
|
113 |
+
"sha256:981a64063242a2c6c88dda33ccafe3583026847961fe56636b6a00c47674e258",
|
114 |
+
"sha256:21e47d2ff9c75e25880dd12b316db11379e9afc98b39e9516149d189c15c564b",
|
115 |
+
"sha256:f6b822c68f68f48d480d23fcfcd1d4df7d42ff03cf5d7b574d09e662c0b95b43",
|
116 |
+
"sha256:53fa7aa7643a22eeadcf8b781b97a51f37d43ba1d897a05238aa7e4d11bc0667",
|
117 |
+
"sha256:95ce1a70323d47c0f6b8d6cfd3c14c38cb30d51fd1ab4f6414734fa33a78b17e",
|
118 |
+
"sha256:b7a06a523dfeaf417da630d46ad4f4e11ca1bae6202c9312c4cb987dde5792fc",
|
119 |
+
"sha256:585e8db44b8f3af2a4152b00dd8a7a36bc1d2aba7de5e50fc17a54178428f0d6",
|
120 |
+
"sha256:102933e14b726bd4fdfafb541e122ad36c150732aee36db409d8c8766e11537e",
|
121 |
+
"sha256:15f92238487d93f7f34a3ba03be3bd4615c69cffc88388b4dd1ea99af74fc1bf",
|
122 |
+
"sha256:319190dd7fa08c23332215782b563a9ef12b76fb15e4a325915592b825eca9ed",
|
123 |
+
"sha256:af14e9628c0a3152b6a1fbba4471e6a3e5f5567ecae614f84b84ff3441c58692",
|
124 |
+
"sha256:72bc3f91a25a87fd87eb57983c8cefbb8aa5bacd50d73516ade398271d652d77",
|
125 |
+
"sha256:c3905f10786dcf386f3f6cebe0ae4a36f47e5e256471161fb944ca537e97e928",
|
126 |
+
"sha256:3344079d73a4849341aaaecd9b391141824b8c9a96732fbd6ef95ba9566895d3"
|
127 |
+
],
|
128 |
+
"version": "==4.5"
|
129 |
+
},
|
130 |
+
"coveralls": {
|
131 |
+
"hashes": [
|
132 |
+
"sha256:84dd8c88c5754e8db70a682f537e2781366064aa3cdd6b24c2dcecbd3181187c",
|
133 |
+
"sha256:510682001517bcca1def9f6252df6ce730fcb9831c62d9fff7c7d55b6fdabdf3"
|
134 |
+
],
|
135 |
+
"version": "==1.2.0"
|
136 |
+
},
|
137 |
+
"docopt": {
|
138 |
+
"hashes": [
|
139 |
+
"sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"
|
140 |
+
],
|
141 |
+
"version": "==0.6.2"
|
142 |
+
},
|
143 |
+
"docutils": {
|
144 |
+
"hashes": [
|
145 |
+
"sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6",
|
146 |
+
"sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6",
|
147 |
+
"sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274"
|
148 |
+
],
|
149 |
+
"version": "==0.14"
|
150 |
+
},
|
151 |
+
"flake8": {
|
152 |
+
"hashes": [
|
153 |
+
"sha256:c7841163e2b576d435799169b78703ad6ac1bbb0f199994fc05f700b2a90ea37",
|
154 |
+
"sha256:7253265f7abd8b313e3892944044a365e3f4ac3fcdcfb4298f55ee9ddf188ba0"
|
155 |
+
],
|
156 |
+
"version": "==3.5.0"
|
157 |
+
},
|
158 |
+
"identify": {
|
159 |
+
"hashes": [
|
160 |
+
"sha256:804e6af41604b11e0b8e0670ae172ef254b152cb5370c7e6b6e7d0c6e9c6a95e",
|
161 |
+
"sha256:496d3cce9c4088664e4840e0e01db460820bffa13f03a3016078d99feda0cd74"
|
162 |
+
],
|
163 |
+
"version": "==1.0.7"
|
164 |
+
},
|
165 |
+
"idna": {
|
166 |
+
"hashes": [
|
167 |
+
"sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4",
|
168 |
+
"sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f"
|
169 |
+
],
|
170 |
+
"version": "==2.6"
|
171 |
+
},
|
172 |
+
"imagesize": {
|
173 |
+
"hashes": [
|
174 |
+
"sha256:6ebdc9e0ad188f9d1b2cdd9bc59cbe42bf931875e829e7a595e6b3abdc05cdfb",
|
175 |
+
"sha256:0ab2c62b87987e3252f89d30b7cedbec12a01af9274af9ffa48108f2c13c6062"
|
176 |
+
],
|
177 |
+
"version": "==0.7.1"
|
178 |
+
},
|
179 |
+
"jinja2": {
|
180 |
+
"hashes": [
|
181 |
+
"sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd",
|
182 |
+
"sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4"
|
183 |
+
],
|
184 |
+
"version": "==2.10"
|
185 |
+
},
|
186 |
+
"markupsafe": {
|
187 |
+
"hashes": [
|
188 |
+
"sha256:a6be69091dac236ea9c6bc7d012beab42010fa914c459791d627dad4910eb665"
|
189 |
+
],
|
190 |
+
"version": "==1.0"
|
191 |
+
},
|
192 |
+
"mccabe": {
|
193 |
+
"hashes": [
|
194 |
+
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
|
195 |
+
"sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
|
196 |
+
],
|
197 |
+
"version": "==0.6.1"
|
198 |
+
},
|
199 |
+
"mock": {
|
200 |
+
"hashes": [
|
201 |
+
"sha256:5ce3c71c5545b472da17b72268978914d0252980348636840bd34a00b5cc96c1",
|
202 |
+
"sha256:b158b6df76edd239b8208d481dc46b6afd45a846b7812ff0ce58971cf5bc8bba"
|
203 |
+
],
|
204 |
+
"version": "==2.0.0"
|
205 |
+
},
|
206 |
+
"nodeenv": {
|
207 |
+
"hashes": [
|
208 |
+
"sha256:98835dab727f94a713eacc7234e3db6777a55cafb60f391485011899e5c818df"
|
209 |
+
],
|
210 |
+
"version": "==1.2.0"
|
211 |
+
},
|
212 |
+
"pbr": {
|
213 |
+
"hashes": [
|
214 |
+
"sha256:60c25b7dfd054ef9bb0ae327af949dd4676aa09ac3a9471cdc871d8a9213f9ac",
|
215 |
+
"sha256:05f61c71aaefc02d8e37c0a3eeb9815ff526ea28b3b76324769e6158d7f95be1"
|
216 |
+
],
|
217 |
+
"version": "==3.1.1"
|
218 |
+
},
|
219 |
+
"pluggy": {
|
220 |
+
"hashes": [
|
221 |
+
"sha256:7f8ae7f5bdf75671a718d2daf0a64b7885f74510bcd98b1a0bb420eb9a9d0cff"
|
222 |
+
],
|
223 |
+
"version": "==0.6.0"
|
224 |
+
},
|
225 |
+
"pre-commit": {
|
226 |
+
"hashes": [
|
227 |
+
"sha256:036ac395075609b0ee7017dd356df42e486ccb8d675493e497492795146eab46",
|
228 |
+
"sha256:6d6646f5a390677c5c9cd61d5a2613b89d93f079725047aff16fddda9a3ce77f"
|
229 |
+
],
|
230 |
+
"version": "==1.6.0"
|
231 |
+
},
|
232 |
+
"py": {
|
233 |
+
"hashes": [
|
234 |
+
"sha256:8cca5c229d225f8c1e3085be4fcf306090b00850fefad892f9d96c7b6e2f310f",
|
235 |
+
"sha256:ca18943e28235417756316bfada6cd96b23ce60dd532642690dcfdaba988a76d"
|
236 |
+
],
|
237 |
+
"version": "==1.5.2"
|
238 |
+
},
|
239 |
+
"pycodestyle": {
|
240 |
+
"hashes": [
|
241 |
+
"sha256:6c4245ade1edfad79c3446fadfc96b0de2759662dc29d07d80a6f27ad1ca6ba9",
|
242 |
+
"sha256:682256a5b318149ca0d2a9185d365d8864a768a28db66a84a2ea946bcc426766"
|
243 |
+
],
|
244 |
+
"version": "==2.3.1"
|
245 |
+
},
|
246 |
+
"pyflakes": {
|
247 |
+
"hashes": [
|
248 |
+
"sha256:08bd6a50edf8cffa9fa09a463063c425ecaaf10d1eb0335a7e8b1401aef89e6f",
|
249 |
+
"sha256:8d616a382f243dbf19b54743f280b80198be0bca3a5396f1d2e1fca6223e8805"
|
250 |
+
],
|
251 |
+
"version": "==1.6.0"
|
252 |
+
},
|
253 |
+
"pygments": {
|
254 |
+
"hashes": [
|
255 |
+
"sha256:78f3f434bcc5d6ee09020f92ba487f95ba50f1e3ef83ae96b9d5ffa1bab25c5d",
|
256 |
+
"sha256:dbae1046def0efb574852fab9e90209b23f556367b5a320c0bcb871c77c3e8cc"
|
257 |
+
],
|
258 |
+
"version": "==2.2.0"
|
259 |
+
},
|
260 |
+
"pytest": {
|
261 |
+
"hashes": [
|
262 |
+
"sha256:95fa025cd6deb5d937e04e368a00552332b58cae23f63b76c8c540ff1733ab6d",
|
263 |
+
"sha256:6074ea3b9c999bd6d0df5fa9d12dd95ccd23550df2a582f5f5b848331d2e82ca"
|
264 |
+
],
|
265 |
+
"version": "==3.4.0"
|
266 |
+
},
|
267 |
+
"pytest-cov": {
|
268 |
+
"hashes": [
|
269 |
+
"sha256:890fe5565400902b0c78b5357004aab1c814115894f4f21370e2433256a3eeec",
|
270 |
+
"sha256:03aa752cf11db41d281ea1d807d954c4eda35cfa1b21d6971966cc041bbf6e2d"
|
271 |
+
],
|
272 |
+
"version": "==2.5.1"
|
273 |
+
},
|
274 |
+
"pytest-mock": {
|
275 |
+
"hashes": [
|
276 |
+
"sha256:7ed6e7e8c636fd320927c5d73aedb77ac2eeb37196c3410e6176b7c92fdf2f69",
|
277 |
+
"sha256:920d1167af5c2c2ad3fa0717d0c6c52e97e97810160c15721ac895cac53abb1c"
|
278 |
+
],
|
279 |
+
"version": "==1.6.3"
|
280 |
+
},
|
281 |
+
"pytz": {
|
282 |
+
"hashes": [
|
283 |
+
"sha256:80af0f3008046b9975242012a985f04c5df1f01eed4ec1633d56cc47a75a6a48",
|
284 |
+
"sha256:feb2365914948b8620347784b6b6da356f31c9d03560259070b2f30cff3d469d",
|
285 |
+
"sha256:59707844a9825589878236ff2f4e0dc9958511b7ffaae94dc615da07d4a68d33",
|
286 |
+
"sha256:d0ef5ef55ed3d37854320d4926b04a4cb42a2e88f71da9ddfdacfde8e364f027",
|
287 |
+
"sha256:c41c62827ce9cafacd6f2f7018e4f83a6f1986e87bfd000b8cfbd4ab5da95f1a",
|
288 |
+
"sha256:8cc90340159b5d7ced6f2ba77694d946fc975b09f1a51d93f3ce3bb399396f94",
|
289 |
+
"sha256:dd2e4ca6ce3785c8dd342d1853dd9052b19290d5bf66060846e5dc6b8d6667f7",
|
290 |
+
"sha256:699d18a2a56f19ee5698ab1123bbcc1d269d061996aeb1eda6d89248d3542b82",
|
291 |
+
"sha256:fae4cffc040921b8a2d60c6cf0b5d662c1190fe54d718271db4eb17d44a185b7"
|
292 |
+
],
|
293 |
+
"version": "==2017.3"
|
294 |
+
},
|
295 |
+
"pyyaml": {
|
296 |
+
"hashes": [
|
297 |
+
"sha256:3262c96a1ca437e7e4763e2843746588a965426550f3797a79fca9c6199c431f",
|
298 |
+
"sha256:16b20e970597e051997d90dc2cddc713a2876c47e3d92d59ee198700c5427736",
|
299 |
+
"sha256:e863072cdf4c72eebf179342c94e6989c67185842d9997960b3e69290b2fa269",
|
300 |
+
"sha256:bc6bced57f826ca7cb5125a10b23fd0f2fff3b7c4701d64c439a300ce665fff8",
|
301 |
+
"sha256:c01b880ec30b5a6e6aa67b09a2fe3fb30473008c85cd6a67359a1b15ed6d83a4",
|
302 |
+
"sha256:827dc04b8fa7d07c44de11fabbc888e627fa8293b695e0f99cb544fdfa1bf0d1",
|
303 |
+
"sha256:592766c6303207a20efc445587778322d7f73b161bd994f227adaa341ba212ab",
|
304 |
+
"sha256:5f84523c076ad14ff5e6c037fe1c89a7f73a3e04cf0377cb4d017014976433f3",
|
305 |
+
"sha256:0c507b7f74b3d2dd4d1322ec8a94794927305ab4cebbe89cc47fe5e81541e6e8",
|
306 |
+
"sha256:b4c423ab23291d3945ac61346feeb9a0dc4184999ede5e7c43e1ffb975130ae6",
|
307 |
+
"sha256:ca233c64c6e40eaa6c66ef97058cdc80e8d0157a443655baa1b2966e812807ca",
|
308 |
+
"sha256:4474f8ea030b5127225b8894d626bb66c01cda098d47a2b0d3429b6700af9fd8",
|
309 |
+
"sha256:326420cbb492172dec84b0f65c80942de6cedb5233c413dd824483989c000608",
|
310 |
+
"sha256:5ac82e411044fb129bae5cfbeb3ba626acb2af31a8d17d175004b70862a741a7"
|
311 |
+
],
|
312 |
+
"version": "==3.12"
|
313 |
+
},
|
314 |
+
"requests": {
|
315 |
+
"hashes": [
|
316 |
+
"sha256:6a1b267aa90cac58ac3a765d067950e7dbbf75b1da07e895d1f594193a40a38b",
|
317 |
+
"sha256:9c443e7324ba5b85070c4a818ade28bfabedf16ea10206da1132edaa6dda237e"
|
318 |
+
],
|
319 |
+
"version": "==2.18.4"
|
320 |
+
},
|
321 |
+
"six": {
|
322 |
+
"hashes": [
|
323 |
+
"sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb",
|
324 |
+
"sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9"
|
325 |
+
],
|
326 |
+
"version": "==1.11.0"
|
327 |
+
},
|
328 |
+
"snowballstemmer": {
|
329 |
+
"hashes": [
|
330 |
+
"sha256:9f3bcd3c401c3e862ec0ebe6d2c069ebc012ce142cce209c098ccb5b09136e89",
|
331 |
+
"sha256:919f26a68b2c17a7634da993d91339e288964f93c274f1343e3bbbe2096e1128"
|
332 |
+
],
|
333 |
+
"version": "==1.2.1"
|
334 |
+
},
|
335 |
+
"sphinx": {
|
336 |
+
"hashes": [
|
337 |
+
"sha256:d5b91b4dad56ffc9f19425ebaa7bc23290a0a2e9035781d5bc54822384663277",
|
338 |
+
"sha256:832bed0dc6099c2abca957d90ff55bc1a6ec4425c13fc144adbae68a970e3775"
|
339 |
+
],
|
340 |
+
"version": "==1.6.7"
|
341 |
+
},
|
342 |
+
"sphinx-rtd-theme": {
|
343 |
+
"hashes": [
|
344 |
+
"sha256:62ee4752716e698bad7de8a18906f42d33664128eea06c46b718fc7fbd1a9f5c",
|
345 |
+
"sha256:2df74b8ff6fae6965c527e97cca6c6c944886aae474b490e17f92adfbe843417"
|
346 |
+
],
|
347 |
+
"version": "==0.2.4"
|
348 |
+
},
|
349 |
+
"sphinxcontrib-websupport": {
|
350 |
+
"hashes": [
|
351 |
+
"sha256:f4932e95869599b89bf4f80fc3989132d83c9faa5bf633e7b5e0c25dffb75da2",
|
352 |
+
"sha256:7a85961326aa3a400cd4ad3c816d70ed6f7c740acd7ce5d78cd0a67825072eb9"
|
353 |
+
],
|
354 |
+
"version": "==1.0.1"
|
355 |
+
},
|
356 |
+
"urllib3": {
|
357 |
+
"hashes": [
|
358 |
+
"sha256:06330f386d6e4b195fbfc736b297f58c5a892e4440e54d294d7004e3a9bbea1b",
|
359 |
+
"sha256:cc44da8e1145637334317feebd728bd869a35285b93cbb4cca2577da7e62db4f"
|
360 |
+
],
|
361 |
+
"version": "==1.22"
|
362 |
+
},
|
363 |
+
"virtualenv": {
|
364 |
+
"hashes": [
|
365 |
+
"sha256:39d88b533b422825d644087a21e78c45cf5af0ef7a99a1fc9fbb7b481e5c85b0",
|
366 |
+
"sha256:02f8102c2436bb03b3ee6dede1919d1dac8a427541652e5ec95171ec8adbc93a"
|
367 |
+
],
|
368 |
+
"version": "==15.1.0"
|
369 |
+
}
|
370 |
+
}
|
371 |
+
}
|
requirements.txt → pytube/contrib/__init__.py
RENAMED
File without changes
|
pytube/{playlist.py → contrib/playlist.py}
RENAMED
@@ -2,15 +2,17 @@
|
|
2 |
"""
|
3 |
Module to download a complete playlist from a youtube channel
|
4 |
"""
|
5 |
-
|
|
|
6 |
from pytube import request
|
7 |
from pytube.__main__ import YouTube
|
8 |
|
|
|
|
|
9 |
|
10 |
class Playlist(object):
|
11 |
-
"""
|
12 |
-
|
13 |
-
a whole YouTube playlist
|
14 |
"""
|
15 |
|
16 |
def __init__(self, url):
|
@@ -18,26 +20,26 @@ class Playlist(object):
|
|
18 |
self.video_urls = []
|
19 |
|
20 |
def construct_playlist_url(self):
|
21 |
-
"""
|
22 |
-
There are two kinds of playlist urls in YouTube. One that
|
23 |
contains watch?v= in URL, another one contains the "playlist?list="
|
24 |
portion. It is preferable to work with the later one.
|
|
|
25 |
:return: playlist url -> string
|
26 |
"""
|
27 |
|
28 |
-
if
|
29 |
-
base_url =
|
30 |
-
playlist_code = self.playlist_url.split(
|
31 |
return base_url + playlist_code
|
32 |
|
33 |
# url is already in the desired format, so just return it
|
34 |
return self.playlist_url
|
35 |
|
36 |
def parse_links(self):
|
37 |
-
"""
|
38 |
-
Parse the video links from the page source, extracts and
|
39 |
returns the /watch?v= part from video link href
|
40 |
It's an alternative for BeautifulSoup
|
|
|
41 |
:return: list
|
42 |
"""
|
43 |
|
@@ -45,52 +47,42 @@ class Playlist(object):
|
|
45 |
req = request.get(url)
|
46 |
|
47 |
# split the page source by line and process each line
|
48 |
-
content = [x for x in req.split(
|
49 |
-
link_list = [x.split('href="', 1)[1].split(
|
50 |
|
51 |
return link_list
|
52 |
|
53 |
def populate_video_urls(self):
|
54 |
-
"""
|
55 |
-
Construct complete links of all the videos in playlist and
|
56 |
populate video_urls list
|
|
|
57 |
:return: urls -> string
|
58 |
"""
|
59 |
|
60 |
-
base_url =
|
61 |
link_list = self.parse_links()
|
62 |
|
63 |
for video_id in link_list:
|
64 |
complete_url = base_url + video_id
|
65 |
-
# print complete_url
|
66 |
self.video_urls.append(complete_url)
|
67 |
|
68 |
def download_all(self):
|
69 |
-
"""
|
70 |
-
Download all the videos in the the playlist. Initially, download
|
71 |
resolution is 720p (or highest available), later more option
|
72 |
should be added to download resolution of choice
|
73 |
-
|
74 |
-
:
|
75 |
"""
|
76 |
|
77 |
self.populate_video_urls()
|
78 |
-
|
79 |
-
|
80 |
-
print("Starting download...\n")
|
81 |
|
82 |
for link in self.video_urls:
|
83 |
yt = YouTube(link)
|
84 |
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
print("Downloading:", yt.title)
|
89 |
-
except UnicodeEncodeError:
|
90 |
-
print("(title cannot be shown due to unicode error)")
|
91 |
-
|
92 |
-
yt.streams.filter(progressive=True,
|
93 |
-
subtype="mp4").order_by(
|
94 |
-
"resolution").desc().first().download()
|
95 |
|
96 |
-
|
|
|
2 |
"""
|
3 |
Module to download a complete playlist from a youtube channel
|
4 |
"""
|
5 |
+
import logging
|
6 |
+
|
7 |
from pytube import request
|
8 |
from pytube.__main__ import YouTube
|
9 |
|
10 |
+
logger = logging.getLogger(__name__)
|
11 |
+
|
12 |
|
13 |
class Playlist(object):
|
14 |
+
"""Handles all the task of manipulating and downloading a whole YouTube
|
15 |
+
playlist
|
|
|
16 |
"""
|
17 |
|
18 |
def __init__(self, url):
|
|
|
20 |
self.video_urls = []
|
21 |
|
22 |
def construct_playlist_url(self):
|
23 |
+
"""There are two kinds of playlist urls in YouTube. One that
|
|
|
24 |
contains watch?v= in URL, another one contains the "playlist?list="
|
25 |
portion. It is preferable to work with the later one.
|
26 |
+
|
27 |
:return: playlist url -> string
|
28 |
"""
|
29 |
|
30 |
+
if 'watch?v=' in self.playlist_url:
|
31 |
+
base_url = 'https://www.youtube.com/playlist?list='
|
32 |
+
playlist_code = self.playlist_url.split('&list=')[1]
|
33 |
return base_url + playlist_code
|
34 |
|
35 |
# url is already in the desired format, so just return it
|
36 |
return self.playlist_url
|
37 |
|
38 |
def parse_links(self):
|
39 |
+
"""Parse the video links from the page source, extracts and
|
|
|
40 |
returns the /watch?v= part from video link href
|
41 |
It's an alternative for BeautifulSoup
|
42 |
+
|
43 |
:return: list
|
44 |
"""
|
45 |
|
|
|
47 |
req = request.get(url)
|
48 |
|
49 |
# split the page source by line and process each line
|
50 |
+
content = [x for x in req.split('\n') if 'pl-video-title-link' in x]
|
51 |
+
link_list = [x.split('href="', 1)[1].split('&', 1)[0] for x in content]
|
52 |
|
53 |
return link_list
|
54 |
|
55 |
def populate_video_urls(self):
|
56 |
+
"""Construct complete links of all the videos in playlist and
|
|
|
57 |
populate video_urls list
|
58 |
+
|
59 |
:return: urls -> string
|
60 |
"""
|
61 |
|
62 |
+
base_url = 'https://www.youtube.com'
|
63 |
link_list = self.parse_links()
|
64 |
|
65 |
for video_id in link_list:
|
66 |
complete_url = base_url + video_id
|
|
|
67 |
self.video_urls.append(complete_url)
|
68 |
|
69 |
def download_all(self):
|
70 |
+
"""Download all the videos in the the playlist. Initially, download
|
|
|
71 |
resolution is 720p (or highest available), later more option
|
72 |
should be added to download resolution of choice
|
73 |
+
|
74 |
+
TODO(nficano): Add option to download resolution of user's choice
|
75 |
"""
|
76 |
|
77 |
self.populate_video_urls()
|
78 |
+
logger.debug('total videos found: ', len(self.video_urls))
|
79 |
+
logger.debug('starting download')
|
|
|
80 |
|
81 |
for link in self.video_urls:
|
82 |
yt = YouTube(link)
|
83 |
|
84 |
+
yt.streams.filter(
|
85 |
+
progressive=True, subtype='mp4',
|
86 |
+
).order_by('resolution').desc().first().download()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
87 |
|
88 |
+
logger.debug('download complete')
|
setup.cfg
CHANGED
@@ -3,7 +3,7 @@ commit = True
|
|
3 |
tag = True
|
4 |
current_version = 9.0.3
|
5 |
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(\-(?P<release>[a-z]+))?
|
6 |
-
serialize =
|
7 |
{major}.{minor}.{patch}
|
8 |
|
9 |
[metadata]
|
@@ -15,6 +15,5 @@ description-file = README.md
|
|
15 |
|
16 |
[coverage:run]
|
17 |
source = pytube
|
18 |
-
omit =
|
19 |
pytube/compat.py
|
20 |
-
|
|
|
3 |
tag = True
|
4 |
current_version = 9.0.3
|
5 |
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(\-(?P<release>[a-z]+))?
|
6 |
+
serialize =
|
7 |
{major}.{minor}.{patch}
|
8 |
|
9 |
[metadata]
|
|
|
15 |
|
16 |
[coverage:run]
|
17 |
source = pytube
|
18 |
+
omit =
|
19 |
pytube/compat.py
|
|
tests/requirements.txt
DELETED
@@ -1,10 +0,0 @@
|
|
1 |
-
bumpversion==0.5.3
|
2 |
-
coveralls==1.0
|
3 |
-
flake8==3.3.0
|
4 |
-
mock==1.3.0
|
5 |
-
pre-commit==0.15.0
|
6 |
-
pytest==3.1.3
|
7 |
-
pytest-cov==2.4.0
|
8 |
-
pytest-mock==1.6.3
|
9 |
-
sphinx==1.6.4
|
10 |
-
sphinx-rtd-theme==0.2.4
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/test_playlist.py
CHANGED
@@ -4,33 +4,40 @@ from pytube import playlist
|
|
4 |
|
5 |
def test_construct():
|
6 |
ob = playlist.Playlist(
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
|
|
12 |
|
13 |
assert ob.construct_playlist_url() == expected
|
14 |
|
15 |
|
16 |
def test_link_parse():
|
17 |
ob = playlist.Playlist(
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
|
|
|
|
|
|
24 |
assert ob.parse_links() == expected
|
25 |
|
26 |
|
27 |
def test_populate():
|
28 |
ob = playlist.Playlist(
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
|
|
|
|
|
|
34 |
|
35 |
ob.populate_video_urls()
|
36 |
assert ob.video_urls == expected
|
@@ -38,6 +45,7 @@ def test_populate():
|
|
38 |
|
39 |
def test_download():
|
40 |
ob = playlist.Playlist(
|
41 |
-
|
42 |
-
|
|
|
43 |
ob.download_all()
|
|
|
4 |
|
5 |
def test_construct():
|
6 |
ob = playlist.Playlist(
|
7 |
+
'https://www.youtube.com/watch?v=m5q2GCsteQs&list='
|
8 |
+
'PL525f8ds9RvsXDl44X6Wwh9t3fCzFNApw',
|
9 |
+
)
|
10 |
+
expected = 'https://www.youtube.com/' \
|
11 |
+
'playlist?list=' \
|
12 |
+
'PL525f8ds9RvsXDl44X6Wwh9t3fCzFNApw'
|
13 |
|
14 |
assert ob.construct_playlist_url() == expected
|
15 |
|
16 |
|
17 |
def test_link_parse():
|
18 |
ob = playlist.Playlist(
|
19 |
+
'https://www.youtube.com/watch?v=m5q2GCsteQs&list='
|
20 |
+
'PL525f8ds9RvsXDl44X6Wwh9t3fCzFNApw',
|
21 |
+
)
|
22 |
+
|
23 |
+
expected = [
|
24 |
+
'/watch?v=m5q2GCsteQs',
|
25 |
+
'/watch?v=5YK63cXyJ2Q',
|
26 |
+
'/watch?v=Rzt4rUPFYD4',
|
27 |
+
]
|
28 |
assert ob.parse_links() == expected
|
29 |
|
30 |
|
31 |
def test_populate():
|
32 |
ob = playlist.Playlist(
|
33 |
+
'https://www.youtube.com/watch?v=m5q2GCsteQs&list='
|
34 |
+
'PL525f8ds9RvsXDl44X6Wwh9t3fCzFNApw',
|
35 |
+
)
|
36 |
+
expected = [
|
37 |
+
'https://www.youtube.com/watch?v=m5q2GCsteQs',
|
38 |
+
'https://www.youtube.com/watch?v=5YK63cXyJ2Q',
|
39 |
+
'https://www.youtube.com/watch?v=Rzt4rUPFYD4',
|
40 |
+
]
|
41 |
|
42 |
ob.populate_video_urls()
|
43 |
assert ob.video_urls == expected
|
|
|
45 |
|
46 |
def test_download():
|
47 |
ob = playlist.Playlist(
|
48 |
+
'https://www.youtube.com/watch?v=lByG_AgKS9k&list='
|
49 |
+
'PL525f8ds9RvuerPZ3bZygmNiYw2sP4BDk',
|
50 |
+
)
|
51 |
ob.download_all()
|