[tool.poetry]
name = "beets"
version = "2.5.1"
description = "music tagger and library organizer"
authors = ["Adrian Sampson <adrian@radbox.org>"]
maintainers = ["Serene-Arc"]
license = "MIT"
readme = "README.rst"
homepage = "https://beets.io/"
repository = "https://github.com/beetbox/beets"
documentation = "https://beets.readthedocs.io/en/stable/"
classifiers = [
    "Topic :: Multimedia :: Sound/Audio",
    "Topic :: Multimedia :: Sound/Audio :: Players :: MP3",
    "License :: OSI Approved :: MIT License",
    "Environment :: Console",
    "Environment :: Web Environment",
    "Programming Language :: Python",
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.10",
    "Programming Language :: Python :: 3.11",
    "Programming Language :: Python :: 3.12",
    "Programming Language :: Python :: 3.13",
    "Programming Language :: Python :: Implementation :: CPython",
]
packages = [
    { include = "beets" },
    { include = "beetsplug" },
]
include = [ # extra files to include in the sdist
    { path = "docs", format = "sdist" },
    { path = "extra", format = "sdist" },
    { path = "man/**/*", format = "sdist" },
    { path = "test/*.py", format = "sdist" },
    { path = "test/rsrc/**/*", format = "sdist" },
]
exclude = ["docs/_build", "docs/modd.conf", "docs/**/*.css"]

[tool.poetry.urls]
Changelog = "https://github.com/beetbox/beets/blob/master/docs/changelog.rst"
"Bug Tracker" = "https://github.com/beetbox/beets/issues"

[tool.poetry.dependencies]
python = ">=3.10,<4"

colorama = { version = "*", markers = "sys_platform == 'win32'" }
confuse = ">=2.1.0"
jellyfish = "*"
lap = ">=0.5.12"
mediafile = ">=0.12.0"
musicbrainzngs = ">=0.4"
numpy = [
    { python = "<3.13", version = ">=2.0.2" },
    { python = ">=3.13", version = ">=2.3.4" },
]
platformdirs = ">=3.5.0"
pyyaml = "*"
typing_extensions = "*"
unidecode = ">=1.3.6"

beautifulsoup4 = { version = "*", optional = true }
dbus-python = { version = "*", optional = true }
flask = { version = "*", optional = true }
flask-cors = { version = "*", optional = true }
langdetect = { version = "*", optional = true }
librosa = { version = ">=0.11", optional = true }
scipy = [ # for librosa
    { python = "<3.13", version = ">=1.13.1", optional = true },
    { python = ">=3.13", version = ">=1.16.1", optional = true },
]
numba = [ # for librosa
    { python = "<3.13", version = ">=0.60", optional = true },
    { python = ">=3.13", version = ">=0.62.1", optional = true },
]
mutagen = { version = ">=1.33", optional = true }
Pillow = { version = "*", optional = true }
py7zr = { version = "*", optional = true }
pyacoustid = { version = "*", optional = true }
PyGObject = { version = "*", optional = true }
pylast = { version = "*", optional = true }
python-mpd2 = { version = ">=0.4.2", optional = true }
python3-discogs-client = { version = ">=2.3.15", optional = true }
pyxdg = { version = "*", optional = true }
rarfile = { version = "*", optional = true }
reflink = { version = "*", optional = true }
requests = { version = "*", optional = true }
resampy = { version = ">=0.4.3", optional = true }
requests-oauthlib = { version = ">=0.6.1", optional = true }
soco = { version = "*", optional = true }

docutils = { version = ">=0.20.1", optional = true }
pydata-sphinx-theme = { version = "*", optional = true }
sphinx = { version = "*", optional = true }
sphinx-design = { version = ">=0.6.1", optional = true }
sphinx-copybutton = { version = ">=0.5.2", optional = true }

[tool.poetry.group.test.dependencies]
beautifulsoup4 = "*"
codecov = ">=2.1.13"
flask = "*"
langdetect = "*"
mock = "*"
pylast = "*"
pytest = "*"
pytest-cov = "*"
pytest-flask = "*"
python-mpd2 = "*"
python3-discogs-client = ">=2.3.15"
py7zr = "*"
pyxdg = "*"
rarfile = "*"
requests-mock = ">=1.12.1"
requests_oauthlib = "*"
responses = ">=0.3.0"

[tool.poetry.group.lint.dependencies]
docstrfmt = ">=1.11.1"
ruff = ">=0.6.4"
sphinx-lint = ">=1.0.0"

[tool.poetry.group.typing.dependencies]
mypy = "*"
types-beautifulsoup4 = "*"
types-docutils = ">=0.22.2.20251006"
types-mock = "*"
types-Flask-Cors = "*"
types-Pillow = "*"
types-PyYAML = "*"
types-requests = "*"
types-urllib3 = "*"

[tool.poetry.group.release.dependencies]
click = ">=8.1.7"
packaging = ">=24.0"
tomli = ">=2.0.1"

[tool.poetry.extras]
# inline comments note required external / non-python dependencies
absubmit = ["requests"] # extractor binary from https://acousticbrainz.org/download
aura = ["flask", "flask-cors", "Pillow"]
autobpm = ["librosa", "resampy"]
# badfiles # mp3val and flac
beatport = ["requests-oauthlib"]
bpd = ["PyGObject"] # gobject-introspection, gstreamer1.0-plugins-base, python3-gst-1.0
chroma = ["pyacoustid"] # chromaprint or fpcalc
# convert # ffmpeg
docs = [
    "docutils",
    "pydata-sphinx-theme",
    "sphinx",
    "sphinx-lint",
    "sphinx-design",
    "sphinx-copybutton",
]
discogs = ["python3-discogs-client"]
embedart = ["Pillow"] # ImageMagick
embyupdate = ["requests"]
fetchart = ["beautifulsoup4", "langdetect", "Pillow", "requests"]
import = ["py7zr", "rarfile"]
# ipfs # go-ipfs
# keyfinder # KeyFinder
kodiupdate = ["requests"]
lastgenre = ["pylast"]
lastimport = ["pylast"]
lyrics = ["beautifulsoup4", "langdetect", "requests"]
metasync = ["dbus-python"]
mpdstats = ["python-mpd2"]
plexupdate = ["requests"]
reflink = ["reflink"]
replaygain = [
    "PyGObject",
] # python-gi and GStreamer 1.0+ or mp3gain/aacgain or Python Audio Tools or ffmpeg
scrub = ["mutagen"]
sonosupdate = ["soco"]
thumbnails = ["Pillow", "pyxdg"]
web = ["flask", "flask-cors"]

[tool.poetry.scripts]
beet = "beets.ui:main"

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

[tool.pipx-install]
poethepoet = ">=0.26"
poetry = ">=1.8,<2"

[tool.poe.tasks.build]
help = "Build the package"
shell = """
make -C docs man
rm -rf man
mv docs/_build/man .
poetry build
"""

[tool.poe.tasks.bump]
help = "Bump project version and update relevant files"
cmd = "python ./extra/release.py bump $version"
args = { version = { help = "The new version to set", positional = true, required = true } }

[tool.poe.tasks.changelog]
help = "Print the latest version's changelog in Markdown"
cmd = "python ./extra/release.py changelog"

[tool.poe.tasks.check-docs-links]
help = "Check the documentation for broken URLs"
cmd = "make -C docs linkcheck"

[tool.poe.tasks.check-format]
help = "Check the code for style issues"
cmd = "ruff format --check --diff"

[tool.poe.tasks.check-types]
help = "Check the code for typing issues. Accepts mypy options."
cmd = "mypy"

[tool.poe.tasks.docs]
help = "Build documentation"
args = [{ name = "COMMANDS", positional = true, multiple = true, default = "html" }]
cmd = "make -C docs $COMMANDS"

[tool.poe.tasks.format]
help = "Format the codebase"
cmd = "ruff format"

[tool.poe.tasks.format-docs]
help = "Format the documentation"
cmd = "docstrfmt docs *.rst"

[tool.poe.tasks.lint]
help = "Check the code for linting issues. Accepts ruff options."
cmd = "ruff check"

[tool.poe.tasks.lint-docs]
help = "Lint the documentation"
shell = "sphinx-lint --enable all --disable default-role $(git ls-files '*.rst')"

[tool.poe.tasks.update-dependencies]
help = "Update dependencies to their latest versions."
cmd = "poetry update -vv"

[tool.poe.tasks.test]
help = "Run tests with pytest"
cmd = "pytest $OPTS"
env.OPTS.default = "-p no:cov"

[tool.poe.tasks.test-with-coverage]
help = "Run tests and record coverage"
ref = "test"
# record coverage in beets and beetsplug packages
# save xml for coverage upload to coveralls
# save html report for local dev use
# measure coverage across logical branches
# show which tests cover specific lines in the code (see the HTML report)
env.OPTS = """
--cov=beets
--cov=beetsplug
--cov-report=xml:.reports/coverage.xml
--cov-report=html:.reports/html
--cov-branch
--cov-context=test
"""

[tool.poe.tasks.check-temp-files]
help = "Run each test module one by one and check for leftover temp files"
shell = """
setopt nullglob
for file in test/**/*.py; do
  print Temp files created by $file && poe test $file &>/dev/null
  tempfiles=(/tmp/**/tmp* /tmp/beets/**/*)
  if (( $#tempfiles )); then
    print -l $'\t'$^tempfiles
    rm -r --interactive=never $tempfiles &>/dev/null
  fi
done
"""
interpreter = "zsh"

[tool.docstrfmt]
line-length = 80
extend-exclude = [
    "docs/_templates/**/*",
    "docs/api/**/*",
    "README_kr.rst",
]

[tool.ruff]
target-version = "py39"
line-length = 80

[tool.ruff.lint]
select = [
    # "ARG", # flake8-unused-arguments
    # "C4", # flake8-comprehensions
    "E", # pycodestyle
    "F", # pyflakes
    # "B", # flake8-bugbear
    "G", # flake8-logging-format
    "I", # isort
    "ISC", # flake8-implicit-str-concat
    "N", # pep8-naming
    "PT", # flake8-pytest-style
    # "RUF", # ruff
    "UP", # pyupgrade
    "TCH", # flake8-type-checking
    "W", # pycodestyle
]
ignore = [
    "TC006", # no need to quote 'cast's since we use 'from __future__ import annotations'
]

[tool.ruff.lint.per-file-ignores]
"beets/**" = ["PT"]
"test/test_util.py" = ["E501"]

[tool.ruff.lint.isort]
split-on-trailing-comma = false

[tool.ruff.lint.pycodestyle]
max-line-length = 88

[tool.ruff.lint.flake8-pytest-style]
fixture-parentheses = false
mark-parentheses = false
parametrize-names-type = "csv"

[tool.ruff.lint.flake8-unused-arguments]
ignore-variadic-names = true

[tool.ruff.lint.pep8-naming]
classmethod-decorators = ["cached_classproperty"]
extend-ignore-names = ["assert*", "cached_classproperty"]
