def onPlayBackStarted(self): spam('Playback Started') try: if plugin_addon.getSetting('enableEigakan') == 'true': log('Player is set to use Transcoding') self.is_transcoded = True # wait until the player is init'd and playing self.set_duration() self.PlaybackStatus = PlaybackStatus.PLAYING # we are making the player global, so if a stop is issued, then Playing will change while not self.isPlaying( ) and self.PlaybackStatus == PlaybackStatus.PLAYING: xbmc.sleep(250) # TODO get series and populate info so we know if its movie or not # TODO maybe we could read trakt_id from shoko, self.is_movie = False if self.duration > 0 and self.scrobble: scrobble_trakt(self.ep_id, 1, self.getTime(), self.duration, self.is_movie) self.start_loops() except: eh.exception(ErrorPriority.HIGHEST)
def finished_episode(ep_id, file_id, current_time, total_time): _finished = False spam( 'finished_episode > ep_id = %s, file_id = %s, current_time = %s, total_time = %s' % (ep_id, file_id, current_time, total_time)) mark = float(plugin_addon.getSetting('watched_mark')) if plugin_addon.getSetting('external_player').lower() == 'false': pass else: # mitigate the external player, skipping intro/outro/pv so we cut your setting in half mark /= 2 mark /= 100 spam('mark = %s * total (%s) = %s vs current = %s' % (mark, total_time, (total_time * mark), current_time)) if (total_time * mark) <= current_time: _finished = True log('Video current_time (%s) has passed watch mark (%s). Marking is as watched!' % (current_time, (total_time * mark))) # TODO this got broken for addons in Leia18, until this is somehow fixed we count time by hand (in loop) # else: # external set position = 1.0 when it want to mark it as watched (based on configuration of external # if current_time > 0.0: # _finished = True # else: # log('Using an external player, but the settings are set to not mark as watched. Check advancedsettings.xml') #_finished = False if _finished: if int(ep_id) != 0 and plugin_addon.getSetting( 'vote_always') == 'true': spam('vote_always, voting on episode') script_utils.vote_for_episode(ep_id) if ep_id != 0: from shoko_models.v2 import Episode ep = Episode(ep_id, build_full_object=False) spam('mark as watched, episode') ep.set_watched_status(True) # vote on finished series if plugin_addon.getSetting('vote_on_series') == 'true': from shoko_models.v2 import get_series_for_episode series = get_series_for_episode(ep_id) # voting should be only when you really watch full series spam('vote_on_series, mark: %s / %s' % (series.sizes.watched_episodes, series.sizes.total_episodes)) if series.sizes.watched_episodes - series.sizes.total_episodes == 0: script_utils.vote_for_series(series.id) elif file_id != 0: # file watched states pass # refresh only when we really did watch episode, this way we wait until all action after watching are executed script_utils.arbiter(10, 'Container.Refresh')
def get_client_settings(): settings = {} try: settings = json.loads( pyproxy.get_json(eigakan_host + '/api/clientid/%s' % clientid)) except http_error as er: if er.code == 404: log('Client profile not found on Eigakan, sending new one...') kodi_utils.send_profile() return settings
def clean_videolibrary_scan(): log('====> VideoLibrary Scan: Start <====') index_start = 0 index_stop = 10 index_count = 100 json_id = 0 while index_stop < index_count: spam('=====> series : %s -- %s / %s' % (index_start, index_stop, index_count)) results = kodi_rpc( 'VideoLibrary.GetEpisodes', '"properties":["tvshowid","title","uniqueid"]', ',"limits":{"start":%d,"end":%d}' % (index_start, index_stop), json_id) if 'result' not in results: spam('====> no result found <====') break json_id = int(results['id']) index_start = results['result']['limits'].get('start', 0) index_stop = results['result']['limits'].get('end', 0) index_count = results['result']['limits'].get('total', 0) for tvshows in results['result'].get('episodes', []): tid = tvshows.get('tvshowid', 0) eid = tvshows.get('episodeid', 0) said = 0 # shoko_aid seid = 0 # shoko_eid aaid = 0 # anidb_aid aeid = 0 # anidb_eid if 'uniqueid' in tvshows: if 'shoko_aid' in tvshows[ 'uniqueid'] and 'shoko_eid' in tvshows['uniqueid']: said = tvshows['uniqueid'].get('shoko_aid', 0) seid = tvshows['uniqueid'].get('shoko_eid', 0) aaid = tvshows['uniqueid'].get('anidb_aid', 0) aeid = tvshows['uniqueid'].get('anidb_eid', 0) spam(' ====> uniqueid: %s => %s => %s, %s => %s => %s' % (tid, said, aaid, eid, seid, aeid)) if tid > 0 and eid > 0 and said > 0 and seid > 0: if not series.check(tid=tid, aid=aaid, sid=said): series.add_map(tid=tid, aid=aaid, sid=said) spam(' ====> add_series: %s => %s, %s' % (tid, aaid, said)) if not episodes.check(vlid=eid, eid=aeid, sid=seid): episodes.add_map(vlid=eid, eid=aeid, sid=seid) spam(' ====> add_episod: %s => %s, %s' % (eid, aeid, seid)) index_step = index_stop - index_start index_start = index_stop index_stop += index_step log('====> VideoLibrary Scan: Finish')
def process_queue_of_watched_episodes(): s_watch = True if xbmcaddon.Addon('service.nakamori').getSetting( 'sv-watch') == 'true' else False s_rate = True if xbmcaddon.Addon('service.nakamori').getSetting( 'sv-rate') == 'true' else False if s_watch or s_rate: log('===> process_queue_of_watched_episodes()') queue = sync.get_queue() for q in queue: a_aid = q[0] a_eid = q[1] shoko_eid = q[2] rating = q[3] vl_ep_id = episodes.get(eid=a_eid, sid=shoko_eid) if vl_ep_id is not None: vl_ep_id = vl_ep_id[0] # we found mapping spam('===> process_queue_of_watched_episodes vl_ep_id: %s' % vl_ep_id) r = kodi_rpc( 'VideoLibrary.GetEpisodeDetails', '"episodeid": %s,"properties":["playcount","rating"]' % vl_ep_id) if 'result' in r and 'episodedetails' in r['result']: if s_watch: if r['result']['episodedetails']['playcount'] == 0: m = kodi_rpc( 'VideoLibrary.SetEpisodeDetails', '"episodeid":%s,"playcount": 1' % vl_ep_id) if m.get('result', '') == "OK": spam('===> mark watched %s - OK' % vl_ep_id) if s_rate: if rating > 0: # maybe add force to re-rate it ? if r['result']['episodedetails']['rating'] == 0: m = kodi_rpc( 'VideoLibrary.SetEpisodeDetails', '"episodeid":%s,"rating": %d' % (vl_ep_id, rating)) if m.get('result', '') == "OK": spam('===> rating %s - %s' % (vl_ep_id, rating)) else: spam('----> missing mapping ! %s %s %s <----' % (a_aid, a_eid, shoko_eid)) # try to clear queue return sync.clear_queue(len(queue)) else: log('===> process_queue_of_watched_episodes() - DISABLED') return False
def onAVStarted(self): # Will be called when Kodi has a video or audiostream, before playing file spam('onAVStarted') # isExternalPlayer() ONLY works when isPlaying(), other than that it throw 0 always # setting it before results in false setting try: is_external = str(kodi_proxy.external_player(self)).lower() plugin_addon.setSetting(id='external_player', value=is_external) except: eh.exception(ErrorPriority.HIGH) spam(self) if kodi_proxy.external_player(self): log('Using External Player') self.is_external = True
def is_dialog_active(): x = -1 try: x = xbmcgui.getCurrentWindowDialogId() x = int(x) log('----- > is_dialog_window_is_visible: %s' % x) # if there is any, wait 0.25s xbmc.sleep(250) except: eh.spam('----- > is_dialog_is_visible: NONE') pass # https://github.com/xbmc/xbmc/blob/master/xbmc/guilib/WindowIDs.h # 10138 - busy,loading if 10099 <= x <= 10160: return True return False
def query_last_watched_episodes(): s_watch = True if xbmcaddon.Addon('service.nakamori').getSetting( 'sv-watch') == 'true' else False s_rate = True if xbmcaddon.Addon('service.nakamori').getSetting( 'sv-rate') == 'true' else False if s_watch or s_rate: log('====> query_last_watched_episodes') from nakamori_utils.globalvars import server # [{"type":"ep","view":1,"view_date":"2019-09-03T13:42:36.9194063+02:00","eptype":"Episode","epnumber":10,"aid":14662,"eid":219322,"id":74,"name":"Episode 10","summary":"Episode Overview not Available","year":"2019","air":"2019-09-02","rating":"2.80","votes":"1","art":{}}] today = date.today().strftime("%Y-%m-%d") offset = 0 limit = 100 # setting without limit results in loop url = server + '/api/ep/last_watched?query=%s&limit=%s&offset=%s' % ( today, limit, offset) spam('====> url: %s' % url) x = pyproxy.get_json(url, True) if x is not None and len(x) > 2: # [] x = json.loads(x) while len(x) > 0: for y in x: if isinstance(y, dict): spam('====> query_last_watched_episodes x: %s %s' % (type(y), y)) watch_date = y.get('view_date', '') aid = y.get('aid', 0) eid = y.get('eid', 0) shoko_eid = y.get('id', 0) user_rating = 0 if s_rate: user_rating = y.get('userrating', 0) sync.add_to_queue(aid, eid, shoko_eid, user_rating) offset = offset + limit url = server + '/api/ep/last_watched?query=%s&limit=%s&offset=%s' % ( today, limit, offset) spam('====> url: %s' % url) x = pyproxy.get_json(url, True) if x is None: break if x == '[]': break # finish checking sync.add_date(today) else: log('====> query_last_watched_episodes - DISABLED')
def debug_init(): """ start debugger if it's enabled also dump argv if spamLog :return: """ if plugin_addon.getSetting('remote_debug') == 'true': # try pycharm first try: import pydevd # try to connect multiple times...in case we forgot to start it # TODO Show a message to the user that we are waiting on the debugger connected = False tries = 0 while not connected and tries < 60: try: pydevd.settrace(host=plugin_addon.getSetting('remote_ip'), stdoutToServer=True, stderrToServer=True, port=5678, suspend=False) eh.spam('Connected to debugger') connected = True except: tries += 1 # we keep this message the same, as kodi will merge them into Previous line repeats... eh.spam('Failed to connect to debugger') xbmc.sleep(1000) except (ImportError, NameError): eh.log( 'unable to import pycharm debugger, falling back on the web-pdb' ) try: import web_pdb web_pdb.set_trace() except Exception: eh.exception(ErrorPriority.NORMAL, 'Unable to start debugger, disabling it') plugin_addon.setSetting('remote_debug', 'false') except: eh.exception(ErrorPriority.HIGHEST, 'Unable to start debugger') eh.spam('argv:', sys.argv)
def onPlayBackSeek(self, time_to_seek, seek_offset): log('Playback Paused - time_to_seek=%s seek_offset=%s' % (time_to_seek, seek_offset)) self.time = self.getTime() self.scrobble_time()
def process_transcoder(file_id, file_url, force_transcode_play=False): """ :param file_id: :param file_url: :param force_transcode_play: force transcode :return: """ is_transcoded = False m3u8_url = '' subs_type = '' is_finished = False if plugin_addon.getSetting( 'enableEigakan') != 'true' and not force_transcode_play: return is_transcoded, m3u8_url, subs_type, is_finished video_url = trancode_url(file_id) post_data = '"file":"' + file_url + '"' is_dash = True end_url = eigakan_host + '/api/video/%s/%s/end.eigakan' % (clientid, file_id) if is_dash: m3u8_url = eigakan_host + '/api/video/%s/%s/play.mpd' % (clientid, file_id) ts_url = eigakan_host + '/api/video/%s/%s/%s' % (clientid, file_id, magic_chunk) else: m3u8_url = eigakan_host + '/api/video/%s/%s/play.m3u8' % (clientid, file_id) ts_url = eigakan_host + '/api/video/%s/%s/play0.ts' % (clientid, file_id) try: kodi_utils.check_eigakan() # server is alive so send profile of device we didn't before if plugin_addon.getSetting('eigakan_handshake') == 'false': kodi_utils.send_profile() settings = get_client_settings() # check if file is already transcoded is_finished = pyproxy.head(url_in=end_url) if not is_finished: # let's probe file, maybe we already know which streams we want busy.create(plugin_addon.getLocalizedString(30160), plugin_addon.getLocalizedString(30177)) audio_streams, subs_streams = eigakan_utils.probe_file( file_id, file_url) busy.close() # pick streams that are preferred via profile on eigakan a_index, s_index, subs_type = eigakan_utils.pick_best_streams( audio_streams, subs_streams) # region BUSY Dialog Hell # please wait, Sending request to Transcode server... busy.create(plugin_addon.getLocalizedString(30160), plugin_addon.getLocalizedString(30165)) if a_index > -1: post_data += ',"audio":"%s"' % a_index if s_index > -1: post_data += ',"subtitles":"%s"' % s_index pyproxy.post_json(video_url, post_data, custom_timeout=0.1) # non blocking xbmc.sleep(1000) # busy.close() try_count = 0 found = False # please wait,waiting for being queued busy.update(0, plugin_addon.getLocalizedString(30192)) while True: if busy.iscanceled(): break if eigakan_utils.is_fileid_added_to_transcoder(file_id): break try_count += 1 busy.update(try_count) xbmc.sleep(1000) try_count = 0 found = False # plase wait, waiting for subs to be dump busy.update(try_count, plugin_addon.getLocalizedString(30205)) while True: if busy.iscanceled(): break ask_for_subs = json.loads( pyproxy.get_json(eigakan_host + '/api/queue/%s' % file_id)) if ask_for_subs is None: ask_for_subs = {} y = ask_for_subs.get('queue', {"videos": {}}).get('videos', {}) for k in y: if int(k) == int(file_id): found = True break if found: break if found: break try_count += 1 if try_count >= 100: try_count = 0 busy.update(try_count, plugin_addon.getLocalizedString(30218)) busy.update(try_count) xbmc.sleep(1000) try_count = 0 found = False # please waiting, waiting for starting transcode busy.update(try_count, plugin_addon.getLocalizedString(30206)) while True: if busy.iscanceled(): break ask_for_subs = json.loads( pyproxy.get_json(eigakan_host + '/api/queue/%s' % file_id)) if ask_for_subs is None: ask_for_subs = {} x = ask_for_subs.get('queue', {"videos": {}}).get('videos', {}) for k in x: if int(k) == int(file_id): percent = x[k].get('percent', 0) if int(percent) > 0: found = True log('percent found of transcoding: %s' % percent) break if found: break try_count += 1 if try_count >= 100: try_count = 0 busy.update(try_count, plugin_addon.getLocalizedString(30218)) busy.update(try_count) xbmc.sleep(1000) try_count = 0 # please wait, Waiting for response from Server... busy.update(try_count, plugin_addon.getLocalizedString(30164)) while True: if busy.iscanceled(): break if pyproxy.head(url_in=ts_url) is False: try_count += 1 busy.update(try_count) xbmc.sleep(1000) else: break busy.close() # endregion if pyproxy.head(url_in=ts_url): is_transcoded = True except: eh.exception(ErrorPriority.BLOCKING) try: busy.close() except: pass return is_transcoded, m3u8_url, subs_type, is_finished
def player_loop(player, is_transcoded, is_transcode_finished, ep_id, party_mode): try: monitor = xbmc.Monitor() # seek to beginning of stream :hack: https://github.com/peak3d/inputstream.adaptive/issues/94 if is_transcoded: while not xbmc.Player().isPlayingVideo(): monitor.waitForAbort(0.25) if not is_transcode_finished: if xbmc.Player().isPlayingVideo(): log('Seek back - so the stream is from beginning') # TODO part1: hack is temporary and not working in 100% # TODO part2: (with small segments + fast cpu, you wont start from 1st segment) #xbmc.executebuiltin('Seek(-60)') xbmc.executeJSONRPC( '{"jsonrpc":"2.0","method":"Player.Seek","params":{"playerid":1,"value":{"seconds":0}},"id":1}' ) while player.PlaybackStatus != PlaybackStatus.STOPPED and player.PlaybackStatus != PlaybackStatus.ENDED: xbmc.sleep(500) if player.PlaybackStatus == PlaybackStatus.STOPPED or player.PlaybackStatus == PlaybackStatus.ENDED: log('Playback Ended - Shutting Down: ', monitor.abortRequested()) if player.is_finished: log('post-finish: start events') if ep_id != 0: from shoko_models.v2 import Episode ep = Episode(ep_id, build_full_object=False) spam('mark as watched, episode') ep.set_watched_status(True) # wait till directory is loaded while kodi_utils.is_dialog_active(): xbmc.sleep(500) # refresh it, so it moves onto next item and the mark watched is refreshed kodi_utils.refresh() # wait till it load again while kodi_utils.is_dialog_active(): xbmc.sleep(500) if int(ep_id) != 0 and plugin_addon.getSetting( 'vote_always') == 'true' and not party_mode: spam('vote_always, voting on episode') script_utils.vote_for_episode(ep_id) if int(ep_id) != 0 and plugin_addon.getSetting( 'vote_on_series') == 'true' and not party_mode: from shoko_models.v2 import get_series_for_episode series = get_series_for_episode(ep_id) # voting should be only when you really watch full series spam('vote_on_series, mark: %s / %s' % (series.sizes.watched_episodes, series.sizes.total_episodes)) if series.sizes.watched_episodes - series.sizes.total_episodes == 0: script_utils.vote_for_series(series.id) return -1 else: log( 'Playback Ended - Playback status was not "Stopped" or "Ended". It was ', player.PlaybackStatus) return 0 except: eh.exception(ErrorPriority.HIGHEST) return -1
def play_video(file_id, ep_id=0, mark_as_watched=True, resume=False, force_direct_play=False, force_transcode_play=False, party_mode=False): """ Plays a file :param file_id: file ID. It is needed to look up the file :param ep_id: episode ID, not needed, but it fills in a lot of info :param mark_as_watched: should we mark it after playback :param resume: should we auto-resume :param force_direct_play: force direct play :param force_transcode_play: force transcoding file :return: True if successfully playing """ eh.spam('Processing play_video %s %s %s %s %s %s' % (file_id, ep_id, mark_as_watched, resume, force_direct_play, force_transcode_play)) from shoko_models.v2 import Episode, File, get_series_for_episode # check if we're already playing something player = xbmc.Player() if player.isPlayingVideo(): playing_item = player.getPlayingFile() log('Player is currently playing %s' % playing_item) log('Player Stopping') player.stop() # wait for it to stop while True: try: if not player.isPlayingVideo(): break xbmc.sleep(500) continue except: pass # now continue file_url = '' if int(ep_id) != 0: ep = Episode(ep_id, build_full_object=True) series = get_series_for_episode(ep_id) ep.series_id = series.id ep.series_name = series.name item = ep.get_listitem() f = ep.get_file_with_id(file_id) else: f = File(file_id, build_full_object=True) item = f.get_listitem() if item is not None: if resume: # TODO looks like this does nothing... item.resume() else: item.setProperty('ResumeTime', '0') file_url = f.url_for_player if f is not None else None if file_url is not None: is_transcoded = False m3u8_url = '' subs_extension = '' is_finished = False if not force_direct_play: if 'smb://' in file_url: file_url = f.remote_url_for_player is_transcoded, m3u8_url, subs_extension, is_finished = process_transcoder( file_id, file_url, force_transcode_play) player = Player() player.feed(file_id, ep_id, f.duration, m3u8_url if is_transcoded else file_url, mark_as_watched) try: item.setProperty('IsPlayable', 'true') if is_transcoded: #player.play(item=m3u8_url) url_for_player = m3u8_url item.setPath(url_for_player) item.setProperty('inputstreamaddon', 'inputstream.adaptive') item.setProperty('inputstream.adaptive.manifest_type', 'mpd') item.setMimeType('application/dash+xml') item.setContentLookup(False) # TODO maybe extract all subs and include them ? subs_url = eigakan_host + '/api/video/%s/%s/subs.%s' % ( clientid, file_id, subs_extension) if pyproxy.head(url_in=subs_url): item.setSubtitles([ subs_url, ]) item.addStreamInfo('subtitle', { 'language': 'Default', }) else: #file_url = f.remote_url_for_player #player.play(item=file_url, listitem=item) url_for_player = f.url_for_player # file_url item.setPath(url_for_player) handle = int(sys.argv[1]) if handle == -1: player.play(item=url_for_player, listitem=item) else: # thanks to anxdpanic for pointing in right direction xbmcplugin.setResolvedUrl(handle, True, item) except: eh.exception(ErrorPriority.BLOCKING) # leave player alive so we can handle onPlayBackStopped/onPlayBackEnded # TODO Move the instance to Service, so that it is never disposed xbmc.sleep(int(plugin_addon.getSetting('player_sleep'))) return player_loop(player, is_transcoded, is_finished, ep_id, party_mode)