2 requires = ["hatchling"]
3 build-backend = "hatchling.build"
8 {name = "pukkandan", email = "pukkandan.ytdlp@gmail.com"},
9 {name = "Grub4K", email = "contact@grub4k.xyz"},
10 {name = "bashonly", email = "bashonly@protonmail.com"},
11 {name = "coletdjnz", email = "coletdjnz@protonmail.com"},
12 {name = "sepro", email = "sepro@sepr0.com"},
14 description = "A feature-rich command-line audio/video downloader"
16 requires-python = ">=3.8"
25 license = {file = "LICENSE"}
27 "Topic :: Multimedia :: Video",
28 "Development Status :: 5 - Production/Stable",
29 "Environment :: Console",
30 "Programming Language :: Python",
31 "Programming Language :: Python :: 3 :: Only",
32 "Programming Language :: Python :: 3.8",
33 "Programming Language :: Python :: 3.9",
34 "Programming Language :: Python :: 3.10",
35 "Programming Language :: Python :: 3.11",
36 "Programming Language :: Python :: 3.12",
37 "Programming Language :: Python :: 3.13",
38 "Programming Language :: Python :: Implementation",
39 "Programming Language :: Python :: Implementation :: CPython",
40 "Programming Language :: Python :: Implementation :: PyPy",
41 "License :: OSI Approved :: The Unlicense (Unlicense)",
42 "Operating System :: OS Independent",
46 "brotli; implementation_name=='cpython'",
47 "brotlicffi; implementation_name!='cpython'",
51 "requests>=2.32.2,<3",
52 "urllib3>=1.26.17,<3",
56 [project.optional-dependencies]
59 "curl-cffi==0.5.10; os_name=='nt' and implementation_name=='cpython'",
60 "curl-cffi>=0.5.10,!=0.6.*,<0.7.2; os_name!='nt' and implementation_name=='cpython'",
70 "setuptools>=71.0.2", # 71.0.0 broke pyinstaller
75 "yt-dlp[static-analysis]",
84 "pytest-rerunfailures~=14.0",
87 "pyinstaller>=6.10.0", # Windows temp cleanup fixed in 6.10.0
91 Documentation = "https://github.com/yt-dlp/yt-dlp#readme"
92 Repository = "https://github.com/yt-dlp/yt-dlp"
93 Tracker = "https://github.com/yt-dlp/yt-dlp/issues"
94 Funding = "https://github.com/yt-dlp/yt-dlp/blob/master/Collaborators.md#collaborators"
97 yt-dlp = "yt_dlp:main"
99 [project.entry-points.pyinstaller40]
100 hook-dirs = "yt_dlp.__pyinstaller:get_hook_dirs"
102 [tool.hatch.build.targets.sdist]
107 "/.gitignore", # included by default, needed for auto-excludes
109 "/LICENSE", # included as license
110 "/pyproject.toml", # included by default
111 "/README.md", # included as readme
113 "/supportedsites.md",
116 "/yt_dlp/extractor/lazy_extractors.py",
118 "/AUTHORS", # included by default
123 [tool.hatch.build.targets.wheel]
124 packages = ["yt_dlp"]
125 artifacts = ["/yt_dlp/extractor/lazy_extractors.py"]
127 [tool.hatch.build.targets.wheel.shared-data]
128 "completions/bash/yt-dlp" = "share/bash-completion/completions/yt-dlp"
129 "completions/zsh/_yt-dlp" = "share/zsh/site-functions/_yt-dlp"
130 "completions/fish/yt-dlp.fish" = "share/fish/vendor_completions.d/yt-dlp.fish"
131 "README.txt" = "share/doc/yt_dlp/README.txt"
132 "yt-dlp.1" = "share/man/man1/yt-dlp.1"
135 path = "yt_dlp/version.py"
136 pattern = "_pkg_version = '(?P<version>[^']+)'"
138 [tool.hatch.envs.default]
139 features = ["curl-cffi", "default"]
140 dependencies = ["pre-commit"]
144 [tool.hatch.envs.default.scripts]
145 setup = "pre-commit install --config .pre-commit-hatch.yaml"
146 yt-dlp = "python -Werror -Xdev -m yt_dlp {args}"
148 [tool.hatch.envs.hatch-static-analysis]
150 features = ["static-analysis"]
151 dependencies = [] # override hatch ruff version
152 config-path = "pyproject.toml"
154 [tool.hatch.envs.hatch-static-analysis.scripts]
155 format-check = "autopep8 --diff {args:.}"
156 format-fix = "autopep8 --in-place {args:.}"
157 lint-check = "ruff check {args:.}"
158 lint-fix = "ruff check --fix {args:.}"
160 [tool.hatch.envs.hatch-test]
163 "pytest-randomly~=3.15",
164 "pytest-xdist[psutil]~=3.5",
167 [tool.hatch.envs.hatch-test.scripts]
168 run = "python -m devscripts.run_tests {args}"
169 run-cov = "echo Code coverage not implemented && exit 1"
171 [[tool.hatch.envs.hatch-test.matrix]]
186 "E402", # module-import-not-at-top-of-file
187 "E501", # line-too-long
188 "E731", # lambda-assignment
189 "E741", # ambiguous-variable-name
190 "UP036", # outdated-version-block
191 "B006", # mutable-argument-default
192 "B008", # function-call-in-default-argument
193 "B011", # assert-false
194 "B017", # assert-raises-exception
195 "B023", # function-uses-loop-variable (false positives)
196 "B028", # no-explicit-stacklevel
197 "B904", # raise-without-from-inside-except
198 "C401", # unnecessary-generator-set
199 "C402", # unnecessary-generator-dict
200 "PIE790", # unnecessary-placeholder
201 "SIM102", # collapsible-if
202 "SIM108", # if-else-block-instead-of-if-exp
203 "SIM112", # uncapitalized-environment-variables
204 "SIM113", # enumerate-for-loop
205 "SIM114", # if-with-same-arms
206 "SIM115", # open-file-with-context-handler
207 "SIM117", # multiple-with-statements
208 "SIM223", # expr-and-false
209 "SIM300", # yoda-conditions
210 "TD001", # invalid-todo-tag
211 "TD002", # missing-todo-author
212 "TD003", # missing-todo-link
213 "PLE0604", # invalid-all-object (false positives)
214 "PLE0643", # potential-index-error (false positives)
215 "PLW0603", # global-statement
216 "PLW1510", # subprocess-run-without-check
217 "PLW2901", # redefined-loop-name
218 "RUF001", # ambiguous-unicode-character-string
219 "RUF012", # mutable-class-default
220 "RUF100", # unused-noqa (flake8 has slightly different behavior)
223 "E", # pycodestyle Error
224 "W", # pycodestyle Warning
228 "N803", # invalid-argument-name
229 "N804", # invalid-first-argument-name-for-class-method
231 "B", # flake8-bugbear
232 "A", # flake8-builtins
233 "COM", # flake8-commas
234 "C4", # flake8-comprehensions
235 "FA", # flake8-future-annotations
236 "ISC", # flake8-implicit-str-concat
237 "ICN003", # banned-import-from
239 "T20", # flake8-print
240 "RSE", # flake8-raise
241 "RET504", # unnecessary-assign
242 "SIM", # flake8-simplify
243 "TID251", # banned-api
245 "PLC", # Pylint Convention
246 "PLE", # Pylint Error
247 "PLW", # Pylint Warning
248 "RUF", # Ruff-specific rules
251 [tool.ruff.lint.per-file-ignores]
252 "devscripts/lazy_load_template.py" = [
253 "F401", # unused-import
255 "!yt_dlp/extractor/**.py" = [
257 "ICN003", # banned-import-from
258 "T20", # flake8-print
259 "A002", # builtin-argument-shadowing
260 "C408", # unnecessary-collection-call
262 "yt_dlp/jsinterp.py" = [
263 "UP031", # printf-string-formatting
266 [tool.ruff.lint.isort]
267 known-first-party = [
272 relative-imports-order = "closest-to-furthest"
274 [tool.ruff.lint.flake8-quotes]
275 docstring-quotes = "double"
276 multiline-quotes = "single"
277 inline-quotes = "single"
280 [tool.ruff.lint.pep8-naming]
281 classmethod-decorators = [
282 "yt_dlp.utils.classproperty",
285 [tool.ruff.lint.flake8-import-conventions]
307 [tool.ruff.lint.flake8-tidy-imports.banned-api]
308 "yt_dlp.compat.compat_str".msg = "Use `str` instead."
309 "yt_dlp.compat.compat_b64decode".msg = "Use `base64.b64decode` instead."
310 "yt_dlp.compat.compat_urlparse".msg = "Use `urllib.parse` instead."
311 "yt_dlp.compat.compat_parse_qs".msg = "Use `urllib.parse.parse_qs` instead."
312 "yt_dlp.compat.compat_urllib_parse_unquote".msg = "Use `urllib.parse.unquote` instead."
313 "yt_dlp.compat.compat_urllib_parse_urlencode".msg = "Use `urllib.parse.urlencode` instead."
314 "yt_dlp.compat.compat_urllib_parse_urlparse".msg = "Use `urllib.parse.urlparse` instead."
315 "yt_dlp.compat.compat_shlex_quote".msg = "Use `yt_dlp.utils.shell_quote` instead."
316 "yt_dlp.utils.error_to_compat_str".msg = "Use `str` instead."
319 max_line_length = 120
380 [tool.pytest.ini_options]
381 addopts = "-ra -v --strict-markers"