[ie] Resolve `language` to ISO639-2 for ISM formats (#11359)
[yt-dlp3.git] / pyproject.toml
blob55bd55bb9e61cf9cc31bd63d9ccde43f7b0f4929
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.7.0",
81 test = [
82     "pytest~=8.1",
83     "pytest-rerunfailures~=14.0",
85 pyinstaller = [
86     "pyinstaller>=6.10.0",  # Windows temp cleanup fixed in 6.10.0
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     "UP036",   # outdated-version-block
190     "B006",    # mutable-argument-default
191     "B008",    # function-call-in-default-argument
192     "B011",    # assert-false
193     "B017",    # assert-raises-exception
194     "B023",    # function-uses-loop-variable (false positives)
195     "B028",    # no-explicit-stacklevel
196     "B904",    # raise-without-from-inside-except
197     "C401",    # unnecessary-generator-set
198     "C402",    # unnecessary-generator-dict
199     "PIE790",  # unnecessary-placeholder
200     "SIM102",  # collapsible-if
201     "SIM108",  # if-else-block-instead-of-if-exp
202     "SIM112",  # uncapitalized-environment-variables
203     "SIM113",  # enumerate-for-loop
204     "SIM114",  # if-with-same-arms
205     "SIM115",  # open-file-with-context-handler
206     "SIM117",  # multiple-with-statements
207     "SIM223",  # expr-and-false
208     "SIM300",  # yoda-conditions
209     "TD001",   # invalid-todo-tag
210     "TD002",   # missing-todo-author
211     "TD003",   # missing-todo-link
212     "PLE0604", # invalid-all-object (false positives)
213     "PLE0643", # potential-index-error (false positives)
214     "PLW0603", # global-statement
215     "PLW1510", # subprocess-run-without-check
216     "PLW2901", # redefined-loop-name
217     "RUF001",  # ambiguous-unicode-character-string
218     "RUF012",  # mutable-class-default
219     "RUF100",  # unused-noqa (flake8 has slightly different behavior)
221 select = [
222     "E",      # pycodestyle Error
223     "W",      # pycodestyle Warning
224     "F",      # Pyflakes
225     "I",      # isort
226     "Q",      # flake8-quotes
227     "N803",   # invalid-argument-name
228     "N804",   # invalid-first-argument-name-for-class-method
229     "UP",     # pyupgrade
230     "B",      # flake8-bugbear
231     "A",      # flake8-builtins
232     "COM",    # flake8-commas
233     "C4",     # flake8-comprehensions
234     "FA",     # flake8-future-annotations
235     "ISC",    # flake8-implicit-str-concat
236     "ICN003", # banned-import-from
237     "PIE",    # flake8-pie
238     "T20",    # flake8-print
239     "RSE",    # flake8-raise
240     "RET504", # unnecessary-assign
241     "SIM",    # flake8-simplify
242     "TID251", # banned-api
243     "TD",     # flake8-todos
244     "PLC",    # Pylint Convention
245     "PLE",    # Pylint Error
246     "PLW",    # Pylint Warning
247     "RUF",    # Ruff-specific rules
250 [tool.ruff.lint.per-file-ignores]
251 "devscripts/lazy_load_template.py" = [
252     "F401",   # unused-import
254 "!yt_dlp/extractor/**.py" = [
255     "I",      # isort
256     "ICN003", # banned-import-from
257     "T20",    # flake8-print
258     "A002",   # builtin-argument-shadowing
259     "C408",   # unnecessary-collection-call
261 "yt_dlp/jsinterp.py" = [
262     "UP031",  # printf-string-formatting
265 [tool.ruff.lint.isort]
266 known-first-party = [
267     "bundle",
268     "devscripts",
269     "test",
271 relative-imports-order = "closest-to-furthest"
273 [tool.ruff.lint.flake8-quotes]
274 docstring-quotes = "double"
275 multiline-quotes = "single"
276 inline-quotes = "single"
277 avoid-escape = false
279 [tool.ruff.lint.pep8-naming]
280 classmethod-decorators = [
281     "yt_dlp.utils.classproperty",
284 [tool.ruff.lint.flake8-import-conventions]
285 banned-from = [
286     "base64",
287     "datetime",
288     "functools",
289     "glob",
290     "hashlib",
291     "itertools",
292     "json",
293     "math",
294     "os",
295     "pathlib",
296     "random",
297     "re",
298     "string",
299     "sys",
300     "time",
301     "urllib.parse",
302     "uuid",
303     "xml",
306 [tool.ruff.lint.flake8-tidy-imports.banned-api]
307 "yt_dlp.compat.compat_str".msg = "Use `str` instead."
308 "yt_dlp.compat.compat_b64decode".msg = "Use `base64.b64decode` instead."
309 "yt_dlp.compat.compat_urlparse".msg = "Use `urllib.parse` instead."
310 "yt_dlp.compat.compat_parse_qs".msg = "Use `urllib.parse.parse_qs` instead."
311 "yt_dlp.compat.compat_urllib_parse_unquote".msg = "Use `urllib.parse.unquote` instead."
312 "yt_dlp.compat.compat_urllib_parse_urlencode".msg = "Use `urllib.parse.urlencode` instead."
313 "yt_dlp.compat.compat_urllib_parse_urlparse".msg = "Use `urllib.parse.urlparse` instead."
314 "yt_dlp.compat.compat_shlex_quote".msg = "Use `yt_dlp.utils.shell_quote` instead."
315 "yt_dlp.utils.error_to_compat_str".msg = "Use `str` instead."
317 [tool.autopep8]
318 max_line_length = 120
319 recursive = true
320 exit-code = true
321 jobs = 0
322 select = [
323     "E101",
324     "E112",
325     "E113",
326     "E115",
327     "E116",
328     "E117",
329     "E121",
330     "E122",
331     "E123",
332     "E124",
333     "E125",
334     "E126",
335     "E127",
336     "E128",
337     "E129",
338     "E131",
339     "E201",
340     "E202",
341     "E203",
342     "E211",
343     "E221",
344     "E222",
345     "E223",
346     "E224",
347     "E225",
348     "E226",
349     "E227",
350     "E228",
351     "E231",
352     "E241",
353     "E242",
354     "E251",
355     "E252",
356     "E261",
357     "E262",
358     "E265",
359     "E266",
360     "E271",
361     "E272",
362     "E273",
363     "E274",
364     "E275",
365     "E301",
366     "E302",
367     "E303",
368     "E304",
369     "E305",
370     "E306",
371     "E502",
372     "E701",
373     "E702",
374     "E704",
375     "W391",
376     "W504",
379 [tool.pytest.ini_options]
380 addopts = "-ra -v --strict-markers"
381 markers = [
382     "download",