예제 #1
0
    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))
예제 #2
0
    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)
예제 #3
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
예제 #5
0
    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
예제 #7
0
    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)