diff --git a/README.md b/README.md index cfe690e..67f0ce8 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@

Optimisé pour le contenu francophone

- StreamFusion Logo + StreamFusion Logo

@@ -20,6 +20,7 @@ StreamFusion est un addon avancé pour Stremio, spécialement conçu pour améli - **Intégration de Zilean** : Indexe les hashlist de DébridMediaManager pour accéder aux contenus en cache chez les débrideurs. - **Intégration de Real-Debrid** : Permet la redistribution des liens de streaming en direct et l'ajout de torrents depuis Stremio. - **Intégration de AllDebrid** : Offre un accès aux liens de streaming et aux torrents via AllDebrid. +- **Intégration de TorBox** : Offre un accès aux liens de streaming et aux torrents avec TorBox. - **Tri optimisé pour le contenu français** : Offre des résultats ciblés et de qualité, avec reconnaissance des langues et des teams. - **Sécurité renforcée** : Protège l'application avec une clé API via une interface de gestion. diff --git a/docs/en/StreamFusion/streamfusion.md b/docs/en/StreamFusion/streamfusion.md index 89ad3e9..5701409 100644 --- a/docs/en/StreamFusion/streamfusion.md +++ b/docs/en/StreamFusion/streamfusion.md @@ -10,6 +10,7 @@ StreamFusion is an advanced addon for Stremio, specially designed to enhance the - **Zilean integration**: Indexes DébridMediaManager hashlists to access cached content from debridders. - **Real-Debrid integration**: Allows direct redistribution of streaming links and torrent addition from Stremio. - **AllDebrid integration**: Provides access to streaming links and torrents via AllDebrid. +- **TorBox integration**: Provides access to streaming links and torrents via TorBox. - **Optimized sorting for French content**: Offers targeted and quality results, with language and team recognition. - **Enhanced security**: Protects the application with an API key via a management interface. diff --git a/docs/fr/StreamFusion/images/dark_logo_streamfusion.png b/docs/fr/StreamFusion/images/dark_logo_streamfusion.png new file mode 100644 index 0000000..1311e7e Binary files /dev/null and b/docs/fr/StreamFusion/images/dark_logo_streamfusion.png differ diff --git a/docs/fr/StreamFusion/streamfusion.md b/docs/fr/StreamFusion/streamfusion.md index 4cdba19..cc010fe 100644 --- a/docs/fr/StreamFusion/streamfusion.md +++ b/docs/fr/StreamFusion/streamfusion.md @@ -1,5 +1,5 @@

- StreamFusion + StreamFusion

Présentation de StreamFusion

@@ -11,6 +11,7 @@ StreamFusion est un addon avancé pour Stremio, spécialement conçu pour améli - **Intégration de Zilean** : Indexe les hashlist de DébridMediaManager pour accéder aux contenus en cache chez les débrideurs. - **Intégration de Real-Debrid** : Permet la redistribution des liens de streaming en direct et l'ajout de torrents depuis Stremio. - **Intégration de AllDebrid** : Offre un accès aux liens de streaming et aux torrents via AllDebrid. +- **Intégration de TorBox** : Offre un accès aux liens de streaming et aux torrents avec TorBox. - **Tri optimisé pour le contenu français** : Offre des résultats ciblés et de qualité, avec reconnaissance des langues et des teams. - **Sécurité renforcée** : Protège l'application avec une clé API via une interface de gestion. diff --git a/pyproject.toml b/pyproject.toml index 2586d96..982a1fa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "stream-fusion" -version = "2.1.0" +version = "2.2.0" description = "StreamFusion is an advanced plugin for Stremio that significantly enhances its streaming capabilities with debrid service." authors = ["LimeDrive "] readme = "README.md" diff --git a/stream_fusion/constants.py b/stream_fusion/constants.py index 12b3345..006fc35 100644 --- a/stream_fusion/constants.py +++ b/stream_fusion/constants.py @@ -4,16 +4,12 @@ NO_CONFIG = {'streams': [{'url': "#", 'title': "No configuration found"}]} JACKETT_ERROR = {'streams': [{'url': "#", 'title': "An error occured"}]} -# CACHER_URL = "https://stremio-jackett-cacher.elfhosted.com/" - NO_CACHE_HEADERS = { "Cache-Control": "no-store, no-cache, must-revalidate, max-age=0", "Pragma": "no-cache", "Expires": "0", } -NO_CACHE_VIDEO_URL = "https://github.com/aymene69/stremio-jackett/raw/main/source/videos/nocache.mp4" - EXCLUDED_TRACKERS = ['0day.kiev', '1ptbar', '2 Fast 4 You', '2xFree', '3ChangTrai', '3D Torrents', '3Wmg', '4thD', '52PT', '720pier', 'Abnormal', 'ABtorrents', 'Acid-Lounge', 'Across The Tasman', 'Aftershock', 'AGSVPT', 'Aidoru!Online', 'Aither (API)', 'AlphaRatio', 'Amigos Share Club', 'AniDUB', @@ -122,9 +118,6 @@ "FRENCH": r"\b(?:FRENCH|FR)\b", } -# REDIS_HOST = 'redis' -# REDIS_PORT = 6379 - class CustomException(Exception): def __init__(self, status_code: int, message: Any): self.status_code = status_code diff --git a/stream_fusion/services/postgresql/dao/torrentitem_dao.py b/stream_fusion/services/postgresql/dao/torrentitem_dao.py index dff9bc4..8ed8975 100644 --- a/stream_fusion/services/postgresql/dao/torrentitem_dao.py +++ b/stream_fusion/services/postgresql/dao/torrentitem_dao.py @@ -1,5 +1,5 @@ from typing import List, Optional -from fastapi import Depends, HTTPException +from fastapi import Depends from sqlalchemy import select, func from sqlalchemy.ext.asyncio import AsyncSession from datetime import datetime, timezone @@ -24,11 +24,10 @@ async def create_torrent_item(self, torrent_item: TorrentItem, id: str) -> Torre await self.session.flush() await self.session.refresh(new_item) - logger.success(f"Created new TorrentItem: {new_item.id}") + logger.debug(f"TorrentItemDAO: Created new TorrentItem: {new_item.id}") return new_item except Exception as e: - logger.error(f"Error creating TorrentItem: {str(e)}") - raise HTTPException(status_code=500, detail="Internal server error") + logger.error(f"TorrentItemDAO: Error creating TorrentItem: {str(e)}") async def get_all_torrent_items(self, limit: int, offset: int) -> List[TorrentItemModel]: async with self.session.begin(): @@ -36,11 +35,10 @@ async def get_all_torrent_items(self, limit: int, offset: int) -> List[TorrentIt query = select(TorrentItemModel).limit(limit).offset(offset) result = await self.session.execute(query) items = result.scalars().all() - logger.info(f"Retrieved {len(items)} TorrentItems") + logger.debug(f"TorrentItemDAO: Retrieved {len(items)} TorrentItems") return items except Exception as e: - logger.error(f"Error retrieving TorrentItems: {str(e)}") - raise HTTPException(status_code=500, detail="Internal server error") + logger.error(f"TorrentItemDAO: Error retrieving TorrentItems: {str(e)}") async def get_torrent_item_by_id(self, item_id: str) -> Optional[TorrentItemModel]: async with self.session.begin(): @@ -49,14 +47,14 @@ async def get_torrent_item_by_id(self, item_id: str) -> Optional[TorrentItemMode result = await self.session.execute(query) db_item = result.scalar_one_or_none() if db_item: - logger.info(f"Retrieved TorrentItem: {item_id}") + logger.debug(f"TorrentItemDAO: Retrieved TorrentItem: {item_id}") return db_item else: - logger.info(f"TorrentItem not found: {item_id}") + logger.debug(f"TorrentItemDAO: TorrentItem not found: {item_id}") return None except Exception as e: - logger.error(f"Error retrieving TorrentItem {item_id}: {str(e)}") - raise HTTPException(status_code=500, detail="Internal server error") + logger.error(f"TorrentItemDAO: Error retrieving TorrentItem {item_id}: {str(e)}") + return None async def update_torrent_item(self, item_id: str, torrent_item: TorrentItem) -> TorrentItemModel: async with self.session.begin(): @@ -66,8 +64,8 @@ async def update_torrent_item(self, item_id: str, torrent_item: TorrentItem) -> db_item = result.scalar_one_or_none() if not db_item: - logger.warning(f"TorrentItem not found for update: {item_id}") - raise HTTPException(status_code=404, detail="TorrentItem not found") + logger.warning(f"TorrentItemDAO: TorrentItem not found for update: {item_id}") + return None # Update fields for key, value in torrent_item.__dict__.items(): @@ -77,13 +75,11 @@ async def update_torrent_item(self, item_id: str, torrent_item: TorrentItem) -> await self.session.flush() await self.session.refresh(db_item) - logger.info(f"Updated TorrentItem: {item_id}") + logger.debug(f"TorrentItemDAO: Updated TorrentItem: {item_id}") return db_item - except HTTPException: - raise except Exception as e: - logger.error(f"Error updating TorrentItem {item_id}: {str(e)}") - raise HTTPException(status_code=500, detail="Internal server error") + logger.error(f"TorrentItemDAO: Error updating TorrentItem {item_id}: {str(e)}") + return None async def delete_torrent_item(self, item_id: str) -> bool: async with self.session.begin(): @@ -94,14 +90,14 @@ async def delete_torrent_item(self, item_id: str) -> bool: if db_item: await self.session.delete(db_item) - logger.info(f"Deleted TorrentItem: {item_id}") + logger.debug(f"TorrentItemDAO: Deleted TorrentItem: {item_id}") return True else: - logger.warning(f"TorrentItem not found for deletion: {item_id}") + logger.warning(f"TorrentItemDAO: TorrentItem not found for deletion: {item_id}") return False except Exception as e: - logger.error(f"Error deleting TorrentItem {item_id}: {str(e)}") - raise HTTPException(status_code=500, detail="Internal server error") + logger.error(f"TorrentItemDAO: Error deleting TorrentItem {item_id}: {str(e)}") + return False async def get_torrent_items_by_info_hash(self, info_hash: str) -> List[TorrentItemModel]: async with self.session.begin(): @@ -109,24 +105,24 @@ async def get_torrent_items_by_info_hash(self, info_hash: str) -> List[TorrentIt query = select(TorrentItemModel).where(TorrentItemModel.info_hash == info_hash) result = await self.session.execute(query) items = result.scalars().all() - logger.info(f"Retrieved {len(items)} TorrentItems with info_hash: {info_hash}") + logger.debug(f"TorrentItemDAO: Retrieved {len(items)} TorrentItems with info_hash: {info_hash}") return items except Exception as e: - logger.error(f"Error retrieving TorrentItems by info_hash {info_hash}: {str(e)}") - raise HTTPException(status_code=500, detail="Internal server error") - + logger.error(f"TorrentItemDAO: Error retrieving TorrentItems by info_hash {info_hash}: {str(e)}") + return None + async def get_torrent_items_by_indexer(self, indexer: str) -> List[TorrentItemModel]: async with self.session.begin(): try: query = select(TorrentItemModel).where(TorrentItemModel.indexer == indexer) result = await self.session.execute(query) items = result.scalars().all() - logger.info(f"Retrieved {len(items)} TorrentItems from indexer: {indexer}") + logger.debug(f"TorrentItemDAO: Retrieved {len(items)} TorrentItems from indexer: {indexer}") return items except Exception as e: - logger.error(f"Error retrieving TorrentItems by indexer {indexer}: {str(e)}") - raise HTTPException(status_code=500, detail="Internal server error") - + logger.error(f"TorrentItemDAO: Error retrieving TorrentItems by indexer {indexer}: {str(e)}") + return None + async def is_torrent_item_cached(self, item_id: str) -> bool: async with self.session.begin(): try: @@ -134,11 +130,11 @@ async def is_torrent_item_cached(self, item_id: str) -> bool: result = await self.session.execute(query) count = result.scalar_one() is_cached = count > 0 - logger.info(f"TorrentItem {item_id} {'is' if is_cached else 'is not'} in cache") + logger.debug(f"TorrentItemDAO: TorrentItem {item_id} {'is' if is_cached else 'is not'} in cache") return is_cached except Exception as e: - logger.error(f"Error checking if TorrentItem {item_id} is cached: {str(e)}") - raise HTTPException(status_code=500, detail="Internal server error") + logger.error(f"TorrentItemDAO: Error checking if TorrentItem {item_id} is cached: {str(e)}") + return None async def get_torrent_items_by_type(self, item_type: str) -> List[TorrentItemModel]: async with self.session.begin(): @@ -146,11 +142,11 @@ async def get_torrent_items_by_type(self, item_type: str) -> List[TorrentItemMod query = select(TorrentItemModel).where(TorrentItemModel.type == item_type) result = await self.session.execute(query) items = result.scalars().all() - logger.info(f"Retrieved {len(items)} TorrentItems of type: {item_type}") + logger.debug(f"TorrentItemDAO: Retrieved {len(items)} TorrentItems of type: {item_type}") return items except Exception as e: - logger.error(f"Error retrieving TorrentItems by type {item_type}: {str(e)}") - raise HTTPException(status_code=500, detail="Internal server error") + logger.error(f"TorrentItemDAO: Error retrieving TorrentItems by type {item_type}: {str(e)}") + return None async def get_torrent_items_by_availability(self, available: bool) -> List[TorrentItemModel]: async with self.session.begin(): @@ -158,29 +154,8 @@ async def get_torrent_items_by_availability(self, available: bool) -> List[Torre query = select(TorrentItemModel).where(TorrentItemModel.availability == available) result = await self.session.execute(query) items = result.scalars().all() - logger.info(f"Retrieved {len(items)} TorrentItems with availability: {available}") + logger.debug(f"TorrentItemDAO: Retrieved {len(items)} TorrentItems with availability: {available}") return items except Exception as e: - logger.error(f"Error retrieving TorrentItems by availability {available}: {str(e)}") - raise HTTPException(status_code=500, detail="Internal server error") - - # async def update_torrent_item_availability(self, item_id: str, available: bool) -> bool: - # async with self.session.begin(): - # try: - # query = select(TorrentItemModel).where(TorrentItemModel.id == item_id) - # result = await self.session.execute(query) - # db_item = result.scalar_one_or_none() - - # if not db_item: - # logger.warning(f"TorrentItem not found for availability update: {item_id}") - # return False - - # db_item.availability = available - # db_item.updated_at = int(datetime.now(timezone.utc).timestamp()) - # await self.session.flush() - - # logger.info(f"Updated availability for TorrentItem {item_id}: {available}") - # return True - # except Exception as e: - # logger.error(f"Error updating availability for TorrentItem {item_id}: {str(e)}") - # raise HTTPException(status_code=500, detail="Internal server error") \ No newline at end of file + logger.error(f"TorrentItemDAO: Error retrieving TorrentItems by availability {available}: {str(e)}") + return None \ No newline at end of file diff --git a/stream_fusion/settings.py b/stream_fusion/settings.py index 5a45d6c..69a9b84 100644 --- a/stream_fusion/settings.py +++ b/stream_fusion/settings.py @@ -16,11 +16,26 @@ class LogLevel(str, enum.Enum): ERROR = "ERROR" FATAL = "FATAL" + class DebridService(str, enum.Enum): """Possible debrid services.""" RD = "RD" AD = "AD" + TB = "TB" + + +class NoCacheVideoLanguages(str, enum.Enum): + """Possible languages for which to not cache video results.""" + + FR = "https://github.com/LimeDrive/stream-fusion/raw/refs/heads/develop/stream_fusion/static/videos/fr_download_video.mp4" + EN = "https://github.com/LimeDrive/stream-fusion/raw/refs/heads/develop/stream_fusion/static/videos/en_download_video.mp4" + + @classmethod + def get_url(cls, language): + """Get the video URL for a given language.""" + return cls[language.upper()].value + def get_default_worker_count(): """ @@ -29,13 +44,15 @@ def get_default_worker_count(): """ return min(max(multiprocessing.cpu_count() * 2, 2), 6) + def check_env_variable(var_name): value = os.getenv(var_name.upper()) - + if value and isinstance(value, str) and len(value.strip()) >= 10: return True return False + class Settings(BaseSettings): """Settings for the application""" @@ -53,18 +70,24 @@ class Settings(BaseSettings): ) ) use_https: bool = False - default_debrid_service: DebridService = DebridService.RD + download_service: DebridService = DebridService.TB + no_cache_video_language: NoCacheVideoLanguages = NoCacheVideoLanguages.FR # PROXY - proxied_link: bool = check_env_variable("RD_TOKEN") or check_env_variable("AD_TOKEN") + proxied_link: bool = check_env_variable("RD_TOKEN") or check_env_variable( + "AD_TOKEN" + ) proxy_url: str | URL | None = None playback_proxy: bool | None = ( None # If set, the link will be proxied through the given proxy. ) + proxy_buffer_size: int = 1024 * 1024 # REALDEBRID rd_token: str | None = None rd_unique_account: bool = check_env_variable("RD_TOKEN") + rd_base_url: str = "https://api.real-debrid.com/rest" + rd_api_version: str = "1.0" # ALLDEBRID ad_token: str | None = None @@ -72,6 +95,14 @@ class Settings(BaseSettings): ad_user_app: str = "streamfusion" ad_user_ip: str | None = None ad_use_proxy: bool = check_env_variable("PROXY_URL") + ad_base_url: str = "https://api.alldebrid.com" + ad_api_version: str = "v4" + + # TORBOX + tb_token: str | None = None + tb_unique_account: bool = check_env_variable("TB_TOKEN") + tb_base_url: str = "https://api.torbox.app" + tb_api_version: str = "v1" # LOGGING log_level: LogLevel = LogLevel.INFO @@ -82,12 +113,12 @@ class Settings(BaseSettings): secret_api_key: str | None = None security_hide_docs: bool = True - # POSTGRESQL_DB + # POSTGRESQL_DB # TODO: Change the values, but break dev environment pg_host: str = "stremio-postgres" pg_port: int = 5432 - pg_user: str = "streamfusion" #"stremio" - pg_pass: str = "streamfusion" #"stremio" + pg_user: str = "streamfusion" # "stremio" + pg_pass: str = "streamfusion" # "stremio" pg_base: str = "streamfusion" pg_echo: bool = False @@ -139,15 +170,15 @@ class Settings(BaseSettings): develop: bool = False reload: bool = False - @field_validator('proxy_url') + @field_validator("proxy_url") @classmethod def validate_and_create_proxy_url(cls, v: str | None) -> URL | None: if v is None: return None - - v = v.strip('"\'') - if not v.startswith(('http://', 'https://')): - v = 'http://' + v + + v = v.strip("\"'") + if not v.startswith(("http://", "https://")): + v = "http://" + v try: return URL(v) except ValueError as e: @@ -168,6 +199,7 @@ def pg_url(self) -> URL: password=self.pg_pass, path=f"/{self.pg_base}", ) + @property def jackett_url(self) -> URL: """ @@ -214,6 +246,13 @@ def redis_url(self) -> URL: env_file=".env", secrets_dir="/run/secrets", env_file_encoding="utf-8" ) + @property + def no_cache_video_url(self) -> str: + """ + Get the URL for the no-cache video based on the selected language. + """ + return self.no_cache_video_language.value + try: settings = Settings() diff --git a/stream_fusion/static/config.js b/stream_fusion/static/config.js index 68f6e80..210927f 100644 --- a/stream_fusion/static/config.js +++ b/stream_fusion/static/config.js @@ -215,7 +215,7 @@ function resetADAuthButton() { } function handleUniqueAccounts() { - const accounts = ['debrid_rd', 'debrid_ad', 'sharewood', 'yggflix']; + const accounts = ['debrid_rd', 'debrid_ad', 'debrid_tb', 'sharewood', 'yggflix']; accounts.forEach(account => { const checkbox = document.getElementById(account); @@ -251,20 +251,24 @@ function updateDebridOrderList() { const rdEnabled = document.getElementById('debrid_rd').checked || document.getElementById('debrid_rd').disabled; const adEnabled = document.getElementById('debrid_ad').checked || document.getElementById('debrid_ad').disabled; + const tbEnabled = document.getElementById('debrid_tb').checked || document.getElementById('debrid_tb').disabled; if (debridOrder.length === 0 || !debridOrder.every(service => (service === 'Real-Debrid' && rdEnabled) || - (service === 'AllDebrid' && adEnabled) + (service === 'AllDebrid' && adEnabled) || + (service === 'TorBox' && tbEnabled) )) { debridOrder = []; if (rdEnabled) debridOrder.push('Real-Debrid'); if (adEnabled) debridOrder.push('AllDebrid'); + if (tbEnabled) debridOrder.push('TorBox'); } debridOrder.forEach(serviceName => { if ((serviceName === 'Real-Debrid' && rdEnabled) || - (serviceName === 'AllDebrid' && adEnabled)) { + (serviceName === 'AllDebrid' && adEnabled) || + (serviceName === 'TorBox' && tbEnabled)) { addDebridToList(serviceName); } }); @@ -275,6 +279,9 @@ function updateDebridOrderList() { if (adEnabled && !debridOrder.includes('AllDebrid')) { addDebridToList('AllDebrid'); } + if (tbEnabled && !debridOrder.includes('TorBox')) { + addDebridToList('TorBox'); + } Sortable.create(debridOrderList, { animation: 150, @@ -324,6 +331,7 @@ function updateDebridDownloaderOptions() { const rdEnabled = document.getElementById('debrid_rd').checked || document.getElementById('debrid_rd').disabled; const adEnabled = document.getElementById('debrid_ad').checked || document.getElementById('debrid_ad').disabled; + const tbEnabled = document.getElementById('debrid_tb').checked || document.getElementById('debrid_tb').disabled; let firstOption = null; @@ -331,10 +339,13 @@ function updateDebridDownloaderOptions() { firstOption = addDebridDownloaderOption('Real-Debrid'); } if (adEnabled) { + firstOption = addDebridDownloaderOption('AllDebrid'); + } + if (tbEnabled) { if (!firstOption) { - firstOption = addDebridDownloaderOption('AllDebrid'); + firstOption = addDebridDownloaderOption('TorBox'); } else { - addDebridDownloaderOption('AllDebrid'); + addDebridDownloaderOption('TorBox'); } } @@ -375,6 +386,8 @@ function updateProviderFields() { document.getElementById('debrid_rd').disabled; const ADdebridChecked = document.getElementById('debrid_ad').checked || document.getElementById('debrid_ad').disabled; + const TBdebridChecked = document.getElementById('debrid_tb').checked || + document.getElementById('debrid_tb').disabled; const cacheChecked = document.getElementById('cache')?.checked; const yggflixChecked = document.getElementById('yggflix')?.checked || document.getElementById('yggflix')?.disabled; @@ -384,6 +397,7 @@ function updateProviderFields() { // Mise à jour de l'affichage des champs setElementDisplay('rd_debrid-fields', RDdebridChecked ? 'block' : 'none'); setElementDisplay('ad_debrid-fields', ADdebridChecked ? 'block' : 'none'); + setElementDisplay('tb_debrid-fields', TBdebridChecked ? 'block' : 'none'); setElementDisplay('cache-fields', cacheChecked ? 'block' : 'none'); setElementDisplay('ygg-fields', yggflixChecked ? 'block' : 'none'); setElementDisplay('sharewood-fields', sharewoodChecked ? 'block' : 'none'); @@ -392,7 +406,7 @@ function updateProviderFields() { const debridOrderList = document.getElementById('debridOrderList'); if (debridOrderCheckbox && debridOrderList) { - const anyDebridEnabled = RDdebridChecked || ADdebridChecked; + const anyDebridEnabled = RDdebridChecked || ADdebridChecked || TBdebridChecked; debridOrderCheckbox.disabled = !anyDebridEnabled; @@ -416,14 +430,15 @@ function updateProviderFields() { function ensureDebridConsistency() { const RDdebridChecked = document.getElementById('debrid_rd').checked; const ADdebridChecked = document.getElementById('debrid_ad').checked; + const TBdebridChecked = document.getElementById('debrid_tb').checked; const debridOrderChecked = document.getElementById('debrid_order').checked; - if (!RDdebridChecked && !ADdebridChecked) { + if (!RDdebridChecked && !ADdebridChecked && !TBdebridChecked) { document.getElementById('debrid_order').checked = false; document.getElementById('debridOrderList').classList.add('hidden'); } - if (debridOrderChecked && !RDdebridChecked && !ADdebridChecked) { + if (debridOrderChecked && !RDdebridChecked && !ADdebridChecked && !TBdebridChecked) { document.getElementById('debrid_order').checked = false; } @@ -473,6 +488,9 @@ function loadData() { languages: ['fr', 'multi'], debrid_rd: false, debrid_ad: false, + debrid_tb: false, + tb_usenet: false, + tb_search: false, debrid_order: false }; @@ -502,19 +520,24 @@ function loadData() { const serviceArray = decodedData.service || []; setElementValue('debrid_rd', serviceArray.includes('Real-Debrid'), defaultConfig.debrid_rd); setElementValue('debrid_ad', serviceArray.includes('AllDebrid'), defaultConfig.debrid_ad); + setElementValue('debrid_tb', serviceArray.includes('TorBox'), defaultConfig.debrid_tb); setElementValue('debrid_order', serviceArray.length > 0, defaultConfig.debrid_order); - + // Catalogues setElementValue('ctg_yggtorrent', decodedData.yggtorrentCtg, defaultConfig.ctg_yggtorrent); setElementValue('ctg_yggflix', decodedData.yggflixCtg, defaultConfig.ctg_yggflix); - + // Tokens et passkeys setElementValue('rd_token_info', decodedData.RDToken, ''); setElementValue('ad_token_info', decodedData.ADToken, ''); + setElementValue('tb_token_info', decodedData.TBToken, ''); setElementValue('sharewoodPasskey', decodedData.sharewoodPasskey, ''); setElementValue('yggPasskey', decodedData.yggPasskey, ''); setElementValue('ApiKey', decodedData.apiKey, ''); setElementValue('exclusion-keywords', (decodedData.exclusionKeywords || []).join(', '), ''); + + setElementValue('tb_usenet', decodedData.TBUsenet, defaultConfig.tb_usenet); + setElementValue('tb_search', decodedData.TBSearch, defaultConfig.tb_search); handleUniqueAccounts(); updateProviderFields(); @@ -539,6 +562,9 @@ function getLink(method) { service: [], RDToken: document.getElementById('rd_token_info')?.value, ADToken: document.getElementById('ad_token_info')?.value, + TBToken: document.getElementById('tb_token_info')?.value, + TBUsenet: document.getElementById('tb_usenet')?.checked, + TBSearch: document.getElementById('tb_search')?.checked, sharewoodPasskey: document.getElementById('sharewoodPasskey')?.value, maxSize: parseInt(document.getElementById('maxSize').value) || 16, exclusionKeywords: document.getElementById('exclusion-keywords').value.split(',').map(keyword => keyword.trim()).filter(keyword => keyword !== ''), @@ -571,6 +597,7 @@ function getLink(method) { if (data.cache && !data.cacheUrl) missingRequiredFields.push("Cache URL"); if (data.service.includes('Real-Debrid') && document.getElementById('rd_token_info') && !data.RDToken) missingRequiredFields.push("Real-Debrid Account Connection"); if (data.service.includes('AllDebrid') && document.getElementById('ad_token_info') && !data.ADToken) missingRequiredFields.push("AllDebrid Account Connection"); + if (data.service.includes('TorBox') && document.getElementById('tb_token_info') && !data.TBToken) missingRequiredFields.push("TorBox Account Connection"); if (data.languages.length === 0) missingRequiredFields.push("Languages"); if (!data.apiKey) missingRequiredFields.push("API Key"); if (data.yggflix && document.getElementById('yggPasskey') && !data.yggPasskey) missingRequiredFields.push("Ygg Passkey"); diff --git a/stream_fusion/static/dark_logo_streamfusion.png b/stream_fusion/static/dark_logo_streamfusion.png new file mode 100644 index 0000000..1311e7e Binary files /dev/null and b/stream_fusion/static/dark_logo_streamfusion.png differ diff --git a/stream_fusion/static/index.html b/stream_fusion/static/index.html index 44ed561..ba33b7f 100644 --- a/stream_fusion/static/index.html +++ b/stream_fusion/static/index.html @@ -39,29 +39,30 @@ logo
-

StreamFusion configuration

+

FRENCH Optimized - Stremio Addon

- FRENCH Optimized - Stremio Addon

-
-

Api-Key Settings

-

Enter your API key information - here.

- -
-
- - + Stream-Fusion +
+
+
+
+
+ + +
+
+
-
-

Streaming Settings

-

Configure your streaming - preferences here.

+
+

Streaming Settings

+

Configure your streaming + preferences here.

+
@@ -92,6 +93,20 @@

Streaming Settings

+
+
+ +
+
+ +

Activate TorBox service for streaming. +

+
+
+
Streaming Settings
-

Torrent Providers

-

Configure your torrent provider - settings here.

+
+

Torrent Providers

+

Configure your torrent provider + settings here.

+
{% if jackett_enable %} @@ -191,9 +208,12 @@

Torrent Providers

-

Debrid Custom Config

-

Customize your debrid service - settings here.

+
+

Debrid Custom Config

+

Customize your debrid service + settings here.

+
+
@@ -226,43 +246,12 @@

Debrid Custom Config

-
-

Catalogs Providers

-

Configure your catalogs - preferences here.

- -
-
-
- -
-
- -

Activate yggflix catalogs for discovers - contents.

-
-
- -
-
- -
-
- -

Activate yggtorrent catalogs for discovers - contents.

-
-
-
-
- {% if not rd_unique_account %}