Log("\n*** PlayStream started ***\n") try: import wingdbstub except: pass VERSION = "0.1.33" CHANNEL_NAME = "PlayStream" PREFIX = '/video/playstream' DEV = True if Platform.MachineIdentifier == 'Unit testing' else False REPO_URL = "http://files.blue.lv/PlayStream.bundle/?sort=time&order=asc" DOWNLOAD_URL = "http://files.blue.lv/PlayStream.bundle/" ART = "art-default.jpg" ICON = "icon-default.png" BACK = "back.png" FOLDER = "folder.png" VIDEO = "video.png" SEARCH = "folder_search.png" PREFS = "prefs.png" UPDATE = "update.png" import sys, os, re import traceback, glob, inspect, urllib, zipfile import requests import content from content.ContentSources import ContentSources from content import util import Framework #from Framework.api.objectkit import * from DumbTools import DumbKeyboard, DumbPrefs #C:\Users\user\AppData\Local\Plex Media Server\Plug-ins tmp_dir = Core.storage.join_path(Core.app_support_path, "Cache") plugins_dir = Core.storage.join_path(Core.app_support_path, "Plug-ins", "") #Log("tmp_dir= "+tmp_dir) sources_directory = os.path.join(os.path.dirname(inspect.getsourcefile(content)), "sources") use_streams_file_remote = Prefs["general_use_streams_file_remote"] streams_file = Prefs["general_streams_file"] streams_file_remote = Prefs["general_streams_file_remote"] Log("[playstream] Create sources objects") if use_streams_file_remote: cfg_file = streams_file_remote cfg_file2 = streams_file else: cfg_file = streams_file cfg_file2 = None try: sources = ContentSources(sources_directory, cfg_file, cfg_file2) except Exception as e: Log(traceback.format_exc()) cfg = sources.plugins["config"] Log("[playstream] Using streams_file=%s"% cfg.streams_file) ObjectContainer(header="Info", message="Using streams_file %s, reload plugin" % cfg.streams_file) data0 = None title0 = None history = [] view_modes = { "List": 65586, "InfoList": 65592, "MediaPreview": 458803, "Showcase": 458810, "Coverflow": 65591, "PanelStream": 131124, "WallStream": 131125, "Songs": 65593, "Seasons": 65593, "Albums": 131123, "Episodes": 65590,"ImageStream":458809,"Pictures":131123 } ''' for source in sources.plugins: if not ("options" in dir(sources.plugins[source]) and sources.plugins[source].options): continue options = sources.plugins[source].options if not options: continue for option in options: key="%s_%s"%(source,option) if key in ("viaplay_device"): continue # exception list, value = plugin.get_setting(key) options[option] = value sources.plugins[source].options_write(options) prefix = "" ''' ################################################################################################### def Start(): ObjectContainer.title1 = CHANNEL_NAME ObjectContainer.art = R(ART) Plugin.AddViewGroup("InfoList", viewMode="InfoList", mediaType="items") Plugin.AddViewGroup("List", viewMode="List", mediaType="items") #DirectoryObject.thumb = R(ICON) #DirectoryObject.art = R(ART) #EpisodeObject.thumb = R(ICON) #EpisodeObject.art = R(ART) #VideoClipObject.thumb = R(ICON) #VideoClipObject.art = R(ART) #HTTP.CacheTime = CACHE_1DAY #HTTP.Headers['User-Agent'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36' def ValidatePrefs(): #preferences = Prefs._sandbox.preferences.get() for source in sources.plugins: if not ("options" in dir(sources.plugins[source]) and sources.plugins[source].options): continue options = sources.plugins[source].options if not options: continue for option in options: key="%s_%s"%(source,option) if key in ("viaplay_device"): continue # exception list, value = Prefs[key] options[option] = value if not DEV: sources.plugins[source].options_write(options) if Prefs["general_use_streams_file_remote"]: cfg.set_streams_file(Prefs["general_streams_file_remote"]) else: cfg.set_streams_file(Prefs["general_streams_file"]) #################################################################################################### @handler(PREFIX, CHANNEL_NAME, art=ART, thumb=ICON) def Main(): data = "config::home" oc = Menu(data, "Home") oc.view_group = "InfoList" ValidatePrefs() return oc #################################################################################################### @route(PREFIX+'/{data}/') def Menu2(data, title): return Menu(data, title) @route(PREFIX+'/search') def Search(query, data, title): #data = data.replace("%2F", "/") Log("Search data="+data) Log("Search query="+query) data2 = data.format(query) return Menu(data2, title) @route(PREFIX+'/{data}') def Menu(data, title, **kwargs): #includeBandwidths=1, checkFiles=0, includeConcerts=0, includeExtras=0, includeOnDeck=0, includePopularLeaves=1&includeChapters=1&checkFiles=1): global data0, title0, history #sources.plugins["config"].read_streams() Log("[playstream] ** Menu call: %s - %s" % ( data, title)) if not data: data = u"config::home" # Process history #if data == "back": # data = history[-1][0].replace("/", "%2F") if history and history[-1][0] else "config::home" # title = history[-1][1] if history and history[-1][0] else "Home" #if data == data0 or (len(history) > 2 and data == history[-2]): # # fake call, skipped # Log("history skipped") #else: # if history and history[-1][0] and data == history[-1][0]: # history.pop() # Log("history poped") # else: # history.append((data0, title0)) # Log("history added %s" % data0) #hst = "" #for h in history: hst += "\n%s" % h[0] #Log("[playstream] history="+hst) # #data0 = data #title0 = title data = data.replace("%2F", "/") ### Processig call ### Log("[playstream] processing data=%s"%data) try: is_video = sources.is_video(data) except: Log(traceback.format_exc()) return ObjectContainer(header="Error", message=unicode(e)) ### Video handling ### if is_video: try: streams = sources.get_streams(data) except Exception,e: Log(traceback.format_exc()) return ObjectContainer(header="Error", message=unicode(e)) if streams: data2 = data.replace("/", "%2F") title = streams[0]["name"] if isinstance(streams[0]["name"] , unicode) else streams[0]["name"].decode("utf8") if not title: title = "Title" desc = streams[0]["desc"] if isinstance(streams[0]["desc"] , unicode) else streams[0]["desc"] .decode("utf8") img = streams[0]["img"] if isinstance(streams[0]["img"] , unicode) else streams[0]["img"] .decode("utf8") vco = VideoClipObject( key=Callback(Menu, data=data2, title=title ), rating_key=data2, title=title, summary=desc, thumb=thumb_data(img, video=True) ) for i, stream in enumerate(streams): headers = stream["headers"] if "headers" in stream and stream["headers"] else {"User-Agent":"Plex"} lang = stream["lang"] if "lang" in stream else "?" quality = stream["quality"] if "quality" in stream else "stream%s" % i resolution = "%s %s" % (lang, quality) if isinstance(resolution,str): resolution = resolution.decode("utf8") Log(resolution) height = "720" #"720" width = "1280" #"1280" vco.add(MediaObject( #bitrate=0, #container=Container.MPEGTS, video_resolution = resolution, height = height, width = width, optimized_for_streaming = True, parts=[PartObject( key=HTTPLiveStreamURL(stream["url"]), http_headers = headers )] )) include_container = True return ObjectContainer(objects = [vco]) if include_container else vco else: return ObjectContainer(header="Error", message="No streams found!") ### List handling ### oc = ObjectContainer(title2=title) oc.view_group = "InfoList" try: content = sources.get_content(data) except Exception,e: Log(traceback.format_exc()) return ObjectContainer(header="Error", message=unicode(e)) Log( "[playstream] %s items returned"%len(content)) for item in content: if item[1] == "back": continue # nerādam back data2 = item[1] #data2 = urllib.quote(data2, safe="") data2 = data2.replace("/", "%2F") data2 = data2 if isinstance(data2, unicode) else data2.decode("utf8") title = item[0] if isinstance(item[0], unicode) else item[0].decode("utf8") if not title: title = "Title" img = item[2] #if isinstance(item[2], unicode) else item[2].decode("utf8") desc = item[3] if isinstance(item[3], unicode) else item[3].decode("utf8") # Search item # if "{0}" in data2: #q = "aaa" # TODO InputDirectoryObject ##data = data.format(q) #TODO problēma ar latviešu burtiem iekš DumbInput if Client.Product in DumbKeyboard.clients: DumbKeyboard(PREFIX, oc, Search, dktitle = title, dkthumb = thumb_data(SEARCH), title=title, data=data2 ) else: oc.add(InputDirectoryObject( key=Callback(Search, data=data2, title=title), title = title, thumb = thumb_data(img), #Resource.ContentsOfURLWithFallback(img, fallback=R(ICON)), prompt=desc, summary =desc )) # Video item # elif sources.is_video(item[1]): oc.add(VideoClipObject( key=Callback(Menu, data=data2, title=title), title = title, thumb = thumb_data(img, video=True), #Resource.ContentsOfURLWithFallback(img, fallback=R(ICON)), summary = desc, rating_key=data2 )) # List object # else: oc.add(DirectoryObject( key=Callback(Menu, data=data2, title=title), title = title, thumb = thumb_data(img) if not data2=="back" else R(BACK), summary =desc )) if data == "config::home": if Client.Product in DumbPrefs.clients: DumbPrefs(PREFIX, oc, title = "Plugin options", thumb = R(PREFS)) else: oc.add(PrefsObject( title="Plugin options", summary="Update plugin options", thumb=R(PREFS), art=R(ART) )) ValidatePrefs() version2 = get_repo_version() if version2 > VERSION: msg = "Update from %s to %s" % (VERSION, version2) else: msg = "Current version - %s" % (VERSION) oc.add(DirectoryObject( key=Callback(UpdateMenu), title = msg, thumb = R(UPDATE), summary ="Check whether update exist and offer to update" )) return oc #@route(PREFIX+'/updatemenu') def UpdateMenu(): Log("UpdateMenu") version2 = get_repo_version() if version2 > VERSION: msg = "Current version - %s, new version available - %s" % (VERSION, version2) else: msg = "Current version - %s, no new version available" % (VERSION) oc = ObjectContainer(title1=msg, title2=msg, no_history=True) oc.add(DirectoryObject(key=Callback(UpdatePlugin, ver=version2), title="Update plugin", summary=msg, thumb = R(UPDATE))) return oc # @route(PREFIX+'/updateplugin') def UpdatePlugin(ver): Log("UpdatePlugin") Log("ver="+ver) if not ver: ver = get_repo_version() #return ObjectContainer(header="Error", message="ver=" % ver) fname = "PlayStream.bundle-%s.zip" % ver url = DOWNLOAD_URL + fname path = os.path.join(tmp_dir, fname) try: urllib.urlretrieve(url, path) with zipfile.ZipFile(path,"r") as zip_ref: zip_ref.extractall(plugins_dir) except: return ObjectContainer(header="Error", message="Can not download \n%s" % url) return ObjectContainer(header="Info", message="Plugin version updated to %s, reload plugin" % ver) def get_repo_version(): try: r = requests.get(REPO_URL) if r.status_code <> 200: Log("[playstream] Can not reach repo") return "" vers = re.findall('href=".*PlayStream\.bundle-(\d+\.\d+\.\d+)\.zip"', r.content) #vers.sort(reverse=True) repo_version = vers[-1] if vers else 0 return repo_version except: Log("[playstream] Can not reach repo") return "" def thumb_data(img, video=False): default = R(VIDEO) if video else R(FOLDER) if img.startswith('http'): img2 = Resource.ContentsOfURLWithFallback(img, default) elif img in ("default", ""): img2 = default else: img2 = R(img) return img2