refactor(allanime provider):clean up and move allanime api to allanime folder

This commit is contained in:
Benex254
2024-08-05 09:46:57 +03:00
parent 2173372aa7
commit f34706206a
28 changed files with 215 additions and 409 deletions

View File

Before

Width:  |  Height:  |  Size: 971 KiB

After

Width:  |  Height:  |  Size: 971 KiB

View File

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 96 KiB

View File

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 114 KiB

View File

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 93 KiB

View File

Before

Width:  |  Height:  |  Size: 690 KiB

After

Width:  |  Height:  |  Size: 690 KiB

View File

Before

Width:  |  Height:  |  Size: 718 KiB

After

Width:  |  Height:  |  Size: 718 KiB

View File

Before

Width:  |  Height:  |  Size: 813 KiB

After

Width:  |  Height:  |  Size: 813 KiB

View File

Before

Width:  |  Height:  |  Size: 763 KiB

After

Width:  |  Height:  |  Size: 763 KiB

View File

Before

Width:  |  Height:  |  Size: 518 KiB

After

Width:  |  Height:  |  Size: 518 KiB

View File

Before

Width:  |  Height:  |  Size: 212 KiB

After

Width:  |  Height:  |  Size: 212 KiB

View File

Before

Width:  |  Height:  |  Size: 578 KiB

After

Width:  |  Height:  |  Size: 578 KiB

View File

Before

Width:  |  Height:  |  Size: 644 KiB

After

Width:  |  Height:  |  Size: 644 KiB

View File

Before

Width:  |  Height:  |  Size: 566 KiB

After

Width:  |  Height:  |  Size: 566 KiB

View File

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

View File

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

View File

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

@@ -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()

View File

@@ -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"

View File

@@ -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

View File

@@ -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
}
}
"""

View File

@@ -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

View File

@@ -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"
}
]
}
}

View File

@@ -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
}
}