Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion GUI/manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
import os
import sys


# Fix PYTHONPATH
current_dir = os.path.dirname(os.path.abspath(__file__))
parent_dir = os.path.dirname(current_dir)
if parent_dir not in sys.path:
sys.path.insert(0, parent_dir)



def main():
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "webgui.settings")
from django.core.management import execute_from_command_line
Expand Down
1 change: 0 additions & 1 deletion GUI/searchapp/__init__.py

This file was deleted.

72 changes: 72 additions & 0 deletions GUI/searchapp/api/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# 06-06-2025 By @FrancescoGrazioso -> "https://github.com/FrancescoGrazioso"


from typing import Dict, Type


# Internal utilities
from .base import BaseStreamingAPI
from .streamingcommunity import StreamingCommunityAPI
from .animeunity import AnimeUnityAPI


_API_REGISTRY: Dict[str, Type[BaseStreamingAPI]] = {
'streamingcommunity': StreamingCommunityAPI,
'animeunity': AnimeUnityAPI,
}


def get_api(site_name: str) -> BaseStreamingAPI:
"""
Get API instance for a specific site.

Args:
site_name: Name of the streaming site

Returns:
Instance of the appropriate API class
"""
site_key = site_name.lower().split('_')[0]

if site_key not in _API_REGISTRY:
raise ValueError(
f"Unsupported site: {site_name}. "
f"Available sites: {', '.join(_API_REGISTRY.keys())}"
)

api_class = _API_REGISTRY[site_key]
return api_class()


def get_available_sites() -> list:
"""
Get list of available streaming sites.

Returns:
List of site names
"""
return list(_API_REGISTRY.keys())


def register_api(site_name: str, api_class: Type[BaseStreamingAPI]):
"""
Register a new API class.

Args:
site_name: Name of the site
api_class: API class that inherits from BaseStreamingAPI
"""
if not issubclass(api_class, BaseStreamingAPI):
raise ValueError(f"{api_class} must inherit from BaseStreamingAPI")

_API_REGISTRY[site_name.lower()] = api_class


__all__ = [
'BaseStreamingAPI',
'StreamingCommunityAPI',
'AnimeUnityAPI',
'get_api',
'get_available_sites',
'register_api'
]
142 changes: 142 additions & 0 deletions GUI/searchapp/api/animeunity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# 06-06-2025 By @FrancescoGrazioso -> "https://github.com/FrancescoGrazioso"


import importlib
from typing import List, Optional


# Internal utilities
from .base import BaseStreamingAPI, MediaItem, Season, Episode


# External utilities
from StreamingCommunity.Util.config_json import config_manager
from StreamingCommunity.Api.Site.animeunity.util.ScrapeSerie import ScrapeSerieAnime



class AnimeUnityAPI(BaseStreamingAPI):
def __init__(self):
super().__init__()
self.site_name = "animeunity"
self._load_config()
self._search_fn = None

def _load_config(self):
"""Load site configuration."""
self.base_url = (config_manager.get_site("animeunity", "full_url") or "").rstrip("/")

def _get_search_fn(self):
"""Lazy load the search function."""
if self._search_fn is None:
module = importlib.import_module("StreamingCommunity.Api.Site.animeunity")
self._search_fn = getattr(module, "search")
return self._search_fn

def search(self, query: str) -> List[MediaItem]:
"""
Search for content on AnimeUnity.

Args:
query: Search term

Returns:
List of MediaItem objects
"""
try:
search_fn = self._get_search_fn()
database = search_fn(query, get_onlyDatabase=True)

results = []
if database and hasattr(database, 'media_list'):
for element in database.media_list:
item_dict = element.__dict__.copy() if hasattr(element, '__dict__') else {}

media_item = MediaItem(
id=item_dict.get('id'),
title=item_dict.get('name'),
slug=item_dict.get('slug', ''),
type=item_dict.get('type'),
url=item_dict.get('url'),
poster=item_dict.get('image'),
raw_data=item_dict
)
results.append(media_item)

return results

except Exception as e:
raise Exception(f"AnimeUnity search error: {e}")

def get_series_metadata(self, media_item: MediaItem) -> Optional[List[Season]]:
"""
Get seasons and episodes for an AnimeUnity series.
Note: AnimeUnity typically has single season anime.

Args:
media_item: MediaItem to get metadata for

Returns:
List of Season objects (usually one season), or None if not a series
"""
# Check if it's a movie or OVA
if media_item.is_movie:
return None

try:
scraper = ScrapeSerieAnime(self.base_url)
scraper.setup(series_name=media_item.slug, media_id=media_item.id)

episodes_count = scraper.get_count_episodes()
if not episodes_count:
return None

# AnimeUnity typically has single season
episodes = []
for ep_num in range(1, episodes_count + 1):
episode = Episode(
number=ep_num,
name=f"Episodio {ep_num}",
id=ep_num
)
episodes.append(episode)

season = Season(number=1, episodes=episodes)
return [season]

except Exception as e:
raise Exception(f"Error getting series metadata: {e}")

def start_download(self, media_item: MediaItem, season: Optional[str] = None, episodes: Optional[str] = None) -> bool:
"""
Start downloading from AnimeUnity.

Args:
media_item: MediaItem to download
season: Season number (typically 1 for anime)
episodes: Episode selection

Returns:
True if download started successfully
"""
try:
search_fn = self._get_search_fn()

# Prepare direct_item from MediaItem
direct_item = media_item.raw_data or media_item.to_dict()

# For AnimeUnity, we only use episode selection
selections = None
if episodes:
selections = {'episode': episodes}

elif not media_item.is_movie:
# Default: download all episodes
selections = {'episode': '*'}

# Execute download
search_fn(direct_item=direct_item, selections=selections)
return True

except Exception as e:
raise Exception(f"Download error: {e}")
Loading