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.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:
def __init__(self, model:AnimeScreenModel):
self.model = model
@@ -13,12 +15,19 @@ class AnimeScreenController:
def get_view(self) -> AnimeScreenView:
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 cached_anime_data:=Cache.get("data.anime",f"{id}"):
data = cached_anime_data
else:
data = self.model.get_anime_data(id)
if data[0]:
d = data[1]["data"]["Page"]["media"][0]
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):
self.model.update_user_anime_list(*args)

View File

@@ -13,3 +13,16 @@ class DownloadsScreenController:
def get_view(self) -> DownloadsScreenView:
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 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.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
class HomeScreenController:
@@ -34,6 +38,7 @@ class HomeScreenController:
most_popular_cards_container.container.add_widget(card)
self.view.main_container.add_widget(most_popular_cards_container)
else:
Logger.error("Home Screen:Failed to load most popular anime")
self.populate_errors.append("Most Popular Anime")
def favourite_anime(self):
@@ -46,6 +51,7 @@ class HomeScreenController:
most_favourite_cards_container.container.add_widget(card)
self.view.main_container.add_widget(most_favourite_cards_container)
else:
Logger.error("Home Screen:Failed to load most favourite anime")
self.populate_errors.append("Most favourite Anime")
def trending_anime(self):
@@ -58,6 +64,7 @@ class HomeScreenController:
trending_cards_container.container.add_widget(card)
self.view.main_container.add_widget(trending_cards_container)
else:
Logger.error("Home Screen:Failed to load trending anime")
self.populate_errors.append("trending Anime")
def highest_scored_anime(self):
@@ -70,10 +77,10 @@ class HomeScreenController:
most_scored_cards_container.container.add_widget(card)
self.view.main_container.add_widget(most_scored_cards_container)
else:
Logger.error("Home Screen:Failed to load highest scored anime")
self.populate_errors.append("Most scored Anime")
def recently_updated_anime(self):
most_recently_updated_cards_container = MediaCardsContainer()
most_recently_updated_cards_container.list_name = "Most Recently Updated"
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)
self.view.main_container.add_widget(most_recently_updated_cards_container)
else:
Logger.error("Home Screen:Failed to load recently updated anime")
self.populate_errors.append("Most recently updated Anime")
def upcoming_anime(self):
@@ -95,6 +103,7 @@ class HomeScreenController:
upcoming_cards_container.container.add_widget(card)
self.view.main_container.add_widget(upcoming_cards_container)
else:
Logger.error("Home Screen:Failed to load upcoming anime")
self.populate_errors.append("upcoming Anime")
def populate_home_screen(self):
@@ -109,7 +118,6 @@ class HomeScreenController:
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)}")
def update_my_list(self,*args):
self.model.update_user_anime_list(*args)

View File

@@ -1,9 +1,12 @@
from inspect import isgenerator
from kivy.logger import Logger
from kivy.clock import Clock
from kivy.utils import difference
from View import MyListScreenView
from Model import MyListScreenModel
from View.components.media_card.media_card import MediaCardsContainer
from Utility import show_notification
from Utility import show_notification,user_data_helper
class MyListScreenController:
"""
@@ -14,8 +17,21 @@ class MyListScreenController:
"""
def __init__(self, model:MyListScreenModel):
self.model = model # Model.main_screen.MyListScreenModel
self.model = model
self.view = MyListScreenView(controller=self, model=self.model)
# self.populate_home_screen()
self.requested_update_my_list_screen()
def get_view(self) -> MyListScreenView:
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 Utility import show_notification
from kivy.clock import Clock
from kivy.logger import Logger
from View import SearchScreenView
from Model import SearchScreenModel
from kivy.clock import Clock
from Utility import show_notification
class SearchScreenController:
def __init__(self, model:SearchScreenModel):
self.model = model # Model.main_screen.MainScreenModel
self.view = SearchScreenView(controller=self, model=self.model)
def get_view(self) -> SearchScreenView:
return self.view
def update_trending_anime(self):
trending_cards_generator = self.model.get_trending_anime()
if isgenerator(trending_cards_generator):
@@ -21,6 +25,7 @@ class SearchScreenController:
card.pos_hint = {'center_x': 0.5}
self.view.update_trending_sidebar(card)
else:
Logger.error("Home Screen:Failed to load trending anime")
self.populate_errors.append("trending Anime")
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.update_trending_anime())
else:
Logger.error(f"Home Screen:Failed to search for {anime_title}")
show_notification("Failed to search",f"{search_Results.get('Error')}")
self.view.is_searching = False

View File

@@ -5,3 +5,16 @@ class DownloadsScreenModel(BaseScreenModel):
"""
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 Utility.media_card_loader import MediaCardLoader
from kivy.storage.jsonstore import JsonStore
from Model.base_model import BaseScreenModel
from Utility import MediaCardLoader,show_notification
user_data= JsonStore("user_data.json")
class MyListScreenModel(BaseScreenModel):
data = {}
def search_for_anime(self,anime_title,**kwargs):
success,self.data = AniList.search(query=anime_title,**kwargs)
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 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):
for anime_item in self.data["data"]["Page"]["media"]:
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 Utility import show_notification
from libs.anilist import AniList
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):
data = {}
def get_trending_anime(self):
def get_trending_anime(self)->MediaCard|dict:
success,data = AniList.get_trending()
if success:
def _data_generator():
def _data_generator()->Generator[MediaCard,MediaCard,MediaCard]:
for anime_item in data["data"]["Page"]["media"]:
yield MediaCardLoader.media_card(anime_item)
return _data_generator()

View File

@@ -14,13 +14,13 @@ from Utility import anilist_data_helper, user_data_helper
# 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()
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
@@ -72,7 +72,7 @@ class MediaCardDataLoader(object):
self._resume_cond.release()
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]:
yt = YouTube(yt_watch_url)
@@ -94,10 +94,9 @@ class MediaCardDataLoader(object):
yt_watch_link = kwargs['yt_watch_link']
try:
data = self.cached_fetch_data(yt_watch_link)
print("Update: ",data)
except Exception as e:
data = None
print(f"Not accesible:{e} ")
Logger.error("Pytube:{e}")
self._q_done.appendleft((yt_watch_link, data))
self._trigger_update()
@@ -125,13 +124,11 @@ class MediaCardDataLoader(object):
# got one client to update
if data:
# client.set_preview_image(data[0])
trailer_url = data[1]
print(trailer_url,"-----------------")
if trailer_url:
client.set_trailer_url(trailer_url)
print(client.title,self._running)
Cache.append("anime",yt_watch_link,data)
Logger.info(f"Pytube:Found trailer url for {client.title}")
Cache.append("yt_stream_links.anime",yt_watch_link,data)
self._client.remove((c_yt_watch_link, client))
self._trigger_update()
@@ -140,10 +137,10 @@ class MediaCardDataLoader(object):
**kwargs):
media_card = MediaCard()
media_card.anime_id = anime_item["id"]
media_card.anime_id = anime_id = anime_item["id"]
# TODO: ADD language preference
if anime_item["title"]["english"]:
if anime_item["title"].get("english"):
media_card.title = anime_item["title"]["english"]
else:
media_card.title = anime_item["title"]["romaji"]
@@ -175,7 +172,7 @@ class MediaCardDataLoader(object):
if anime_item.get("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
if anime_item["averageScore"]:
@@ -188,7 +185,7 @@ class MediaCardDataLoader(object):
# Setting up trailer info to be gotten if available
if anime_item["trailer"]:
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[1] not in (None,False):
media_card.set_preview_image(data[0])
@@ -204,7 +201,7 @@ class MediaCardDataLoader(object):
'current_anime':anime_item["id"],
'kwargs': kwargs})
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._trigger_update()
return media_card
@@ -235,4 +232,4 @@ class LoaderThreadPool(MediaCardDataLoader):
MediaCardLoader = LoaderThreadPool()
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 datetime import date,datetime
from kivy.logger import Logger
today = date.today()
now = datetime.now()
@@ -15,28 +15,56 @@ yt_cache = JsonStore("yt_cache.json")
# Get the user data
def get_user_animelist():
def get_user_anime_list()->list:
try:
return user_data.get("my_list")["list"] # returns a list of anime ids
except:
return user_data.get("user_anime_list")["user_anime_list"] # returns a list of anime ids
except Exception as e:
Logger.warning(f"User Data:Read failure:{e}")
return []
def update_user_anime_list(new_list:list):
def update_user_anime_list(updated_list:list):
try:
user_data.put("my_list",list=set(new_list))
except:
pass
updated_list_ = list(set(updated_list))
user_data.put("user_anime_list",user_anime_list=updated_list_)
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:
name_of_yt_cache = f"{today}{0 if now.hour>=12 else 1}"
return yt_cache["yt_stream_links"][f"{name_of_yt_cache}"]
except:
return user_data.get("user_downloads")["user_downloads"] # returns a list of anime ids
except Exception as e:
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 []
def update_anime_trailer_cache(yt_stream_links:list):
try:
name_of_yt_cache = f"{today}{0 if now.hour>=12 else 1}"
yt_cache.put("yt_stream_links",**{f"{name_of_yt_cache}":yt_stream_links})
except:
pass
yt_cache.put("yt_stream_links",**{f"{yt_anime_trailer_cache_name}":yt_stream_links})
except Exception as e:
Logger.warning(f"User Data:Update failure:{e}")

View File

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

View File

@@ -1,6 +1,6 @@
from datetime import datetime
from kivy.properties import ObjectProperty,DictProperty
from kivy.properties import ObjectProperty,DictProperty,StringProperty
from Utility import anilist_data_helper
from View.base_screen import BaseScreenView
@@ -8,6 +8,7 @@ from .components import (AnimeHeader,AnimeSideBar,AnimeDescription,AnimeReviews,
class AnimeScreenView(BaseScreenView):
caller_screen_name = StringProperty()
header:AnimeHeader = ObjectProperty()
side_bar:AnimeSideBar = ObjectProperty()
rankings_bar:RankingsBar = ObjectProperty()
@@ -15,8 +16,9 @@ class AnimeScreenView(BaseScreenView):
anime_characters:AnimeCharacters = ObjectProperty()
anime_reviews:AnimeReviews = ObjectProperty()
data = DictProperty()
def update_layout(self,data):
anime_id = 0
def update_layout(self,data:dict,caller_screen_name:str):
self.caller_screen_name = caller_screen_name
self.data = data
# uitlity functions
@@ -114,3 +116,5 @@ class AnimeScreenView(BaseScreenView):
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"
pos_hint: {'center_x': 0.5}
MDButton:
on_press: print("presed")
on_press:
root.screen.add_to_user_anime_list()
add_to_user_list_label.text = "Added to MyAnimeList"
MDButtonText:
text:"Add to MyList"
id:add_to_user_list_label
text:"Add to MyAnimeList"
MDButton:
on_press:
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):
super(DownloadAnimeDialog,self).__init__(**kwargs)
self.data = data
self.anime_id = self.data["id"]
if title:=data["title"].get("romaji"):
self.ids.title_field.text = title
elif title:=data["title"].get("english"):
@@ -22,4 +23,4 @@ class DownloadAnimeDialog(ThemableBehavior,StencilBehavior,CommonElevationBehavi
default_cmds["quality"] = 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
shorten:False
bold:True
markup:True
font_style: "Label"
role: "large"
md_bg_color:self.theme_cls.secondaryContainerColor
padding:"10dp"
<AnimeSideBar>:
size_hint_x: None
width: dp(300)
@@ -52,7 +50,6 @@
MDButtonText:
text:"Watch on Animdl"
FitBoxLayout:
SideBarHeaderLabel:
text:"Alternative Titles"
@@ -62,7 +59,6 @@
text: "[color={}]English:[/color] {}".format(get_hex_from_color(self.theme_cls.primaryColor),root.alternative_titles["english"])
SideBarLabel:
text: "[color={}]Japanese:[/color] {}".format(get_hex_from_color(self.theme_cls.primaryColor),root.alternative_titles["japanese"])
FitBoxLayout:
SideBarHeaderLabel:
text:"Information"
@@ -105,4 +101,3 @@
SideBarHeaderLabel:
text:"External Links"
BoxLayout:

View File

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

View File

@@ -1,3 +1,4 @@
from kivy.clock import Clock
from kivy.properties import ObjectProperty
from kivy.utils import format_bytes_to_human
@@ -12,3 +13,9 @@ class DownloadsScreenView(BaseScreenView):
percentage_completion = (current_bytes_downloaded/total_bytes)*100
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)})"
# 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

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

View File

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

View File

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

View File

@@ -6,7 +6,3 @@ from .AnimeScreen.anime_screen import AnimeScreenView
from .CrashLogScreen.crashlog_screen import CrashLogScreenView
from .DownloadsScreen.download_screen import DownloadsScreenView
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"
on_press:
root.dismiss()
app.show_anime_screen(root.caller.anime_id)
app.show_anime_screen(root.caller.anime_id,root.caller.screen.name)
TooltipMDIconButton:
tooltip_text:"Add to your anime list"
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
class MediaCard(ButtonBehavior,HoverBehavior,MDBoxLayout):
screen = ObjectProperty()
anime_id = NumericProperty()
title = StringProperty()
is_play = ObjectProperty()
@@ -28,7 +29,6 @@ class MediaCard(ButtonBehavior,HoverBehavior,MDBoxLayout):
stars = ListProperty([0,0,0,0,0,0])
cover_image_url = StringProperty()
preview_image = StringProperty()
screen = ObjectProperty()
has_trailer_color = ListProperty([1,1,1,0])
@@ -75,10 +75,12 @@ class MediaCard(ButtonBehavior,HoverBehavior,MDBoxLayout):
popup.open(self)
# ---------------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:
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):
pass

View File

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

View File

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

View File

@@ -9,6 +9,9 @@ from fuzzywuzzy import fuzz
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
class AnimdlApi:
@classmethod
@@ -52,13 +55,15 @@ class AnimdlApi:
failed_downloads = []
successful_downloads = []
anime_title,episodes_to_download = data
anime_title = anime_title.capitalize()
anime_title:str = anime_title.capitalize()
if not episodes_to_download:
return False,None
# 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)
if not os.path.exists(download_location):
os.mkdir(download_location)
@@ -99,7 +104,8 @@ class AnimdlApi:
# determine episode_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("\\","")
episode_download_location = os.path.join(download_location,parsed_episode_title)
if not os.path.exists(episode_download_location):

View File

@@ -14,6 +14,7 @@ from kivy.config import Config
# Config.write()
from kivy.clock import Clock
from kivy.logger import Logger
from kivy.uix.screenmanager import ScreenManager,FadeTransition
from kivy.uix.settings import SettingsWithSidebar,Settings
@@ -24,10 +25,9 @@ from View.screens import screens
from libs.animdl.animdl_api import AnimdlApi
from Utility import themes_available,show_notification,user_data_helper
# TODO: ADD logging across the codebase
# 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([])
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.daemon = True
self.worker_thread.start()
Logger.info("AniXStream:Successfully started background tasks worker")
# initialize downloads worker
self.downloads_worker_thread = Thread(target=self.downloads_worker,args=(self.downloads_queue,))
self.downloads_worker_thread.daemon = True
self.downloads_worker_thread.start()
Logger.info("AniXStream:Successfully started download worker")
def build(self) -> ScreenManager:
self.settings_cls = SettingsWithSidebar
self.generate_application_screens()
if config:=self.config:
if theme_color:=config.get("Preferences","theme_color"):
self.theme_cls.primary_palette = theme_color
if theme_style:=config.get("Preferences","theme_style"):
self.theme_cls.theme_style = theme_style
self.anime_screen = self.manager_screens.get_screen("anime screen")
self.search_screen = self.manager_screens.get_screen("search screen")
self.download_screen = self.manager_screens.get_screen("downloads screen")
return self.manager_screens
def on_start(self,*args):
super().on_start(*args)
pass
def generate_application_screens(self) -> None:
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 ".",
"is_startup_anime_enable":False
})
print(self.config.get("Preferences","is_startup_anime_enable"))
def build_settings(self,settings:Settings):
settings.add_json_panel("Settings",self.config,"settings.json")
@@ -115,6 +119,7 @@ class AniXStreamApp(MDApp):
if value in themes_available:
self.theme_cls.primary_palette = value
else:
Logger.warning("Settings:An invalid theme has been entered and will be ignored")
config.set("Preferences","theme_color","Cyan")
config.write()
case "theme_style":
@@ -123,6 +128,7 @@ class AniXStreamApp(MDApp):
def on_stop(self):
if self.animdl_streaming_subprocess:
self.animdl_streaming_subprocess.terminate()
Logger.info("Animdl:Successfully terminated existing animdl subprocess")
# custom methods
# TODO: may move theme to a personalized class
@@ -131,9 +137,24 @@ class AniXStreamApp(MDApp):
self.manager_screens.current = "search screen"
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.anime_screen.controller.update_anime_view(id)
self.anime_screen.controller.update_anime_view(id,caller_screen_name)
def watch_on_allanime(self,title_):
"""
@@ -146,24 +167,31 @@ class AniXStreamApp(MDApp):
title,link = anime
parsed_link = f"https://allmanga.to/bangumi/{link.split('/')[-1]}"
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")
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")
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")
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")
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")
self.add_anime_to_user_downloads_list(anime_id)
# TODO:Add custom download cmds functionality
on_progress = lambda *args:self.download_screen.on_episode_download_progress(*args)
output_path = self.config.get("Preferences","downloads_dir")
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)
Logger.info(f"Downloader:Successfully Queued {default_cmds['title']} for downloading")
else:
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)
@@ -199,7 +227,7 @@ class AniXStreamApp(MDApp):
else:
stream_func = lambda:self.stream_anime_with_custom_input_cmds(*custom_options)
self.queue.put(stream_func)
Logger.info(f"Animdl:Successfully started to stream {title}")
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]}}