mirror of
https://github.com/Benexl/FastAnime.git
synced 2026-01-16 06:42:43 -08:00
renamed app to anixstream
This commit is contained in:
61
anixstream/View/AnimeScreen/anime_screen.kv
Normal file
61
anixstream/View/AnimeScreen/anime_screen.kv
Normal file
@@ -0,0 +1,61 @@
|
||||
<AnimeScreenView>:
|
||||
md_bg_color: self.theme_cls.backgroundColor
|
||||
header:header
|
||||
side_bar:side_bar
|
||||
rankings_bar:rankings_bar
|
||||
anime_description:anime_description
|
||||
anime_characters:anime_characters
|
||||
anime_reviews:anime_reviews
|
||||
MDBoxLayout:
|
||||
orientation: 'vertical'
|
||||
MDBoxLayout:
|
||||
orientation: 'vertical'
|
||||
size_hint_y:None
|
||||
height: self.minimum_height
|
||||
MDBoxLayout:
|
||||
adaptive_height:True
|
||||
MDIconButton:
|
||||
icon: "arrow-left"
|
||||
on_release:
|
||||
root.manager_screens.current = root.caller_screen_name
|
||||
MDScrollView:
|
||||
size_hint:1,1
|
||||
MDBoxLayout:
|
||||
id:main_container
|
||||
size_hint_y:None
|
||||
padding:"10dp"
|
||||
spacing:"10dp"
|
||||
height: self.minimum_height
|
||||
orientation:"vertical"
|
||||
AnimeHeader:
|
||||
id:header
|
||||
MDBoxLayout:
|
||||
size_hint_y:None
|
||||
height: self.minimum_height
|
||||
AnimeSideBar:
|
||||
id:side_bar
|
||||
screen:root
|
||||
size_hint_y:None
|
||||
height:max(self.parent.height,self.minimum_height)
|
||||
|
||||
MDBoxLayout:
|
||||
spacing:"10dp"
|
||||
orientation:"vertical"
|
||||
size_hint_y:None
|
||||
height: max(self.parent.height,self.minimum_height)
|
||||
RankingsBar:
|
||||
id:rankings_bar
|
||||
Controls:
|
||||
screen:root
|
||||
cols:3 if root.width < 1100 else 5
|
||||
MDBoxLayout:
|
||||
adaptive_height:True
|
||||
padding:"20dp"
|
||||
orientation:"vertical"
|
||||
AnimeDescription:
|
||||
id:anime_description
|
||||
AnimeCharacters:
|
||||
id:anime_characters
|
||||
AnimeReviews:
|
||||
id:anime_reviews
|
||||
BoxLayout:
|
||||
136
anixstream/View/AnimeScreen/anime_screen.py
Normal file
136
anixstream/View/AnimeScreen/anime_screen.py
Normal file
@@ -0,0 +1,136 @@
|
||||
from datetime import datetime
|
||||
|
||||
from kivy.properties import ObjectProperty, DictProperty, StringProperty
|
||||
|
||||
from Utility import anilist_data_helper
|
||||
from libs.anilist import AnilistBaseMediaDataSchema
|
||||
|
||||
from View.base_screen import BaseScreenView
|
||||
from .components import (
|
||||
AnimeHeader,
|
||||
AnimeSideBar,
|
||||
AnimeDescription,
|
||||
AnimeReviews,
|
||||
AnimeCharacters,
|
||||
AnimdlStreamDialog,
|
||||
DownloadAnimeDialog,
|
||||
RankingsBar,
|
||||
)
|
||||
|
||||
|
||||
class AnimeScreenView(BaseScreenView):
|
||||
"""The anime screen view"""
|
||||
caller_screen_name = StringProperty()
|
||||
header: AnimeHeader = ObjectProperty()
|
||||
side_bar: AnimeSideBar = ObjectProperty()
|
||||
rankings_bar: RankingsBar = ObjectProperty()
|
||||
anime_description: AnimeDescription = ObjectProperty()
|
||||
anime_characters: AnimeCharacters = ObjectProperty()
|
||||
anime_reviews: AnimeReviews = ObjectProperty()
|
||||
data = DictProperty()
|
||||
anime_id = 0
|
||||
|
||||
def update_layout(self, data: AnilistBaseMediaDataSchema, caller_screen_name: str):
|
||||
self.caller_screen_name = caller_screen_name
|
||||
self.data = data
|
||||
# uitlity functions
|
||||
|
||||
# variables
|
||||
english_title = data["title"]["english"]
|
||||
jp_title = data["title"]["romaji"]
|
||||
studios = data["studios"]["nodes"]
|
||||
|
||||
# update header
|
||||
self.header.titles = f"{english_title}\n{jp_title}"
|
||||
if banner_image := data["bannerImage"]:
|
||||
self.header.banner_image = banner_image
|
||||
|
||||
# -----side bar-----
|
||||
|
||||
# update image
|
||||
self.side_bar.image = data["coverImage"]["extraLarge"]
|
||||
|
||||
# update alternative titles
|
||||
alternative_titles = {
|
||||
"synonyms": anilist_data_helper.format_list_data_with_comma(
|
||||
data["synonyms"]
|
||||
), # list
|
||||
"japanese": jp_title,
|
||||
"english": english_title,
|
||||
}
|
||||
self.side_bar.alternative_titles = alternative_titles
|
||||
|
||||
# update information
|
||||
information = {
|
||||
"episodes": data["episodes"],
|
||||
"status": data["status"],
|
||||
"nextAiringEpisode": anilist_data_helper.extract_next_airing_episode(
|
||||
data["nextAiringEpisode"]
|
||||
),
|
||||
"aired": f"{anilist_data_helper.format_anilist_date_object(data['startDate'])} to {anilist_data_helper.format_anilist_date_object(data['endDate'])}",
|
||||
"premiered": f"{data['season']} {data['seasonYear']}",
|
||||
"broadcast": data["format"],
|
||||
"countryOfOrigin": data["countryOfOrigin"],
|
||||
"hashtag": data["hashtag"],
|
||||
"studios": anilist_data_helper.format_list_data_with_comma(
|
||||
[studio["name"] for studio in studios if studio["isAnimationStudio"]]
|
||||
), # { "name": "Sunrise", "isAnimationStudio": true }
|
||||
"producers": anilist_data_helper.format_list_data_with_comma(
|
||||
[
|
||||
studio["name"]
|
||||
for studio in studios
|
||||
if not studio["isAnimationStudio"]
|
||||
]
|
||||
),
|
||||
"source": data["source"],
|
||||
"genres": anilist_data_helper.format_list_data_with_comma(data["genres"]),
|
||||
"duration": data["duration"],
|
||||
}
|
||||
self.side_bar.information = information
|
||||
|
||||
# update statistics
|
||||
statistics = [*[(stat["context"], stat["rank"]) for stat in data["rankings"]]]
|
||||
self.side_bar.statistics = statistics
|
||||
|
||||
# update tags
|
||||
self.side_bar.tags = [(tag["name"], tag["rank"]) for tag in data["tags"]]
|
||||
|
||||
# update external links
|
||||
|
||||
external_links = [
|
||||
("AniList", data["siteUrl"]),
|
||||
*[(site["site"], site["url"]) for site in data["externalLinks"]],
|
||||
]
|
||||
self.side_bar.external_links = external_links
|
||||
|
||||
self.rankings_bar.rankings = {
|
||||
"Popularity": data["popularity"],
|
||||
"Favourites": data["favourites"],
|
||||
"AverageScore": data["averageScore"] if data["averageScore"] else 0,
|
||||
}
|
||||
|
||||
self.anime_description.description = data["description"]
|
||||
|
||||
self.anime_characters.characters = [
|
||||
(character["node"], character["voiceActors"])
|
||||
for character in data["characters"]["edges"]
|
||||
] # list (character,actor)
|
||||
|
||||
self.anime_reviews.reviews = data["reviews"]["nodes"]
|
||||
|
||||
def stream_anime_with_custom_cmds_dialog(self,mpv=False):
|
||||
"""
|
||||
Called when user wants to stream with custom commands
|
||||
"""
|
||||
|
||||
AnimdlStreamDialog(self.data,mpv).open()
|
||||
|
||||
def open_download_anime_dialog(self):
|
||||
"""
|
||||
Opens the download anime dialog
|
||||
"""
|
||||
|
||||
DownloadAnimeDialog(self.data).open()
|
||||
|
||||
def add_to_user_anime_list(self, *args):
|
||||
self.app.add_anime_to_user_anime_list(self.model.anime_id)
|
||||
10
anixstream/View/AnimeScreen/components/__init__.py
Normal file
10
anixstream/View/AnimeScreen/components/__init__.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from .side_bar import AnimeSideBar
|
||||
from .header import AnimeHeader
|
||||
from .rankings_bar import RankingsBar
|
||||
from .controls import Controls
|
||||
from .description import AnimeDescription
|
||||
from .characters import AnimeCharacters
|
||||
from .review import AnimeReviews
|
||||
|
||||
from .animdl_stream_dialog import AnimdlStreamDialog
|
||||
from .download_anime_dialog import DownloadAnimeDialog
|
||||
@@ -0,0 +1,63 @@
|
||||
<StreamDialogLabel@MDLabel>:
|
||||
adaptive_height:True
|
||||
max_lines:0
|
||||
shorten:False
|
||||
markup:True
|
||||
font_style: "Label"
|
||||
role: "medium"
|
||||
bold:True
|
||||
<StreamDialogHeaderLabel@MDLabel>:
|
||||
adaptive_height:True
|
||||
halign:"center"
|
||||
max_lines:0
|
||||
shorten:False
|
||||
bold:True
|
||||
markup:True
|
||||
font_style: "Title"
|
||||
role: "medium"
|
||||
md_bg_color:self.theme_cls.secondaryContainerColor
|
||||
padding:"10dp"
|
||||
|
||||
|
||||
|
||||
<AnimdlStreamDialog>
|
||||
md_bg_color:self.theme_cls.backgroundColor
|
||||
radius:8
|
||||
size_hint:None,None
|
||||
height:"500dp"
|
||||
width:"400dp"
|
||||
MDBoxLayout:
|
||||
spacing: '10dp'
|
||||
padding:"10dp"
|
||||
orientation:"vertical"
|
||||
StreamDialogHeaderLabel:
|
||||
text:"Stream Anime"
|
||||
StreamDialogLabel:
|
||||
text:"Title"
|
||||
MDTextField:
|
||||
id:title_field
|
||||
required:True
|
||||
StreamDialogLabel:
|
||||
text:"Range"
|
||||
MDTextField:
|
||||
id:range_field
|
||||
required:True
|
||||
StreamDialogLabel:
|
||||
text:"Latest"
|
||||
MDTextField:
|
||||
id:latest_field
|
||||
required:True
|
||||
StreamDialogLabel:
|
||||
text:"Quality"
|
||||
MDTextField:
|
||||
id:quality_field
|
||||
required:True
|
||||
MDBoxLayout:
|
||||
orientation:"vertical"
|
||||
MDButton:
|
||||
pos_hint: {'center_x': 0.5}
|
||||
on_press:root.stream_anime(app)
|
||||
MDButtonIcon:
|
||||
icon:"rss"
|
||||
MDButtonText:
|
||||
text:"Stream"
|
||||
@@ -0,0 +1,69 @@
|
||||
from kivy.clock import Clock
|
||||
from kivy.uix.modalview import ModalView
|
||||
|
||||
from kivymd.uix.behaviors import (
|
||||
StencilBehavior,
|
||||
CommonElevationBehavior,
|
||||
BackgroundColorBehavior,
|
||||
)
|
||||
from kivymd.theming import ThemableBehavior
|
||||
|
||||
|
||||
class AnimdlStreamDialog(
|
||||
ThemableBehavior,
|
||||
StencilBehavior,
|
||||
CommonElevationBehavior,
|
||||
BackgroundColorBehavior,
|
||||
ModalView,
|
||||
):
|
||||
"""The anime streaming dialog"""
|
||||
|
||||
def __init__(self, data, mpv, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.data = data
|
||||
self.mpv = mpv
|
||||
if title := data["title"].get("romaji"):
|
||||
self.ids.title_field.text = title
|
||||
elif title := data["title"].get("english"):
|
||||
self.ids.title_field.text = title
|
||||
|
||||
self.ids.quality_field.text = "best"
|
||||
|
||||
def _stream_anime(self, app):
|
||||
if self.mpv:
|
||||
streaming_cmds = {}
|
||||
title = self.ids.title_field.text
|
||||
streaming_cmds["title"] = title
|
||||
|
||||
episodes_range = self.ids.range_field.text
|
||||
if episodes_range:
|
||||
streaming_cmds["episodes_range"] = episodes_range
|
||||
|
||||
quality = self.ids.quality_field.text
|
||||
if quality:
|
||||
streaming_cmds["quality"] = quality
|
||||
else:
|
||||
streaming_cmds["quality"] = "best"
|
||||
|
||||
app.watch_on_animdl(streaming_cmds)
|
||||
else:
|
||||
cmds = []
|
||||
title = self.ids.title_field.text
|
||||
cmds.append(title)
|
||||
|
||||
episodes_range = self.ids.range_field.text
|
||||
if episodes_range:
|
||||
cmds = [*cmds, "-r", episodes_range]
|
||||
|
||||
latest = self.ids.latest_field.text
|
||||
if latest:
|
||||
cmds = [*cmds, "-s", latest]
|
||||
|
||||
quality = self.ids.quality_field.text
|
||||
if quality:
|
||||
cmds = [*cmds, "-q", quality]
|
||||
|
||||
app.watch_on_animdl(custom_options=cmds)
|
||||
self.dismiss()
|
||||
def stream_anime(self, app):
|
||||
Clock.schedule_once(lambda _: self._stream_anime(app))
|
||||
71
anixstream/View/AnimeScreen/components/characters.kv
Normal file
71
anixstream/View/AnimeScreen/components/characters.kv
Normal file
@@ -0,0 +1,71 @@
|
||||
#:import get_hex_from_color kivy.utils.get_hex_from_color
|
||||
|
||||
<CharactersContainer@MDBoxLayout>:
|
||||
adaptive_height:True
|
||||
md_bg_color:self.theme_cls.surfaceContainerLowColor
|
||||
padding:"10dp"
|
||||
orientation:"vertical"
|
||||
|
||||
<CharacterText@MDLabel>:
|
||||
adaptive_height:True
|
||||
max_lines:0
|
||||
shorten:False
|
||||
markup:True
|
||||
font_style: "Body"
|
||||
role: "small"
|
||||
|
||||
<CharacterHeader@MDBoxLayout>:
|
||||
adaptive_height:True
|
||||
spacing:"10dp"
|
||||
<CharacterAvatar@FitImage>
|
||||
radius:50
|
||||
size_hint:None,None
|
||||
height:"50dp"
|
||||
width:"50dp"
|
||||
|
||||
<CharacterSecondaryContainer@MDBoxLayout>:
|
||||
adaptive_height:True
|
||||
orientation:"vertical"
|
||||
<AnimeCharacter>:
|
||||
spacing:"5dp"
|
||||
adaptive_height:True
|
||||
orientation:"vertical"
|
||||
CharacterHeader:
|
||||
padding:"10dp"
|
||||
CharacterAvatar:
|
||||
source:root.character["image"]
|
||||
CharacterText:
|
||||
text: root.character["name"]
|
||||
pos_hint:{"center_y":.5}
|
||||
|
||||
CharacterSecondaryContainer:
|
||||
spacing:"5dp"
|
||||
MDDivider:
|
||||
CharacterText:
|
||||
text: "Details"
|
||||
MDDivider:
|
||||
CharacterText:
|
||||
text:"[color={}]Gender:[/color] {}".format(get_hex_from_color(self.theme_cls.primaryColor),root.character["gender"])
|
||||
CharacterText:
|
||||
text:"[color={}]Date Of Birth:[/color] {}".format(get_hex_from_color(self.theme_cls.primaryColor),root.character["dateOfBirth"])
|
||||
CharacterText:
|
||||
text:"[color={}]Age:[/color] {}".format(get_hex_from_color(self.theme_cls.primaryColor),root.character["age"])
|
||||
CharacterText:
|
||||
text:"[color={}]Description:[/color] {}".format(get_hex_from_color(self.theme_cls.primaryColor),root.character["description"])
|
||||
max_lines:5
|
||||
CharacterText:
|
||||
text:"[color={}]Voice Actors:[/color] {}".format(get_hex_from_color(self.theme_cls.primaryColor),root.voice_actors["name"])
|
||||
MDDivider:
|
||||
|
||||
|
||||
<AnimeCharacters>:
|
||||
adaptive_height:True
|
||||
container:container
|
||||
orientation:"vertical"
|
||||
HeaderLabel:
|
||||
text:"Characters"
|
||||
halign:"left"
|
||||
CharactersContainer:
|
||||
id:container
|
||||
|
||||
|
||||
56
anixstream/View/AnimeScreen/components/characters.py
Normal file
56
anixstream/View/AnimeScreen/components/characters.py
Normal file
@@ -0,0 +1,56 @@
|
||||
from kivy.clock import Clock
|
||||
from kivy.properties import ObjectProperty, ListProperty
|
||||
|
||||
from kivymd.uix.boxlayout import MDBoxLayout
|
||||
|
||||
|
||||
class AnimeCharacter(MDBoxLayout):
|
||||
"""an Anime character data"""
|
||||
|
||||
voice_actors = ObjectProperty({"name": "", "image": ""})
|
||||
character = ObjectProperty(
|
||||
{
|
||||
"name": "",
|
||||
"gender": "",
|
||||
"dateOfBirth": "",
|
||||
"image": "",
|
||||
"age": "",
|
||||
"description": "",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class AnimeCharacters(MDBoxLayout):
|
||||
"""The anime characters card"""
|
||||
|
||||
container = ObjectProperty()
|
||||
characters = ListProperty()
|
||||
|
||||
def update_characters_card(self, instance, characters):
|
||||
format_date = lambda date_: (
|
||||
f"{date_['day']}/{date_['month']}/{date_['year']}" if date_ else ""
|
||||
)
|
||||
|
||||
self.container.clear_widgets()
|
||||
for character_ in characters: # character (character,actor)
|
||||
character = character_[0]
|
||||
actors = character_[1]
|
||||
|
||||
anime_character = AnimeCharacter()
|
||||
anime_character.character = {
|
||||
"name": character["name"]["full"],
|
||||
"gender": character["gender"],
|
||||
"dateOfBirth": format_date(character["dateOfBirth"]),
|
||||
"image": character["image"]["medium"],
|
||||
"age": character["age"],
|
||||
"description": character["description"],
|
||||
}
|
||||
anime_character.voice_actors = {
|
||||
"name": ", ".join([actor["name"]["full"] for actor in actors])
|
||||
}
|
||||
|
||||
# anime_character.voice_actor =
|
||||
self.container.add_widget(anime_character)
|
||||
|
||||
def on_characters(self, *args):
|
||||
Clock.schedule_once(lambda _: self.update_characters_card(*args))
|
||||
32
anixstream/View/AnimeScreen/components/controls.kv
Normal file
32
anixstream/View/AnimeScreen/components/controls.kv
Normal file
@@ -0,0 +1,32 @@
|
||||
<Controls>
|
||||
adaptive_height:True
|
||||
padding:"10dp"
|
||||
spacing:"10dp"
|
||||
pos_hint: {'center_x': 0.5}
|
||||
cols:3
|
||||
MDButton:
|
||||
on_press:
|
||||
root.screen.add_to_user_anime_list()
|
||||
add_to_user_list_label.text = "Added to MyAnimeList"
|
||||
MDButtonText:
|
||||
id:add_to_user_list_label
|
||||
text:"Add to MyAnimeList"
|
||||
MDButton:
|
||||
on_press:
|
||||
if root.screen:root.screen.stream_anime_with_custom_cmds_dialog()
|
||||
MDButtonText:
|
||||
text:"Watch on Animdl"
|
||||
MDButton:
|
||||
on_press:
|
||||
if root.screen:root.screen.stream_anime_with_custom_cmds_dialog(mpv=True)
|
||||
MDButtonText:
|
||||
text:"Watch on mpv"
|
||||
MDButton:
|
||||
on_press: app.watch_on_allanime(root.screen.data["title"]["romaji"]) if root.screen.data["title"]["romaji"] else app.watch_on_allanime(root.screen.data["title"]["english"])
|
||||
MDButtonText:
|
||||
text:"Watch on AllAnime"
|
||||
MDButton:
|
||||
on_press:
|
||||
if root.screen:root.screen.open_download_anime_dialog()
|
||||
MDButtonText:
|
||||
text:"Download Anime"
|
||||
9
anixstream/View/AnimeScreen/components/controls.py
Normal file
9
anixstream/View/AnimeScreen/components/controls.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from kivy.properties import ObjectProperty
|
||||
|
||||
from kivymd.uix.gridlayout import MDGridLayout
|
||||
|
||||
|
||||
class Controls(MDGridLayout):
|
||||
"""The diferent controls available"""
|
||||
|
||||
screen = ObjectProperty()
|
||||
23
anixstream/View/AnimeScreen/components/description.kv
Normal file
23
anixstream/View/AnimeScreen/components/description.kv
Normal file
@@ -0,0 +1,23 @@
|
||||
<DescriptionContainer@MDBoxLayout>:
|
||||
adaptive_height:True
|
||||
md_bg_color:self.theme_cls.surfaceContainerLowColor
|
||||
padding:"10dp"
|
||||
|
||||
<DescriptionText@MDLabel>:
|
||||
adaptive_height:True
|
||||
max_lines:0
|
||||
shorten:False
|
||||
markup:True
|
||||
font_style: "Body"
|
||||
role: "small"
|
||||
<AnimeDescription>:
|
||||
orientation:"vertical"
|
||||
adaptive_height:True
|
||||
HeaderLabel:
|
||||
halign:"left"
|
||||
text:"Description"
|
||||
DescriptionContainer:
|
||||
DescriptionText:
|
||||
text:root.description
|
||||
|
||||
|
||||
9
anixstream/View/AnimeScreen/components/description.py
Normal file
9
anixstream/View/AnimeScreen/components/description.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from kivy.properties import StringProperty
|
||||
|
||||
from kivymd.uix.boxlayout import MDBoxLayout
|
||||
|
||||
|
||||
class AnimeDescription(MDBoxLayout):
|
||||
"""The anime description"""
|
||||
|
||||
description = StringProperty()
|
||||
@@ -0,0 +1,58 @@
|
||||
<DownloadDialogLabel@MDLabel>:
|
||||
adaptive_height:True
|
||||
max_lines:0
|
||||
shorten:False
|
||||
markup:True
|
||||
font_style: "Label"
|
||||
role: "medium"
|
||||
bold:True
|
||||
<DownloadDialogHeaderLabel@MDLabel>:
|
||||
adaptive_height:True
|
||||
halign:"center"
|
||||
max_lines:0
|
||||
shorten:False
|
||||
bold:True
|
||||
markup:True
|
||||
font_style: "Title"
|
||||
role: "medium"
|
||||
md_bg_color:self.theme_cls.secondaryContainerColor
|
||||
padding:"10dp"
|
||||
|
||||
|
||||
|
||||
<DownloadAnimeDialog>
|
||||
md_bg_color:self.theme_cls.backgroundColor
|
||||
radius:8
|
||||
size_hint:None,None
|
||||
height:"500dp"
|
||||
width:"400dp"
|
||||
MDBoxLayout:
|
||||
spacing: '10dp'
|
||||
padding:"10dp"
|
||||
orientation:"vertical"
|
||||
DownloadDialogHeaderLabel:
|
||||
text:"Download Anime"
|
||||
DownloadDialogLabel:
|
||||
text:"Title"
|
||||
MDTextField:
|
||||
id:title_field
|
||||
required:True
|
||||
DownloadDialogLabel:
|
||||
text:"Range"
|
||||
MDTextField:
|
||||
id:range_field
|
||||
required:True
|
||||
DownloadDialogLabel:
|
||||
text:"Quality"
|
||||
MDTextField:
|
||||
id:quality_field
|
||||
required:True
|
||||
MDBoxLayout:
|
||||
orientation:"vertical"
|
||||
MDButton:
|
||||
pos_hint: {'center_x': 0.5}
|
||||
on_press:root.download_anime(app)
|
||||
MDButtonIcon:
|
||||
icon:"download"
|
||||
MDButtonText:
|
||||
text:"Download"
|
||||
@@ -0,0 +1,42 @@
|
||||
from kivy.uix.modalview import ModalView
|
||||
|
||||
from kivymd.uix.behaviors import (
|
||||
StencilBehavior,
|
||||
CommonElevationBehavior,
|
||||
BackgroundColorBehavior,
|
||||
)
|
||||
from kivymd.theming import ThemableBehavior
|
||||
|
||||
|
||||
# from main import AniXStreamApp
|
||||
class DownloadAnimeDialog(
|
||||
ThemableBehavior,
|
||||
StencilBehavior,
|
||||
CommonElevationBehavior,
|
||||
BackgroundColorBehavior,
|
||||
ModalView,
|
||||
):
|
||||
"""The download anime dialog"""
|
||||
|
||||
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"):
|
||||
self.ids.title_field.text = title
|
||||
self.ids.quality_field.text = "best"
|
||||
|
||||
def download_anime(self, app):
|
||||
default_cmds = {}
|
||||
title = self.ids.title_field.text
|
||||
default_cmds["title"] = title
|
||||
if episodes_range := self.ids.range_field.text:
|
||||
default_cmds["episodes_range"] = episodes_range
|
||||
|
||||
if quality := self.ids.range_field.text:
|
||||
default_cmds["quality"] = quality
|
||||
|
||||
app.download_anime(self.anime_id, default_cmds)
|
||||
self.dismiss()
|
||||
20
anixstream/View/AnimeScreen/components/header.kv
Normal file
20
anixstream/View/AnimeScreen/components/header.kv
Normal file
@@ -0,0 +1,20 @@
|
||||
<AnimeHeader>:
|
||||
adaptive_height:True
|
||||
orientation: 'vertical'
|
||||
MDBoxLayout:
|
||||
adaptive_height:True
|
||||
md_bg_color:self.theme_cls.secondaryContainerColor
|
||||
MDLabel:
|
||||
text: root.titles
|
||||
adaptive_height:True
|
||||
padding:"5dp"
|
||||
bold:True
|
||||
shorten:False
|
||||
max_lines:2
|
||||
font_style:"Label"
|
||||
role:"large"
|
||||
FitImage:
|
||||
size_hint_y: None
|
||||
height: dp(250)
|
||||
source: root.banner_image if root.banner_image else app.default_banner_image
|
||||
|
||||
8
anixstream/View/AnimeScreen/components/header.py
Normal file
8
anixstream/View/AnimeScreen/components/header.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from kivy.properties import StringProperty
|
||||
|
||||
from kivymd.uix.boxlayout import MDBoxLayout
|
||||
|
||||
|
||||
class AnimeHeader(MDBoxLayout):
|
||||
titles = StringProperty()
|
||||
banner_image = StringProperty()
|
||||
81
anixstream/View/AnimeScreen/components/rankings_bar.kv
Normal file
81
anixstream/View/AnimeScreen/components/rankings_bar.kv
Normal file
@@ -0,0 +1,81 @@
|
||||
#:set yellow [.9,.9,0,.9]
|
||||
|
||||
<RankingsLabel@MDLabel>:
|
||||
max_lines:0
|
||||
shorten:False
|
||||
markup:True
|
||||
font_style: "Label"
|
||||
role: "medium"
|
||||
|
||||
<RankingsHeaderLabel@MDLabel>:
|
||||
color:self.theme_cls.primaryColor
|
||||
bold:True
|
||||
max_lines:0
|
||||
shorten:False
|
||||
font_style: "Label"
|
||||
role: "large"
|
||||
|
||||
|
||||
<RankingsDivider@MDDivider>:
|
||||
orientation:"vertical"
|
||||
|
||||
<RankingsBoxLayout@MDBoxLayout>:
|
||||
orientation:"vertical"
|
||||
padding:"20dp"
|
||||
<RankingsBar>:
|
||||
size_hint_y:None
|
||||
height:dp(100)
|
||||
line_color:self.theme_cls.secondaryColor
|
||||
padding:"10dp"
|
||||
RankingsBoxLayout:
|
||||
size_hint_x:.4
|
||||
RankingsHeaderLabel:
|
||||
text:"Average Score"
|
||||
MDBoxLayout:
|
||||
adaptive_width:True
|
||||
MDBoxLayout:
|
||||
adaptive_size:True
|
||||
pos_hint: {'center_y': .5}
|
||||
MDIcon:
|
||||
icon: "star"
|
||||
color:yellow
|
||||
disabled: not((root.rankings["AverageScore"]/100)*6>=1)
|
||||
MDIcon:
|
||||
color:yellow
|
||||
disabled: not(root.rankings["AverageScore"]/100*6>=2)
|
||||
icon: "star"
|
||||
MDIcon:
|
||||
color:yellow
|
||||
disabled: not(root.rankings["AverageScore"]/100*6>=3)
|
||||
icon: "star"
|
||||
MDIcon:
|
||||
color:yellow
|
||||
disabled: not(root.rankings["AverageScore"]/100*6>=4)
|
||||
icon: "star"
|
||||
MDIcon:
|
||||
color:yellow
|
||||
icon: "star"
|
||||
disabled: not(root.rankings["AverageScore"]/100*6>=5)
|
||||
MDIcon:
|
||||
color:yellow
|
||||
icon: "star"
|
||||
disabled: not(root.rankings["AverageScore"]/100*6>=6)
|
||||
RankingsLabel:
|
||||
adaptive_width:True
|
||||
text: '{}'.format(root.rankings["AverageScore"]/10)
|
||||
RankingsDivider:
|
||||
RankingsBoxLayout:
|
||||
size_hint_x:.3
|
||||
RankingsHeaderLabel:
|
||||
text:"Popularity"
|
||||
RankingsLabel:
|
||||
text: '{}'.format(root.rankings["Popularity"])
|
||||
RankingsDivider:
|
||||
RankingsBoxLayout:
|
||||
size_hint_x:.3
|
||||
RankingsHeaderLabel:
|
||||
text:"Favourites"
|
||||
RankingsLabel:
|
||||
text: '{}'.format(root.rankings["Favourites"])
|
||||
|
||||
|
||||
13
anixstream/View/AnimeScreen/components/rankings_bar.py
Normal file
13
anixstream/View/AnimeScreen/components/rankings_bar.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from kivy.properties import DictProperty
|
||||
|
||||
from kivymd.uix.boxlayout import MDBoxLayout
|
||||
|
||||
|
||||
class RankingsBar(MDBoxLayout):
|
||||
rankings = DictProperty(
|
||||
{
|
||||
"Popularity": 0,
|
||||
"Favourites": 0,
|
||||
"AverageScore": 0,
|
||||
}
|
||||
)
|
||||
50
anixstream/View/AnimeScreen/components/review.kv
Normal file
50
anixstream/View/AnimeScreen/components/review.kv
Normal file
@@ -0,0 +1,50 @@
|
||||
#:import get_hex_from_color kivy.utils.get_hex_from_color
|
||||
|
||||
|
||||
<ReviewContainer@MDBoxLayout>:
|
||||
adaptive_height:True
|
||||
md_bg_color:self.theme_cls.surfaceContainerLowColor
|
||||
padding:"10dp"
|
||||
orientation:"vertical"
|
||||
|
||||
<ReviewText@MDLabel>:
|
||||
adaptive_height:True
|
||||
max_lines:0
|
||||
shorten:False
|
||||
markup:True
|
||||
font_style: "Body"
|
||||
role: "small"
|
||||
|
||||
<ReviewHeader@MDBoxLayout>:
|
||||
adaptive_height:True
|
||||
spacing:"10dp"
|
||||
padding:"10dp"
|
||||
|
||||
<ReviewerAvatar@FitImage>
|
||||
radius:50
|
||||
size_hint:None,None
|
||||
height:"50dp"
|
||||
width:"50dp"
|
||||
|
||||
<AnimeReview>
|
||||
orientation:"vertical"
|
||||
adaptive_height:True
|
||||
ReviewHeader:
|
||||
ReviewerAvatar:
|
||||
source:root.review["avatar"]
|
||||
ReviewText:
|
||||
pos_hint: {'center_y': 0.5}
|
||||
text:root.review["username"]
|
||||
MDDivider:
|
||||
ReviewText:
|
||||
text:root.review["summary"]
|
||||
MDDivider:
|
||||
<AnimeReviews>:
|
||||
container:container
|
||||
adaptive_height:True
|
||||
orientation:"vertical"
|
||||
HeaderLabel:
|
||||
halign:"left"
|
||||
text:"reviews"
|
||||
ReviewContainer:
|
||||
id:container
|
||||
29
anixstream/View/AnimeScreen/components/review.py
Normal file
29
anixstream/View/AnimeScreen/components/review.py
Normal file
@@ -0,0 +1,29 @@
|
||||
from kivy.properties import ObjectProperty, ListProperty
|
||||
from kivy.clock import Clock
|
||||
|
||||
from kivymd.uix.boxlayout import MDBoxLayout
|
||||
|
||||
|
||||
class AnimeReview(MDBoxLayout):
|
||||
review = ObjectProperty({"username": "", "avatar": "", "summary": ""})
|
||||
|
||||
|
||||
class AnimeReviews(MDBoxLayout):
|
||||
"""anime reviews"""
|
||||
|
||||
reviews = ListProperty()
|
||||
container = ObjectProperty()
|
||||
|
||||
def on_reviews(self, *args):
|
||||
Clock.schedule_once(lambda _: self.update_reviews_card(*args))
|
||||
|
||||
def update_reviews_card(self, instance, reviews):
|
||||
self.container.clear_widgets()
|
||||
for review in reviews:
|
||||
review_ = AnimeReview()
|
||||
review_.review = {
|
||||
"username": review["user"]["name"],
|
||||
"avatar": review["user"]["avatar"]["medium"],
|
||||
"summary": review["summary"],
|
||||
}
|
||||
self.container.add_widget(review_)
|
||||
102
anixstream/View/AnimeScreen/components/side_bar.kv
Normal file
102
anixstream/View/AnimeScreen/components/side_bar.kv
Normal file
@@ -0,0 +1,102 @@
|
||||
#:import get_hex_from_color kivy.utils.get_hex_from_color
|
||||
|
||||
<FitBoxLayout@MDBoxLayout>:
|
||||
size_hint_y:None
|
||||
height:self.minimum_height
|
||||
padding:"10dp"
|
||||
spacing:"10dp"
|
||||
orientation: 'vertical'
|
||||
pos_hint: {'center_x': 0.5}
|
||||
<SideBarLabel>:
|
||||
adaptive_height:True
|
||||
max_lines:0
|
||||
shorten:False
|
||||
markup:True
|
||||
font_style: "Label"
|
||||
role: "medium"
|
||||
<HeaderLabel>:
|
||||
adaptive_height:True
|
||||
md_bg_color:self.theme_cls.secondaryContainerColor
|
||||
MDLabel:
|
||||
text:root.text
|
||||
adaptive_height:True
|
||||
halign:root.halign
|
||||
max_lines:0
|
||||
shorten:False
|
||||
bold:True
|
||||
font_style: "Label"
|
||||
role: "large"
|
||||
padding:"10dp"
|
||||
|
||||
<AnimeSideBar>:
|
||||
size_hint_x: None
|
||||
width: dp(300)
|
||||
orientation: 'vertical'
|
||||
line_color:self.theme_cls.secondaryColor
|
||||
statistics_container:statistics_container
|
||||
tags_container:tags_container
|
||||
external_links_container:external_links_container
|
||||
FitBoxLayout:
|
||||
FitImage:
|
||||
source:root.image
|
||||
size_hint:None,None
|
||||
height:dp(250)
|
||||
width:dp(200)
|
||||
pos_hint: {'center_x': 0.5}
|
||||
MDButton:
|
||||
pos_hint: {'center_x': 0.5}
|
||||
on_press:
|
||||
if root.screen:root.screen.stream_anime_with_custom_cmds_dialog(mpv=True)
|
||||
MDButtonText:
|
||||
text:"Watch with mpv"
|
||||
FitBoxLayout:
|
||||
HeaderLabel:
|
||||
text:"Alternative Titles"
|
||||
SideBarLabel:
|
||||
text: "[color={}]Synonyms:[/color] {}".format(get_hex_from_color(self.theme_cls.primaryColor),root.alternative_titles["synonyms"])
|
||||
SideBarLabel:
|
||||
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:
|
||||
HeaderLabel:
|
||||
text:"Information"
|
||||
SideBarLabel:
|
||||
text: "[color={}]Episodes:[/color] {}".format(get_hex_from_color(self.theme_cls.primaryColor),root.information["episodes"])
|
||||
SideBarLabel:
|
||||
text: "[color={}]Status:[/color] {}".format(get_hex_from_color(self.theme_cls.primaryColor),root.information["status"])
|
||||
SideBarLabel:
|
||||
text: "[color={}]Next Airing Episode:[/color] {}".format(get_hex_from_color(self.theme_cls.primaryColor),root.information["nextAiringEpisode"])
|
||||
SideBarLabel:
|
||||
text: "[color={}]Aired:[/color] {}".format(get_hex_from_color(self.theme_cls.primaryColor),root.information["aired"])
|
||||
SideBarLabel:
|
||||
text: "[color={}]Premiered:[/color] {}".format(get_hex_from_color(self.theme_cls.primaryColor),root.information["premiered"])
|
||||
SideBarLabel:
|
||||
text: "[color={}]Broadcast:[/color] {}".format(get_hex_from_color(self.theme_cls.primaryColor),root.information["broadcast"])
|
||||
SideBarLabel:
|
||||
text: "[color={}]Country Of Origin:[/color] {}".format(get_hex_from_color(self.theme_cls.primaryColor),root.information["countryOfOrigin"])
|
||||
SideBarLabel:
|
||||
text: "[color={}]Hashtag:[/color] {}".format(get_hex_from_color(self.theme_cls.primaryColor),root.information["hashtag"])
|
||||
SideBarLabel:
|
||||
text: "[color={}]Studios:[/color] {}".format(get_hex_from_color(self.theme_cls.primaryColor),root.information["studios"])
|
||||
SideBarLabel:
|
||||
text: "[color={}]Producers:[/color] {}".format(get_hex_from_color(self.theme_cls.primaryColor),root.information["producers"])
|
||||
SideBarLabel:
|
||||
text: "[color={}]Source:[/color] {}".format(get_hex_from_color(self.theme_cls.primaryColor),root.information["source"])
|
||||
SideBarLabel:
|
||||
text: "[color={}]Genres:[/color] {}".format(get_hex_from_color(self.theme_cls.primaryColor),root.information["genres"])
|
||||
SideBarLabel:
|
||||
text: "[color={}]Duration:[/color] {} minutes".format(get_hex_from_color(self.theme_cls.primaryColor),root.information["duration"])
|
||||
FitBoxLayout:
|
||||
id:statistics_container
|
||||
HeaderLabel:
|
||||
text:"Rankings"
|
||||
FitBoxLayout:
|
||||
id:tags_container
|
||||
HeaderLabel:
|
||||
text:"Tags"
|
||||
FitBoxLayout:
|
||||
id:external_links_container
|
||||
HeaderLabel:
|
||||
text:"External Links"
|
||||
BoxLayout:
|
||||
98
anixstream/View/AnimeScreen/components/side_bar.py
Normal file
98
anixstream/View/AnimeScreen/components/side_bar.py
Normal file
@@ -0,0 +1,98 @@
|
||||
from kivy.properties import ObjectProperty, StringProperty, DictProperty, ListProperty
|
||||
from kivy.utils import get_hex_from_color
|
||||
from kivy.factory import Factory
|
||||
|
||||
from kivymd.uix.boxlayout import MDBoxLayout
|
||||
from kivymd.uix.label import MDLabel
|
||||
|
||||
|
||||
class HeaderLabel(MDBoxLayout):
|
||||
text = StringProperty()
|
||||
halign = StringProperty("center")
|
||||
|
||||
|
||||
Factory.register("HeaderLabel", HeaderLabel)
|
||||
|
||||
|
||||
class SideBarLabel(MDLabel):
|
||||
pass
|
||||
|
||||
|
||||
# TODO:Switch to using the kivy_markup_module
|
||||
class AnimeSideBar(MDBoxLayout):
|
||||
screen = ObjectProperty()
|
||||
image = StringProperty()
|
||||
alternative_titles = DictProperty(
|
||||
{
|
||||
"synonyms": "",
|
||||
"english": "",
|
||||
"japanese": "",
|
||||
}
|
||||
)
|
||||
information = DictProperty(
|
||||
{
|
||||
"episodes": "",
|
||||
"status": "",
|
||||
"aired": "",
|
||||
"nextAiringEpisode": "",
|
||||
"premiered": "",
|
||||
"broadcast": "",
|
||||
"countryOfOrigin": "",
|
||||
"hashtag": "",
|
||||
"studios": "", # { "name": "Sunrise", "isAnimationStudio": true }
|
||||
"source": "",
|
||||
"genres": "",
|
||||
"duration": "",
|
||||
"producers": "",
|
||||
}
|
||||
)
|
||||
statistics = ListProperty()
|
||||
statistics_container = ObjectProperty()
|
||||
external_links = ListProperty()
|
||||
external_links_container = ObjectProperty()
|
||||
tags = ListProperty()
|
||||
tags_container = ObjectProperty()
|
||||
|
||||
def on_statistics(self, instance, value):
|
||||
self.statistics_container.clear_widgets()
|
||||
header = HeaderLabel()
|
||||
header.text = "Rankings"
|
||||
self.statistics_container.add_widget(header)
|
||||
for stat in value:
|
||||
# stat (rank,context)
|
||||
label = SideBarLabel()
|
||||
label.text = "[color={}]{}:[/color] {}".format(
|
||||
get_hex_from_color(label.theme_cls.primaryColor),
|
||||
stat[0].capitalize(),
|
||||
f"{stat[1]}",
|
||||
)
|
||||
self.statistics_container.add_widget(label)
|
||||
|
||||
def on_tags(self, instance, value):
|
||||
self.tags_container.clear_widgets()
|
||||
header = HeaderLabel()
|
||||
header.text = "Tags"
|
||||
self.tags_container.add_widget(header)
|
||||
for tag in value:
|
||||
label = SideBarLabel()
|
||||
label.text = "[color={}]{}:[/color] {}".format(
|
||||
get_hex_from_color(label.theme_cls.primaryColor),
|
||||
tag[0].capitalize(),
|
||||
f"{tag[1]} %",
|
||||
)
|
||||
self.tags_container.add_widget(label)
|
||||
|
||||
def on_external_links(self, instance, value):
|
||||
self.external_links_container.clear_widgets()
|
||||
header = HeaderLabel()
|
||||
header.text = "External Links"
|
||||
self.external_links_container.add_widget(header)
|
||||
for site in value:
|
||||
# stat (rank,context)
|
||||
label = SideBarLabel()
|
||||
label.text = "[color={}]{}:[/color] {}".format(
|
||||
get_hex_from_color(label.theme_cls.primaryColor),
|
||||
site[0].capitalize(),
|
||||
site[1],
|
||||
)
|
||||
self.external_links_container.add_widget(label)
|
||||
21
anixstream/View/CrashLogScreen/crashlog_screen.kv
Normal file
21
anixstream/View/CrashLogScreen/crashlog_screen.kv
Normal file
@@ -0,0 +1,21 @@
|
||||
#:import get_color_from_hex kivy.utils.get_color_from_hex
|
||||
#:import StringProperty kivy.properties.StringProperty
|
||||
|
||||
<CrashLogScreenView>
|
||||
md_bg_color: self.theme_cls.backgroundColor
|
||||
# main_container:main_container
|
||||
MDBoxLayout:
|
||||
NavRail:
|
||||
screen:root
|
||||
MDAnchorLayout:
|
||||
anchor_y: 'top'
|
||||
padding:"10dp"
|
||||
MDBoxLayout:
|
||||
orientation: 'vertical'
|
||||
SearchBar:
|
||||
MDScrollView:
|
||||
MDLabel:
|
||||
text:root.crash_text
|
||||
adaptive_height:True
|
||||
markup:True
|
||||
padding:"10dp"
|
||||
21
anixstream/View/CrashLogScreen/crashlog_screen.py
Normal file
21
anixstream/View/CrashLogScreen/crashlog_screen.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from kivy.properties import StringProperty
|
||||
|
||||
from View.base_screen import BaseScreenView
|
||||
from Utility.utils import read_crash_file
|
||||
from Utility.kivy_markup_helper import color_text, bolden
|
||||
|
||||
|
||||
class CrashLogScreenView(BaseScreenView):
|
||||
"""The crash log screen"""
|
||||
|
||||
crash_text = StringProperty()
|
||||
|
||||
def __init__(self, **kw):
|
||||
super().__init__(**kw)
|
||||
if crashes := read_crash_file():
|
||||
self.crash_text = crashes
|
||||
else:
|
||||
self.crash_text = color_text(
|
||||
f"No Crashes so far :) and if there are any in the future {bolden('please report! Okay?')}",
|
||||
self.theme_cls.primaryColor,
|
||||
)
|
||||
28
anixstream/View/DownloadsScreen/components/task_card.kv
Normal file
28
anixstream/View/DownloadsScreen/components/task_card.kv
Normal file
@@ -0,0 +1,28 @@
|
||||
#:import color_text Utility.kivy_markup_helper.color_text
|
||||
|
||||
<TaskText@MDLabel>:
|
||||
adaptive_height:True
|
||||
max_lines:0
|
||||
shorten:False
|
||||
markup:True
|
||||
font_style: "Label"
|
||||
role: "large"
|
||||
bold:True
|
||||
|
||||
|
||||
<TaskCard>:
|
||||
adaptive_height:True
|
||||
radius:8
|
||||
padding:"20dp"
|
||||
md_bg_color:self.theme_cls.surfaceContainerHighColor
|
||||
|
||||
TaskText:
|
||||
size_hint_x:.8
|
||||
text:color_text(root.anime_task_name,root.theme_cls.primaryColor)
|
||||
TaskText:
|
||||
size_hint_x:.2
|
||||
# color:self.theme_cls.surfaceDimColor
|
||||
theme_text_color:"Secondary"
|
||||
text:color_text(root.episodes_to_download,root.theme_cls.secondaryColor)
|
||||
MDIcon:
|
||||
icon:"download"
|
||||
12
anixstream/View/DownloadsScreen/components/task_card.py
Normal file
12
anixstream/View/DownloadsScreen/components/task_card.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from kivy.properties import StringProperty
|
||||
|
||||
from kivymd.uix.boxlayout import MDBoxLayout
|
||||
|
||||
# TODO: add a progress bar to show the individual progress of each task
|
||||
class TaskCard(MDBoxLayout):
|
||||
anime_task_name = StringProperty()
|
||||
episodes_to_download = StringProperty()
|
||||
def __init__(self, anime_title:str, episodes:str, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.anime_task_name = f"{anime_title}"
|
||||
self.episodes_to_download = f"Episodes: {episodes}"
|
||||
58
anixstream/View/DownloadsScreen/download_screen.kv
Normal file
58
anixstream/View/DownloadsScreen/download_screen.kv
Normal file
@@ -0,0 +1,58 @@
|
||||
#:import get_color_from_hex kivy.utils.get_color_from_hex
|
||||
#:import StringProperty kivy.properties.StringProperty
|
||||
|
||||
<DownloadsScreenLabel@MDLabel>:
|
||||
adaptive_height:True
|
||||
max_lines:0
|
||||
shorten:False
|
||||
markup:True
|
||||
font_style: "Label"
|
||||
role: "large"
|
||||
bold:True
|
||||
<DownloadsScreenView>
|
||||
md_bg_color: self.theme_cls.backgroundColor
|
||||
main_container:main_container
|
||||
download_progress_label:download_progress_label
|
||||
progress_bar:progress_bar
|
||||
MDBoxLayout:
|
||||
NavRail:
|
||||
screen:root
|
||||
MDAnchorLayout:
|
||||
anchor_y: 'top'
|
||||
MDBoxLayout:
|
||||
orientation: 'vertical'
|
||||
SearchBar:
|
||||
MDScrollView:
|
||||
size_hint:.95,1
|
||||
MDBoxLayout:
|
||||
id:main_container
|
||||
orientation:"vertical"
|
||||
padding:"40dp"
|
||||
pos_hint:{"center_x":.5}
|
||||
spacing:"10dp"
|
||||
adaptive_height:True
|
||||
HeaderLabel:
|
||||
text:"Download Tasks"
|
||||
halign:"left"
|
||||
MDIcon:
|
||||
padding:"10dp"
|
||||
pos_hint:{"center_y":.5}
|
||||
icon:"clock"
|
||||
MDBoxLayout:
|
||||
size_hint_y:None
|
||||
height:"40dp"
|
||||
spacing:"10dp"
|
||||
padding:"10dp"
|
||||
md_bg_color:self.theme_cls.secondaryContainerColor
|
||||
DownloadsScreenLabel:
|
||||
id:download_progress_label
|
||||
size_hint_x: .6
|
||||
text:"Try Downloading sth :)"
|
||||
pos_hint: {'center_y': .5}
|
||||
MDLinearProgressIndicator:
|
||||
id: progress_bar
|
||||
size_hint_x: .4
|
||||
size_hint_y:None
|
||||
height:"10dp"
|
||||
type: "determinate"
|
||||
pos_hint: {'center_y': .5}
|
||||
32
anixstream/View/DownloadsScreen/download_screen.py
Normal file
32
anixstream/View/DownloadsScreen/download_screen.py
Normal file
@@ -0,0 +1,32 @@
|
||||
from kivy.clock import Clock
|
||||
from kivy.properties import ObjectProperty
|
||||
from kivy.logger import Logger
|
||||
from kivy.utils import format_bytes_to_human
|
||||
|
||||
from View.base_screen import BaseScreenView
|
||||
from .components.task_card import TaskCard
|
||||
|
||||
|
||||
class DownloadsScreenView(BaseScreenView):
|
||||
main_container = ObjectProperty()
|
||||
progress_bar = ObjectProperty()
|
||||
download_progress_label = ObjectProperty()
|
||||
|
||||
def on_new_download_task(self, anime_title: str, episodes: str | None):
|
||||
if not episodes:
|
||||
episodes = "All"
|
||||
Clock.schedule_once(
|
||||
lambda _: self.main_container.add_widget(TaskCard(anime_title, episodes))
|
||||
)
|
||||
|
||||
def on_episode_download_progress(
|
||||
self, current_bytes_downloaded, total_bytes, episode_info
|
||||
):
|
||||
percentage_completion = round((current_bytes_downloaded / total_bytes) * 100)
|
||||
progress_text = f"Downloading: {episode_info['anime_title']} - {episode_info['episode']} ({format_bytes_to_human(current_bytes_downloaded)}/{format_bytes_to_human(total_bytes)})"
|
||||
if (percentage_completion % 5) == 0:
|
||||
self.progress_bar.value = max(min(percentage_completion, 100), 0)
|
||||
self.download_progress_label.text = progress_text
|
||||
|
||||
def update_layout(self, widget):
|
||||
self.user_anime_list_container.add_widget(widget)
|
||||
60
anixstream/View/HelpScreen/help_screen.kv
Normal file
60
anixstream/View/HelpScreen/help_screen.kv
Normal file
@@ -0,0 +1,60 @@
|
||||
#:import get_color_from_hex kivy.utils.get_color_from_hex
|
||||
#:import StringProperty kivy.properties.StringProperty
|
||||
|
||||
<HelpCard@MDBoxLayout>
|
||||
spacing:"10dp"
|
||||
orientation:"vertical"
|
||||
adaptive_height:True
|
||||
md_bg_color:self.theme_cls.surfaceContainerLowColor
|
||||
theme_text_color:"Secondary"
|
||||
|
||||
<HelpHeaderLabel@HeaderLabel>
|
||||
halign:"left"
|
||||
|
||||
<HelpDescription@MDLabel>:
|
||||
adaptive_height:True
|
||||
max_lines:0
|
||||
shorten:False
|
||||
markup:True
|
||||
font_style: "Body"
|
||||
padding:"10dp"
|
||||
role: "large"
|
||||
|
||||
<HelpScreenView>
|
||||
md_bg_color: self.theme_cls.backgroundColor
|
||||
# main_container:main_container
|
||||
MDBoxLayout:
|
||||
NavRail:
|
||||
screen:root
|
||||
MDAnchorLayout:
|
||||
anchor_y: 'top'
|
||||
padding:"10dp"
|
||||
MDBoxLayout:
|
||||
orientation: 'vertical'
|
||||
SearchBar:
|
||||
MDScrollView:
|
||||
size_hint_x:.95
|
||||
MDBoxLayout:
|
||||
adaptive_height:True
|
||||
padding:"10dp"
|
||||
orientation:"vertical"
|
||||
HelpCard:
|
||||
HelpHeaderLabel:
|
||||
text:"Animdl Commands"
|
||||
HelpDescription:
|
||||
text:root.animdl_help
|
||||
HelpCard:
|
||||
HelpHeaderLabel:
|
||||
text:"Installing Animdl"
|
||||
HelpDescription:
|
||||
text:root.installing_animdl_help
|
||||
HelpCard:
|
||||
HelpHeaderLabel:
|
||||
text:"Available Themes"
|
||||
HelpDescription:
|
||||
text:root.available_themes
|
||||
HelpCard:
|
||||
HelpHeaderLabel:
|
||||
text:"About"
|
||||
HelpDescription:
|
||||
text:"This app was made to be a gui wrapper for any and all anime cli tools. Inoder to solve the age old problem of getting the same experience from the cli as you would in a website"
|
||||
72
anixstream/View/HelpScreen/help_screen.py
Normal file
72
anixstream/View/HelpScreen/help_screen.py
Normal file
@@ -0,0 +1,72 @@
|
||||
from kivy.properties import ObjectProperty, StringProperty
|
||||
|
||||
from View.base_screen import BaseScreenView
|
||||
from Utility.kivy_markup_helper import bolden, color_text, underline
|
||||
from Utility.data import themes_available
|
||||
|
||||
|
||||
class HelpScreenView(BaseScreenView):
|
||||
main_container = ObjectProperty()
|
||||
animdl_help = StringProperty()
|
||||
installing_animdl_help = StringProperty()
|
||||
available_themes = StringProperty()
|
||||
|
||||
def __init__(self, **kw):
|
||||
super(HelpScreenView, self).__init__(**kw)
|
||||
self.animdl_help = f"""
|
||||
{underline(color_text(bolden('Streaming Commands'),self.theme_cls.surfaceBrightColor))}
|
||||
{color_text(bolden('-r:'),self.theme_cls.primaryFixedDimColor)} specifies the range of episodes
|
||||
example: {color_text(('animdl stream <Anime title> -r 1-4'),self.theme_cls.tertiaryColor)}
|
||||
explanation:in this case gets 4 episodes 1 to 4
|
||||
{color_text(('-s:'),self.theme_cls.primaryFixedDimColor)} special selector for the most recent episodes or basically selects from the end
|
||||
example: {color_text(('animdl stream <Anime title> -s 4'),self.theme_cls.tertiaryColor)}
|
||||
explanation: in this case gets the latest 4 episodes
|
||||
{color_text(('-q:'),self.theme_cls.primaryFixedDimColor)} sets the quality of the stream
|
||||
example: {color_text(('animdl stream <Anime title> -q best'),self.theme_cls.tertiaryColor)}
|
||||
explanation: The quality of the anime stream should be the best possible others include 1080,720... plus worst
|
||||
{underline(color_text(bolden('Downloading Commands'),self.theme_cls.surfaceBrightColor))}
|
||||
{color_text(bolden('-r:'),self.theme_cls.primaryFixedDimColor)} specifies the range of episodes
|
||||
example: {color_text(('animdl download <Anime title> -r 1-4'),self.theme_cls.tertiaryColor)}
|
||||
explanation:in this case gets 4 episodes 1 to 4
|
||||
{color_text(('-s:'),self.theme_cls.primaryFixedDimColor)} special selector for the most recent episodes or basically selects from the end
|
||||
example: {color_text(('animdl download <Anime title> -s 4'),self.theme_cls.tertiaryColor)}
|
||||
explanation: in this case gets the latest 4 episodes
|
||||
{color_text(('-q:'),self.theme_cls.primaryFixedDimColor)} sets the quality of the download
|
||||
example: {color_text(('animdl download <Anime title> -q best'),self.theme_cls.tertiaryColor)}
|
||||
explanation: The quality of the anime download should be the best possible others include 1080,720... plus worst
|
||||
"""
|
||||
self.installing_animdl_help = f"""
|
||||
This works on windows only and should be done in powershell
|
||||
1. First install pyenv with the following command:
|
||||
{color_text(('Invoke-WebRequest -UseBasicParsing -Uri "https://raw.githubusercontent.com/pyenv-win/pyenv-win/master/pyenv-win/install-pyenv-win.ps1" -OutFile "./install-pyenv-win.ps1"; &"./install-pyenv-win.ps1"'),self.theme_cls.tertiaryColor)}
|
||||
2. run the following command to check if successsful:
|
||||
{color_text(('pyenv --version '),self.theme_cls.tertiaryColor)}
|
||||
3. run the following command to install python 3.10
|
||||
{color_text(('pyenv install 3.10'),self.theme_cls.tertiaryColor)}
|
||||
4. To confirm successful install of python 3.10 run the following command and check if 3.10 is listed:
|
||||
{color_text(('pyenv -l'),self.theme_cls.tertiaryColor)}
|
||||
5. Next run:
|
||||
{color_text(('pyenv local 3.10'),self.theme_cls.tertiaryColor)} (if in anixstream directory to set python 3.10 as local interpreter)
|
||||
or run:
|
||||
{color_text(('pyenv global 3.10'),self.theme_cls.tertiaryColor)} (if in another directory to set python version 3.10 as global interpreter)
|
||||
6. Check if success by running and checking if output is 3.10:
|
||||
{color_text(('python --version'),self.theme_cls.tertiaryColor)}
|
||||
7. Run:
|
||||
{color_text(('python -m pip install animdl'),self.theme_cls.tertiaryColor)}
|
||||
8. Check if success by running:
|
||||
{color_text(('python -m animdl'),self.theme_cls.tertiaryColor)}
|
||||
{color_text(('Note:'),self.theme_cls.secondaryColor)}
|
||||
All this instructions should be done from the folder you choose to install
|
||||
aniXstream but incase you have never installed python should work any where
|
||||
{bolden('-----------------------------')}
|
||||
Now enjoy :)
|
||||
{bolden('-----------------------------')}
|
||||
"""
|
||||
self.available_themes = "\n".join(themes_available)
|
||||
|
||||
def model_is_changed(self) -> None:
|
||||
"""
|
||||
Called whenever any change has occurred in the data model.
|
||||
The view in this method tracks these changes and updates the UI
|
||||
according to these changes.
|
||||
"""
|
||||
0
anixstream/View/HomeScreen/__init__.py
Normal file
0
anixstream/View/HomeScreen/__init__.py
Normal file
0
anixstream/View/HomeScreen/components/__init__.py
Normal file
0
anixstream/View/HomeScreen/components/__init__.py
Normal file
23
anixstream/View/HomeScreen/home_screen.kv
Normal file
23
anixstream/View/HomeScreen/home_screen.kv
Normal file
@@ -0,0 +1,23 @@
|
||||
<HomeScreenView>
|
||||
md_bg_color: self.theme_cls.backgroundColor
|
||||
main_container:main_container
|
||||
MDBoxLayout:
|
||||
NavRail:
|
||||
screen:root
|
||||
MDAnchorLayout:
|
||||
anchor_y: 'top'
|
||||
padding:"10dp"
|
||||
MDBoxLayout:
|
||||
orientation: 'vertical'
|
||||
id:p
|
||||
SearchBar:
|
||||
MDScrollView:
|
||||
size_hint:1,1
|
||||
MDBoxLayout:
|
||||
id:main_container
|
||||
padding:"50dp","5dp","50dp","150dp"
|
||||
spacing:"10dp"
|
||||
orientation: 'vertical'
|
||||
size_hint_y:None
|
||||
height:max(self.minimum_height,p.height,1800)
|
||||
|
||||
7
anixstream/View/HomeScreen/home_screen.py
Normal file
7
anixstream/View/HomeScreen/home_screen.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from kivy.properties import ObjectProperty
|
||||
|
||||
from View.base_screen import BaseScreenView
|
||||
|
||||
|
||||
class HomeScreenView(BaseScreenView):
|
||||
main_container = ObjectProperty()
|
||||
28
anixstream/View/MylistScreen/my_list_screen.kv
Normal file
28
anixstream/View/MylistScreen/my_list_screen.kv
Normal file
@@ -0,0 +1,28 @@
|
||||
<MyListScreenView>
|
||||
md_bg_color: self.theme_cls.backgroundColor
|
||||
user_anime_list_container:user_anime_list_container
|
||||
MDBoxLayout:
|
||||
size_hint:1,1
|
||||
NavRail:
|
||||
screen:root
|
||||
MDAnchorLayout:
|
||||
anchor_y: 'top'
|
||||
padding:"10dp"
|
||||
size_hint:1,1
|
||||
MDBoxLayout:
|
||||
spacing:"40dp"
|
||||
orientation: 'vertical'
|
||||
size_hint:.95,1
|
||||
SearchBar:
|
||||
MDScrollView:
|
||||
pos_hint:{"center_x":.5}
|
||||
size_hint:1,1
|
||||
MDGridLayout:
|
||||
spacing: '40dp'
|
||||
padding: "100dp","50dp","10dp","200dp"
|
||||
id:user_anime_list_container
|
||||
cols:4 if root.width<=1100 else 5
|
||||
size_hint_y:None
|
||||
height:self.minimum_height
|
||||
|
||||
|
||||
21
anixstream/View/MylistScreen/my_list_screen.py
Normal file
21
anixstream/View/MylistScreen/my_list_screen.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from kivy.properties import ObjectProperty, StringProperty, DictProperty
|
||||
from kivy.clock import Clock
|
||||
|
||||
from View.base_screen import BaseScreenView
|
||||
|
||||
|
||||
class MyListScreenView(BaseScreenView):
|
||||
user_anime_list_container = ObjectProperty()
|
||||
|
||||
def model_is_changed(self) -> None:
|
||||
"""
|
||||
Called whenever any change has occurred in the data model.
|
||||
The view in this method tracks these changes and updates the UI
|
||||
according to these changes.
|
||||
"""
|
||||
|
||||
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)
|
||||
3
anixstream/View/SearchScreen/components/__init__.py
Normal file
3
anixstream/View/SearchScreen/components/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from .filters import Filters
|
||||
from .pagination import SearchResultsPagination
|
||||
from .trending_sidebar import TrendingAnimeSideBar
|
||||
27
anixstream/View/SearchScreen/components/filters.kv
Normal file
27
anixstream/View/SearchScreen/components/filters.kv
Normal file
@@ -0,0 +1,27 @@
|
||||
<FilterDropDown>:
|
||||
MDDropDownItemText:
|
||||
text: root.text
|
||||
|
||||
<FilterLabel@MDLabel>:
|
||||
adaptive_width:True
|
||||
|
||||
<Filters>:
|
||||
adaptive_height:True
|
||||
spacing:"10dp"
|
||||
size_hint_x:.95
|
||||
pos_hint:{"center_x":.5}
|
||||
padding:"10dp"
|
||||
md_bg_color:self.theme_cls.surfaceContainerLowColor
|
||||
|
||||
FilterLabel:
|
||||
text:"Sort By"
|
||||
FilterDropDown:
|
||||
id:sort_filter
|
||||
text:root.filters["sort"]
|
||||
on_release: root.open_filter_menu(self,"sort")
|
||||
FilterLabel:
|
||||
text:"Status"
|
||||
FilterDropDown:
|
||||
id:status_filter
|
||||
text:root.filters["status"]
|
||||
on_release: root.open_filter_menu(self,"status")
|
||||
83
anixstream/View/SearchScreen/components/filters.py
Normal file
83
anixstream/View/SearchScreen/components/filters.py
Normal file
@@ -0,0 +1,83 @@
|
||||
from kivy.properties import StringProperty, DictProperty
|
||||
|
||||
from kivymd.uix.boxlayout import MDBoxLayout
|
||||
from kivymd.uix.dropdownitem import MDDropDownItem
|
||||
from kivymd.uix.menu import MDDropdownMenu
|
||||
|
||||
|
||||
class FilterDropDown(MDDropDownItem):
|
||||
text: str = StringProperty()
|
||||
|
||||
|
||||
class Filters(MDBoxLayout):
|
||||
filters: dict = DictProperty({"sort": "SEARCH_MATCH", "status": "FINISHED"})
|
||||
|
||||
def open_filter_menu(self, menu_item, filter_name):
|
||||
items = []
|
||||
match filter_name:
|
||||
case "sort":
|
||||
items = [
|
||||
"ID",
|
||||
"ID_DESC",
|
||||
"TITLE_ROMANJI",
|
||||
"TITLE_ROMANJI_DESC",
|
||||
"TITLE_ENGLISH",
|
||||
"TITLE_ENGLISH_DESC",
|
||||
"TITLE_NATIVE",
|
||||
"TITLE_NATIVE_DESC",
|
||||
"TYPE",
|
||||
"TYPE_DESC",
|
||||
"FORMAT",
|
||||
"FORMAT_DESC",
|
||||
"START_DATE",
|
||||
"START_DATE_DESC",
|
||||
"END_DATE",
|
||||
"END_DATE_DESC",
|
||||
"SCORE",
|
||||
"SCORE_DESC",
|
||||
"TRENDING",
|
||||
"TRENDING_DESC",
|
||||
"EPISODES",
|
||||
"EPISODES_DESC",
|
||||
"DURATION",
|
||||
"DURATION_DESC",
|
||||
"STATUS",
|
||||
"STATUS_DESC",
|
||||
"UPDATED_AT",
|
||||
"UPDATED_AT_DESC",
|
||||
"SEARCH_MATCH",
|
||||
"POPULARITY",
|
||||
"POPULARITY_DESC",
|
||||
"FAVOURITES",
|
||||
"FAVOURITES_DESC",
|
||||
]
|
||||
case "status":
|
||||
items = [
|
||||
"FINISHED",
|
||||
"RELEASING",
|
||||
"NOT_YET_RELEASED",
|
||||
"CANCELLED",
|
||||
"HIATUS",
|
||||
]
|
||||
case _:
|
||||
items = []
|
||||
if items:
|
||||
menu_items = [
|
||||
{
|
||||
"text": f"{item}",
|
||||
"on_release": lambda filter_value=f"{item}": self.filter_menu_callback(
|
||||
filter_name, filter_value
|
||||
),
|
||||
}
|
||||
for item in items
|
||||
]
|
||||
MDDropdownMenu(caller=menu_item, items=menu_items).open()
|
||||
|
||||
def filter_menu_callback(self, filter_name, filter_value):
|
||||
match filter_name:
|
||||
case "sort":
|
||||
self.ids.sort_filter.text = filter_value
|
||||
self.filters["sort"] = filter_value
|
||||
case "status":
|
||||
self.ids.status_filter.text = filter_value
|
||||
self.filters["status"] = filter_value
|
||||
21
anixstream/View/SearchScreen/components/pagination.kv
Normal file
21
anixstream/View/SearchScreen/components/pagination.kv
Normal file
@@ -0,0 +1,21 @@
|
||||
<PaginationLabel@MDLabel>:
|
||||
max_lines:0
|
||||
shorten:False
|
||||
adaptive_height:True
|
||||
font_style: "Label"
|
||||
pos_hint:{"center_y":.5}
|
||||
halign:"center"
|
||||
role: "medium"
|
||||
|
||||
<SearchResultsPagination>:
|
||||
md_bg_color:self.theme_cls.surfaceContainerLowColor
|
||||
radius:8
|
||||
adaptive_height:True
|
||||
MDIconButton:
|
||||
icon:"arrow-left"
|
||||
on_release:root.search_view.previous_page()
|
||||
PaginationLabel:
|
||||
text:"Page {} of {}".format(root.current_page,root.total_pages)
|
||||
MDIconButton:
|
||||
icon:"arrow-right"
|
||||
on_release:root.search_view.next_page()
|
||||
9
anixstream/View/SearchScreen/components/pagination.py
Normal file
9
anixstream/View/SearchScreen/components/pagination.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from kivy.properties import ObjectProperty, NumericProperty
|
||||
|
||||
from kivymd.uix.boxlayout import MDBoxLayout
|
||||
|
||||
|
||||
class SearchResultsPagination(MDBoxLayout):
|
||||
current_page = NumericProperty()
|
||||
total_pages = NumericProperty()
|
||||
search_view = ObjectProperty()
|
||||
@@ -0,0 +1,6 @@
|
||||
<TrendingAnimeSideBar>:
|
||||
orientation: 'vertical'
|
||||
adaptive_height:True
|
||||
md_bg_color:self.theme_cls.surfaceContainerLowColor
|
||||
pos_hint: {'center_x': 0.5}
|
||||
padding:"25dp","25dp","25dp","200dp"
|
||||
@@ -0,0 +1,5 @@
|
||||
from kivymd.uix.boxlayout import MDBoxLayout
|
||||
|
||||
|
||||
class TrendingAnimeSideBar(MDBoxLayout):
|
||||
pass
|
||||
58
anixstream/View/SearchScreen/search_screen.kv
Normal file
58
anixstream/View/SearchScreen/search_screen.kv
Normal file
@@ -0,0 +1,58 @@
|
||||
<SearchScreenView>
|
||||
md_bg_color: self.theme_cls.backgroundColor
|
||||
search_results_container:search_results_container
|
||||
trending_anime_sidebar:trending_anime_sidebar
|
||||
search_results_pagination:search_results_pagination
|
||||
filters:filters
|
||||
MDBoxLayout:
|
||||
size_hint:1,1
|
||||
NavRail:
|
||||
screen:root
|
||||
MDAnchorLayout:
|
||||
anchor_y: 'top'
|
||||
padding:"10dp"
|
||||
size_hint:1,1
|
||||
|
||||
MDBoxLayout:
|
||||
orientation: 'vertical'
|
||||
size_hint:1,1
|
||||
SearchBar:
|
||||
MDBoxLayout:
|
||||
spacing:"20dp"
|
||||
padding:"75dp","10dp","100dp","0dp"
|
||||
MDBoxLayout:
|
||||
orientation: 'vertical'
|
||||
size_hint:1,1
|
||||
Filters:
|
||||
id:filters
|
||||
MDBoxLayout:
|
||||
spacing:"20dp"
|
||||
MDScrollView:
|
||||
size_hint:1,1
|
||||
MDBoxLayout:
|
||||
orientation: 'vertical'
|
||||
size_hint_y:None
|
||||
height:max(self.parent.parent.height,self.minimum_height)
|
||||
MDGridLayout:
|
||||
pos_hint: {'center_x': 0.5}
|
||||
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)
|
||||
SearchResultsPagination:
|
||||
id:search_results_pagination
|
||||
search_view:root
|
||||
MDBoxLayout:
|
||||
orientation:"vertical"
|
||||
size_hint_y:1
|
||||
size_hint_x:None
|
||||
width: dp(250)
|
||||
HeaderLabel:
|
||||
text:"Trending"
|
||||
MDScrollView:
|
||||
TrendingAnimeSideBar:
|
||||
id:trending_anime_sidebar
|
||||
height:max(self.parent.parent.height,self.minimum_height)
|
||||
|
||||
67
anixstream/View/SearchScreen/search_screen.py
Normal file
67
anixstream/View/SearchScreen/search_screen.py
Normal file
@@ -0,0 +1,67 @@
|
||||
from kivy.properties import ObjectProperty, StringProperty
|
||||
from kivy.clock import Clock
|
||||
|
||||
from View.base_screen import BaseScreenView
|
||||
from .components import TrendingAnimeSideBar, Filters, SearchResultsPagination
|
||||
|
||||
|
||||
class SearchScreenView(BaseScreenView):
|
||||
trending_anime_sidebar: TrendingAnimeSideBar = ObjectProperty()
|
||||
search_results_pagination: SearchResultsPagination = ObjectProperty()
|
||||
filters: Filters = ObjectProperty()
|
||||
|
||||
search_results_container = ObjectProperty()
|
||||
search_term: str = StringProperty()
|
||||
is_searching = False
|
||||
has_next_page = False
|
||||
current_page = 0
|
||||
total_pages = 0
|
||||
|
||||
def handle_search_for_anime(self, search_widget=None, page=None):
|
||||
if search_widget:
|
||||
search_term = search_widget.text
|
||||
elif page:
|
||||
search_term = self.search_term
|
||||
else:
|
||||
return
|
||||
|
||||
if search_term and not (self.is_searching):
|
||||
self.search_term = search_term
|
||||
self.search_results_container.clear_widgets()
|
||||
if filters := self.filters.filters:
|
||||
Clock.schedule_once(
|
||||
lambda _: self.controller.requested_search_for_anime(
|
||||
search_term, **filters, page=page
|
||||
)
|
||||
)
|
||||
else:
|
||||
Clock.schedule_once(
|
||||
lambda _: self.controller.requested_search_for_anime(
|
||||
search_term, page=page
|
||||
)
|
||||
)
|
||||
|
||||
def update_layout(self, widget):
|
||||
self.search_results_container.add_widget(widget)
|
||||
|
||||
def update_pagination(self, pagination_info):
|
||||
self.search_results_pagination.current_page = self.current_page = (
|
||||
pagination_info["currentPage"]
|
||||
)
|
||||
self.search_results_pagination.total_pages = self.total_pages = max(
|
||||
int(pagination_info["total"] / 30), 1
|
||||
)
|
||||
self.has_next_page = pagination_info["hasNextPage"]
|
||||
|
||||
def next_page(self):
|
||||
if self.has_next_page:
|
||||
page = self.current_page + 1
|
||||
self.handle_search_for_anime(page=page)
|
||||
|
||||
def previous_page(self):
|
||||
if self.current_page > 1:
|
||||
page = self.current_page - 1
|
||||
self.handle_search_for_anime(page=page)
|
||||
|
||||
def update_trending_sidebar(self, trending_anime):
|
||||
self.trending_anime_sidebar.add_widget(trending_anime)
|
||||
8
anixstream/View/__init__.py
Normal file
8
anixstream/View/__init__.py
Normal file
@@ -0,0 +1,8 @@
|
||||
# screens
|
||||
from .HomeScreen.home_screen import HomeScreenView
|
||||
from .SearchScreen.search_screen import SearchScreenView
|
||||
from .MylistScreen.my_list_screen import MyListScreenView
|
||||
from .AnimeScreen.anime_screen import AnimeScreenView
|
||||
from .CrashLogScreen.crashlog_screen import CrashLogScreenView
|
||||
from .DownloadsScreen.download_screen import DownloadsScreenView
|
||||
from .HelpScreen.help_screen import HelpScreenView
|
||||
72
anixstream/View/base_screen.py
Normal file
72
anixstream/View/base_screen.py
Normal file
@@ -0,0 +1,72 @@
|
||||
from kivy.properties import ObjectProperty, StringProperty
|
||||
|
||||
from kivymd.app import MDApp
|
||||
from kivymd.uix.screen import MDScreen
|
||||
from kivymd.uix.navigationrail import MDNavigationRail, MDNavigationRailItem
|
||||
from kivymd.uix.boxlayout import MDBoxLayout
|
||||
from kivymd.uix.button import MDIconButton
|
||||
from kivymd.uix.tooltip import MDTooltip
|
||||
|
||||
from Utility.observer import Observer
|
||||
|
||||
|
||||
class NavRail(MDNavigationRail):
|
||||
screen = ObjectProperty()
|
||||
|
||||
|
||||
class SearchBar(MDBoxLayout):
|
||||
screen = ObjectProperty()
|
||||
|
||||
|
||||
class Tooltip(MDTooltip):
|
||||
pass
|
||||
|
||||
|
||||
class TooltipMDIconButton(Tooltip, MDIconButton):
|
||||
tooltip_text = StringProperty()
|
||||
|
||||
|
||||
class CommonNavigationRailItem(MDNavigationRailItem):
|
||||
icon = StringProperty()
|
||||
text = StringProperty()
|
||||
|
||||
|
||||
class BaseScreenView(MDScreen, Observer):
|
||||
"""
|
||||
A base class that implements a visual representation of the model data.
|
||||
The view class must be inherited from this class.
|
||||
"""
|
||||
|
||||
controller = ObjectProperty()
|
||||
"""
|
||||
Controller object - :class:`~Controller.controller_screen.ClassScreenControler`.
|
||||
|
||||
:attr:`controller` is an :class:`~kivy.properties.ObjectProperty`
|
||||
and defaults to `None`.
|
||||
"""
|
||||
|
||||
model = ObjectProperty()
|
||||
"""
|
||||
Model object - :class:`~Model.model_screen.ClassScreenModel`.
|
||||
|
||||
:attr:`model` is an :class:`~kivy.properties.ObjectProperty`
|
||||
and defaults to `None`.
|
||||
"""
|
||||
|
||||
manager_screens = ObjectProperty()
|
||||
"""
|
||||
Screen manager object - :class:`~kivymd.uix.screenmanager.MDScreenManager`.
|
||||
|
||||
:attr:`manager_screens` is an :class:`~kivy.properties.ObjectProperty`
|
||||
and defaults to `None`.
|
||||
"""
|
||||
|
||||
def __init__(self, **kw):
|
||||
super().__init__(**kw)
|
||||
# Often you need to get access to the application object from the view
|
||||
# class. You can do this using this attribute.
|
||||
from main import AniXStreamApp
|
||||
|
||||
self.app: AniXStreamApp = MDApp.get_running_app() # type: ignore
|
||||
# Adding a view class as observer.
|
||||
self.model.add_observer(self)
|
||||
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"
|
||||
50
anixstream/View/screens.py
Normal file
50
anixstream/View/screens.py
Normal file
@@ -0,0 +1,50 @@
|
||||
from Controller import (
|
||||
SearchScreenController,
|
||||
HomeScreenController,
|
||||
MyListScreenController,
|
||||
AnimeScreenController,
|
||||
DownloadsScreenController,
|
||||
HelpScreenController,
|
||||
CrashLogScreenController,
|
||||
)
|
||||
from Model import (
|
||||
HomeScreenModel,
|
||||
SearchScreenModel,
|
||||
MyListScreenModel,
|
||||
AnimeScreenModel,
|
||||
DownloadsScreenModel,
|
||||
HelpScreenModel,
|
||||
CrashLogScreenModel,
|
||||
)
|
||||
|
||||
|
||||
screens = {
|
||||
"home screen": {
|
||||
"model": HomeScreenModel,
|
||||
"controller": HomeScreenController,
|
||||
},
|
||||
"search screen": {
|
||||
"model": SearchScreenModel,
|
||||
"controller": SearchScreenController,
|
||||
},
|
||||
"my list screen": {
|
||||
"model": MyListScreenModel,
|
||||
"controller": MyListScreenController,
|
||||
},
|
||||
"anime screen": {
|
||||
"model": AnimeScreenModel,
|
||||
"controller": AnimeScreenController,
|
||||
},
|
||||
"crashlog screen": {
|
||||
"model": CrashLogScreenModel,
|
||||
"controller": CrashLogScreenController,
|
||||
},
|
||||
"downloads screen": {
|
||||
"model": DownloadsScreenModel,
|
||||
"controller": DownloadsScreenController,
|
||||
},
|
||||
"help screen": {
|
||||
"model": HelpScreenModel,
|
||||
"controller": HelpScreenController,
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user