diff --git a/assets/screenshots/anime_screen.png b/.assets/screenshots/anime_screen.png similarity index 100% rename from assets/screenshots/anime_screen.png rename to .assets/screenshots/anime_screen.png diff --git a/assets/screenshots/anime_screen_2.png b/.assets/screenshots/anime_screen_2.png similarity index 100% rename from assets/screenshots/anime_screen_2.png rename to .assets/screenshots/anime_screen_2.png diff --git a/assets/screenshots/anime_screen_3.png b/.assets/screenshots/anime_screen_3.png similarity index 100% rename from assets/screenshots/anime_screen_3.png rename to .assets/screenshots/anime_screen_3.png diff --git a/assets/screenshots/downloads.png b/.assets/screenshots/downloads.png similarity index 100% rename from assets/screenshots/downloads.png rename to .assets/screenshots/downloads.png diff --git a/assets/screenshots/help.png b/.assets/screenshots/help.png similarity index 100% rename from assets/screenshots/help.png rename to .assets/screenshots/help.png diff --git a/assets/screenshots/home.png b/.assets/screenshots/home.png similarity index 100% rename from assets/screenshots/home.png rename to .assets/screenshots/home.png diff --git a/assets/screenshots/home_2.png b/.assets/screenshots/home_2.png similarity index 100% rename from assets/screenshots/home_2.png rename to .assets/screenshots/home_2.png diff --git a/assets/screenshots/home_3.png b/.assets/screenshots/home_3.png similarity index 100% rename from assets/screenshots/home_3.png rename to .assets/screenshots/home_3.png diff --git a/assets/screenshots/home_4.png b/.assets/screenshots/home_4.png similarity index 100% rename from assets/screenshots/home_4.png rename to .assets/screenshots/home_4.png diff --git a/assets/screenshots/home_5.png b/.assets/screenshots/home_5.png similarity index 100% rename from assets/screenshots/home_5.png rename to .assets/screenshots/home_5.png diff --git a/assets/screenshots/myanimelist.png b/.assets/screenshots/myanimelist.png similarity index 100% rename from assets/screenshots/myanimelist.png rename to .assets/screenshots/myanimelist.png diff --git a/assets/screenshots/search_1.png b/.assets/screenshots/search_1.png similarity index 100% rename from assets/screenshots/search_1.png rename to .assets/screenshots/search_1.png diff --git a/assets/screenshots/search_2.png b/.assets/screenshots/search_2.png similarity index 100% rename from assets/screenshots/search_2.png rename to .assets/screenshots/search_2.png diff --git a/assets/screenshots/search_3.png b/.assets/screenshots/search_3.png similarity index 100% rename from assets/screenshots/search_3.png rename to .assets/screenshots/search_3.png diff --git a/assets/screenshots/settings_1.png b/.assets/screenshots/settings_1.png similarity index 100% rename from assets/screenshots/settings_1.png rename to .assets/screenshots/settings_1.png diff --git a/assets/screenshots/settings_2.png b/.assets/screenshots/settings_2.png similarity index 100% rename from assets/screenshots/settings_2.png rename to .assets/screenshots/settings_2.png diff --git a/assets/screenshots/theme_example_1.png b/.assets/screenshots/theme_example_1.png similarity index 100% rename from assets/screenshots/theme_example_1.png rename to .assets/screenshots/theme_example_1.png diff --git a/assets/screenshots/theme_example_2.png b/.assets/screenshots/theme_example_2.png similarity index 100% rename from assets/screenshots/theme_example_2.png rename to .assets/screenshots/theme_example_2.png diff --git a/assets/screenshots/theme_example_3.png b/.assets/screenshots/theme_example_3.png similarity index 100% rename from assets/screenshots/theme_example_3.png rename to .assets/screenshots/theme_example_3.png diff --git a/assets/screenshots/trailer_fullscreen.png b/.assets/screenshots/trailer_fullscreen.png similarity index 100% rename from assets/screenshots/trailer_fullscreen.png rename to .assets/screenshots/trailer_fullscreen.png diff --git a/fastanime/libs/anime_provider/__init__.py b/fastanime/libs/anime_provider/allanime/__init__.py similarity index 100% rename from fastanime/libs/anime_provider/__init__.py rename to fastanime/libs/anime_provider/allanime/__init__.py diff --git a/fastanime/libs/anime_provider/allanime_api.py b/fastanime/libs/anime_provider/allanime/api.py similarity index 57% rename from fastanime/libs/anime_provider/allanime_api.py rename to fastanime/libs/anime_provider/allanime/api.py index 5cd4a39..fe4eff8 100644 --- a/fastanime/libs/anime_provider/allanime_api.py +++ b/fastanime/libs/anime_provider/allanime/api.py @@ -1,116 +1,22 @@ import json -import re +import logging import requests -# TODO: move constants to own file -ALLANIME_BASE = "allanime.day" -ALLANIME_REFERER = "https://allanime.to/" -ALLANIME_API_ENDPOINT = "https://api.{}/api/".format(ALLANIME_BASE) +from .gql_queries import ALLANIME_SHOW_GQL, ALLANIME_SEARCH_GQL, ALLANIME_EPISODES_GQL +from .constants import ( + ALLANIME_BASE, + ALLANIME_REFERER, + ALLANIME_API_ENDPOINT, + USER_AGENT, +) +from .utils import decode_hex_string +from .data_types import ( + AllAnimeEpisode, + AllAnimeSearchResults, +) - -# TODO: move th gql queries to own files -ALLANIME_SEARCH_GQL = """ -query( - $search: SearchInput - $limit: Int - $page: Int - $translationType: VaildTranslationTypeEnumType - $countryOrigin: VaildCountryOriginEnumType - ) { - shows( - search: $search - limit: $limit - page: $page - translationType: $translationType - countryOrigin: $countryOrigin - ) { - pageInfo { - total - } - edges { - _id - name - availableEpisodes - __typename - } - } -} -""" - - -ALLANIME_EPISODES_GQL = """\ -query ($showId: String!, $translationType: VaildTranslationTypeEnumType!, $episodeString: String!) { - episode( - showId: $showId - translationType: $translationType - episodeString: $episodeString - ) { - - episodeString - sourceUrls - notes - } -}""" - -ALLANIME_SHOW_GQL = """ -query ($showId: String!) { - show( - _id: $showId - ) { - - _id - name - availableEpisodesDetail - - } -} -""" - - -# TODO: creat a utility module for this -# Dictionary to map hex values to characters -hex_to_char = { - "01": "9", - "08": "0", - "05": "=", - "0a": "2", - "0b": "3", - "0c": "4", - "07": "?", - "00": "8", - "5c": "d", - "0f": "7", - "5e": "f", - "17": "/", - "54": "l", - "09": "1", - "48": "p", - "4f": "w", - "0e": "6", - "5b": "c", - "5d": "e", - "0d": "5", - "53": "k", - "1e": "&", - "5a": "b", - "59": "a", - "4a": "r", - "4c": "t", - "4e": "v", - "57": "o", - "51": "i", -} - - -def decode_hex_string(hex_string): - # Split the hex string into pairs of characters - hex_pairs = re.findall("..", hex_string) - - # Decode each hex pair - decoded_chars = [hex_to_char.get(pair.lower(), pair) for pair in hex_pairs] - - return "".join(decoded_chars) +Logger = logging.getLogger(__name__) # TODO: create tests for the api @@ -122,24 +28,22 @@ class AllAnimeAPI: api_endpoint = ALLANIME_API_ENDPOINT - def _fetch_gql(self, query: str, variables: dict): + def _fetch_gql(self, query: str, variables: dict) -> dict: response = requests.get( self.api_endpoint, params={ "variables": json.dumps(variables), "query": query, }, - headers={ - "Referer": ALLANIME_REFERER, - "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0", - }, + headers={"Referer": ALLANIME_REFERER, "User-Agent": USER_AGENT}, ) if response.status_code != 200: - print(response.content) return {} return response.json().get("data", {}) - def search_for_anime(self, user_query: str, translation_type: str = "sub"): + def search_for_anime( + self, user_query: str, translation_type: str = "sub" + ) -> AllAnimeSearchResults | dict: search = {"allowAdult": False, "allowUnknown": False, "query": user_query} limit = 40 translationtype = translation_type @@ -160,7 +64,7 @@ class AllAnimeAPI: def get_anime_episode( self, allanime_show_id: str, episode_string: str, translation_type: str = "sub" - ): + ) -> AllAnimeEpisode | dict: variables = { "showId": allanime_show_id, "translationType": translation_type, @@ -190,23 +94,27 @@ class AllAnimeAPI: embed_url, headers={ "Referer": ALLANIME_REFERER, - "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0", + "User-Agent": USER_AGENT, }, ) if resp.status_code == 200: match embed["sourceName"]: case "Luf-mp4": + Logger.debug("allanime:Found streams from gogoanime") yield "gogoanime", resp.json() case "Kir": + Logger.debug("allanime:Found streams from wetransfer") yield "wetransfer", resp.json() case "S-mp4": + Logger.debug("allanime:Found streams from sharepoint") yield "sharepoint", resp.json() case "Sak": + Logger.debug("allanime:Found streams from dropbox") yield "dropbox", resp.json() case _: yield "Unknown", resp.json() else: - return None + return {} anime_provider = AllAnimeAPI() @@ -216,30 +124,7 @@ if __name__ == "__main__": # lets see if it works :) import subprocess import sys - - def run_fzf(options): - """ - Run fzf with a list of options and return the selected option. - """ - # Join the list of options into a single string with newlines - options_str = "\n".join(options) - - # Run fzf as a subprocess - result = subprocess.run( - ["fzf"], - input=options_str, - text=True, - stdout=subprocess.PIPE, - ) - - # Check if fzf was successful - if result.returncode == 0: - # Return the selected option - return result.stdout.strip() - else: - # Handle the case where fzf fails or is canceled - print("fzf was canceled or failed") - return None + from .utils import run_fzf anime = input("Enter the anime name: ") translation = input("Enter the translation type: ") @@ -247,7 +132,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") @@ -260,7 +145,6 @@ 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"] @@ -284,11 +168,14 @@ if __name__ == "__main__": if not episode_streams: raise Exception("No streams found") episode_streams = list(episode_streams) - print(episode_streams) + stream_links = [] + for server in episode_streams: + stream_links = [ + *stream_links, + *[stream["link"] for stream in server[1]["links"]], + ] + stream_links = stream_link = run_fzf([*stream_links, "quit"]) - 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() diff --git a/fastanime/libs/anime_provider/allanime/constants.py b/fastanime/libs/anime_provider/allanime/constants.py new file mode 100644 index 0000000..185d93a --- /dev/null +++ b/fastanime/libs/anime_provider/allanime/constants.py @@ -0,0 +1,4 @@ +ALLANIME_BASE = "allanime.day" +ALLANIME_REFERER = "https://allanime.to/" +ALLANIME_API_ENDPOINT = "https://api.{}/api/".format(ALLANIME_BASE) +USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0" diff --git a/fastanime/libs/anime_provider/allanime/data_types.py b/fastanime/libs/anime_provider/allanime/data_types.py new file mode 100644 index 0000000..5e83e6f --- /dev/null +++ b/fastanime/libs/anime_provider/allanime/data_types.py @@ -0,0 +1,49 @@ +from typing import TypedDict + + +class AllAnimeEpisodesInfo(TypedDict): + dub: int + sub: int + raw: int + + +class AllAnimePageInfo(TypedDict): + total: int + + +class AllAnimeShow(TypedDict): + _id: str + name: str + availableEpisodes: AllAnimeEpisodesInfo + __typename: str + + +class AllAnimeShows(TypedDict): + pageInfo: AllAnimePageInfo + edges: list[AllAnimeShow] + + +class AllAnimeSearchResults(TypedDict): + shows: AllAnimeShows + + +class AllAnimeSourcesDownloads(TypedDict): + sourceName: str + dowloadUrl: str + + +class AllAnimeSources(TypedDict): + sourceUrl: str + priority: float + sandbox: str + sourceName: str + type: str + className: str + streamerId: str + downloads: AllAnimeSourcesDownloads + + +class AllAnimeEpisode(TypedDict): + episodeString: str + sourceUrls: list[AllAnimeSources] + notes: str | None diff --git a/fastanime/libs/anime_provider/allanime/gql_queries.py b/fastanime/libs/anime_provider/allanime/gql_queries.py new file mode 100644 index 0000000..4faada7 --- /dev/null +++ b/fastanime/libs/anime_provider/allanime/gql_queries.py @@ -0,0 +1,56 @@ +ALLANIME_SEARCH_GQL = """ +query( + $search: SearchInput + $limit: Int + $page: Int + $translationType: VaildTranslationTypeEnumType + $countryOrigin: VaildCountryOriginEnumType + ) { + shows( + search: $search + limit: $limit + page: $page + translationType: $translationType + countryOrigin: $countryOrigin + ) { + pageInfo { + total + } + edges { + _id + name + availableEpisodes + __typename + } + } +} +""" + + +ALLANIME_EPISODES_GQL = """\ +query ($showId: String!, $translationType: VaildTranslationTypeEnumType!, $episodeString: String!) { + episode( + showId: $showId + translationType: $translationType + episodeString: $episodeString + ) { + + episodeString + sourceUrls + notes + } +}""" + +ALLANIME_SHOW_GQL = """ +query ($showId: String!) { + show( + _id: $showId + ) { + + _id + name + availableEpisodesDetail + + } +} +""" diff --git a/fastanime/libs/anime_provider/allanime/utils.py b/fastanime/libs/anime_provider/allanime/utils.py new file mode 100644 index 0000000..1c8c323 --- /dev/null +++ b/fastanime/libs/anime_provider/allanime/utils.py @@ -0,0 +1,71 @@ +import re +import subprocess + + +# Dictionary to map hex values to characters +hex_to_char = { + "01": "9", + "08": "0", + "05": "=", + "0a": "2", + "0b": "3", + "0c": "4", + "07": "?", + "00": "8", + "5c": "d", + "0f": "7", + "5e": "f", + "17": "/", + "54": "l", + "09": "1", + "48": "p", + "4f": "w", + "0e": "6", + "5b": "c", + "5d": "e", + "0d": "5", + "53": "k", + "1e": "&", + "5a": "b", + "59": "a", + "4a": "r", + "4c": "t", + "4e": "v", + "57": "o", + "51": "i", +} + + +def decode_hex_string(hex_string): + # Split the hex string into pairs of characters + hex_pairs = re.findall("..", hex_string) + + # Decode each hex pair + decoded_chars = [hex_to_char.get(pair.lower(), pair) for pair in hex_pairs] + + return "".join(decoded_chars) + + +def run_fzf(options): + """ + Run fzf with a list of options and return the selected option. + """ + # Join the list of options into a single string with newlines + options_str = "\n".join(options) + + # Run fzf as a subprocess + result = subprocess.run( + ["fzf"], + input=options_str, + text=True, + stdout=subprocess.PIPE, + ) + + # Check if fzf was successful + if result.returncode == 0: + # Return the selected option + return result.stdout.strip() + else: + # Handle the case where fzf fails or is canceled + print("fzf was canceled or failed") + return None diff --git a/fastanime/libs/anime_provider/data3.json b/fastanime/libs/anime_provider/data3.json deleted file mode 100644 index b091db0..0000000 --- a/fastanime/libs/anime_provider/data3.json +++ /dev/null @@ -1,159 +0,0 @@ -{ - "shows": { - "pageInfo": { - "total": 22279 - }, - "edges": [ - { - "_id": "JsbMBPoTsp3RkGfrf", - "name": "Sword Art Online: Progressive Movie: Kuraki Yuuyami no Scherzo", - "availableEpisodes": { - "sub": 1, - "dub": 1, - "raw": 0 - }, - "__typename": "Show" - }, - { - "_id": "YXEMgd5FLhWD2mn8Z", - "name": "Sword Art Online: Progressive Movie - Hoshi Naki Yoru no Aria", - "availableEpisodes": { - "sub": 1, - "dub": 1, - "raw": 0 - }, - "__typename": "Show" - }, - { - "_id": "ALj8oEW6mhWuQ8pHT", - "name": "Sword Art Online Movie: Ordinal Scale: Sword Art Offline", - "availableEpisodes": { - "sub": 1, - "dub": 0, - "raw": 0 - }, - "__typename": "Show" - }, - { - "_id": "DWZeL5vDhEokJQFXR", - "name": "Sword Art Offline II", - "availableEpisodes": { - "sub": 9, - "dub": 0, - "raw": 0 - }, - "__typename": "Show" - }, - { - "_id": "fgEBgLfkzEWaCSHAu", - "name": "Sword Art Online: Sword Art Offline", - "availableEpisodes": { - "sub": 9, - "dub": 0, - "raw": 0 - }, - "__typename": "Show" - }, - { - "_id": "BvzJ7btmDGBvJCC4P", - "name": "Sword Art Online: Extra Edition", - "availableEpisodes": { - "sub": 1, - "dub": 1, - "raw": 0 - }, - "__typename": "Show" - }, - { - "_id": "4mjsuWL9sAK97zQjL", - "name": "Sword Art Offline: Extra Edition", - "availableEpisodes": { - "sub": 1, - "dub": 0, - "raw": 0 - }, - "__typename": "Show" - }, - { - "_id": "np7xM323v5pH6KY9e", - "name": "Sword Art Online: Alicization", - "availableEpisodes": { - "sub": 25, - "dub": 25, - "raw": 0 - }, - "__typename": "Show" - }, - { - "_id": "36bqKyjS3tFybyz7j", - "name": "Sword Art Online", - "availableEpisodes": { - "sub": 25, - "dub": 25, - "raw": 0 - }, - "__typename": "Show" - }, - { - "_id": "ierXJrDYNiALG5haP", - "name": "Sword Art Online II", - "availableEpisodes": { - "sub": 26, - "dub": 25, - "raw": 0 - }, - "__typename": "Show" - }, - { - "_id": "tkywv9gMqH7mYw4N5", - "name": "Sword Art Online: Alicization - War of Underworld Season 2", - "availableEpisodes": { - "sub": 11, - "dub": 11, - "raw": 0 - }, - "__typename": "Show" - }, - { - "_id": "z2stShodurTBWbXgM", - "name": "Sword Art Online: Alicization - War of Underworld", - "availableEpisodes": { - "sub": 14, - "dub": 12, - "raw": 0 - }, - "__typename": "Show" - }, - { - "_id": "WwdepehfXq8gpgku9", - "name": "Sword Art Online Alternative: Gun Gale Online", - "availableEpisodes": { - "sub": 13, - "dub": 12, - "raw": 0 - }, - "__typename": "Show" - }, - { - "_id": "hjHnDp4NRh9zpcTLJ", - "name": "Sword Art Online: Ordinal Scale", - "availableEpisodes": { - "sub": 1, - "dub": 1, - "raw": 0 - }, - "__typename": "Show" - }, - { - "_id": "TYTW9KtABBNiMcp5w", - "name": "Sword Art Online II: Debriefing", - "availableEpisodes": { - "sub": 1, - "dub": 0, - "raw": 0 - }, - "__typename": "Show" - } - ] - } -} diff --git a/fastanime/libs/anime_provider/final.json b/fastanime/libs/anime_provider/final.json deleted file mode 100644 index 8a8a089..0000000 --- a/fastanime/libs/anime_provider/final.json +++ /dev/null @@ -1,102 +0,0 @@ -{ - "episode": { - "episodeString": "12", - "sourceUrls": [ - { - "sourceUrl": "--175948514e4c4f57175b54575b5307515c050f5c0a0c0f0b0f0c0e590a0c0b5b0a0c0a010a010f080e5e0e0a0f0d0f0a0f0c0e0b0e0f0e5a0e5e0e000e090a000e5e0e010a010e590e010e0f0e0a0a000f0e0e5d0f0e0b010e5e0e0a0b5a0c5a0d0a0c0b0f5d0c010d0a0e5c0b0d0a080f0a0e5e0f0a0e590e0b0b5a0d0d0f090e010f0c0e0a0a5c0c0f0f0c0f0a0a5c0c010e000e590e5e0e000e0b0a0b0b0d0c0f0a5c0c0f0e590e5e0e0d0e5e0f5b0e0f0f0a0e5e0e010e000a080f0a0f5e0f0e0e0b0f0d0f0b0e0c0b5a0d0d0d0b0c0c0a080f0d0f0b0e0c0b5a0a080e0d0e010f080e0b0f0c0b5a0d5e0b0c0b5e0b0c0d5b0d5d0c5e0f080e0d0b0d0e0a0f080e0d0e5a0d0f0f0a0d5e0d5d0c5b0b0e0c590d090b5e0f0b0e0c0c090e590f0b0d5b0d0d0b0f0e5d0e0c0c090e590e5b0e0f0d5d0f0e0e5d0e0a0c090e590f080e0c0e5e0b0b0f090e0c0e5a0e0d0b5a0a0c0a590a0c0f0d0f0a0f0c0e0b0e0f0e5a0e0b0f0c0c5e0e0a0a0c0b5b0a0c0f080e5e0e0a0e0b0e010f0d0f0a0f0c0e0b0e0f0e5a0e5e0e010a0c0a590a0c0e0a0e0f0f0a0e0b0a0c0b5b0a0c0b0c0b0e0b0c0b0a0a5a0b0e0b080a5a0b0e0b090d0a0b0f0b5e0b5b0b0b0b5e0b5b0b0e0b0e0a000b0e0b0e0b0e0d5b0a0c0f5a1e4a5d5e5d4a5d4a05", - "priority": 7.7, - "sourceName": "Luf-mp4", - "type": "iframe", - "className": "", - "streamerId": "allanime" - }, - { - "sourceUrl": "//vidstreaming.io/load.php?id=MTExOTk3&title=Sword+Art+Online%3A+Alicization&typesub=SUB&sub=&cover=Y292ZXIvc3dvcmQtYXJ0LW9ubGluZS1hbGljaXphdGlvbi5wbmc=", - "priority": 4, - "sourceName": "Vid-mp4", - "type": "iframe", - "className": "", - "streamerId": "allanime", - "downloads": { - "sourceName": "Gl", - "downloadUrl": "https://embtaku.pro/download?id=MTExOTk3&title=Sword+Art+Online%3A+Alicization&typesub=SUB&sub=&cover=Y292ZXIvc3dvcmQtYXJ0LW9ubGluZS1hbGljaXphdGlvbi5wbmc=&sandbox=allow-forms%20allow-scripts%20allow-same-origin%20allow-downloads" - } - }, - { - "sourceUrl": "https://streamsb.net/e/kag9w71l9jq0.html", - "priority": 5.5, - "sourceName": "Ss-Hls", - "type": "iframe", - "className": "text-danger", - "streamerId": "allanime", - "downloads": { - "sourceName": "StreamSB", - "downloadUrl": "https://streamsb.net/d/kag9w71l9jq0.html&sandbox=allow-forms%20allow-scripts%20allow-same-origin%20allow-downloads" - } - }, - { - "sourceUrl": "--175948514e4c4f57175b54575b5307515c050f5c0a0c0f0b0f0c0e590a0c0b5b0a0c0a010f080e5e0e0a0e0b0e010f0d0a010e000f0e0b090f5d0c5a0b0d0b0c0b0d0f080b0b0f0e0c5d0b080c5c0d5e0b5e0e0b0a010f0d0f0b0e0c0a010b0f0b0c0a0c0a590a0c0f0d0f0a0f0c0e0b0e0f0e5a0e0b0f0c0c5e0e0a0a0c0b5b0a0c0c0a0f0c0e010f0e0e0c0e010f5d0a0c0a590a0c0e0a0e0f0f0a0e0b0a0c0b5b0a0c0b0c0b0e0b0c0b0a0a5a0b0e0b080a5a0b0e0b090d0a0b0f0b5e0b5b0b0b0b5e0b5b0b0e0b0e0a000b0e0b0e0b0e0d5b0a0c0a590a0c0f0a0f0c0e0f0e000f0d0e590e0f0f0a0e5e0e010e000d0a0f5e0f0e0e0b0a0c0b5b0a0c0f0d0f0b0e0c0a0c0a590a0c0e5c0e0b0f5e0a0c0b5b0a0c0e0b0f0e0a5a0a010f080e5e0e0a0e0b0e010f0d0a010e000f0e0b090f5d0c5a0b0d0b0c0b0d0f080b0b0f0e0c5d0b080c5c0d5e0b5e0e0b0a010f0d0f0b0e0c0a010b0f0b0c0a0c0f5a", - "priority": 7, - "sourceName": "Sak", - "type": "iframe", - "className": "", - "streamerId": "allanime" - }, - { - "sourceUrl": "--504c4c484b0217174c5757544b165e594b4c0c4b485d5d5c164a4b4e4817174e515c5d574b1756480f40750b0a0b4e0d48700e7361015d174b4d5a17090a", - "priority": 7.9, - "sourceName": "Yt-mp4", - "type": "player", - "className": "", - "streamerId": "allanime" - }, - { - "sourceUrl": "https://mp4upload.com/embed-bayej7b93in0.html", - "priority": 4, - "sourceName": "Mp4", - "type": "iframe", - "sandbox": "allow-forms allow-scripts allow-same-origin", - "className": "", - "streamerId": "allanime" - }, - { - "sourceUrl": "https://ok.ru/videoembed/2731036445330", - "priority": 3.5, - "sourceName": "Ok", - "type": "iframe", - "sandbox": "allow-forms allow-scripts allow-same-origin", - "className": "text-info", - "streamerId": "allanime" - }, - { - "sourceUrl": "--175948514e4c4f57175b54575b5307515c050f5c0a0c0f0b0f0c0e590a0c0b5b0a0c0b0b0b080e0d0b5e0b0a0b0d0d010e0c0b5e0b0c0b080b0f0b0d0e0c0b0a0b0b0b0e0b080b090b0a0b5e0b0f0b080b5e0b5d0e0a0b080e0f0b5e0b0b0e080b0f0b0e0e0c0b0f0b5d0e0f0b0c0e0a0a0e0f590a0e0b0d0e0f0b0d0e080b0a0e0f0e080b0b0b080e0a0b090b0a0b090e0b0b5d0b5e0e0d0b0d0b0e0b0f0b0b0b080e0b0e080b080e0d0b5d0b5d0e0d0b0b0b080e0c0a0e0f590a0e0e5a0e0b0e0a0e5e0e0f0a010b0b0b080e0d0b5e0b0a0b0d0d010e0c0b5e0b0c0b080b0f0b0d0e0c0b0a0b0b0b0e0b080b090b0a0b5e0b0f0b080b5e0b5d0e0a0b080e0f0b5e0b0b0e080b0f0b0e0e0c0b0f0b5d0e0f0b0c0e0a0e080b0e0b0e0b0f0a000e5b0f0e0e090a0e0f590a0e0a590b090b0c0b0e0f0e0a590b0f0b0e0b5d0b0e0f0e0a590b0a0b5d0b0e0f0e0a590a0c0a590a0c0f0d0f0a0f0c0e0b0e0f0e5a0e0b0f0c0c5e0e0a0a0c0b5b0a0c0d090e5e0f5d0a0c0a590a0c0e0a0e0f0f0a0e0b0a0c0b5b0a0c0b0c0b0e0b0c0b0a0a5a0b0e0b080a5a0b0e0b090d0a0b0f0b5e0b5b0b0b0b5e0b5b0b0e0b0e0a000b0e0b0e0b0e0d5b0a0c0a590a0c0f0a0f0c0e0f0e000f0d0e590e0f0f0a0e5e0e010e000d0a0f5e0f0e0e0b0a0c0b5b0a0c0f0d0f0b0e0c0a0c0a590a0c0e5c0e0b0f5e0a0c0b5b0a0c0e0b0f0e0a5a0e000f0e0b090f5d0c5a0b0d0b0c0b0d0f080b0b0f0e0c5d0b080c5c0d5e0b5e0e0b0d010b0f0b0c0d010f0d0f0b0e0c0a0c0f5a", - "priority": 8.5, - "sourceName": "Default", - "type": "iframe", - "className": "text-info", - "streamerId": "allanime" - }, - { - "sourceUrl": "--175948514e4c4f57175b54575b5307515c050f5c0a0c0f0b0f0c0e590a0c0b5b0a0c0a010f0d0e5e0f0a0e0b0f0d0a010e0f0e000e5e0e5a0e0b0a010d0d0e5d0e0f0f0c0e0b0e0a0a0e0c0a0e010e0d0f0b0e5a0e0b0e000f0a0f0d0a010e000f0e0b090f5d0c5a0b0d0b0c0b0d0f080b0b0f0e0c5d0b080c5c0d5e0b5e0e0b0d010b0f0b0c0d010f0d0f0b0e0c0a000e5a0f0e0b0a0a0c0a590a0c0f0d0f0a0f0c0e0b0e0f0e5a0e0b0f0c0c5e0e0a0a0c0b5b0a0c0d0d0e5d0e0f0f0c0e0b0f0e0e010e5e0e000f0a0a0c0a590a0c0e0a0e0f0f0a0e0b0a0c0b5b0a0c0b0c0b0e0b0c0b0a0a5a0b0e0b080a5a0b0e0b090d0a0b0f0b5e0b5b0b0b0b5e0b5b0b0e0b0e0a000b0e0b0e0b0e0d5b0a0c0a590a0c0f0a0f0c0e0f0e000f0d0e590e0f0f0a0e5e0e010e000d0a0f5e0f0e0e0b0a0c0b5b0a0c0f0d0f0b0e0c0a0c0a590a0c0e5c0e0b0f5e0a0c0b5b0a0c0e0b0f0e0a5a0e000f0e0b090f5d0c5a0b0d0b0c0b0d0f080b0b0f0e0c5d0b080c5c0d5e0b5e0e0b0d010b0f0b0c0d010f0d0f0b0e0c0a0c0f5a", - "priority": 7.4, - "sourceName": "S-mp4", - "type": "iframe", - "className": "", - "streamerId": "allanime", - "downloads": { - "sourceName": "S-mp4", - "downloadUrl": "https://blog.allanime.day/apivtwo/clock/download?id=7d2473746a243c2429756f7263752967686f6b6329556e6774636226426965736b63687275296876317e4b3534357033764e304d5f3f6359373459757364286b7632242a2475727463676b63744f62243c24556e67746376696f6872242a2462677263243c24343634322b36302b363152373f3c333f3c3636283636365c242a24626971686a696762243c727473637b" - } - }, - { - "sourceUrl": "--175948514e4c4f57175b54575b5307515c050f5c0a0c0f0b0f0c0e590a0c0b5b0a0c0e5d0f0a0f0a0f0e0f0d0b5b0a010a010f0b0f0d0e0b0f0c0f0d0e0d0e590e010f0b0e0a0a000e0d0e010e5a0a010e0b0e5a0e0c0e0b0e0a0a5a0f0f0b5e0e080e590f080e5b0e5b0b0d0e0c0b090f0d0f0f0a000e5d0f0a0e5a0e590a0c0a590a0c0f0d0f0a0f0c0e0b0e0f0e5a0e0b0f0c0c5e0e0a0a0c0b5b0a0c0d0b0f0d0e0b0f0c0f080e5e0e0a0a0c0a590a0c0e0a0e0f0f0a0e0b0a0c0b5b0a0c0b0c0b0e0b0c0b0a0a5a0b0e0b080a5a0b0e0b090d0a0b0f0b5e0b5b0b0b0b5e0b5b0b0e0b0e0a000b0e0b0e0b0e0d5b0a0c0a590a0c0f0a0f0c0e0f0e000f0d0e590e0f0f0a0e5e0e010e000d0a0f5e0f0e0e0b0a0c0b5b0a0c0f0d0f0b0e0c0a0c0a590a0c0e5c0e0b0f5e0a0c0b5b0a0c0e0b0f0e0a5a0e000f0e0b090f5d0c5a0b0d0b0c0b0d0f080b0b0f0e0c5d0b080c5c0d5e0b5e0e0b0d010b0f0b0c0d010f0d0f0b0e0c0a0c0f5a", - "priority": 1, - "sourceName": "Uv-mp4", - "type": "iframe", - "className": "", - "streamerId": "allanime" - } - ], - "notes": null - } -}