update detection.py, sharewood_service.py, torrent_service.py and views.py

This commit is contained in:
LimeDrive 2024-07-21 22:57:02 +02:00
parent 67124a572d
commit 2eec98acb8
4 changed files with 268 additions and 12 deletions

View file

@ -6,14 +6,14 @@ def detect_languages(torrent_name):
"fr": r"\b(?:FR(?:ench|a|e|anc[eê]s)?|V(?:O?F(?:F|I|i)?|O?Q)|TRUEFRENCH|VOST(?:FR)?|SUBFRENCH)\b",
"en": r"\b(?:EN(?:G(?:LISH)?)?|VOST(?:EN)?|SUBBED)\b",
"es": r"\b(?:ES(?:P(?:ANISH)?)?|VOSE|SUBESP)\b",
"de": r"\b(?:DE(?:UTSCH|RMAN)?|GER(?:MAN)?|SUBGER)\b",
"it": r"\b(?:IT(?:A(?:LIAN)?)?|SUBITA)\b",
"pt": r"\b(?:PT(?:-BR)?|POR(?:TUGUESE)?|LEGENDADO)\b",
"ru": r"\b(?:RU(?:S(?:SIAN)?)?|SUBSRUS)\b",
"in": r"\b(?:INDIAN|HINDI|TELUGU|TAMIL|KANNADA|MALAYALAM|PUNJABI|MARATHI|BENGALI|GUJARATI|URDU|ODIA|ASSAMESE|KONKANI|MANIPURI|NEPALI|SANSKRIT|SINHALA|SINDHI|TIBETAN|BHOJPURI|DHIVEHI|KASHMIRI|KURUKH|MAITHILI|NEWARI|RAJASTHANI|SANTALI|SINDHI|TULU)\b",
"nl": r"\b(?:NL(?:D)?|DUTCH|SUBSNL)\b",
"hu": r"\b(?:HU(?:N(?:GARIAN)?)?|SUBHUN)\b",
"la": r"\b(?:LA(?:TIN(?:O)?)?)\b",
# "de": r"\b(?:DE(?:UTSCH|RMAN)?|GER(?:MAN)?|SUBGER)\b",
# "it": r"\b(?:IT(?:A(?:LIAN)?)?|SUBITA)\b",
# "pt": r"\b(?:PT(?:-BR)?|POR(?:TUGUESE)?|LEGENDADO)\b",
# "ru": r"\b(?:RU(?:S(?:SIAN)?)?|SUBSRUS)\b",
# "in": r"\b(?:INDIAN|HINDI|TELUGU|TAMIL|KANNADA|MALAYALAM|PUNJABI|MARATHI|BENGALI|GUJARATI|URDU|ODIA|ASSAMESE|KONKANI|MANIPURI|NEPALI|SANSKRIT|SINHALA|SINDHI|TIBETAN|BHOJPURI|DHIVEHI|KASHMIRI|KURUKH|MAITHILI|NEWARI|RAJASTHANI|SANTALI|SINDHI|TULU)\b",
# "nl": r"\b(?:NL(?:D)?|DUTCH|SUBSNL)\b",
# "hu": r"\b(?:HU(?:N(?:GARIAN)?)?|SUBHUN)\b",
# "la": r"\b(?:LA(?:TIN(?:O)?)?)\b",
"multi": r"\b(?:MULTI(?:LANG(?:UE)?)?|DUAL(?:AUDIO)?|VF2)\b",
}

View file

@ -0,0 +1,216 @@
import re
import urllib.parse
from typing import List, Union
from RTN import parse
from stream_fusion.logging_config import logger
from stream_fusion.utils.detection import detect_languages
from stream_fusion.utils.sharewood.sharewood_result import SharewoodResult
from stream_fusion.utils.models.movie import Movie
from stream_fusion.utils.models.series import Series
from stream_fusion.settings import settings
from stream_fusion.utils.sharewood.sharewood_api import SharewoodAPI
class SharewoodService:
"""Service for searching media on Sharewood."""
def __init__(self, config: dict):
self.sharewood = SharewoodAPI()
self.sharewood_url = settings.sharewood_url
self.sharewood_passkey = config.get("sharewoodPasskey")
def search(self, media: Union[Movie, Series]) -> List[SharewoodResult]:
"""
Search for a media (movie or series) on sharewood.
Args:
media (Union[Movie, Series]): The media to search for.
Returns:
List[sharewoodResult]: List of search results.
Raises:
TypeError: If the media type is neither Movie nor Series.
"""
if isinstance(media, Movie):
results = self.__search_movie(media)
elif isinstance(media, Series):
results = self.__search_series(media)
else:
raise TypeError("Only Movie and Series types are allowed as media!")
return self.__post_process_results(results, media)
def __convert_size(self, size_str):
units = {
"b": 1,
"kb": 1e3,
"mb": 1e6,
"gb": 1e9,
"tb": 1e12,
"pb": 1e15,
"kib": 1024,
"mib": 1024**2,
"gib": 1024**3,
"tib": 1024**4,
"pib": 1024**5,
}
value, unit = size_str.split()
value = float(value)
unit = unit.lower()
if unit not in units:
raise ValueError(f"Unité non supportée : {unit}")
size = value * units[unit]
return int(size)
def __clean_title(self, title):
pronouns_to_remove = [
"le",
"la",
"les",
"l'",
"un",
"une",
"des",
"du",
"de",
"à",
"au",
"aux",
"the",
"a",
"an",
"some",
"of",
"to",
"at",
"in",
"on",
"for",
"he",
"she",
"it",
"they",
"we",
"you",
"i",
"me",
"him",
"her",
"them",
"us",
"il",
"elle",
"on",
"nous",
"vous",
"ils",
"elles",
"je",
"tu",
"moi",
"toi",
"lui",
]
title = title.lower()
title = re.sub(r"[^a-zA-Z0-9\s]", " ", title)
words = title.split()
words = [word for word in words if word not in pronouns_to_remove]
cleaned_title = " ".join(words)
cleaned_title = re.sub(r"\s+", " ", cleaned_title).strip()
return cleaned_title
def __deduplicate_api_results(self, api_results):
unique_results = set()
deduplicated_results = []
for result in api_results:
result_tuple = tuple(sorted(result.items()))
if result_tuple not in unique_results:
unique_results.add(result_tuple)
deduplicated_results.append(result)
return deduplicated_results
def __remove_duplicate_titles(self, titles):
seen = set()
return [
title
for title in titles
if not (title.lower() in seen or seen.add(title.lower()))
]
def __search_movie(self, movie: Movie) -> List[dict]:
unique_titles = self.__remove_duplicate_titles(movie.titles)
clean_titles = [self.__clean_title(title) for title in unique_titles]
results = []
for title in clean_titles:
results.extend(self.sharewood.search(query=title, category=1))
return self.__deduplicate_api_results(results)
def __search_series(self, series: Series) -> List[dict]:
unique_titles = self.__remove_duplicate_titles(series.titles)
clean_titles = [self.__clean_title(title) for title in unique_titles]
search_texts = clean_titles.copy()
if hasattr(series, "season") and hasattr(series, "episode"):
search_texts.extend(
[f"{title} {series.season}{series.episode}" for title in clean_titles]
)
results = []
for text in search_texts:
results.extend(self.sharewood.search(query=text, category=1))
return self.__deduplicate_api_results(results)
def __filter_out_no_seeders(self, results: List[dict]) -> List[dict]:
"""Filter out results with less than 5 seeders."""
return [result for result in results if result.get("seeders", 0) >= 5]
def __process_download_link(self, id: int) -> str:
"""Generate the download link for a given torrent."""
return f"{self.sharewood_url}/{self.sharewood_passkey}/{id}/download"
def __generate_magnet_link(self, info_hash: str, name: str) -> str:
"""Generate the magnet link for a given torrent."""
encoded_name = urllib.parse.quote(name)
tracker = f"{self.sharewood_url}/announce/{self.sharewood_passkey}"
encoded_tracker = urllib.parse.quote(tracker)
return f"magnet:?xt=urn:btih:{info_hash}&dn={encoded_name}&tr={encoded_tracker}"
def __post_process_results(
self, results: List[dict], media: Union[Movie, Series]
) -> List[SharewoodResult]:
"""Process raw search results and convert them to sharewoodResult objects."""
if not results:
logger.info(f"No results found on sharewood for: {media.titles[0]}")
return []
results = self.__filter_out_no_seeders(results)
logger.info(f"{len(results)} results found on sharewood for: {media.titles[0]}")
items = []
for result in results:
item = SharewoodResult()
item.raw_title = result.get("name", "...")
item.info_hash = result.get("info_hash", None)
item.size = self.__convert_size(result.get("size", 0))
item.link = (
self.__process_download_link(result.get("id"))
if result.get("id")
else None
)
item.magnet = self.__generate_magnet_link(item.info_hash, item.raw_title)
item.indexer = "API - Sharewood"
item.seeders = result.get("seeders", 0)
item.privacy = "private"
item.languages = detect_languages(item.raw_title)
item.type = media.type
item.parsed_data = parse(item.raw_title)
items.append(item)
return items

View file

@ -10,6 +10,7 @@ import requests
from RTN import parse
from stream_fusion.utils.jackett.jackett_result import JackettResult
from stream_fusion.utils.sharewood.sharewood_result import SharewoodResult
from stream_fusion.utils.zilean.zilean_result import ZileanResult
from stream_fusion.utils.yggfilx.yggflix_result import YggflixResult
from stream_fusion.services.ygg_conn import YggSessionManager
@ -27,17 +28,17 @@ class TorrentService:
# self.__ygg_session_manager = YggSessionManager(config)
# self.__ygg_session = self.__ygg_session_manager.get_session()
def convert_and_process(self, results: List[JackettResult | ZileanResult | YggflixResult]):
def convert_and_process(self, results: List[JackettResult | ZileanResult | YggflixResult | SharewoodResult]):
threads = []
torrent_items_queue = queue.Queue()
def thread_target(result: JackettResult | ZileanResult | YggflixResult):
def thread_target(result: JackettResult | ZileanResult | YggflixResult | SharewoodResult):
torrent_item = result.convert_to_torrent_item()
if torrent_item.link.startswith("magnet:"):
processed_torrent_item = self.__process_magnet(torrent_item)
# elif torrent_item.link.startswith(settings.ygg_url):
# processed_torrent_item = self.__process_ygg_web_url(torrent_item)
elif torrent_item.link.startswith(settings.sharewood_url):
processed_torrent_item = self.__process_sharewood_web_url(torrent_item)
elif torrent_item.link.startswith(settings.ygg_proxy_url):
processed_torrent_item = self.__process_ygg_proxy_url(torrent_item)
else:
@ -80,6 +81,27 @@ class TorrentService:
return result
def __process_sharewood_web_url(self, result: TorrentItem):
if not self.config["sharewood"]:
logger.error("Sharewood is not enabled in the config. Skipping processing of Sharewood URL.")
try:
response = self.__session.get(result.link, timeout=5)
time.sleep(1) # API limit 1 request per second
except requests.exceptions.RequestException:
self.logger.error(f"Error while processing url: {result.link}")
return result
except requests.exceptions.ReadTimeout:
self.logger.error(f"Timeout while processing url: {result.link}")
return result
if response.status_code == 200:
return self.__process_torrent(result, response.content)
else:
self.logger.error(f"Error code {response.status_code} while processing ygg url: {result.link}")
return result
def __process_ygg_proxy_url(self, result: TorrentItem):
if not self.config["yggflix"]:

View file

@ -16,6 +16,7 @@ from stream_fusion.utils.filter_results import (
)
from stream_fusion.utils.jackett.jackett_result import JackettResult
from stream_fusion.utils.jackett.jackett_service import JackettService
from stream_fusion.utils.sharewood.sharewood_service import SharewoodService
from stream_fusion.utils.yggfilx.yggflix_result import YggflixResult
from stream_fusion.utils.yggfilx.yggflix_service import YggflixService
from stream_fusion.utils.metdata.cinemeta import Cinemeta
@ -182,6 +183,23 @@ async def get_results(
)
search_results = merge_items(search_results, yggflix_search_results)
if config["sharewood"] and len(search_results) < int(
config["minCachedResults"]
):
sharewood_service = SharewoodService(config)
sharewood_search_results = sharewood_service.search(media)
if sharewood_search_results:
logger.info(
f"Found {len(sharewood_search_results)} results from Sharewood"
)
sharewood_search_results = filter_items(
sharewood_search_results, media, config=config
)
sharewood_search_results = torrent_service.convert_and_process(
sharewood_search_results
)
search_results = merge_items(search_results, sharewood_search_results)
if config["jackett"] and len(search_results) < int(
config["minCachedResults"]
):