mediaflow-proxy 2.4.4__tar.gz → 2.4.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/PKG-INFO +13 -4
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/README.md +12 -3
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/configs.py +2 -0
- mediaflow_proxy-2.4.6/mediaflow_proxy/extractors/vavoo.py +296 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/remuxer/media_source.py +2 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/routes/telegram.py +189 -75
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/utils/redis_utils.py +52 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/utils/telegram.py +199 -22
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/pyproject.toml +1 -1
- mediaflow_proxy-2.4.4/mediaflow_proxy/extractors/vavoo.py +0 -252
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/.gitignore +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/LICENSE +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/__init__.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/const.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/drm/__init__.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/drm/decrypter.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/extractors/F16Px.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/extractors/__init__.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/extractors/base.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/extractors/dlhd.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/extractors/doodstream.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/extractors/factory.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/extractors/fastream.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/extractors/filelions.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/extractors/filemoon.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/extractors/gupload.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/extractors/livetv.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/extractors/lulustream.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/extractors/maxstream.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/extractors/mixdrop.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/extractors/okru.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/extractors/sportsonline.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/extractors/streamtape.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/extractors/streamwish.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/extractors/supervideo.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/extractors/turbovidplay.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/extractors/uqload.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/extractors/vidmoly.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/extractors/vidoza.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/extractors/vixcloud.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/extractors/voe.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/handlers.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/main.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/middleware.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/mpd_processor.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/remuxer/__init__.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/remuxer/audio_transcoder.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/remuxer/codec_utils.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/remuxer/container_probe.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/remuxer/ebml_parser.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/remuxer/hls_manifest.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/remuxer/mkv_demuxer.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/remuxer/mp4_muxer.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/remuxer/mp4_parser.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/remuxer/pyav_demuxer.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/remuxer/transcode_handler.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/remuxer/transcode_pipeline.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/remuxer/ts_muxer.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/remuxer/video_transcoder.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/routes/__init__.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/routes/acestream.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/routes/extractor.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/routes/playlist_builder.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/routes/proxy.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/routes/speedtest.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/routes/xtream.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/schemas.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/speedtest/__init__.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/speedtest/models.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/speedtest/providers/all_debrid.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/speedtest/providers/base.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/speedtest/providers/real_debrid.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/speedtest/service.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/static/index.html +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/static/logo.png +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/static/playlist_builder.html +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/static/speedtest.html +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/static/speedtest.js +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/static/url_generator.html +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/utils/__init__.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/utils/acestream.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/utils/aes.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/utils/aesgcm.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/utils/base64_utils.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/utils/base_prebuffer.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/utils/cache_utils.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/utils/codec.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/utils/compat.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/utils/constanttime.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/utils/crypto_utils.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/utils/cryptomath.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/utils/dash_prebuffer.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/utils/deprecations.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/utils/extractor_helpers.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/utils/hls_prebuffer.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/utils/hls_utils.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/utils/http_client.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/utils/http_utils.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/utils/m3u8_processor.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/utils/mpd_utils.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/utils/packed.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/utils/python_aes.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/utils/python_aesgcm.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/utils/rate_limit_handlers.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/utils/rijndael.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/utils/stream_transformers.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/utils/tlshashlib.py +0 -0
- {mediaflow_proxy-2.4.4 → mediaflow_proxy-2.4.6}/mediaflow_proxy/utils/tlshmac.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mediaflow-proxy
|
|
3
|
-
Version: 2.4.
|
|
3
|
+
Version: 2.4.6
|
|
4
4
|
Summary: A high-performance proxy server for streaming media, supporting HTTP(S), HLS, and MPEG-DASH with real-time DRM decryption.
|
|
5
5
|
Project-URL: Homepage, https://github.com/mhdzumair/mediaflow-proxy
|
|
6
6
|
Project-URL: Repository, https://github.com/mhdzumair/mediaflow-proxy
|
|
@@ -381,7 +381,7 @@ TELEGRAM_SESSION_STRING=your_session_string_here
|
|
|
381
381
|
|
|
382
382
|
| Endpoint | Description |
|
|
383
383
|
|----------|-------------|
|
|
384
|
-
| `/proxy/telegram/stream` | Stream media from t.me link or file_id |
|
|
384
|
+
| `/proxy/telegram/stream` | Stream media from t.me link, chat IDs, document_id, or file_id |
|
|
385
385
|
| `/proxy/telegram/stream/{filename}` | Stream with custom filename |
|
|
386
386
|
| `/proxy/telegram/transcode/playlist.m3u8` | HLS transcode playlist (recommended for browser playback and smooth seeking) |
|
|
387
387
|
| `/proxy/telegram/transcode/init.mp4` | fMP4 init segment for Telegram transcode playlist |
|
|
@@ -394,8 +394,9 @@ TELEGRAM_SESSION_STRING=your_session_string_here
|
|
|
394
394
|
| Parameter | Description |
|
|
395
395
|
|-----------|-------------|
|
|
396
396
|
| `d` or `url` | t.me link (e.g., `https://t.me/channel/123`) |
|
|
397
|
-
| `chat_id` | Chat/Channel ID (use with `message_id`) - numeric ID or @username |
|
|
397
|
+
| `chat_id` | Chat/Channel ID (use with `message_id` or `document_id`) - numeric ID or @username |
|
|
398
398
|
| `message_id` | Message ID within the chat (use with `chat_id`) |
|
|
399
|
+
| `document_id` | Telegram document ID (use with `chat_id`, optionally add `file_size` to enforce size match while resolving) |
|
|
399
400
|
| `file_id` | Bot API file_id (use with `file_size`) |
|
|
400
401
|
| `file_size` | File size in bytes (required when using `file_id`) |
|
|
401
402
|
| `transcode` | Set to `true` for direct transcode mode on `/proxy/telegram/stream` (URL Generator defaults to `/proxy/telegram/transcode/playlist.m3u8` when no start time is set) |
|
|
@@ -412,8 +413,13 @@ TELEGRAM_SESSION_STRING=your_session_string_here
|
|
|
412
413
|
- `chat_id=-1001234567890&message_id=123` - Private channel/supergroup by numeric ID
|
|
413
414
|
- `chat_id=@channelname&message_id=123` - Public channel by username
|
|
414
415
|
|
|
415
|
-
**Option 3:
|
|
416
|
+
**Option 3: chat_id + document_id**
|
|
417
|
+
- `chat_id=-1001234567890&document_id=6743210987654321` - Resolves by scanning recent chat messages
|
|
418
|
+
- Optional: add `file_size=52428800` to require both document ID and size to match
|
|
419
|
+
|
|
420
|
+
**Option 4: Bot API file_id**
|
|
416
421
|
- `file_id=BQACAgI...&file_size=1048576` - Direct streaming by file_id
|
|
422
|
+
- `chat_id=-1001234567890&file_id=BQACAgI...&file_size=1048576` - If the file reference is stale, server can resolve by scanning this chat
|
|
417
423
|
- Requires `file_size` parameter for range request support (seeking in video players)
|
|
418
424
|
- Get file_id and file_size from Telegram Bot API's `getFile` response
|
|
419
425
|
|
|
@@ -432,6 +438,9 @@ mpv "http://localhost:8888/proxy/telegram/stream?chat_id=@channelname&message_id
|
|
|
432
438
|
# Stream using Bot API file_id (requires file_size)
|
|
433
439
|
mpv "http://localhost:8888/proxy/telegram/stream?file_id=BQACAgIAAxkBAAI...&file_size=52428800&api_password=your_password"
|
|
434
440
|
|
|
441
|
+
# Stream using chat_id + document_id (server scans recent messages)
|
|
442
|
+
mpv "http://localhost:8888/proxy/telegram/stream?chat_id=-1001234567890&document_id=6743210987654321&api_password=your_password"
|
|
443
|
+
|
|
435
444
|
# Stream with custom filename
|
|
436
445
|
mpv "http://localhost:8888/proxy/telegram/stream/movie.mp4?d=https://t.me/channelname/123&api_password=your_password"
|
|
437
446
|
|
|
@@ -340,7 +340,7 @@ TELEGRAM_SESSION_STRING=your_session_string_here
|
|
|
340
340
|
|
|
341
341
|
| Endpoint | Description |
|
|
342
342
|
|----------|-------------|
|
|
343
|
-
| `/proxy/telegram/stream` | Stream media from t.me link or file_id |
|
|
343
|
+
| `/proxy/telegram/stream` | Stream media from t.me link, chat IDs, document_id, or file_id |
|
|
344
344
|
| `/proxy/telegram/stream/{filename}` | Stream with custom filename |
|
|
345
345
|
| `/proxy/telegram/transcode/playlist.m3u8` | HLS transcode playlist (recommended for browser playback and smooth seeking) |
|
|
346
346
|
| `/proxy/telegram/transcode/init.mp4` | fMP4 init segment for Telegram transcode playlist |
|
|
@@ -353,8 +353,9 @@ TELEGRAM_SESSION_STRING=your_session_string_here
|
|
|
353
353
|
| Parameter | Description |
|
|
354
354
|
|-----------|-------------|
|
|
355
355
|
| `d` or `url` | t.me link (e.g., `https://t.me/channel/123`) |
|
|
356
|
-
| `chat_id` | Chat/Channel ID (use with `message_id`) - numeric ID or @username |
|
|
356
|
+
| `chat_id` | Chat/Channel ID (use with `message_id` or `document_id`) - numeric ID or @username |
|
|
357
357
|
| `message_id` | Message ID within the chat (use with `chat_id`) |
|
|
358
|
+
| `document_id` | Telegram document ID (use with `chat_id`, optionally add `file_size` to enforce size match while resolving) |
|
|
358
359
|
| `file_id` | Bot API file_id (use with `file_size`) |
|
|
359
360
|
| `file_size` | File size in bytes (required when using `file_id`) |
|
|
360
361
|
| `transcode` | Set to `true` for direct transcode mode on `/proxy/telegram/stream` (URL Generator defaults to `/proxy/telegram/transcode/playlist.m3u8` when no start time is set) |
|
|
@@ -371,8 +372,13 @@ TELEGRAM_SESSION_STRING=your_session_string_here
|
|
|
371
372
|
- `chat_id=-1001234567890&message_id=123` - Private channel/supergroup by numeric ID
|
|
372
373
|
- `chat_id=@channelname&message_id=123` - Public channel by username
|
|
373
374
|
|
|
374
|
-
**Option 3:
|
|
375
|
+
**Option 3: chat_id + document_id**
|
|
376
|
+
- `chat_id=-1001234567890&document_id=6743210987654321` - Resolves by scanning recent chat messages
|
|
377
|
+
- Optional: add `file_size=52428800` to require both document ID and size to match
|
|
378
|
+
|
|
379
|
+
**Option 4: Bot API file_id**
|
|
375
380
|
- `file_id=BQACAgI...&file_size=1048576` - Direct streaming by file_id
|
|
381
|
+
- `chat_id=-1001234567890&file_id=BQACAgI...&file_size=1048576` - If the file reference is stale, server can resolve by scanning this chat
|
|
376
382
|
- Requires `file_size` parameter for range request support (seeking in video players)
|
|
377
383
|
- Get file_id and file_size from Telegram Bot API's `getFile` response
|
|
378
384
|
|
|
@@ -391,6 +397,9 @@ mpv "http://localhost:8888/proxy/telegram/stream?chat_id=@channelname&message_id
|
|
|
391
397
|
# Stream using Bot API file_id (requires file_size)
|
|
392
398
|
mpv "http://localhost:8888/proxy/telegram/stream?file_id=BQACAgIAAxkBAAI...&file_size=52428800&api_password=your_password"
|
|
393
399
|
|
|
400
|
+
# Stream using chat_id + document_id (server scans recent messages)
|
|
401
|
+
mpv "http://localhost:8888/proxy/telegram/stream?chat_id=-1001234567890&document_id=6743210987654321&api_password=your_password"
|
|
402
|
+
|
|
394
403
|
# Stream with custom filename
|
|
395
404
|
mpv "http://localhost:8888/proxy/telegram/stream/movie.mp4?d=https://t.me/channelname/123&api_password=your_password"
|
|
396
405
|
|
|
@@ -95,6 +95,8 @@ class Settings(BaseSettings):
|
|
|
95
95
|
telegram_session_string: SecretStr | None = None # Persistent session string (avoids re-authentication).
|
|
96
96
|
telegram_max_connections: int = 8 # Max parallel DC connections for downloads (max 20, careful of floods).
|
|
97
97
|
telegram_request_timeout: int = 30 # Request timeout in seconds.
|
|
98
|
+
telegram_document_scan_limit: int = 500 # Max recent messages to scan when resolving chat_id+document_id.
|
|
99
|
+
telegram_document_cache_ttl: int = 3600 # TTL (seconds) for cached document_id->message_id mappings.
|
|
98
100
|
|
|
99
101
|
# Transcode settings
|
|
100
102
|
enable_transcode: bool = True # Whether to enable on-the-fly transcoding endpoints (MKV→fMP4, HLS VOD).
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import time
|
|
3
|
+
import re
|
|
4
|
+
import uuid
|
|
5
|
+
from typing import Any, Dict, Optional
|
|
6
|
+
from urllib.parse import quote
|
|
7
|
+
|
|
8
|
+
from mediaflow_proxy.extractors.base import BaseExtractor, ExtractorError
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class VavooExtractor(BaseExtractor):
|
|
14
|
+
"""Vavoo URL extractor per risolvere link vavoo.to"""
|
|
15
|
+
|
|
16
|
+
API_UA = "electron-fetch/1.0 electron (+https://github.com/arantes555/electron-fetch)"
|
|
17
|
+
TS_UA = "VAVOO/2.6"
|
|
18
|
+
|
|
19
|
+
def __init__(self, request_headers: dict):
|
|
20
|
+
super().__init__(request_headers)
|
|
21
|
+
self.mediaflow_endpoint = "proxy_stream_endpoint"
|
|
22
|
+
|
|
23
|
+
async def _get_auth_signature(self) -> Optional[str]:
|
|
24
|
+
"""Get authentication signature via /api/app/ping (electron flow)."""
|
|
25
|
+
unique_id = uuid.uuid4().hex[:16]
|
|
26
|
+
headers = {
|
|
27
|
+
"user-agent": self.API_UA,
|
|
28
|
+
"accept": "*/*",
|
|
29
|
+
"Accept-Language": "de",
|
|
30
|
+
"Accept-Encoding": "gzip, deflate",
|
|
31
|
+
"content-type": "application/json; charset=utf-8",
|
|
32
|
+
"Connection": "close",
|
|
33
|
+
}
|
|
34
|
+
body = {
|
|
35
|
+
"token": "8Us2TfjeOFrzqFFTEjL3E5KfdAWGa5PV3wQe60uK4BmzlkJRMYFu0ufaM_eeDXKS2U04XUuhbDTgGRJrJARUwzDyCcRToXhW5AcDekfFMfwNUjuieeQ1uzeDB9YWyBL2cn5Al3L3gTnF8Vk1t7rPwkBob0swvxA",
|
|
36
|
+
"reason": "player.enter",
|
|
37
|
+
"locale": "de",
|
|
38
|
+
"theme": "dark",
|
|
39
|
+
"metadata": {
|
|
40
|
+
"device": {
|
|
41
|
+
"type": "Desktop",
|
|
42
|
+
"brand": "Unknown",
|
|
43
|
+
"model": "Unknown",
|
|
44
|
+
"name": "Unknown",
|
|
45
|
+
"uniqueId": unique_id,
|
|
46
|
+
},
|
|
47
|
+
"os": {"name": "windows", "version": "10.0.22631", "abis": [], "host": "electron"},
|
|
48
|
+
"app": {
|
|
49
|
+
"platform": "electron",
|
|
50
|
+
"version": "3.1.4",
|
|
51
|
+
"buildId": "288045000",
|
|
52
|
+
"engine": "jsc",
|
|
53
|
+
"signatures": [],
|
|
54
|
+
"installer": "unknown",
|
|
55
|
+
},
|
|
56
|
+
"version": {"package": "tv.vavoo.app", "binary": "3.1.4", "js": "3.1.4"},
|
|
57
|
+
},
|
|
58
|
+
"appFocusTime": 27229,
|
|
59
|
+
"playerActive": True,
|
|
60
|
+
"playDuration": 0,
|
|
61
|
+
"devMode": False,
|
|
62
|
+
"hasAddon": False,
|
|
63
|
+
"castConnected": False,
|
|
64
|
+
"package": "tv.vavoo.app",
|
|
65
|
+
"version": "3.1.4",
|
|
66
|
+
"process": "app",
|
|
67
|
+
"firstAppStart": int(time.time() * 1000) - 86400000,
|
|
68
|
+
"lastAppStart": int(time.time() * 1000),
|
|
69
|
+
"ipLocation": "",
|
|
70
|
+
"adblockEnabled": False,
|
|
71
|
+
"proxy": {"supported": ["ss"], "engine": "ss", "enabled": False, "autoServer": True, "id": "ca-bhs"},
|
|
72
|
+
"iap": {"supported": True},
|
|
73
|
+
}
|
|
74
|
+
try:
|
|
75
|
+
resp = await self._make_request(
|
|
76
|
+
"https://www.vavoo.tv/api/app/ping",
|
|
77
|
+
method="POST",
|
|
78
|
+
json=body,
|
|
79
|
+
headers=headers,
|
|
80
|
+
timeout=15,
|
|
81
|
+
retries=2,
|
|
82
|
+
)
|
|
83
|
+
try:
|
|
84
|
+
result = resp.json()
|
|
85
|
+
except Exception:
|
|
86
|
+
logger.warning("Vavoo ping returned non-json response (status=%s).", resp.status)
|
|
87
|
+
return None
|
|
88
|
+
addon_sig = result.get("addonSig") if isinstance(result, dict) else None
|
|
89
|
+
if addon_sig:
|
|
90
|
+
logger.info("Successfully obtained Vavoo auth signature (electron mode)")
|
|
91
|
+
return addon_sig
|
|
92
|
+
logger.warning("No addonSig in Vavoo API response: %s", result)
|
|
93
|
+
return None
|
|
94
|
+
except Exception as e:
|
|
95
|
+
logger.debug("_get_auth_signature error: %s", e)
|
|
96
|
+
return None
|
|
97
|
+
|
|
98
|
+
async def _get_ts_signature(self) -> Optional[str]:
|
|
99
|
+
"""Get TS-based signature via /api/box/ping2 (fallback)."""
|
|
100
|
+
vec = "9frjpxPjxSNilxJPCJ0XGYs6scej3dW/h/VWlnKUiLSG8IP7mfyDU7NirOlld+VtCKGj03XjetfliDMhIev7wcARo+YTU8KPFuVQP9E2DVXzY2BFo1NhE6qEmPfNDnm74eyl/7iFJ0EETm6XbYyz8IKBkAqPN/Spp3PZ2ulKg3QBSDxcVN4R5zRn7OsgLJ2CNTuWkd/h451lDCp+TtTuvnAEhcQckdsydFhTZCK5IiWrrTIC/d4qDXEd+GtOP4hPdoIuCaNzYfX3lLCwFENC6RZoTBYLrcKVVgbqyQZ7DnLqfLqvf3z0FVUWx9H21liGFpByzdnoxyFkue3NzrFtkRL37xkx9ITucepSYKzUVEfyBh+/3mtzKY26VIRkJFkpf8KVcCRNrTRQn47Wuq4gC7sSwT7eHCAydKSACcUMMdpPSvbvfOmIqeBNA83osX8FPFYUMZsjvYNEE3arbFiGsQlggBKgg1V3oN+5ni3Vjc5InHg/xv476LHDFnNdAJx448ph3DoAiJjr2g4ZTNynfSxdzA68qSuJY8UjyzgDjG0RIMv2h7DlQNjkAXv4k1BrPpfOiOqH67yIarNmkPIwrIV+W9TTV/yRyE1LEgOr4DK8uW2AUtHOPA2gn6P5sgFyi68w55MZBPepddfYTQ+E1N6R/hWnMYPt/i0xSUeMPekX47iucfpFBEv9Uh9zdGiEB+0P3LVMP+q+pbBU4o1NkKyY1V8wH1Wilr0a+q87kEnQ1LWYMMBhaP9yFseGSbYwdeLsX9uR1uPaN+u4woO2g8sw9Y5ze5XMgOVpFCZaut02I5k0U4WPyN5adQjG8sAzxsI3KsV04DEVymj224iqg2Lzz53Xz9yEy+7/85ILQpJ6llCyqpHLFyHq/kJxYPhDUF755WaHJEaFRPxUqbparNX+mCE9Xzy7Q/KTgAPiRS41FHXXv+7XSPp4cy9jli0BVnYf13Xsp28OGs/D8Nl3NgEn3/eUcMN80JRdsOrV62fnBVMBNf36+LbISdvsFAFr0xyuPGmlIETcFyxJkrGZnhHAxwzsvZ+Uwf8lffBfZFPRrNv+tgeeLpatVcHLHZGeTgWWml6tIHwWUqv2TVJeMkAEL5PPS4Gtbscau5HM+FEjtGS+KClfX1CNKvgYJl7mLDEf5ZYQv5kHaoQ6RcPaR6vUNn02zpq5/X3EPIgUKF0r/0ctmoT84B2J1BKfCbctdFY9br7JSJ6DvUxyde68jB+Il6qNcQwTFj4cNErk4x719Y42NoAnnQYC2/qfL/gAhJl8TKMvBt3Bno+va8ve8E0z8yEuMLUqe8OXLce6nCa+L5LYK1aBdb60BYbMeWk1qmG6Nk9OnYLhzDyrd9iHDd7X95OM6X5wiMVZRn5ebw4askTTc50xmrg4eic2U1w1JpSEjdH/u/hXrWKSMWAxaj34uQnMuWxPZEXoVxzGyuUbroXRfkhzpqmqqqOcypjsWPdq5BOUGL/Riwjm6yMI0x9kbO8+VoQ6RYfjAbxNriZ1cQ+AW1fqEgnRWXmjt4Z1M0ygUBi8w71bDML1YG6UHeC2cJ2CCCxSrfycKQhpSdI1QIuwd2eyIpd4LgwrMiY3xNWreAF+qobNxvE7ypKTISNrz0iYIhU0aKNlcGwYd0FXIRfKVBzSBe4MRK2pGLDNO6ytoHxvJweZ8h1XG8RWc4aB5gTnB7Tjiqym4b64lRdj1DPHJnzD4aqRixpXhzYzWVDN2kONCR5i2quYbnVFN4sSfLiKeOwKX4JdmzpYixNZXjLkG14seS6KR0Wl8Itp5IMIWFpnNokjRH76RYRZAcx0jP0V5/GfNNTi5QsEU98en0SiXHQGXnROiHpRUDXTl8FmJORjwXc0AjrEMuQ2FDJDmAIlKUSLhjbIiKw3iaqp5TVyXuz0ZMYBhnqhcwqULqtFSuIKpaW8FgF8QJfP2frADf4kKZG1bQ99MrRrb2A="
|
|
101
|
+
try:
|
|
102
|
+
resp = await self._make_request(
|
|
103
|
+
"https://www.vavoo.tv/api/box/ping2",
|
|
104
|
+
method="POST",
|
|
105
|
+
data={"vec": vec},
|
|
106
|
+
timeout=15,
|
|
107
|
+
retries=2,
|
|
108
|
+
)
|
|
109
|
+
try:
|
|
110
|
+
result = resp.json()
|
|
111
|
+
except Exception:
|
|
112
|
+
return None
|
|
113
|
+
return (result.get("response") or {}).get("signed")
|
|
114
|
+
except Exception as e:
|
|
115
|
+
logger.debug("_get_ts_signature error: %s", e)
|
|
116
|
+
return None
|
|
117
|
+
|
|
118
|
+
async def _resolve_with_auth(self, url: str, signature: str) -> Optional[str]:
|
|
119
|
+
"""Resolve a Vavoo link using the MediaHubMX API with electron-mode signature."""
|
|
120
|
+
headers = {
|
|
121
|
+
"user-agent": self.API_UA,
|
|
122
|
+
"accept": "*/*",
|
|
123
|
+
"Accept-Language": "de",
|
|
124
|
+
"Accept-Encoding": "gzip, deflate",
|
|
125
|
+
"content-type": "application/json; charset=utf-8",
|
|
126
|
+
"Connection": "close",
|
|
127
|
+
"mediahubmx-signature": signature,
|
|
128
|
+
}
|
|
129
|
+
payload = {"language": "de", "region": "AT", "url": url, "clientVersion": "3.1.4"}
|
|
130
|
+
try:
|
|
131
|
+
resp = await self._make_request(
|
|
132
|
+
"https://vavoo.to/mediahubmx-resolve.json",
|
|
133
|
+
method="POST",
|
|
134
|
+
json=payload,
|
|
135
|
+
headers=headers,
|
|
136
|
+
timeout=15,
|
|
137
|
+
retries=3,
|
|
138
|
+
backoff_factor=0.6,
|
|
139
|
+
)
|
|
140
|
+
try:
|
|
141
|
+
result = resp.json()
|
|
142
|
+
except Exception:
|
|
143
|
+
logger.warning("Vavoo resolve returned non-json (status=%s)", resp.status)
|
|
144
|
+
return None
|
|
145
|
+
logger.debug("Vavoo API response: %s", result)
|
|
146
|
+
if isinstance(result, list) and result and isinstance(result[0], dict) and result[0].get("url"):
|
|
147
|
+
return str(result[0]["url"])
|
|
148
|
+
if isinstance(result, dict):
|
|
149
|
+
if result.get("url"):
|
|
150
|
+
return str(result["url"])
|
|
151
|
+
if isinstance(result.get("data"), dict) and result["data"].get("url"):
|
|
152
|
+
return str(result["data"]["url"])
|
|
153
|
+
logger.warning("No URL found in Vavoo API response: %s", result)
|
|
154
|
+
return None
|
|
155
|
+
except Exception as e:
|
|
156
|
+
logger.debug("_resolve_with_auth error: %s", e)
|
|
157
|
+
return None
|
|
158
|
+
|
|
159
|
+
async def _follow_stream_url(self, url: str) -> str:
|
|
160
|
+
"""Follow redirects and extract final stream URL."""
|
|
161
|
+
stream_headers = {
|
|
162
|
+
"User-Agent": self.API_UA,
|
|
163
|
+
"Accept": "*/*",
|
|
164
|
+
"Accept-Encoding": "gzip, deflate",
|
|
165
|
+
"Connection": "close",
|
|
166
|
+
}
|
|
167
|
+
try:
|
|
168
|
+
resp = await self._make_request(url, method="HEAD", headers=stream_headers, timeout=15, retries=1)
|
|
169
|
+
final_url = str(getattr(resp, "url", url))
|
|
170
|
+
ctype = (getattr(resp, "headers", {}).get("Content-Type") or "").lower()
|
|
171
|
+
if "text/html" in ctype:
|
|
172
|
+
resp2 = await self._make_request(url, method="GET", headers=stream_headers, timeout=15, retries=1)
|
|
173
|
+
text = getattr(resp2, "text", "") or ""
|
|
174
|
+
m3u8 = re.findall(r'(https?://[^\s"\'<>]+\.m3u8[^\s"\'<>]*)', text)
|
|
175
|
+
if m3u8:
|
|
176
|
+
return m3u8[0]
|
|
177
|
+
generic = re.findall(
|
|
178
|
+
r'(https?://[^\s"\'<>]+(?:\.ts|/live/|/stream/|/playlist|/index)[^\s"\'<>]*)', text
|
|
179
|
+
)
|
|
180
|
+
if generic:
|
|
181
|
+
return generic[0]
|
|
182
|
+
return final_url
|
|
183
|
+
except Exception:
|
|
184
|
+
return url
|
|
185
|
+
|
|
186
|
+
async def _build_ts_fallback(self, url: str) -> Optional[str]:
|
|
187
|
+
"""Build a .ts fallback URL for vavoo-iptv streams using ping2 signature."""
|
|
188
|
+
if "vavoo-iptv" not in url:
|
|
189
|
+
return None
|
|
190
|
+
ts_sig = await self._get_ts_signature()
|
|
191
|
+
if not ts_sig:
|
|
192
|
+
return None
|
|
193
|
+
base = re.sub(r"/index\.m3u8(?:\?.*)?$", "", url.replace("vavoo-iptv", "live2")).rstrip("/")
|
|
194
|
+
ts_url = f"{base}.ts?n=1&b=5&vavoo_auth={quote(ts_sig, safe='')}"
|
|
195
|
+
try:
|
|
196
|
+
resp = await self._make_request(
|
|
197
|
+
ts_url, method="GET", headers={"User-Agent": self.TS_UA}, timeout=15, retries=1
|
|
198
|
+
)
|
|
199
|
+
if getattr(resp, "status", 400) < 400:
|
|
200
|
+
return ts_url
|
|
201
|
+
except Exception:
|
|
202
|
+
pass
|
|
203
|
+
return None
|
|
204
|
+
|
|
205
|
+
async def _resolve_web_vod_link(self, url: str) -> str:
|
|
206
|
+
"""Resolve a web-vod API link by getting the redirect Location header."""
|
|
207
|
+
try:
|
|
208
|
+
resp = await self._make_request(
|
|
209
|
+
url,
|
|
210
|
+
method="GET",
|
|
211
|
+
headers={"Accept": "application/json"},
|
|
212
|
+
timeout=10,
|
|
213
|
+
retries=2,
|
|
214
|
+
allow_redirects=False,
|
|
215
|
+
)
|
|
216
|
+
status = getattr(resp, "status", 0)
|
|
217
|
+
if status in (301, 302, 303, 307, 308):
|
|
218
|
+
location = getattr(resp, "headers", {}).get("Location") or getattr(resp, "headers", {}).get("location")
|
|
219
|
+
if location:
|
|
220
|
+
logger.info("Vavoo web-vod redirected to: %s", location)
|
|
221
|
+
return location
|
|
222
|
+
if status == 200:
|
|
223
|
+
text = getattr(resp, "text", "") or ""
|
|
224
|
+
if text and text.startswith("http"):
|
|
225
|
+
logger.info("Vavoo web-vod resolved to: %s", text.strip())
|
|
226
|
+
return text.strip()
|
|
227
|
+
raise ExtractorError(f"Vavoo web-vod API returned unexpected status {status}")
|
|
228
|
+
except ExtractorError:
|
|
229
|
+
raise
|
|
230
|
+
except Exception as e:
|
|
231
|
+
raise ExtractorError(f"Failed to resolve Vavoo web-vod link: {e}")
|
|
232
|
+
|
|
233
|
+
async def extract(self, url: str, **kwargs) -> Dict[str, Any]:
|
|
234
|
+
"""Extract Vavoo stream URL.
|
|
235
|
+
|
|
236
|
+
Flow:
|
|
237
|
+
1. Auth Resolve Mode: electron-mode signature → mediahubmx-resolve
|
|
238
|
+
2. TS Fallback Mode: ping2 signature → live2 .ts URL
|
|
239
|
+
3. Direct Fallback: raw URL with VAVOO UA
|
|
240
|
+
"""
|
|
241
|
+
if "vavoo.to" not in url:
|
|
242
|
+
raise ExtractorError("Not a valid Vavoo URL")
|
|
243
|
+
|
|
244
|
+
# Web-VOD links (new format)
|
|
245
|
+
if "/web-vod/api/get" in url:
|
|
246
|
+
resolved_url = await self._resolve_web_vod_link(url)
|
|
247
|
+
stream_headers = {
|
|
248
|
+
"user-agent": self.API_UA,
|
|
249
|
+
"referer": "https://vavoo.to/",
|
|
250
|
+
}
|
|
251
|
+
return {
|
|
252
|
+
"destination_url": resolved_url,
|
|
253
|
+
"request_headers": stream_headers,
|
|
254
|
+
"mediaflow_endpoint": self.mediaflow_endpoint,
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
resolved_url = None
|
|
258
|
+
stream_headers = None
|
|
259
|
+
|
|
260
|
+
# Mode 1: Auth Resolve (electron signature + mediahubmx)
|
|
261
|
+
sig = await self._get_auth_signature()
|
|
262
|
+
if sig:
|
|
263
|
+
candidate = await self._resolve_with_auth(url, sig)
|
|
264
|
+
if candidate:
|
|
265
|
+
candidate = await self._follow_stream_url(candidate)
|
|
266
|
+
resolved_url = candidate
|
|
267
|
+
stream_headers = {
|
|
268
|
+
"user-agent": self.API_UA,
|
|
269
|
+
"referer": "https://vavoo.to/",
|
|
270
|
+
"origin": "https://vavoo.to",
|
|
271
|
+
"mediahubmx-signature": sig,
|
|
272
|
+
}
|
|
273
|
+
logger.info("Using Auth Resolve Mode: %s", resolved_url)
|
|
274
|
+
|
|
275
|
+
# Mode 2: TS Fallback (ping2 + live2 .ts)
|
|
276
|
+
if not resolved_url:
|
|
277
|
+
ts_url = await self._build_ts_fallback(url)
|
|
278
|
+
if ts_url:
|
|
279
|
+
resolved_url = ts_url
|
|
280
|
+
stream_headers = {"user-agent": self.TS_UA}
|
|
281
|
+
logger.info("Using TS Fallback Mode: %s", resolved_url)
|
|
282
|
+
|
|
283
|
+
# Mode 3: Direct Fallback
|
|
284
|
+
if not resolved_url:
|
|
285
|
+
resolved_url = url
|
|
286
|
+
stream_headers = {
|
|
287
|
+
"user-agent": self.TS_UA,
|
|
288
|
+
"referer": "https://vavoo.to/",
|
|
289
|
+
}
|
|
290
|
+
logger.info("Using Direct Fallback Mode: %s", resolved_url)
|
|
291
|
+
|
|
292
|
+
return {
|
|
293
|
+
"destination_url": resolved_url,
|
|
294
|
+
"request_headers": stream_headers,
|
|
295
|
+
"mediaflow_endpoint": self.mediaflow_endpoint,
|
|
296
|
+
}
|
|
@@ -132,6 +132,8 @@ class TelegramMediaSource:
|
|
|
132
132
|
raw = f"file_id:{ref.file_id}"
|
|
133
133
|
elif ref.chat_id is not None and ref.message_id is not None:
|
|
134
134
|
raw = f"chat:{ref.chat_id}:msg:{ref.message_id}"
|
|
135
|
+
elif ref.chat_id is not None and ref.document_id is not None:
|
|
136
|
+
raw = f"chat:{ref.chat_id}:doc:{ref.document_id}"
|
|
135
137
|
else:
|
|
136
138
|
return ""
|
|
137
139
|
return hashlib.sha256(raw.encode()).hexdigest()[:16]
|