mirror of
https://github.com/Benexl/FastAnime.git
synced 2026-01-14 13:56:32 -08:00
renamed app to anixstream
This commit is contained in:
1
anixstream/View/components/__init__.py
Normal file
1
anixstream/View/components/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .media_card import MediaCard,MediaCardsContainer
|
||||
@@ -0,0 +1,4 @@
|
||||
from kivy.uix.modalview import ModalView
|
||||
|
||||
class AnimdlDialogPopup(ModalView):
|
||||
pass
|
||||
3
anixstream/View/components/general.kv
Normal file
3
anixstream/View/components/general.kv
Normal file
@@ -0,0 +1,3 @@
|
||||
<MDLabel>:
|
||||
allow_copy:True
|
||||
allow_selection:True
|
||||
1
anixstream/View/components/media_card/__init__.py
Normal file
1
anixstream/View/components/media_card/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .media_card import MediaCard,MediaCardsContainer
|
||||
@@ -0,0 +1,2 @@
|
||||
from .media_player import MediaPopupVideoPlayer
|
||||
from .media_popup import MediaPopup
|
||||
@@ -0,0 +1,19 @@
|
||||
<MediaCardsContainer>
|
||||
size_hint:1,None
|
||||
height:max(self.minimum_height,dp(350),container.minimum_height)
|
||||
container:container
|
||||
orientation: 'vertical'
|
||||
padding:"10dp"
|
||||
spacing:"5dp"
|
||||
MDLabel:
|
||||
text:root.list_name
|
||||
MDScrollView:
|
||||
size_hint:1,None
|
||||
height:container.minimum_height
|
||||
MDBoxLayout:
|
||||
id:container
|
||||
spacing:"10dp"
|
||||
padding:"0dp","10dp","100dp","10dp"
|
||||
size_hint:None,None
|
||||
height:self.minimum_height
|
||||
width:self.minimum_width
|
||||
@@ -0,0 +1,10 @@
|
||||
from kivy.uix.videoplayer import VideoPlayer
|
||||
|
||||
# TODO: make fullscreen exp better
|
||||
class MediaPopupVideoPlayer(VideoPlayer):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def on_fullscreen(self, instance, value):
|
||||
super().on_fullscreen(instance, value)
|
||||
# self.state = "pause"
|
||||
170
anixstream/View/components/media_card/components/media_popup.kv
Normal file
170
anixstream/View/components/media_card/components/media_popup.kv
Normal file
@@ -0,0 +1,170 @@
|
||||
#:import get_hex_from_color kivy.utils.get_hex_from_color
|
||||
#:set yellow [.9,.9,0,.9]
|
||||
|
||||
<SingleLineLabel@MDLabel>:
|
||||
shorten:True
|
||||
shorten_from:"right"
|
||||
adaptive_height:True
|
||||
|
||||
<PopupBoxLayout@MDBoxLayout>
|
||||
adaptive_height:True
|
||||
<Video>:
|
||||
fit_mode:"fill"
|
||||
|
||||
# TODO: subdivide each main component to its own file
|
||||
<MediaPopup>
|
||||
size_hint: None, None
|
||||
height: dp(530)
|
||||
width: dp(400)
|
||||
radius:[5,5,5,5]
|
||||
md_bg_color:self.theme_cls.backgroundColor
|
||||
anchor_y: 'top'
|
||||
player:player
|
||||
MDBoxLayout:
|
||||
orientation: 'vertical'
|
||||
MDRelativeLayout:
|
||||
size_hint_y: None
|
||||
height: dp(280)
|
||||
line_color:root.caller.has_trailer_color
|
||||
line_width:1
|
||||
MediaPopupVideoPlayer:
|
||||
id:player
|
||||
source: root.caller.trailer_url
|
||||
thumbnail:app.default_anime_image
|
||||
state:"play" if root.caller.trailer_url else "stop"
|
||||
# fit_mode:"fill"
|
||||
size_hint_y: None
|
||||
height: dp(280)
|
||||
PopupBoxLayout:
|
||||
padding: "10dp","5dp"
|
||||
spacing:"5dp"
|
||||
pos_hint: {'left': 1,'top': 1}
|
||||
MDIcon:
|
||||
icon: "star"
|
||||
color:yellow
|
||||
disabled: not(root.caller.stars[0])
|
||||
MDIcon:
|
||||
color:yellow
|
||||
disabled: not(root.caller.stars[1])
|
||||
icon: "star"
|
||||
MDIcon:
|
||||
color:yellow
|
||||
disabled: not(root.caller.stars[2])
|
||||
icon: "star"
|
||||
MDIcon:
|
||||
color:yellow
|
||||
disabled: not(root.caller.stars[3])
|
||||
icon: "star"
|
||||
MDIcon:
|
||||
color:yellow
|
||||
icon: "star"
|
||||
disabled: not(root.caller.stars[4])
|
||||
MDIcon:
|
||||
color: yellow
|
||||
icon: "star"
|
||||
disabled: not(root.caller.stars[5])
|
||||
|
||||
MDLabel:
|
||||
text: f"{root.caller.episodes} Episodes"
|
||||
halign:"right"
|
||||
font_style:"Label"
|
||||
role:"medium"
|
||||
bold:True
|
||||
pos_hint: {'center_y': 0.5}
|
||||
adaptive_height:True
|
||||
color: 0,0,0,.7
|
||||
|
||||
PopupBoxLayout:
|
||||
padding:"5dp"
|
||||
pos_hint: {'bottom': 1}
|
||||
SingleLineLabel:
|
||||
text:root.caller.media_status
|
||||
opacity:.8
|
||||
halign:"left"
|
||||
font_style:"Label"
|
||||
role:"medium"
|
||||
bold:True
|
||||
pos_hint: {'center_y': .5}
|
||||
SingleLineLabel:
|
||||
text:root.caller.first_aired_on
|
||||
opacity:.8
|
||||
halign:"right"
|
||||
font_style:"Label"
|
||||
role:"medium"
|
||||
bold:True
|
||||
pos_hint: {'center_y': .5}
|
||||
# header
|
||||
MDBoxLayout:
|
||||
orientation: 'vertical'
|
||||
padding:"10dp"
|
||||
spacing:"10dp"
|
||||
PopupBoxLayout:
|
||||
PopupBoxLayout:
|
||||
pos_hint: {'center_y': 0.5}
|
||||
TooltipMDIconButton:
|
||||
tooltip_text:root.caller.title
|
||||
icon: "play-circle"
|
||||
on_press:
|
||||
root.dismiss()
|
||||
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"
|
||||
on_release:
|
||||
root.caller.is_in_my_list = not(root.caller.is_in_my_list)
|
||||
self.icon = "plus-circle" if not(root.caller.is_in_my_list) else "check-circle"
|
||||
TooltipMDIconButton:
|
||||
disabled:True
|
||||
tooltip_text:"Coming soon"
|
||||
icon: "bell-circle" if not(root.caller.is_in_my_notify) else "bell-check"
|
||||
PopupBoxLayout:
|
||||
pos_hint: {'center_y': 0.5}
|
||||
orientation: 'vertical'
|
||||
|
||||
SingleLineLabel:
|
||||
font_style:"Label"
|
||||
role:"small"
|
||||
text: f"[color={get_hex_from_color(self.theme_cls.primaryColor)}]"+"Genres: "+"[/color]"+root.caller.genres
|
||||
markup:True
|
||||
PopupBoxLayout:
|
||||
SingleLineLabel:
|
||||
font_style:"Label"
|
||||
role:"small"
|
||||
markup:True
|
||||
text: f"[color={get_hex_from_color(self.theme_cls.primaryColor)}]"+"Popularity: "+"[/color]"+root.caller.popularity
|
||||
SingleLineLabel:
|
||||
font_style:"Label"
|
||||
markup:True
|
||||
role:"small"
|
||||
text: f"[color={get_hex_from_color(self.theme_cls.primaryColor)}]"+"Favourites: "+"[/color]"+root.caller.favourites
|
||||
MDScrollView:
|
||||
size_hint:1,1
|
||||
do_scroll_y:True
|
||||
MDLabel:
|
||||
font_style:"Body"
|
||||
role:"small"
|
||||
text:root.caller.description
|
||||
adaptive_height:True
|
||||
# footer
|
||||
PopupBoxLayout:
|
||||
orientation:"vertical"
|
||||
SingleLineLabel:
|
||||
font_style:"Label"
|
||||
markup:True
|
||||
role:"small"
|
||||
text: f"[color={get_hex_from_color(self.theme_cls.primaryColor)}]"+"Next Airing Episode: "+"[/color]"+root.caller.next_airing_episode
|
||||
SingleLineLabel:
|
||||
font_style:"Label"
|
||||
role:"small"
|
||||
markup:True
|
||||
text: f"[color={get_hex_from_color(self.theme_cls.primaryColor)}]"+"Studios: " + "[/color]"+root.caller.studios
|
||||
SingleLineLabel:
|
||||
font_style:"Label"
|
||||
markup:True
|
||||
role:"small"
|
||||
text: f"[color={get_hex_from_color(self.theme_cls.primaryColor)}]"+"Producers: " + "[/color]"+root.caller.producers
|
||||
SingleLineLabel:
|
||||
font_style:"Label"
|
||||
markup:True
|
||||
role:"small"
|
||||
text: f"[color={get_hex_from_color(self.theme_cls.primaryColor)}]"+"Tags: "+"[/color]"+root.caller.tags
|
||||
@@ -0,0 +1,87 @@
|
||||
from kivy.properties import ObjectProperty
|
||||
from kivy.clock import Clock
|
||||
from kivy.animation import Animation
|
||||
from kivy.uix.modalview import ModalView
|
||||
|
||||
from kivymd.theming import ThemableBehavior
|
||||
from kivymd.uix.behaviors import (
|
||||
BackgroundColorBehavior,
|
||||
StencilBehavior,
|
||||
CommonElevationBehavior,
|
||||
HoverBehavior,
|
||||
)
|
||||
|
||||
|
||||
class MediaPopup(
|
||||
ThemableBehavior,
|
||||
HoverBehavior,
|
||||
StencilBehavior,
|
||||
CommonElevationBehavior,
|
||||
BackgroundColorBehavior,
|
||||
ModalView,
|
||||
):
|
||||
caller = ObjectProperty()
|
||||
player = ObjectProperty()
|
||||
|
||||
def __init__(self, caller, *args, **kwarg):
|
||||
self.caller = caller
|
||||
super(MediaPopup, self).__init__(*args, **kwarg)
|
||||
self.player.bind(fullscreen=self.handle_clean_fullscreen_transition)
|
||||
|
||||
def open(self, *_args, **kwargs):
|
||||
"""Display the modal in the Window.
|
||||
|
||||
When the view is opened, it will be faded in with an animation. If you
|
||||
don't want the animation, use::
|
||||
|
||||
view.open(animation=False)
|
||||
|
||||
"""
|
||||
from kivy.core.window import Window
|
||||
|
||||
if self._is_open:
|
||||
return
|
||||
self._window = Window
|
||||
self._is_open = True
|
||||
self.dispatch("on_pre_open")
|
||||
Window.add_widget(self)
|
||||
Window.bind(on_resize=self._align_center, on_keyboard=self._handle_keyboard)
|
||||
self.center = self.caller.to_window(*self.caller.center)
|
||||
self.fbind("center", self._align_center)
|
||||
self.fbind("size", self._align_center)
|
||||
if kwargs.get("animation", True):
|
||||
ani = Animation(_anim_alpha=1.0, d=self._anim_duration)
|
||||
ani.bind(on_complete=lambda *_args: self.dispatch("on_open"))
|
||||
ani.start(self)
|
||||
else:
|
||||
self._anim_alpha = 1.0
|
||||
self.dispatch("on_open")
|
||||
|
||||
def _align_center(self, *_args):
|
||||
if self._is_open:
|
||||
self.center = self.caller.to_window(*self.caller.center)
|
||||
|
||||
def on_leave(self, *args):
|
||||
def _leave(dt):
|
||||
self.player.state = "stop"
|
||||
if self.player._video:
|
||||
self.player._video.unload()
|
||||
|
||||
if not self.hovering:
|
||||
self.dismiss()
|
||||
|
||||
Clock.schedule_once(_leave, 2)
|
||||
|
||||
def handle_clean_fullscreen_transition(self,instance,fullscreen):
|
||||
if not fullscreen:
|
||||
if not self._is_open:
|
||||
instance.state = "stop"
|
||||
if vid:=instance._video:
|
||||
vid.unload()
|
||||
else:
|
||||
instance.state = "stop"
|
||||
if vid:=instance._video:
|
||||
vid.unload()
|
||||
self.dismiss()
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<Tooltip>
|
||||
MDTooltipPlain:
|
||||
text:root.tooltip_text
|
||||
24
anixstream/View/components/media_card/media_card.kv
Normal file
24
anixstream/View/components/media_card/media_card.kv
Normal file
@@ -0,0 +1,24 @@
|
||||
<MediaCard>
|
||||
adaptive_height:True
|
||||
spacing:"5dp"
|
||||
image:"https://s4.anilist.co/file/anilistcdn/media/anime/cover/small/bx163270-oxwgbe43Cpog.jpg"
|
||||
size_hint_x: None
|
||||
width:dp(100)
|
||||
on_release:
|
||||
self.open()
|
||||
FitImage:
|
||||
source:root.cover_image_url
|
||||
fit_mode:"fill"
|
||||
size_hint: None, None
|
||||
width: dp(100)
|
||||
height: dp(150)
|
||||
MDDivider:
|
||||
color:root.has_trailer_color
|
||||
SingleLineLabel:
|
||||
font_style:"Label"
|
||||
role:"medium"
|
||||
text:root.title
|
||||
max_lines:2
|
||||
halign:"center"
|
||||
color:self.theme_cls.secondaryColor
|
||||
|
||||
96
anixstream/View/components/media_card/media_card.py
Normal file
96
anixstream/View/components/media_card/media_card.py
Normal file
@@ -0,0 +1,96 @@
|
||||
from kivy.properties import (
|
||||
ObjectProperty,
|
||||
StringProperty,
|
||||
BooleanProperty,
|
||||
ListProperty,
|
||||
NumericProperty,
|
||||
)
|
||||
from kivy.clock import Clock
|
||||
from kivy.uix.behaviors import ButtonBehavior
|
||||
|
||||
from kivymd.uix.boxlayout import MDBoxLayout
|
||||
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()
|
||||
trailer_url = StringProperty()
|
||||
episodes = StringProperty()
|
||||
favourites = StringProperty()
|
||||
popularity = StringProperty()
|
||||
media_status = StringProperty("Releasing")
|
||||
is_in_my_list = BooleanProperty(False)
|
||||
is_in_my_notify = BooleanProperty(False)
|
||||
genres = StringProperty()
|
||||
first_aired_on = StringProperty()
|
||||
description = StringProperty()
|
||||
producers = StringProperty()
|
||||
studios = StringProperty()
|
||||
next_airing_episode = StringProperty()
|
||||
tags = StringProperty()
|
||||
stars = ListProperty([0, 0, 0, 0, 0, 0])
|
||||
cover_image_url = StringProperty()
|
||||
preview_image = StringProperty()
|
||||
has_trailer_color = ListProperty([.5, .5, .5, .5])
|
||||
|
||||
def __init__(self, trailer_url=None, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.orientation = "vertical"
|
||||
|
||||
if trailer_url:
|
||||
self.trailer_url = trailer_url
|
||||
self.adaptive_size = True
|
||||
|
||||
def on_enter(self):
|
||||
def _open_popup(dt):
|
||||
if self.hovering:
|
||||
window = self.get_parent_window()
|
||||
if window:
|
||||
for widget in window.children: # type: ignore
|
||||
if isinstance(widget, MediaPopup):
|
||||
return
|
||||
self.open()
|
||||
|
||||
Clock.schedule_once(_open_popup, 5)
|
||||
|
||||
def on_popup_open(self, popup: MediaPopup):
|
||||
popup.center = self.center
|
||||
|
||||
def on_dismiss(self, popup: MediaPopup):
|
||||
popup.player.state = "stop"
|
||||
if popup.player._video:
|
||||
popup.player._video.unload()
|
||||
|
||||
def set_preview_image(self, image):
|
||||
self.preview_image = image
|
||||
|
||||
def set_trailer_url(self, trailer_url):
|
||||
self.trailer_url = trailer_url
|
||||
self.has_trailer_color = self.theme_cls.primaryColor
|
||||
|
||||
def open(self, *_):
|
||||
popup = MediaPopup(self)
|
||||
popup.title = self.title
|
||||
popup.bind(on_dismiss=self.on_dismiss, on_open=self.on_popup_open)
|
||||
popup.open(self)
|
||||
|
||||
# ---------------respond to user actions and call appropriate model-------------------------
|
||||
def on_is_in_my_list(self, instance, in_user_anime_list):
|
||||
if self.screen:
|
||||
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
|
||||
|
||||
|
||||
class MediaCardsContainer(MDBoxLayout):
|
||||
container = ObjectProperty()
|
||||
list_name = StringProperty()
|
||||
45
anixstream/View/components/navrail.kv
Normal file
45
anixstream/View/components/navrail.kv
Normal file
@@ -0,0 +1,45 @@
|
||||
<CommonNavigationRailItem>
|
||||
MDNavigationRailItemIcon:
|
||||
icon:root.icon
|
||||
MDNavigationRailItemLabel:
|
||||
text: root.text
|
||||
|
||||
|
||||
<NavRail>:
|
||||
anchor:"top"
|
||||
type: "labeled"
|
||||
md_bg_color: self.theme_cls.secondaryContainerColor
|
||||
MDNavigationRailFabButton:
|
||||
icon: "home"
|
||||
on_press:
|
||||
root.screen.manager_screens.current = "home screen"
|
||||
CommonNavigationRailItem:
|
||||
icon: "magnify"
|
||||
text: "Search"
|
||||
on_press:
|
||||
root.screen.manager_screens.current = "search screen"
|
||||
|
||||
CommonNavigationRailItem:
|
||||
icon: "bookmark"
|
||||
text: "MyList"
|
||||
on_press:
|
||||
root.screen.manager_screens.current = "my list screen"
|
||||
CommonNavigationRailItem:
|
||||
icon: "download-circle"
|
||||
text: "Downloads"
|
||||
on_press:
|
||||
root.screen.manager_screens.current = "downloads screen"
|
||||
CommonNavigationRailItem:
|
||||
icon: "cog"
|
||||
text: "settings"
|
||||
on_press:app.open_settings()
|
||||
CommonNavigationRailItem:
|
||||
icon: "help-circle"
|
||||
text: "Help"
|
||||
on_press:
|
||||
root.screen.manager_screens.current = "help screen"
|
||||
CommonNavigationRailItem:
|
||||
icon: "bug"
|
||||
text: "debug"
|
||||
on_press:
|
||||
root.screen.manager_screens.current = "crashlog screen"
|
||||
3
anixstream/View/components/tooltip.kv
Normal file
3
anixstream/View/components/tooltip.kv
Normal file
@@ -0,0 +1,3 @@
|
||||
<Tooltip>
|
||||
MDTooltipPlain:
|
||||
text:root.tooltip_text
|
||||
18
anixstream/View/components/top_app_bar.kv
Normal file
18
anixstream/View/components/top_app_bar.kv
Normal file
@@ -0,0 +1,18 @@
|
||||
<SearchBar>:
|
||||
pos_hint: {'center_x': 0.5,'top': 1}
|
||||
padding: "10dp"
|
||||
adaptive_height:True
|
||||
size_hint_x:.75
|
||||
spacing: '20dp'
|
||||
MDTextField:
|
||||
size_hint_x:1
|
||||
required:True
|
||||
on_text_validate:
|
||||
app.search_for_anime(args[0])
|
||||
MDTextFieldLeadingIcon:
|
||||
icon: "magnify"
|
||||
MDTextFieldHintText:
|
||||
text: "Search for anime"
|
||||
MDIconButton:
|
||||
pos_hint: {'center_y': 0.5}
|
||||
icon: "account-circle"
|
||||
Reference in New Issue
Block a user