Metadata-Version: 2.5
Name: tvlogo.monster
Version: 0.1.1
Summary: Python client for tvlogo.monster, the free, non-profit TV channel logo API (Canada first): tvg-id, epg.monster id, call sign or name in, logo URL out.
Project-URL: Homepage, https://search.tvlogo.monster
Project-URL: Documentation, https://search.tvlogo.monster/docs
Project-URL: API, https://api.tvlogo.monster/docs
Author: tvlogo.monster contributors
License-Expression: MIT
License-File: LICENSE
Keywords: call-sign,canada,channel,epg,iptv,logo,m3u,tv,tvg-id,xmltv
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Multimedia :: Video
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: httpx<1,>=0.23
Provides-Extra: test
Requires-Dist: pytest>=7; extra == 'test'
Description-Content-Type: text/markdown

<p align="center">
  <img src="https://search.tvlogo.monster/static/img/mascot-pypi-260.png" alt="The tvlogo.monster mascot with a blue and yellow python wrapped around his raised foot" width="220" height="214">
</p>

# tvlogo.monster — Python client

Python client for **[tvlogo.monster](https://search.tvlogo.monster)**, a free, non-profit, open-source
database and API of TV channel logos (Canada first). One key in, one logo out: give it an
[iptv-org](https://github.com/iptv-org) `tvg-id`, an [epg.monster](https://epg.monster) channel id, a
Canadian call sign or a channel name, and get back a ready-to-use PNG URL on `i.tvlogo.monster`
(64, 128, 256 or 512 px, transparent square canvas). No API key, no account.

```bash
pip install tvlogo.monster
```

The distribution is called `tvlogo.monster` (pip also accepts `tvlogo-monster`); the import name is `tvlogo`.
Python 3.9+, one dependency ([httpx](https://www.python-httpx.org/)), fully typed (`py.typed`).

## Quickstart

This is the same code the website's code panel generates for the default key:

```python
from tvlogo import Client

# 1. one client per program (reuses the HTTPS connection, no API key needed)
client = Client()

# 2. resolve one key: tvg-id, epg.monster id, call sign or channel name
logo = client.resolve(
    "CTV.ca@CKYDT",
    mode="national",  # network logo for every affiliate (e.g. all CTV stations get the CTV logo)
)

# 3. None = no logo for this key (429 rate limits are retried after Retry-After)
if logo is None:
    raise SystemExit("no logo for this key")

# 4. use the image URL, e.g. as tvg-logo="..." in an M3U playlist
print(logo.image_url)        # https://i.tvlogo.monster/tZtP9.png
# which level answered: station, brand, network or channel
print(logo.resolved_level)   # network
# True when a fallback was used (e.g. no dark variant -> default)
print(logo.fallback, logo.theme_served)
```

More:

```python
logo = client.resolve("CKY-DT", namespace="call_sign", theme="dark", size=128)
logo.url(256)                        # same image, other size
client.download("CBCTelevision.ca", "logos/")   # saves logos/<id>.png, returns the Path
client.search("Citytv", limit=5)     # [SearchResult(name=..., id=..., image_url=...), ...]
client.stats()                       # catalogue counts
client.params()                      # allowed values and defaults, straight from the API

from tvlogo import image_url, parse_tvg_id
image_url("tZtP9", 64)               # https://i.tvlogo.monster/tZtP9-64.png (offline)
parse_tvg_id("CTV.ca@CKYDT")         # TvgId(channel='CTV.ca', feed='CKYDT') (offline)
```

Async:

```python
import asyncio
from tvlogo import AsyncClient

async def main():
    async with AsyncClient() as client:
        logo = await client.resolve("CTV.ca@CKYDT")
        print(logo.image_url if logo else "no logo")

asyncio.run(main())
```

## Options

`client.resolve(key, *, namespace, mode, theme, era, size)` — anything left out uses the API default.

| Option | Values (default first) | Meaning |
|---|---|---|
| `key` | string, max 200 chars | tvg-id (`CTV.ca@CKYDT`), epg.monster id (`CTV.Winnipeg.ca`), call sign (`CKY-DT`) or channel name (`CTV Winnipeg`) |
| `namespace` | `auto`, `iptv-org`, `epg.monster`, `call_sign`, `name` | Which id system the key belongs to. `auto` tries tvg-id, epg.monster id, call sign, then name. `key_type=` is accepted as an alias, as are `tvg-id`, `epg`, `callsign`. |
| `mode` | `local`, `national` | See below |
| `theme` | `default`, `dark`, `light`, `mono` | Preferred variant (`monochrome` accepted). Falls back to `default` when the variant does not exist. |
| `era` | `current`, `historic` | `historic` = an older design when known, else current (`new`/`old` accepted). |
| `size` | `512`, `256`, `128`, `64` | Square PNG size in px |

`Client(base_url="https://api.tvlogo.monster", timeout=10, *, max_retries=3, max_retry_wait=60, user_agent=None, http_client=None)`

The returned `Logo` has `image_url`, `public_id` (5-character image id), `resolved_level`, `fallback`,
`theme_served`, `era_served`, `name`, `country`, `namespace`, `matched_key`, `source`, `trademark_note`, …
and `raw` (the full JSON). `resolve()` returns `None` when there is no logo; `client.logo(key)` is the same
but raises `NotFound`.

## Modes and fallback

- **`local`** (default): the station's own logo (e.g. *CTV Winnipeg* / CKY-DT). When there is none, it
  falls back level by level: **station → brand → network → channel**.
- **`national`**: the network logo for every affiliate (every CTV station gets the CTV logo).

`resolved_level` tells you which level answered. Themes fall back to `default` and eras to `current`;
`fallback=True` plus `theme_served` / `era_served` tell you when you got something other than what you asked for.

## Rate limits and etiquette

The API is free and run by volunteers. Limits are per IP (token bucket: burst 60, about 2 lookups/s
sustained). Over the limit you get HTTP 429 with `Retry-After`; repeated violations lead to temporary blocks.

The client waits `Retry-After` and retries up to `max_retries` times (default 3), backs off on 502/503/504
and network errors, and raises `RateLimited` if it is still limited (or if the server asks for a wait longer
than `max_retry_wait`). Please:

- reuse one `Client`, cache results (logos rarely change; responses are cacheable for 5 minutes), and
- for bulk jobs (whole M3U playlists) go sequentially rather than in parallel, or put the image URLs
  straight into your playlist instead of re-resolving on every run.

Every request sends `User-Agent: tvlogo-python/0.1.0 (+https://search.tvlogo.monster)`.

## Errors

`TvlogoError` ← `InvalidParameter` (bad option, raised before any request; also a `ValueError`),
`APIError` (`status_code`, `detail`, `payload`) ← `NotFound` (404/410), `RateLimited` (`retry_after`).

## Command line

```console
$ tvlogo resolve CTV.ca@CKYDT --mode national
https://i.tvlogo.monster/tZtP9.png
$ tvlogo resolve CKY-DT --theme dark --size 128 --json
$ tvlogo search CBC --limit 5
$ tvlogo download CBCTelevision.ca logos/ --size 256
$ tvlogo url tZtP9 --size 64
$ tvlogo stats
```

(`python -m tvlogo ...` works too.) Exit code 1 means "no logo", 2 means an error.

## Links

- Website and search: <https://search.tvlogo.monster>
- Documentation: <https://search.tvlogo.monster/docs>
- API reference (Swagger): <https://api.tvlogo.monster/docs> · OpenAPI: <https://api.tvlogo.monster/openapi.json>
- Prompt for AI assistants: <https://search.tvlogo.monster/ai-prompt>
- Legal: <https://search.tvlogo.monster/legal>

## Legal

Channel logos are trademarks and/or copyright of their respective owners. They are shown for
identification only; tvlogo.monster claims no ownership and is not affiliated with or endorsed by any
broadcaster. **Logo images are not licensed by the project** — check with the owner before using them
beyond identification. The client code is **MIT** licensed; the metadata and mappings returned by the API
are **CC BY 4.0**. This package never bundles logo images.
