Metadata-Version: 2.4
Name: bentu-downloader
Version: 0.1.4
Summary: A standalone Python video downloader toolkit for public platform media and direct streams.
Author: Bentu Contributors
License-Expression: MIT
Project-URL: Homepage, https://github.com/YOUR_USERNAME/bentu-downloader
Project-URL: Repository, https://github.com/YOUR_USERNAME/bentu-downloader
Project-URL: Issues, https://github.com/YOUR_USERNAME/bentu-downloader/issues
Keywords: video,download,downloader,hls,media
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
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 :: Internet :: WWW/HTTP
Classifier: Topic :: Multimedia :: Video
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# Bentu Downloader

Bentu Downloader ist eine eigenstaendige Python-Bibliothek und ein Terminal-Programm zum Herunterladen von oeffentlich erreichbaren Videos, direkten Videodateien und einfachen Videostreams.

Das Paket kann auf PyPI veroeffentlicht und danach so installiert werden:

```bash
python -m pip install bentu-downloader
```

Im Python-Code wird es so importiert:

```python
import bentu_downloader
```

Python erlaubt keine Bindestriche in `import`-Namen. Deshalb heisst das PyPI-Paket `bentu-downloader`, aber der Import heisst `bentu_downloader`.

## Features

- Python-Bibliothek fuer eigene Programme
- Terminal-Befehl `bentu`
- Download von direkten `.mp4`, `.webm`, `.mov`, `.m4v`-Links
- Download von direkten Audio-Dateien wie `.mp3`, `.m4a`, `.aac`, `.ogg`
- Download von einfachen unverschluesselten `.m3u8`-Streams
- HTML-Seiten nach sichtbaren Media-Links durchsuchen
- Schutz gegen kaputte Downloads: HTML wird nicht als MP4 gespeichert
- SSL-Unterstuetzung mit normalem Python-SSL und optional `certifi`
- Keine benoetigten Drittanbieter-Bibliotheken fuer den Bentu-Kern
- Open-Source-tauglich mit MIT-Lizenz

## Wichtiger Hinweis

Bentu braucht keine andere Downloader-Bibliothek fuer direkte Media-Links und einfache Streams.

Plattformseiten wie YouTube, Instagram oder TikTok sind aber oft zuerst normale HTML-Webseiten. Wenn die Webseite keinen oeffentlichen direkten Media-Link bereitstellt, speichert Bentu keine falsche `.mp4`, sondern bricht mit einer klaren Fehlermeldung ab.

Bentu umgeht keine Logins, Captchas, Paywalls, DRM, private APIs, geschuetzte Signaturen oder andere Zugriffsschutz-Systeme.

## Installation

Von PyPI:

```bash
python -m pip install bentu-downloader
```

Update:

```bash
python -m pip install -U bentu-downloader
```

Aus diesem Repository lokal:

```bash
python -m pip install -e .
```

## Schnellstart In Python

```python
import bentu_downloader

result = bentu_downloader.download(
    "https://example.com/video.mp4",
    output="downloads/video.mp4",
)

print("Gespeichert:", result.path)
```

Mit automatischem Dateinamen:

```python
import bentu_downloader

result = bentu_downloader.download(
    "https://example.com/video.mp4",
    output="downloads/%(title)s.%(ext)s",
)

print(result.path)
```

Informationen auslesen:

```python
import bentu_downloader

info = bentu_downloader.extract_info("https://example.com/video.mp4")

print(info.title)
print(info.formats)
```

## Terminal Nutzung

Download:

```bash
bentu "https://example.com/video.mp4"
```

Als MP4 speichern:

```bash
bentu "https://example.com/video.mp4" -o "downloads/video.mp4"
```

Mit Titel und Endung:

```bash
bentu "https://example.com/video.mp4" -o "downloads/%(title)s.%(ext)s"
```

Infos anzeigen:

```bash
bentu "https://example.com/video.mp4" --print-info
```

Formate anzeigen:

```bash
bentu "https://example.com/video.mp4" --list-formats
```

Alle Optionen:

```bash
bentu --help
```

## YouTube, Instagram Und TikTok Beispiel

Im Ordner [examples/social_downloader.py](examples/social_downloader.py) liegt ein aufgeraeumtes Beispielprogramm. Es ist aus dem funktionierenden Testcode entstanden und zeigt ein Menue fuer:

- YouTube Download
- Instagram Download
- TikTok Download

Dieses Beispiel nutzt optional `btch-downloader`, um fuer diese Plattformen direkte Media-URLs zu bekommen. Bentu selbst bleibt weiterhin eigenstaendig und hat keine Pflicht-Abhaengigkeiten.

Optionale Beispiel-Abhaengigkeiten installieren:

```bash
python -m pip install -r examples/requirements-social.txt
```

Beispiel starten:

```bash
python examples/social_downloader.py
```

Dann erscheint:

```text
Social Downloader
=================
1 YouTube Download
2 Instagram Download
3 TikTok Download
```

Danach Zahl eingeben, Link einfuegen und Enter druecken. Dateien werden im Ordner `downloads/` gespeichert.

## Kompakter Code Fuer Eigene Social-Downloader

Dieser Code zeigt das Prinzip aus dem Beispiel: Plattform-API fragen, Media-URL finden, Datei speichern.

```python
from __future__ import annotations

import asyncio
from pathlib import Path
from urllib.parse import unquote, urlparse
from urllib.request import Request, urlopen


DOWNLOAD_DIR = Path("downloads")


async def fetch_platform_info(platform: str, url: str):
    import btch_downloader

    fn_name = {
        "youtube": "youtube",
        "instagram": "igdl",
        "tiktok": "ttdl",
    }[platform]

    result = getattr(btch_downloader, fn_name)(url)
    if hasattr(result, "__await__"):
        result = await result
    return result


def extract_media_urls(platform: str, result) -> list[str]:
    urls: list[str] = []

    if platform == "youtube" and isinstance(result, dict):
        for key in ("mp4", "mp3"):
            value = result.get(key)
            if isinstance(value, str) and value.startswith(("http://", "https://")):
                urls.append(value)

    elif platform == "tiktok" and isinstance(result, dict):
        for key in ("video", "audio"):
            value = result.get(key)
            if isinstance(value, str) and value.startswith(("http://", "https://")):
                urls.append(value)

    elif platform == "instagram":
        if isinstance(result, list):
            for item in result:
                if isinstance(item, dict):
                    value = item.get("url")
                    if isinstance(value, str) and value.startswith(("http://", "https://")):
                        urls.append(value)
        elif isinstance(result, dict):
            value = result.get("url")
            if isinstance(value, str) and value.startswith(("http://", "https://")):
                urls.append(value)

    return list(dict.fromkeys(urls))


def sanitize_filename(value: str) -> str:
    cleaned = "".join("_" if char in '\\/:*?"<>|\n\r\t' else char for char in value)
    cleaned = " ".join(cleaned.split()).strip(". ")
    return cleaned[:120] or "download"


def build_target_path(title: str, media_url: str, index: int) -> Path:
    parsed = urlparse(media_url)
    suffix = Path(unquote(parsed.path)).suffix.lower()
    if not suffix or len(suffix) > 8:
        suffix = ".mp4"

    safe_title = sanitize_filename(title)
    if index > 1:
        safe_title = f"{safe_title}_{index}"
    return DOWNLOAD_DIR / f"{safe_title}{suffix}"


def download_file(media_url: str, target: Path) -> int:
    request = Request(media_url, headers={"User-Agent": "Mozilla/5.0"})
    target.parent.mkdir(parents=True, exist_ok=True)

    bytes_written = 0
    with urlopen(request, timeout=60.0) as response, target.open("wb") as output:
        while True:
            chunk = response.read(1024 * 256)
            if not chunk:
                break
            output.write(chunk)
            bytes_written += len(chunk)
    return bytes_written


def extract_title(platform: str, result) -> str:
    if isinstance(result, dict):
        title = result.get("title")
        if isinstance(title, str) and title.strip():
            return title.strip()
    return f"{platform}_download"


def download_platform_video(platform: str, url: str) -> None:
    DOWNLOAD_DIR.mkdir(parents=True, exist_ok=True)
    result = asyncio.run(fetch_platform_info(platform, url))
    media_urls = extract_media_urls(platform, result)

    if not media_urls:
        raise RuntimeError("Keine direkte Media-URL gefunden.")

    title = extract_title(platform, result)
    for index, media_url in enumerate(media_urls, start=1):
        target = build_target_path(title, media_url, index)
        bytes_written = download_file(media_url, target)
        print(f"Fertig: {target} ({bytes_written} Bytes)")


if __name__ == "__main__":
    download_platform_video("youtube", "https://www.youtube.com/watch?v=VIDEO_ID")
```

Fuer das komplette Menue-Programm nutze [examples/social_downloader.py](examples/social_downloader.py).

## Bentu Als Fallback Verwenden

Wenn du schon eine URL hast, kannst du Bentu direkt verwenden:

```python
import bentu_downloader

result = bentu_downloader.download(
    "https://example.com/video.mp4",
    output="downloads/%(title)s.%(ext)s",
    overwrite=True,
)

print(result.path)
```

## Fehler: HTML Statt Video

Wenn diese Meldung erscheint:

```text
The server returned HTML, not a media file.
```

dann hat der Server eine Webseite geliefert, keine echte Media-Datei. Bentu speichert diese Webseite nicht als MP4, weil die Datei sonst kaputt waere.

Moegliche Loesungen:

- direkten `.mp4`-, `.webm`- oder `.m3u8`-Link verwenden
- `bentu URL --print-info` ausfuehren
- bei Seiten mit Referer-Schutz `--add-header "Referer:https://example.com/"` nutzen
- fuer Plattformen mit eigener Datenstruktur einen Provider oder ein Social-Beispiel wie oben verwenden

## SSL Hinweis

Bentu nutzt automatisch den normalen Python-SSL-Kontext. Wenn `certifi` installiert ist, nutzt Bentu automatisch dessen Zertifikate.

```bash
python -m pip install certifi
```

## Projektstruktur

```text
.
├── LICENSE
├── README.md
├── pyproject.toml
├── examples/
│   ├── requirements-social.txt
│   └── social_downloader.py
├── scripts/
│   └── bump_version.py
├── src/
│   ├── bentu/
│   └── bentu_downloader/
└── tests/
```

## Tests

```bash
python -m unittest discover -s tests
```

## Paket Bauen

Jeder PyPI-Upload braucht eine neue Versionsnummer. Eine Version, die schon bei PyPI liegt, kann nicht noch einmal hochgeladen werden.

Version erhoehen:

```bash
python scripts/bump_version.py 0.1.5
```

Alte Build-Dateien loeschen:

```bash
rm -rf dist build
```

Bauen:

```bash
python -m pip install build
python -m build
```

Hochladen:

```bash
python -m pip install twine
python -m twine upload dist/*
```

## GitHub Veroeffentlichung

1. Repository auf GitHub erstellen.
2. Platzhalter in `pyproject.toml` ersetzen:

```toml
Homepage = "https://github.com/YOUR_USERNAME/bentu-downloader"
Repository = "https://github.com/YOUR_USERNAME/bentu-downloader"
Issues = "https://github.com/YOUR_USERNAME/bentu-downloader/issues"
```

3. Dateien committen:

```bash
git add .
git commit -m "Initial open source release"
git branch -M main
git remote add origin https://github.com/YOUR_USERNAME/bentu-downloader.git
git push -u origin main
```

## Lizenz

Dieses Projekt steht unter der MIT-Lizenz. Siehe [LICENSE](LICENSE).

## Rechtlicher Hinweis

Lade nur Inhalte herunter, fuer die du die Rechte oder eine Erlaubnis hast. Bentu ist fuer legale Downloads, eigene Inhalte, erlaubte Medien, direkte Media-Links und oeffentliche Streams gedacht.
