def __init__(self): """ Creates a new instance of the Vault class """ self.__newKeyGeneratedInConstructor = False # : This was the very first time a key was generated # ask for PIN of no key is present if Vault.__Key is None: howto_shown = self.__show_howto() key = self.__get_application_key() # type: bytes # was there a key? No, let's initialize it. if key is None: Logger.warning( "No Application Key present. Initializing a new one.") # Show the how to if it was not already shown during this __init__() if not howto_shown: self.__show_howto(force=True) key = self.__get_new_key() if not self.change_pin(key): raise RuntimeError("Error creating Application Key.") Logger.info( "Created a new Application Key with MD5: %s (length=%s)", EncodingHelper.encode_md5(key), len(key)) self.__newKeyGeneratedInConstructor = True Vault.__Key = key Logger.trace("Using Application Key with MD5: %s (length=%s)", EncodingHelper.encode_md5(key), len(key))
def __init__(self, title, url, type="folder"): """ Creates a new MediaItem. The `url` can contain an url to a site more info about the item can be retrieved, for instance for a video item to retrieve the media url, or in case of a folder where child items can be retrieved. Essential is that no encoding (like UTF8) is specified in the title of the item. This is all taken care of when creating Kodi items in the different methods. :param str|unicode title: The title of the item, used for appearance in lists. :param str|unicode url: Url that used for further information retrieval. :param str type: Type of MediaItem (folder, video, audio). Defaults to 'folder'. """ name = title.strip() self.name = name self.url = url self.actionUrl = None self.MediaItemParts = [] self.description = "" self.thumb = "" # : The local or remote image for the thumbnail of episode self.fanart = "" # : The fanart url self.icon = "" # : low quality icon for list self.__date = "" # : value show in interface self.__timestamp = datetime.min # : value for sorting, this one is set to minimum so if non is set, it's shown at the bottom self.__expires_datetime = None # : datetime value of the expire time self.type = type # : video, audio, folder, append, page, playlist self.dontGroup = False # : if set to True this item will not be auto grouped. self.isLive = False # : if set to True, the item will have a random QuerySting param self.isGeoLocked = False # : if set to True, the item is GeoLocked to the channels language (o) self.isDrmProtected = False # : if set to True, the item is DRM protected and cannot be played (^) self.isPaid = False # : if set to True, the item is a Paid item and cannot be played (*) self.__infoLabels = dict() # : Additional Kodi InfoLabels self.complete = False self.items = [] self.HttpHeaders = dict() # : http headers for the item data retrieval # Items that are not essential for pickled self.isCloaked = False self.metaData = dict() # : Additional data that is for internal / routing use only # GUID used for identification of the object. Do not set from script, MD5 needed # to prevent UTF8 issues try: self.guid = "%s%s" % (EncodingHelper.encode_md5(title), EncodingHelper.encode_md5(url or "")) except: Logger.error("Error setting GUID for title:'%s' and url:'%s'. Falling back to UUID", title, url, exc_info=True) self.guid = self.__get_uuid() self.guidValue = int("0x%s" % (self.guid,), 0)
def __init__(self, channel_info): """ Initialisation of the class. All class variables should be instantiated here and this method should not be overridden by any derived classes. :param ChannelInfo channel_info: The channel info object to base this channel on. """ chn_class.Channel.__init__(self, channel_info) # ============== Actual channel setup STARTS here and should be overwritten from derived classes =============== self.noImage = "nosnlimage.png" # setup the urls # self.mainListUri = "http://nos.nl/" self.mainListUri = "#getcategories" # we need specific headers: APK:NosHttpClientHelper.java salt = int(time.time()) # Some more work for keys that seemed required. # Logger.Trace("Found Salt: %s and Key: %s", salt, key) # key = "%sRM%%j%%l@g@w_A%%" % (salt,) # key = EncodingHelper.encode_md5(key, toUpper=False) # self.httpHeaders = {"X-NOS-App": "Google/x86;Android/4.4.4;nl.nos.app/3.1", # "X-NOS-Salt": salt, # "X-NOS-Key": key} user_agent = "%s;%d;%s/%s;Android/%s;nl.nos.app/%s" % ( "nos", salt, "Google", "Nexus", "6.0", "5.1.1") string = ";UB}7Gaji==JPHtjX3@c%s" % (user_agent, ) string = EncodingHelper.encode_md5(string, to_upper=False).zfill(32) xnos = string + base64.b64encode(user_agent) self.httpHeaders = {"X-Nos": xnos} self.baseUrl = "http://nos.nl" # setup the main parsing data self._add_data_parser(self.mainListUri, preprocessor=self.get_categories) self._add_data_parser( "*", # No longer used: preprocessor=self.AddNextPage, json=True, parser=[ 'items', ], creator=self.create_json_video, updater=self.update_json_video) self._add_data_parser("*", json=True, parser=[ 'links', ], creator=self.create_page_item) #=============================================================================================================== # non standard items # self.__ignore_cookie_law() self.__pageSize = 50 # ====================================== Actual channel setup STOPS here ======================================= return
def update_video_item(self, item): """ Updates an existing MediaItem with more data. Used to update none complete MediaItems (self.complete = False). This could include opening the item's URL to fetch more data and then process that data or retrieve it's real media-URL. The method should at least: * cache the thumbnail to disk (use self.noImage if no thumb is available). * set at least one MediaItemPart with a single MediaStream. * set self.complete = True. if the returned item does not have a MediaItemPart then the self.complete flag will automatically be set back to False. :param MediaItem item: the original MediaItem that needs updating. :return: The original item with more data added to it's properties. :rtype: MediaItem """ Logger.debug('Starting update_video_item for %s (%s)', item.name, self.channelName) data = UriHandler.open(item.url, proxy=self.proxy) json = JsonHelper(data, Logger.instance()) video_data = json.get_value("video") if video_data: part = item.create_new_empty_media_part() if self.localIP: part.HttpHeaders.update(self.localIP) # get the videos video_urls = video_data.get("videoReferences") for video_url in video_urls: # Logger.Trace(videoUrl) stream_info = video_url['url'] if "manifest.f4m" in stream_info: continue elif "master.m3u8" in stream_info: for s, b in M3u8.get_streams_from_m3u8(stream_info, self.proxy, headers=part.HttpHeaders): item.complete = True part.append_media_stream(s, b) # subtitles subtitles = video_data.get("subtitleReferences") if subtitles and subtitles[0]["url"]: Logger.trace(subtitles) sub_url = subtitles[0]["url"] file_name = "%s.srt" % (EncodingHelper.encode_md5(sub_url),) sub_data = UriHandler.open(sub_url, proxy=self.proxy) # correct the subs regex = re.compile(r"^1(\d:)", re.MULTILINE) sub_data = re.sub(regex, r"0\g<1>", sub_data) sub_data = re.sub(r"--> 1(\d):", r"--> 0\g<1>:", sub_data) local_complete_path = os.path.join(Config.cacheDir, file_name) Logger.debug("Saving subtitle to: %s", local_complete_path) with open(local_complete_path, 'w') as f: f.write(sub_data) part.Subtitle = local_complete_path item.complete = True return item
def download_subtitle(url, file_name="", format='sami', proxy=None, replace=None): """Downloads a SAMI and stores the SRT in the cache folder Arguments: @param url: string - URL location of the SAMI file @param file_name: string - [opt] Filename to use to store the subtitle in SRT format. if not specified, an MD5 hash of the URL with .xml extension will be used @param format: string - Defines the source format. Defaults to Sami. @param proxy: Proxy - If specified, a proxy will be used @param replace: dict - Dictionary with key to will be replaced with their values @return: The full patch of the cached SRT file. """ if file_name == "": Logger.debug( "No filename present, generating filename using MD5 hash of url." ) file_name = "%s.srt" % (EncodingHelper.encode_md5(url), ) elif not file_name.endswith(".srt"): Logger.debug("No SRT extension present, appending it.") file_name = "%s.srt" % (file_name, ) srt = "" try: local_complete_path = os.path.join(Config.cacheDir, file_name) # no need to download it again! if os.path.exists(local_complete_path): return local_complete_path Logger.trace("Opening Subtitle URL") raw = UriHandler.open(url, proxy=proxy) if UriHandler.instance().status.error: Logger.warning("Could not retrieve subtitle from %s", url) return "" if raw == "": Logger.warning( "Empty Subtitle path found. Not setting subtitles.") return "" # try to decode it as `raw` should be a string. if isinstance(raw, bytes): try: raw = raw.decode() except: # fix some weird chars try: raw = raw.replace("\x96", "-") except: Logger.error("Error replacing some weird chars.") Logger.warning( "Converting input to UTF-8 using 'unicode_escape'") raw = raw.decode('unicode_escape') # do some auto detection if raw.startswith("WEBVTT") and format != "webvtt": Logger.info( "Discovered subtitle format 'webvtt' instead of '%s'", format) format = "webvtt" if format.lower() == 'sami': srt = SubtitleHelper.__convert_sami_to_srt(raw) elif format.lower() == 'srt': srt = raw elif format.lower() == 'webvtt': srt = SubtitleHelper.__convert_web_vtt_to_srt( raw) # With Krypton and Leia VTT is supported natively elif format.lower() == 'ttml': srt = SubtitleHelper.__convert_ttml_to_srt(raw) elif format.lower() == 'dcsubtitle': srt = SubtitleHelper.__convert_dc_subtitle_to_srt(raw) elif format.lower() == 'json': srt = SubtitleHelper.__convert_json_subtitle_to_srt(raw) elif format.lower() == 'm3u8srt': srt = SubtitleHelper.__convert_m3u8_srt_to_subtitle_to_srt( raw, url, proxy) else: error = "Uknown subtitle format: %s" % (format, ) raise NotImplementedError(error) if replace: Logger.debug("Replacing SRT data: %s", replace) for needle in replace: srt = srt.replace(needle, replace[needle]) with io.open(local_complete_path, 'w', encoding="utf-8") as f: f.write(srt) Logger.info("Saved SRT as %s", local_complete_path) return local_complete_path except: Logger.error("Error handling Subtitle file: [%s]", srt, exc_info=True) return ""
def update_video_item(self, item): """ Updates an existing MediaItem with more data. Used to update none complete MediaItems (self.complete = False). This could include opening the item's URL to fetch more data and then process that data or retrieve it's real media-URL. The method should at least: * cache the thumbnail to disk (use self.noImage if no thumb is available). * set at least one MediaItemPart with a single MediaStream. * set self.complete = True. if the returned item does not have a MediaItemPart then the self.complete flag will automatically be set back to False. :param MediaItem item: the original MediaItem that needs updating. :return: The original item with more data added to it's properties. :rtype: MediaItem """ Logger.debug('Starting update_video_item for %s (%s)', item.name, self.channelName) data = UriHandler.open(item.url) json = JsonHelper(data, Logger.instance()) video_data = json.get_value("video") if video_data: part = item.create_new_empty_media_part() # Get the videos video_infos = video_data.get("videoReferences") # Similar to SVT supported_formats = { "dash": 2, "dash-avc-51": 3, "hls": 0, "hls-ts-avc-51": 1, "ios": 0 } for video_info in video_infos: video_type = video_info["playerType"] video_url = video_info['url'] if video_type not in supported_formats: Logger.debug("Ignoring %s: %s", video_type, video_url) continue if "hls" in video_type or "ios" in video_type: M3u8.update_part_with_m3u8_streams(part, video_url) elif "dash" in video_type: stream = part.append_media_stream(video_url, supported_formats[video_type]) Mpd.set_input_stream_addon_input(stream) else: continue # stream_info = video_info['url'] # if "manifest.f4m" in stream_info: # continue # elif "master.m3u8" in stream_info: # for s, b in M3u8.get_streams_from_m3u8(stream_info, headers=part.HttpHeaders): # item.complete = True # part.append_media_stream(s, b) # subtitles subtitles = video_data.get("subtitleReferences") if subtitles and subtitles[0]["url"]: Logger.trace(subtitles) sub_url = subtitles[0]["url"] file_name = "%s.srt" % (EncodingHelper.encode_md5(sub_url),) sub_data = UriHandler.open(sub_url) # correct the subs regex = re.compile(r"^1(\d:)", re.MULTILINE) sub_data = re.sub(regex, r"0\g<1>", sub_data) sub_data = re.sub(r"--> 1(\d):", r"--> 0\g<1>:", sub_data) local_complete_path = os.path.join(Config.cacheDir, file_name) Logger.debug("Saving subtitle to: %s", local_complete_path) with open(local_complete_path, 'w') as f: f.write(sub_data) part.Subtitle = local_complete_path item.complete = True return item
def __init__(self, title, url, type="folder", tv_show_title=None, content_type=contenttype.EPISODES, depickle=False): """ Creates a new MediaItem. The `url` can contain an url to a site more info about the item can be retrieved, for instance for a video item to retrieve the media url, or in case of a folder where child items can be retrieved. Essential is that no encoding (like UTF8) is specified in the title of the item. This is all taken care of when creating Kodi items in the different methods. :param str|unicode title: The title of the item, used for appearance in lists. :param str|unicode url: Url that used for further information retrieval. :param str type: Type of MediaItem (folder, video, audio). Defaults to 'folder'. :param str|None tv_show_title: The title of the TV Show to which the episode belongs. :param str content_type: The Kodi content type of the child items: files, songs, artists, albums, movies, tvshows, episodes, musicvideos, videos, images, games. Defaults to 'episodes' :param bool depickle: Is the constructor called while depickling. """ name = title.strip() self.name = name self.tv_show_title = tv_show_title self.url = url self.actionUrl = None self.MediaItemParts = [] self.description = "" self.thumb = "" # : The thumbnail (16:9, min 520x293) self.fanart = "" # : The fanart url (16:9, min 720p) self.icon = "" # : Low quality icon for list (1:1, min 256x256) self.poster = "" # : Poster artwork (2:3, min 500x750) self.__date = "" # : value show in interface self.__timestamp = datetime.min # : value for sorting, this one is set to minimum so if non is set, it's shown at the bottom self.__expires_datetime = None # : datetime value of the expire time self.type = type # : video, audio, folder, append, page, playlist self.dontGroup = False # : if set to True this item will not be auto grouped. self.isLive = False # : if set to True, the item will have a random QuerySting param self.isGeoLocked = False # : if set to True, the item is GeoLocked to the channels language (o) self.isDrmProtected = False # : if set to True, the item is DRM protected and cannot be played (^) self.isPaid = False # : if set to True, the item is a Paid item and cannot be played (*) self.__infoLabels = dict() # : Additional Kodi InfoLabels self.complete = False self.items = [] self.HttpHeaders = dict() # : http headers for the item data retrieval # Items that are not essential for pickled self.isCloaked = False self.metaData = dict( ) # : Additional data that is for internal / routing use only # Kodi content types: files, songs, artists, albums, movies, tvshows, episodes, # musicvideos, videos, images, games. Defaults to 'episodes' self.content_type = content_type if depickle: # While deplickling we don't need to do the guid/guidValue calculations. They will # be set from the __setstate__() return # GUID used for identification of the object. Do not set from script, MD5 needed # to prevent UTF8 issues try: self.guid = "%s%s" % (EncodingHelper.encode_md5(title), EncodingHelper.encode_md5(url or "")) except: Logger.error( "Error setting GUID for title:'%s' and url:'%s'. Falling back to UUID", title, url, exc_info=True) self.guid = self.__get_uuid() self.guidValue = int("0x%s" % (self.guid, ), 0)