added logging, completed anime liast screen and refined code base

This commit is contained in:
Benex254
2024-08-05 09:46:53 +03:00
parent a59d8e5599
commit 047ce29da3
29 changed files with 264 additions and 139 deletions

View File

@@ -1,10 +1,12 @@
from inspect import isgenerator
from View import AnimeScreenView
from Model import AnimeScreenModel
from View.components import MediaCardsContainer
from Utility import show_notification
from kivy.clock import Clock from kivy.clock import Clock
from kivy.logger import Logger
from kivy.cache import Cache
from Model import AnimeScreenModel
from View import AnimeScreenView
Cache.register("data.anime",limit=20,timeout=600)
class AnimeScreenController: class AnimeScreenController:
def __init__(self, model:AnimeScreenModel): def __init__(self, model:AnimeScreenModel):
self.model = model self.model = model
@@ -13,12 +15,19 @@ class AnimeScreenController:
def get_view(self) -> AnimeScreenView: def get_view(self) -> AnimeScreenView:
return self.view return self.view
def update_anime_view(self,id:int): def update_anime_view(self,id:int,caller_screen_name:str):
if self.model.anime_id != id: if self.model.anime_id != id:
data = self.model.get_anime_data(id) if cached_anime_data:=Cache.get("data.anime",f"{id}"):
data = cached_anime_data
else:
data = self.model.get_anime_data(id)
if data[0]: if data[0]:
d = data[1]["data"]["Page"]["media"][0]
self.model.anime_id = id self.model.anime_id = id
Clock.schedule_once(lambda _:self.view.update_layout(data[1]["data"]["Page"]["media"][0])) Clock.schedule_once(lambda _:self.view.update_layout(d,caller_screen_name))
Logger.info(f"Anime Screen:Success in opening anime of id: {id}")
Cache.append("data.anime",f"{id}",data)
def update_my_list(self,*args): def update_my_list(self,*args):
self.model.update_user_anime_list(*args) self.model.update_user_anime_list(*args)

View File

@@ -13,3 +13,16 @@ class DownloadsScreenController:
def get_view(self) -> DownloadsScreenView: def get_view(self) -> DownloadsScreenView:
return self.view return self.view
# def requested_update_my_list_screen(self):
# user_anime_list = user_data_helper.get_user_anime_list()
# if animes_to_add:=difference(user_anime_list,self.model.already_in_user_anime_list):
# Logger.info("My List Screen:User anime list change;updating screen")
# anime_cards = self.model.update_my_anime_list_view(animes_to_add)
# self.model.already_in_user_anime_list = user_anime_list
# if isgenerator(anime_cards):
# for result_card in anime_cards:
# result_card.screen = self.view
# self.view.update_layout(result_card)

View File

@@ -1,10 +1,14 @@
from inspect import isgenerator from inspect import isgenerator
from View.HomeScreen.home_screen import HomeScreenView
from Model.home_screen import HomeScreenModel
from View.components.media_card.media_card import MediaCardsContainer
from Utility import show_notification
from kivy.clock import Clock from kivy.clock import Clock
from kivy.logger import Logger
from View import HomeScreenView
from Model import HomeScreenModel
from View.components import MediaCardsContainer
from Utility import show_notification
# TODO:Move the update home screen to homescreen.py # TODO:Move the update home screen to homescreen.py
class HomeScreenController: class HomeScreenController:
@@ -34,6 +38,7 @@ class HomeScreenController:
most_popular_cards_container.container.add_widget(card) most_popular_cards_container.container.add_widget(card)
self.view.main_container.add_widget(most_popular_cards_container) self.view.main_container.add_widget(most_popular_cards_container)
else: else:
Logger.error("Home Screen:Failed to load most popular anime")
self.populate_errors.append("Most Popular Anime") self.populate_errors.append("Most Popular Anime")
def favourite_anime(self): def favourite_anime(self):
@@ -46,6 +51,7 @@ class HomeScreenController:
most_favourite_cards_container.container.add_widget(card) most_favourite_cards_container.container.add_widget(card)
self.view.main_container.add_widget(most_favourite_cards_container) self.view.main_container.add_widget(most_favourite_cards_container)
else: else:
Logger.error("Home Screen:Failed to load most favourite anime")
self.populate_errors.append("Most favourite Anime") self.populate_errors.append("Most favourite Anime")
def trending_anime(self): def trending_anime(self):
@@ -58,6 +64,7 @@ class HomeScreenController:
trending_cards_container.container.add_widget(card) trending_cards_container.container.add_widget(card)
self.view.main_container.add_widget(trending_cards_container) self.view.main_container.add_widget(trending_cards_container)
else: else:
Logger.error("Home Screen:Failed to load trending anime")
self.populate_errors.append("trending Anime") self.populate_errors.append("trending Anime")
def highest_scored_anime(self): def highest_scored_anime(self):
@@ -70,10 +77,10 @@ class HomeScreenController:
most_scored_cards_container.container.add_widget(card) most_scored_cards_container.container.add_widget(card)
self.view.main_container.add_widget(most_scored_cards_container) self.view.main_container.add_widget(most_scored_cards_container)
else: else:
Logger.error("Home Screen:Failed to load highest scored anime")
self.populate_errors.append("Most scored Anime") self.populate_errors.append("Most scored Anime")
def recently_updated_anime(self): def recently_updated_anime(self):
most_recently_updated_cards_container = MediaCardsContainer() most_recently_updated_cards_container = MediaCardsContainer()
most_recently_updated_cards_container.list_name = "Most Recently Updated" most_recently_updated_cards_container.list_name = "Most Recently Updated"
most_recently_updated_cards_generator = self.model.get_most_recently_updated_anime() most_recently_updated_cards_generator = self.model.get_most_recently_updated_anime()
@@ -83,6 +90,7 @@ class HomeScreenController:
most_recently_updated_cards_container.container.add_widget(card) most_recently_updated_cards_container.container.add_widget(card)
self.view.main_container.add_widget(most_recently_updated_cards_container) self.view.main_container.add_widget(most_recently_updated_cards_container)
else: else:
Logger.error("Home Screen:Failed to load recently updated anime")
self.populate_errors.append("Most recently updated Anime") self.populate_errors.append("Most recently updated Anime")
def upcoming_anime(self): def upcoming_anime(self):
@@ -95,6 +103,7 @@ class HomeScreenController:
upcoming_cards_container.container.add_widget(card) upcoming_cards_container.container.add_widget(card)
self.view.main_container.add_widget(upcoming_cards_container) self.view.main_container.add_widget(upcoming_cards_container)
else: else:
Logger.error("Home Screen:Failed to load upcoming anime")
self.populate_errors.append("upcoming Anime") self.populate_errors.append("upcoming Anime")
def populate_home_screen(self): def populate_home_screen(self):
@@ -109,7 +118,6 @@ class HomeScreenController:
if self.populate_errors: if self.populate_errors:
show_notification(f"Failed to fetch all home screen data",f"Theres probably a problem with your internet connection or anilist servers are down.\nFailed include:{', '.join(self.populate_errors)}") show_notification(f"Failed to fetch all home screen data",f"Theres probably a problem with your internet connection or anilist servers are down.\nFailed include:{', '.join(self.populate_errors)}")
def update_my_list(self,*args): def update_my_list(self,*args):
self.model.update_user_anime_list(*args) self.model.update_user_anime_list(*args)

View File

@@ -1,9 +1,12 @@
from inspect import isgenerator from inspect import isgenerator
from kivy.logger import Logger
from kivy.clock import Clock
from kivy.utils import difference
from View import MyListScreenView from View import MyListScreenView
from Model import MyListScreenModel from Model import MyListScreenModel
from View.components.media_card.media_card import MediaCardsContainer from Utility import show_notification,user_data_helper
from Utility import show_notification
class MyListScreenController: class MyListScreenController:
""" """
@@ -14,8 +17,21 @@ class MyListScreenController:
""" """
def __init__(self, model:MyListScreenModel): def __init__(self, model:MyListScreenModel):
self.model = model # Model.main_screen.MyListScreenModel self.model = model
self.view = MyListScreenView(controller=self, model=self.model) self.view = MyListScreenView(controller=self, model=self.model)
# self.populate_home_screen() self.requested_update_my_list_screen()
def get_view(self) -> MyListScreenView: def get_view(self) -> MyListScreenView:
return self.view return self.view
def requested_update_my_list_screen(self):
user_anime_list = user_data_helper.get_user_anime_list()
if animes_to_add:=difference(user_anime_list,self.model.already_in_user_anime_list):
Logger.info("My List Screen:User anime list change;updating screen")
anime_cards = self.model.update_my_anime_list_view(animes_to_add)
self.model.already_in_user_anime_list = user_anime_list
if isgenerator(anime_cards):
for result_card in anime_cards:
result_card.screen = self.view
self.view.update_layout(result_card)

View File

@@ -1,17 +1,21 @@
from inspect import isgenerator from inspect import isgenerator
from Utility import show_notification
from kivy.clock import Clock
from kivy.logger import Logger
from View import SearchScreenView from View import SearchScreenView
from Model import SearchScreenModel from Model import SearchScreenModel
from kivy.clock import Clock from Utility import show_notification
class SearchScreenController: class SearchScreenController:
def __init__(self, model:SearchScreenModel): def __init__(self, model:SearchScreenModel):
self.model = model # Model.main_screen.MainScreenModel self.model = model # Model.main_screen.MainScreenModel
self.view = SearchScreenView(controller=self, model=self.model) self.view = SearchScreenView(controller=self, model=self.model)
def get_view(self) -> SearchScreenView: def get_view(self) -> SearchScreenView:
return self.view return self.view
def update_trending_anime(self): def update_trending_anime(self):
trending_cards_generator = self.model.get_trending_anime() trending_cards_generator = self.model.get_trending_anime()
if isgenerator(trending_cards_generator): if isgenerator(trending_cards_generator):
@@ -21,6 +25,7 @@ class SearchScreenController:
card.pos_hint = {'center_x': 0.5} card.pos_hint = {'center_x': 0.5}
self.view.update_trending_sidebar(card) self.view.update_trending_sidebar(card)
else: else:
Logger.error("Home Screen:Failed to load trending anime")
self.populate_errors.append("trending Anime") self.populate_errors.append("trending Anime")
def requested_search_for_anime(self,anime_title,**kwargs): def requested_search_for_anime(self,anime_title,**kwargs):
@@ -33,5 +38,6 @@ class SearchScreenController:
Clock.schedule_once(lambda _:self.view.update_pagination(self.model.pagination_info)) Clock.schedule_once(lambda _:self.view.update_pagination(self.model.pagination_info))
Clock.schedule_once(lambda _:self.update_trending_anime()) Clock.schedule_once(lambda _:self.update_trending_anime())
else: else:
Logger.error(f"Home Screen:Failed to search for {anime_title}")
show_notification("Failed to search",f"{search_Results.get('Error')}") show_notification("Failed to search",f"{search_Results.get('Error')}")
self.view.is_searching = False self.view.is_searching = False

View File

@@ -4,4 +4,17 @@ from Model.base_model import BaseScreenModel
class DownloadsScreenModel(BaseScreenModel): class DownloadsScreenModel(BaseScreenModel):
""" """
Handles the download screen logic Handles the download screen logic
""" """
# already_in_user_anime_list = []
# def update_my_anime_list_view(self,not_yet_in_user_anime_list:list,**kwargs):
# success,self.data = AniList.search(id_in=not_yet_in_user_anime_list)
# if success:
# return self.media_card_generator()
# else:
# show_notification(f"Failed to update my list screen view",self.data["Error"])
# return None
# def media_card_generator(self):
# for anime_item in self.data["data"]["Page"]["media"]:
# yield MediaCardLoader.media_card(anime_item)

View File

@@ -1,25 +1,18 @@
import os
from Model.base_model import BaseScreenModel
from Utility import show_notification
from libs.anilist import AniList from libs.anilist import AniList
from Utility.media_card_loader import MediaCardLoader from Model.base_model import BaseScreenModel
from kivy.storage.jsonstore import JsonStore from Utility import MediaCardLoader,show_notification
user_data= JsonStore("user_data.json")
class MyListScreenModel(BaseScreenModel): class MyListScreenModel(BaseScreenModel):
data = {} already_in_user_anime_list = []
def search_for_anime(self,anime_title,**kwargs): def update_my_anime_list_view(self,not_yet_in_user_anime_list:list,**kwargs):
success,self.data = AniList.search(query=anime_title,**kwargs) success,self.data = AniList.search(id_in=not_yet_in_user_anime_list)
if success: if success:
return self.media_card_generator() return self.media_card_generator()
else: else:
show_notification(f"Failed to search for {anime_title}",self.data["Error"]) show_notification(f"Failed to update my list screen view",self.data["Error"])
return None
def media_card_generator(self): def media_card_generator(self):
for anime_item in self.data["data"]["Page"]["media"]: for anime_item in self.data["data"]["Page"]["media"]:
yield MediaCardLoader.media_card(anime_item) yield MediaCardLoader.media_card(anime_item)
self.pagination_info = self.extract_pagination_info()
def extract_pagination_info(self):
pagination_info = None
return pagination_info

View File

@@ -1,18 +1,19 @@
import os from typing import Generator
from Model.base_model import BaseScreenModel from Model.base_model import BaseScreenModel
from Utility import show_notification
from libs.anilist import AniList from libs.anilist import AniList
from Utility.media_card_loader import MediaCardLoader from Utility.media_card_loader import MediaCardLoader
from kivy.storage.jsonstore import JsonStore from View.components import MediaCard
from Utility import show_notification
user_data= JsonStore("user_data.json")
class SearchScreenModel(BaseScreenModel): class SearchScreenModel(BaseScreenModel):
data = {} data = {}
def get_trending_anime(self): def get_trending_anime(self)->MediaCard|dict:
success,data = AniList.get_trending() success,data = AniList.get_trending()
if success: if success:
def _data_generator(): def _data_generator()->Generator[MediaCard,MediaCard,MediaCard]:
for anime_item in data["data"]["Page"]["media"]: for anime_item in data["data"]["Page"]["media"]:
yield MediaCardLoader.media_card(anime_item) yield MediaCardLoader.media_card(anime_item)
return _data_generator() return _data_generator()

View File

@@ -14,13 +14,13 @@ from Utility import anilist_data_helper, user_data_helper
# Register anime cache in memory # Register anime cache in memory
Cache.register("anime") Cache.register("yt_stream_links.anime")
user_anime_list = user_data_helper.get_user_animelist() user_anime_list = user_data_helper.get_user_anime_list()
yt_stream_links = user_data_helper.get_anime_trailer_cache() yt_stream_links = user_data_helper.get_anime_trailer_cache()
for link in yt_stream_links: for link in yt_stream_links:
Cache.append("anime",link[0],tuple(link[1])) Cache.append("yt_stream_links.anime",link[0],tuple(link[1]))
# for youtube video links gotten from from pytube which is blocking # for youtube video links gotten from from pytube which is blocking
@@ -72,7 +72,7 @@ class MediaCardDataLoader(object):
self._resume_cond.release() self._resume_cond.release()
def cached_fetch_data(self,yt_watch_url): def cached_fetch_data(self,yt_watch_url):
data:tuple = Cache.get("anime",yt_watch_url) # type: ignore # trailer_url is the yt_watch_link data:tuple = Cache.get("yt_stream_links.anime",yt_watch_url) # type: ignore # trailer_url is the yt_watch_link
if not data[0]: if not data[0]:
yt = YouTube(yt_watch_url) yt = YouTube(yt_watch_url)
@@ -94,10 +94,9 @@ class MediaCardDataLoader(object):
yt_watch_link = kwargs['yt_watch_link'] yt_watch_link = kwargs['yt_watch_link']
try: try:
data = self.cached_fetch_data(yt_watch_link) data = self.cached_fetch_data(yt_watch_link)
print("Update: ",data)
except Exception as e: except Exception as e:
data = None data = None
print(f"Not accesible:{e} ") Logger.error("Pytube:{e}")
self._q_done.appendleft((yt_watch_link, data)) self._q_done.appendleft((yt_watch_link, data))
self._trigger_update() self._trigger_update()
@@ -118,20 +117,18 @@ class MediaCardDataLoader(object):
yt_watch_link, data= self._q_done.pop() yt_watch_link, data= self._q_done.pop()
except IndexError: except IndexError:
return return
# update client # update client
for c_yt_watch_link, client in self._client[:]: for c_yt_watch_link, client in self._client[:]:
if yt_watch_link != c_yt_watch_link: if yt_watch_link != c_yt_watch_link:
continue continue
# got one client to update # got one client to update
if data: if data:
# client.set_preview_image(data[0])
trailer_url = data[1] trailer_url = data[1]
print(trailer_url,"-----------------")
if trailer_url: if trailer_url:
client.set_trailer_url(trailer_url) client.set_trailer_url(trailer_url)
print(client.title,self._running) Logger.info(f"Pytube:Found trailer url for {client.title}")
Cache.append("anime",yt_watch_link,data) Cache.append("yt_stream_links.anime",yt_watch_link,data)
self._client.remove((c_yt_watch_link, client)) self._client.remove((c_yt_watch_link, client))
self._trigger_update() self._trigger_update()
@@ -140,10 +137,10 @@ class MediaCardDataLoader(object):
**kwargs): **kwargs):
media_card = MediaCard() media_card = MediaCard()
media_card.anime_id = anime_item["id"] media_card.anime_id = anime_id = anime_item["id"]
# TODO: ADD language preference # TODO: ADD language preference
if anime_item["title"]["english"]: if anime_item["title"].get("english"):
media_card.title = anime_item["title"]["english"] media_card.title = anime_item["title"]["english"]
else: else:
media_card.title = anime_item["title"]["romaji"] media_card.title = anime_item["title"]["romaji"]
@@ -175,7 +172,7 @@ class MediaCardDataLoader(object):
if anime_item.get("genres"): if anime_item.get("genres"):
media_card.genres = anilist_data_helper.format_list_data_with_comma(anime_item["genres"]) media_card.genres = anilist_data_helper.format_list_data_with_comma(anime_item["genres"])
if anime_item["id"] in user_anime_list: if anime_id in user_anime_list:
media_card.is_in_my_list = True media_card.is_in_my_list = True
if anime_item["averageScore"]: if anime_item["averageScore"]:
@@ -188,7 +185,7 @@ class MediaCardDataLoader(object):
# Setting up trailer info to be gotten if available # Setting up trailer info to be gotten if available
if anime_item["trailer"]: if anime_item["trailer"]:
yt_watch_link = "https://youtube.com/watch?v="+anime_item["trailer"]["id"] yt_watch_link = "https://youtube.com/watch?v="+anime_item["trailer"]["id"]
data = Cache.get("anime",yt_watch_link) # type: ignore # trailer_url is the yt_watch_link data = Cache.get("yt_stream_links.anime",yt_watch_link) # type: ignore # trailer_url is the yt_watch_link
if data: if data:
if data[1] not in (None,False): if data[1] not in (None,False):
media_card.set_preview_image(data[0]) media_card.set_preview_image(data[0])
@@ -204,7 +201,7 @@ class MediaCardDataLoader(object):
'current_anime':anime_item["id"], 'current_anime':anime_item["id"],
'kwargs': kwargs}) 'kwargs': kwargs})
if not kwargs.get('nocache', False): if not kwargs.get('nocache', False):
Cache.append('anime',yt_watch_link, (False,False)) Cache.append('yt_stream_links.anime',yt_watch_link, (False,False))
self._start_wanted = True self._start_wanted = True
self._trigger_update() self._trigger_update()
return media_card return media_card
@@ -235,4 +232,4 @@ class LoaderThreadPool(MediaCardDataLoader):
MediaCardLoader = LoaderThreadPool() MediaCardLoader = LoaderThreadPool()
Logger.info('MediaCardLoader: using a thread pool of {} workers'.format( Logger.info('MediaCardLoader: using a thread pool of {} workers'.format(
MediaCardLoader.num_workers)) MediaCardLoader._num_workers))

View File

@@ -5,7 +5,7 @@ Contains Helper functions to read and write the user data files
from kivy.storage.jsonstore import JsonStore from kivy.storage.jsonstore import JsonStore
from datetime import date,datetime from datetime import date,datetime
from kivy.logger import Logger
today = date.today() today = date.today()
now = datetime.now() now = datetime.now()
@@ -15,28 +15,56 @@ yt_cache = JsonStore("yt_cache.json")
# Get the user data # Get the user data
def get_user_animelist(): def get_user_anime_list()->list:
try: try:
return user_data.get("my_list")["list"] # returns a list of anime ids return user_data.get("user_anime_list")["user_anime_list"] # returns a list of anime ids
except: except Exception as e:
Logger.warning(f"User Data:Read failure:{e}")
return [] return []
def update_user_anime_list(new_list:list): def update_user_anime_list(updated_list:list):
try: try:
user_data.put("my_list",list=set(new_list)) updated_list_ = list(set(updated_list))
except: user_data.put("user_anime_list",user_anime_list=updated_list_)
pass except Exception as e:
Logger.warning(f"User Data:Update failure:{e}")
def get_anime_trailer_cache():
# Get the user data
def get_user_downloads()->list:
try: try:
name_of_yt_cache = f"{today}{0 if now.hour>=12 else 1}" return user_data.get("user_downloads")["user_downloads"] # returns a list of anime ids
return yt_cache["yt_stream_links"][f"{name_of_yt_cache}"] except Exception as e:
except: Logger.warning(f"User Data:Read failure:{e}")
return []
def update_user_downloads(updated_list:list):
try:
user_data.put("user_downloads",user_downloads=list(set(updated_list)))
except Exception as e:
Logger.warning(f"User Data:Update failure:{e}")
# Yt persistent anime trailer cache
t = 1
if now.hour<=6:
t = 1
elif now.hour<=12:
t = 2
elif now.hour<=18:
t = 3
else:
t = 4
yt_anime_trailer_cache_name = f"{today}{t}"
def get_anime_trailer_cache()->list:
try:
return yt_cache["yt_stream_links"][f"{yt_anime_trailer_cache_name}"]
except Exception as e:
Logger.warning(f"User Data:Read failure:{e}")
return [] return []
def update_anime_trailer_cache(yt_stream_links:list): def update_anime_trailer_cache(yt_stream_links:list):
try: try:
name_of_yt_cache = f"{today}{0 if now.hour>=12 else 1}" yt_cache.put("yt_stream_links",**{f"{yt_anime_trailer_cache_name}":yt_stream_links})
yt_cache.put("yt_stream_links",**{f"{name_of_yt_cache}":yt_stream_links}) except Exception as e:
except: Logger.warning(f"User Data:Update failure:{e}")
pass

View File

@@ -17,7 +17,7 @@
MDIconButton: MDIconButton:
icon: "arrow-left" icon: "arrow-left"
on_release: on_release:
root.manager_screens.current = root.manager_screens.previous() root.manager_screens.current = root.caller_screen_name
MDScrollView: MDScrollView:
size_hint:1,1 size_hint:1,1
MDBoxLayout: MDBoxLayout:
@@ -57,4 +57,4 @@
id:anime_characters id:anime_characters
AnimeReviews: AnimeReviews:
id:anime_reviews id:anime_reviews
MDBoxLayout: BoxLayout:

View File

@@ -1,6 +1,6 @@
from datetime import datetime from datetime import datetime
from kivy.properties import ObjectProperty,DictProperty from kivy.properties import ObjectProperty,DictProperty,StringProperty
from Utility import anilist_data_helper from Utility import anilist_data_helper
from View.base_screen import BaseScreenView from View.base_screen import BaseScreenView
@@ -8,6 +8,7 @@ from .components import (AnimeHeader,AnimeSideBar,AnimeDescription,AnimeReviews,
class AnimeScreenView(BaseScreenView): class AnimeScreenView(BaseScreenView):
caller_screen_name = StringProperty()
header:AnimeHeader = ObjectProperty() header:AnimeHeader = ObjectProperty()
side_bar:AnimeSideBar = ObjectProperty() side_bar:AnimeSideBar = ObjectProperty()
rankings_bar:RankingsBar = ObjectProperty() rankings_bar:RankingsBar = ObjectProperty()
@@ -15,8 +16,9 @@ class AnimeScreenView(BaseScreenView):
anime_characters:AnimeCharacters = ObjectProperty() anime_characters:AnimeCharacters = ObjectProperty()
anime_reviews:AnimeReviews = ObjectProperty() anime_reviews:AnimeReviews = ObjectProperty()
data = DictProperty() data = DictProperty()
anime_id = 0
def update_layout(self,data): def update_layout(self,data:dict,caller_screen_name:str):
self.caller_screen_name = caller_screen_name
self.data = data self.data = data
# uitlity functions # uitlity functions
@@ -114,3 +116,5 @@ class AnimeScreenView(BaseScreenView):
DownloadAnimeDialog(self.data).open() DownloadAnimeDialog(self.data).open()
def add_to_user_anime_list(self,*args):
self.app.add_anime_to_user_anime_list(self.model.anime_id)

View File

@@ -4,9 +4,12 @@
spacing:"10dp" spacing:"10dp"
pos_hint: {'center_x': 0.5} pos_hint: {'center_x': 0.5}
MDButton: MDButton:
on_press: print("presed") on_press:
root.screen.add_to_user_anime_list()
add_to_user_list_label.text = "Added to MyAnimeList"
MDButtonText: MDButtonText:
text:"Add to MyList" id:add_to_user_list_label
text:"Add to MyAnimeList"
MDButton: MDButton:
on_press: on_press:
if root.screen:root.screen.stream_anime_with_custom_cmds_dialog() if root.screen:root.screen.stream_anime_with_custom_cmds_dialog()

View File

@@ -6,6 +6,7 @@ class DownloadAnimeDialog(ThemableBehavior,StencilBehavior,CommonElevationBehavi
def __init__(self,data,**kwargs): def __init__(self,data,**kwargs):
super(DownloadAnimeDialog,self).__init__(**kwargs) super(DownloadAnimeDialog,self).__init__(**kwargs)
self.data = data self.data = data
self.anime_id = self.data["id"]
if title:=data["title"].get("romaji"): if title:=data["title"].get("romaji"):
self.ids.title_field.text = title self.ids.title_field.text = title
elif title:=data["title"].get("english"): elif title:=data["title"].get("english"):
@@ -22,4 +23,4 @@ class DownloadAnimeDialog(ThemableBehavior,StencilBehavior,CommonElevationBehavi
default_cmds["quality"] = quality default_cmds["quality"] = quality
# print(title,episodes_range,latest,quality) # print(title,episodes_range,latest,quality)
app.download_anime(default_cmds) app.download_anime(self.anime_id,default_cmds)

View File

@@ -23,13 +23,11 @@
max_lines:0 max_lines:0
shorten:False shorten:False
bold:True bold:True
markup:True
font_style: "Label" font_style: "Label"
role: "large" role: "large"
md_bg_color:self.theme_cls.secondaryContainerColor md_bg_color:self.theme_cls.secondaryContainerColor
padding:"10dp" padding:"10dp"
<AnimeSideBar>: <AnimeSideBar>:
size_hint_x: None size_hint_x: None
width: dp(300) width: dp(300)
@@ -52,7 +50,6 @@
MDButtonText: MDButtonText:
text:"Watch on Animdl" text:"Watch on Animdl"
FitBoxLayout: FitBoxLayout:
SideBarHeaderLabel: SideBarHeaderLabel:
text:"Alternative Titles" text:"Alternative Titles"
@@ -62,7 +59,6 @@
text: "[color={}]English:[/color] {}".format(get_hex_from_color(self.theme_cls.primaryColor),root.alternative_titles["english"]) text: "[color={}]English:[/color] {}".format(get_hex_from_color(self.theme_cls.primaryColor),root.alternative_titles["english"])
SideBarLabel: SideBarLabel:
text: "[color={}]Japanese:[/color] {}".format(get_hex_from_color(self.theme_cls.primaryColor),root.alternative_titles["japanese"]) text: "[color={}]Japanese:[/color] {}".format(get_hex_from_color(self.theme_cls.primaryColor),root.alternative_titles["japanese"])
FitBoxLayout: FitBoxLayout:
SideBarHeaderLabel: SideBarHeaderLabel:
text:"Information" text:"Information"
@@ -104,5 +100,4 @@
id:external_links_container id:external_links_container
SideBarHeaderLabel: SideBarHeaderLabel:
text:"External Links" text:"External Links"
BoxLayout: BoxLayout:

View File

@@ -11,7 +11,7 @@
bold:True bold:True
<DownloadsScreenView> <DownloadsScreenView>
md_bg_color: self.theme_cls.backgroundColor md_bg_color: self.theme_cls.backgroundColor
# main_container:main_container main_container:main_container
download_progress_label:download_progress_label download_progress_label:download_progress_label
progress_bar:progress_bar progress_bar:progress_bar
MDBoxLayout: MDBoxLayout:
@@ -23,13 +23,13 @@
orientation: 'vertical' orientation: 'vertical'
SearchBar: SearchBar:
MDScrollView: MDScrollView:
size_hint:1,1 size_hint:.95,1
MDGridLayout: MDGridLayout:
padding: "75dp","50dp","10dp","100dp"
spacing:"40dp"
id:main_container id:main_container
cols:5 cols:5
adaptive_height:True adaptive_height:True
MDLabel:
text:"Dowloads"
MDBoxLayout: MDBoxLayout:
size_hint_y:None size_hint_y:None
height:"40dp" height:"40dp"

View File

@@ -1,3 +1,4 @@
from kivy.clock import Clock
from kivy.properties import ObjectProperty from kivy.properties import ObjectProperty
from kivy.utils import format_bytes_to_human from kivy.utils import format_bytes_to_human
@@ -12,3 +13,9 @@ class DownloadsScreenView(BaseScreenView):
percentage_completion = (current_bytes_downloaded/total_bytes)*100 percentage_completion = (current_bytes_downloaded/total_bytes)*100
self.progress_bar.value= max(min(percentage_completion,100),0) self.progress_bar.value= max(min(percentage_completion,100),0)
self.download_progress_label.text = f"Downloading: {episode_info['anime_title']} - {episode_info['episode']} ({format_bytes_to_human(current_bytes_downloaded)}/{format_bytes_to_human(total_bytes)})" self.download_progress_label.text = f"Downloading: {episode_info['anime_title']} - {episode_info['episode']} ({format_bytes_to_human(current_bytes_downloaded)}/{format_bytes_to_human(total_bytes)})"
# def on_enter(self):
# Clock.schedule_once(lambda _:self.controller.requested_update_my_list_screen())
def update_layout(self,widget):
self.user_anime_list_container.add_widget(widget)

View File

@@ -20,10 +20,4 @@
orientation: 'vertical' orientation: 'vertical'
size_hint_y:None size_hint_y:None
height:max(self.minimum_height,p.height,1800) height:max(self.minimum_height,p.height,1800)
MDBoxLayout:
size_hint_y:None
height:self.minimum_height
MDLabel:
text: "By BeneX"

View File

@@ -1,6 +1,6 @@
<MyListScreenView> <MyListScreenView>
md_bg_color: self.theme_cls.backgroundColor md_bg_color: self.theme_cls.backgroundColor
my_list_container:my_list_container user_anime_list_container:user_anime_list_container
MDBoxLayout: MDBoxLayout:
size_hint:1,1 size_hint:1,1
NavRail: NavRail:
@@ -9,18 +9,18 @@
anchor_y: 'top' anchor_y: 'top'
padding:"10dp" padding:"10dp"
size_hint:1,1 size_hint:1,1
MDBoxLayout: MDBoxLayout:
spacing:"40dp"
orientation: 'vertical' orientation: 'vertical'
id:p size_hint:.95,1
size_hint:1,1
SearchBar: SearchBar:
MDScrollView: MDScrollView:
pos_hint:{"center_x":.5}
size_hint:1,1 size_hint:1,1
MDGridLayout: MDGridLayout:
spacing: '10dp' spacing: '40dp'
padding: "75dp","50dp","10dp","100dp" padding: "100dp","50dp","10dp","200dp"
id:my_list_container id:user_anime_list_container
cols:5 cols:5
size_hint_y:None size_hint_y:None
height:self.minimum_height height:self.minimum_height

View File

@@ -1,9 +1,10 @@
from kivy.properties import ObjectProperty,StringProperty,DictProperty from kivy.properties import ObjectProperty,StringProperty,DictProperty
from kivy.clock import Clock
from View.base_screen import BaseScreenView from View.base_screen import BaseScreenView
class MyListScreenView(BaseScreenView): class MyListScreenView(BaseScreenView):
my_list_container = ObjectProperty() user_anime_list_container = ObjectProperty()
def model_is_changed(self) -> None: def model_is_changed(self) -> None:
""" """
Called whenever any change has occurred in the data model. Called whenever any change has occurred in the data model.
@@ -11,8 +12,9 @@ class MyListScreenView(BaseScreenView):
according to these changes. according to these changes.
""" """
def on_enter(self):
Clock.schedule_once(lambda _:self.controller.requested_update_my_list_screen())
def update_layout(self,widget): def update_layout(self,widget):
pass self.user_anime_list_container.add_widget(widget)
def add_pagination(self,pagination_info):
pass

View File

@@ -50,6 +50,7 @@
size_hint_x:None size_hint_x:None
width: dp(250) width: dp(250)
MDLabel: MDLabel:
md_bg_color:self.theme_cls.secondaryContainerColor
adaptive_height:True adaptive_height:True
halign:"center" halign:"center"
max_lines:0 max_lines:0
@@ -59,7 +60,6 @@
font_style: "Label" font_style: "Label"
role: "large" role: "large"
text:"Trending" text:"Trending"
md_bg_color:self.theme_cls.secondaryContainerColor
padding:"10dp" padding:"10dp"
MDScrollView: MDScrollView:
TrendingAnimeSideBar: TrendingAnimeSideBar:

View File

@@ -6,7 +6,3 @@ from .AnimeScreen.anime_screen import AnimeScreenView
from .CrashLogScreen.crashlog_screen import CrashLogScreenView from .CrashLogScreen.crashlog_screen import CrashLogScreenView
from .DownloadsScreen.download_screen import DownloadsScreenView from .DownloadsScreen.download_screen import DownloadsScreenView
from .HelpScreen.help_screen import HelpScreenView from .HelpScreen.help_screen import HelpScreenView
# others
from .components.animdl_dialog.animdl_dialog import AnimdlDialogPopup
from .DownloadsScreen.download_screen import DownloadAnimePopup

View File

@@ -104,7 +104,7 @@
icon: "play-circle" icon: "play-circle"
on_press: on_press:
root.dismiss() root.dismiss()
app.show_anime_screen(root.caller.anime_id) app.show_anime_screen(root.caller.anime_id,root.caller.screen.name)
TooltipMDIconButton: TooltipMDIconButton:
tooltip_text:"Add to your anime list" tooltip_text:"Add to your anime list"
icon: "plus-circle" if not(root.caller.is_in_my_list) else "check-circle" icon: "plus-circle" if not(root.caller.is_in_my_list) else "check-circle"

View File

@@ -8,6 +8,7 @@ from kivymd.uix.behaviors import HoverBehavior
from .components import MediaPopup from .components import MediaPopup
class MediaCard(ButtonBehavior,HoverBehavior,MDBoxLayout): class MediaCard(ButtonBehavior,HoverBehavior,MDBoxLayout):
screen = ObjectProperty()
anime_id = NumericProperty() anime_id = NumericProperty()
title = StringProperty() title = StringProperty()
is_play = ObjectProperty() is_play = ObjectProperty()
@@ -28,7 +29,6 @@ class MediaCard(ButtonBehavior,HoverBehavior,MDBoxLayout):
stars = ListProperty([0,0,0,0,0,0]) stars = ListProperty([0,0,0,0,0,0])
cover_image_url = StringProperty() cover_image_url = StringProperty()
preview_image = StringProperty() preview_image = StringProperty()
screen = ObjectProperty()
has_trailer_color = ListProperty([1,1,1,0]) has_trailer_color = ListProperty([1,1,1,0])
@@ -75,10 +75,12 @@ class MediaCard(ButtonBehavior,HoverBehavior,MDBoxLayout):
popup.open(self) popup.open(self)
# ---------------respond to user actions and call appropriate model------------------------- # ---------------respond to user actions and call appropriate model-------------------------
def on_is_in_my_list(self,instance,value): def on_is_in_my_list(self,instance,in_user_anime_list):
if self.screen: if self.screen:
self.screen.controller.update_my_list(self.anime_id,value) if in_user_anime_list:
self.screen.app.add_anime_to_user_anime_list(self.anime_id)
else:
self.screen.app.remove_anime_from_user_anime_list(self.anime_id)
def on_trailer_url(self,*args): def on_trailer_url(self,*args):
pass pass

View File

@@ -38,6 +38,7 @@ class AniList:
query:str|None=None, query:str|None=None,
sort:list[str]|None=None, sort:list[str]|None=None,
genre_in:list[str]|None=None, genre_in:list[str]|None=None,
id_in:list[int]|None=None,
genre_not_in:list[str]|None=None, genre_not_in:list[str]|None=None,
popularity_greater:int|None=None, popularity_greater:int|None=None,
popularity_lesser:int|None=None, popularity_lesser:int|None=None,

View File

@@ -1,6 +1,7 @@
optional_variables = "\ optional_variables = "\
$page:Int,\ $page:Int,\
$sort:[MediaSort],\ $sort:[MediaSort],\
$id_in:[Int],\
$genre_in:[String],\ $genre_in:[String],\
$genre_not_in:[String],\ $genre_not_in:[String],\
$tag_in:[String],\ $tag_in:[String],\
@@ -29,6 +30,7 @@ query($query:String,%s){
} }
media( media(
search:$query, search:$query,
id_in:$id_in,
genre_in:$genre_in, genre_in:$genre_in,
genre_not_in:$genre_not_in, genre_not_in:$genre_not_in,
tag_in:$tag_in, tag_in:$tag_in,

View File

@@ -9,6 +9,9 @@ from fuzzywuzzy import fuzz
broken_link_pattern = r"https://tools.fast4speed.rsvp/\w*" broken_link_pattern = r"https://tools.fast4speed.rsvp/\w*"
def path_parser(path:str)->str:
return path.replace(":","").replace("/", "").replace("\\","").replace("\"","").replace("'","").replace("<","").replace(">","").replace("|","").replace("?","").replace(".","").replace("*","")
# TODO: WRITE Docs for each method # TODO: WRITE Docs for each method
class AnimdlApi: class AnimdlApi:
@classmethod @classmethod
@@ -52,13 +55,15 @@ class AnimdlApi:
failed_downloads = [] failed_downloads = []
successful_downloads = [] successful_downloads = []
anime_title,episodes_to_download = data anime_title,episodes_to_download = data
anime_title = anime_title.capitalize() anime_title:str = anime_title.capitalize()
if not episodes_to_download: if not episodes_to_download:
return False,None return False,None
# determine download location # determine download location
parsed_anime_title = anime_title.replace(":","").replace("/", "").replace("\\","")
parsed_anime_title = path_parser(anime_title)
download_location = os.path.join(output_path,parsed_anime_title) download_location = os.path.join(output_path,parsed_anime_title)
if not os.path.exists(download_location): if not os.path.exists(download_location):
os.mkdir(download_location) os.mkdir(download_location)
@@ -99,7 +104,8 @@ class AnimdlApi:
# determine episode_title # determine episode_title
if title:=stream.get("title"): if title:=stream.get("title"):
episode_title = f"{episode_title} - {title}" episode_title = f"{episode_title} - {path_parser(title)}"
parsed_episode_title = episode_title.replace(":","").replace("/", "").replace("\\","") parsed_episode_title = episode_title.replace(":","").replace("/", "").replace("\\","")
episode_download_location = os.path.join(download_location,parsed_episode_title) episode_download_location = os.path.join(download_location,parsed_episode_title)
if not os.path.exists(episode_download_location): if not os.path.exists(episode_download_location):

View File

@@ -14,6 +14,7 @@ from kivy.config import Config
# Config.write() # Config.write()
from kivy.clock import Clock from kivy.clock import Clock
from kivy.logger import Logger
from kivy.uix.screenmanager import ScreenManager,FadeTransition from kivy.uix.screenmanager import ScreenManager,FadeTransition
from kivy.uix.settings import SettingsWithSidebar,Settings from kivy.uix.settings import SettingsWithSidebar,Settings
@@ -24,10 +25,9 @@ from View.screens import screens
from libs.animdl.animdl_api import AnimdlApi from libs.animdl.animdl_api import AnimdlApi
from Utility import themes_available,show_notification,user_data_helper from Utility import themes_available,show_notification,user_data_helper
# TODO: ADD logging across the codebase
# Ensure the user data fields exist # Ensure the user data fields exist
if not(user_data_helper.user_data.exists("my_list")): if not(user_data_helper.user_data.exists("user_anime_list")):
user_data_helper.update_user_anime_list([]) user_data_helper.update_user_anime_list([])
if not(user_data_helper.yt_cache.exists("yt_stream_links")): if not(user_data_helper.yt_cache.exists("yt_stream_links")):
@@ -65,27 +65,32 @@ class AniXStreamApp(MDApp):
self.worker_thread = Thread(target=self.worker,args=(self.queue,)) self.worker_thread = Thread(target=self.worker,args=(self.queue,))
self.worker_thread.daemon = True self.worker_thread.daemon = True
self.worker_thread.start() self.worker_thread.start()
Logger.info("AniXStream:Successfully started background tasks worker")
# initialize downloads worker # initialize downloads worker
self.downloads_worker_thread = Thread(target=self.downloads_worker,args=(self.downloads_queue,)) self.downloads_worker_thread = Thread(target=self.downloads_worker,args=(self.downloads_queue,))
self.downloads_worker_thread.daemon = True self.downloads_worker_thread.daemon = True
self.downloads_worker_thread.start() self.downloads_worker_thread.start()
Logger.info("AniXStream:Successfully started download worker")
def build(self) -> ScreenManager: def build(self) -> ScreenManager:
self.settings_cls = SettingsWithSidebar self.settings_cls = SettingsWithSidebar
self.generate_application_screens() self.generate_application_screens()
if config:=self.config: if config:=self.config:
if theme_color:=config.get("Preferences","theme_color"): if theme_color:=config.get("Preferences","theme_color"):
self.theme_cls.primary_palette = theme_color self.theme_cls.primary_palette = theme_color
if theme_style:=config.get("Preferences","theme_style"): if theme_style:=config.get("Preferences","theme_style"):
self.theme_cls.theme_style = theme_style self.theme_cls.theme_style = theme_style
self.anime_screen = self.manager_screens.get_screen("anime screen") self.anime_screen = self.manager_screens.get_screen("anime screen")
self.search_screen = self.manager_screens.get_screen("search screen") self.search_screen = self.manager_screens.get_screen("search screen")
self.download_screen = self.manager_screens.get_screen("downloads screen") self.download_screen = self.manager_screens.get_screen("downloads screen")
return self.manager_screens return self.manager_screens
def on_start(self,*args): def on_start(self,*args):
super().on_start(*args) pass
def generate_application_screens(self) -> None: def generate_application_screens(self) -> None:
for i, name_screen in enumerate(screens.keys()): for i, name_screen in enumerate(screens.keys()):
@@ -103,7 +108,6 @@ class AniXStreamApp(MDApp):
"downloads_dir": plyer.storagepath.get_videos_dir() if plyer.storagepath.get_videos_dir() else ".", "downloads_dir": plyer.storagepath.get_videos_dir() if plyer.storagepath.get_videos_dir() else ".",
"is_startup_anime_enable":False "is_startup_anime_enable":False
}) })
print(self.config.get("Preferences","is_startup_anime_enable"))
def build_settings(self,settings:Settings): def build_settings(self,settings:Settings):
settings.add_json_panel("Settings",self.config,"settings.json") settings.add_json_panel("Settings",self.config,"settings.json")
@@ -115,6 +119,7 @@ class AniXStreamApp(MDApp):
if value in themes_available: if value in themes_available:
self.theme_cls.primary_palette = value self.theme_cls.primary_palette = value
else: else:
Logger.warning("Settings:An invalid theme has been entered and will be ignored")
config.set("Preferences","theme_color","Cyan") config.set("Preferences","theme_color","Cyan")
config.write() config.write()
case "theme_style": case "theme_style":
@@ -123,7 +128,8 @@ class AniXStreamApp(MDApp):
def on_stop(self): def on_stop(self):
if self.animdl_streaming_subprocess: if self.animdl_streaming_subprocess:
self.animdl_streaming_subprocess.terminate() self.animdl_streaming_subprocess.terminate()
Logger.info("Animdl:Successfully terminated existing animdl subprocess")
# custom methods # custom methods
# TODO: may move theme to a personalized class # TODO: may move theme to a personalized class
def search_for_anime(self,search_field,**kwargs): def search_for_anime(self,search_field,**kwargs):
@@ -131,9 +137,24 @@ class AniXStreamApp(MDApp):
self.manager_screens.current = "search screen" self.manager_screens.current = "search screen"
self.search_screen.handle_search_for_anime(search_field,**kwargs) self.search_screen.handle_search_for_anime(search_field,**kwargs)
def show_anime_screen(self,id): def add_anime_to_user_anime_list(self,id:int):
updated_list = user_data_helper.get_user_anime_list()
updated_list.append(id)
user_data_helper.update_user_anime_list(updated_list)
def remove_anime_from_user_anime_list(self,id:int):
updated_list = user_data_helper.get_user_anime_list()
if updated_list.count(id): updated_list.remove(id)
user_data_helper.update_user_anime_list(updated_list)
def add_anime_to_user_downloads_list(self,id:int):
updated_list = user_data_helper.get_user_downloads()
updated_list.append(id)
user_data_helper.get_user_downloads(updated_list)
def show_anime_screen(self,id:int,caller_screen_name:str):
self.manager_screens.current = "anime screen" self.manager_screens.current = "anime screen"
self.anime_screen.controller.update_anime_view(id) self.anime_screen.controller.update_anime_view(id,caller_screen_name)
def watch_on_allanime(self,title_): def watch_on_allanime(self,title_):
""" """
@@ -146,24 +167,31 @@ class AniXStreamApp(MDApp):
title,link = anime title,link = anime
parsed_link = f"https://allmanga.to/bangumi/{link.split('/')[-1]}" parsed_link = f"https://allmanga.to/bangumi/{link.split('/')[-1]}"
else: else:
Logger.error(f"AniXStream:Failed to open {title} in browser on allanime site")
show_notification("Failure",f"Failed to open {title} in browser on allanime site") show_notification("Failure",f"Failed to open {title} in browser on allanime site")
if webbrowser.open(parsed_link): if webbrowser.open(parsed_link):
Logger.info(f"AniXStream:Successfully opened {title} in browser allanime site")
show_notification("Success",f"Successfully opened {title} in browser allanime site") show_notification("Success",f"Successfully opened {title} in browser allanime site")
else: else:
Logger.error(f"AniXStream:Failed to open {title} in browser on allanime site")
show_notification("Failure",f"Failed to open {title} in browser on allanime site") show_notification("Failure",f"Failed to open {title} in browser on allanime site")
def download_anime_complete(self,successful_downloads:list,failed_downloads:list,anime_title:str): def download_anime_complete(self,successful_downloads:list,failed_downloads:list,anime_title:str):
show_notification(f"Finished Dowloading {anime_title}",f"There were {len(successful_downloads)} successful downloads and {len(failed_downloads)} failed downloads") show_notification(f"Finished Dowloading {anime_title}",f"There were {len(successful_downloads)} successful downloads and {len(failed_downloads)} failed downloads")
Logger.info(f"Downloader:Finished Downloading {anime_title} and there were {len(failed_downloads)} failed downloads")
def download_anime(self,default_cmds): def download_anime(self,anime_id:int,default_cmds:dict):
show_notification("New Download Task Queued",f"{default_cmds.get('title')} has been queued for downloading") show_notification("New Download Task Queued",f"{default_cmds.get('title')} has been queued for downloading")
self.add_anime_to_user_downloads_list(anime_id)
# TODO:Add custom download cmds functionality # TODO:Add custom download cmds functionality
on_progress = lambda *args:self.download_screen.on_episode_download_progress(*args) on_progress = lambda *args:self.download_screen.on_episode_download_progress(*args)
output_path = self.config.get("Preferences","downloads_dir") output_path = self.config.get("Preferences","downloads_dir")
if episodes_range:=default_cmds.get("episodes_range"): if episodes_range:=default_cmds.get("episodes_range"):
download_task =lambda: AnimdlApi.download_anime_by_title(default_cmds["title"],on_progress,self.download_anime_complete,output_path,episodes_range) # ,default_cmds["quality"] download_task =lambda: AnimdlApi.download_anime_by_title(default_cmds['title'],on_progress,self.download_anime_complete,output_path,episodes_range) # ,default_cmds["quality"]
self.downloads_queue.put(download_task) self.downloads_queue.put(download_task)
Logger.info(f"Downloader:Successfully Queued {default_cmds['title']} for downloading")
else: else:
download_task =lambda: AnimdlApi.download_anime_by_title(default_cmds["title"],on_progress,self.download_anime_complete,output_path) # ,default_cmds.get("quality") download_task =lambda: AnimdlApi.download_anime_by_title(default_cmds["title"],on_progress,self.download_anime_complete,output_path) # ,default_cmds.get("quality")
self.downloads_queue.put(download_task) self.downloads_queue.put(download_task)
@@ -199,7 +227,7 @@ class AniXStreamApp(MDApp):
else: else:
stream_func = lambda:self.stream_anime_with_custom_input_cmds(*custom_options) stream_func = lambda:self.stream_anime_with_custom_input_cmds(*custom_options)
self.queue.put(stream_func) self.queue.put(stream_func)
Logger.info(f"Animdl:Successfully started to stream {title}")
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -1 +1 @@
{"my_list": {"list": [1535, 20605, 21519, 21, 5114, 133007, 166710, 116674, 11061, 21745, 9253, 153406, 166613, 133845, 143103, 20996, 162804, 125367, 21827]}} {"user_anime_list": {"user_anime_list": [116674, 21640, 17641, 117612, 269, 6702, 20657, 20626, 8247, 107226, 19163, 15583]}}