From 9bf017ab4ba271f9bd533e10b0660ef493a6d74c Mon Sep 17 00:00:00 2001 From: Shaun Date: Mon, 23 Oct 2017 09:25:07 +1100 Subject: [plugin.video.embycon] 1.3.40 (#1450) * plugin.video.embycon-1.3.40 * [plugin.video.embycon] 1.3.40 * [plugin.video.embycon] 1.3.40 * [plugin.video.embycon] 1.3.40 --- plugin.video.embycon/addon.xml | 2 +- .../language/resource.language.en_gb/strings.po | 28 +- plugin.video.embycon/resources/lib/action_menu.py | 46 + .../resources/lib/downloadutils.py | 30 +- plugin.video.embycon/resources/lib/functions.py | 369 ++++--- .../resources/lib/menu_functions.py | 33 +- plugin.video.embycon/resources/lib/play_utils.py | 27 +- plugin.video.embycon/resources/lib/translation.py | 6 +- plugin.video.embycon/resources/lib/utils.py | 45 +- plugin.video.embycon/resources/lib/widgets.py | 398 ++++++++ plugin.video.embycon/resources/settings.xml | 10 +- .../resources/skins/default/720p/ActionMenu.xml | 67 ++ .../resources/skins/default/720p/ResumeDialog.xml | 1 - .../resources/skins/skin.estuary/xml/Home-17.1.xml | 12 +- .../skins/skin.estuary/xml/Home-17.3-elec.xml | 1064 ++++++++++++++++++++ .../resources/skins/skin.estuary/xml/Home-17.3.xml | 18 +- plugin.video.embycon/service.py | 57 +- 17 files changed, 1938 insertions(+), 275 deletions(-) create mode 100644 plugin.video.embycon/resources/lib/action_menu.py create mode 100644 plugin.video.embycon/resources/lib/widgets.py create mode 100644 plugin.video.embycon/resources/skins/default/720p/ActionMenu.xml create mode 100644 plugin.video.embycon/resources/skins/skin.estuary/xml/Home-17.3-elec.xml diff --git a/plugin.video.embycon/addon.xml b/plugin.video.embycon/addon.xml index fc03295..e985c77 100644 --- a/plugin.video.embycon/addon.xml +++ b/plugin.video.embycon/addon.xml @@ -1,7 +1,7 @@ diff --git a/plugin.video.embycon/resources/language/resource.language.en_gb/strings.po b/plugin.video.embycon/resources/language/resource.language.en_gb/strings.po index 3394583..17e02ed 100644 --- a/plugin.video.embycon/resources/language/resource.language.en_gb/strings.po +++ b/plugin.video.embycon/resources/language/resource.language.en_gb/strings.po @@ -94,6 +94,14 @@ msgctxt "#30025" msgid "Password:" msgstr "" +msgctxt "#30026" +msgid "Widget item select action" +msgstr "" + +msgctxt "#30027" +msgid "Show watched in recently added" +msgstr "" + msgctxt "#30044" msgid "Incorrect Username/Password" msgstr "" @@ -187,7 +195,7 @@ msgid "Play Error" msgstr "" msgctxt "#30129" -msgid "This item is not playable" +msgid "Default" msgstr "" msgctxt "#30135" @@ -327,7 +335,11 @@ msgid "Prompt to delete episode after %" msgstr "" msgctxt "#30218" -msgid "Prompt to play next episode after %" +msgid "Play next episode after %" +msgstr "" + +msgctxt "#30219" +msgid " - Prompt before play" msgstr "" msgctxt "#30220" @@ -367,7 +379,7 @@ msgid "Episodes" msgstr "" msgctxt "#30236" -msgid "Save" +msgid "Action for h265(hevc) video files" msgstr "" msgctxt "#30237" @@ -375,7 +387,7 @@ msgid "Start from beginning" msgstr "" msgctxt "#30238" -msgid "Default Sort" +msgid "Playback Overrides" msgstr "" msgctxt "#30245" @@ -549,3 +561,11 @@ msgstr "" msgctxt "#30288" msgid " - Latest" msgstr "" + +msgctxt "#30289" +msgid "TV Show (Genre)" +msgstr "" + +msgctxt "#30290" +msgid "All" +msgstr "" diff --git a/plugin.video.embycon/resources/lib/action_menu.py b/plugin.video.embycon/resources/lib/action_menu.py new file mode 100644 index 0000000..3521e44 --- /dev/null +++ b/plugin.video.embycon/resources/lib/action_menu.py @@ -0,0 +1,46 @@ +import xbmc +import xbmcaddon +import xbmcgui + +from simple_logging import SimpleLogging + +log = SimpleLogging(__name__) + +class ActionMenu(xbmcgui.WindowXMLDialog): + + selected_action = None + action_items = None + + def __init__(self, *args, **kwargs): + log.debug("ActionMenu: __init__") + xbmcgui.WindowXML.__init__(self, *args, **kwargs) + + def onInit(self): + log.debug("ActionMenu: onInit") + self.action_exitkeys_id = [10, 13] + + self.listControl = self.getControl(3000) + self.listControl.addItems(self.action_items) + self.setFocus(self.listControl) + + bg_image = self.getControl(3010) + bg_image.setHeight(50 * len(self.action_items) + 20) + + def onFocus(self, controlId): + pass + + def doAction(self, actionID): + pass + + def onClick(self, controlID): + if (controlID == 3000): + self.selected_action = self.listControl.getSelectedItem() + log.debug("ActionMenu: Selected Item:" + str(self.selected_action)) + self.close() + + def setActionItems(self, action_items): + self.action_items = action_items + + def getActionItem(self): + return self.selected_action + diff --git a/plugin.video.embycon/resources/lib/downloadutils.py b/plugin.video.embycon/resources/lib/downloadutils.py index de42284..3805038 100644 --- a/plugin.video.embycon/resources/lib/downloadutils.py +++ b/plugin.video.embycon/resources/lib/downloadutils.py @@ -81,12 +81,8 @@ class DownloadUtils(): def getArtwork(self, data, art_type, parent=False, index="0", width=10000, height=10000, server=None): id = data.get("Id") - ''' - if data.get("Type") == "Season": # For seasons: primary (poster), thumb and banner get season art, rest series art - if art_type != "Primary" and art_type != "Thumb" and art_type != "Banner": - id = data.get("SeriesId") - ''' - if data.get("Type") == "Episode": # For episodes: primary (episode thumb) gets episode art, rest series art. + + if data.get("Type") in ["Episode", "Season"]: if art_type != "Primary" or parent == True: id = data.get("SeriesId") @@ -117,7 +113,7 @@ class DownloadUtils(): imageTag = data.get("ImageTags").get(art_type) log.debug("Image Tag:" + imageTag) elif (parent == True): - if (itemType == "Episode") and (art_type == 'Primary'): + if (itemType == "Episode" or itemType == "Season") and art_type == 'Primary': tagName = 'SeriesPrimaryImageTag' idName = 'SeriesId' else: @@ -311,7 +307,7 @@ class DownloadUtils(): log.debug("EmbyCon Authentication Header : " + str(headers)) return headers - def downloadUrl(self, url, suppress=False, postBody=None, method="GET", popup=0, authenticate=True): + def downloadUrl(self, url, suppress=False, postBody=None, method="GET", popup=0, authenticate=True, headers=None): log.debug("downloadUrl") settings = xbmcaddon.Addon(id='plugin.video.embycon') @@ -325,6 +321,22 @@ class DownloadUtils(): if url.find("{ItemLimit}") != -1: show_x_filtered_items = settings.getSetting("show_x_filtered_items") url = url.replace("{ItemLimit}", show_x_filtered_items) + if url.find("{IsUnplayed}") != -1 or url.find("{,IsUnplayed}") != -1 or url.find("{IsUnplayed,}") != -1 \ + or url.find("{,IsUnplayed,}") != -1: + show_latest_unplayed = settings.getSetting("show_latest_unplayed") == "true" + if show_latest_unplayed: + url = url.replace("{IsUnplayed}", "") + url = url.replace("{,IsUnplayed}", "") + url = url.replace("{IsUnplayed,}", "") + url = url.replace("{,IsUnplayed,}", "") + elif url.find("{IsUnplayed}") != -1: + url = url.replace("{IsUnplayed}", "IsUnplayed") + elif url.find("{,IsUnplayed}") != -1: + url = url.replace("{,IsUnplayed}", ",IsUnplayed") + elif url.find("{IsUnplayed,}") != -1: + url = url.replace("{IsUnplayed,}", "IsUnplayed,") + elif url.find("{,IsUnplayed,}") != -1: + url = url.replace("{,IsUnplayed,}", ",IsUnplayed,") log.debug(url) return_data = "null" @@ -395,6 +407,8 @@ class DownloadUtils(): return_data = gzipper.read() else: return_data = retData + if headers is not None and isinstance(headers, dict): + headers.update(data.getheaders()) log.debug("Data Len After : " + str(len(return_data))) log.debug("====== 200 returned =======") log.debug("Content-Type : " + str(contentType)) diff --git a/plugin.video.embycon/resources/lib/functions.py b/plugin.video.embycon/resources/lib/functions.py index 34f0322..6f8f44e 100644 --- a/plugin.video.embycon/resources/lib/functions.py +++ b/plugin.video.embycon/resources/lib/functions.py @@ -25,6 +25,8 @@ from simple_logging import SimpleLogging from menu_functions import displaySections, showMovieAlphaList, showGenreList, showWidgets, showSearch from translation import i18n from server_sessions import showServerSessions +from action_menu import ActionMenu +from widgets import getWidgetContent, getWidgetContentCast, getWidgetContentSimilar, getWidgetContentNextUp, getSuggestions, getWidgetUrlContent, checkForNewContent __addon__ = xbmcaddon.Addon(id='plugin.video.embycon') __addondir__ = xbmc.translatePath(__addon__.getAddonInfo('profile')) @@ -63,7 +65,6 @@ def mainEntryPoint(): except: params = {} - checkService() home_window = HomeWindow() if (len(params) == 0): @@ -108,10 +109,14 @@ def mainEntryPoint(): delete(item_id) elif mode == "MOVIE_ALPHA": showMovieAlphaList() - elif mode == "MOVIE_GENRA": + elif mode == "MOVIE_GENRE": showGenreList() + elif mode == "SERIES_GENRE": + showGenreList(item_type="series") elif mode == "WIDGETS": showWidgets() + elif mode == "SHOW_MENU": + showMenu(params) elif mode == "SHOW_SETTINGS": __addon__.openSettings() WINDOW = xbmcgui.getCurrentWindowId() @@ -122,7 +127,17 @@ def mainEntryPoint(): home_window.setProperty("force_data_reload", "true") xbmc.executebuiltin("Container.Refresh") elif mode == "WIDGET_CONTENT": - getWigetContent(int(sys.argv[1]), params) + getWidgetContent(int(sys.argv[1]), params) + elif mode == "WIDGET_CONTENT_CAST": + getWidgetContentCast(int(sys.argv[1]), params) + elif mode == "WIDGET_CONTENT_SIMILAR": + getWidgetContentSimilar(int(sys.argv[1]), params) + elif mode == "WIDGET_CONTENT_NEXTUP": + getWidgetContentNextUp(int(sys.argv[1]), params) + elif mode == "WIDGET_CONTENT_SUGGESTIONS": + getSuggestions(int(sys.argv[1]), params) + elif mode == "WIDGET_CONTENT_URL": + getWidgetUrlContent(int(sys.argv[1]), params) elif mode == "PARENT_CONTENT": checkServer(notify=False) showParentContent(sys.argv[0], int(sys.argv[1]), params) @@ -187,6 +202,7 @@ def markWatched(item_id): downloadUtils.downloadUrl(url, postBody="", method="POST") home_window = HomeWindow() home_window.setProperty("force_data_reload", "true") + checkForNewContent() xbmc.executebuiltin("Container.Refresh") @@ -196,6 +212,7 @@ def markUnwatched(item_id): downloadUtils.downloadUrl(url, method="DELETE") home_window = HomeWindow() home_window.setProperty("force_data_reload", "true") + checkForNewContent() xbmc.executebuiltin("Container.Refresh") @@ -205,6 +222,7 @@ def markFavorite(item_id): downloadUtils.downloadUrl(url, postBody="", method="POST") home_window = HomeWindow() home_window.setProperty("force_data_reload", "true") + checkForNewContent() xbmc.executebuiltin("Container.Refresh") @@ -214,6 +232,7 @@ def unmarkFavorite(item_id): downloadUtils.downloadUrl(url, method="DELETE") home_window = HomeWindow() home_window.setProperty("force_data_reload", "true") + checkForNewContent() xbmc.executebuiltin("Container.Refresh") @@ -226,6 +245,8 @@ def delete(item_id): progress.create(i18n('deleting'), i18n('waiting_server_delete')) downloadUtils.downloadUrl(url, method="DELETE") progress.close() + home_window = HomeWindow() + checkForNewContent() xbmc.executebuiltin("Container.Refresh") @@ -248,6 +269,8 @@ def addGUIItem(url, details, extraData, display_options, folder=True): # Create the URL to pass to the item if folder: u = sys.argv[0] + "?url=" + urllib.quote(url) + mode + "&media_type=" + extraData["itemtype"] + if extraData.get("name_format"): + u += '&name_format=' + urllib.quote(extraData.get("name_format")) else: u = sys.argv[0] + "?item_id=" + url + "&mode=PLAY" @@ -257,7 +280,7 @@ def addGUIItem(url, details, extraData, display_options, folder=True): listItemName = details.get('title', i18n('unknown')) # calculate percentage - cappedPercentage = None + cappedPercentage = 0 if (extraData.get('resumetime') != None and int(extraData.get('resumetime')) > 0): duration = float(extraData.get('duration')) if (duration > 0): @@ -270,10 +293,6 @@ def addGUIItem(url, details, extraData, display_options, folder=True): watched = int(extraData.get('WatchedEpisodes')) percentage = int((float(watched) / float(totalItems)) * 100.0) cappedPercentage = percentage - if (cappedPercentage == 0): - cappedPercentage = None - if (cappedPercentage == 100): - cappedPercentage = None countsAdded = False addCounts = display_options.get("addCounts", True) @@ -282,7 +301,10 @@ def addGUIItem(url, details, extraData, display_options, folder=True): listItemName = listItemName + " (" + extraData.get('UnWatchedEpisodes') + ")" addResumePercent = display_options.get("addResumePercent", True) - if (countsAdded == False and addResumePercent and details.get('title') != None and cappedPercentage != None): + if (countsAdded == False + and addResumePercent + and details.get('title') != None + and cappedPercentage not in [0, 100]): listItemName = listItemName + " (" + str(cappedPercentage) + "%)" subtitle_available = display_options.get("addSubtitleAvailable", False) @@ -300,7 +322,7 @@ def addGUIItem(url, details, extraData, display_options, folder=True): log.debug("Setting thumbnail as " + thumbPath) # calculate percentage - if (cappedPercentage != None): + if (cappedPercentage != 0): list_item.setProperty("complete_percentage", str(cappedPercentage)) # For all end items @@ -312,7 +334,8 @@ def addGUIItem(url, details, extraData, display_options, folder=True): # StartPercent - artTypes = ['thumb', 'poster', 'fanart', 'clearlogo', 'discart', 'banner', 'clearart', 'landscape', 'tvshow.poster'] + artTypes = ['thumb', 'poster', 'fanart', 'clearlogo', 'discart', 'banner', 'clearart', + 'landscape', 'tvshow.poster', 'tvshow.clearart', 'tvshow.banner', 'tvshow.landscape'] artLinks = {} for artType in artTypes: artLinks[artType] = extraData.get(artType, '') @@ -352,6 +375,8 @@ def addGUIItem(url, details, extraData, display_options, folder=True): videoInfoLabels["director"] = extraData.get('director') videoInfoLabels["writer"] = extraData.get('writer') videoInfoLabels["year"] = extraData.get('year') + videoInfoLabels["premiered"] = extraData.get('premieredate') + videoInfoLabels["dateadded"] = extraData.get('dateadded') videoInfoLabels["studio"] = extraData.get('studio') videoInfoLabels["genre"] = extraData.get('genre') @@ -477,6 +502,7 @@ def setSort(pluginhandle, viewType): xbmcplugin.addSortMethod(pluginhandle, xbmcplugin.SORT_METHOD_VIDEO_SORT_TITLE_IGNORE_THE) xbmcplugin.addSortMethod(pluginhandle, xbmcplugin.SORT_METHOD_VIDEO_YEAR) + xbmcplugin.addSortMethod(pluginhandle, xbmcplugin.SORT_METHOD_DATEADDED) xbmcplugin.addSortMethod(pluginhandle, xbmcplugin.SORT_METHOD_GENRE) xbmcplugin.addSortMethod(pluginhandle, xbmcplugin.SORT_METHOD_UNSORTED) xbmcplugin.addSortMethod(pluginhandle, xbmcplugin.SORT_METHOD_NONE) @@ -539,6 +565,15 @@ def getContent(url, params): xbmcplugin.endOfDirectory(pluginhandle, cacheToDisc=False) + # if the view master addon is available then run the script + try: + view_addon = xbmcaddon.Addon("script.viewmaster") + if view_addon is not None: + xbmc.executebuiltin('RunScript(' + xbmc.translatePath( + "special://home/addons/script.viewmaster/default.py") + ',' + viewType + ')') + except: + pass + if (progress != None): progress.update(100, i18n('done')) progress.close() @@ -596,7 +631,10 @@ def processDirectory(results, progress, params): item_count = len(result) current_item = 1 - + first_season_item = None + total_unwatched = 0 + total_episodes = 0 + total_watched = 0 for item in result: if (progress != None): @@ -609,16 +647,27 @@ def processDirectory(results, progress, params): item_type = str(item.get("Type")).encode('utf-8') - tempEpisode = item.get("IndexNumber") - if tempEpisode is not None: - if tempEpisode < 10: - tempEpisode = "0" + str(tempEpisode) + if item_type == "Season" and first_season_item is None: + first_season_item = item + + # set the episode number + tempEpisode = "" + if item_type == "Episode": + tempEpisode = item.get("IndexNumber") + if tempEpisode is not None: + if tempEpisode < 10: + tempEpisode = "0" + str(tempEpisode) + else: + tempEpisode = str(tempEpisode) else: - tempEpisode = str(tempEpisode) - else: - tempEpisode = "" + tempEpisode = "" - tempSeason = item.get("ParentIndexNumber") + # set the season number + tempSeason = None + if item_type == "Episode": + tempSeason = item.get("ParentIndexNumber") + elif item_type == "Season": + tempSeason = item.get("IndexNumber") if tempSeason is not None: if tempSeason < 10: tempSeason = "0" + str(tempSeason) @@ -664,6 +713,12 @@ def processDirectory(results, progress, params): tokens = (item.get("PremiereDate")).split("T") premiere_date = tokens[0] + try: + date_added = item['DateCreated'] + date_added = date_added.split('.')[0].replace('T', " ") + except KeyError: + date_added = "" + # add the premiered date for Upcoming TV if item.get("LocationType") == "Virtual": airtime = item.get("AirTime") @@ -797,6 +852,9 @@ def processDirectory(results, progress, params): WatchedEpisodes = 0 if userData.get("UnplayedItemCount") == None else TotalEpisodes - userData.get("UnplayedItemCount") UnWatchedEpisodes = 0 if userData.get("UnplayedItemCount") == None else userData.get("UnplayedItemCount") NumEpisodes = TotalEpisodes + total_unwatched += UnWatchedEpisodes + total_episodes += TotalEpisodes + total_watched += WatchedEpisodes art = getArt(item, server) # Populate the extraData list @@ -809,12 +867,16 @@ def processDirectory(results, progress, params): 'clearart': art['clearart'], 'landscape': art['landscape'], 'tvshow.poster': art['tvshow.poster'], + 'tvshow.clearart': art['tvshow.clearart'], + 'tvshow.banner': art['tvshow.banner'], + 'tvshow.landscape': art['tvshow.landscape'], 'id': id, 'mpaa': item.get("OfficialRating"), 'rating': item.get("CommunityRating"), 'criticrating': item.get("CriticRating"), 'year': production_year, 'premieredate': premiere_date, + 'dateadded': date_added, 'locationtype': item.get("LocationType"), 'studio': studio, 'genre': genre, @@ -839,6 +901,7 @@ def processDirectory(results, progress, params): 'WatchedEpisodes': str(WatchedEpisodes), 'UnWatchedEpisodes': str(UnWatchedEpisodes), 'NumEpisodes': str(NumEpisodes), + 'OriginalTitle': item.get("Name").encode('utf-8'), 'itemtype': item_type, 'SubtitleLang': subtitle_lang, 'SubtitleAvailable': subtitle_available} @@ -869,165 +932,95 @@ def processDirectory(results, progress, params): u = id dirItems.append(addGUIItem(u, details, extraData, display_options, folder=False)) - return dirItems - -def getWigetContent(handle, params): - log.debug("getWigetContent Called" + str(params)) - server = downloadUtils.getServer() - - type = params.get("type") - if (type == None): - log.error("getWigetContent type not set") - return - - itemsUrl = ("{server}/emby/Users/{userid}/Items" - "?Limit=20" - "&format=json" - "&ImageTypeLimit=1" - "&IsMissing=False") - - if (type == "recent_movies"): - xbmcplugin.setContent(handle, 'movies') - itemsUrl += ("&Recursive=true" - "&SortBy=DateCreated" - "&SortOrder=Descending" - "&Filters=IsUnplayed,IsNotFolder" - "&IsVirtualUnaired=false" - "&IsMissing=False" - "&IncludeItemTypes=Movie") - elif (type == "inprogress_movies"): - xbmcplugin.setContent(handle, 'movies') - itemsUrl += ("&Recursive=true" - "&SortBy=DatePlayed" - "&SortOrder=Descending" - "&Filters=IsResumable" - "&IsVirtualUnaired=false" - "&IsMissing=False" - "&IncludeItemTypes=Movie") - elif (type == "random_movies"): - xbmcplugin.setContent(handle, 'movies') - itemsUrl += ("&Recursive=true" - "&SortBy=Random" - "&SortOrder=Descending" - "&Filters=IsUnplayed,IsNotFolder" - "&IsVirtualUnaired=false" - "&IsMissing=False" - "&IncludeItemTypes=Movie") - elif (type == "recent_episodes"): - xbmcplugin.setContent(handle, 'episodes') - itemsUrl += ("&Recursive=true" - "&SortBy=DateCreated" - "&SortOrder=Descending" - "&Filters=IsUnplayed,IsNotFolder" - "&IsVirtualUnaired=false" - "&IsMissing=False" - "&IncludeItemTypes=Episode") - elif (type == "inprogress_episodes"): - xbmcplugin.setContent(handle, 'episodes') - itemsUrl += ("&Recursive=true" - "&SortBy=DatePlayed" - "&SortOrder=Descending" - "&Filters=IsResumable" - "&IsVirtualUnaired=false" - "&IsMissing=False" - "&IncludeItemTypes=Episode") - elif (type == "nextup_episodes"): - xbmcplugin.setContent(handle, 'episodes') - itemsUrl = ("{server}/emby/Shows/NextUp" - "?Limit=20" - "&userid={userid}" - "&Recursive=true" - "&format=json" - "&ImageTypeLimit=1") - - log.debug("WIDGET_DATE_URL: " + itemsUrl) - - # get the items - jsonData = downloadUtils.downloadUrl(itemsUrl, suppress=False, popup=1) - log.debug("Recent(Items) jsonData: " + jsonData) - result = json.loads(jsonData) - - if result is None: - return [] - - result = result.get("Items") - if (result == None): - result = [] - - itemCount = 1 - listItems = [] - for item in result: - item_id = item.get("Id") - name = item.get("Name") - episodeDetails = "" - log.debug("WIDGET_DATE_NAME: " + name) - - title = item.get("Name") - tvshowtitle = "" - - if (item.get("Type") == "Episode" and item.get("SeriesName") != None): - - eppNumber = "X" - tempEpisodeNumber = "0" - if (item.get("IndexNumber") != None): - eppNumber = item.get("IndexNumber") - if eppNumber < 10: - tempEpisodeNumber = "0" + str(eppNumber) - else: - tempEpisodeNumber = str(eppNumber) - - seasonNumber = item.get("ParentIndexNumber") - if seasonNumber < 10: - tempSeasonNumber = "0" + str(seasonNumber) - else: - tempSeasonNumber = str(seasonNumber) - - episodeDetails = "S" + tempSeasonNumber + "E" + tempEpisodeNumber - name = item.get("SeriesName") + " " + episodeDetails - tvshowtitle = episodeDetails - title = item.get("SeriesName") - - art = getArt(item, server, widget=True) - - if kodi_version > 17: - list_item = xbmcgui.ListItem(label=name, iconImage=art['thumb'], offscreen=True) - else: - list_item = xbmcgui.ListItem(label=name, iconImage=art['thumb']) - - # list_item.setLabel2(episodeDetails) - list_item.setInfo(type="Video", infoLabels={"title": title, "tvshowtitle": tvshowtitle}) - list_item.setProperty('fanart_image', art['fanart']) # back compat - list_item.setProperty('discart', art['discart']) # not avail to setArt - list_item.setArt(art) - # add count - list_item.setProperty("item_index", str(itemCount)) - itemCount = itemCount + 1 - - list_item.setProperty('IsPlayable', 'true') - - totalTime = str(int(float(item.get("RunTimeTicks", "0")) / (10000000 * 60))) - list_item.setProperty('TotalTime', str(totalTime)) - - # add progress percent - userData = item.get("UserData") - if (userData != None): - playBackTicks = float(userData.get("PlaybackPositionTicks")) - if (playBackTicks != None and playBackTicks > 0): - runTimeTicks = float(item.get("RunTimeTicks", "0")) - if (runTimeTicks > 0): - playBackPos = int(((playBackTicks / 1000) / 10000) / 60) - list_item.setProperty('ResumeTime', str(playBackPos)) - - percentage = int((playBackTicks / runTimeTicks) * 100.0) - list_item.setProperty("complete_percentage", str(percentage)) - - playurl = "plugin://plugin.video.embycon/?item_id=" + item_id + '&mode=PLAY' + # add the all episodes item + if first_season_item is not None: + series_url = ('{server}/emby/Users/{userid}/items' + + '?ParentId=' + str(first_season_item.get("SeriesId")).encode('utf-8') + + '&IsVirtualUnAired=false' + + '&IsMissing=false' + + '&Fields=' + detailsString + + '&Recursive=true' + + '&IncludeItemTypes=Episode' + + '&format=json') + played = 0 + overlay = "7" + if total_unwatched == 0: + played = 1 + overlay = "6" + details = {'title': i18n('all'), + 'Overlay': overlay, + 'playcount': str(played) + } + art = getArt(first_season_item, server) + # Populate the extraData list + extraData = {'thumb': art['tvshow.poster'], + 'fanart': art['fanart'], + 'poster': art['tvshow.poster'], + 'banner': art['tvshow.banner'], + 'clearlogo': art['clearlogo'], + 'discart': art['discart'], + 'clearart': art['clearart'], + 'landscape': art['landscape'], + 'tvshow.poster': art['tvshow.poster'], + 'tvshow.clearart': art['tvshow.clearart'], + 'tvshow.banner': art['tvshow.banner'], + 'tvshow.landscape': art['tvshow.landscape'], + 'itemtype': 'Episodes', + 'UnWatchedEpisodes': str(total_unwatched), + 'TotalEpisodes': str(total_episodes), + 'WatchedEpisodes': str(total_watched), + 'playcount': str(played), + 'mode': 'GET_CONTENT', + 'name_format': 'Episode|episode_name_format'} + dirItems.append(addGUIItem(series_url, details, extraData, {}, folder=True)) - itemTupple = (playurl, list_item, False) - listItems.append(itemTupple) + return dirItems - xbmcplugin.addDirectoryItems(handle, listItems) - xbmcplugin.endOfDirectory(handle, cacheToDisc=False) +def showMenu(params): + log.debug("showMenu(): " + str(params)) + + action_items = [] + li = xbmcgui.ListItem("Play") + li.setProperty('menu_id', 'play') + action_items.append(li) + li = xbmcgui.ListItem("Force Transcode") + li.setProperty('menu_id', 'transcode') + action_items.append(li) + li = xbmcgui.ListItem("Mark Watched") + li.setProperty('menu_id', 'mark_watched') + action_items.append(li) + li = xbmcgui.ListItem("Mark Unwatched") + li.setProperty('menu_id', 'mark_unwatched') + action_items.append(li) + li = xbmcgui.ListItem("Delete") + li.setProperty('menu_id', 'delete') + action_items.append(li) + + action_menu = ActionMenu("ActionMenu.xml", PLUGINPATH, "default", "720p") + action_menu.setActionItems(action_items) + action_menu.doModal() + selected_action_item = action_menu.getActionItem() + selected_action = "" + if selected_action_item is not None: + selected_action = selected_action_item.getProperty('menu_id') + log.debug("Menu Action Selected: " + str(selected_action_item)) + del action_menu + + if selected_action == "play": + log.debug("Play Item") + PLAY(params) + elif selected_action == "transcode": + params['force_transcode'] = 'true' + PLAY(params) + elif selected_action == "mark_watched": + markWatched(params["item_id"]) + xbmc.executebuiltin("XBMC.ReloadSkin()") + elif selected_action == "mark_unwatched": + markUnwatched(params["item_id"]) + xbmc.executebuiltin("XBMC.ReloadSkin()") + elif selected_action == "delete": + delete(params["item_id"]) + xbmc.executebuiltin("XBMC.ReloadSkin()") def showContent(pluginName, handle, params): @@ -1067,28 +1060,6 @@ def showParentContent(pluginName, handle, params): log.debug("showParentContent Content Url : " + str(contentUrl)) getContent(contentUrl, params) -def checkService(): - home_window = HomeWindow() - time_stamp = home_window.getProperty("Service_Timestamp") - loops = 0 - while not time_stamp and not xbmc.Monitor().abortRequested(): - loops = loops + 1 - if loops == 100: - log.error("EmbyCon Service Not Running, no time stamp, exiting") - xbmcgui.Dialog().ok(i18n('error'), i18n('service_not_running'), i18n('restart_kodi')) - sys.exit() - xbmc.sleep(200) - time_stamp = home_window.getProperty("Service_Timestamp") - - log.debug("EmbyCon Service Timestamp: " + time_stamp) - log.debug("EmbyCon Current Timestamp: " + str(int(time.time()))) - - if ((int(time_stamp) + 240) < int(time.time())): - log.error("EmbyCon Service Not Running, time stamp to old, exiting") - xbmcgui.Dialog().ok(i18n('error'), i18n('service_not_running'), i18n('restart_kodi')) - sys.exit() - - def search(handle, params): log.debug('search Called: ' + str(params)) item_type = params.get('item_type') diff --git a/plugin.video.embycon/resources/lib/menu_functions.py b/plugin.video.embycon/resources/lib/menu_functions.py index 6fc0838..8d21d4f 100644 --- a/plugin.video.embycon/resources/lib/menu_functions.py +++ b/plugin.video.embycon/resources/lib/menu_functions.py @@ -20,7 +20,7 @@ downloadUtils = DownloadUtils() __addon__ = xbmcaddon.Addon(id='plugin.video.embycon') -def showGenreList(): +def showGenreList(item_type=None): log.debug("== ENTER: showGenreList() ==") server = downloadUtils.getServer() @@ -29,8 +29,20 @@ def showGenreList(): detailsString = getDetailsString() + kodi_type = "Movies" + emby_type = "Movie" + if item_type is not None and item_type == "series": + emby_type = "Series" + kodi_type = "tvshows" + try: - jsonData = downloadUtils.downloadUrl("{server}/emby/Genres?SortBy=SortName&SortOrder=Ascending&IncludeTypes=Movie&Recursive=true&UserId={userid}&format=json") + jsonData = downloadUtils.downloadUrl("{server}/emby/Genres?" + + "SortBy=SortName" + + "&SortOrder=Ascending" + + "&IncludeItemTypes=" + emby_type + + "&Recursive=true" + + "&UserId={userid}" + + "&format=json") log.debug("GENRE_LIST_DATA : " + jsonData) except Exception, msg: error = "Get connect : " + str(msg) @@ -44,11 +56,11 @@ def showGenreList(): for genre in result: item_data = {} item_data['title'] = genre.get("Name") - item_data['media_type'] = "Movies" + item_data['media_type'] = kodi_type item_data['thumbnail'] = downloadUtils.getArtwork(genre, "Thumb", server=server) item_data['path'] = ('{server}/emby/Users/{userid}/Items?Fields=' + detailsString + '&Recursive=true&GenreIds=' + genre.get("Id") + - '&IncludeItemTypes=Movie' + + '&IncludeItemTypes=' + emby_type + '&ImageTypeLimit=1&format=json') collections.append(item_data) @@ -129,8 +141,9 @@ def displaySections(): log.debug("addMenuDirectoryItem: " + collection.get('title', i18n('unknown')) + " " + str(url)) addMenuDirectoryItem(collection.get('title', i18n('unknown')), url, thumbnail=collection.get("thumbnail")) - addMenuDirectoryItem(i18n('movies_genre'), "plugin://plugin.video.embycon/?mode=MOVIE_GENRA") + addMenuDirectoryItem(i18n('movies_genre'), "plugin://plugin.video.embycon/?mode=MOVIE_GENRE") addMenuDirectoryItem(i18n('movies_az'), "plugin://plugin.video.embycon/?mode=MOVIE_ALPHA") + addMenuDirectoryItem(i18n('tvshow_genre'), "plugin://plugin.video.embycon/?mode=SERIES_GENRE") addMenuDirectoryItem(i18n('search'), "plugin://plugin.video.embycon/?mode=SEARCH") addMenuDirectoryItem(i18n('show_clients'), "plugin://plugin.video.embycon/?mode=SHOW_SERVER_SESSIONS") @@ -265,7 +278,7 @@ def getCollections(detailsString): '&Fields=' + detailsString + '&SortBy=DateCreated' + '&SortOrder=Descending' + - '&Filters=IsUnplayed,IsNotFolder' + + '&Filters={IsUnplayed,}IsNotFolder' + '&Recursive=true' + '&IncludeItemTypes=Episode' + '&ImageTypeLimit=1' + @@ -326,7 +339,7 @@ def getCollections(detailsString): '&Fields=' + detailsString + '&SortBy=DateCreated' + '&SortOrder=Descending' + - '&Filters=IsUnplayed,IsNotFolder' + + '&Filters={IsUnplayed,}IsNotFolder' + '&ImageTypeLimit=1' + '&format=json'), 'media_type': collection_type}) @@ -377,7 +390,7 @@ def getCollections(detailsString): '&SortBy=DateCreated' + '&Fields=' + detailsString + '&SortOrder=Descending' + - '&Filters=IsUnplayed,IsNotFolder' + + '&Filters={IsUnplayed,}IsNotFolder' + '&IncludeItemTypes=Movie' + '&ImageTypeLimit=1' + '&format=json') @@ -451,7 +464,7 @@ def getCollections(detailsString): '&SortBy=DateCreated' + '&Fields=' + detailsString + '&SortOrder=Descending' + - '&Filters=IsUnplayed' + + '&Filters={IsUnplayed}' + '&IsVirtualUnaired=false' + '&IsMissing=False' + '&IncludeItemTypes=Episode' + @@ -483,7 +496,7 @@ def getCollections(detailsString): '&SortBy=DateCreated' + '&Fields=' + detailsString + '&SortOrder=Descending' + - '&Filters=IsUnplayed,IsNotFolder' + + '&Filters={IsUnplayed,}IsNotFolder' + '&IsVirtualUnaired=false' + '&IsMissing=False' + '&IncludeItemTypes=Episode' + diff --git a/plugin.video.embycon/resources/lib/play_utils.py b/plugin.video.embycon/resources/lib/play_utils.py index f7cba7c..8e579f1 100644 --- a/plugin.video.embycon/resources/lib/play_utils.py +++ b/plugin.video.embycon/resources/lib/play_utils.py @@ -30,7 +30,6 @@ def playFile(play_info): settings = xbmcaddon.Addon('plugin.video.embycon') addon_path = settings.getAddonInfo('path') - playback_type = settings.getSetting("playback_type") jump_back_amount = int(settings.getSetting("jump_back_amount")) server = downloadUtils.getServer() @@ -89,12 +88,12 @@ def playFile(play_info): playurl, listitem_props = PlayUtils().getStrmDetails(result) if not playurl: - playurl = PlayUtils().getPlayUrl(id, result, force_transcode) + playurl, playback_type = PlayUtils().getPlayUrl(id, result, force_transcode) log.debug("Play URL: " + playurl + " ListItem Properties: " + str(listitem_props)) playback_type_string = "DirectPlay" - if playback_type == "2" or force_transcode: + if playback_type == "2": playback_type_string = "Transcode" elif playback_type == "1": playback_type_string = "DirectStream" @@ -108,9 +107,22 @@ def playFile(play_info): else: result["Overview"] = playback_type_string - list_item = xbmcgui.ListItem(label=result.get("Name", i18n('missing_title')), path=playurl) + item_title = result.get("Name", i18n('missing_title')) + add_episode_number = settings.getSetting('addEpisodeNumber') == 'true' + if result.get("Type") == "Episode" and add_episode_number: + episode_num = result.get("IndexNumber") + if episode_num is not None: + if episode_num < 10: + episode_num = "0" + str(episode_num) + else: + episode_num = str(episode_num) + else: + episode_num = "" + item_title = episode_num + " - " + item_title + + list_item = xbmcgui.ListItem(label=item_title, path=playurl) - list_item = setListItemProps(id, list_item, result, server, listitem_props) + list_item = setListItemProps(id, list_item, result, server, listitem_props, item_title) playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO) playlist.clear() @@ -138,7 +150,7 @@ def playFile(play_info): xbmc.sleep(100) # xbmc.Player().play() -def setListItemProps(id, listItem, result, server, extra_props): +def setListItemProps(id, listItem, result, server, extra_props, title): # set up item and item info thumbID = id eppNum = -1 @@ -152,13 +164,14 @@ def setListItemProps(id, listItem, result, server, extra_props): listItem.setProperty('IsPlayable', 'true') listItem.setProperty('IsFolder', 'false') + listItem.setProperty('id', result.get("Id")) for prop in extra_props: listItem.setProperty(prop[0], prop[1]) # play info details = { - 'title': result.get("Name", i18n('missing_title')), + 'title': title, 'plot': result.get("Overview") } diff --git a/plugin.video.embycon/resources/lib/translation.py b/plugin.video.embycon/resources/lib/translation.py index f0c2214..2f327c5 100644 --- a/plugin.video.embycon/resources/lib/translation.py +++ b/plugin.video.embycon/resources/lib/translation.py @@ -50,9 +50,7 @@ STRINGS = { 'series': 30233, 'seasons': 30234, 'episodes': 30235, - 'save': 30236, 'start_from_beginning': 30237, - 'default_sort': 30238, 'next_page': 30245, 'search': 30246, 'widgets': 30247, @@ -97,5 +95,7 @@ STRINGS = { '_unwatched': 30285, 'movies_unwatched': 30286, 'tvshows_latest' : 30287, - '_latest' : 30288 + '_latest' : 30288, + 'tvshow_genre': 30289, + 'all': 30290 } diff --git a/plugin.video.embycon/resources/lib/utils.py b/plugin.video.embycon/resources/lib/utils.py index 05a1094..9c6a8d3 100644 --- a/plugin.video.embycon/resources/lib/utils.py +++ b/plugin.video.embycon/resources/lib/utils.py @@ -24,14 +24,32 @@ class PlayUtils(): log.debug("playback_type: FORCED_TRANSCODE") playurl = None + is_h265 = False + streams = result.get("MediaStreams", []) + for stream in streams: + if stream.get("Type", "") == "Video" and stream.get("Codec", "") in ["hevc", "h265"]: + is_h265 = True + break + if is_h265: + log.debug("H265_IS_TRUE") + h265_action = addonSettings.getSetting("h265_action") + if h265_action == "1": + log.debug("H265 override play action: setting to Direct Streaming") + playback_type = "1" + elif h265_action == "2": + log.debug("H265 override play action: setting to Transcode Streaming") + playback_type = "2" + + if force_transcode: + playback_type = "2" + # transcode - if playback_type == "2" or force_transcode: + if playback_type == "2": playback_bitrate = addonSettings.getSetting("playback_bitrate") log.debug("playback_bitrate: " + playback_bitrate) - width_options = ["640", "720", "1024", "1280", "1440", "1600", "1920", "2600", "4096"] - playback_max_width = width_options[int(addonSettings.getSetting("playback_max_width"))] + playback_max_width = addonSettings.getSetting("playback_max_width") playback_video_force_8 = addonSettings.getSetting("playback_video_force_8") == "true" clientInfo = ClientInformation() @@ -78,7 +96,7 @@ class PlayUtils(): playurl = playurl + "&api_key=" + user_token log.debug("Playback URL: " + playurl) - return playurl.encode('utf-8') + return playurl.encode('utf-8'), playback_type def getStrmDetails(self, result): playurl = None @@ -112,7 +130,7 @@ def getDetailsString(): include_people = addonSettings.getSetting("include_people") == "true" include_overview = addonSettings.getSetting("include_overview") == "true" - detailsString = "EpisodeCount,SeasonCount,Path,Genres,Studios,CumulativeRunTimeTicks,Etag" + detailsString = "DateCreated,EpisodeCount,SeasonCount,Path,Genres,Studios,CumulativeRunTimeTicks,Etag" if include_media: detailsString += ",MediaStreams" @@ -151,7 +169,10 @@ def getArt(item, server, widget=False): 'clearart': '', 'discart': '', 'landscape': '', - 'tvshow.poster': '' + 'tvshow.poster': '', + 'tvshow.clearart': '', + 'tvshow.banner': '', + 'tvshow.landscape': '' } item_id = item.get("Id") @@ -164,10 +185,20 @@ def getArt(item, server, widget=False): else: art['thumb'] = downloadUtils.getArtwork(item, "Primary", server=server) + if item.get("Type") == "Episode" or item.get("Type") == "Season": + art['tvshow.poster'] = downloadUtils.getArtwork(item, "Primary", parent=True, server=server) + art['tvshow.clearart'] = downloadUtils.getArtwork(item, "Logo", parent=True, server=server) + art['tvshow.banner'] = downloadUtils.getArtwork(item, "Banner", parent=True, server=server) + art['tvshow.landscape'] = downloadUtils.getArtwork(item, "Thumb", parent=True, server=server) + elif item.get("Type") == "Series": + art['tvshow.poster'] = downloadUtils.getArtwork(item, "Primary", parent=False, server=server) + art['tvshow.clearart'] = downloadUtils.getArtwork(item, "Logo", parent=False, server=server) + art['tvshow.banner'] = downloadUtils.getArtwork(item, "Banner", parent=False, server=server) + art['tvshow.landscape'] = downloadUtils.getArtwork(item, "Thumb", parent=False, server=server) + if item.get("Type") == "Episode": art['thumb'] = art['thumb'] if art['thumb'] else downloadUtils.getArtwork(item, "Thumb", server=server) art['landscape'] = art['thumb'] if art['thumb'] else downloadUtils.getArtwork(item, "Thumb", parent=True, server=server) - art['tvshow.poster'] = downloadUtils.getArtwork(item, "Primary", parent=True, server=server) else: art['poster'] = art['thumb'] diff --git a/plugin.video.embycon/resources/lib/widgets.py b/plugin.video.embycon/resources/lib/widgets.py new file mode 100644 index 0000000..c3098d4 --- /dev/null +++ b/plugin.video.embycon/resources/lib/widgets.py @@ -0,0 +1,398 @@ + +import xbmcaddon +import xbmcplugin +import xbmcgui +import xbmc +import json +import urllib +import hashlib + +from downloadutils import DownloadUtils +from utils import getArt +from datamanager import DataManager +from simple_logging import SimpleLogging +from kodi_utils import HomeWindow + +log = SimpleLogging(__name__) +downloadUtils = DownloadUtils() +dataManager = DataManager() +kodi_version = int(xbmc.getInfoLabel('System.BuildVersion')[:2]) + +def checkForNewContent(): + log.debug("checkForNewContent Called") + + added_url = ('{server}/emby/Users/{userid}/Items' + + '?Recursive=true' + + '&limit=1' + + '&Fields=DateCreated,Etag' + + '&SortBy=DateCreated' + + '&SortOrder=Descending' + + '&IncludeItemTypes=Movie,Episode' + + '&ImageTypeLimit=0' + + '&format=json') + + added_result = downloadUtils.downloadUrl(added_url, suppress=True) + result = json.loads(added_result) + log.debug("LATEST_ADDED_ITEM:" + str(result)) + + last_added_date = "" + if result is not None: + items = result.get("Items", []) + if len(items) > 0: + item = items[0] + last_added_date = item.get("Etag", "") + log.debug("last_added_date: " + last_added_date) + + played_url = ('{server}/emby/Users/{userid}/Items' + + '?Recursive=true' + + '&limit=1' + + '&Fields=DateCreated,Etag' + + '&SortBy=DatePlayed' + + '&SortOrder=Descending' + + '&IncludeItemTypes=Movie,Episode' + + '&ImageTypeLimit=0' + + '&format=json') + + played_result = downloadUtils.downloadUrl(played_url, suppress=True) + result = json.loads(played_result) + log.debug("LATEST_PLAYED_ITEM:" + str(result)) + + last_played_date = "" + if result is not None: + items = result.get("Items", []) + if len(items) > 0: + item = items[0] + last_played_date = item.get("Etag", "") + log.debug("last_played_date: " + last_played_date) + + home_window = HomeWindow() + current_widget_hash = home_window.getProperty("embycon_widget_reload") + log.debug("Current Widget Hash: " + str(current_widget_hash)) + + m = hashlib.md5() + m.update(last_played_date + last_added_date) + new_widget_hash = m.hexdigest() + log.debug("New Widget Hash: " + str(new_widget_hash)) + + if current_widget_hash != new_widget_hash: + home_window.setProperty("embycon_widget_reload", new_widget_hash) + log.debug("Setting New Widget Hash: " + str(new_widget_hash)) + + +def getWidgetUrlContent(handle, params): + log.debug("getWidgetUrlContent Called" + str(params)) + + request = params["url"] + request = urllib.unquote(request) + request = "{server}/emby/" + request + "&ImageTypeLimit=1&format=json" + log.debug("getWidgetUrlContent URL:" + request) + + select_action = params.get("action", None) + + listItems = populateWidgetItems(request, override_select_action=select_action) + + xbmcplugin.addDirectoryItems(handle, listItems) + xbmcplugin.endOfDirectory(handle, cacheToDisc=False) + + +def getSuggestions(handle, params): + log.debug("getSuggestions Called" + str(params)) + + itemsUrl = ("{server}/emby/Movies/Recommendations" + + "?userId={userid}" + + "&categoryLimit=1" + + "&ItemLimit=8" + + "&format=json" + + "&ImageTypeLimit=1" + + "&IsMissing=False") + + listItems = populateWidgetItems(itemsUrl) + + xbmcplugin.addDirectoryItems(handle, listItems) + xbmcplugin.endOfDirectory(handle, cacheToDisc=False) + +def getWidgetContentNextUp(handle, params): + log.debug("getWidgetContentNextUp Called" + str(params)) + + itemsUrl = ("{server}/emby/Shows/NextUp?SeriesId=" + params["id"] + + "&userId={userid}" + + "&Limit={ItemLimit}" + + "&format=json" + + "&ImageTypeLimit=1" + + "&IsMissing=False") + + listItems = populateWidgetItems(itemsUrl) + + xbmcplugin.addDirectoryItems(handle, listItems) + xbmcplugin.endOfDirectory(handle, cacheToDisc=False) + + +def getWidgetContentSimilar(handle, params): + log.debug("getWisgetContentSimilarMovies Called" + str(params)) + + itemsUrl = ("{server}/emby/Items/" + params["id"] + "/Similar" + "?userId={userid}" + + "&Limit={ItemLimit}" + + "&format=json" + + "&ImageTypeLimit=1" + + "&IsMissing=False" + + "&fields=PrimaryImageAspectRatio,UserData,CanDelete") + + listItems = populateWidgetItems(itemsUrl) + + xbmcplugin.addDirectoryItems(handle, listItems) + xbmcplugin.endOfDirectory(handle, cacheToDisc=False) + + +def getWidgetContentCast(handle, params): + log.debug("getWigetContentCast Called" + str(params)) + server = downloadUtils.getServer() + + id = params["id"] + jsonData = downloadUtils.downloadUrl("{server}/emby/Users/{userid}/Items/" + id + "?format=json", + suppress=False, popup=1) + result = json.loads(jsonData) + log.debug("ItemInfo: " + str(result)) + + listItems = [] + people = result.get("People") + if (people != None): + for person in people: + #if (person.get("Type") == "Director"): + # director = director + person.get("Name") + ' ' + #if (person.get("Type") == "Writing"): + # writer = person.get("Name") + #if (person.get("Type") == "Writer"): + # writer = person.get("Name") + if (person.get("Type") == "Actor"): + person_name = person.get("Name") + person_role = person.get("Role") + person_id = person.get("Id") + person_tag = person.get("PrimaryImageTag") + person_thumbnail = downloadUtils.imageUrl(person_id, "Primary", 0, 400, 400, person_tag, server=server) + + if kodi_version > 17: + list_item = xbmcgui.ListItem(label=person_name, iconImage=person_thumbnail, offscreen=True) + else: + list_item = xbmcgui.ListItem(label=person_name, iconImage=person_thumbnail) + + artLinks = {} + artLinks["thumb"] = person_thumbnail + artLinks["poster"] = person_thumbnail + list_item.setArt(artLinks) + + if person_role: + list_item.setLabel2(person_role) + + itemTupple = ("", list_item, False) + listItems.append(itemTupple) + + xbmcplugin.addDirectoryItems(handle, listItems) + xbmcplugin.endOfDirectory(handle, cacheToDisc=False) + + +def populateWidgetItems(itemsUrl, override_select_action=None): + + server = downloadUtils.getServer() + settings = xbmcaddon.Addon(id='plugin.video.embycon') + select_action = settings.getSetting("widget_select_action") + + if override_select_action is not None: + select_action = str(override_select_action) + + log.debug("WIDGET_DATE_URL: " + itemsUrl) + + # get the items + jsonData = downloadUtils.downloadUrl(itemsUrl, suppress=False, popup=1) + log.debug("Widget(Items) jsonData: " + jsonData) + result = json.loads(jsonData) + + if result is not None and isinstance(result, dict) and result.get("Items") is not None: + simmilarTo = result.get("BaselineItemName", None) + result = result.get("Items") + elif result is not None and isinstance(result, list) and len(result) > 0: + simmilarTo = result[0].get("BaselineItemName", None) + result = result[0].get("Items") + else: + result = [] + + itemCount = 1 + listItems = [] + for item in result: + item_id = item.get("Id") + name = item.get("Name") + episodeDetails = "" + log.debug("WIDGET_DATE_NAME: " + name) + + title = item.get("Name") + tvshowtitle = "" + + if (item.get("Type") == "Episode" and item.get("SeriesName") != None): + + eppNumber = "X" + tempEpisodeNumber = "0" + if (item.get("IndexNumber") != None): + eppNumber = item.get("IndexNumber") + if eppNumber < 10: + tempEpisodeNumber = "0" + str(eppNumber) + else: + tempEpisodeNumber = str(eppNumber) + + seasonNumber = item.get("ParentIndexNumber") + if seasonNumber < 10: + tempSeasonNumber = "0" + str(seasonNumber) + else: + tempSeasonNumber = str(seasonNumber) + + episodeDetails = "S" + tempSeasonNumber + "E" + tempEpisodeNumber + name = item.get("SeriesName") + " " + episodeDetails + tvshowtitle = episodeDetails + title = item.get("SeriesName") + + art = getArt(item, server, widget=True) + + if kodi_version > 17: + list_item = xbmcgui.ListItem(label=name, iconImage=art['thumb'], offscreen=True) + else: + list_item = xbmcgui.ListItem(label=name, iconImage=art['thumb']) + + # list_item.setLabel2(episodeDetails) + + production_year = item.get("ProductionYear") + if not production_year and item.get("PremiereDate"): + production_year = int(item.get("PremiereDate")[:4]) + + overlay = "0" + playCount = "0" + + # add progress percent + userData = item.get("UserData") + if (userData != None): + if userData.get("Played") == True: + playCount = "1" + overlay = "5" + else: + overlay = "6" + + playBackTicks = float(userData.get("PlaybackPositionTicks")) + if (playBackTicks != None and playBackTicks > 0): + runTimeTicks = float(item.get("RunTimeTicks", "0")) + if (runTimeTicks > 0): + playBackPos = int(((playBackTicks / 1000) / 10000) / 60) + list_item.setProperty('ResumeTime', str(playBackPos)) + + percentage = int((playBackTicks / runTimeTicks) * 100.0) + list_item.setProperty("complete_percentage", str(percentage)) + + video_info_label = {"title": title, + "tvshowtitle": tvshowtitle, + "year": production_year, + "Overlay": overlay, + "playcount": playCount} + + list_item.setInfo(type="Video", infoLabels=video_info_label) + list_item.setProperty('fanart_image', art['fanart']) # back compat + list_item.setProperty('discart', art['discart']) # not avail to setArt + list_item.setArt(art) + # add count + list_item.setProperty("item_index", str(itemCount)) + itemCount = itemCount + 1 + + list_item.setProperty('IsPlayable', 'true') + + totalTime = str(int(float(item.get("RunTimeTicks", "0")) / (10000000 * 60))) + list_item.setProperty('TotalTime', str(totalTime)) + + list_item.setProperty('id', item_id) + + if simmilarTo is not None: + list_item.setProperty('suggested_from_watching', simmilarTo) + + if select_action == "1": + playurl = "plugin://plugin.video.embycon/?item_id=" + item_id + '&mode=PLAY' + elif select_action == "0": + playurl = "plugin://plugin.video.embycon/?item_id=" + item_id + '&mode=SHOW_MENU' + + itemTupple = (playurl, list_item, False) + listItems.append(itemTupple) + + return listItems + + +def getWidgetContent(handle, params): + log.debug("getWigetContent Called" + str(params)) + + type = params.get("type") + if (type == None): + log.error("getWigetContent type not set") + return + + itemsUrl = ("{server}/emby/Users/{userid}/Items" + + "?Limit={ItemLimit}" + + "&format=json" + + "&ImageTypeLimit=1" + + "&IsMissing=False") + + if (type == "recent_movies"): + xbmcplugin.setContent(handle, 'movies') + itemsUrl += ("&Recursive=true" + + "&SortBy=DateCreated" + + "&SortOrder=Descending" + + "&Filters={IsUnplayed,}IsNotFolder" + + "&IsVirtualUnaired=false" + + "&IsMissing=False" + + "&IncludeItemTypes=Movie") + elif (type == "inprogress_movies"): + xbmcplugin.setContent(handle, 'movies') + itemsUrl += ("&Recursive=true" + + "&SortBy=DatePlayed" + + "&SortOrder=Descending" + + "&Filters=IsResumable" + + "&IsVirtualUnaired=false" + + "&IsMissing=False" + + "&IncludeItemTypes=Movie") + elif (type == "random_movies"): + xbmcplugin.setContent(handle, 'movies') + watched = params.get("watched", "") == "true" + if watched: + itemsUrl += "&Filters=IsPlayed,IsNotFolder" + else: + itemsUrl += "&Filters={IsUnplayed,}IsNotFolder" + itemsUrl += ("&Recursive=true" + + "&SortBy=Random" + + "&SortOrder=Descending" + + "&IsVirtualUnaired=false" + + "&IsMissing=False" + + "&IncludeItemTypes=Movie") + elif (type == "recent_episodes"): + xbmcplugin.setContent(handle, 'episodes') + itemsUrl += ("&Recursive=true" + + "&SortBy=DateCreated" + + "&SortOrder=Descending" + + "&Filters={IsUnplayed,}IsNotFolder" + + "&IsVirtualUnaired=false" + + "&IsMissing=False" + + "&IncludeItemTypes=Episode") + elif (type == "inprogress_episodes"): + xbmcplugin.setContent(handle, 'episodes') + itemsUrl += ("&Recursive=true" + + "&SortBy=DatePlayed" + + "&SortOrder=Descending" + + "&Filters=IsResumable" + + "&IsVirtualUnaired=false" + + "&IsMissing=False" + + "&IncludeItemTypes=Episode") + elif (type == "nextup_episodes"): + xbmcplugin.setContent(handle, 'episodes') + itemsUrl = ("{server}/emby/Shows/NextUp" + + "?Limit={ItemLimit}" + "&userid={userid}" + + "&Recursive=true" + + "&format=json" + + "&ImageTypeLimit=1") + + listItems = populateWidgetItems(itemsUrl) + + xbmcplugin.addDirectoryItems(handle, listItems) + xbmcplugin.endOfDirectory(handle, cacheToDisc=False) + diff --git a/plugin.video.embycon/resources/settings.xml b/plugin.video.embycon/resources/settings.xml index 4bb7a5a..301caf6 100644 --- a/plugin.video.embycon/resources/settings.xml +++ b/plugin.video.embycon/resources/settings.xml @@ -13,7 +13,7 @@ - + @@ -21,13 +21,17 @@ - + + + + + @@ -46,6 +50,8 @@ + + diff --git a/plugin.video.embycon/resources/skins/default/720p/ActionMenu.xml b/plugin.video.embycon/resources/skins/default/720p/ActionMenu.xml new file mode 100644 index 0000000..11daa26 --- /dev/null +++ b/plugin.video.embycon/resources/skins/default/720p/ActionMenu.xml @@ -0,0 +1,67 @@ + + + 3000 + 2 + + 1 + 450 + 200 + + + + + 0 + 0 + 380 + 300 + bg.png + + + + 10 + 10 + 360 + + auto + + + 0 + 0 + 360 + 45 + white.png + + + 360 + 0 + 5 + 45 + + 99FFFFFF + font14 + center + + + + + 0 + 0 + 360 + 45 + white.png + + + 360 + 0 + 5 + 45 + + FFFFFFFF + font14 + center + + + + + + \ No newline at end of file diff --git a/plugin.video.embycon/resources/skins/default/720p/ResumeDialog.xml b/plugin.video.embycon/resources/skins/default/720p/ResumeDialog.xml index 9ff6bad..9f1116a 100644 --- a/plugin.video.embycon/resources/skins/default/720p/ResumeDialog.xml +++ b/plugin.video.embycon/resources/skins/default/720p/ResumeDialog.xml @@ -7,7 +7,6 @@ 450 280 - dialogeffect diff --git a/plugin.video.embycon/resources/skins/skin.estuary/xml/Home-17.1.xml b/plugin.video.embycon/resources/skins/skin.estuary/xml/Home-17.1.xml index ef46639..cb9b540 100644 --- a/plugin.video.embycon/resources/skins/skin.estuary/xml/Home-17.1.xml +++ b/plugin.video.embycon/resources/skins/skin.estuary/xml/Home-17.1.xml @@ -52,19 +52,19 @@ WidgetGroupListCommon - + - + - + @@ -79,19 +79,19 @@ WidgetGroupListCommon - + - + - + diff --git a/plugin.video.embycon/resources/skins/skin.estuary/xml/Home-17.3-elec.xml b/plugin.video.embycon/resources/skins/skin.estuary/xml/Home-17.3-elec.xml new file mode 100644 index 0000000..cb996ed --- /dev/null +++ b/plugin.video.embycon/resources/skins/skin.estuary/xml/Home-17.3-elec.xml @@ -0,0 +1,1064 @@ + + + ClearProperty(first_load_done, 10000) + 9000 + background + + + HiddenObject + Focus + SetFocus(2000) + noop + Control.HasFocus(20000) + + + HiddenObject + Focus + SetFocus(2000) + noop + Control.HasFocus(20001) + + DefaultBackground + + DepthBackground + FullScreenDimensions + scale + 600 + conditional + WindowOpen + WindowClose + VisibleChange + $VAR[HomeFanartVar] + !Player.HasMedia + + + Conditional + + 462 + + + + + + + + + OpenClose_Right + + + String.IsEqual(Container(9000).ListItem.Property(id),emby_movies) + + + + + WidgetGroupListCommon + + + + + + + + + + + + + + + + + + + + + + String.IsEqual(Container(9000).ListItem.Property(id),emby_tvshows) + + + + + WidgetGroupListCommon + + + + + + + + + + + + + + + + + + + + + + + String.IsEqual(Container(9000).ListItem.Property(id),movies) + + + + + WidgetGroupListCommon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + String.IsEqual(Container(9000).ListItem.Property(id),tvshows) + + + + + WidgetGroupListCommon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + String.IsEqual(Container(9000).ListItem.Property(id),music) + + + + + WidgetGroupListCommon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + String.IsEqual(Container(9000).ListItem.Property(id),addons) + + + + + WidgetGroupListCommon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + String.IsEqual(Container(9000).ListItem.Property(id),video) + + + + + WidgetGroupListCommon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + String.IsEqual(Container(9000).ListItem.Property(id),livetv) + + + + + WidgetGroupListCommon + + 390 + 0 + 0 + 36 + horizontal + PVR.IsRecordingTV | PVR.HasNonRecordingTVTimer + center + + 674 + PVR.IsRecordingTV + + + + + + + + + 674 + PVR.HasNonRecordingTVTimer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + String.IsEqual(Container(9000).ListItem.Property(id),radio) + + + + + WidgetGroupListCommon + + 390 + 25 + 36 + horizontal + right + 1360 + PVR.IsRecordingRadio | PVR.HasNonRecordingRadioTimer + + 680 + PVR.IsRecordingRadio + + + + + + + + + PVR.HasNonRecordingRadioTimer + 680 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + String.IsEqual(Container(9000).ListItem.Property(id),favorites) + + + + + 65 + 0 + 0 + 0 + 9000 + 9000 + 14100 + 14100 + $INFO[ListItem.FileNameAndPath] + 2 + 500 + vertical + Integer.IsGreater(Container(14100).NumItems,0) | Container(14100).IsUpdating + + + 130 + + + + + + + + + DepthContentPopout + 130 + Focus + UnFocus + + + + + + + favourites:// + + + + + + + + + + + + + String.IsEqual(Container(9000).ListItem.Property(id),weather) + + + + + WidgetGroupListCommon + + Weather info + 68 + 70 + 102 + 300 + !String.IsEmpty(Weather.plugin) + + 90 + 100% + dialogs/dialog-bg.png + + + 840 + 60 + center + 24 + 60 + right + font30_title + + + + 840 + 120 + center + 24 + 60 + right + font14 + + + + 50 + 50 + 20 + horizontal + left + -110 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + String.IsEqual(Container(9000).ListItem.Property(id),musicvideos) + + + + + WidgetGroupListCommon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + String.IsEqual(Container(9000).ListItem.Property(id),pictures) + + + + + WidgetGroupListCommon + + + + + + + + + + String.IsEqual(Container(9000).ListItem.Property(id),disc) + + + + + + + + + + + + + + + + DepthContentPanel + OpenClose_Left + + + + + 0 + 240 + 462 + -10 + 6 + 1 + ClearProperty(listposition,home) + SetFocus($INFO[Container(9000).ListItem.Property(menu_id)]) + 700 + 700 + 500 + + + UnFocus + + 0 + 0 + 462 + 95 + lists/focus.png + Conditional + + + -3 + 1 + 95 + 95 + $INFO[ListItem.Art(thumb)] + Focus + + + 0 + 0 + 95 + 95 + colors/black.png + Conditional + + + + -3 + 1 + 95 + 95 + $INFO[ListItem.Art(thumb)] + + + 104 + 0 + 95 + 560 + center + font37 + + text_shadow + + + + + -3 + 1 + 95 + 95 + $INFO[ListItem.Art(thumb)] + + + 104 + 0 + 95 + 560 + center + font37 + + text_shadow + + + + + + + ActivateWindow(Videos,videodb://movies/titles/,return) + ActivateWindow(Videos,plugin://plugin.video.embycon/?mode=SHOW_CONTENT&item_type=Movie&media_type=movies,return) + SetProperty(first_load_done, done, 10000) + $NUMBER[3000] + icons/sidemenu/movies.png + emby_movies + + + + ActivateWindow(Videos,videodb://movies/titles/,return) + ActivateWindow(Videos,plugin://plugin.video.embycon/?mode=SHOW_CONTENT&item_type=Series&media_type=tvshows,return) + SetProperty(first_load_done, done, 10000) + $NUMBER[4000] + icons/sidemenu/tv.png + emby_tvshows + + + + + ActivateWindow(Videos,videodb://movies/titles/,return) + ActivateWindow(Videos,sources://video/,return) + $NUMBER[5000] + icons/sidemenu/movies.png + movies + !Skin.HasSetting(HomeMenuNoMovieButton) + + + + ActivateWindow(Videos,videodb://tvshows/titles/,return) + ActivateWindow(Videos,sources://video/,return) + $NUMBER[6000] + icons/sidemenu/tv.png + tvshows + !Skin.HasSetting(HomeMenuNoTVShowButton) + + + + ActivateWindow(Music,root,return) + $NUMBER[7000] + icons/sidemenu/music.png + music + !Skin.HasSetting(HomeMenuNoMusicButton) + + + + PlayDisc + $NUMBER[21000] + icons/sidemenu/disc.png + disc + System.HasMediaDVD + + + + $NUMBER[16000] + ActivateWindow(Videos,musicvideos,return) + icons/sidemenu/musicvideos.png + musicvideos + !Skin.HasSetting(HomeMenuNoMusicVideoButton) + + + + $NUMBER[12000] + ActivateWindow(TVChannels) + icons/sidemenu/livetv.png + livetv + !Skin.HasSetting(HomeMenuNoTVButton) + + + + $NUMBER[13000] + ActivateWindow(RadioChannels) + icons/sidemenu/radio.png + radio + !Skin.HasSetting(HomeMenuNoRadioButton) + + + + $NUMBER[8000] + ActivateWindow(1100) + icons/sidemenu/addons.png + addons + !Skin.HasSetting(HomeMenuNoProgramsButton) + + + + ActivateWindow(Pictures) + $NUMBER[4000] + icons/sidemenu/pictures.png + pictures + !Skin.HasSetting(HomeMenuNoPicturesButton) + + + + ActivateWindow(Videos,root) + $NUMBER[11000] + icons/sidemenu/videos.png + video + !Skin.HasSetting(HomeMenuNoVideosButton) + + + + ActivateWindow(favourites) + $NUMBER[14000] + icons/sidemenu/favourites.png + favorites + !Skin.HasSetting(HomeMenuNoFavButton) + + + + ActivateWindow(Weather) + ReplaceWindow(servicesettings,weather) + $NUMBER[15000] + icons/sidemenu/weather.png + weather + !Skin.HasSetting(HomeMenuNoWeatherButton) + + + + + horizontal + 0 + -8 + 480 + 110 + 100 + SetFocus(9000) + PageDown + PageDown + SetFocus(9000) + PageUp + PageUp + 2000 + justify + + + + + + + + + + + + + + + + + + + + + + + + + + + + BottomBar + + + + + DepthBars + conditional + WindowOpen + WindowClose + 30 + 90 + + keep + 56 + 56 + icons/logo.png + + + 40 + 10 + keep + 192 + 36 + icons/logo-text.png + + + + conditional + WindowOpen + WindowClose + 0 + 0 + 39 + 100% + font12 + 1 + + button_focus + button_focus + text_shadow + FFC0C0C0 + + + + diff --git a/plugin.video.embycon/resources/skins/skin.estuary/xml/Home-17.3.xml b/plugin.video.embycon/resources/skins/skin.estuary/xml/Home-17.3.xml index f12f5c5..8c73c20 100644 --- a/plugin.video.embycon/resources/skins/skin.estuary/xml/Home-17.3.xml +++ b/plugin.video.embycon/resources/skins/skin.estuary/xml/Home-17.3.xml @@ -52,19 +52,19 @@ WidgetGroupListCommon - + - + - + @@ -79,19 +79,19 @@ WidgetGroupListCommon - + - + - + @@ -856,14 +856,16 @@ - RunAddon(plugin.video.embycon, /?mode=SHOW_CONTENT&item_type=Movie&media_type=movies) + ActivateWindow(Videos,plugin://plugin.video.embycon/?mode=SHOW_CONTENT&item_type=Movie&media_type=movies,return) + $NUMBER[3000] icons/sidemenu/movies.png emby_movies - RunAddon(plugin.video.embycon, /?mode=SHOW_CONTENT&item_type=Series&media_type=tvshows) + ActivateWindow(Videos,plugin://plugin.video.embycon/?mode=SHOW_CONTENT&item_type=Series&media_type=tvshows,return) + $NUMBER[4000] icons/sidemenu/tv.png emby_tvshows diff --git a/plugin.video.embycon/service.py b/plugin.video.embycon/service.py index 2a71d93..0502978 100644 --- a/plugin.video.embycon/service.py +++ b/plugin.video.embycon/service.py @@ -6,12 +6,14 @@ import xbmcaddon import xbmcgui import time import json +import traceback from resources.lib.downloadutils import DownloadUtils from resources.lib.simple_logging import SimpleLogging from resources.lib.play_utils import playFile from resources.lib.kodi_utils import HomeWindow from resources.lib.translation import i18n +from resources.lib.widgets import checkForNewContent # clear user and token when logging in home_window = HomeWindow() @@ -77,6 +79,7 @@ def promptForStopActions(item_id, current_possition): settings = xbmcaddon.Addon(id='plugin.video.embycon') prompt_next_percentage = int(settings.getSetting('promptPlayNextEpisodePercentage')) + play_prompt = settings.getSetting('promptPlayNextEpisodePercentage_prompt') == "true" prompt_delete_episode_percentage = int(settings.getSetting('promptDeleteEpisodePercentage')) prompt_delete_movie_percentage = int(settings.getSetting('promptDeleteMoviePercentage')) @@ -151,8 +154,14 @@ def promptForStopActions(item_id, current_possition): item_list = items_result.get("Items", []) for item in item_list: index = item.get("IndexNumber", -1) - if index > item_index: # find the next episode in the season - resp = xbmcgui.Dialog().yesno(i18n("play_next_title"), i18n("play_next_question"), autoclose=10000) + if index == item_index + 1: # find the very next episode in the season + + resp = True + if play_prompt: + #next_epp_name = str(index) + " of " + str(item_list[-1].get("IndexNumber", -1)) + " - " + item.get("Name", "n/a") + next_epp_name = ("%02d - " % (index,)) + item.get("Name", "n/a") + resp = xbmcgui.Dialog().yesno(i18n("play_next_title"), i18n("play_next_question"), next_epp_name, autoclose=10000) + if resp: next_item_id = item.get("Id") log.debug("Playing Next Episode: %s" % next_item_id) @@ -287,9 +296,10 @@ class Service(xbmc.Player): monitor = Service() -last_progress_update = time.time() -download_utils.checkVersion() home_window = HomeWindow() +last_progress_update = time.time() +last_content_check = time.time() +last_version_check = 0 # monitor.abortRequested() is causes issues, it currently triggers for all addon cancelations which causes # the service to exit when a user cancels an addon load action. This is a bug in Kodi. @@ -297,28 +307,37 @@ home_window = HomeWindow() while not xbmc.abortRequested: - if xbmc.Player().isPlaying(): - - try: + try: + if xbmc.Player().isPlaying(): + # if playing every 10 seconds updated the server with progress if (time.time() - last_progress_update) > 10: last_progress_update = time.time() sendProgress() + else: + # if we have a play item them trigger playback + play_data = home_window.getProperty("play_item_message") + if play_data: + home_window.clearProperty("play_item_message") + play_info = json.loads(play_data) + playFile(play_info) + + # if not playing every 60 seonds check for new widget content + if (time.time() - last_content_check) > 60: + last_content_check = time.time() + checkForNewContent() + + # check version + if (time.time() - last_version_check) > (60 * 60 * 6): # every 6 hours + last_version_check = time.time() + download_utils.checkVersion() + + except Exception as error: + log.error("Exception in Playback Monitor : " + str(error)) + log.error(traceback.format_exc()) - except Exception as error: - log.error("Exception in Playback Monitor : " + str(error)) - - else: - play_data = home_window.getProperty("play_item_message") - if play_data: - home_window.clearProperty("play_item_message") - play_info = json.loads(play_data) - playFile(play_info) - - home_window.setProperty("Service_Timestamp", str(int(time.time()))) xbmc.sleep(1000) # clear user and token when loggin off -home_window.clearProperty("Service_Timestamp") home_window.clearProperty("userid") home_window.clearProperty("AccessToken") home_window.clearProperty("Params") -- cgit v1.2.3