Skip to content

Entry

SeaDexEntry

SeaDexEntry(
    base_url: str = "https://releases.moe",
    client: Client | None = None,
)

Client to interact with SeaDex entries.

Parameters:

Name Type Description Default
base_url str

The base URL of SeaDex, used for constructing API queries.

'https://releases.moe'
client Client

An httpx.Client instance used to make requests to SeaDex.

None

Examples:

with SeaDexEntry() as entry:
    tamako = entry.from_title("tamako love story")
    for torrent in tamako.torrents:
        if torrent.is_best and torrent.tracker.is_public():
            print(torrent.release_group)
            #> LYS1TH3A
            #> Okay-Subs
Source code in src/seadex/_entry.py
def __init__(self, base_url: str = "https://releases.moe", client: Client | None = None) -> None:
    """
    Client to interact with SeaDex entries.

    Parameters
    ----------
    base_url : str, optional
        The base URL of SeaDex, used for constructing API queries.
    client : Client, optional
        An [httpx.Client][] instance used to make requests to SeaDex.

        [httpx.Client]: https://www.python-httpx.org/advanced/#client

    Examples
    --------
    ```py
    with SeaDexEntry() as entry:
        tamako = entry.from_title("tamako love story")
        for torrent in tamako.torrents:
            if torrent.is_best and torrent.tracker.is_public():
                print(torrent.release_group)
                #> LYS1TH3A
                #> Okay-Subs
    ```

    """
    self._base_url = base_url
    self._endpoint = urljoin(self._base_url, "/api/collections/entries/records")
    self._client = httpx_client() if client is None else client

base_url property

base_url: str

Base URL, used for constructing API queries.

close

close() -> None

Close the underlying HTTP client connection.

Source code in src/seadex/_entry.py
def close(self) -> None:
    """
    Close the underlying HTTP client connection.
    """
    self._client.close()

from_filename

from_filename(filename: StrPath) -> Iterator[EntryRecord]

Yield entries that contain a torrent with the specified filename.

Parameters:

Name Type Description Default
filename StrPath

The filename to search for.

required

Yields:

Type Description
EntryRecord

The retrieved entry.

Source code in src/seadex/_entry.py
def from_filename(self, filename: StrPath, /) -> Iterator[EntryRecord]:
    """
    Yield entries that contain a torrent with the specified filename.

    Parameters
    ----------
    filename : StrPath
        The filename to search for.

    Yields
    ------
    EntryRecord
        The retrieved entry.

    """
    yield from self.__from_filter(f'trs.files?~\'"name":"{basename(filename)}"\'', paginate=False)

from_filter

from_filter(filter: str) -> Iterator[EntryRecord]

Yield entries from SeaDex that match the given filter expression.

Refer to the filter argument in the PocketBase API documentation for details on constructing valid filter expressions.

Parameters:

Name Type Description Default
filter str

The filter expression.

required

Yields:

Type Description
EntryRecord

The retrieved entry.

Raises:

Type Description
TypeError

If filter is not a string.

Source code in src/seadex/_entry.py
def from_filter(self, filter: str, /) -> Iterator[EntryRecord]:
    """
    Yield entries from SeaDex that match the given filter expression.

    Refer to the `filter` argument in the [PocketBase API documentation][]
    for details on constructing valid filter expressions.

    [PocketBase API documentation]: https://pocketbase.io/docs/api-records/#listsearch-records

    Parameters
    ----------
    filter : str
        The filter expression.

    Yields
    ------
    EntryRecord
        The retrieved entry.

    Raises
    ------
    TypeError
        If `filter` is not a string.

    """
    if not isinstance(filter, str):
        errmsg = f"'filter' must be a string, not {type(filter).__name__}."
        raise TypeError(errmsg)

    yield from self.__from_filter(filter, paginate=True)

from_id

from_id(id: int | str) -> EntryRecord

Retrieve an entry by its ID.

Parameters:

Name Type Description Default
id int | str

The ID of the entry. Can be an AniList ID (integer) or a SeaDex database ID (string).

required

Returns:

Type Description
EntryRecord

The retrieved entry.

Raises:

Type Description
EntryNotFoundError

If no entry is found for the provided ID.

Source code in src/seadex/_entry.py
def from_id(self, id: int | str, /) -> EntryRecord:
    """
    Retrieve an entry by its ID.

    Parameters
    ----------
    id : int | str
        The ID of the entry. Can be an AniList ID (integer)
        or a SeaDex database ID (string).

    Returns
    -------
    EntryRecord
        The retrieved entry.

    Raises
    ------
    EntryNotFoundError
        If no entry is found for the provided ID.

    """
    filter = f"alID={id}" if isinstance(id, int) else f"id='{id}'"
    entries = self.__from_filter(filter, paginate=False)

    try:
        return next(entries)
    except StopIteration:
        errmsg = f"No seadex entry found for id: {id}"
        raise EntryNotFoundError(errmsg) from None

from_infohash

from_infohash(infohash: str) -> Iterator[EntryRecord]

Yield entries that contain a torrent with the specified infohash.

Parameters:

Name Type Description Default
infohash str

The infohash to search for.

required

Yields:

Type Description
EntryRecord

The retrieved entry.

Raises:

Type Description
TypeError

If infohash is not a string.

ValueError

If infohash is not a 40-character hexadecimal string.

Source code in src/seadex/_entry.py
def from_infohash(self, infohash: str, /) -> Iterator[EntryRecord]:
    """
    Yield entries that contain a torrent with the specified infohash.

    Parameters
    ----------
    infohash : str
        The infohash to search for.

    Yields
    ------
    EntryRecord
        The retrieved entry.

    Raises
    ------
    TypeError
        If `infohash` is not a string.
    ValueError
        If `infohash` is not a 40-character hexadecimal string.

    """
    if not isinstance(infohash, str):
        errmsg = f"'infohash' must be a string, not {type(infohash).__name__}."
        raise TypeError(errmsg)

    infohash = infohash.lower().strip()

    if not re.match(r"^[0-9a-f]{40}$", infohash):
        errmsg = "Invalid infohash format. Must be a 40-character hexadecimal string."
        raise ValueError(errmsg)

    yield from self.__from_filter(f"trs.infoHash?='{infohash}'", paginate=False)

from_title

from_title(title: str) -> EntryRecord

Retrieve an entry by its anime title.

Parameters:

Name Type Description Default
title str

The title of the anime to search for.

required

Returns:

Type Description
EntryRecord

The retrieved entry.

Raises:

Type Description
EntryNotFoundError

If no entry is found for the provided title.

Source code in src/seadex/_entry.py
def from_title(self, title: str, /) -> EntryRecord:
    """
    Retrieve an entry by its anime title.

    Parameters
    ----------
    title : str
        The title of the anime to search for.

    Returns
    -------
    EntryRecord
        The retrieved entry.

    Raises
    ------
    EntryNotFoundError
        If no entry is found for the provided title.

    """
    try:
        response = self._client.post(
            "https://graphql.anilist.co",
            json={
                "query": "query ($search: String!) { Media(search: $search, type: ANIME) { id title { english romaji } } }",
                "variables": {"search": title},
            },
        ).raise_for_status()

        media = response.json()["data"]["Media"]
        anilist_id = media["id"]

        entries = self.__from_filter(f"alID={anilist_id}", paginate=False)
        entry_record = next(entries)
        entry_record._anilist_title = media["title"]["english"] or media["title"]["romaji"]  # type: ignore[attr-defined]
        return entry_record

    except (StopIteration, TypeError):
        errmsg = f"No seadex entry found for title: {title}"
        raise EntryNotFoundError(errmsg) from None

iterator

iterator() -> Iterator[EntryRecord]

Lazily iterate over all the entries in SeaDex.

Yields:

Type Description
EntryRecord

The retrieved entry.

Source code in src/seadex/_entry.py
def iterator(self) -> Iterator[EntryRecord]:
    """
    Lazily iterate over all the entries in SeaDex.

    Yields
    ------
    EntryRecord
        The retrieved entry.

    """
    yield from self.__from_filter(None, paginate=True)