feat(anime_screen):implement crude streaming with allanime api

This commit is contained in:
Benex254
2024-08-05 09:46:56 +03:00
parent 2e2572db3f
commit 91041304ff
32 changed files with 188 additions and 1049 deletions

View File

@@ -1,6 +1,4 @@
from kivy.cache import Cache
from kivy.clock import Clock
from kivy.logger import Logger
from ..Model import AnimeScreenModel
from ..View import AnimeScreenView
@@ -18,25 +16,13 @@ class AnimeScreenController:
def get_view(self) -> AnimeScreenView:
return self.view
def update_anime_view(self, id: int, caller_screen_name: str):
"""method called to update the anime screen when a new
def fetch_streams(self, anime_title, episode="1"):
self.view.current_anime_data = self.model.get_anime_data_from_provider(
anime_title
)
self.view.current_links = self.model.get_episode_streams(episode)
Args:
id (int): the anilst id of the anime
caller_screen_name (str): the screen thats calling this method; used internally to switch back to this screen
"""
if self.model.anime_id != id:
if cached_anime_data := Cache.get("data.anime", f"{id}"):
data = cached_anime_data
else:
data = self.model.get_anime_data(id)
if data[0]:
self.model.anime_id = id
Clock.schedule_once(
lambda _: self.view.update_layout(
data[1]["data"]["Page"]["media"][0], caller_screen_name
)
)
Logger.info(f"Anime Screen:Success in opening anime of id: {id}")
Cache.append("data.anime", f"{id}", data)
def update_anime_view(self, id, title, caller_screen_name):
self.fetch_streams(title)
self.view.current_title = title
self.view.caller_screen_name = caller_screen_name

View File

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

View File

@@ -21,8 +21,8 @@ class MyListScreenController:
def __init__(self, model: MyListScreenModel):
self.model = model
self.view = MyListScreenView(controller=self, model=self.model)
if len(self.requested_update_my_list_screen()) > 30:
self.requested_update_my_list_screen(2)
# if len(self.requested_update_my_list_screen()) > 30:
# self.requested_update_my_list_screen(2)
def get_view(self) -> MyListScreenView:
return self.view

View File

@@ -1,5 +1,30 @@
from ..libs.anilist import AniList
from .base_model import BaseScreenModel
from ..libs.anime_provider.allanime_api import anime_provider
from kivy.cache import Cache
from fuzzywuzzy import fuzz
def anime_title_percentage_match(
possible_user_requested_anime_title: str, title: str
) -> int:
"""Returns the percentage match between the possible title and user title
Args:
possible_user_requested_anime_title (str): an Animdl search result title
title (str): the anime title the user wants
Returns:
int: the percentage match
"""
print(locals())
percentage_ratio = fuzz.ratio(title, possible_user_requested_anime_title)
print(percentage_ratio)
return percentage_ratio
Cache.register("streams.anime", limit=10)
class AnimeScreenModel(BaseScreenModel):
@@ -7,6 +32,48 @@ class AnimeScreenModel(BaseScreenModel):
data = {}
anime_id = 0
current_anime_data = None
current_anime_id = "0"
current_title = ""
def get_anime_data_from_provider(self, anime_title: str, id=None):
if self.current_title == anime_title and self.current_anime_data:
return self.current_anime_data
search_results = anime_provider.search_for_anime(anime_title)
if search_results:
_search_results = search_results["shows"]["edges"]
result = max(
_search_results,
key=lambda x: anime_title_percentage_match(x["name"], anime_title),
)
self.current_anime_id = result["_id"]
self.current_anime_data = anime_provider.get_anime(result["_id"])
self.current_title = anime_title
return self.current_anime_data
def get_episode_streams(self, episode):
if self.current_anime_data:
episode_streams = anime_provider.get_anime_episode(
self.current_anime_id, episode
)
streams = anime_provider.get_episode_streams(episode_streams)
if streams:
_streams = list(streams)
streams = []
for stream in _streams:
streams.append(
{
f"{stream[0]}": [
_stream["link"] for _stream in stream[1]["links"]
]
}
)
return streams
# should return {type:{provider:streamlink}}
def get_anime_data(self, id: int):
return AniList.get_anime(id)

View File

@@ -19,10 +19,10 @@ class MediaCardDataLoader(object):
media_card_data["anime_id"] = anime_id = anime_item["id"]
# TODO: ADD language preference
if anime_item["title"].get("english"):
media_card_data["title"] = anime_item["title"]["english"]
else:
if anime_item["title"].get("romaji"):
media_card_data["title"] = anime_item["title"]["romaji"]
else:
media_card_data["title"] = anime_item["title"]["english"]
media_card_data["cover_image_url"] = anime_item["coverImage"]["medium"]

View File

@@ -1,61 +1,54 @@
<AnimeBoxLayout@MDBoxLayout>:
adaptive_height:True
orientation:'vertical'
<AnimeLabel@MDLabel>:
adaptive_height:True
bold:True
<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)
adaptive_height:True
MDIconButton:
icon:"arrow-left"
on_press:root.manager_screens.current = root.caller_screen_name
MDTextField:
on_text_validate:
root.update_current_link(self)
VideoPlayer:
source:root.current_link
AnimeBoxLayout:
AnimeLabel:
text:"Sub"
MDSegmentedButton:
id:pl
multiselect:False
MDSegmentedButtonItem:
on_active:
pl.selected_segments = [self]
root.update_current_video_stream("gogoanime")
MDSegmentButtonLabel:
text:"GoGoAnime"
MDSegmentedButtonItem:
on_active:
root.update_current_video_stream("dropbox")
pl.selected_segments = [self]
MDSegmentButtonLabel:
text:"DropBox"
MDSegmentedButtonItem:
on_active:
root.update_current_video_stream("sharepoint")
pl.selected_segments = [self]
MDSegmentButtonLabel:
text:"Share Point"
MDSegmentedButtonItem:
on_active:
root.update_current_video_stream("wetransfer")
pl.selected_segments = [self]
MDSegmentButtonLabel:
text:"weTransfer"
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:

View File

@@ -1,134 +1,33 @@
from kivy.properties import DictProperty, ObjectProperty, StringProperty
from kivy.properties import ListProperty, ObjectProperty, StringProperty
from ...libs.anilist import AnilistBaseMediaDataSchema
from ...Utility import anilist_data_helper
from ...View.base_screen import BaseScreenView
from .components import (
AnimdlStreamDialog,
AnimeCharacters,
AnimeDescription,
AnimeHeader,
AnimeReviews,
AnimeSideBar,
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
current_link = StringProperty(
"https://uc951f724c20bbec8df447bac605.dl.dropboxusercontent.com/cd/0/get/CUdx6k2qw-zqY86ftfFHqkmPqGuVrfjpE68B_EkcvZXcZLnjim_ZTHd-qNVb_mEbos9UsuhY8FJGdgf86RUZ-IJqZtz3tt8_CUVTloQAeZ47HtNiKjQ0ESvYdLuwqDjqwK2rNfsfiZI2cXBaKiUyJtljEeRL8whSff2wA9Z4tX1cow/file"
)
current_links = ListProperty([])
current_anime_data = ObjectProperty()
caller_screen_name = ObjectProperty()
current_title = StringProperty()
def update_layout(self, data: AnilistBaseMediaDataSchema, caller_screen_name: str):
self.caller_screen_name = caller_screen_name
self.data = data
# uitlity functions
return
# variables
english_title = data["title"]["english"]
jp_title = data["title"]["romaji"]
studios = data["studios"]["nodes"]
def update_current_video_stream(self, server, is_dub=False):
for link in self.current_links:
if stream_link := link.get(server):
print(link)
self.current_link = stream_link[0]
break
# print(link)
# 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 update_current_link(self, field):
self.controller.fetch_streams(self.current_title, field.text)
def add_to_user_anime_list(self, *args):
self.app.add_anime_to_user_anime_list(self.model.anime_id)

View File

@@ -1,9 +0,0 @@
from .animdl_stream_dialog import AnimdlStreamDialog
from .characters import AnimeCharacters
from .controls import Controls
from .description import AnimeDescription
from .download_anime_dialog import DownloadAnimeDialog
from .header import AnimeHeader
from .rankings_bar import RankingsBar
from .review import AnimeReviews
from .side_bar import AnimeSideBar

View File

@@ -1,63 +0,0 @@
<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"

View File

@@ -1,69 +0,0 @@
from kivy.clock import Clock
from kivy.uix.modalview import ModalView
from kivymd.theming import ThemableBehavior
from kivymd.uix.behaviors import (
BackgroundColorBehavior,
CommonElevationBehavior,
StencilBehavior,
)
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))

View File

@@ -1,71 +0,0 @@
#: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

View File

@@ -1,53 +0,0 @@
from kivy.clock import Clock
from kivy.properties import ListProperty, ObjectProperty
from kivymd.uix.boxlayout import MDBoxLayout
from ....Utility.anilist_data_helper import format_anilist_date_object
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):
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_anilist_date_object(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))

View File

@@ -1,32 +0,0 @@
<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"

View File

@@ -1,8 +0,0 @@
from kivy.properties import ObjectProperty
from kivymd.uix.gridlayout import MDGridLayout
class Controls(MDGridLayout):
"""The diferent controls available"""
screen = ObjectProperty()

View File

@@ -1,23 +0,0 @@
<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

View File

@@ -1,8 +0,0 @@
from kivy.properties import StringProperty
from kivymd.uix.boxlayout import MDBoxLayout
class AnimeDescription(MDBoxLayout):
"""The anime description"""
description = StringProperty()

View File

@@ -1,58 +0,0 @@
<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"

View File

@@ -1,41 +0,0 @@
from kivy.uix.modalview import ModalView
from kivymd.theming import ThemableBehavior
from kivymd.uix.behaviors import (
BackgroundColorBehavior,
CommonElevationBehavior,
StencilBehavior,
)
# 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()

View File

@@ -1,20 +0,0 @@
<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

View File

@@ -1,7 +0,0 @@
from kivy.properties import StringProperty
from kivymd.uix.boxlayout import MDBoxLayout
class AnimeHeader(MDBoxLayout):
titles = StringProperty()
banner_image = StringProperty()

View File

@@ -1,81 +0,0 @@
#: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"])

View File

@@ -1,12 +0,0 @@
from kivy.properties import DictProperty
from kivymd.uix.boxlayout import MDBoxLayout
class RankingsBar(MDBoxLayout):
rankings = DictProperty(
{
"Popularity": 0,
"Favourites": 0,
"AverageScore": 0,
}
)

View File

@@ -1,50 +0,0 @@
#: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

View File

@@ -1,28 +0,0 @@
from kivy.clock import Clock
from kivy.properties import ListProperty, ObjectProperty
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_)

View File

@@ -1,102 +0,0 @@
#: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:

View File

@@ -1,97 +0,0 @@
from kivy.factory import Factory
from kivy.properties import DictProperty, ListProperty, ObjectProperty, StringProperty
from kivy.utils import get_hex_from_color
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)

View File

@@ -30,6 +30,11 @@ class CommonNavigationRailItem(MDNavigationRailItem):
text = StringProperty()
class HeaderLabel(MDBoxLayout):
text = StringProperty()
halign = StringProperty("left")
class BaseScreenView(MDScreen, Observer):
"""
A base class that implements a visual representation of the model data.

View File

@@ -1,3 +1,19 @@
<MDLabel>:
allow_copy:True
allow_selection:True
allow_selection:True
<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"

View File

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

View File

@@ -244,9 +244,9 @@ class AniXStreamApp(MDApp):
updated_list.append(id)
user_data_helper.get_user_downloads()
def show_anime_screen(self, id: int, caller_screen_name: str):
def show_anime_screen(self, id: int, title, caller_screen_name: str):
self.manager_screens.current = "anime screen"
self.anime_screen.controller.update_anime_view(id, caller_screen_name)
self.anime_screen.controller.update_anime_view(id, title, caller_screen_name)
def download_anime_complete(
self, successful_downloads: list, failed_downloads: list, anime_title: str

View File

@@ -196,15 +196,15 @@ class AllAnimeAPI:
if resp.status_code == 200:
match embed["sourceName"]:
case "Luf-mp4":
return "gogoanime", resp.json()
yield "gogoanime", resp.json()
case "Kir":
return "wetransfer", resp.json()
yield "wetransfer", resp.json()
case "S-mp4":
return "sharepoint", resp.json()
yield "sharepoint", resp.json()
case "Sak":
return "dropbox", resp.json()
yield "dropbox", resp.json()
case _:
return "Unknown", resp.json()
yield "Unknown", resp.json()
else:
return None
@@ -247,6 +247,7 @@ if __name__ == "__main__":
search_results = anime_provider.search_for_anime(
anime, translation_type=translation.strip()
)
print(search_results)
if not search_results:
raise Exception("No results found")
@@ -259,7 +260,7 @@ if __name__ == "__main__":
anime_result = list(filter(lambda x: x["name"] == anime, search_results))[0]
anime_data = anime_provider.get_anime(anime_result["_id"])
print(anime_data)
if anime_data is None:
raise Exception("Anime not found")
availableEpisodesDetail = anime_data["show"]["availableEpisodesDetail"]
@@ -279,11 +280,15 @@ if __name__ == "__main__":
raise Exception("Episode not found")
episode_streams = anime_provider.get_episode_streams(episode_data)
if not episode_streams:
raise Exception("No streams found")
stream_links = [stream["link"] for stream in episode_streams[1]["links"]]
stream_link = run_fzf([*stream_links, "quit"])
episode_streams = list(episode_streams)
print(episode_streams)
stream_links = [stream["link"] for stream in episode_streams[2][1]["links"]]
stream_link = run_fzf([*stream_links, "quit"])
print(stream_link)
if stream_link == "quit":
print("Have a nice day")
sys.exit()