Release 2024.12.06
[yt-dlp.git] / pyproject.toml
blob96e2d669a4352ff762c9be23c945310339db78eb
1 [build-system]
2 requires = ["hatchling"]
3 build-backend = "hatchling.build"
5 [project]
6 name = "yt-dlp"
7 maintainers = [
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"
15 readme = "README.md"
16 requires-python = ">=3.9"
17 keywords = [
18     "youtube-dl",
19     "video-downloader",
20     "youtube-downloader",
21     "sponsorblock",
22     "youtube-dlc",
23     "yt-dlp",
25 license = {file = "LICENSE"}
26 classifiers = [
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.9",
33     "Programming Language :: Python :: 3.10",
34     "Programming Language :: Python :: 3.11",
35     "Programming Language :: Python :: 3.12",
36     "Programming Language :: Python :: 3.13",
37     "Programming Language :: Python :: Implementation",
38     "Programming Language :: Python :: Implementation :: CPython",
39     "Programming Language :: Python :: Implementation :: PyPy",
40     "License :: OSI Approved :: The Unlicense (Unlicense)",
41     "Operating System :: OS Independent",
43 dynamic = ["version"]
44 dependencies = []
46 [project.optional-dependencies]
47 default = [
48     "brotli; implementation_name=='cpython'",
49     "brotlicffi; implementation_name!='cpython'",
50     "certifi",
51     "mutagen",
52     "pycryptodomex",
53     "requests>=2.32.2,<3",
54     "urllib3>=1.26.17,<3",
55     "websockets>=13.0",
57 curl-cffi = [
58     "curl-cffi==0.5.10; os_name=='nt' and implementation_name=='cpython'",
59     "curl-cffi>=0.5.10,!=0.6.*,<0.7.2; os_name!='nt' and implementation_name=='cpython'",
61 secretstorage = [
62     "cffi",
63     "secretstorage",
65 build = [
66     "build",
67     "hatchling",
68     "pip",
69     "setuptools>=71.0.2",  # 71.0.0 broke pyinstaller
70     "wheel",
72 dev = [
73     "pre-commit",
74     "yt-dlp[static-analysis]",
75     "yt-dlp[test]",
77 static-analysis = [
78     "autopep8~=2.0",
79     "ruff~=0.8.0",
81 test = [
82     "pytest~=8.1",
83     "pytest-rerunfailures~=14.0",
85 pyinstaller = [
86     "pyinstaller>=6.11.1",  # Windows temp cleanup fixed in 6.11.1
89 [project.urls]
90 Documentation = "https://github.com/yt-dlp/yt-dlp#readme"
91 Repository = "https://github.com/yt-dlp/yt-dlp"
92 Tracker = "https://github.com/yt-dlp/yt-dlp/issues"
93 Funding = "https://github.com/yt-dlp/yt-dlp/blob/master/Collaborators.md#collaborators"
95 [project.scripts]
96 yt-dlp = "yt_dlp:main"
98 [project.entry-points.pyinstaller40]
99 hook-dirs = "yt_dlp.__pyinstaller:get_hook_dirs"
101 [tool.hatch.build.targets.sdist]
102 include = [
103     "/yt_dlp",
104     "/devscripts",
105     "/test",
106     "/.gitignore",  # included by default, needed for auto-excludes
107     "/Changelog.md",
108     "/LICENSE",  # included as license
109     "/pyproject.toml",  # included by default
110     "/README.md",  # included as readme
111     "/setup.cfg",
112     "/supportedsites.md",
114 artifacts = [
115     "/yt_dlp/extractor/lazy_extractors.py",
116     "/completions",
117     "/AUTHORS",  # included by default
118     "/README.txt",
119     "/yt-dlp.1",
122 [tool.hatch.build.targets.wheel]
123 packages = ["yt_dlp"]
124 artifacts = ["/yt_dlp/extractor/lazy_extractors.py"]
126 [tool.hatch.build.targets.wheel.shared-data]
127 "completions/bash/yt-dlp" = "share/bash-completion/completions/yt-dlp"
128 "completions/zsh/_yt-dlp" = "share/zsh/site-functions/_yt-dlp"
129 "completions/fish/yt-dlp.fish" = "share/fish/vendor_completions.d/yt-dlp.fish"
130 "README.txt" = "share/doc/yt_dlp/README.txt"
131 "yt-dlp.1" = "share/man/man1/yt-dlp.1"
133 [tool.hatch.version]
134 path = "yt_dlp/version.py"
135 pattern = "_pkg_version = '(?P<version>[^']+)'"
137 [tool.hatch.envs.default]
138 features = ["curl-cffi", "default"]
139 dependencies = ["pre-commit"]
140 path = ".venv"
141 installer = "uv"
143 [tool.hatch.envs.default.scripts]
144 setup = "pre-commit install --config .pre-commit-hatch.yaml"
145 yt-dlp = "python -Werror -Xdev -m yt_dlp {args}"
147 [tool.hatch.envs.hatch-static-analysis]
148 detached = true
149 features = ["static-analysis"]
150 dependencies = []  # override hatch ruff version
151 config-path = "pyproject.toml"
153 [tool.hatch.envs.hatch-static-analysis.scripts]
154 format-check = "autopep8 --diff {args:.}"
155 format-fix = "autopep8 --in-place {args:.}"
156 lint-check = "ruff check {args:.}"
157 lint-fix = "ruff check --fix {args:.}"
159 [tool.hatch.envs.hatch-test]
160 features = ["test"]
161 dependencies = [
162     "pytest-randomly~=3.15",
163     "pytest-xdist[psutil]~=3.5",
166 [tool.hatch.envs.hatch-test.scripts]
167 run = "python -m devscripts.run_tests {args}"
168 run-cov = "echo Code coverage not implemented && exit 1"
170 [[tool.hatch.envs.hatch-test.matrix]]
171 python = [
172     "3.9",
173     "3.10",
174     "3.11",
175     "3.12",
176     "3.13",
177     "pypy3.10",
180 [tool.ruff]
181 line-length = 120
183 [tool.ruff.lint]
184 ignore = [
185     "E402",    # module-import-not-at-top-of-file
186     "E501",    # line-too-long
187     "E731",    # lambda-assignment
188     "E741",    # ambiguous-variable-name
189     "UP031",   # printf-string-formatting
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)
222 select = [
223     "E",      # pycodestyle Error
224     "W",      # pycodestyle Warning
225     "F",      # Pyflakes
226     "I",      # isort
227     "Q",      # flake8-quotes
228     "N803",   # invalid-argument-name
229     "N804",   # invalid-first-argument-name-for-class-method
230     "UP",     # pyupgrade
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
238     "PIE",    # flake8-pie
239     "T20",    # flake8-print
240     "RSE",    # flake8-raise
241     "RET504", # unnecessary-assign
242     "SIM",    # flake8-simplify
243     "TID251", # banned-api
244     "TD",     # flake8-todos
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" = [
256     "I",      # isort
257     "ICN003", # banned-import-from
258     "T20",    # flake8-print
259     "A002",   # builtin-argument-shadowing
260     "C408",   # unnecessary-collection-call
263 [tool.ruff.lint.isort]
264 known-first-party = [
265     "bundle",
266     "devscripts",
267     "test",
269 relative-imports-order = "closest-to-furthest"
271 [tool.ruff.lint.flake8-quotes]
272 docstring-quotes = "double"
273 multiline-quotes = "single"
274 inline-quotes = "single"
275 avoid-escape = false
277 [tool.ruff.lint.pep8-naming]
278 classmethod-decorators = [
279     "yt_dlp.utils.classproperty",
282 [tool.ruff.lint.flake8-import-conventions]
283 banned-from = [
284     "base64",
285     "datetime",
286     "functools",
287     "glob",
288     "hashlib",
289     "itertools",
290     "json",
291     "math",
292     "os",
293     "pathlib",
294     "random",
295     "re",
296     "string",
297     "sys",
298     "time",
299     "urllib.parse",
300     "uuid",
301     "xml",
304 [tool.ruff.lint.flake8-tidy-imports.banned-api]
305 "yt_dlp.compat.compat_str".msg = "Use `str` instead."
306 "yt_dlp.compat.compat_b64decode".msg = "Use `base64.b64decode` instead."
307 "yt_dlp.compat.compat_urlparse".msg = "Use `urllib.parse` instead."
308 "yt_dlp.compat.compat_parse_qs".msg = "Use `urllib.parse.parse_qs` instead."
309 "yt_dlp.compat.compat_urllib_parse_unquote".msg = "Use `urllib.parse.unquote` instead."
310 "yt_dlp.compat.compat_urllib_parse_urlencode".msg = "Use `urllib.parse.urlencode` instead."
311 "yt_dlp.compat.compat_urllib_parse_urlparse".msg = "Use `urllib.parse.urlparse` instead."
312 "yt_dlp.compat.compat_shlex_quote".msg = "Use `yt_dlp.utils.shell_quote` instead."
313 "yt_dlp.utils.error_to_compat_str".msg = "Use `str` instead."
314 "yt_dlp.utils.bytes_to_intlist".msg = "Use `list` instead."
315 "yt_dlp.utils.intlist_to_bytes".msg = "Use `bytes` instead."
316 "yt_dlp.utils.decodeArgument".msg = "Do not use"
317 "yt_dlp.utils.decodeFilename".msg = "Do not use"
318 "yt_dlp.utils.encodeFilename".msg = "Do not use"
319 "yt_dlp.compat.compat_os_name".msg = "Use `os.name` instead."
320 "yt_dlp.compat.compat_realpath".msg = "Use `os.path.realpath` instead."
321 "yt_dlp.compat.functools".msg = "Use `functools` instead."
322 "yt_dlp.utils.decodeOption".msg = "Do not use"
323 "yt_dlp.utils.compiled_regex_type".msg = "Use `re.Pattern` instead."
325 [tool.autopep8]
326 max_line_length = 120
327 recursive = true
328 exit-code = true
329 jobs = 0
330 select = [
331     "E101",
332     "E112",
333     "E113",
334     "E115",
335     "E116",
336     "E117",
337     "E121",
338     "E122",
339     "E123",
340     "E124",
341     "E125",
342     "E126",
343     "E127",
344     "E128",
345     "E129",
346     "E131",
347     "E201",
348     "E202",
349     "E203",
350     "E211",
351     "E221",
352     "E222",
353     "E223",
354     "E224",
355     "E225",
356     "E226",
357     "E227",
358     "E228",
359     "E231",
360     "E241",
361     "E242",
362     "E251",
363     "E252",
364     "E261",
365     "E262",
366     "E265",
367     "E266",
368     "E271",
369     "E272",
370     "E273",
371     "E274",
372     "E275",
373     "E301",
374     "E302",
375     "E303",
376     "E304",
377     "E305",
378     "E306",
379     "E502",
380     "E701",
381     "E702",
382     "E704",
383     "W391",
384     "W504",
387 [tool.pytest.ini_options]
388 addopts = "-ra -v --strict-markers"
389 markers = [
390     "download",