pyproject.toml 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. [build-system]
  2. requires = ["hatchling"]
  3. build-backend = "hatchling.build"
  4. [project]
  5. name = "yt-dlp"
  6. maintainers = [
  7. {name = "pukkandan", email = "pukkandan.ytdlp@gmail.com"},
  8. {name = "Grub4K", email = "contact@grub4k.xyz"},
  9. {name = "bashonly", email = "bashonly@protonmail.com"},
  10. {name = "coletdjnz", email = "coletdjnz@protonmail.com"},
  11. {name = "sepro", email = "sepro@sepr0.com"},
  12. ]
  13. description = "A feature-rich command-line audio/video downloader"
  14. readme = "README.md"
  15. requires-python = ">=3.9"
  16. keywords = [
  17. "youtube-dl",
  18. "video-downloader",
  19. "youtube-downloader",
  20. "sponsorblock",
  21. "youtube-dlc",
  22. "yt-dlp",
  23. ]
  24. license = {file = "LICENSE"}
  25. classifiers = [
  26. "Topic :: Multimedia :: Video",
  27. "Development Status :: 5 - Production/Stable",
  28. "Environment :: Console",
  29. "Programming Language :: Python",
  30. "Programming Language :: Python :: 3 :: Only",
  31. "Programming Language :: Python :: 3.9",
  32. "Programming Language :: Python :: 3.10",
  33. "Programming Language :: Python :: 3.11",
  34. "Programming Language :: Python :: 3.12",
  35. "Programming Language :: Python :: 3.13",
  36. "Programming Language :: Python :: Implementation",
  37. "Programming Language :: Python :: Implementation :: CPython",
  38. "Programming Language :: Python :: Implementation :: PyPy",
  39. "License :: OSI Approved :: The Unlicense (Unlicense)",
  40. "Operating System :: OS Independent",
  41. ]
  42. dynamic = ["version"]
  43. dependencies = []
  44. [project.optional-dependencies]
  45. default = [
  46. "brotli; implementation_name=='cpython'",
  47. "brotlicffi; implementation_name!='cpython'",
  48. "certifi",
  49. "mutagen",
  50. "pycryptodomex",
  51. "requests>=2.32.2,<3",
  52. "urllib3>=1.26.17,<3",
  53. "websockets>=13.0,<14",
  54. ]
  55. curl-cffi = [
  56. "curl-cffi==0.5.10; os_name=='nt' and implementation_name=='cpython'",
  57. "curl-cffi>=0.5.10,!=0.6.*,<0.7.2; os_name!='nt' and implementation_name=='cpython'",
  58. ]
  59. secretstorage = [
  60. "cffi",
  61. "secretstorage",
  62. ]
  63. build = [
  64. "build",
  65. "hatchling",
  66. "pip",
  67. "setuptools>=71.0.2", # 71.0.0 broke pyinstaller
  68. "wheel",
  69. ]
  70. dev = [
  71. "pre-commit",
  72. "yt-dlp[static-analysis]",
  73. "yt-dlp[test]",
  74. ]
  75. static-analysis = [
  76. "autopep8~=2.0",
  77. "ruff~=0.7.0",
  78. ]
  79. test = [
  80. "pytest~=8.1",
  81. "pytest-rerunfailures~=14.0",
  82. ]
  83. pyinstaller = [
  84. "pyinstaller>=6.11.1", # Windows temp cleanup fixed in 6.11.1
  85. ]
  86. [project.urls]
  87. Documentation = "https://github.com/yt-dlp/yt-dlp#readme"
  88. Repository = "https://github.com/yt-dlp/yt-dlp"
  89. Tracker = "https://github.com/yt-dlp/yt-dlp/issues"
  90. Funding = "https://github.com/yt-dlp/yt-dlp/blob/master/Collaborators.md#collaborators"
  91. [project.scripts]
  92. yt-dlp = "yt_dlp:main"
  93. [project.entry-points.pyinstaller40]
  94. hook-dirs = "yt_dlp.__pyinstaller:get_hook_dirs"
  95. [tool.hatch.build.targets.sdist]
  96. include = [
  97. "/yt_dlp",
  98. "/devscripts",
  99. "/test",
  100. "/.gitignore", # included by default, needed for auto-excludes
  101. "/Changelog.md",
  102. "/LICENSE", # included as license
  103. "/pyproject.toml", # included by default
  104. "/README.md", # included as readme
  105. "/setup.cfg",
  106. "/supportedsites.md",
  107. ]
  108. artifacts = [
  109. "/yt_dlp/extractor/lazy_extractors.py",
  110. "/completions",
  111. "/AUTHORS", # included by default
  112. "/README.txt",
  113. "/yt-dlp.1",
  114. ]
  115. [tool.hatch.build.targets.wheel]
  116. packages = ["yt_dlp"]
  117. artifacts = ["/yt_dlp/extractor/lazy_extractors.py"]
  118. [tool.hatch.build.targets.wheel.shared-data]
  119. "completions/bash/yt-dlp" = "share/bash-completion/completions/yt-dlp"
  120. "completions/zsh/_yt-dlp" = "share/zsh/site-functions/_yt-dlp"
  121. "completions/fish/yt-dlp.fish" = "share/fish/vendor_completions.d/yt-dlp.fish"
  122. "README.txt" = "share/doc/yt_dlp/README.txt"
  123. "yt-dlp.1" = "share/man/man1/yt-dlp.1"
  124. [tool.hatch.version]
  125. path = "yt_dlp/version.py"
  126. pattern = "_pkg_version = '(?P<version>[^']+)'"
  127. [tool.hatch.envs.default]
  128. features = ["curl-cffi", "default"]
  129. dependencies = ["pre-commit"]
  130. path = ".venv"
  131. installer = "uv"
  132. [tool.hatch.envs.default.scripts]
  133. setup = "pre-commit install --config .pre-commit-hatch.yaml"
  134. yt-dlp = "python -Werror -Xdev -m yt_dlp {args}"
  135. [tool.hatch.envs.hatch-static-analysis]
  136. detached = true
  137. features = ["static-analysis"]
  138. dependencies = [] # override hatch ruff version
  139. config-path = "pyproject.toml"
  140. [tool.hatch.envs.hatch-static-analysis.scripts]
  141. format-check = "autopep8 --diff {args:.}"
  142. format-fix = "autopep8 --in-place {args:.}"
  143. lint-check = "ruff check {args:.}"
  144. lint-fix = "ruff check --fix {args:.}"
  145. [tool.hatch.envs.hatch-test]
  146. features = ["test"]
  147. dependencies = [
  148. "pytest-randomly~=3.15",
  149. "pytest-xdist[psutil]~=3.5",
  150. ]
  151. [tool.hatch.envs.hatch-test.scripts]
  152. run = "python -m devscripts.run_tests {args}"
  153. run-cov = "echo Code coverage not implemented && exit 1"
  154. [[tool.hatch.envs.hatch-test.matrix]]
  155. python = [
  156. "3.9",
  157. "3.10",
  158. "3.11",
  159. "3.12",
  160. "3.13",
  161. "pypy3.10",
  162. ]
  163. [tool.ruff]
  164. line-length = 120
  165. [tool.ruff.lint]
  166. ignore = [
  167. "E402", # module-import-not-at-top-of-file
  168. "E501", # line-too-long
  169. "E731", # lambda-assignment
  170. "E741", # ambiguous-variable-name
  171. "UP036", # outdated-version-block
  172. "B006", # mutable-argument-default
  173. "B008", # function-call-in-default-argument
  174. "B011", # assert-false
  175. "B017", # assert-raises-exception
  176. "B023", # function-uses-loop-variable (false positives)
  177. "B028", # no-explicit-stacklevel
  178. "B904", # raise-without-from-inside-except
  179. "C401", # unnecessary-generator-set
  180. "C402", # unnecessary-generator-dict
  181. "PIE790", # unnecessary-placeholder
  182. "SIM102", # collapsible-if
  183. "SIM108", # if-else-block-instead-of-if-exp
  184. "SIM112", # uncapitalized-environment-variables
  185. "SIM113", # enumerate-for-loop
  186. "SIM114", # if-with-same-arms
  187. "SIM115", # open-file-with-context-handler
  188. "SIM117", # multiple-with-statements
  189. "SIM223", # expr-and-false
  190. "SIM300", # yoda-conditions
  191. "TD001", # invalid-todo-tag
  192. "TD002", # missing-todo-author
  193. "TD003", # missing-todo-link
  194. "PLE0604", # invalid-all-object (false positives)
  195. "PLE0643", # potential-index-error (false positives)
  196. "PLW0603", # global-statement
  197. "PLW1510", # subprocess-run-without-check
  198. "PLW2901", # redefined-loop-name
  199. "RUF001", # ambiguous-unicode-character-string
  200. "RUF012", # mutable-class-default
  201. "RUF100", # unused-noqa (flake8 has slightly different behavior)
  202. ]
  203. select = [
  204. "E", # pycodestyle Error
  205. "W", # pycodestyle Warning
  206. "F", # Pyflakes
  207. "I", # isort
  208. "Q", # flake8-quotes
  209. "N803", # invalid-argument-name
  210. "N804", # invalid-first-argument-name-for-class-method
  211. "UP", # pyupgrade
  212. "B", # flake8-bugbear
  213. "A", # flake8-builtins
  214. "COM", # flake8-commas
  215. "C4", # flake8-comprehensions
  216. "FA", # flake8-future-annotations
  217. "ISC", # flake8-implicit-str-concat
  218. "ICN003", # banned-import-from
  219. "PIE", # flake8-pie
  220. "T20", # flake8-print
  221. "RSE", # flake8-raise
  222. "RET504", # unnecessary-assign
  223. "SIM", # flake8-simplify
  224. "TID251", # banned-api
  225. "TD", # flake8-todos
  226. "PLC", # Pylint Convention
  227. "PLE", # Pylint Error
  228. "PLW", # Pylint Warning
  229. "RUF", # Ruff-specific rules
  230. ]
  231. [tool.ruff.lint.per-file-ignores]
  232. "devscripts/lazy_load_template.py" = [
  233. "F401", # unused-import
  234. ]
  235. "!yt_dlp/extractor/**.py" = [
  236. "I", # isort
  237. "ICN003", # banned-import-from
  238. "T20", # flake8-print
  239. "A002", # builtin-argument-shadowing
  240. "C408", # unnecessary-collection-call
  241. ]
  242. "yt_dlp/jsinterp.py" = [
  243. "UP031", # printf-string-formatting
  244. ]
  245. [tool.ruff.lint.isort]
  246. known-first-party = [
  247. "bundle",
  248. "devscripts",
  249. "test",
  250. ]
  251. relative-imports-order = "closest-to-furthest"
  252. [tool.ruff.lint.flake8-quotes]
  253. docstring-quotes = "double"
  254. multiline-quotes = "single"
  255. inline-quotes = "single"
  256. avoid-escape = false
  257. [tool.ruff.lint.pep8-naming]
  258. classmethod-decorators = [
  259. "yt_dlp.utils.classproperty",
  260. ]
  261. [tool.ruff.lint.flake8-import-conventions]
  262. banned-from = [
  263. "base64",
  264. "datetime",
  265. "functools",
  266. "glob",
  267. "hashlib",
  268. "itertools",
  269. "json",
  270. "math",
  271. "os",
  272. "pathlib",
  273. "random",
  274. "re",
  275. "string",
  276. "sys",
  277. "time",
  278. "urllib.parse",
  279. "uuid",
  280. "xml",
  281. ]
  282. [tool.ruff.lint.flake8-tidy-imports.banned-api]
  283. "yt_dlp.compat.compat_str".msg = "Use `str` instead."
  284. "yt_dlp.compat.compat_b64decode".msg = "Use `base64.b64decode` instead."
  285. "yt_dlp.compat.compat_urlparse".msg = "Use `urllib.parse` instead."
  286. "yt_dlp.compat.compat_parse_qs".msg = "Use `urllib.parse.parse_qs` instead."
  287. "yt_dlp.compat.compat_urllib_parse_unquote".msg = "Use `urllib.parse.unquote` instead."
  288. "yt_dlp.compat.compat_urllib_parse_urlencode".msg = "Use `urllib.parse.urlencode` instead."
  289. "yt_dlp.compat.compat_urllib_parse_urlparse".msg = "Use `urllib.parse.urlparse` instead."
  290. "yt_dlp.compat.compat_shlex_quote".msg = "Use `yt_dlp.utils.shell_quote` instead."
  291. "yt_dlp.utils.error_to_compat_str".msg = "Use `str` instead."
  292. "yt_dlp.utils.bytes_to_intlist".msg = "Use `list` instead."
  293. "yt_dlp.utils.intlist_to_bytes".msg = "Use `bytes` instead."
  294. "yt_dlp.utils.decodeArgument".msg = "Do not use"
  295. "yt_dlp.utils.decodeFilename".msg = "Do not use"
  296. "yt_dlp.utils.encodeFilename".msg = "Do not use"
  297. "yt_dlp.compat.compat_os_name".msg = "Use `os.name` instead."
  298. "yt_dlp.compat.compat_realpath".msg = "Use `os.path.realpath` instead."
  299. "yt_dlp.compat.functools".msg = "Use `functools` instead."
  300. "yt_dlp.utils.decodeOption".msg = "Do not use"
  301. "yt_dlp.utils.compiled_regex_type".msg = "Use `re.Pattern` instead."
  302. [tool.autopep8]
  303. max_line_length = 120
  304. recursive = true
  305. exit-code = true
  306. jobs = 0
  307. select = [
  308. "E101",
  309. "E112",
  310. "E113",
  311. "E115",
  312. "E116",
  313. "E117",
  314. "E121",
  315. "E122",
  316. "E123",
  317. "E124",
  318. "E125",
  319. "E126",
  320. "E127",
  321. "E128",
  322. "E129",
  323. "E131",
  324. "E201",
  325. "E202",
  326. "E203",
  327. "E211",
  328. "E221",
  329. "E222",
  330. "E223",
  331. "E224",
  332. "E225",
  333. "E226",
  334. "E227",
  335. "E228",
  336. "E231",
  337. "E241",
  338. "E242",
  339. "E251",
  340. "E252",
  341. "E261",
  342. "E262",
  343. "E265",
  344. "E266",
  345. "E271",
  346. "E272",
  347. "E273",
  348. "E274",
  349. "E275",
  350. "E301",
  351. "E302",
  352. "E303",
  353. "E304",
  354. "E305",
  355. "E306",
  356. "E502",
  357. "E701",
  358. "E702",
  359. "E704",
  360. "W391",
  361. "W504",
  362. ]
  363. [tool.pytest.ini_options]
  364. addopts = "-ra -v --strict-markers"
  365. markers = [
  366. "download",
  367. ]