feat(anime screen, search screen): finish basic ui for anime screen with ep selection and update search screen to use recycle view

This commit is contained in:
Benex254
2024-08-05 09:46:57 +03:00
parent 91041304ff
commit 1e0e139e47
18 changed files with 236 additions and 111 deletions

1
.gitignore vendored
View File

@@ -4,6 +4,7 @@
*.ass
vids
data/
.project/
crashdump.txt
# Byte-compiled / optimized / DLL files
__pycache__/

View File

@@ -1,14 +1,44 @@
# Changes
# # only load trailers when needed
-# # only load trailers when needed
- change how media card loader class works
- create function to get a trailer url and cache it for 360 secs
- refactor the codebase to reflect proposed changes
# # # change how media card loader class works
-# # # change how media card loader class works
- remove unnecesarry code
- make the media card function to still work so it doesn't cause a break in existing functionality- add a get trailer function which takes a callback
# # Use one media popup for all the media cards
# - # Use one media popup for all the media cards
# 9 june
# # search screen
change the search results layout to be a recycle grid layout
change the trending bar to be a recyle box layout
AFFECTED:
- search results
- trendig bar
# # anime screen
# # # Features
- video player
- controls
- next button + previous button
- auto next
- refresh button
- play in mpv button
- episodes bar
- servers bar
NOTE:
the affected:
anime screen model, view, controller

View File

@@ -21,6 +21,9 @@ class AnimeScreenController:
anime_title
)
self.view.current_links = self.model.get_episode_streams(episode)
# TODO: add auto start
#
# self.view.current_link = self.view.current_links[0]["gogoanime"][0]
def update_anime_view(self, id, title, caller_screen_name):
self.fetch_streams(title)

View File

@@ -119,9 +119,9 @@ class HomeScreenController:
self.populate_errors = []
Clock.schedule_once(lambda _: self.trending_anime(), 1)
Clock.schedule_once(lambda _: self.highest_scored_anime(), 2)
Clock.schedule_once(lambda _: self.popular_anime(), 3)
Clock.schedule_once(lambda _: self.recently_updated_anime(), 5)
# Clock.schedule_once(lambda _: self.popular_anime(), 3)
# Clock.schedule_once(lambda _: self.favourite_anime(), 4)
# Clock.schedule_once(lambda _: self.recently_updated_anime(), 5)
# Clock.schedule_once(lambda _: self.upcoming_anime(), 6)
if self.populate_errors:

View File

@@ -21,10 +21,10 @@ class SearchScreenController:
"""Gets and adds the trending anime to the search screen"""
trending_cards_generator = self.model.get_trending_anime()
if isgenerator(trending_cards_generator):
self.view.trending_anime_sidebar.clear_widgets()
# self.view.trending_anime_sidebar.data = []
for card in trending_cards_generator:
card.screen = self.view
card.pos_hint = {"center_x": 0.5}
card["screen"] = self.view
# card["pos_hint"] = {"center_x": 0.5}
self.view.update_trending_sidebar(card)
else:
Logger.error("Home Screen:Failed to load trending anime")
@@ -34,7 +34,7 @@ class SearchScreenController:
search_Results = self.model.search_for_anime(anime_title, **kwargs)
if isgenerator(search_Results):
for result_card in search_Results:
result_card.screen = self.view
result_card["screen"] = self.view
self.view.update_layout(result_card)
Clock.schedule_once(
lambda _: self.view.update_pagination(self.model.pagination_info)

View File

@@ -6,7 +6,7 @@ from fuzzywuzzy import fuzz
def anime_title_percentage_match(
possible_user_requested_anime_title: str, title: str
possible_user_requested_anime_title: str, title: tuple
) -> int:
"""Returns the percentage match between the possible title and user title
@@ -19,7 +19,10 @@ def anime_title_percentage_match(
"""
print(locals())
percentage_ratio = fuzz.ratio(title, possible_user_requested_anime_title)
percentage_ratio = max(
fuzz.ratio(title[0].lower(), possible_user_requested_anime_title.lower()),
fuzz.ratio(title[1].lower(), possible_user_requested_anime_title.lower()),
)
print(percentage_ratio)
return percentage_ratio
@@ -36,11 +39,11 @@ class AnimeScreenModel(BaseScreenModel):
current_anime_id = "0"
current_title = ""
def get_anime_data_from_provider(self, anime_title: str, id=None):
def get_anime_data_from_provider(self, anime_title: tuple, id=None):
if self.current_title == anime_title and self.current_anime_data:
return self.current_anime_data
search_results = anime_provider.search_for_anime(anime_title)
search_results = anime_provider.search_for_anime(anime_title[0])
if search_results:
_search_results = search_results["shows"]["edges"]

View File

@@ -16,13 +16,22 @@ class MediaCardDataLoader(object):
anime_item: AnilistBaseMediaDataSchema,
):
media_card_data = {}
media_card_data["viewclass"] = "MediaCard"
media_card_data["anime_id"] = anime_id = anime_item["id"]
# TODO: ADD language preference
if anime_item["title"].get("romaji"):
media_card_data["title"] = anime_item["title"]["romaji"]
media_card_data["_title"] = (
anime_item["title"]["romaji"],
str(anime_item["title"]["english"]),
)
else:
media_card_data["title"] = anime_item["title"]["english"]
media_card_data["_title"] = (
anime_item["title"]["english"],
str(anime_item["title"]["romaji"]),
)
media_card_data["cover_image_url"] = anime_item["coverImage"]["medium"]

View File

@@ -6,49 +6,78 @@
adaptive_height:True
bold:True
<EpisodeButton>:
pos_hint:{"center_y":0.5,"center_x":0.5}
on_press:root.change_episode_callback(root.text)
radius: 10
MDButtonText:
text:root.text
<AnimeScreenView>:
md_bg_color: self.theme_cls.backgroundColor
episodes_container:episodes_container
MDBoxLayout:
padding:"10dp"
orientation: 'vertical'
MDBoxLayout:
adaptive_height:True
MDIconButton:
icon:"arrow-left"
on_press:root.manager_screens.current = root.caller_screen_name
MDTextField:
on_text_validate:
root.update_current_link(self)
VideoPlayer:
source:root.current_link
MDBoxLayout:
VideoPlayer:
source:root.current_link
AnimeBoxLayout:
AnimeLabel:
text:"Sub"
MDSegmentedButton:
id:pl
multiselect:False
MDSegmentedButtonItem:
on_active:
pl.selected_segments = [self]
root.update_current_video_stream("gogoanime")
MDSegmentButtonLabel:
text:"GoGoAnime"
MDSegmentedButtonItem:
on_active:
root.update_current_video_stream("dropbox")
pl.selected_segments = [self]
MDSegmentButtonLabel:
text:"DropBox"
MDSegmentedButtonItem:
on_active:
root.update_current_video_stream("sharepoint")
pl.selected_segments = [self]
MDSegmentButtonLabel:
text:"Share Point"
MDSegmentedButtonItem:
on_active:
root.update_current_video_stream("wetransfer")
pl.selected_segments = [self]
MDSegmentButtonLabel:
text:"weTransfer"
padding: "20dp"
radius:5
spacing:"10dp"
md_bg_color: self.theme_cls.surfaceContainerLowColor
AnimeBoxLayout:
AnimeLabel:
text:"Sub servers: "
MDSegmentedButton:
id:pl
multiselect:False
MDSegmentedButtonItem:
on_active:
pl.selected_segments = [self]
root.update_current_video_stream("gogoanime")
MDSegmentButtonLabel:
text:"GoGoAnime"
MDSegmentedButtonItem:
on_active:
root.update_current_video_stream("dropbox")
pl.selected_segments = [self]
MDSegmentButtonLabel:
text:"DropBox"
MDSegmentedButtonItem:
on_active:
root.update_current_video_stream("sharepoint")
pl.selected_segments = [self]
MDSegmentButtonLabel:
text:"Share Point"
MDSegmentedButtonItem:
on_active:
root.update_current_video_stream("wetransfer")
pl.selected_segments = [self]
MDSegmentButtonLabel:
text:"weTransfer"
MDDivider:
MDRecycleView:
id: episodes_container
size_hint_y:None
height:"50dp"
key_viewclass:"viewclass"
RecycleBoxLayout:
size_hint: None,1
key_size:"width"
spacing:"10dp"
width:self.minimum_width
default_size_hint:0,0
default_size:30,30
default_pos_hint:{"center_y":0.5,"center_x":0.5}

View File

@@ -1,33 +1,60 @@
from kivy.properties import ListProperty, ObjectProperty, StringProperty
from kivy.uix.widget import Factory
from kivymd.uix.button import MDButton
from ...libs.anilist import AnilistBaseMediaDataSchema
from ...View.base_screen import BaseScreenView
class EpisodeButton(MDButton):
text = StringProperty()
change_episode_callback = ObjectProperty()
Factory.register("EpisodeButton", cls=EpisodeButton)
class AnimeScreenView(BaseScreenView):
"""The anime screen view"""
current_link = StringProperty(
"https://uc951f724c20bbec8df447bac605.dl.dropboxusercontent.com/cd/0/get/CUdx6k2qw-zqY86ftfFHqkmPqGuVrfjpE68B_EkcvZXcZLnjim_ZTHd-qNVb_mEbos9UsuhY8FJGdgf86RUZ-IJqZtz3tt8_CUVTloQAeZ47HtNiKjQ0ESvYdLuwqDjqwK2rNfsfiZI2cXBaKiUyJtljEeRL8whSff2wA9Z4tX1cow/file"
)
current_link = StringProperty()
current_links = ListProperty([])
current_anime_data = ObjectProperty()
caller_screen_name = ObjectProperty()
current_title = StringProperty()
current_title = ()
episodes_container = ObjectProperty()
def update_layout(self, data: AnilistBaseMediaDataSchema, caller_screen_name: str):
return
def __init__(self, **kwargs):
super().__init__(**kwargs)
# self.update_episodes(100)
def update_episodes(self, episodes_list):
self.episodes_container.data = []
for episode in episodes_list:
self.episodes_container.data.append(
{
"viewclass": "EpisodeButton",
"text": str(episode),
"change_episode_callback": lambda x=episode: self.update_current_episode(
x
),
}
)
def on_current_anime_data(self, instance, value):
data = value["show"]
self.update_episodes(data["availableEpisodesDetail"]["sub"][::-1])
def update_current_episode(self, episode):
self.controller.fetch_streams(self.current_title, episode)
# self.current_link = self.current_links[0]["gogoanime"][0]
def update_current_video_stream(self, server, is_dub=False):
for link in self.current_links:
if stream_link := link.get(server):
print(link)
self.current_link = stream_link[0]
break
# print(link)
def update_current_link(self, field):
self.controller.fetch_streams(self.current_title, field.text)
def add_to_user_anime_list(self, *args):
self.app.add_anime_to_user_anime_list(self.model.anime_id)

View File

@@ -1,6 +1,23 @@
<TrendingAnimeSideBar>:
orientation: 'vertical'
adaptive_height:True
md_bg_color:self.theme_cls.surfaceContainerLowColor
padding:"25dp","25dp","25dp","200dp"
pos_hint: {'center_x': 0.5}
padding:"25dp","25dp","25dp","200dp"
key_viewclass:"viewclass"
size_hint: None,1
width:"250dp"
pos_hint: {'center_x':.5}
RecycleBoxLayout:
orientation: 'vertical'
key_size:"height"
key_viewclass:"viewclass"
pos_hint: {'center_x': 0.8}
size_hint:None,None
default_size_hint:None, None
default_pos_hint:{"center_x":0.8}
default_size:dp(150),dp(100)
width:"250dp"
spacing:"10dp"
height:max(self.minimum_height,500)
#padding:"0dp","10dp","100dp","10dp"
# height:max(self.parent.parent.height,self.minimum_height+100)

View File

@@ -1,5 +1,5 @@
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.recycleview import MDRecycleView
class TrendingAnimeSideBar(MDBoxLayout):
class TrendingAnimeSideBar(MDRecycleView):
pass

View File

@@ -33,17 +33,22 @@
orientation: 'vertical'
size_hint_y:None
height:max(self.parent.parent.height,self.minimum_height)
MDGridLayout:
pos_hint: {'center_x': 0.5}
MDRecycleView:
id:search_results_container
spacing: '40dp'
padding: "25dp","50dp","75dp","200dp"
cols:3 if root.width <= 1100 else 5
size_hint_y:None
height:max(self.parent.parent.height,self.minimum_height)
key_viewclass:"viewclass"
MDRecycleGridLayout:
pos_hint: {'center_x': 0.5}
spacing: '40dp'
padding: "25dp","50dp","75dp","200dp"
default_size_hint:None,None
default_size:dp(100),dp(150)
cols:3 if root.width <= 1100 else 5
size_hint_y:None
height:max(self.parent.parent.height,self.minimum_height)
SearchResultsPagination:
id:search_results_pagination
search_view:root
MDBoxLayout:
orientation:"vertical"
size_hint_y:1
@@ -51,8 +56,5 @@
width: dp(250)
HeaderLabel:
text:"Trending"
MDScrollView:
TrendingAnimeSideBar:
id:trending_anime_sidebar
height:max(self.parent.parent.height,self.minimum_height)
TrendingAnimeSideBar:
id:trending_anime_sidebar

View File

@@ -28,7 +28,7 @@ class SearchScreenView(BaseScreenView):
if search_term and not (self.is_searching):
self.search_term = search_term
self.search_results_container.clear_widgets()
self.search_results_container.data = []
if filters := self.filters.filters:
Clock.schedule_once(
lambda _: self.controller.requested_search_for_anime(
@@ -43,7 +43,7 @@ class SearchScreenView(BaseScreenView):
)
def update_layout(self, widget):
self.search_results_container.add_widget(widget)
self.search_results_container.data.append(widget)
def update_pagination(self, pagination_info):
self.search_results_pagination.current_page = self.current_page = (
@@ -65,4 +65,4 @@ class SearchScreenView(BaseScreenView):
self.handle_search_for_anime(page=page)
def update_trending_sidebar(self, trending_anime):
self.trending_anime_sidebar.add_widget(trending_anime)
self.trending_anime_sidebar.data.append(trending_anime)

View File

@@ -9,15 +9,14 @@
bold:True
adaptive_height:True
text:root.list_name
MDScrollView:
MDRecycleView:
id:container
key_viewclass:"viewclass"
key_size:"width"
RecycleBoxLayout:
size_hint:None,1
width:self.minimum_width
default_size_hint:None, None
default_size:dp(150),dp(100)
spacing:"10dp"
padding:"0dp","10dp","100dp","10dp"
MDRecycleView:
id:container
key_viewclass:"viewclass"
key_size:"width"
RecycleBoxLayout:
size_hint:None,1
width:self.minimum_width
default_size_hint:None, None
default_size:dp(150),dp(100)
spacing:"10dp"
padding:"0dp","10dp","100dp","10dp"

View File

@@ -30,9 +30,11 @@
# TODO: Remove the test source
MediaPopupVideoPlayer:
id:player
source: root.caller.trailer_url if root.caller.trailer_url else 'https://www088.vipanicdn.net/streamhls/abae70787c7bd2fcd4fab986c2a5aeba/ep.7.1703900604.m3u8'
source: root.caller.trailer_url #if root.caller.trailer_url else 'https://www088.vipanicdn.net/streamhls/abae70787c7bd2fcd4fab986c2a5aeba/ep.7.1703900604.m3u8'
thumbnail:app.default_anime_image
state:"play" if root.caller.trailer_url else "stop"
#state:"play" if root.caller.trailer_url else "stop"
on_state:
root.caller._get_trailer()
# fit_mode:"fill"
size_hint_y: None
height: dp(280)
@@ -103,11 +105,12 @@
PopupBoxLayout:
pos_hint: {'center_y': 0.5}
TooltipMDIconButton:
tooltip_text:root.caller.title
tooltip_text:"Play"
icon: "play-circle"
on_press:
root.dismiss()
app.show_anime_screen(root.caller.anime_id,root.caller.title,root.caller.screen.name)
app.show_anime_screen(root.caller.anime_id,root.caller._title,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

@@ -3,6 +3,7 @@
spacing:"5dp"
size_hint_x: None
width:dp(100)
height: dp(150)
FitImage:
source:root.cover_image_url
fit_mode:"fill"

View File

@@ -39,6 +39,7 @@ class MediaCard(HoverBehavior, MDBoxLayout):
preview_image = StringProperty()
has_trailer_color = ListProperty([0.5, 0.5, 0.5, 0.5])
_popup_opened = False
_title = ()
def __init__(self, trailer_url=None, **kwargs):
super().__init__(**kwargs)
@@ -118,19 +119,19 @@ class MediaCard(HoverBehavior, MDBoxLayout):
popup.bind(on_dismiss=self.on_dismiss, on_open=self.on_popup_open)
popup.open(self)
def _get_trailer(dt):
if trailer := self._trailer_url:
# trailer stuff
from ....Utility.media_card_loader import media_card_loader
def _get_trailer(self):
if self.trailer_url:
return
if trailer := self._trailer_url:
# trailer stuff
from ....Utility.media_card_loader import media_card_loader
if trailer_url := media_card_loader.get_trailer_from_pytube(
trailer, self.title
):
self.trailer_url = trailer_url
else:
self._trailer_url = ""
Clock.schedule_once(_get_trailer, 1)
if trailer_url := media_card_loader.get_trailer_from_pytube(
trailer, self.title
):
self.trailer_url = trailer_url
else:
self._trailer_url = ""
# ---------------respond to user actions and call appropriate model-------------------------
def on_is_in_my_list(self, instance, in_user_anime_list):

View File

@@ -26,14 +26,14 @@ screens = {
"model": SearchScreenModel,
"controller": SearchScreenController,
},
"my list screen": {
"model": MyListScreenModel,
"controller": MyListScreenController,
},
"anime screen": {
"model": AnimeScreenModel,
"controller": AnimeScreenController,
},
"my list screen": {
"model": MyListScreenModel,
"controller": MyListScreenController,
},
"crashlog screen": {
"model": CrashLogScreenModel,
"controller": CrashLogScreenController,