mirror of
https://github.com/g0ldyy/comet.git
synced 2026-01-12 01:16:12 +01:00
feat: add ChillLink API endpoints and refactor stream description formatting logic
This commit is contained in:
+9
-3
@@ -9,7 +9,8 @@ from fastapi.staticfiles import StaticFiles
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
from starlette.requests import Request
|
||||
|
||||
from comet.api.endpoints import admin, base, config, manifest, playback
|
||||
from comet.api.endpoints import (admin, base, chilllink, config, manifest,
|
||||
playback)
|
||||
from comet.api.endpoints import stream as streams_router
|
||||
from comet.background_scraper.worker import background_scraper
|
||||
from comet.core.database import (cleanup_expired_locks,
|
||||
@@ -118,8 +119,12 @@ tags_metadata = [
|
||||
"description": "Endpoints for configuring Comet.",
|
||||
},
|
||||
{
|
||||
"name": "Stremio Add-on",
|
||||
"description": "Standard Stremio add-on endpoints.",
|
||||
"name": "Stremio",
|
||||
"description": "Standard Stremio endpoints.",
|
||||
},
|
||||
{
|
||||
"name": "ChillLink",
|
||||
"description": "Chillio specific endpoints.",
|
||||
},
|
||||
{
|
||||
"name": "Admin",
|
||||
@@ -153,3 +158,4 @@ app.include_router(manifest.router)
|
||||
app.include_router(admin.router)
|
||||
app.include_router(playback.router)
|
||||
app.include_router(streams_router.streams)
|
||||
app.include_router(chilllink.router)
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
import random
|
||||
import string
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, BackgroundTasks, Query, Request
|
||||
|
||||
from comet.api.endpoints.stream import stream as get_streams
|
||||
from comet.core.config_validation import config_check
|
||||
from comet.core.models import settings
|
||||
from comet.debrid.manager import get_debrid_extension
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get(
|
||||
"/manifest",
|
||||
tags=["ChillLink"],
|
||||
summary="Add-on Manifest",
|
||||
description="Returns the add-on manifest.",
|
||||
)
|
||||
@router.get(
|
||||
"/{b64config}/manifest",
|
||||
tags=["ChillLink"],
|
||||
summary="Add-on Manifest",
|
||||
description="Returns the add-on manifest with existing configuration.",
|
||||
)
|
||||
async def chilllink_manifest(request: Request, b64config: str = None):
|
||||
config = config_check(b64config)
|
||||
|
||||
manifest = {
|
||||
"id": f"{settings.ADDON_ID}.{''.join(random.choice(string.ascii_letters) for _ in range(4))}",
|
||||
"version": "2.0.0",
|
||||
"description": "Chillio's fastest debrid search add-on.",
|
||||
"supported_endpoints": {"feeds": None, "streams": "/streams"},
|
||||
}
|
||||
|
||||
debrid_extension = get_debrid_extension(config["debridService"])
|
||||
manifest["name"] = (
|
||||
f"{settings.ADDON_NAME}{(' | ' + debrid_extension) if debrid_extension != 'TORRENT' else ''}"
|
||||
)
|
||||
|
||||
return manifest
|
||||
|
||||
|
||||
@router.get(
|
||||
"/streams",
|
||||
tags=["ChillLink"],
|
||||
summary="Stream Provider",
|
||||
description="Returns a list of streams for the specified media.",
|
||||
)
|
||||
@router.get(
|
||||
"/{b64config}/streams",
|
||||
tags=["ChillLink"],
|
||||
summary="Stream Provider",
|
||||
description="Returns a list of streams for the specified media with existing configuration.",
|
||||
)
|
||||
async def chilllink_streams(
|
||||
request: Request,
|
||||
background_tasks: BackgroundTasks,
|
||||
imdbID: str = Query(...),
|
||||
type: str = Query(...),
|
||||
season: Optional[int] = Query(None),
|
||||
episode: Optional[int] = Query(None),
|
||||
b64config: Optional[str] = None,
|
||||
):
|
||||
config = config_check(b64config)
|
||||
if config["debridService"] == "torrent":
|
||||
return {
|
||||
"sources": [
|
||||
{
|
||||
"id": "comet.fast",
|
||||
"title": "You need to configure a debrid service to use Comet in Chillio.",
|
||||
"url": "https://comet.fast",
|
||||
"metadata": [],
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
if type == "movie":
|
||||
media_id = imdbID
|
||||
elif type == "series":
|
||||
media_id = f"{imdbID}:{season}:{episode}"
|
||||
else:
|
||||
return {"sources": []}
|
||||
|
||||
stremio_response = await get_streams(
|
||||
request=request,
|
||||
media_type=type,
|
||||
media_id=media_id,
|
||||
background_tasks=background_tasks,
|
||||
b64config=b64config,
|
||||
chilllink=True,
|
||||
)
|
||||
|
||||
stremio_streams = stremio_response.get("streams", [])
|
||||
|
||||
sources = []
|
||||
for stream in stremio_streams:
|
||||
sources.append(
|
||||
{
|
||||
"id": stream["behaviorHints"]["bingeGroup"],
|
||||
"title": stream["behaviorHints"]["filename"],
|
||||
"url": stream["url"],
|
||||
"metadata": stream["_chilllink"],
|
||||
}
|
||||
)
|
||||
|
||||
return {"sources": sources}
|
||||
@@ -12,13 +12,13 @@ router = APIRouter()
|
||||
|
||||
@router.get(
|
||||
"/manifest.json",
|
||||
tags=["Stremio Add-on"],
|
||||
tags=["Stremio"],
|
||||
summary="Add-on Manifest",
|
||||
description="Returns the add-on manifest.",
|
||||
)
|
||||
@router.get(
|
||||
"/{b64config}/manifest.json",
|
||||
tags=["Stremio Add-on"],
|
||||
tags=["Stremio"],
|
||||
summary="Add-on Manifest",
|
||||
description="Returns the add-on manifest with existing configuration.",
|
||||
)
|
||||
|
||||
@@ -18,7 +18,7 @@ router = APIRouter()
|
||||
|
||||
@router.get(
|
||||
"/{b64config}/playback/{hash}/{index}/{season}/{episode}/{torrent_name:path}",
|
||||
tags=["Stremio Add-on"],
|
||||
tags=["Stremio"],
|
||||
summary="Playback Proxy",
|
||||
description="Proxies the playback request to the Debrid service or returns a cached link.",
|
||||
)
|
||||
|
||||
@@ -13,7 +13,8 @@ from comet.metadata.manager import MetadataScraper
|
||||
from comet.services.debrid import DebridService
|
||||
from comet.services.lock import DistributedLock, is_scrape_in_progress
|
||||
from comet.services.orchestration import TorrentManager
|
||||
from comet.utils.formatting import format_title
|
||||
from comet.utils.formatting import (format_chilllink, format_title,
|
||||
get_formatted_components)
|
||||
from comet.utils.network import get_client_ip
|
||||
from comet.utils.parsing import parse_media_id
|
||||
|
||||
@@ -116,13 +117,13 @@ async def wait_for_scrape_completion(media_id: str, context: str = ""):
|
||||
|
||||
@streams.get(
|
||||
"/stream/{media_type}/{media_id}.json",
|
||||
tags=["Stremio Add-on"],
|
||||
tags=["Stremio"],
|
||||
summary="Stream Provider",
|
||||
description="Returns a list of streams for the specified media.",
|
||||
)
|
||||
@streams.get(
|
||||
"/{b64config}/stream/{media_type}/{media_id}.json",
|
||||
tags=["Stremio Add-on"],
|
||||
tags=["Stremio"],
|
||||
summary="Stream Provider",
|
||||
description="Returns a list of streams for the specified media with existing configuration.",
|
||||
)
|
||||
@@ -132,6 +133,7 @@ async def stream(
|
||||
media_id: str,
|
||||
background_tasks: BackgroundTasks,
|
||||
b64config: str = None,
|
||||
chilllink: bool = False,
|
||||
):
|
||||
if "tmdb:" in media_id:
|
||||
return {"streams": []}
|
||||
@@ -453,16 +455,18 @@ async def stream(
|
||||
)
|
||||
|
||||
torrent_title = torrent["title"]
|
||||
formatted_components = get_formatted_components(
|
||||
rtn_data,
|
||||
torrent_title,
|
||||
torrent["seeders"],
|
||||
torrent["size"],
|
||||
torrent["tracker"],
|
||||
config["resultFormat"],
|
||||
)
|
||||
|
||||
the_stream = {
|
||||
"name": f"[{debrid_extension}{debrid_emoji}] Comet {rtn_data.resolution}",
|
||||
"description": format_title(
|
||||
rtn_data,
|
||||
torrent_title,
|
||||
torrent["seeders"],
|
||||
torrent["size"],
|
||||
torrent["tracker"],
|
||||
config["resultFormat"],
|
||||
),
|
||||
"description": format_title(formatted_components),
|
||||
"behaviorHints": {
|
||||
"bingeGroup": "comet|" + info_hash,
|
||||
"videoSize": torrent["size"],
|
||||
@@ -470,6 +474,11 @@ async def stream(
|
||||
},
|
||||
}
|
||||
|
||||
if chilllink:
|
||||
the_stream["_chilllink"] = format_chilllink(
|
||||
formatted_components, torrent["cached"]
|
||||
)
|
||||
|
||||
if debrid_service == "torrent":
|
||||
the_stream["infoHash"] = info_hash
|
||||
|
||||
|
||||
+77
-61
@@ -180,7 +180,7 @@ def format_group_info(data: ParsedData):
|
||||
return " • ".join(group_parts) if group_parts else ""
|
||||
|
||||
|
||||
def format_title(
|
||||
def get_formatted_components(
|
||||
data: ParsedData,
|
||||
ttitle: str,
|
||||
seeders: int,
|
||||
@@ -189,74 +189,90 @@ def format_title(
|
||||
result_format: list,
|
||||
):
|
||||
has_all = "all" in result_format
|
||||
components = {}
|
||||
|
||||
if has_all or "title" in result_format:
|
||||
components["title"] = f"📄 {ttitle}"
|
||||
|
||||
if has_all or "video_info" in result_format:
|
||||
info = format_video_info(data)
|
||||
if info:
|
||||
components["video"] = f"📹 {info}"
|
||||
|
||||
if has_all or "audio_info" in result_format:
|
||||
info = format_audio_info(data)
|
||||
if info:
|
||||
components["audio"] = f"🔊 {info}"
|
||||
|
||||
if has_all or "quality_info" in result_format:
|
||||
info = format_quality_info(data)
|
||||
if info:
|
||||
components["quality"] = f"⭐ {info}"
|
||||
|
||||
if has_all or "release_group" in result_format:
|
||||
info = format_group_info(data)
|
||||
if info:
|
||||
components["group"] = f"🏷️ {info}"
|
||||
|
||||
if (has_all or "seeders" in result_format) and seeders is not None:
|
||||
components["seeders"] = f"👤 {seeders}"
|
||||
|
||||
if has_all or "size" in result_format:
|
||||
components["size"] = f"💾 {format_bytes(size)}"
|
||||
|
||||
if has_all or "tracker" in result_format:
|
||||
components["tracker"] = f"🔎 {tracker}"
|
||||
|
||||
if (
|
||||
(has_all or "languages" in result_format)
|
||||
and hasattr(data, "languages")
|
||||
and data.languages
|
||||
):
|
||||
formatted_languages = "/".join(
|
||||
get_language_emoji(language) for language in data.languages
|
||||
)
|
||||
components["languages"] = formatted_languages
|
||||
|
||||
return components
|
||||
|
||||
|
||||
def format_title(components: dict):
|
||||
lines = []
|
||||
|
||||
show_title = has_all or "title" in result_format
|
||||
if show_title:
|
||||
lines.append(f"📄 {ttitle}")
|
||||
if "title" in components:
|
||||
lines.append(components["title"])
|
||||
|
||||
show_video = has_all or "video_info" in result_format
|
||||
show_audio = has_all or "audio_info" in result_format
|
||||
show_quality = has_all or "quality_info" in result_format
|
||||
show_group = has_all or "release_group" in result_format
|
||||
video_audio = [components[k] for k in ["video", "audio"] if k in components]
|
||||
if video_audio:
|
||||
lines.append(" | ".join(video_audio))
|
||||
|
||||
video_audio_parts = []
|
||||
quality_group = [components[k] for k in ["quality", "group"] if k in components]
|
||||
if quality_group:
|
||||
lines.append(" | ".join(quality_group))
|
||||
|
||||
if show_video:
|
||||
video_info = format_video_info(data)
|
||||
if video_info:
|
||||
video_audio_parts.append(f"📹 {video_info}")
|
||||
info = [components[k] for k in ["seeders", "size", "tracker"] if k in components]
|
||||
if info:
|
||||
lines.append(" ".join(info))
|
||||
|
||||
if show_audio:
|
||||
audio_info = format_audio_info(data)
|
||||
if audio_info:
|
||||
video_audio_parts.append(f"🔊 {audio_info}")
|
||||
|
||||
if video_audio_parts:
|
||||
lines.append(" | ".join(video_audio_parts))
|
||||
|
||||
quality_parts = []
|
||||
|
||||
if show_quality:
|
||||
quality_info = format_quality_info(data)
|
||||
if quality_info:
|
||||
quality_parts.append(f"⭐ {quality_info}")
|
||||
|
||||
if show_group:
|
||||
groups = format_group_info(data)
|
||||
if groups:
|
||||
quality_parts.append(f"🏷️ {groups}")
|
||||
|
||||
if quality_parts:
|
||||
lines.append(" | ".join(quality_parts))
|
||||
|
||||
show_seeders = has_all or "seeders" in result_format
|
||||
show_size = has_all or "size" in result_format
|
||||
show_tracker = has_all or "tracker" in result_format
|
||||
|
||||
info_parts = []
|
||||
|
||||
if show_seeders and seeders is not None:
|
||||
info_parts.append(f"👤 {seeders}")
|
||||
|
||||
if show_size:
|
||||
info_parts.append(f"💾 {format_bytes(size)}")
|
||||
|
||||
if show_tracker:
|
||||
info_parts.append(f"🔎 {tracker}")
|
||||
|
||||
if info_parts:
|
||||
lines.append(" ".join(info_parts))
|
||||
|
||||
show_languages = has_all or "languages" in result_format
|
||||
if show_languages:
|
||||
if hasattr(data, "languages") and data.languages:
|
||||
formatted_languages = "/".join(
|
||||
get_language_emoji(language) for language in data.languages
|
||||
)
|
||||
lines.append(f"{formatted_languages}")
|
||||
if "languages" in components:
|
||||
lines.append(components["languages"])
|
||||
|
||||
if not lines:
|
||||
return "Empty result format configuration"
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def format_chilllink(components: dict, cached: bool):
|
||||
metadata = []
|
||||
|
||||
if cached:
|
||||
metadata.append("⚡ Instant")
|
||||
else:
|
||||
metadata.append("⬇️ Not Cached")
|
||||
|
||||
for key, value in components.items():
|
||||
if key != "title":
|
||||
metadata.append(value)
|
||||
|
||||
return metadata
|
||||
|
||||
Reference in New Issue
Block a user