Improved next page feature
[plugin.video.crunchyroll.git] / resources / lib / controller.py
blob:a/resources/lib/controller.py -> blob:b/resources/lib/controller.py
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Crunchyroll # Crunchyroll
# Copyright (C) 2018 MrKrabat # Copyright (C) 2018 MrKrabat
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as # it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the # published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version. # License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details. # GNU Affero General Public License for more details.
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
   
import sys import sys
import json  
import time import time
   
import xbmc import xbmc
import xbmcgui import xbmcgui
import xbmcplugin import xbmcplugin
   
from . import api from . import api
from . import view from . import view
   
   
def showQueue(args): def showQueue(args):
""" shows anime queue/playlist """ shows anime queue/playlist
""" """
# api request # api request
payload = {"media_types": "anime|drama", payload = {"media_types": "anime|drama",
"fields": "media.name,media.media_id,media.collection_id,media.collection_name,media.description,media.episode_number,media.created, \ "fields": "media.name,media.media_id,media.collection_id,media.collection_name,media.description,media.episode_number,media.created, \
media.screenshot_image,media.premium_only,media.premium_available,media.available,media.premium_available,media.duration, \ media.screenshot_image,media.premium_only,media.premium_available,media.available,media.premium_available,media.duration, \
series.series_id,series.year,series.publisher_name,series.rating,series.genres,series.landscape_image"} series.series_id,series.year,series.publisher_name,series.rating,series.genres,series.landscape_image"}
req = api.request(args, "queue", payload) req = api.request(args, "queue", payload)
   
# check for error # check for error
if req["error"]: if req["error"]:
view.add_item(args, {"title": args._addon.getLocalizedString(30061)}) view.add_item(args, {"title": args._addon.getLocalizedString(30061)})
view.endofdirectory() view.endofdirectory()
return False return False
   
# display media # display media
for item in req["data"]: for item in req["data"]:
# video no longer available # video no longer available
if not ("most_likely_media" in item and "series" in item and item["most_likely_media"]["available"] and item["most_likely_media"]["premium_available"]): if not ("most_likely_media" in item and "series" in item and item["most_likely_media"]["available"] and item["most_likely_media"]["premium_available"]):
continue continue
   
# add to view # add to view
view.add_item(args, view.add_item(args,
{"title": item["most_likely_media"]["collection_name"] + " #" + item["most_likely_media"]["episode_number"] + " - " + item["most_likely_media"]["name"], {"title": item["most_likely_media"]["collection_name"] + " #" + item["most_likely_media"]["episode_number"] + " - " + item["most_likely_media"]["name"],
"tvshowtitle": item["most_likely_media"]["collection_name"], "tvshowtitle": item["most_likely_media"]["collection_name"],
"duration": item["most_likely_media"]["duration"], "duration": item["most_likely_media"]["duration"],
"playcount": 1 if (100/float(item["most_likely_media"]["duration"]))*int(item["playhead"]) > 90 else 0, "playcount": 1 if (100/float(item["most_likely_media"]["duration"]))*int(item["playhead"]) > 90 else 0,
"episode": item["most_likely_media"]["episode_number"], "episode": item["most_likely_media"]["episode_number"],
"episode_id": item["most_likely_media"]["media_id"], "episode_id": item["most_likely_media"]["media_id"],
"collection_id": item["most_likely_media"]["collection_id"], "collection_id": item["most_likely_media"]["collection_id"],
"series_id": item["series"]["series_id"], "series_id": item["series"]["series_id"],
"plot": item["most_likely_media"]["description"], "plot": item["most_likely_media"]["description"],
"plotoutline": item["most_likely_media"]["description"], "plotoutline": item["most_likely_media"]["description"],
"genre": ", ".join(item["series"]["genres"]), "genre": ", ".join(item["series"]["genres"]),
"year": item["series"]["year"], "year": item["series"]["year"],
"aired": item["most_likely_media"]["created"][:10], "aired": item["most_likely_media"]["created"][:10],
"premiered": item["most_likely_media"]["created"][:10], "premiered": item["most_likely_media"]["created"][:10],
"studio": item["series"]["publisher_name"], "studio": item["series"]["publisher_name"],
"rating": int(item["series"]["rating"])/10.0, "rating": int(item["series"]["rating"])/10.0,
"thumb": item["most_likely_media"]["screenshot_image"]["fwidestar_url"] if item["most_likely_media"]["premium_only"] else item["most_likely_media"]["screenshot_image"]["full_url"], "thumb": item["most_likely_media"]["screenshot_image"]["fwidestar_url"] if item["most_likely_media"]["premium_only"] else item["most_likely_media"]["screenshot_image"]["full_url"],
"fanart": item["series"]["landscape_image"]["full_url"], "fanart": item["series"]["landscape_image"]["full_url"],
"mode": "videoplay"}, "mode": "videoplay"},
isFolder=False) isFolder=False)
   
view.endofdirectory() view.endofdirectory()
return True return True
   
   
def searchAnime(args): def searchAnime(args):
"""Search for anime """Search for anime
""" """
# ask for search string # ask for search string
d = xbmcgui.Dialog().input(args._addon.getLocalizedString(30021), type=xbmcgui.INPUT_ALPHANUM) if not hasattr(args, "search"):
if not d: d = xbmcgui.Dialog().input(args._addon.getLocalizedString(30021), type=xbmcgui.INPUT_ALPHANUM)
return if not d:
  return
  else:
  d = args.search
   
# api request # api request
payload = {"media_types": "anime|drama", payload = {"media_types": "anime|drama",
"q": d, "q": d,
  "limit": 30,
  "offset": int(getattr(args, "offset", 0)),
"fields": "series.name,series.series_id,series.description,series.year,series.publisher_name, \ "fields": "series.name,series.series_id,series.description,series.year,series.publisher_name, \
series.genres,series.portrait_image,series.landscape_image"} series.genres,series.portrait_image,series.landscape_image"}
req = api.request(args, "autocomplete", payload) req = api.request(args, "autocomplete", payload)
   
# check for error # check for error
if req["error"]: if req["error"]:
view.add_item(args, {"title": args._addon.getLocalizedString(30061)}) view.add_item(args, {"title": args._addon.getLocalizedString(30061)})
view.endofdirectory() view.endofdirectory()
return False return False
   
# display media # display media
for item in req["data"]: for item in req["data"]:
# add to view # add to view
view.add_item(args, view.add_item(args,
{"title": item["name"], {"title": item["name"],
"tvshowtitle": item["name"], "tvshowtitle": item["name"],
"series_id": item["series_id"], "series_id": item["series_id"],
"plot": item["description"], "plot": item["description"],
"plotoutline": item["description"], "plotoutline": item["description"],
"genre": ", ".join(item["genres"]), "genre": ", ".join(item["genres"]),
"year": item["year"], "year": item["year"],
"studio": item["publisher_name"], "studio": item["publisher_name"],
"thumb": item["portrait_image"]["full_url"], "thumb": item["portrait_image"]["full_url"],
"fanart": item["landscape_image"]["full_url"], "fanart": item["landscape_image"]["full_url"],
"mode": "series"}, "mode": "series"},
isFolder=True) isFolder=True)
   
  # show next page button
  if len(req["data"]) >= 30:
  view.add_item(args,
  {"title": args._addon.getLocalizedString(30044),
  "offset": int(getattr(args, "offset", 0)) + 30,
  "search": d,
  "mode": args.mode},
  isFolder=True)
   
view.endofdirectory() view.endofdirectory()
return True return True
   
   
def showHistory(args): def showHistory(args):
""" shows history of watched anime """ shows history of watched anime
""" """
# api request # api request
payload = {"media_types": "anime|drama", payload = {"media_types": "anime|drama",
  "limit": 30,
  "offset": int(getattr(args, "offset", 0)),
"fields": "media.name,media.media_id,media.collection_id,media.collection_name,media.description,media.episode_number,media.created, \ "fields": "media.name,media.media_id,media.collection_id,media.collection_name,media.description,media.episode_number,media.created, \
media.screenshot_image,media.premium_only,media.premium_available,media.available,media.premium_available,media.duration,media.playhead, \ media.screenshot_image,media.premium_only,media.premium_available,media.available,media.premium_available,media.duration,media.playhead, \
series.series_id,series.year,series.publisher_name,series.rating,series.genres,series.landscape_image"} series.series_id,series.year,series.publisher_name,series.rating,series.genres,series.landscape_image"}
req = api.request(args, "recently_watched", payload) req = api.request(args, "recently_watched", payload)
   
# check for error # check for error
if req["error"]: if req["error"]:
view.add_item(args, {"title": args._addon.getLocalizedString(30061)}) view.add_item(args, {"title": args._addon.getLocalizedString(30061)})
view.endofdirectory() view.endofdirectory()
return False return False
   
# display media # display media
for item in req["data"]: for item in req["data"]:
# video no longer available # video no longer available
if not ("media" in item and "series" in item and item["media"]["available"] and item["media"]["premium_available"]): if not ("media" in item and "series" in item and item["media"]["available"] and item["media"]["premium_available"]):
continue continue
   
# add to view # add to view
view.add_item(args, view.add_item(args,
{"title": item["media"]["collection_name"] + " #" + item["media"]["episode_number"] + " - " + item["media"]["name"], {"title": item["media"]["collection_name"] + " #" + item["media"]["episode_number"] + " - " + item["media"]["name"],
"tvshowtitle": item["media"]["collection_name"], "tvshowtitle": item["media"]["collection_name"],
"duration": item["media"]["duration"], "duration": item["media"]["duration"],
"playcount": 1 if (100/float(item["media"]["duration"]))*int(item["media"]["playhead"]) > 90 else 0, "playcount": 1 if (100/float(item["media"]["duration"]))*int(item["media"]["playhead"]) > 90 else 0,
"episode": item["media"]["episode_number"], "episode": item["media"]["episode_number"],
"episode_id": item["media"]["media_id"], "episode_id": item["media"]["media_id"],
"collection_id": item["media"]["collection_id"], "collection_id": item["media"]["collection_id"],
"series_id": item["series"]["series_id"], "series_id": item["series"]["series_id"],
"plot": item["media"]["description"], "plot": item["media"]["description"],
"plotoutline": item["media"]["description"], "plotoutline": item["media"]["description"],
"genre": ", ".join(item["series"]["genres"]), "genre": ", ".join(item["series"]["genres"]),
"year": item["series"]["year"], "year": item["series"]["year"],
"aired": item["media"]["created"][:10], "aired": item["media"]["created"][:10],
"premiered": item["media"]["created"][:10], "premiered": item["media"]["created"][:10],
"studio": item["series"]["publisher_name"], "studio": item["series"]["publisher_name"],
"rating": int(item["series"]["rating"])/10.0, "rating": int(item["series"]["rating"])/10.0,
"thumb": item["media"]["screenshot_image"]["fwidestar_url"] if item["media"]["premium_only"] else item["media"]["screenshot_image"]["full_url"], "thumb": item["media"]["screenshot_image"]["fwidestar_url"] if item["media"]["premium_only"] else item["media"]["screenshot_image"]["full_url"],
"fanart": item["series"]["landscape_image"]["full_url"], "fanart": item["series"]["landscape_image"]["full_url"],
"mode": "videoplay"}, "mode": "videoplay"},
isFolder=False) isFolder=False)
   
  # show next page button
  if len(req["data"]) >= 30:
  view.add_item(args,
  {"title": args._addon.getLocalizedString(30044),
  "offset": int(getattr(args, "offset", 0)) + 30,
  "mode": args.mode},
  isFolder=True)
   
view.endofdirectory() view.endofdirectory()
return True return True
   
   
def listSeries(args, mode): def listSeries(args, mode):
""" view all anime from selected mode """ view all anime from selected mode
""" """
# api request # api request
payload = {"media_type": args.genre, payload = {"media_type": args.genre,
"filter": mode, "filter": mode,
"limit": 30, "limit": 30,
"offset": int(getattr(args, "offset", 0)), "offset": int(getattr(args, "offset", 0)),
"fields": "series.name,series.series_id,series.description,series.year,series.publisher_name, \ "fields": "series.name,series.series_id,series.description,series.year,series.publisher_name, \
series.genres,series.portrait_image,series.landscape_image"} series.genres,series.portrait_image,series.landscape_image"}
req = api.request(args, "list_series", payload) req = api.request(args, "list_series", payload)
   
# check for error # check for error
if req["error"]: if req["error"]:
view.add_item(args, {"title": args._addon.getLocalizedString(30061)}) view.add_item(args, {"title": args._addon.getLocalizedString(30061)})
view.endofdirectory() view.endofdirectory()
return False return False
   
# display media # display media
for item in req["data"]: for item in req["data"]:
# add to view # add to view
view.add_item(args, view.add_item(args,
{"title": item["name"], {"title": item["name"],
"tvshowtitle": item["name"], "tvshowtitle": item["name"],
"series_id": item["series_id"], "series_id": item["series_id"],
"plot": item["description"], "plot": item["description"],
"plotoutline": item["description"], "plotoutline": item["description"],
"genre": ", ".join(item["genres"]), "genre": ", ".join(item["genres"]),
"year": item["year"], "year": item["year"],
"studio": item["publisher_name"], "studio": item["publisher_name"],
"thumb": item["portrait_image"]["full_url"], "thumb": item["portrait_image"]["full_url"],
"fanart": item["landscape_image"]["full_url"], "fanart": item["landscape_image"]["full_url"],
"mode": "series"}, "mode": "series"},
isFolder=True) isFolder=True)
   
# show next page button # show next page button
if len(req["data"]) >= 30: if len(req["data"]) >= 30:
view.add_item(args, view.add_item(args,
{"title": args._addon.getLocalizedString(30044), {"title": args._addon.getLocalizedString(30044),
"offset": int(getattr(args, "offset", 0)) + 30, "offset": int(getattr(args, "offset", 0)) + 30,
  "search": getattr(args, "search", ""),
"mode": args.mode}, "mode": args.mode},
isFolder=True) isFolder=True)
   
view.endofdirectory() view.endofdirectory()
return True return True
   
   
def listFilter(args, mode): def listFilter(args, mode):
""" view all anime from selected mode """ view all anime from selected mode
""" """
# test if filter is selected # test if filter is selected
if hasattr(args, "search"): if hasattr(args, "search"):
return listSeries(args, "tag:" + args.search) return listSeries(args, "tag:" + args.search)
   
# api request # api request
payload = {"media_type": args.genre} payload = {"media_type": args.genre}
req = api.request(args, "categories", payload) req = api.request(args, "categories", payload)
   
# check for error # check for error
if req["error"]: if req["error"]:
view.add_item(args, {"title": args._addon.getLocalizedString(30061)}) view.add_item(args, {"title": args._addon.getLocalizedString(30061)})
view.endofdirectory() view.endofdirectory()
return False return False
   
# display media # display media
for item in req["data"][mode]: for item in req["data"][mode]:
# add to view # add to view
view.add_item(args, view.add_item(args,
{"title": item["label"], {"title": item["label"],
"search": item["tag"], "search": item["tag"],
"mode": args.mode}, "mode": args.mode},
isFolder=True) isFolder=True)
   
view.endofdirectory() view.endofdirectory()
return True return True
   
   
def viewSeries(args): def viewSeries(args):
""" view all seasons/arcs of an anime """ view all seasons/arcs of an anime
""" """
# api request # api request
payload = {"series_id": args.series_id, payload = {"series_id": args.series_id,
"fields": "collection.name,collection.collection_id,collection.description,collection.media_type,collection.created, \ "fields": "collection.name,collection.collection_id,collection.description,collection.media_type,collection.created, \
collection.season,collection.complete,collection.portrait_image,collection.landscape_image"} collection.season,collection.complete,collection.portrait_image,collection.landscape_image"}
req = api.request(args, "list_collections", payload) req = api.request(args, "list_collections", payload)
   
# check for error # check for error
if req["error"]: if req["error"]:
view.add_item(args, {"title": args._addon.getLocalizedString(30061)}) view.add_item(args, {"title": args._addon.getLocalizedString(30061)})
view.endofdirectory() view.endofdirectory()
return False return False
   
# display media # display media
for item in req["data"]: for item in req["data"]:
# add to view # add to view
view.add_item(args, view.add_item(args,
{"title": item["name"], {"title": item["name"],
"tvshowtitle": item["name"], "tvshowtitle": item["name"],
"season": item["season"], "season": item["season"],
"collection_id": item["collection_id"], "collection_id": item["collection_id"],
"series_id": args.series_id, "series_id": args.series_id,
"plot": item["description"], "plot": item["description"],
"plotoutline": item["description"], "plotoutline": item["description"],
"genre": item["media_type"], "genre": item["media_type"],
"aired": item["created"][:10], "aired": item["created"][:10],
"premiered": item["created"][:10], "premiered": item["created"][:10],
"status": u"Completed" if item["complete"] else u"Continuing", "status": u"Completed" if item["complete"] else u"Continuing",
"thumb": item["portrait_image"]["full_url"] if item["portrait_image"] else args.thumb, "thumb": item["portrait_image"]["full_url"] if item["portrait_image"] else args.thumb,
"fanart": item["landscape_image"]["full_url"] if item["landscape_image"] else args.fanart, "fanart": item["landscape_image"]["full_url"] if item["landscape_image"] else args.fanart,
"mode": "episodes"}, "mode": "episodes"},
isFolder=True) isFolder=True)
   
view.endofdirectory() view.endofdirectory()
return True return True
   
   
def viewEpisodes(args): def viewEpisodes(args):
""" view all episodes of season """ view all episodes of season
""" """
# api request # api request
payload = {"collection_id": args.collection_id, payload = {"collection_id": args.collection_id,
  "limit": 30,
  "offset": int(getattr(args, "offset", 0)),
"fields": "media.name,media.media_id,media.collection_id,media.collection_name,media.description,media.episode_number,media.created,media.series_id, \ "fields": "media.name,media.media_id,media.collection_id,media.collection_name,media.description,media.episode_number,media.created,media.series_id, \
media.screenshot_image,media.premium_only,media.premium_available,media.available,media.premium_available,media.duration,media.playhead"} media.screenshot_image,media.premium_only,media.premium_available,media.available,media.premium_available,media.duration,media.playhead"}
req = api.request(args, "list_media", payload) req = api.request(args, "list_media", payload)
   
# check for error # check for error
if req["error"]: if req["error"]:
view.add_item(args, {"title": args._addon.getLocalizedString(30061)}) view.add_item(args, {"title": args._addon.getLocalizedString(30061)})
view.endofdirectory() view.endofdirectory()
return False return False
   
# display media # display media
for item in req["data"]: for item in req["data"]:
# add to view # add to view
view.add_item(args, view.add_item(args,
{"title": item["collection_name"] + " #" + item["episode_number"] + " - " + item["name"], {"title": item["collection_name"] + " #" + item["episode_number"] + " - " + item["name"],
"tvshowtitle": item["collection_name"], "tvshowtitle": item["collection_name"],
"duration": item["duration"], "duration": item["duration"],
"playcount": 1 if (100/float(item["duration"]))*int(item["playhead"]) > 90 else 0, "playcount": 1 if (100/float(item["duration"]))*int(item["playhead"]) > 90 else 0,
"episode": item["episode_number"], "episode": item["episode_number"],
"episode_id": item["media_id"], "episode_id": item["media_id"],
"collection_id": args.collection_id, "collection_id": args.collection_id,
"series_id": item["series_id"], "series_id": item["series_id"],
"plot": item["description"], "plot": item["description"],
"plotoutline": item["description"], "plotoutline": item["description"],
"aired": item["created"][:10], "aired": item["created"][:10],
"premiered": item["created"][:10], "premiered": item["created"][:10],
"thumb": item["screenshot_image"]["fwidestar_url"] if item["premium_only"] else item["screenshot_image"]["full_url"], "thumb": item["screenshot_image"]["fwidestar_url"] if item["premium_only"] else item["screenshot_image"]["full_url"],
"fanart": args.fanart, "fanart": args.fanart,
"mode": "videoplay"}, "mode": "videoplay"},
isFolder=False) isFolder=False)
   
  # show next page button
  if len(req["data"]) >= 30:
  view.add_item(args,
  {"title": args._addon.getLocalizedString(30044),
  "collection_id": args.collection_id,
  "offset": int(getattr(args, "offset", 0)) + 30,
  "thumb": args.thumb,
  "fanart": args.fanart,
  "mode": args.mode},
  isFolder=True)
   
view.endofdirectory() view.endofdirectory()
return True return True
   
   
def startplayback(args): def startplayback(args):
""" plays an episode """ plays an episode
""" """
# api request # api request
payload = {"media_id": args.episode_id, payload = {"media_id": args.episode_id,
"fields": "media.duration,media.playhead,media.stream_data"} "fields": "media.duration,media.playhead,media.stream_data"}
req = api.request(args, "info", payload) req = api.request(args, "info", payload)
   
# check for error # check for error
if req["error"]: if req["error"]:
item = xbmcgui.ListItem(getattr(args, "title", "Title not provided")) item = xbmcgui.ListItem(getattr(args, "title", "Title not provided"))
xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, item) xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, item)
return False return False
   
# get stream url # get stream url
url = req["data"]["stream_data"]["streams"][0]["url"] url = req["data"]["stream_data"]["streams"][0]["url"]
for stream in req["data"]["stream_data"]["streams"]: for stream in req["data"]["stream_data"]["streams"]:
# TODO: get user selected quality # TODO: get user selected quality
url = stream["url"] url = stream["url"]
break break
   
# start playback # start playback
item = xbmcgui.ListItem(getattr(args, "title", "Title not provided"), path=url) item = xbmcgui.ListItem(getattr(args, "title", "Title not provided"), path=url)
item.setMimeType("application/vnd.apple.mpegurl") item.setMimeType("application/vnd.apple.mpegurl")
item.setContentLookup(False) item.setContentLookup(False)
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, item) xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, item)
   
if args._addon.getSetting("sync_playtime") == "true": if args._addon.getSetting("sync_playtime") == "true":
# wait for video to begin # wait for video to begin
player = xbmc.Player() player = xbmc.Player()
timeout = time.time() + 20 timeout = time.time() + 20
while not xbmc.getCondVisibility("Player.IsInternetStream"): while not xbmc.getCondVisibility("Player.IsInternetStream"):
xbmc.sleep(50) xbmc.sleep(50)
# timeout to prevent infinite loop # timeout to prevent infinite loop
if time.time() > timeout: if time.time() > timeout:
xbmc.log("[PLUGIN] %s: Timeout reached, video did not start in 20 seconds" % args._addonname, xbmc.LOGERROR) xbmc.log("[PLUGIN] %s: Timeout reached, video did not start in 20 seconds" % args._addonname, xbmc.LOGERROR)
return return
   
# ask if user want to continue playback # ask if user want to continue playback
resume = (100/float(req["data"]["duration"])) * int(req["data"]["playhead"]) resume = (100/float(req["data"]["duration"])) * int(req["data"]["playhead"])
if resume >= 5 and resume <= 90: if resume >= 5 and resume <= 90:
player.pause() player.pause()
if xbmcgui.Dialog().yesno(args._addonname, args._addon.getLocalizedString(30065) % resume): if xbmcgui.Dialog().yesno(args._addonname, args._addon.getLocalizedString(30065) % resume):
player.seekTime(int(req["data"]["playhead"]) - 5) player.seekTime(int(req["data"]["playhead"]) - 5)
player.pause() player.pause()
   
# update playtime at crunchyroll # update playtime at crunchyroll
try: try:
while url == player.getPlayingFile(): while url == player.getPlayingFile():
# wait 10 seconds # wait 10 seconds
xbmc.sleep(10000) xbmc.sleep(10000)
   
if url == player.getPlayingFile(): if url == player.getPlayingFile():
# api request # api request
payload = {"event": "playback_status", payload = {"event": "playback_status",
"media_id": args.episode_id, "media_id": args.episode_id,
"playhead": int(player.getTime())} "playhead": int(player.getTime())}
api.request(args, "log", payload) api.request(args, "log", payload)
except RuntimeError: except RuntimeError:
xbmc.log("[PLUGIN] %s: Playback aborted" % args._addonname, xbmc.LOGDEBUG) xbmc.log("[PLUGIN] %s: Playback aborted" % args._addonname, xbmc.LOGDEBUG)