Add beets_music_videos plugin for managing music videos
- Implemented a new beets plugin to import and manage music videos, supporting various video formats and providing metadata from IMVDB and MusicBrainz for autotagging. - Added installation instructions and configuration options in README.md. - Created IMVDBApi client for interacting with the IMVDB API. - Defined typing for various API responses and utility functions for mapping MusicBrainz data to beets TrackInfo. - Included desktop entry and JSON info files for a video titled "[SAST] The Evolution of Search-based Software Testing". - Added utility functions for handling artist credits and related metadata. - Introduced a grabbed.txt file for tracking video sources.
This commit is contained in:
1
NA.info.json
Normal file
1
NA.info.json
Normal file
File diff suppressed because one or more lines are too long
137
README.md
Normal file
137
README.md
Normal file
@@ -0,0 +1,137 @@
|
||||
# beets_music_videos
|
||||
|
||||
A [beets](https://beets.io/) plugin that lets you import and manage **music videos** (and other non-audio media) in your library. Beets normally only handles audio formats supported by its `mediafile` layer; this plugin treats selected video extensions as importable items, creates library entries for them, and provides metadata from IMVDB and MusicBrainz for autotagging.
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
- **Import video files** — Treats configured video extensions (e.g. `.mp4`, `.mkv`, `.webm`) as importable media. Files are added as beets `Item`s without going through `MediaFile`, so formats like MKV and WebM that beets doesn’t natively support can still be imported.
|
||||
- **Autotagging** — Supplies track-level candidates from:
|
||||
- **IMVDB** (Internet Music Video Database), via API search by artist + title.
|
||||
- **MusicBrainz** — video recordings search (`video: true`) for artist and title, with rich metadata (artist credits, release date, work/composer/lyricist, ISRC, etc.).
|
||||
- **Resolution probing** — Uses `ffprobe` to read video width/height and exposes them as flexible attributes (`video_width`, `video_height`, `video_resolution`) for path formats and queries.
|
||||
- **Safe tagging** — Writes embedded metadata only for formats that `mediafile` supports (MP4/M4V). For other formats (WebM, MKV, AVI), tags are not written to avoid corruption.
|
||||
- **Import behavior** — When a music video is already inside the library directory, the importer skips deleting the “original” after copy/move so you don’t lose the file.
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
1. **Requirements**
|
||||
- Python ≥ 3.12
|
||||
- beets ≥ 2.6.0, < 3.0.0
|
||||
- [FFmpeg](https://ffmpeg.org/) (for `ffprobe`) — optional; used only for resolution probing. Without it, resolution fields are left unset.
|
||||
|
||||
2. **Install the plugin** (from this repo):
|
||||
```bash
|
||||
cd beets_music_videos
|
||||
pip install -e .
|
||||
```
|
||||
Or add the project as a path dependency in your environment so beets can load `beetsplug.beets_music_videos`.
|
||||
|
||||
3. **Enable it** in `config.yaml`:
|
||||
```yaml
|
||||
plugins: beets_music_videos
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
Configure the plugin under the `beets_music_videos:` key in your beets config.
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|-------------------|----------|----------------------------------------------|-------------|
|
||||
| `extensions` | list | `[".mp4", ".m4v", ".mkv", ".avi", ".webm"]` | File extensions to treat as importable video. Values are normalized to lowercase with a leading dot (e.g. `mp4` → `.mp4`). |
|
||||
| `imvdb_api_key` | string | `""` | IMVDB API key for video search. If empty, IMVDB search is skipped; MusicBrainz video search still runs. Get a key at [IMVDB](https://imvdb.com/). |
|
||||
|
||||
### Example
|
||||
|
||||
```yaml
|
||||
plugins: beets_music_videos
|
||||
|
||||
beets_music_videos:
|
||||
extensions: [".mp4", ".m4v", ".mkv", ".webm"]
|
||||
imvdb_api_key: YOUR_IMVDB_API_KEY
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## How it works
|
||||
|
||||
### Import flow
|
||||
|
||||
1. **Discovery** — Beets’ import task factory reads each path. The plugin patches `ImportTaskFactory.read_item` so that when the file extension is in `extensions`, it builds an `Item` directly instead of using `Item.from_path()` / `MediaFile`. That allows unsupported video formats to become library items.
|
||||
2. **Item creation** — For those paths, the plugin:
|
||||
- Sets `path`, `mtime`, and a default `title` from the filename stem.
|
||||
- Runs `ffprobe` to get video width/height and sets `video_width`, `video_height`, and `video_resolution`.
|
||||
- Sets `media_type` to `"music_video"` so you can query e.g. `media_type:music_video`.
|
||||
- For **MP4/M4V** only, reads embedded tags (title, artist, album, albumartist, length) via `MediaFile` so autotag has better initial metadata.
|
||||
3. **Autotag** — For each such item, beets calls the plugin’s `item_candidates(item, artist, title)`. The plugin:
|
||||
- Queries **IMVDB** (if `imvdb_api_key` is set) with artist + title.
|
||||
- Queries **MusicBrainz** for video recordings with artist and title.
|
||||
- Returns combined `TrackInfo` candidates; beets then picks the best match and applies metadata.
|
||||
4. **File operations** — The plugin patches `ImportTask.manipulate_files` so that when writing tags it only calls `item.try_write()` for music videos whose extension is supported by `MediaFile` (MP4/M4V). Other video formats are not written to avoid corruption. It also adjusts behavior when the “original” is already inside the library directory so the file isn’t deleted after move.
|
||||
|
||||
### Metadata sources
|
||||
|
||||
- **IMVDB** — Used only for track-level search. Returns candidates with `data_source="IMVDB"` (song title, artists, year, etc.). Requires `imvdb_api_key`.
|
||||
- **MusicBrainz** — Video recording search with `video: true`. Returns candidates with `data_source="MusicBrainz"` and full track metadata (artist credits, date, work, composer, lyricist, remixer, arranger, ISRC, length, etc.). No API key required.
|
||||
|
||||
---
|
||||
|
||||
## Item attributes (music videos)
|
||||
|
||||
Library items created for video files have standard beets fields plus these flexible attributes:
|
||||
|
||||
| Attribute | Type | Description |
|
||||
|--------------------|--------|-------------|
|
||||
| `media_type` | string | Set to `"music_video"` for all items created by this plugin. Use in queries: `beet list media_type:music_video`. |
|
||||
| `video_width` | int | Video width in pixels (from `ffprobe`). |
|
||||
| `video_height` | int | Video height in pixels (from `ffprobe`). |
|
||||
| `video_resolution` | string | e.g. `"1920x1080"` (from `ffprobe`). |
|
||||
|
||||
For **MP4/M4V**, initial `title`, `artist`, `album`, `albumartist`, and `length` are filled from embedded tags when available; otherwise title defaults to the filename stem.
|
||||
|
||||
---
|
||||
|
||||
## Path formats
|
||||
|
||||
You can use the extra attributes in `paths:` and `path_format:`:
|
||||
|
||||
```yaml
|
||||
paths:
|
||||
default: '$albumartist/$album [$year]/$track. $title'
|
||||
singleton: '$artist - $title - $year'
|
||||
```
|
||||
|
||||
Flexible attributes are available as `$video_width`, `$video_height`, `$video_resolution`, and `$media_type` in path formats.
|
||||
|
||||
---
|
||||
|
||||
## Query examples
|
||||
|
||||
- List all music videos:
|
||||
`beet list media_type:music_video`
|
||||
- 1080p only:
|
||||
`beet list video_height:1080`
|
||||
- By resolution string:
|
||||
`beet list video_resolution:1920x1080`
|
||||
|
||||
---
|
||||
|
||||
## Dependencies
|
||||
|
||||
- **beets** (≥ 2.6.0, < 3.0.0)
|
||||
- **lapidary** (≥ 0.12.3) — used for the IMVDB API client
|
||||
- **typing-extensions** (≥ 4.15.0)
|
||||
- **FFmpeg** — optional; provides `ffprobe` for resolution. If missing, resolution fields are not set.
|
||||
|
||||
The plugin uses beets’ built-in MusicBrainz integration (`MusicBrainzAPIMixin`) for MusicBrainz video search; no extra MusicBrainz package is required beyond what beets uses.
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
Apache 2.0.
|
||||
@@ -0,0 +1,6 @@
|
||||
[Desktop Entry]
|
||||
Encoding=UTF-8
|
||||
Name=[SAST] The Evolution of Search-based Software Testing
|
||||
Type=Link
|
||||
URL=https://www.youtube.com/watch?v=y1xe1z2kmOg
|
||||
Icon=text-html
|
||||
File diff suppressed because one or more lines are too long
94
beetsplug/beets_music_videos/IMVDBApi.py
Normal file
94
beetsplug/beets_music_videos/IMVDBApi.py
Normal file
@@ -0,0 +1,94 @@
|
||||
from typing import Any
|
||||
from lapidary.runtime import (
|
||||
Body,
|
||||
ClientBase,
|
||||
Query,
|
||||
Response,
|
||||
Responses,
|
||||
UnexpectedResponse,
|
||||
get,
|
||||
)
|
||||
from typing import Annotated, Any, Literal, Sequence, cast, Self
|
||||
from httpx_auth import HeaderApiKey
|
||||
|
||||
from beetsplug.beets_music_videos.types import SearchResponseType, VideoType
|
||||
from beets import logging
|
||||
|
||||
log = logging.getLogger("beets")
|
||||
|
||||
|
||||
class IMVDBApi(ClientBase): # type: ignore
|
||||
"""Lapidary-based IMVDB client. Use api_key in security; do not hardcode."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
api_key: str,
|
||||
base_url: str = "https://imvdb.com/api/v1",
|
||||
**kwargs: Any,
|
||||
) -> None: # type: ignore
|
||||
# Some APIs return 500/502 for non-browser User-Agent; send a browser-like one.
|
||||
headers = dict(kwargs.pop("headers", {}))
|
||||
headers.setdefault(
|
||||
"User-Agent",
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
||||
)
|
||||
super().__init__( # type: ignore
|
||||
base_url=base_url,
|
||||
security=[{"api_key": [api_key]}],
|
||||
headers=headers,
|
||||
**kwargs,
|
||||
)
|
||||
# Send API key as header IMVDB-APP-KEY on every request.
|
||||
self.lapidary_authenticate(
|
||||
api_key=HeaderApiKey(api_key, header_name="IMVDB-APP-KEY"),
|
||||
)
|
||||
|
||||
@get("/search/videos") # type: ignore[misc]
|
||||
def video_search(
|
||||
self: Self,
|
||||
q: Annotated[str, Query()],
|
||||
) -> Annotated[
|
||||
SearchResponseType,
|
||||
Responses(
|
||||
responses={
|
||||
"2xx": Response(body=Body({"application/json": SearchResponseType})),
|
||||
}
|
||||
),
|
||||
]:
|
||||
"""Search IMVDB; returns response with .results (list of VideoType).
|
||||
|
||||
Implemented at runtime by Lapidary; this stub is never executed.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
# def sync_video_search(self, query: str) -> list[VideoType]:
|
||||
# """Sync wrapper used by the plugin. Runs video_search and returns .results."""
|
||||
# import asyncio
|
||||
# # Reuse a single event loop to avoid "Event loop is closed" on subsequent calls.
|
||||
# try:
|
||||
# loop = asyncio.get_event_loop()
|
||||
# if loop.is_closed():
|
||||
# loop = asyncio.new_event_loop()
|
||||
# asyncio.set_event_loop(loop)
|
||||
# except RuntimeError:
|
||||
# loop = asyncio.new_event_loop()
|
||||
# asyncio.set_event_loop(loop)
|
||||
# try:
|
||||
# resp = loop.run_until_complete(self.video_search(q=query))
|
||||
# return resp["results"]
|
||||
# except UnexpectedResponse as e:
|
||||
# # Lapidary raises UnexpectedResponse when body validation fails
|
||||
# # (e.g. API shape differs from SearchResponseType). For 200, parse
|
||||
# # body and return results.
|
||||
# if e.response.status_code == 200:
|
||||
# try:
|
||||
# body = e.response.json()
|
||||
# if isinstance(body, dict):
|
||||
# return cast(list[VideoType], body["results"])
|
||||
# except Exception:
|
||||
# pass
|
||||
# log.debug("imvdb search failed for %r: %s", query[:50], e)
|
||||
# return []
|
||||
# except Exception as e:
|
||||
# log.debug("imvdb search failed for %r: %s", query[:50], e)
|
||||
# return []
|
||||
File diff suppressed because it is too large
Load Diff
135
beetsplug/beets_music_videos/_typing.py
Normal file
135
beetsplug/beets_music_videos/_typing.py
Normal file
@@ -0,0 +1,135 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from typing_extensions import NotRequired, TypedDict
|
||||
|
||||
JSONDict = dict[str, Any]
|
||||
|
||||
|
||||
class LRCLibAPI:
|
||||
class Item(TypedDict):
|
||||
"""Lyrics data item returned by the LRCLib API."""
|
||||
|
||||
id: int
|
||||
name: str
|
||||
trackName: str
|
||||
artistName: str
|
||||
albumName: str
|
||||
duration: float | None
|
||||
instrumental: bool
|
||||
plainLyrics: str
|
||||
syncedLyrics: str | None
|
||||
|
||||
|
||||
class GeniusAPI:
|
||||
"""Genius API data types.
|
||||
|
||||
This documents *only* the fields that are used in the plugin.
|
||||
:attr:`SearchResult` is an exception, since I thought some of the other
|
||||
fields might be useful in the future.
|
||||
"""
|
||||
|
||||
class DateComponents(TypedDict):
|
||||
year: int
|
||||
month: int
|
||||
day: int
|
||||
|
||||
class Artist(TypedDict):
|
||||
api_path: str
|
||||
header_image_url: str
|
||||
id: int
|
||||
image_url: str
|
||||
is_meme_verified: bool
|
||||
is_verified: bool
|
||||
name: str
|
||||
url: str
|
||||
|
||||
class Stats(TypedDict):
|
||||
unreviewed_annotations: int
|
||||
hot: bool
|
||||
|
||||
class SearchResult(TypedDict):
|
||||
annotation_count: int
|
||||
api_path: str
|
||||
artist_names: str
|
||||
full_title: str
|
||||
header_image_thumbnail_url: str
|
||||
header_image_url: str
|
||||
id: int
|
||||
lyrics_owner_id: int
|
||||
lyrics_state: str
|
||||
path: str
|
||||
primary_artist_names: str
|
||||
pyongs_count: int | None
|
||||
relationships_index_url: str
|
||||
release_date_components: GeniusAPI.DateComponents
|
||||
release_date_for_display: str
|
||||
release_date_with_abbreviated_month_for_display: str
|
||||
song_art_image_thumbnail_url: str
|
||||
song_art_image_url: str
|
||||
stats: GeniusAPI.Stats
|
||||
title: str
|
||||
title_with_featured: str
|
||||
url: str
|
||||
featured_artists: list[GeniusAPI.Artist]
|
||||
primary_artist: GeniusAPI.Artist
|
||||
primary_artists: list[GeniusAPI.Artist]
|
||||
|
||||
class SearchHit(TypedDict):
|
||||
result: GeniusAPI.SearchResult
|
||||
|
||||
class SearchResponse(TypedDict):
|
||||
hits: list[GeniusAPI.SearchHit]
|
||||
|
||||
class Search(TypedDict):
|
||||
response: GeniusAPI.SearchResponse
|
||||
|
||||
|
||||
class GoogleCustomSearchAPI:
|
||||
class Response(TypedDict):
|
||||
"""Search response from the Google Custom Search API.
|
||||
|
||||
If the search returns no results, the :attr:`items` field is not found.
|
||||
"""
|
||||
|
||||
items: NotRequired[list[GoogleCustomSearchAPI.Item]]
|
||||
|
||||
class Item(TypedDict):
|
||||
"""A Google Custom Search API result item.
|
||||
|
||||
:attr:`title` field is shown to the user in the search interface, thus
|
||||
it gets truncated with an ellipsis for longer queries. For most
|
||||
results, the full title is available as ``og:title`` metatag found
|
||||
under the :attr:`pagemap` field. Note neither this metatag nor the
|
||||
``pagemap`` field is guaranteed to be present in the data.
|
||||
"""
|
||||
|
||||
title: str
|
||||
link: str
|
||||
pagemap: NotRequired[GoogleCustomSearchAPI.Pagemap]
|
||||
|
||||
class Pagemap(TypedDict):
|
||||
"""Pagemap data with a single meta tags dict in a list."""
|
||||
|
||||
metatags: list[JSONDict]
|
||||
|
||||
|
||||
class TranslatorAPI:
|
||||
class Language(TypedDict):
|
||||
"""Language data returned by the translator API."""
|
||||
|
||||
language: str
|
||||
score: float
|
||||
|
||||
class Translation(TypedDict):
|
||||
"""Translation data returned by the translator API."""
|
||||
|
||||
text: str
|
||||
to: str
|
||||
|
||||
class Response(TypedDict):
|
||||
"""Response from the translator API."""
|
||||
|
||||
detectedLanguage: TranslatorAPI.Language
|
||||
translations: list[TranslatorAPI.Translation]
|
||||
@@ -5,6 +5,7 @@ from typing_extensions import TypedDict
|
||||
|
||||
class ImageSizes(TypedDict, total=False):
|
||||
"""Image URL set; keys are size codes (o=original, l=large, b=big, t=thumb)."""
|
||||
|
||||
o: str
|
||||
l: str
|
||||
b: str
|
||||
@@ -19,7 +20,7 @@ class ArtistType(TypedDict):
|
||||
|
||||
|
||||
class VideoType(TypedDict):
|
||||
id: int
|
||||
id: str
|
||||
production_status: str
|
||||
song_title: str
|
||||
url: str
|
||||
|
||||
228
beetsplug/beets_music_videos/utils/mapping.py
Normal file
228
beetsplug/beets_music_videos/utils/mapping.py
Normal file
@@ -0,0 +1,228 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import beets
|
||||
from urllib.parse import urljoin
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from beets.autotag.hooks import TrackInfo
|
||||
from .._typing import JSONDict
|
||||
|
||||
MB_BASE_URL = "https://musicbrainz.org/"
|
||||
|
||||
|
||||
def _artist_ids(credit: list[JSONDict]) -> list[str]:
|
||||
"""
|
||||
Given a list representing an ``artist-credit``,
|
||||
return a list of artist IDs
|
||||
"""
|
||||
artist_ids: list[str] = []
|
||||
for el in credit:
|
||||
artist_ids.append(el["artist"]["id"])
|
||||
|
||||
return artist_ids
|
||||
|
||||
|
||||
def _get_related_artist_names(relations: list[JSONDict], relation_type: str) -> str:
|
||||
"""Given a list representing the artist relationships extract the names of
|
||||
the remixers and concatenate them.
|
||||
"""
|
||||
related_artists: list[str] = []
|
||||
|
||||
for relation in relations:
|
||||
if relation["type"] == relation_type:
|
||||
related_artists.append(relation["artist"]["name"])
|
||||
|
||||
return ", ".join(related_artists)
|
||||
|
||||
|
||||
def track_url(trackid: str) -> str:
|
||||
return urljoin(MB_BASE_URL, f"recording/{trackid}")
|
||||
|
||||
|
||||
def _preferred_alias(
|
||||
aliases: list[JSONDict], languages: list[str] | None = None
|
||||
) -> JSONDict | None:
|
||||
"""Given a list of alias structures for an artist credit, select
|
||||
and return the user's preferred alias or None if no matching
|
||||
"""
|
||||
if not aliases:
|
||||
return None
|
||||
|
||||
# Only consider aliases that have locales set.
|
||||
valid_aliases = [a for a in aliases if "locale" in a]
|
||||
|
||||
# Get any ignored alias types and lower case them to prevent case issues
|
||||
ignored_alias_types = beets.config["import"]["ignored_alias_types"].as_str_seq()
|
||||
ignored_alias_types = [a.lower() for a in ignored_alias_types]
|
||||
|
||||
# Search configured locales in order.
|
||||
if languages is None:
|
||||
languages = beets.config["import"]["languages"].as_str_seq()
|
||||
|
||||
for locale in languages:
|
||||
# Find matching primary aliases for this locale that are not
|
||||
# being ignored
|
||||
matches: list[JSONDict] = []
|
||||
for alias in valid_aliases:
|
||||
if (
|
||||
alias["locale"] == locale
|
||||
and alias.get("primary")
|
||||
and (alias.get("type") or "").lower() not in ignored_alias_types
|
||||
):
|
||||
matches.append(alias)
|
||||
|
||||
# Skip to the next locale if we have no matches
|
||||
if not matches:
|
||||
continue
|
||||
|
||||
return matches[0]
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def _flatten_artist_credit(credit: list[JSONDict]) -> tuple[str, str, str]:
|
||||
"""Given a list representing an ``artist-credit`` block, flatten the
|
||||
data into a triple of joined artist name strings: canonical, sort, and
|
||||
credit.
|
||||
"""
|
||||
artist_parts, artist_sort_parts, artist_credit_parts = _multi_artist_credit(
|
||||
credit, include_join_phrase=True
|
||||
)
|
||||
return (
|
||||
"".join(artist_parts),
|
||||
"".join(artist_sort_parts),
|
||||
"".join(artist_credit_parts),
|
||||
)
|
||||
|
||||
|
||||
def _multi_artist_credit(
|
||||
credit: list[JSONDict], include_join_phrase: bool
|
||||
) -> tuple[list[str], list[str], list[str]]:
|
||||
"""Given a list representing an ``artist-credit`` block, accumulate
|
||||
data into a triple of joined artist name lists: canonical, sort, and
|
||||
credit.
|
||||
"""
|
||||
artist_parts: list[str] = []
|
||||
artist_sort_parts: list[str] = []
|
||||
artist_credit_parts: list[str] = []
|
||||
for el in credit:
|
||||
alias = _preferred_alias(el["artist"].get("aliases", ()))
|
||||
|
||||
# An artist.
|
||||
if alias:
|
||||
cur_artist_name = alias["name"]
|
||||
else:
|
||||
cur_artist_name = el["artist"]["name"]
|
||||
artist_parts.append(cur_artist_name)
|
||||
|
||||
# Artist sort name.
|
||||
if alias:
|
||||
artist_sort_parts.append(alias["sort-name"])
|
||||
elif "sort-name" in el["artist"]:
|
||||
artist_sort_parts.append(el["artist"]["sort-name"])
|
||||
else:
|
||||
artist_sort_parts.append(cur_artist_name)
|
||||
|
||||
# Artist credit.
|
||||
if "name" in el:
|
||||
artist_credit_parts.append(el["name"])
|
||||
else:
|
||||
artist_credit_parts.append(cur_artist_name)
|
||||
|
||||
if include_join_phrase and (joinphrase := el.get("joinphrase")):
|
||||
artist_parts.append(joinphrase)
|
||||
artist_sort_parts.append(joinphrase)
|
||||
artist_credit_parts.append(joinphrase)
|
||||
|
||||
return (
|
||||
artist_parts,
|
||||
artist_sort_parts,
|
||||
artist_credit_parts,
|
||||
)
|
||||
|
||||
|
||||
def map_mb_to_trackinfo(
|
||||
recording: JSONDict,
|
||||
index: int | None = None,
|
||||
medium: int | None = None,
|
||||
medium_index: int | None = None,
|
||||
medium_total: int | None = None,
|
||||
) -> TrackInfo:
|
||||
info = beets.autotag.hooks.TrackInfo(
|
||||
title=recording["title"],
|
||||
track_id=recording["id"],
|
||||
index=index,
|
||||
medium=medium,
|
||||
medium_index=medium_index,
|
||||
medium_total=medium_total,
|
||||
data_source="musicbrainz",
|
||||
data_url=track_url(recording["id"]),
|
||||
)
|
||||
|
||||
info.trackdisambig = recording.get("disambiguation")
|
||||
lyricist: list[str] = []
|
||||
composer: list[str] = []
|
||||
composer_sort: list[str] = []
|
||||
|
||||
if recording.get("artist-credit"):
|
||||
(info.artist, info.artist_sort, info.artist_credit) = _flatten_artist_credit(
|
||||
recording["artist-credit"]
|
||||
)
|
||||
(
|
||||
info.artists,
|
||||
info.artists_sort,
|
||||
info.artists_credit,
|
||||
) = _multi_artist_credit(recording["artist-credit"], include_join_phrase=False)
|
||||
|
||||
info.artists_ids = _artist_ids(recording["artist-credit"])
|
||||
info.artist_id = info.artists_ids[0]
|
||||
|
||||
if recording.get("artist-relations"):
|
||||
info.remixer = _get_related_artist_names(
|
||||
recording["artist-relations"], relation_type="remixer"
|
||||
)
|
||||
|
||||
if recording.get("length"):
|
||||
info.length = int(recording["length"]) / 1000.0
|
||||
|
||||
if recording.get("isrcs"):
|
||||
info.isrc = ";".join(recording["isrcs"])
|
||||
|
||||
for work_relation in recording.get("work-relations", ()):
|
||||
if work_relation["type"] != "performance":
|
||||
continue
|
||||
info.work = work_relation["work"]["title"]
|
||||
info.mb_workid = work_relation["work"]["id"]
|
||||
if "disambiguation" in work_relation["work"]:
|
||||
info.work_disambig = work_relation["work"]["disambiguation"]
|
||||
|
||||
for artist_relation in work_relation["work"].get("artist-relations", ()):
|
||||
if "type" in artist_relation:
|
||||
type = artist_relation["type"]
|
||||
if type == "lyricist":
|
||||
lyricist.append(artist_relation["artist"]["name"])
|
||||
elif type == "composer":
|
||||
composer.append(artist_relation["artist"]["name"])
|
||||
composer_sort.append(artist_relation["artist"]["sort-name"])
|
||||
if lyricist:
|
||||
info.lyricist = ", ".join(lyricist)
|
||||
if composer:
|
||||
info.composer = ", ".join(composer)
|
||||
info.composer_sort = ", ".join(composer_sort)
|
||||
|
||||
arranger: list[str] = []
|
||||
for artist_relation in recording.get("artist-relations", ()):
|
||||
if "type" in artist_relation:
|
||||
type = artist_relation["type"]
|
||||
if type == "arranger":
|
||||
arranger.append(artist_relation["artist"]["name"])
|
||||
if arranger:
|
||||
info.arranger = ", ".join(arranger)
|
||||
|
||||
# Supplementary fields provided by plugins
|
||||
extra_trackdatas = beets.plugins.send("mb_track_extract", data=recording)
|
||||
for extra_trackdata in extra_trackdatas:
|
||||
info.update(extra_trackdata)
|
||||
|
||||
return info
|
||||
@@ -1,4 +1,4 @@
|
||||
directory: ./dev-music
|
||||
directory: /mnt/user/data/music_videos/organized/
|
||||
library: ./dev-library.db
|
||||
|
||||
# Enable the in-development plugin from this repo.
|
||||
@@ -17,4 +17,4 @@ paths:
|
||||
singleton: '$artist - $title - $year'
|
||||
|
||||
beets_music_videos:
|
||||
imvdb_api_key: mXfQwBkWPhaUE2y9NbmMEE9JbJ37gtfmRCqjkAGC
|
||||
imvdb_api_key: mXfQwBkWPhaUE2y9NbmMEE9JbJ37gtfmRCqjkAGC
|
||||
|
||||
BIN
dev-library.db
BIN
dev-library.db
Binary file not shown.
4
grabbed.txt
Normal file
4
grabbed.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
vimeo 59530848
|
||||
vimeo 942367640
|
||||
vimeo 1112599798
|
||||
youtube bPjaTcWHi-g
|
||||
274
poetry.lock
generated
274
poetry.lock
generated
@@ -274,6 +274,21 @@ files = [
|
||||
{file = "filetype-1.2.0.tar.gz", hash = "sha256:66b56cd6474bf41d8c54660347d37afcc3f7d1970648de365c102ef77548aadb"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fuzzywuzzy"
|
||||
version = "0.18.0"
|
||||
description = "Fuzzy string matching in python"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "fuzzywuzzy-0.18.0-py2.py3-none-any.whl", hash = "sha256:928244b28db720d1e0ee7587acf660ea49d7e4c632569cad4f1cd7e68a5f0993"},
|
||||
{file = "fuzzywuzzy-0.18.0.tar.gz", hash = "sha256:45016e92264780e58972dca1b3d939ac864b78437422beecebb3095f8efd00e8"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
speedup = ["python-levenshtein (>=0.12)"]
|
||||
|
||||
[[package]]
|
||||
name = "h11"
|
||||
version = "0.16.0"
|
||||
@@ -609,6 +624,102 @@ pydantic = ">=2,<3"
|
||||
python-mimeparse = ">=2,<3"
|
||||
typing-extensions = ">=4.9,<5.0"
|
||||
|
||||
[[package]]
|
||||
name = "levenshtein"
|
||||
version = "0.27.3"
|
||||
description = "Python extension for computing string edit distances and similarities."
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "levenshtein-0.27.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d61eff70799fd5e710625da8a13e5adabd62bfd9f70abb9c531af6cad458cd27"},
|
||||
{file = "levenshtein-0.27.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:477efed87edf72ad0d3870038479ed2f63020a42e69c6a38a32a550e51f8e70e"},
|
||||
{file = "levenshtein-0.27.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8ef99b9827d7d1100fc4398ac5522bd56766b894561c0cbdea0a01b93f24e642"},
|
||||
{file = "levenshtein-0.27.3-cp310-cp310-manylinux_2_24_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9091e8ca9fff6088836abf372f8871fb480e44603defa526e1c3ae2f1d70acc5"},
|
||||
{file = "levenshtein-0.27.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6ffdb2329712c5595eda3532a4f701f87f6c73a0f7aaac240681bf0b54310d63"},
|
||||
{file = "levenshtein-0.27.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:35856330eac1b968b45a5abbc4a3d14279bd9d1224be727cb1aac9ac4928a419"},
|
||||
{file = "levenshtein-0.27.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:5377e237f6a13f5b0618621cca7992848993470c011716c3ad09cdf19c3b13ab"},
|
||||
{file = "levenshtein-0.27.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e30614186eb5c43833b62ae7d893a116b88373eec8cf3f3d62ba51aa5962d8ea"},
|
||||
{file = "levenshtein-0.27.3-cp310-cp310-win32.whl", hash = "sha256:5499342fd6b003bd5abc28790c7b333884838f7fd8c50570a6520bbaf5e2a35b"},
|
||||
{file = "levenshtein-0.27.3-cp310-cp310-win_amd64.whl", hash = "sha256:9e2792730388bec6a85d4d3e3a9b53b8a4b509722bea1a78a39a1a0a7d8f0e13"},
|
||||
{file = "levenshtein-0.27.3-cp310-cp310-win_arm64.whl", hash = "sha256:8a2a274b55562a49c6e9dadb16d05f6c27ffa98906b55d5c122893457ca6e464"},
|
||||
{file = "levenshtein-0.27.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:245b6ffb6e1b0828cafbce35c500cb3265d0962c121d090669f177968c5a2980"},
|
||||
{file = "levenshtein-0.27.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8f44c98fa23f489eb7b2ad87d5dd24b6a784434bb5edb73f6b0513309c949690"},
|
||||
{file = "levenshtein-0.27.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f5f85a1fc96dfc147bba82b4c67d6346ea26c27ef77a6a9de689118e26dddbe"},
|
||||
{file = "levenshtein-0.27.3-cp311-cp311-manylinux_2_24_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:18ceddd38d0e990d2c1c9b72f3e191dace87e2f8f0446207ce9e9cd2bfdfc8a1"},
|
||||
{file = "levenshtein-0.27.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:222b81adca29ee4128183328c6e1b25a48c817d14a008ab49e74be9df963b293"},
|
||||
{file = "levenshtein-0.27.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ee3769ab6e89c24f901e6b7004100630e86721464d7d0384860a322d7953d3a5"},
|
||||
{file = "levenshtein-0.27.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:03eba8fda9f3f2b4b0760263fa20b20a90ab00cbeeab4d0d9d899b4f77912b0a"},
|
||||
{file = "levenshtein-0.27.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c870b19e2d5c7bc7f16213cc10312b82d873a4d46e1c6d51857a12ef39a76552"},
|
||||
{file = "levenshtein-0.27.3-cp311-cp311-win32.whl", hash = "sha256:1987622e9b8ba2ae47dc27469291da1f58462660fa34f4358e9d9c1830fb1355"},
|
||||
{file = "levenshtein-0.27.3-cp311-cp311-win_amd64.whl", hash = "sha256:a2b2aa81851e01bb09667b07e80c3fbf0f5a7c6ee9cd80caf43cce705e65832a"},
|
||||
{file = "levenshtein-0.27.3-cp311-cp311-win_arm64.whl", hash = "sha256:a084b335c54def1aef9a594b7163faa44dd00056323808bab783f43d8e4c1395"},
|
||||
{file = "levenshtein-0.27.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2de7f095b0ca8e44de9de986ccba661cd0dec3511c751b499e76b60da46805e9"},
|
||||
{file = "levenshtein-0.27.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d9b8b29e5d5145a3c958664c85151b1bb4b26e4ca764380b947e6a96a321217c"},
|
||||
{file = "levenshtein-0.27.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fc975465a51b1c5889eadee1a583b81fba46372b4b22df28973e49e8ddb8f54a"},
|
||||
{file = "levenshtein-0.27.3-cp312-cp312-manylinux_2_24_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:57573ed885118554770979fdee584071b66103f6d50beddeabb54607a1213d81"},
|
||||
{file = "levenshtein-0.27.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23aff800a6dd5d91bb3754a6092085aa7ad46b28e497682c155c74f681cfaa2d"},
|
||||
{file = "levenshtein-0.27.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c08a952432b8ad9dccb145f812176db94c52cda732311ddc08d29fd3bf185b0a"},
|
||||
{file = "levenshtein-0.27.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:3bfcb2d78ab9cc06a1e75da8fcfb7a430fe513d66cfe54c07e50f32805e5e6db"},
|
||||
{file = "levenshtein-0.27.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ba7235f6dcb31a217247468295e2dd4c6c1d3ac81629dc5d355d93e1a5f4c185"},
|
||||
{file = "levenshtein-0.27.3-cp312-cp312-win32.whl", hash = "sha256:ea80d70f1d18c161a209be556b9094968627cbaae620e102459ef9c320a98cbb"},
|
||||
{file = "levenshtein-0.27.3-cp312-cp312-win_amd64.whl", hash = "sha256:fbaa1219d9b2d955339a37e684256a861e9274a3fe3a6ee1b8ea8724c3231ed9"},
|
||||
{file = "levenshtein-0.27.3-cp312-cp312-win_arm64.whl", hash = "sha256:2edbaa84f887ea1d9d8e4440af3fdda44769a7855d581c6248d7ee51518402a8"},
|
||||
{file = "levenshtein-0.27.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e55aa9f9453fd89d4a9ff1f3c4a650b307d5f61a7eed0568a52fbd2ff2eba107"},
|
||||
{file = "levenshtein-0.27.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ae4d484453c48939ecd01c5c213530c68dd5cd6e5090f0091ef69799ec7a8a9f"},
|
||||
{file = "levenshtein-0.27.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d18659832567ee387b266be390da0de356a3aa6cf0e8bc009b6042d8188e131f"},
|
||||
{file = "levenshtein-0.27.3-cp313-cp313-manylinux_2_24_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027b3d142cc8ea2ab4e60444d7175f65a94dde22a54382b2f7b47cc24936eb53"},
|
||||
{file = "levenshtein-0.27.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ffdca6989368cc64f347f0423c528520f12775b812e170a0eb0c10e4c9b0f3ff"},
|
||||
{file = "levenshtein-0.27.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fa00ab389386032b02a1c9050ec3c6aa824d2bbcc692548fdc44a46b71c058c6"},
|
||||
{file = "levenshtein-0.27.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:691c9003c6c481b899a5c2f72e8ce05a6d956a9668dc75f2a3ce9f4381a76dc6"},
|
||||
{file = "levenshtein-0.27.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:12f7fc8bf0c24492fe97905348e020b55b9fc6dbaab7cd452566d1a466cb5e15"},
|
||||
{file = "levenshtein-0.27.3-cp313-cp313-win32.whl", hash = "sha256:9f4872e4e19ee48eed39f214eea4eca42e5ef303f8a4a488d8312370674dbf3a"},
|
||||
{file = "levenshtein-0.27.3-cp313-cp313-win_amd64.whl", hash = "sha256:83aa2422e9a9af2c9d3e56a53e3e8de6bae58d1793628cae48c4282577c5c2c6"},
|
||||
{file = "levenshtein-0.27.3-cp313-cp313-win_arm64.whl", hash = "sha256:d4adaf1edbcf38c3f2e290b52f4dcb5c6deff20308c26ef1127a106bc2d23e9f"},
|
||||
{file = "levenshtein-0.27.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:272e24764b8210337b65a1cfd69ce40df5d2de1a3baf1234e7f06d2826ba2e7a"},
|
||||
{file = "levenshtein-0.27.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:329a8e748a4e14d56daaa11f07bce3fde53385d05bad6b3f6dd9ee7802cdc915"},
|
||||
{file = "levenshtein-0.27.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a5fea1a9c6b9cc8729e467e2174b4359ff6bac27356bb5f31898e596b4ce133a"},
|
||||
{file = "levenshtein-0.27.3-cp313-cp313t-manylinux_2_24_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3a61aa825819b6356555091d8a575d1235bd9c3753a68316a261af4856c3b487"},
|
||||
{file = "levenshtein-0.27.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a51de7a514e8183f0a82f2947d01b014d2391426543b1c076bf5a26328cec4e4"},
|
||||
{file = "levenshtein-0.27.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:53cbf726d6e92040c9be7e594d959d496bd62597ea48eba9d96105898acbeafe"},
|
||||
{file = "levenshtein-0.27.3-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:191b358afead8561c4fcfed22f83c13bb6c8da5f5789e277f0c5aa1c45ca612f"},
|
||||
{file = "levenshtein-0.27.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ba1318d0635b834b8f0397014a7c43f007e65fce396a47614780c881bdff828b"},
|
||||
{file = "levenshtein-0.27.3-cp313-cp313t-win32.whl", hash = "sha256:8dd9e1db6c3b35567043e155a686e4827c4aa28a594bd81e3eea84d3a1bd5875"},
|
||||
{file = "levenshtein-0.27.3-cp313-cp313t-win_amd64.whl", hash = "sha256:7813ecdac7a6223264ebfea0c8d69959c43d21a99694ef28018d22c4265c2af6"},
|
||||
{file = "levenshtein-0.27.3-cp313-cp313t-win_arm64.whl", hash = "sha256:8f05a0d23d13a6f802c7af595d0e43f5b9b98b6ed390cec7a35cb5d6693b882b"},
|
||||
{file = "levenshtein-0.27.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a6728bfae9a86002f0223576675fc7e2a6e7735da47185a1d13d1eaaa73dd4be"},
|
||||
{file = "levenshtein-0.27.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8e5037c4a6f97a238e24aad6f98a1e984348b7931b1b04b6bd02bd4f8238150d"},
|
||||
{file = "levenshtein-0.27.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c6cf5ecf9026bf24cf66ad019c6583f50058fae3e1b3c20e8812455b55d597f1"},
|
||||
{file = "levenshtein-0.27.3-cp314-cp314-manylinux_2_24_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9285084bd2fc19adb47dab54ed4a71f57f78fe0d754e4a01e3c75409a25aed24"},
|
||||
{file = "levenshtein-0.27.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce3bbbe92172a08b599d79956182c6b7ab6ec8d4adbe7237417a363b968ad87b"},
|
||||
{file = "levenshtein-0.27.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9dac48fab9d166ca90e12fb6cf6c7c8eb9c41aacf7136584411e20f7f136f745"},
|
||||
{file = "levenshtein-0.27.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d37a83722dc5326c93d17078e926c4732dc4f3488dc017c6839e34cd16af92b7"},
|
||||
{file = "levenshtein-0.27.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3466cb8294ce586e49dd467560a153ab8d296015c538223f149f9aefd3d9f955"},
|
||||
{file = "levenshtein-0.27.3-cp314-cp314-win32.whl", hash = "sha256:c848bf2457b268672b7e9e73b44f18f49856420ac50b2564cf115a6e4ef82688"},
|
||||
{file = "levenshtein-0.27.3-cp314-cp314-win_amd64.whl", hash = "sha256:742633f024362a4ed6ef9d7e75d68f74b041ae738985fcf55a0e6d1d4cade438"},
|
||||
{file = "levenshtein-0.27.3-cp314-cp314-win_arm64.whl", hash = "sha256:9eed6851224b19e8d588ddb8eb8a4ae3c2dcabf3d1213985f0b94a67e517b1df"},
|
||||
{file = "levenshtein-0.27.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:77de69a345c76227b51a4521cd85442eb3da54c7eb6a06663a20c058fc49e683"},
|
||||
{file = "levenshtein-0.27.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:eba2756dc1f5b962b0ff80e49abb2153d5e809cc5e7fa5e85be9410ce474795d"},
|
||||
{file = "levenshtein-0.27.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c8fcb498287e971d84260f67808ff1a06b3f6212d80fea75cf5155db80606ff"},
|
||||
{file = "levenshtein-0.27.3-cp314-cp314t-manylinux_2_24_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f067092c67464faab13e00a5c1a80da93baca8955d4d49579861400762e35591"},
|
||||
{file = "levenshtein-0.27.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:92415f32c68491203f2855d05eef3277d376182d014cf0859c013c89f277fbbf"},
|
||||
{file = "levenshtein-0.27.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ef61eeaf1e0a42d7d947978d981fe4b9426b98b3dd8c1582c535f10dee044c3f"},
|
||||
{file = "levenshtein-0.27.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:103bb2e9049d1aa0d1216dd09c1c9106ecfe7541bbdc1a0490b9357d42eec8f2"},
|
||||
{file = "levenshtein-0.27.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6a64ddd1986b2a4c468b09544382287315c53585eb067f6e200c337741e057ee"},
|
||||
{file = "levenshtein-0.27.3-cp314-cp314t-win32.whl", hash = "sha256:957244f27dc284ccb030a8b77b8a00deb7eefdcd70052a4b1d96f375780ae9dc"},
|
||||
{file = "levenshtein-0.27.3-cp314-cp314t-win_amd64.whl", hash = "sha256:ccd7eaa6d8048c3ec07c93cfbcdefd4a3ae8c6aca3a370f2023ee69341e5f076"},
|
||||
{file = "levenshtein-0.27.3-cp314-cp314t-win_arm64.whl", hash = "sha256:1d8520b89b7a27bb5aadbcc156715619bcbf556a8ac46ad932470945dca6e1bd"},
|
||||
{file = "levenshtein-0.27.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:d2d7d22b6117a143f0cf101fe18a3ca90bd949fc33716a42d6165b9768d4a78c"},
|
||||
{file = "levenshtein-0.27.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:a55e7a2f317abd28576636e1f840fd268261f447c496a8481a9997a5ce889c59"},
|
||||
{file = "levenshtein-0.27.3-pp311-pypy311_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:55fa5f11952c38186bd4719e936eb4595b3d519218634924928787c36840256c"},
|
||||
{file = "levenshtein-0.27.3-pp311-pypy311_pp73-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:559d3588e6766134d95f84f830cf40166360e1769d253f5f83474bff10a24341"},
|
||||
{file = "levenshtein-0.27.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:82d40da143c1b9e27adcd34a33dfcc4a0761aa717c5f618b9c6f57dec5d7a958"},
|
||||
{file = "levenshtein-0.27.3.tar.gz", hash = "sha256:1ac326b2c84215795163d8a5af471188918b8797b4953ec87aaba22c9c1f9fc0"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
rapidfuzz = ">=3.9.0,<4.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "llvmlite"
|
||||
version = "0.46.0"
|
||||
@@ -1077,6 +1188,21 @@ pygments = ">=2.7.2"
|
||||
[package.extras]
|
||||
dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"]
|
||||
|
||||
[[package]]
|
||||
name = "python-levenshtein"
|
||||
version = "0.27.3"
|
||||
description = "Python extension for computing string edit distances and similarities."
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "python_levenshtein-0.27.3-py3-none-any.whl", hash = "sha256:5d6168a8e8befb25abf04d2952368a446722be10e8ced218d0dc4fd3703a43a1"},
|
||||
{file = "python_levenshtein-0.27.3.tar.gz", hash = "sha256:27dc2d65aeb62a7d6852388f197073296370779286c0860b087357f3b8129a62"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
Levenshtein = "0.27.3"
|
||||
|
||||
[[package]]
|
||||
name = "python-mimeparse"
|
||||
version = "2.0.0"
|
||||
@@ -1175,6 +1301,102 @@ files = [
|
||||
{file = "pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rapidfuzz"
|
||||
version = "3.14.3"
|
||||
description = "rapid fuzzy string matching"
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "rapidfuzz-3.14.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b9fcd4d751a4fffa17aed1dde41647923c72c74af02459ad1222e3b0022da3a1"},
|
||||
{file = "rapidfuzz-3.14.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ad73afb688b36864a8d9b7344a9cf6da186c471e5790cbf541a635ee0f457f2"},
|
||||
{file = "rapidfuzz-3.14.3-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c5fb2d978a601820d2cfd111e2c221a9a7bfdf84b41a3ccbb96ceef29f2f1ac7"},
|
||||
{file = "rapidfuzz-3.14.3-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1d83b8b712fa37e06d59f29a4b49e2e9e8635e908fbc21552fe4d1163db9d2a1"},
|
||||
{file = "rapidfuzz-3.14.3-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:dc8c07801df5206b81ed6bd6c35cb520cf9b6c64b9b0d19d699f8633dc942897"},
|
||||
{file = "rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c71ce6d4231e5ef2e33caa952bfe671cb9fd42e2afb11952df9fad41d5c821f9"},
|
||||
{file = "rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:0e38828d1381a0cceb8a4831212b2f673d46f5129a1897b0451c883eaf4a1747"},
|
||||
{file = "rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da2a007434323904719158e50f3076a4dadb176ce43df28ed14610c773cc9825"},
|
||||
{file = "rapidfuzz-3.14.3-cp310-cp310-win32.whl", hash = "sha256:fce3152f94afcfd12f3dd8cf51e48fa606e3cb56719bccebe3b401f43d0714f9"},
|
||||
{file = "rapidfuzz-3.14.3-cp310-cp310-win_amd64.whl", hash = "sha256:37d3c653af15cd88592633e942f5407cb4c64184efab163c40fcebad05f25141"},
|
||||
{file = "rapidfuzz-3.14.3-cp310-cp310-win_arm64.whl", hash = "sha256:cc594bbcd3c62f647dfac66800f307beaee56b22aaba1c005e9c4c40ed733923"},
|
||||
{file = "rapidfuzz-3.14.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dea2d113e260a5da0c4003e0a5e9fdf24a9dc2bb9eaa43abd030a1e46ce7837d"},
|
||||
{file = "rapidfuzz-3.14.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e6c31a4aa68cfa75d7eede8b0ed24b9e458447db604c2db53f358be9843d81d3"},
|
||||
{file = "rapidfuzz-3.14.3-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:02821366d928e68ddcb567fed8723dad7ea3a979fada6283e6914d5858674850"},
|
||||
{file = "rapidfuzz-3.14.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cfe8df315ab4e6db4e1be72c5170f8e66021acde22cd2f9d04d2058a9fd8162e"},
|
||||
{file = "rapidfuzz-3.14.3-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:769f31c60cd79420188fcdb3c823227fc4a6deb35cafec9d14045c7f6743acae"},
|
||||
{file = "rapidfuzz-3.14.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:54fa03062124e73086dae66a3451c553c1e20a39c077fd704dc7154092c34c63"},
|
||||
{file = "rapidfuzz-3.14.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:834d1e818005ed0d4ae38f6b87b86fad9b0a74085467ece0727d20e15077c094"},
|
||||
{file = "rapidfuzz-3.14.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:948b00e8476a91f510dd1ec07272efc7d78c275d83b630455559671d4e33b678"},
|
||||
{file = "rapidfuzz-3.14.3-cp311-cp311-win32.whl", hash = "sha256:43d0305c36f504232f18ea04e55f2059bb89f169d3119c4ea96a0e15b59e2a91"},
|
||||
{file = "rapidfuzz-3.14.3-cp311-cp311-win_amd64.whl", hash = "sha256:ef6bf930b947bd0735c550683939a032090f1d688dfd8861d6b45307b96fd5c5"},
|
||||
{file = "rapidfuzz-3.14.3-cp311-cp311-win_arm64.whl", hash = "sha256:f3eb0ff3b75d6fdccd40b55e7414bb859a1cda77c52762c9c82b85569f5088e7"},
|
||||
{file = "rapidfuzz-3.14.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:685c93ea961d135893b5984a5a9851637d23767feabe414ec974f43babbd8226"},
|
||||
{file = "rapidfuzz-3.14.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fa7c8f26f009f8c673fbfb443792f0cf8cf50c4e18121ff1e285b5e08a94fbdb"},
|
||||
{file = "rapidfuzz-3.14.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57f878330c8d361b2ce76cebb8e3e1dc827293b6abf404e67d53260d27b5d941"},
|
||||
{file = "rapidfuzz-3.14.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c5f545f454871e6af05753a0172849c82feaf0f521c5ca62ba09e1b382d6382"},
|
||||
{file = "rapidfuzz-3.14.3-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:07aa0b5d8863e3151e05026a28e0d924accf0a7a3b605da978f0359bb804df43"},
|
||||
{file = "rapidfuzz-3.14.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73b07566bc7e010e7b5bd490fb04bb312e820970180df6b5655e9e6224c137db"},
|
||||
{file = "rapidfuzz-3.14.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6de00eb84c71476af7d3110cf25d8fe7c792d7f5fa86764ef0b4ca97e78ca3ed"},
|
||||
{file = "rapidfuzz-3.14.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d7843a1abf0091773a530636fdd2a49a41bcae22f9910b86b4f903e76ddc82dc"},
|
||||
{file = "rapidfuzz-3.14.3-cp312-cp312-win32.whl", hash = "sha256:dea97ac3ca18cd3ba8f3d04b5c1fe4aa60e58e8d9b7793d3bd595fdb04128d7a"},
|
||||
{file = "rapidfuzz-3.14.3-cp312-cp312-win_amd64.whl", hash = "sha256:b5100fd6bcee4d27f28f4e0a1c6b5127bc8ba7c2a9959cad9eab0bf4a7ab3329"},
|
||||
{file = "rapidfuzz-3.14.3-cp312-cp312-win_arm64.whl", hash = "sha256:4e49c9e992bc5fc873bd0fff7ef16a4405130ec42f2ce3d2b735ba5d3d4eb70f"},
|
||||
{file = "rapidfuzz-3.14.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dbcb726064b12f356bf10fffdb6db4b6dce5390b23627c08652b3f6e49aa56ae"},
|
||||
{file = "rapidfuzz-3.14.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1704fc70d214294e554a2421b473779bcdeef715881c5e927dc0f11e1692a0ff"},
|
||||
{file = "rapidfuzz-3.14.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc65e72790ddfd310c2c8912b45106e3800fefe160b0c2ef4d6b6fec4e826457"},
|
||||
{file = "rapidfuzz-3.14.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43e38c1305cffae8472572a0584d4ffc2f130865586a81038ca3965301f7c97c"},
|
||||
{file = "rapidfuzz-3.14.3-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:e195a77d06c03c98b3fc06b8a28576ba824392ce40de8c708f96ce04849a052e"},
|
||||
{file = "rapidfuzz-3.14.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1b7ef2f4b8583a744338a18f12c69693c194fb6777c0e9ada98cd4d9e8f09d10"},
|
||||
{file = "rapidfuzz-3.14.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a2135b138bcdcb4c3742d417f215ac2d8c2b87bde15b0feede231ae95f09ec41"},
|
||||
{file = "rapidfuzz-3.14.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:33a325ed0e8e1aa20c3e75f8ab057a7b248fdea7843c2a19ade0008906c14af0"},
|
||||
{file = "rapidfuzz-3.14.3-cp313-cp313-win32.whl", hash = "sha256:8383b6d0d92f6cd008f3c9216535be215a064b2cc890398a678b56e6d280cb63"},
|
||||
{file = "rapidfuzz-3.14.3-cp313-cp313-win_amd64.whl", hash = "sha256:e6b5e3036976f0fde888687d91be86d81f9ac5f7b02e218913c38285b756be6c"},
|
||||
{file = "rapidfuzz-3.14.3-cp313-cp313-win_arm64.whl", hash = "sha256:7ba009977601d8b0828bfac9a110b195b3e4e79b350dcfa48c11269a9f1918a0"},
|
||||
{file = "rapidfuzz-3.14.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a0a28add871425c2fe94358c6300bbeb0bc2ed828ca003420ac6825408f5a424"},
|
||||
{file = "rapidfuzz-3.14.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:010e12e2411a4854b0434f920e72b717c43f8ec48d57e7affe5c42ecfa05dd0e"},
|
||||
{file = "rapidfuzz-3.14.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cfc3d57abd83c734d1714ec39c88a34dd69c85474918ebc21296f1e61eb5ca8"},
|
||||
{file = "rapidfuzz-3.14.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89acb8cbb52904f763e5ac238083b9fc193bed8d1f03c80568b20e4cef43a519"},
|
||||
{file = "rapidfuzz-3.14.3-cp313-cp313t-manylinux_2_31_armv7l.whl", hash = "sha256:7d9af908c2f371bfb9c985bd134e295038e3031e666e4b2ade1e7cb7f5af2f1a"},
|
||||
{file = "rapidfuzz-3.14.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:1f1925619627f8798f8c3a391d81071336942e5fe8467bc3c567f982e7ce2897"},
|
||||
{file = "rapidfuzz-3.14.3-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:152555187360978119e98ce3e8263d70dd0c40c7541193fc302e9b7125cf8f58"},
|
||||
{file = "rapidfuzz-3.14.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:52619d25a09546b8db078981ca88939d72caa6b8701edd8b22e16482a38e799f"},
|
||||
{file = "rapidfuzz-3.14.3-cp313-cp313t-win32.whl", hash = "sha256:489ce98a895c98cad284f0a47960c3e264c724cb4cfd47a1430fa091c0c25204"},
|
||||
{file = "rapidfuzz-3.14.3-cp313-cp313t-win_amd64.whl", hash = "sha256:656e52b054d5b5c2524169240e50cfa080b04b1c613c5f90a2465e84888d6f15"},
|
||||
{file = "rapidfuzz-3.14.3-cp313-cp313t-win_arm64.whl", hash = "sha256:c7e40c0a0af02ad6e57e89f62bef8604f55a04ecae90b0ceeda591bbf5923317"},
|
||||
{file = "rapidfuzz-3.14.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:442125473b247227d3f2de807a11da6c08ccf536572d1be943f8e262bae7e4ea"},
|
||||
{file = "rapidfuzz-3.14.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1ec0c8c0c3d4f97ced46b2e191e883f8c82dbbf6d5ebc1842366d7eff13cd5a6"},
|
||||
{file = "rapidfuzz-3.14.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2dc37bc20272f388b8c3a4eba4febc6e77e50a8f450c472def4751e7678f55e4"},
|
||||
{file = "rapidfuzz-3.14.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dee362e7e79bae940a5e2b3f6d09c6554db6a4e301cc68343886c08be99844f1"},
|
||||
{file = "rapidfuzz-3.14.3-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:4b39921df948388a863f0e267edf2c36302983459b021ab928d4b801cbe6a421"},
|
||||
{file = "rapidfuzz-3.14.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:beda6aa9bc44d1d81242e7b291b446be352d3451f8217fcb068fc2933927d53b"},
|
||||
{file = "rapidfuzz-3.14.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:6a014ba09657abfcfeed64b7d09407acb29af436d7fc075b23a298a7e4a6b41c"},
|
||||
{file = "rapidfuzz-3.14.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:32eeafa3abce138bb725550c0e228fc7eaeec7059aa8093d9cbbec2b58c2371a"},
|
||||
{file = "rapidfuzz-3.14.3-cp314-cp314-win32.whl", hash = "sha256:adb44d996fc610c7da8c5048775b21db60dd63b1548f078e95858c05c86876a3"},
|
||||
{file = "rapidfuzz-3.14.3-cp314-cp314-win_amd64.whl", hash = "sha256:f3d15d8527e2b293e38ce6e437631af0708df29eafd7c9fc48210854c94472f9"},
|
||||
{file = "rapidfuzz-3.14.3-cp314-cp314-win_arm64.whl", hash = "sha256:576e4b9012a67e0bf54fccb69a7b6c94d4e86a9540a62f1a5144977359133583"},
|
||||
{file = "rapidfuzz-3.14.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:cec3c0da88562727dd5a5a364bd9efeb535400ff0bfb1443156dd139a1dd7b50"},
|
||||
{file = "rapidfuzz-3.14.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d1fa009f8b1100e4880868137e7bf0501422898f7674f2adcd85d5a67f041296"},
|
||||
{file = "rapidfuzz-3.14.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b86daa7419b5e8b180690efd1fdbac43ff19230803282521c5b5a9c83977655"},
|
||||
{file = "rapidfuzz-3.14.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7bd1816db05d6c5ffb3a4df0a2b7b56fb8c81ef584d08e37058afa217da91b1"},
|
||||
{file = "rapidfuzz-3.14.3-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:33da4bbaf44e9755b0ce192597f3bde7372fe2e381ab305f41b707a95ac57aa7"},
|
||||
{file = "rapidfuzz-3.14.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3fecce764cf5a991ee2195a844196da840aba72029b2612f95ac68a8b74946bf"},
|
||||
{file = "rapidfuzz-3.14.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:ecd7453e02cf072258c3a6b8e930230d789d5d46cc849503729f9ce475d0e785"},
|
||||
{file = "rapidfuzz-3.14.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ea188aa00e9bcae8c8411f006a5f2f06c4607a02f24eab0d8dc58566aa911f35"},
|
||||
{file = "rapidfuzz-3.14.3-cp314-cp314t-win32.whl", hash = "sha256:7ccbf68100c170e9a0581accbe9291850936711548c6688ce3bfb897b8c589ad"},
|
||||
{file = "rapidfuzz-3.14.3-cp314-cp314t-win_amd64.whl", hash = "sha256:9ec02e62ae765a318d6de38df609c57fc6dacc65c0ed1fd489036834fd8a620c"},
|
||||
{file = "rapidfuzz-3.14.3-cp314-cp314t-win_arm64.whl", hash = "sha256:e805e52322ae29aa945baf7168b6c898120fbc16d2b8f940b658a5e9e3999253"},
|
||||
{file = "rapidfuzz-3.14.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7cf174b52cb3ef5d49e45d0a1133b7e7d0ecf770ed01f97ae9962c5c91d97d23"},
|
||||
{file = "rapidfuzz-3.14.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:442cba39957a008dfc5bdef21a9c3f4379e30ffb4e41b8555dbaf4887eca9300"},
|
||||
{file = "rapidfuzz-3.14.3-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1faa0f8f76ba75fd7b142c984947c280ef6558b5067af2ae9b8729b0a0f99ede"},
|
||||
{file = "rapidfuzz-3.14.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1e6eefec45625c634926a9fd46c9e4f31118ac8f3156fff9494422cee45207e6"},
|
||||
{file = "rapidfuzz-3.14.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56fefb4382bb12250f164250240b9dd7772e41c5c8ae976fd598a32292449cc5"},
|
||||
{file = "rapidfuzz-3.14.3.tar.gz", hash = "sha256:2491937177868bc4b1e469087601d53f925e8d270ccc21e07404b4b5814b7b5f"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
all = ["numpy"]
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.32.5"
|
||||
@@ -1320,6 +1542,18 @@ dev = ["click (<8.3.0)", "cython-lint (>=0.12.2)", "mypy (==1.10.0)", "pycodesty
|
||||
doc = ["intersphinx_registry", "jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.19.1)", "jupytext", "linkify-it-py", "matplotlib (>=3.5)", "myst-nb (>=1.2.0)", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0,<8.2.0)", "sphinx-copybutton", "sphinx-design (>=0.4.0)", "tabulate"]
|
||||
test = ["Cython", "array-api-strict (>=2.3.1)", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja ; sys_platform != \"emscripten\"", "pooch", "pytest (>=8.0.0)", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"]
|
||||
|
||||
[[package]]
|
||||
name = "six"
|
||||
version = "1.17.0"
|
||||
description = "Python 2 and 3 compatibility utilities"
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"},
|
||||
{file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sniffio"
|
||||
version = "1.3.1"
|
||||
@@ -1371,6 +1605,44 @@ files = [
|
||||
{file = "Unidecode-1.4.0.tar.gz", hash = "sha256:ce35985008338b676573023acc382d62c264f307c8f7963733405add37ea2b23"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uplink"
|
||||
version = "0.10.0"
|
||||
description = "A Declarative HTTP Client for Python."
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "master.zip", hash = "sha256:d52dcd4ac0594c9c6caee72921ba6d7fda9298496c776e44b28cabe4af8c34ac"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
requests = ">=2.18.0"
|
||||
six = ">=1.13.0"
|
||||
uritemplate = ">=3.0.0"
|
||||
|
||||
[package.extras]
|
||||
aiohttp = ["aiohttp (>=3.8.1)"]
|
||||
marshmallow = ["marshmallow (>=2.15.0)"]
|
||||
pydantic = ["pydantic (>=2.0.0)"]
|
||||
twisted = ["twisted (>=21.7.0)"]
|
||||
|
||||
[package.source]
|
||||
type = "url"
|
||||
url = "https://github.com/prkumar/uplink/archive/master.zip"
|
||||
|
||||
[[package]]
|
||||
name = "uritemplate"
|
||||
version = "4.2.0"
|
||||
description = "Implementation of RFC 6570 URI Templates"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "uritemplate-4.2.0-py3-none-any.whl", hash = "sha256:962201ba1c4edcab02e60f9a0d3821e82dfc5d2d6662a21abd533879bdb8a686"},
|
||||
{file = "uritemplate-4.2.0.tar.gz", hash = "sha256:480c2ed180878955863323eea31b0ede668795de182617fef9c6ca09e6ec9d0e"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "2.6.3"
|
||||
@@ -1392,4 +1664,4 @@ zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""]
|
||||
[metadata]
|
||||
lock-version = "2.1"
|
||||
python-versions = ">=3.12,<4"
|
||||
content-hash = "518f916c70ebbfd3d488eb7df1816c2e8f937cc60989a57b3e22c2de6aba5980"
|
||||
content-hash = "dacbfcb0f8f5f99430fab3e586898eab0d7f5bc9c1883476c5884d26d850a36b"
|
||||
|
||||
@@ -14,7 +14,10 @@ dependencies = [
|
||||
"beets (>=2.6.0,<3.0.0)",
|
||||
"mediainfo (>=0.0.9,<0.0.10)",
|
||||
"typing-extensions (>=4.15.0,<5.0.0)",
|
||||
"lapidary (>=0.12.3,<0.13.0)"
|
||||
"lapidary (>=0.12.3,<0.13.0)",
|
||||
"uplink @ https://github.com/prkumar/uplink/archive/master.zip",
|
||||
"fuzzywuzzy (>=0.18.0,<0.19.0)",
|
||||
"python-levenshtein (>=0.27.3,<0.28.0)"
|
||||
]
|
||||
[tool.poetry]
|
||||
packages = [
|
||||
@@ -24,6 +27,8 @@ packages = [
|
||||
typeCheckingMode = "strict"
|
||||
venvPath = "."
|
||||
venv = ".venv"
|
||||
# Local stubs for packages without type information (e.g. uplink)
|
||||
stubPath = "typings"
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
|
||||
BIN
state.pickle
BIN
state.pickle
Binary file not shown.
42
typings/uplink/__init__.pyi
Normal file
42
typings/uplink/__init__.pyi
Normal file
@@ -0,0 +1,42 @@
|
||||
"""Local type stub for uplink (no upstream stubs). Covers Consumer, Session, get, and Query used by beets_music_videos."""
|
||||
|
||||
from typing import Any, Callable, TypeAlias, TypeVar
|
||||
|
||||
_F = TypeVar("_F", bound=Callable[..., Any])
|
||||
|
||||
# Used as a parameter annotation to mark a query string parameter; the value at runtime is str.
|
||||
Query: TypeAlias = str
|
||||
|
||||
class Session:
|
||||
"""Session for a Consumer instance; holds params, headers, etc."""
|
||||
|
||||
@property
|
||||
def params(self) -> dict[str, Any]: ...
|
||||
@property
|
||||
def headers(self) -> dict[str, Any]: ...
|
||||
@property
|
||||
def base_url(self) -> str: ...
|
||||
@property
|
||||
def auth(self) -> Any: ...
|
||||
|
||||
class Consumer:
|
||||
"""Base consumer class for defining API clients."""
|
||||
|
||||
session: Session
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
base_url: str = "",
|
||||
client: Any = None,
|
||||
converters: Any = (),
|
||||
auth: Any = None,
|
||||
hooks: Any = (),
|
||||
**kwargs: Any,
|
||||
) -> None: ...
|
||||
|
||||
def get(
|
||||
uri: str | None = None,
|
||||
args: tuple[Any, ...] = (),
|
||||
) -> Callable[[_F], _F]:
|
||||
"""Decorator that makes the decorated function an HTTP GET request handler."""
|
||||
...
|
||||
Reference in New Issue
Block a user