Skip to content

Commit

Permalink
Rawr
Browse files Browse the repository at this point in the history
  • Loading branch information
Goldenfreddy0703 committed Nov 27, 2024
1 parent ae2a6e5 commit 62715d8
Show file tree
Hide file tree
Showing 15 changed files with 803 additions and 4 deletions.
2 changes: 1 addition & 1 deletion repo/plugin.video.otaku.testing/addon.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.otaku.testing" name="Otaku Testing" provider-name="TeamOtaku" version="5.1.23">
<addon id="plugin.video.otaku.testing" name="Otaku Testing" provider-name="TeamOtaku" version="5.1.24">
<requires>
<import addon="xbmc.python" version="3.0.0"/>
<import addon="script.module.beautifulsoup4"/>
Expand Down
3 changes: 3 additions & 0 deletions repo/plugin.video.otaku.testing/changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
Changelog v5.1.24 (2024-11-27):
- Added Airing Anime Calendar

Changelog v5.1.23 (2024-11-25):
- Added Warning Logs to Watchlist
- Fixed Bugs in Database
Expand Down
14 changes: 14 additions & 0 deletions repo/plugin.video.otaku.testing/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,19 @@ def WATCH_ORDER(payload, params):
control.draw_items(BROWSER.get_watch_order(mal_id), 'tvshows')


@Route('airing_calendar')
def AIRING_CALENDAR(payload, params):
airing = BROWSER.get_airing_calendar()
from resources.lib.windows.anichart import Anichart

anime = Anichart(*('anichart.xml', control.ADDON_PATH), get_anime=OtakuBrowser.get_anime_init, anime_items=airing).doModal()
if not anime:
return

anime, content_type = anime
control.draw_items(anime, content_type)


@Route('airing_last_season')
def AIRING_LAST_SEASON(payload, params):
page = int(params.get('page', 1))
Expand Down Expand Up @@ -547,6 +560,7 @@ def FANART(payload, params):
@Route('')
def LIST_MENU(payload, params):
MENU_ITEMS = [
(control.lang(30957), "airing_calendar", 'airing_anime_calendar.png'),
(control.lang(30901), "airing_last_season", 'airing_anime.png'),
(control.lang(30902), "airing_this_season", 'airing_anime.png'),
(control.lang(30903), "airing_next_season", 'airing_anime.png'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,10 @@ msgctxt "#30956"
msgid "Search TV Shows"
msgstr ""

msgctxt "#30957"
msgid "Airing Anime Calendar"
msgstr ""

######TOOLS & SEARCH DIRECTORY#######
msgctxt "#30010"
msgid "View Changelog"
Expand Down
129 changes: 129 additions & 0 deletions repo/plugin.video.otaku.testing/resources/lib/AniListBrowser.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,39 @@ def get_season_year(self, period='current'):
return season, year


def get_airing_calendar(self, page=1, format_in=''):
import datetime
import time
import itertools

today = datetime.date.today()
today_ts = int(time.mktime(today.timetuple()))
weekStart = today_ts - 86400
weekEnd = today_ts + (86400 * 6)
variables = {
'weekStart': weekStart,
'weekEnd': weekEnd,
'page': page
}

list_ = []

for i in range(0, 4):
popular = self.get_airing_calendar_res(variables, page)
list_.append(popular)

if not popular['pageInfo']['hasNextPage']:
break

page += 1
variables['page'] = page

results = list(map(self.process_airing_view, list_))
results = list(itertools.chain(*results))
airing = database.get(lambda x, y: results, 12, page, self.format_in_type)
return airing


def get_airing_last_season(self, page):
season, year = self.get_season_year('last')
variables = {
Expand Down Expand Up @@ -1685,6 +1718,61 @@ def get_anilist_res(self, variables):
json_res = results['data']['Media']
return json_res

def get_airing_calendar_res(self, variables, page=1):
query = '''
query (
$weekStart: Int,
$weekEnd: Int,
$page: Int,
){
Page(page: $page) {
pageInfo {
hasNextPage
total
}
airingSchedules(
airingAt_greater: $weekStart
airingAt_lesser: $weekEnd
) {
id
episode
airingAt
media {
id
idMal
title {
romaji
userPreferred
english
}
description
countryOfOrigin
genres
averageScore
isAdult
rankings {
rank
type
season
}
coverImage {
extraLarge
}
bannerImage
}
}
}
}
'''

r = requests.post(self._URL, json={'query': query, 'variables': variables})
results = r.json()
if "errors" in results.keys():
return
json_res = results['data']['Page']
return json_res

def get_anilist_res_with_mal_id(self, variables):
query = '''
query($idMal: Int, $type: MediaType){Media(idMal: $idMal, type: $type) {
Expand Down Expand Up @@ -1787,6 +1875,14 @@ def process_watch_order_view(self, json_res):
all_results = list(filter(lambda x: True if x else False, map(mapfunc, res)))
return all_results

def process_airing_view(self, json_res):
import time
filter_json = [x for x in json_res['airingSchedules'] if x['media']['isAdult'] is False]
ts = int(time.time())
mapfunc = partial(self.base_airing_view, ts=ts)
all_results = list(map(mapfunc, filter_json))
return all_results

def process_res(self, res):
self.database_update_show(res)
get_meta.collect_meta([res])
Expand Down Expand Up @@ -1894,6 +1990,39 @@ def base_anilist_view(self, res, completed=None, mal_dub=None):
return utils.parse_view(base, False, True, dub)
return utils.parse_view(base, True, False, dub)

def base_airing_view(self, res, ts):
import datetime
airingAt = datetime.datetime.fromtimestamp(res['airingAt'])
airingAt_day = airingAt.strftime('%A')
airingAt_time = airingAt.strftime('%I:%M %p')
airing_status = 'airing' if res['airingAt'] > ts else 'aired'
rank = None
rankings = res['media']['rankings']
if rankings and rankings[-1]['season']:
rank = rankings[-1]['rank']
genres = res['media']['genres']
if genres:
genres = ' | '.join(genres[:3])
else:
genres = 'Genres Not Found'
title = res['media']['title'][self._TITLE_LANG]
if not title:
title = res['media']['title']['userPreferred']

base = {
'release_title': title,
'poster': res['media']['coverImage']['extraLarge'],
'ep_title': '{} {} {}'.format(res['episode'], airing_status, airingAt_day),
'ep_airingAt': airingAt_time,
'averageScore': res['media']['averageScore'],
'rank': rank,
'plot': res['media']['description'].replace('<br><br>', '[CR]').replace('<br>', '').replace('<i>', '[I]').replace('</i>', '[/I]') if res['media']['description'] else res['media']['description'],
'genres': genres,
'id': res['media']['idMal']
}

return base

def database_update_show(self, res):
mal_id = res.get('idMal')

Expand Down
133 changes: 133 additions & 0 deletions repo/plugin.video.otaku.testing/resources/lib/MalBrowser.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ def __init__(self):
self.adult = 'true' if control.getSetting('search.adult') == "false" else 'false'
self.genre = self.load_genres_from_json() if control.getBool('contentgenre.bool') else ''

self.simkl_cache = None

if self.genre == ('',):
self.genre = ''

Expand Down Expand Up @@ -62,6 +64,13 @@ def process_mal_view(self, res, base_plugin_url, page):
all_results += self.handle_paging(hasNextPage, base_plugin_url, page)
return all_results

def process_airing_view(self, json_res):
import time
ts = int(time.time())
mapfunc = partial(self.base_airing_view, ts=ts)
all_results = list(map(mapfunc, json_res['data']))
return all_results

def process_res(self, res):
self.database_update_show(res)
get_meta.collect_meta([res])
Expand Down Expand Up @@ -152,6 +161,63 @@ def get_season_year(self, period='current'):
season_start_date_last, season_end_date_last, year_start_date_last, year_end_date_last,
season_start_date_next, season_end_date_next, year_start_date_next, year_end_date_next)

def get_airing_calendar(self, page=1, format_in=''):
import time

days_of_week = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday']
list_ = []

for day in days_of_week:
day_results = []
current_page = page
request_count = 0

while True:
retries = 3
popular = None
while retries > 0:
popular = self.get_airing_calendar_res(day, current_page)
if popular and 'data' in popular:
break
retries -= 1
time.sleep(1) # Add delay before retrying

if not popular or 'data' not in popular:
break

day_results.extend(popular['data'])

if not popular['pagination']['has_next_page']:
break

current_page += 1
request_count += 1

if request_count >= 3:
time.sleep(1) # Add delay to respect API rate limit
request_count = 0

day_results.reverse()
list_.extend(day_results)

# Wrap the results in a dictionary that mimics the API response structure
wrapped_results = {
"pagination": {
"last_visible_page": 1,
"has_next_page": False,
"current_page": 1,
"items": {
"count": len(list_),
"total": len(list_),
"per_page": 25
}
},
"data": list_
}

airing = self.process_airing_view(wrapped_results)
return airing


def get_anime(self, mal_id):
res = database.get(self.get_base_res, 24, f"{self._URL}/anime/{mal_id}")
Expand Down Expand Up @@ -1299,6 +1365,14 @@ def get_base_res(url, params=None):
return r.json()


def get_airing_calendar_res(self, day, page=1):
url = f'{self._URL}/schedules?kids=false&sfw=false&limit=25&page={page}&filter={day}'
results = self.get_base_res(url)
if "error" in results.keys():
return
return results


@div_flavor
def recommendation_relation_view(self, res, completed=None, mal_dub=None):
if res.get('entry'):
Expand Down Expand Up @@ -1454,6 +1528,65 @@ def base_mal_view(self, res, completed=None, mal_dub=None):
return utils.parse_view(base, False, True, dub)
return utils.parse_view(base, True, False, dub)

def fetch_and_find_simkl_entry(self, mal_id):
if self.simkl_cache is None:
url = 'https://data.simkl.in/calendar/anime.json'
response = requests.get(url)
if response.status_code == 200:
self.simkl_cache = response.json()
else:
return None

for entry in self.simkl_cache:
if entry['ids']['mal'] == str(mal_id):
return entry
return None

def base_airing_view(self, res, ts):
import datetime
airingAt = datetime.datetime.fromisoformat(res['aired']['from'].replace('Z', '+00:00'))
airingAt_day = airingAt.strftime('%A')
airingAt_time = airingAt.strftime('%I:%M %p')
airing_status = 'airing' if airingAt.timestamp() > ts else 'aired'
rank = None
genres = [genre['name'] for genre in res['genres']]
if genres:
genres = ' | '.join(genres[:3])
else:
genres = 'Genres Not Found'
title = res['title']
episode = res.get('episode', 'N/A')
rating = res['score']

# Find Simkl entry
simkl_entry = self.fetch_and_find_simkl_entry(res['mal_id'])
if simkl_entry:
episode = simkl_entry['episode']['episode']
rating = simkl_entry['ratings']['simkl']['rating']
airingAt = datetime.datetime.fromisoformat(simkl_entry['date'].replace('Z', '+00:00'))
airingAt_day = airingAt.strftime('%A')
airingAt_time = airingAt.strftime('%I:%M %p')
airing_status = 'airing' if airingAt.timestamp() > ts else 'aired'

if rating is not None:
score = f"{rating * 10:.0f}"
else:
score = 'N/A'

base = {
'release_title': title,
'poster': res['images']['jpg']['image_url'],
'ep_title': '{} {} {}'.format(episode, airing_status, airingAt_day),
'ep_airingAt': airingAt_time,
'averageScore': score,
'rank': rank,
'plot': res['synopsis'].replace('<br><br>', '[CR]').replace('<br>', '').replace('<i>', '[I]').replace('</i>', '[/I]') if res['synopsis'] else res['synopsis'],
'genres': genres,
'id': res['mal_id']
}

return base

def database_update_show(self, res):
mal_id = res['mal_id']

Expand Down
4 changes: 4 additions & 0 deletions repo/plugin.video.otaku.testing/resources/lib/ui/control.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,18 @@
completed_json = os.path.join(dataPath, 'completed.json')
genre_json = os.path.join(dataPath, 'genres.json')

IMAGES_PATH = os.path.join(ADDON_PATH, 'resources', 'images')
OTAKU_LOGO_PATH = os.path.join(ADDON_PATH, 'resources', 'images', 'trans-goku.png')
OTAKU_LOGO2_PATH = os.path.join(ADDON_PATH, 'resources', 'images', 'trans-goku-small.png')
OTAKU_LOGO3_PATH = os.path.join(ADDON_PATH, 'resources', 'images', 'trans-goku-large.png')
OTAKU_ICONS_PATH = os.path.join(ADDON_PATH, 'resources', 'images', 'icons', ADDON.getSetting("interface.icons"))
OTAKU_GENRE_PATH = os.path.join(ADDON_PATH, 'resources', 'images', 'genres')

dialogWindow = xbmcgui.WindowDialog
menuItem = xbmcgui.ListItem
execute = xbmc.executebuiltin
get_region = xbmc.getRegion
trakt_gmt_format = '%Y-%m-%dT%H:%M:%S.000Z'
progressDialog = xbmcgui.DialogProgress()
playList = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)

Expand Down
Loading

0 comments on commit 62715d8

Please sign in to comment.