# -*- coding: utf-8 -*- ################################################################################################# import json import logging import requests import os import shutil import sys import xbmc import xbmcgui import xbmcplugin import xbmcvfs import api import artwork import downloadutils import playutils as putils import playlist import read_embyserver as embyserver import shutil import embydb_functions as embydb from database import DatabaseConn from utils import window, settings, language as lang ################################################################################################# log = logging.getLogger("EMBY."+__name__) ################################################################################################# class PlaybackUtils(object): def __init__(self, item=None, item_id=None): self.artwork = artwork.Artwork() self.emby = embyserver.Read_EmbyServer() self.item = item or self.emby.getItem(item_id) self.API = api.API(self.item) self.server = window('emby_server%s' % window('emby_currUser')) self.stack = [] if self.item['Type'] == "Audio": self.playlist = xbmc.PlayList(xbmc.PLAYLIST_MUSIC) else: self.playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO) def _detect_widgets(self): if (not xbmc.getCondVisibility('Window.IsMedia') and ((self.item['Type'] == "Audio" and not xbmc.getCondVisibility('Integer.IsGreater(Playlist.Length(music),1)')) or not xbmc.getCondVisibility('Integer.IsGreater(Playlist.Length(video),1)'))): return True return False def play(self, item_id, dbid=None, force_transcode=False): listitem = xbmcgui.ListItem() log.info("Play called: %s", self.item['Name']) resume = window('emby.resume') window('emby.resume', clear=True) play_url = putils.PlayUtils(self.item, listitem).get_play_url(force_transcode) if not play_url: if play_url == False: # User backed-out of menu self.playlist.clear() return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listitem) seektime = 0 if resume == "true" else self.API.adjust_resume(self.API.get_userdata()['Resume']) if force_transcode: log.info("Clear the playlist.") self.playlist.clear() self.set_playlist(play_url, item_id, listitem, seektime, dbid) ##### SETUP PLAYBACK ''' To get everything to work together, play the first item in the stack with setResolvedUrl, add the rest to the regular playlist. ''' index = max(self.playlist.getposition(), 0) + 1 # Can return -1 force_play = False ''' Krypton 17.6 broke StartOffset. Seems to be working in Leia. For now, set up using StartPercent and adjust a bit to compensate. TODO: Once Leia is fully supported, move back to StartOffset. ''' if seektime: seektime_percent = ((seektime/self.API.get_runtime()) * 100) - 0.40 log.info("seektime detected (percent): %s", seektime_percent) listitem.setProperty('StartPercent', str(seektime_percent)) # Stack: [(url, listitem), (url, ...), ...] self.stack[0][1].setPath(self.stack[0][0]) try: if self._detect_widgets(): # widgets do not fill artwork correctly log.info("Detected widget.") raise IndexError xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, self.stack[0][1]) self.stack.pop(0) # remove the first item we just started. except IndexError: log.info("Playback activated via the context menu or widgets.") force_play = True for stack in self.stack: self.playlist.add(url=stack[0], listitem=stack[1], index=index) index += 1 if force_play: xbmc.Player().play(self.playlist) def set_playlist(self, play_url, item_id, listitem, seektime=None, db_id=None): ##### CHECK FOR INTROS if settings('enableCinema') == "true" and not seektime: self._set_intros(item_id) ##### ADD MAIN ITEM self.set_properties(play_url, listitem) self.set_listitem(listitem, db_id) self.stack.append([play_url, listitem]) ##### ADD ADDITIONAL PARTS if self.item.get('PartCount'): self._set_additional_parts(item_id) def _set_intros(self, item_id): # if we have any play them when the movie/show is not being resumed intros = self.emby.get_intros(item_id) if intros['Items']: enabled = True if settings('askCinema') == "true": resp = xbmcgui.Dialog().yesno("Emby for Kodi", lang(33016)) if not resp: # User selected to not play trailers enabled = False log.info("Skip trailers.") if enabled: for intro in intros['Items']: listitem = xbmcgui.ListItem() url = putils.PlayUtils(intro, listitem).get_play_url() log.info("Adding Intro: %s", url) self.set_artwork(listitem, self.item['Type']) self.set_listitem(listitem) self.stack.append([url, listitem]) window('emby.skip.%s' % self.item['Id'], value="true") def _set_additional_parts(self, item_id): parts = self.emby.get_additional_parts(item_id) for part in parts['Items']: listitem = xbmcgui.ListItem() url = putils.PlayUtils(part, listitem).get_play_url() log.info("Adding additional part: %s", url) # Set listitem and properties for each additional parts pb = PlaybackUtils(part) pb.set_properties(url, listitem) self.stack.append([url, listitem]) def set_listitem(self, listitem, dbid=None): people = self.API.get_people() mediatype = self.item['Type'] metadata = { 'title': self.item.get('Name', "Missing name"), 'year': self.item.get('ProductionYear'), 'plot': self.API.get_overview(), 'director': people.get('Director'), 'writer': people.get('Writer'), 'mpaa': self.API.get_mpaa(), 'genre': " / ".join(self.item['Genres']), 'studio': " / ".join(self.API.get_studios()), 'aired': self.API.get_premiere_date(), 'rating': self.item.get('CommunityRating'), 'votes': self.item.get('VoteCount') } if mediatype == "Episode": # Only for tv shows metadata['mediatype'] = "episode" metadata['TVShowTitle'] = self.item.get('SeriesName', "") metadata['season'] = self.item.get('ParentIndexNumber', -1) metadata['episode'] = self.item.get('IndexNumber', -1) elif mediatype == "Movie": metadata['mediatype'] = "movie" elif mediatype == "MusicVideo": metadata['mediatype'] = "musicvideo" elif mediatype == "Audio": metadata['mediatype'] = "song" else: metadata['mediatype'] = "video" if dbid: metadata['dbid'] = dbid else: metadata['dbid'] = None listitem.setProperty('IsPlayable', 'true') listitem.setProperty('IsFolder', 'false') listitem.setLabel(metadata['title']) listitem.setInfo('music' if mediatype == "Audio" else 'video', infoLabels=metadata) def set_properties(self, url, listitem): # Set all properties necessary for plugin path playback item_id = self.item['Id'] item_type = self.item['Type'] info = window('emby_%s.play.json' % url) window('emby_%s.play.json' % url, clear=True) window('emby_%s.json' % url, { 'url': url, 'runtime': str(self.item.get('RunTimeTicks')), 'type': item_type, 'id': item_id, 'mediasource_id': info.get('mediasource_id', item_id), 'refreshid': self.item.get('SeriesId') if item_type == "Episode" else item_id, 'playmethod': info['playmethod'], 'playsession_id': info['playsession_id'] }) self.set_artwork(listitem, item_type) listitem.setCast(self.API.get_actors()) def set_artwork(self, listitem, item_type): all_artwork = self.artwork.get_all_artwork(self.item, parent_info=True) # Set artwork for listitem if item_type == "Episode": art = { 'poster': "Series.Primary", 'tvshow.poster': "Series.Primary", 'clearart': "Art", 'tvshow.clearart': "Art", 'clearlogo': "Logo", 'tvshow.clearlogo': "Logo", 'discart': "Disc", 'fanart_image': "Backdrop", 'landscape': "Thumb", 'tvshow.landscape': "Thumb", 'thumb': "Primary" } else: art = { 'poster': "Primary", 'clearart': "Art", 'clearlogo': "Logo", 'discart': "Disc", 'fanart_image': "Backdrop", 'landscape': "Thumb", 'thumb': "Primary" } for k_art, e_art in art.items(): if e_art == "Backdrop": self._set_art(listitem, k_art, all_artwork[e_art][0] if all_artwork[e_art] else " ") else: self._set_art(listitem, k_art, all_artwork.get(e_art, " ")) def _set_art(self, listitem, art, path): if art in ('fanart_image', 'small_poster', 'tiny_poster', 'medium_landscape', 'medium_poster', 'small_fanartimage', 'medium_fanartimage', 'fanart_noindicators'): listitem.setProperty(art, path) else: listitem.setArt({art: path}) def play_all(self, item_ids, seektime=None, **kwargs): self.playlist.clear() started = False for item_id in item_ids: listitem = xbmcgui.ListItem() db_id = None item = self.emby.getItem(item_id) play_url = putils.PlayUtils(item, listitem, **kwargs if item_ids.index(item_id) == 0 else {}).get_play_url() if not play_url: log.info("Failed to retrieve playurl") continue log.info("Playurl: %s", play_url) with DatabaseConn('emby') as cursor: item_db = embydb.Embydb_Functions(cursor).getItem_byId(item_id) db_id = item_db[0] if item_db else None pbutils = PlaybackUtils(item) pbutils.set_playlist(play_url, item_id, listitem, seektime if item_ids.index(item_id) == 1 else None, db_id) if item_ids.index(item_id) == 1 and seektime: log.info("Seektime detected: %s", self.API.adjust_resume(seektime)) listitem.setProperty('StartOffset', str(self.API.adjust_resume(seektime))) index = max(pbutils.playlist.getposition(), 0) + 1 # Can return -1 for stack in pbutils.stack: pbutils.playlist.add(url=stack[0], listitem=stack[1], index=index) index += 1 if not started: started = True item = window('emby_%s.json' % play_url) item['forcedaudio'] = kwargs.get('AudioStreamIndex') item['forcedsubs'] = kwargs.get('SubtitleStreamIndex') window('emby_%s.json' % play_url, value=item) player = xbmc.Player() player.play(pbutils.playlist) if started: return True