예제 #1
0
    def UpdateVideoItem(self, item):
        """
        Updates the item
        """

        data = UriHandler.Open(item.url, proxy=self.proxy)

        baseEncode = Regexer.DoRegex(self.mediaUrlRegex, data)[-1]
        jsonData = EncodingHelper().DecodeBase64(baseEncode)
        json = JsonHelper(jsonData, logger=Logger.Instance())
        Logger.Trace(json)

        # "flv": "http://media.dumpert.nl/flv/e2a926ff_10307954_804223649588516_151552487_n.mp4.flv",
        # "tablet": "http://media.dumpert.nl/tablet/e2a926ff_10307954_804223649588516_151552487_n.mp4.mp4",
        # "mobile": "http://media.dumpert.nl/mobile/e2a926ff_10307954_804223649588516_151552487_n.mp4.mp4",

        item.MediaItemParts = []
        part = item.CreateNewEmptyMediaPart()
        streams = json.GetValue()
        for key in streams:
            if key == "flv":
                part.AppendMediaStream(streams[key], 1000)
            elif key == "tablet":
                part.AppendMediaStream(streams[key], 800)
            elif key == "mobile":
                part.AppendMediaStream(streams[key], 450)
            else:
                Logger.Debug("Key '%s' was not used", key)

        item.complete = True
        Logger.Trace("VideoItem updated: %s", item)
        return item
예제 #2
0
    def __init__(self, channelInfo):
        """Initialisation of the class.

        Arguments:
        channelInfo: ChannelInfo - The channel info object to base this channel on.

        All class variables should be instantiated here and this method should not
        be overridden by any derived classes.

        """

        chn_class.Channel.__init__(self, channelInfo)

        # ============== 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())
        # key = "%sRM%%j%%l@g@w_A%%" % (salt,)
        # Logger.Trace("Found Salt: %s and Key: %s", salt, key)
        # key = EncodingHelper.EncodeMD5(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}

        userAgent = "%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" % (userAgent, )
        string = EncodingHelper.EncodeMD5(string, toUpper=False).zfill(32)
        xnos = string + base64.b64encode(userAgent)
        self.httpHeaders = {"X-Nos": xnos}

        self.baseUrl = "http://nos.nl"

        # setup the main parsing data
        self._AddDataParser(self.mainListUri, preprocessor=self.GetCategories)
        self._AddDataParser(
            "*",
            # preprocessor=self.AddNextPage,
            json=True,
            parser=('items', ),
            creator=self.CreateJsonVideo,
            updater=self.UpdateJsonVideo)
        self._AddDataParser("*",
                            json=True,
                            parser=('links', ),
                            creator=self.CreatePageItem)

        #===============================================================================================================
        # non standard items
        # self.__IgnoreCookieLaw()
        self.__pageSize = 50

        # ====================================== Actual channel setup STOPS here =======================================
        return
예제 #3
0
    def __GetUUID(self):
        """Generates a Unique Identifier based on Time and Random Integers"""

        t = long(time.time() * 1000)
        r = long(random.random() * 100000000000000000L)
        a = random.random() * 100000000000000000L
        data = str(t) + ' ' + str(r) + ' ' + str(a)
        data = EncodingHelper.EncodeMD5(data)
        return data
예제 #4
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:
            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. Intializing a new one.")
                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 (lengt=%s)",
                            EncodingHelper.encode_md5(key), len(key))
                self.__newKeyGeneratedInConstructor = True

            Vault.__Key = key
            Logger.trace("Using Application Key with MD5: %s (lengt=%s)", EncodingHelper.encode_md5(key), len(key))
예제 #5
0
    def __init__(self, channelInfo):
        """Initialisation of the class.

        Arguments:
        channelInfo: ChannelInfo - The channel info object to base this channel on.

        All class variables should be instantiated here and this method should not
        be overridden by any derived classes.

        """

        chn_class.Channel.__init__(self, channelInfo)

        # ============== 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())
        key = "%sRM%%j%%l@g@w_A%%" % (salt,)
        Logger.Trace("Found Salt: %s and Key: %s", salt, key)
        key = EncodingHelper.EncodeMD5(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}

        self.baseUrl = "http://nos.nl"

        # setup the main parsing data
        self._AddDataParser(self.mainListUri, preprocessor=self.GetCategories)
        self._AddDataParser("*", preprocessor=self.AddNextPage, json=True,
                            parser=(), creator=self.CreateJsonVideo, updater=self.UpdateJsonVideo)

        self._AddDataParser("http://content.nos.nl/apps/feeds/most-watched-video/", json=True,
                            preprocessor=self.AddNextPage,
                            parser=('items',), creator=self.CreateJsonVideo, updater=self.UpdateJsonVideo)

        #===============================================================================================================
        # non standard items
        # self.__IgnoreCookieLaw()

        # ====================================== Actual channel setup STOPS here =======================================
        return
    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, 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
예제 #8
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.datetime.min  # : value for sorting, this one is set to minimum so if non is set, it's shown at the bottom

        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 identifcation 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)
예제 #9
0
    def DownloadSubtitle(url, fileName="", format='sami', proxy=None, replace=None):
        """Downloads a SAMI and stores the SRT in the cache folder

        Arguments:
        url      : string - URL location of the SAMI file

        Keyword Arguments:
        fileName : string - 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
        format : string   - defines the source format. Defaults to Sami.

        Returns:
        The full patch of the cached SRT file.

        """

        if fileName == "":
            Logger.Debug("No filename present, generating filename using MD5 hash of url.")
            fileName = "%s.srt" % (EncodingHelper.EncodeMD5(url),)
        elif not fileName.endswith(".srt"):
            Logger.Debug("No SRT extension present, appending it.")
            fileName = "%s.srt" % (fileName, )

        srt = ""
        try:
            localCompletePath = os.path.join(Config.cacheDir, fileName)

            # no need to download it again!
            if os.path.exists(localCompletePath):
                return localCompletePath

            Logger.Trace("Opening Subtitle URL")
            raw = UriHandler.Open(url, proxy=proxy)

            if raw == "":
                Logger.Warning("Empty Subtitle path found. Not setting subtitles.")
                return ""

            # try to decode it
            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')

            if format.lower() == 'sami':
                srt = SubtitleHelper.__ConvertSamiToSrt(raw)
            elif format.lower() == 'srt':
                srt = raw
            elif format.lower() == 'webvtt':
                srt = SubtitleHelper.__ConvertWebVttToSrt(raw)
            elif format.lower() == 'ttml':
                srt = SubtitleHelper.__ConvertTtmlToSrt(raw)
            elif format.lower() == 'dcsubtitle':
                srt = SubtitleHelper.__ConvertDCSubtitleToSrt(raw)
            elif format.lower() == 'json':
                srt = SubtitleHelper.__ConvertJsonSubtitleToSrt(raw)
            elif format.lower() == 'm3u8srt':
                srt = SubtitleHelper.__ConvertM3u8SrtToSubtitleToSrt(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])

            f = open(localCompletePath, 'w')
            f.write(srt)
            f.close()
            Logger.Info("Saved SRT as %s", localCompletePath)
            return localCompletePath
        except:
            Logger.Error("Error handling Subtitle file: [%s]", srt, exc_info=True)
            return ""
예제 #10
0
    def UpdateVideoItem(self, item):
        """Updates an existing MediaItem with more data.

        Arguments:
        item : MediaItem - the MediaItem that needs to be updated

        Returns:
        The original item with more data added to it's properties.

        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.

        """

        Logger.Debug('Starting UpdateVideoItem for %s (%s)', item.name,
                     self.channelName)

        data = UriHandler.Open(item.url, proxy=self.proxy)
        json = JsonHelper(data, Logger.Instance())
        videoData = json.GetValue("video")
        if videoData:
            part = item.CreateNewEmptyMediaPart()
            spoofIp = self._GetSetting("spoof_ip", valueForNone="0.0.0.0")
            if spoofIp:
                part.HttpHeaders["X-Forwarded-For"] = spoofIp

            # get the videos
            videoUrls = videoData.get("videoReferences")
            for videoUrl in videoUrls:
                # Logger.Trace(videoUrl)
                streamInfo = videoUrl['url']
                if "manifest.f4m" in streamInfo:
                    continue
                elif "master.m3u8" in streamInfo:
                    for s, b in M3u8.GetStreamsFromM3u8(
                            streamInfo, self.proxy, headers=part.HttpHeaders):
                        item.complete = True
                        part.AppendMediaStream(s, b)

                    #m3u8Data = UriHandler.Open(streamInfo, proxy=self.proxy)

                    #urls = Regexer.DoRegex(self.mediaUrlRegex, m3u8Data)
                    #Logger.Trace(urls)
                    #for url in urls:
                    #part.AppendMediaStream(url[1].strip(), url[0])

            # subtitles
            subtitles = videoData.get("subtitleReferences")
            if subtitles:
                Logger.Trace(subtitles)
                subUrl = subtitles[0]["url"]
                fileName = "%s.srt" % (EncodingHelper.EncodeMD5(subUrl), )
                subData = UriHandler.Open(subUrl, proxy=self.proxy)

                # correct the subs
                regex = re.compile("^1(\d:)", re.MULTILINE)
                subData = re.sub(regex, "0\g<1>", subData)
                subData = re.sub("--> 1(\d):", "--> 0\g<1>:", subData)

                localCompletePath = os.path.join(Config.cacheDir, fileName)
                Logger.Debug("Saving subtitle to: %s", localCompletePath)
                f = open(localCompletePath, 'w')
                f.write(subData)
                f.close()
                part.Subtitle = localCompletePath

            item.complete = True

        return item
예제 #11
0
    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

        """

        data = UriHandler.open(item.url, proxy=self.proxy)
        item.MediaItemParts = []
        part = item.create_new_empty_media_part()

        base_encode = Regexer.do_regex('data-files="([^"]+)', data)
        if base_encode:
            Logger.debug("Loading video from BASE64 encoded JSON data")
            base_encode = base_encode[-1]
            json_data = EncodingHelper.decode_base64(base_encode)
            json = JsonHelper(json_data, logger=Logger.instance())
            Logger.trace(json)

            # "flv": "http://media.dumpert.nl/flv/e2a926ff_10307954_804223649588516_151552487_n.mp4.flv",
            # "tablet": "http://media.dumpert.nl/tablet/e2a926ff_10307954_804223649588516_151552487_n.mp4.mp4",
            # "mobile": "http://media.dumpert.nl/mobile/e2a926ff_10307954_804223649588516_151552487_n.mp4.mp4",

            streams = json.get_value()
            for key in streams:
                if key == "flv":
                    part.append_media_stream(streams[key], 1000)
                elif key == "720p":
                    part.append_media_stream(streams[key], 1200)
                elif key == "1080p":
                    part.append_media_stream(streams[key], 1600)
                elif key == "tablet":
                    part.append_media_stream(streams[key], 800)
                elif key == "mobile":
                    part.append_media_stream(streams[key], 450)
                elif key == "embed" and streams[key].startswith("youtube"):
                    embed_type, youtube_id = streams[key].split(":")
                    url = "https://www.youtube.com/watch?v=%s" % (youtube_id, )
                    for s, b in YouTube.get_streams_from_you_tube(url, self.proxy):
                        item.complete = True
                        part.append_media_stream(s, b)
                else:
                    Logger.debug("Key '%s' was not used", key)
            item.complete = True
            Logger.trace("VideoItem updated: %s", item)
            return item

        youtube_id = Regexer.do_regex("class='yt-iframe'[^>]+src='https://www.youtube.com/embed/([^?]+)", data)
        if youtube_id:
            youtube_id = youtube_id[-1]
            url = "https://www.youtube.com/watch?v=%s" % (youtube_id,)
            for s, b in YouTube.get_streams_from_you_tube(url, self.proxy):
                item.complete = True
                part.append_media_stream(s, b)
        return item