mirror of
https://github.com/beetbox/beets.git
synced 2026-01-10 01:50:34 +01:00
The toctree for plugins now has the same sections as the index page.
Added an new page showing all plugins (including external ones) in alphabetical order.
This commit is contained in:
parent
4d648510cc
commit
b373b16ee9
5 changed files with 387 additions and 82 deletions
|
|
@ -86,6 +86,8 @@ Other changes:
|
|||
case is shown on separate lines.
|
||||
* Refactored library.py file by splitting it into multiple modules within the
|
||||
beets/library directory.
|
||||
* Plugin docs slightly reorganized: added a new `all_plugins` page that lists all
|
||||
plugins in alphabetical order, by default plugins are now categorized
|
||||
|
||||
2.3.1 (May 14, 2025)
|
||||
--------------------
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
# -- Project information -----------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
||||
|
||||
from docs.pluginlist import PluginListDirective
|
||||
|
||||
project = "beets"
|
||||
AUTHOR = "Adrian Sampson"
|
||||
|
|
@ -96,3 +97,4 @@ def skip_member(app, what, name, obj, skip, options):
|
|||
|
||||
def setup(app):
|
||||
app.connect("autodoc-skip-member", skip_member)
|
||||
app.add_directive("pluginlist", PluginListDirective)
|
||||
|
|
|
|||
168
docs/pluginlist.py
Normal file
168
docs/pluginlist.py
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.parsers.rst import Directive, directives
|
||||
|
||||
|
||||
class PluginListDirective(Directive):
|
||||
"""Directive to list all .rst files in a given folder.
|
||||
|
||||
Along with their top-level header, with optional extra user-defined entries.
|
||||
|
||||
Usage:
|
||||
.. filelist::
|
||||
:path: path/to/folder
|
||||
:exclude: file1.rst, file2.rst
|
||||
:extra:
|
||||
overview.rst # file link with implicit title from filename
|
||||
Overview: overview.rst # file link with custom title
|
||||
:ref:`user-guide` # arbitrary reST reference
|
||||
"""
|
||||
|
||||
has_content = False
|
||||
required_arguments = 0
|
||||
optional_arguments = 0
|
||||
|
||||
option_spec = {
|
||||
"path": directives.unchanged_required,
|
||||
"exclude": directives.unchanged,
|
||||
"extra": directives.unchanged,
|
||||
}
|
||||
|
||||
def _extract_title(self, path: Path):
|
||||
"""Extract the first section title from an rst file."""
|
||||
with open(path, encoding="utf-8") as f:
|
||||
lines = [ln.rstrip() for ln in f]
|
||||
for idx, line in enumerate(lines):
|
||||
if not line:
|
||||
continue
|
||||
# Check for underline-style title
|
||||
if idx + 1 < len(lines) and set(lines[idx + 1]) <= set(
|
||||
"= - `:'~^_*+#<>"
|
||||
):
|
||||
underline = lines[idx + 1]
|
||||
if len(underline) >= len(line):
|
||||
return line
|
||||
# Or overline/underline style
|
||||
if idx >= 1 and set(lines[idx - 1]) <= set("= - `:'~^_*+#<>"):
|
||||
overline = lines[idx - 1]
|
||||
if len(overline) >= len(line):
|
||||
return line
|
||||
# Fallback: filename without extension
|
||||
return os.path.splitext(os.path.basename(path))[0]
|
||||
|
||||
def _get_current_src(self) -> Path:
|
||||
"""Get the current source file path."""
|
||||
current_doc = self.state.document.current_source
|
||||
if not current_doc:
|
||||
raise ValueError("Current document source could not be determined.")
|
||||
return Path(current_doc).resolve()
|
||||
|
||||
def run(self):
|
||||
folder_option = self.options.get("path")
|
||||
if not folder_option:
|
||||
error = self.state_machine.reporter.error(
|
||||
'The "path" option is required for the pluginlist directive.',
|
||||
nodes.literal_block(self.block_text, self.block_text),
|
||||
line=self.lineno,
|
||||
)
|
||||
return [error]
|
||||
|
||||
# Resolve folder path relative to current doc file
|
||||
cur_path = self._get_current_src()
|
||||
target_folder = cur_path.joinpath(folder_option).resolve().parent
|
||||
|
||||
if not os.path.isdir(target_folder):
|
||||
error = self.state_machine.reporter.error(
|
||||
f'Path "{folder_option}" resolved to "{target_folder}". '
|
||||
"Could not found or is not a directory.",
|
||||
nodes.literal_block(self.block_text, self.block_text),
|
||||
line=self.lineno,
|
||||
)
|
||||
return [error]
|
||||
|
||||
excludes_raw = self.options.get("exclude", "")
|
||||
excludes = [x.strip() for x in excludes_raw.split(",") if x.strip()]
|
||||
|
||||
# Find .rst files, excluding specified
|
||||
files = [
|
||||
f
|
||||
for f in os.listdir(target_folder)
|
||||
if f.endswith(".rst") and f not in excludes
|
||||
]
|
||||
|
||||
refs = []
|
||||
for filename in files:
|
||||
# Reference to the rst file
|
||||
refuri = (
|
||||
os.path.splitext(os.path.join(folder_option, filename))[
|
||||
0
|
||||
].replace(os.sep, "/")
|
||||
+ ".html"
|
||||
)
|
||||
# Title for the link
|
||||
title = self._extract_title(target_folder.joinpath(filename))
|
||||
|
||||
ref = nodes.reference("", title, internal=True, refuri=refuri)
|
||||
refs.append(ref)
|
||||
|
||||
# Extra entries into refs
|
||||
extra_option = self.options.get("extra", "")
|
||||
if extra_option:
|
||||
from docutils.statemachine import ViewList
|
||||
|
||||
for line in extra_option.splitlines():
|
||||
entry = line.strip()
|
||||
if not entry:
|
||||
continue
|
||||
|
||||
para = nodes.paragraph()
|
||||
|
||||
# If entry is pure reST (contains role/backticks and no file .rst)
|
||||
if (
|
||||
"`" in entry or entry.strip().startswith(":ref")
|
||||
) and ".rst" not in entry:
|
||||
vl = ViewList()
|
||||
vl.append(entry, self.block_text)
|
||||
self.state.nested_parse(vl, self.content_offset, para)
|
||||
else:
|
||||
# file link: either 'file.rst' or 'Title: file.rst'
|
||||
if ":" in entry:
|
||||
title, target = [p.strip() for p in entry.split(":", 1)]
|
||||
else:
|
||||
target = entry
|
||||
title = Path(entry).stem
|
||||
if target.endswith(".rst"):
|
||||
title = self._extract_title(
|
||||
target_folder.joinpath(target)
|
||||
)
|
||||
rel = Path(self.options["path"]) / target
|
||||
refuri = str(rel.with_suffix(".html")).replace(
|
||||
os.sep, "/"
|
||||
)
|
||||
ref = nodes.reference(
|
||||
"", title, internal=True, refuri=refuri
|
||||
)
|
||||
para += ref
|
||||
else:
|
||||
# fallback parse
|
||||
vl = ViewList()
|
||||
vl.append(entry, self.block_text)
|
||||
self.state.nested_parse(vl, self.content_offset, para)
|
||||
|
||||
refs.append(para)
|
||||
|
||||
# Sort refs
|
||||
refs.sort(key=lambda x: x.astext().lower())
|
||||
|
||||
# Build bullet list of links
|
||||
bullet_list = nodes.bullet_list()
|
||||
for ref in refs:
|
||||
item = nodes.list_item()
|
||||
para = nodes.paragraph()
|
||||
para += ref
|
||||
item += para
|
||||
bullet_list += item
|
||||
|
||||
return [bullet_list]
|
||||
60
docs/plugins/all_plugins.rst
Normal file
60
docs/plugins/all_plugins.rst
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
All plugins
|
||||
===========
|
||||
..
|
||||
README: The plugin list is automatically generated from all plugin
|
||||
files in the plugins directory. If you want to add an external
|
||||
plugin, please add it to the extra section of the pluginlist
|
||||
directive.
|
||||
|
||||
The pluginlist directive is defined in
|
||||
:file:`docs/pluglist.py` file.
|
||||
|
||||
.. pluginlist::
|
||||
:path: .
|
||||
:exclude: index.rst, all_plugins.rst
|
||||
:extra:
|
||||
`beets-yearfixer`_
|
||||
`beets-alternatives`_
|
||||
`beet-amazon`_
|
||||
`beets-artistcountry`_
|
||||
`beets-autofix`_
|
||||
`beets-autogenre`_
|
||||
`beets-audible`_
|
||||
`beets-barcode`_
|
||||
`beetcamp`_
|
||||
`beetstream`_
|
||||
`beets-bpmanalyser`_
|
||||
`beets-check`_
|
||||
`A cmus plugin`_
|
||||
`beets-copyartifacts`_
|
||||
`beets-describe`_
|
||||
`drop2beets`_
|
||||
`dsedivec`_
|
||||
`beets-filetote`_
|
||||
`beets-follow`_
|
||||
`beetFs`_
|
||||
`beets-goingrunning`_
|
||||
`beets-ibroadcast`_
|
||||
`beets-id3extract`_
|
||||
`beets-importreplace`_
|
||||
`beets-jiosaavn`_
|
||||
`beets-more`_
|
||||
`beets-mosaic`_
|
||||
`beets-mpd-utils`_
|
||||
`beets-noimport`_
|
||||
`beets-originquery`_
|
||||
`beets-plexsync`_
|
||||
`beets-setlister`_
|
||||
`beet-summarize`_
|
||||
`beets-usertag`_
|
||||
`beets-webm3u`_
|
||||
`beets-webrouter`_
|
||||
`whatlastgenre`_
|
||||
`beets-xtractor`_
|
||||
`beets-ydl`_
|
||||
`beets-ytimport`_
|
||||
`beets-yearfixer`_
|
||||
`beets-youtube`_
|
||||
|
||||
.. include:: index.rst
|
||||
:start-after: other_links
|
||||
|
|
@ -63,89 +63,47 @@ following to your configuration:
|
|||
source_weight: 0.0
|
||||
|
||||
|
||||
.. _autotagger_extensions:
|
||||
|
||||
|
||||
Available Plugins
|
||||
-----------------
|
||||
|
||||
We have organized the plugins into several categories to help you find what
|
||||
you need. The categories are as follows:
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
:depth: 2
|
||||
:backlinks: none
|
||||
|
||||
|
||||
If you prefer to browse the plugins by their names, you can make use of the
|
||||
:doc:`all_plugins <all_plugins>` page, which lists all plugins in alphabetical order.
|
||||
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
absubmit
|
||||
acousticbrainz
|
||||
advancedrewrite
|
||||
albumtypes
|
||||
aura
|
||||
autobpm
|
||||
badfiles
|
||||
bareasc
|
||||
beatport
|
||||
bpd
|
||||
bpm
|
||||
bpsync
|
||||
bucket
|
||||
chroma
|
||||
convert
|
||||
deezer
|
||||
discogs
|
||||
duplicates
|
||||
edit
|
||||
embedart
|
||||
embyupdate
|
||||
export
|
||||
fetchart
|
||||
filefilter
|
||||
fish
|
||||
freedesktop
|
||||
fromfilename
|
||||
ftintitle
|
||||
fuzzy
|
||||
gmusic
|
||||
hook
|
||||
ihate
|
||||
importadded
|
||||
importfeeds
|
||||
info
|
||||
inline
|
||||
ipfs
|
||||
keyfinder
|
||||
kodiupdate
|
||||
lastgenre
|
||||
lastimport
|
||||
limit
|
||||
listenbrainz
|
||||
loadext
|
||||
lyrics
|
||||
mbcollection
|
||||
mbsubmit
|
||||
mbsync
|
||||
metasync
|
||||
missing
|
||||
mpdstats
|
||||
mpdupdate
|
||||
musicbrainz
|
||||
parentwork
|
||||
permissions
|
||||
play
|
||||
playlist
|
||||
plexupdate
|
||||
random
|
||||
replace
|
||||
replaygain
|
||||
rewrite
|
||||
scrub
|
||||
smartplaylist
|
||||
sonosupdate
|
||||
spotify
|
||||
subsonicplaylist
|
||||
subsonicupdate
|
||||
substitute
|
||||
the
|
||||
thumbnails
|
||||
types
|
||||
unimported
|
||||
web
|
||||
zero
|
||||
all_plugins
|
||||
|
||||
|
||||
.. _autotagger_extensions:
|
||||
|
||||
Autotagger Extensions
|
||||
---------------------
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
:caption: Autotagger Extensions
|
||||
:maxdepth: 1
|
||||
|
||||
chroma
|
||||
deezer
|
||||
discogs
|
||||
fromfilename
|
||||
musicbrainz
|
||||
spotify
|
||||
|
||||
|
||||
:doc:`chroma <chroma>`
|
||||
Use acoustic fingerprinting to identify audio files with
|
||||
|
|
@ -173,7 +131,31 @@ Autotagger Extensions
|
|||
.. _Spotify: https://www.spotify.com
|
||||
|
||||
Metadata
|
||||
--------
|
||||
^^^^^^^^
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
:caption: Metadata
|
||||
:maxdepth: 1
|
||||
|
||||
absubmit
|
||||
acousticbrainz
|
||||
advancedrewrite
|
||||
albumtypes
|
||||
autobpm
|
||||
bpsync
|
||||
bpm
|
||||
edit
|
||||
embedart
|
||||
fetchart
|
||||
ftintitle
|
||||
keyfinder
|
||||
lastgenre
|
||||
lyrics
|
||||
metasync
|
||||
replaygain
|
||||
scrub
|
||||
zero
|
||||
|
||||
:doc:`absubmit <absubmit>`
|
||||
Analyse audio with the `streaming_extractor_music`_ program and submit the metadata to an AcousticBrainz server
|
||||
|
|
@ -247,7 +229,19 @@ Metadata
|
|||
.. _streaming_extractor_music: https://acousticbrainz.org/download
|
||||
|
||||
Path Formats
|
||||
------------
|
||||
^^^^^^^^^^^^
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
:caption: Path Formats
|
||||
:maxdepth: 1
|
||||
|
||||
albumtypes
|
||||
advancedrewrite
|
||||
inline
|
||||
rewrite
|
||||
substitute
|
||||
the
|
||||
|
||||
:doc:`albumtypes <albumtypes>`
|
||||
Format album type in path formats.
|
||||
|
|
@ -275,7 +269,21 @@ Path Formats
|
|||
end).
|
||||
|
||||
Interoperability
|
||||
----------------
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
:caption: Interoperability
|
||||
:maxdepth: 1
|
||||
|
||||
aura
|
||||
embyupdate
|
||||
kodiupdate
|
||||
mpdupdate
|
||||
plexupdate
|
||||
sonosupdate
|
||||
subsonicupdate
|
||||
thumbnails
|
||||
|
||||
:doc:`aura <aura>`
|
||||
A server implementation of the `AURA`_ specification.
|
||||
|
|
@ -337,7 +345,28 @@ Interoperability
|
|||
.. _Subsonic: http://www.subsonic.org/
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
:caption: Miscellaneous
|
||||
:maxdepth: 1
|
||||
|
||||
bpd
|
||||
convert
|
||||
duplicates
|
||||
filefilter
|
||||
fuzzy
|
||||
hook
|
||||
ihate
|
||||
info
|
||||
loadext
|
||||
mbcollection
|
||||
mbsubmit
|
||||
missing
|
||||
random
|
||||
types
|
||||
web
|
||||
|
||||
:doc:`bareasc <bareasc>`
|
||||
Search albums and tracks with bare ASCII string matching.
|
||||
|
|
@ -403,10 +432,47 @@ Miscellaneous
|
|||
.. _MPD clients: https://mpd.wikia.com/wiki/Clients
|
||||
.. _mstream: https://github.com/IrosTheBeggar/mStream
|
||||
|
||||
|
||||
The following plugins are not categorized yet. If you have a strong
|
||||
opinion about where they should go, please open a `PR or issue <https://github.com/beetbox/beets>`_.
|
||||
|
||||
|
||||
.. toctree::
|
||||
:caption: Uncategorized Plugins
|
||||
:maxdepth: 1
|
||||
|
||||
badfiles.rst
|
||||
bareasc.rst
|
||||
beatport.rst
|
||||
bucket.rst
|
||||
export.rst
|
||||
fish.rst
|
||||
freedesktop.rst
|
||||
gmusic.rst
|
||||
importadded.rst
|
||||
importfeeds.rst
|
||||
ipfs.rst
|
||||
lastimport.rst
|
||||
limit.rst
|
||||
listenbrainz.rst
|
||||
mbsync.rst
|
||||
mpdstats.rst
|
||||
parentwork.rst
|
||||
permissions.rst
|
||||
play.rst
|
||||
playlist.rst
|
||||
replace.rst
|
||||
smartplaylist.rst
|
||||
subsonicplaylist.rst
|
||||
unimported.rst
|
||||
|
||||
|
||||
|
||||
|
||||
.. _other-plugins:
|
||||
|
||||
Other Plugins
|
||||
-------------
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
In addition to the plugins that come with beets, there are several plugins
|
||||
that are maintained by the beets community. To use an external plugin, there
|
||||
|
|
@ -562,6 +628,9 @@ Here are a few of the plugins written by the beets community:
|
|||
`beets-youtube`_
|
||||
Adds YouTube Music as a tagger data source.
|
||||
|
||||
..
|
||||
other_links
|
||||
|
||||
.. _beets-barcode: https://github.com/8h2a/beets-barcode
|
||||
.. _beetcamp: https://github.com/snejus/beetcamp
|
||||
.. _beetstream: https://github.com/BinaryBrain/Beetstream
|
||||
|
|
@ -609,3 +678,7 @@ Here are a few of the plugins written by the beets community:
|
|||
.. _beets-webm3u: https://github.com/mgoltzsche/beets-webm3u
|
||||
.. _beets-webrouter: https://github.com/mgoltzsche/beets-webrouter
|
||||
.. _beets-autogenre: https://github.com/mgoltzsche/beets-autogenre
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue