コード例 #1
0
    def create_category_item(self, result_set):
        """ Creates a MediaItem of type 'folder' using the result_set from the regex.

        This method creates a new MediaItem from the Regular Expression or Json
        results <result_set>. The method should be implemented by derived classes
        and are specific to the channel.

        :param list[str]|dict[str,str] result_set: The result_set of the self.episodeItemRegex

        :return: A new MediaItem of type 'folder'.
        :rtype: MediaItem|None

        """

        Logger.trace(result_set)

        cat = HtmlEntityHelper.url_encode(result_set['nid'])
        url = "http://webapi.tv4play.se/play/programs?platform=tablet&category=%s" \
              "&fl=nid,name,program_image,category,logo,is_premium" \
              "&per_page=1000&is_active=true&start=0" % (cat, )
        item = MediaItem(result_set['name'], url)
        item.thumb = self.noImage
        item.type = 'folder'
        item.complete = True
        return item
コード例 #2
0
    def search_site(self, url=None):
        """ Creates an list of items by searching the site.

        This method is called when the URL of an item is "searchSite". The channel
        calling this should implement the search functionality. This could also include
        showing of an input keyboard and following actions.

        The %s the url will be replaced with an URL encoded representation of the
        text to search for.

        :param str|None url:     Url to use to search with a %s for the search parameters.

        :return: A list with search results as MediaItems.
        :rtype: list[MediaItem]

        """

        items = []
        if url is None:
            item = MediaItem("Search Not Implented", "", type='video')
            item.icon = self.icon
            items.append(item)
        else:
            items = []
            needle = XbmcWrapper.show_key_board()
            if needle:
                Logger.debug("Searching for '%s'", needle)
                # convert to HTML
                needle = HtmlEntityHelper.url_encode(needle)
                search_url = url % (needle, )
                temp = MediaItem("Search", search_url)
                return self.process_folder_list(temp)

        return items
コード例 #3
0
    def __send_paste_bin(self,
                         name,
                         code,
                         expire='1M',
                         paste_format=None,
                         user_key=None):
        """ Send a file to pastebin.com

        :param str|unicode name:            Name of the logfile paste/gist.
        :param str code:                    The content to post.
        :param str|unicode expire:          Expiration time.
        :param str|unicode paste_format:    The format for the file.
        :param str|unicode user_key:        The user API key.

        :return: The result of the upload.
        :rtype: any

        """

        if not name:
            raise ValueError("Name missing")
        if not code:
            raise ValueError("No code data specified")

        params = {
            'api_option': 'paste',
            'api_paste_private': 1,  # 0=public 1=unlisted 2=private
            'api_paste_name': name,
            'api_paste_expire_date': expire,
            'api_dev_key': self.__apiKey,
            'api_paste_code': code,
        }

        if paste_format:
            params['api_paste_format'] = paste_format
        if user_key:
            params['api_user_key'] = user_key

        post_params = ""
        for k in params.keys():
            post_params = "{0}&{1}={2}".format(
                post_params, k, HtmlEntityHelper.url_encode(str(params[k])))
        post_params = post_params.lstrip("&")

        if self.__logger:
            self.__logger.debug("Posting %d chars to pastebin.com", len(code))

        data = UriHandler.open("http://pastebin.com/api/api_post.php",
                               params=post_params,
                               proxy=self.__proxy)

        if "pastebin.com" not in data:
            raise IOError(data)

        if self.__logger:
            self.__logger.info("PasteBin: %s", data)

        return data
コード例 #4
0
    def get_license_key(key_url,
                        key_type="R",
                        key_headers=None,
                        key_value=None,
                        json_filter=""):
        """ Generates a propery license key value

        # A{SSM} -> not implemented
        # R{SSM} -> raw format
        # B{SSM} -> base64 format URL encoded (b{ssmm} will not URL encode)
        # D{SSM} -> decimal format

        The generic format for a LicenseKey is:
        |<url>|<headers>|<key with placeholders>|<optional json filter>

        The Widevine Decryption Key Identifier (KID) can be inserted via the placeholder {KID}

        :param str key_url:                 The URL where the license key can be obtained.
        :param str|None key_type:           The key type (A, R, B or D).
        :param dict[str,str] key_headers:   A dictionary that contains the HTTP headers to pass.
        :param str key_value:               The value that is beging passed on as the key value.
        :param str json_filter:             If specified selects that json element to extract the
                                            key response.

        :return: A formated license string that can be passed to the adaptive input add-on.
        :rtype: str

        """

        header = ""
        if key_headers:
            for k, v in key_headers.items():
                header = "{0}&{1}={2}".format(header, k,
                                              HtmlEntityHelper.url_encode(v))

        if key_type in ("A", "R", "B"):
            key_value = "{0}{{SSM}}".format(key_type)
        elif key_type == "D":
            if "D{SSM}" not in key_value:
                raise ValueError("Missing D{SSM} placeholder")
            key_value = HtmlEntityHelper.url_encode(key_value)

        return "{0}|{1}|{2}|{3}".format(key_url, header.strip("&"), key_value,
                                        json_filter)
コード例 #5
0
    def search_site(self, url=None):
        """ Creates an list of items by searching the site.

        This method is called when the URL of an item is "searchSite". The channel
        calling this should implement the search functionality. This could also include
        showing of an input keyboard and following actions.

        The %s the url will be replaced with an URL encoded representation of the
        text to search for.

        :param str url:     Url to use to search with a %s for the search parameters.

        :return: A list with search results as MediaItems.
        :rtype: list[MediaItem]

        """

        if self.primaryChannelId:
            shows_url = "https://{0}/content/shows?" \
                        "include=genres%%2Cimages%%2CprimaryChannel.images&" \
                        "filter%%5BprimaryChannel.id%%5D={1}&" \
                        "page%%5Bsize%%5D={2}&query=%s"\
                .format(self.baseUrlApi, self.primaryChannelId or "", self.programPageSize)

            videos_url = "https://{0}/content/videos?decorators=viewingHistory&" \
                         "include=images%%2CprimaryChannel%%2Cshow&" \
                         "filter%%5BprimaryChannel.id%%5D={1}&" \
                         "page%%5Bsize%%5D={2}&query=%s"\
                .format(self.baseUrlApi, self.primaryChannelId or "", self.videoPageSize)
        else:
            shows_url = "https://{0}/content/shows?" \
                        "include=genres%%2Cimages%%2CprimaryChannel.images&" \
                        "page%%5Bsize%%5D={1}&query=%s" \
                .format(self.baseUrlApi, self.programPageSize)

            videos_url = "https://{0}/content/videos?decorators=viewingHistory&" \
                         "include=images%%2CprimaryChannel%%2Cshow&" \
                         "page%%5Bsize%%5D={1}&query=%s" \
                .format(self.baseUrlApi, self.videoPageSize)

        needle = XbmcWrapper.show_key_board()
        if needle:
            Logger.debug("Searching for '%s'", needle)
            needle = HtmlEntityHelper.url_encode(needle)

            search_url = videos_url % (needle, )
            temp = MediaItem("Search", search_url)
            episodes = self.process_folder_list(temp)

            search_url = shows_url % (needle, )
            temp = MediaItem("Search", search_url)
            shows = self.process_folder_list(temp)
            return shows + episodes

        return []
コード例 #6
0
    def __get_video_streams(self, video_id, part):
        """ Fetches the video stream for a given videoId

        @param video_id: (integer) the videoId
        @param part:    (MediaPart) the mediapart to add the streams to
        @return:        (bool) indicating a successfull retrieval

        """

        # hardcoded for now as it does not seem top matter
        dscgeo = '{"countryCode":"%s","expiry":1446917369986}' % (
            self.language.upper(), )
        dscgeo = HtmlEntityHelper.url_encode(dscgeo)
        headers = {"Cookie": "dsc-geo=%s" % (dscgeo, )}

        # send the data
        http, nothing, host, other = self.baseUrl.split("/", 3)
        subdomain, domain = host.split(".", 1)
        url = "https://secure.%s/secure/api/v2/user/authorization/stream/%s?stream_type=hls" \
              % (domain, video_id,)
        data = UriHandler.open(url,
                               proxy=self.proxy,
                               additional_headers=headers,
                               no_cache=True)
        json = JsonHelper(data)
        url = json.get_value("hls")

        if url is None:
            return False

        streams_found = False
        if "?" in url:
            qs = url.split("?")[-1]
        else:
            qs = None
        for s, b in M3u8.get_streams_from_m3u8(url, self.proxy):
            # and we need to append the original QueryString
            if "X-I-FRAME-STREAM" in s:
                continue

            streams_found = True
            if qs is not None:
                if "?" in s:
                    s = "%s&%s" % (s, qs)
                else:
                    s = "%s?%s" % (s, qs)

            part.append_media_stream(s, b)

        return streams_found
コード例 #7
0
    def create_episode_item(self, result_set):
        """ Creates a new MediaItem for an episode.

        This method creates a new MediaItem from the Regular Expression or Json
        results <result_set>. The method should be implemented by derived classes
        and are specific to the channel.

        :param list[str]|dict result_set:   The result_set of the self.episodeItemRegex

        :return: A new MediaItem of type 'folder'.
        :rtype: MediaItem|None

        """

        # Logger.Trace(result_set)
        json = result_set
        title = json["name"]

        program_id = json["nid"]
        program_id = HtmlEntityHelper.url_encode(program_id)
        url = "http://webapi.tv4play.se/play/video_assets" \
              "?platform=tablet&per_page=%s&is_live=false&type=episode&" \
              "page=1&node_nids=%s&start=0" % (self.maxPageSize, program_id, )

        if "channel" in json and json["channel"]:
            # noinspection PyTypeChecker
            channel_id = json["channel"]["nid"]
            Logger.trace("ChannelId found: %s", channel_id)
        else:
            channel_id = "tv4"
            Logger.warning("ChannelId NOT found. Assuming %s", channel_id)

        # match the exact channel or put them in TV4
        is_match_for_channel = channel_id.startswith(self.__channelId)
        is_match_for_channel |= self.channelCode == "tv4se" and not channel_id.startswith(
            "sjuan") and not channel_id.startswith("tv12")
        if not is_match_for_channel:
            Logger.debug("Channel mismatch for '%s': %s vs %s", title,
                         channel_id, self.channelCode)
            return None

        item = MediaItem(title, url)
        item.icon = self.icon
        item.thumb = result_set.get("program_image", self.noImage)
        item.isPaid = result_set.get("is_premium", False)
        return item
コード例 #8
0
    def search_site(self, url=None):
        """ Creates an list of items by searching the site.

        This method is called when the URL of an item is "searchSite". The channel
        calling this should implement the search functionality. This could also include
        showing of an input keyboard and following actions.

        The %s the url will be replaced with an URL encoded representation of the
        text to search for.

        :param str url:     Url to use to search with a %s for the search parameters.

        :return: A list with search results as MediaItems.
        :rtype: list[MediaItem]

        """

        items = []
        needle = XbmcWrapper.show_key_board()
        if not needle:
            return []

        Logger.debug("Searching for '%s'", needle)
        # convert to HTML
        needle = HtmlEntityHelper.url_encode(needle)

        # Search Programma's
        url = "https://search.rtl.nl/?typeRestriction=tvabstract&search={}&page=1&pageSize=99"
        search_url = url.format(needle)
        temp = MediaItem("Search", search_url)
        items += self.process_folder_list(temp) or []

        # Search Afleveringen -> no dates given, so this makes little sense
        # url = "https://search.rtl.nl/?typeRestriction=videoobject&uitzending=true&search={}&page=1&pageSize=99"
        # search_url = url.format(needle)
        # temp = MediaItem("Search", search_url)
        # items += self.process_folder_list(temp) or []

        return items
コード例 #9
0
    def create_category(self, result_set):
        """ Creates a MediaItem of type 'folder' using the result_set from the regex.

        This method creates a new MediaItem from the Regular Expression or Json
        results <result_set>. The method should be implemented by derived classes
        and are specific to the channel.

        :param list[str]|dict[str,str] result_set: The result_set of the self.episodeItemRegex

        :return: A new MediaItem of type 'folder'.
        :rtype: MediaItem|None

        """

        Logger.trace(result_set)

        title = HtmlEntityHelper.url_encode(result_set['title'])
        url = "http://m.schooltv.nl/api/v1/categorieen/%s/afleveringen.json?sort=Nieuwste&age_filter=&size=%s" % (
            title, self.__PageSize)
        item = MediaItem(result_set['title'], url)
        item.thumb = result_set.get('image', self.noImage)
        item.description = "Totaal %(count)s videos" % result_set
        item.icon = self.icon
        return item
コード例 #10
0
    def set_input_stream_addon_input(strm, proxy=None, headers=None, addon="inputstream.adaptive",
                                     manifest_type=None,
                                     license_key=None,
                                     license_type=None,
                                     max_bit_rate=None,
                                     persist_storage=False,
                                     service_certificate=None,
                                     manifest_update=None):
        """ Parsers standard M3U8 lists and returns a list of tuples with streams and bitrates that
        can be used by other methods.

        :param strm:                    (MediaStream) the MediaStream to update
        :param proxy:                   (Proxy) The proxy to use for opening
        :param dict headers:            Possible HTTP Headers
        :param str addon:               Adaptive add-on to use
        :param str manifest_type:       Type of manifest (hls/mpd)
        :param str license_key:         The value of the license key request
        :param str license_type:        The type of license key request used (see below)
        :param int max_bit_rate:        The maximum bitrate to use (optional)
        :param bool persist_storage:    Should we store certificates? And request server certificates?
        :param str service_certificate: Use the specified server certificate
        :param str manifest_update:     How should the manifest be updated

        Can be used like this:

            part = item.create_new_empty_media_part()
            stream = part.append_media_stream(stream_url, 0)
            M3u8.set_input_stream_addon_input(stream, self.proxy, self.headers)
            item.complete = True

        if maxBitRate is not set, the bitrate will be configured via the normal generic Retrospect
        or channel settings.

        """

        if manifest_type is None:
            raise ValueError("No manifest type set")

        strm.Adaptive = True

        # See https://github.com/peak3d/inputstream.adaptive/blob/master/inputstream.adaptive/addon.xml.in
        strm.add_property("inputstreamaddon", addon)
        strm.add_property("inputstream.adaptive.manifest_type", manifest_type)
        if license_key:
            strm.add_property("inputstream.adaptive.license_key", license_key)
        if license_type:
            strm.add_property("inputstream.adaptive.license_type", license_type)
        if max_bit_rate:
            strm.add_property("inputstream.adaptive.max_bandwidth", max_bit_rate * 1000)
        if persist_storage:
            strm.add_property("inputstream.adaptive.license_flags", "persistent_storage")
        if service_certificate is not None:
            strm.add_property("inputstream.adaptive.server_certificate", service_certificate)
        if manifest_update:
            strm.add_property("inputstream.adaptive.manifest_update_parameter", manifest_update)

        if headers:
            header = ""
            for k, v in headers.items():
                header = "{0}&{1}={2}".format(header, k, HtmlEntityHelper.url_encode(v))
            strm.add_property("inputstream.adaptive.stream_headers", header.strip("&"))

        return strm
コード例 #11
0
    def update_video_item(self, item):  # NOSONAR
        """ 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)

        # we need to fetch the actual url as it might differ for single video items
        data, secure_url = UriHandler.header(item.url, proxy=self.proxy)

        # Get the MZID
        secure_url = secure_url.rstrip("/")
        secure_url = "%s.mssecurevideo.json" % (secure_url, )
        data = UriHandler.open(secure_url,
                               proxy=self.proxy,
                               additional_headers=item.HttpHeaders)
        secure_data = JsonHelper(data, logger=Logger.instance())
        mzid = secure_data.get_value(
            list(secure_data.json.keys())[0], "videoid")

        # region New URL retrieval with DRM protection
        # We need a player token
        token_data = UriHandler.open(
            "https://media-services-public.vrt.be/"
            "vualto-video-aggregator-web/rest/external/v1/tokens",
            data="",
            additional_headers={"Content-Type": "application/json"})

        token = JsonHelper(token_data).get_value("vrtPlayerToken")

        asset_url = "https://media-services-public.vrt.be/vualto-video-aggregator-web/rest/" \
                    "external/v1/videos/{0}?vrtPlayerToken={1}&client=vrtvideo"\
            .format(HtmlEntityHelper.url_encode(mzid), HtmlEntityHelper.url_encode(token))
        asset_data = UriHandler.open(asset_url,
                                     proxy=self.proxy,
                                     no_cache=True)
        asset_data = JsonHelper(asset_data)

        drm_key = asset_data.get_value("drm")
        drm_protected = drm_key is not None
        adaptive_available = AddonSettings.use_adaptive_stream_add_on(
            with_encryption=drm_protected)
        part = item.create_new_empty_media_part()
        srt = None

        # see if we prefer hls over dash
        hls_prio = 2 if self._get_setting("hls_over_dash", False) else 0

        for target_url in asset_data.get_value("targetUrls"):
            video_type = target_url["type"]
            video_url = target_url["url"]

            if video_type == "hls_aes" and drm_protected and adaptive_available:
                # no difference in encrypted or not.
                Logger.debug("Found HLS AES encrypted stream and a DRM key")
                stream = part.append_media_stream(video_url, 0)
                M3u8.set_input_stream_addon_input(stream, self.proxy)

            elif video_type == "hls" and not drm_protected:
                # no difference in encrypted or not.
                if adaptive_available:
                    Logger.debug(
                        "Found standard HLS stream and without DRM protection")
                    stream = part.append_media_stream(video_url, hls_prio)
                    M3u8.set_input_stream_addon_input(stream, self.proxy)
                else:
                    m3u8_data = UriHandler.open(video_url, self.proxy)
                    for s, b, a in M3u8.get_streams_from_m3u8(
                            video_url,
                            self.proxy,
                            play_list_data=m3u8_data,
                            map_audio=True):
                        item.complete = True
                        if a:
                            audio_part = a.rsplit("-", 1)[-1]
                            audio_part = "-%s" % (audio_part, )
                            s = s.replace(".m3u8", audio_part)
                        part.append_media_stream(s, b)

                    srt = M3u8.get_subtitle(video_url,
                                            play_list_data=m3u8_data,
                                            proxy=self.proxy)
                    if not srt:
                        continue

                    srt = srt.replace(".m3u8", ".vtt")
                    part.Subtitle = SubtitleHelper.download_subtitle(
                        srt, format="webvtt")

            elif video_type == "mpeg_dash" and adaptive_available:
                if not drm_protected:
                    Logger.debug(
                        "Found standard MPD stream and without DRM protection")
                    stream = part.append_media_stream(video_url, 1)
                    Mpd.set_input_stream_addon_input(stream, self.proxy)
                else:
                    stream = part.append_media_stream(video_url, 1)
                    encryption_json = '{{"token":"{0}","drm_info":[D{{SSM}}],"kid":"{{KID}}"}}'\
                        .format(drm_key)
                    encryption_key = Mpd.get_license_key(
                        key_url="https://widevine-proxy.drm.technology/proxy",
                        key_type="D",
                        key_value=encryption_json,
                        key_headers={
                            "Content-Type": "text/plain;charset=UTF-8"
                        })
                    Mpd.set_input_stream_addon_input(
                        stream, self.proxy, license_key=encryption_key)

            if video_type.startswith("hls") and srt is None:
                srt = M3u8.get_subtitle(video_url, proxy=self.proxy)
                if srt:
                    srt = srt.replace(".m3u8", ".vtt")
                    part.Subtitle = SubtitleHelper.download_subtitle(
                        srt, format="webvtt")

            item.complete = True
        # endregion
        return item
コード例 #12
0
    def get_kodi_play_list_data(self, bitrate, proxy=None):
        """ Returns the playlist items for this MediaItem

        :param int bitrate:             The bitrate of the streams that should be in the
                                        playlist. Given in kbps.
        :param ProxyInfo|None proxy:    The proxy to set

        :return: A list of ListItems that should be added to a playlist with their selected
                 stream url
        :rtype: list[tuple[xbmcgui.ListItem, str]]

        """

        Logger.info("Creating playlist items for Bitrate: %s kbps\n%s",
                    bitrate, self)

        if not bool(bitrate):
            raise ValueError("Bitrate not specified")

        play_list_data = []
        for part in self.MediaItemParts:
            if len(part.MediaStreams) == 0:
                Logger.warning("Ignoring empty MediaPart: %s", part)
                continue

            kodi_item = self.get_kodi_item()
            stream = part.get_media_stream_for_bitrate(bitrate)
            Logger.info("Selected Stream:  %s", stream)
            if stream.Adaptive:
                Adaptive.set_max_bitrate(stream, max_bit_rate=bitrate)

            # Set the actual stream path
            kodi_item.setProperty("path", stream.Url)

            # properties of the Part
            for prop in part.Properties + stream.Properties:
                Logger.trace("Adding property: %s", prop)
                kodi_item.setProperty(prop[0], prop[1])

            # TODO: Apparently if we use the InputStream Adaptive, using the setSubtitles() causes sync issues.
            if part.Subtitle and False:
                Logger.debug("Adding subtitle to ListItem: %s", part.Subtitle)
                kodi_item.setSubtitles([
                    part.Subtitle,
                ])

            # Set any custom Header
            header_params = dict()

            # set proxy information if present
            self.__set_kodi_proxy_info(kodi_item, stream, stream.Url,
                                       header_params, proxy)

            # Now add the actual HTTP headers
            for k in part.HttpHeaders:
                header_params[k] = HtmlEntityHelper.url_encode(
                    part.HttpHeaders[k])

            stream_url = stream.Url
            if header_params:
                kodi_query_string = reduce(
                    lambda x, y: "%s&%s=%s" % (x, y, header_params[y]),
                    header_params.keys(), "")
                kodi_query_string = kodi_query_string.lstrip("&")
                Logger.debug("Adding Kodi Stream parameters: %s\n%s",
                             header_params, kodi_query_string)
                stream_url = "%s|%s" % (stream.Url, kodi_query_string)

            play_list_data.append((kodi_item, stream_url))

        return play_list_data
コード例 #13
0
    def create_video_item(self, result_set):
        """ Creates a MediaItem of type 'video' using the result_set from the regex.

        This method creates a new MediaItem from the Regular Expression or Json
        results <result_set>. The method should be implemented by derived classes
        and are specific to the channel.

        If the item is completely processed an no further data needs to be fetched
        the self.complete property should be set to True. If not set to True, the
        self.update_video_item method is called if the item is focussed or selected
        for playback.

        :param list[str]|dict result_set: The result_set of the self.episodeItemRegex

        :return: A new MediaItem of type 'video' or 'audio' (despite the method's name).
        :rtype: MediaItem|None

        """

        Logger.trace('starting FormatVideoItem for %s', self.channelName)
        # Logger.Trace(result_set)

        # the vmanProgramId (like 1019976) leads to http://anytime.tv4.se/webtv/metafileFlash.smil?p=1019976&bw=1000&emulate=true&sl=true
        program_id = result_set["id"]
        # Logger.Debug("ProgId = %s", programId)

        url = "https://playback-api.b17g.net/media/%s?service=tv4&device=browser&protocol=hls" % (
            program_id, )
        name = result_set["title"]

        item = MediaItem(name, url)
        item.description = result_set["description"]
        if item.description is None:
            item.description = item.name

        # premium_expire_date_time=2099-12-31T00:00:00+01:00
        date = result_set["broadcast_date_time"]
        (date_part, time_part) = date.split("T")
        (year, month, day) = date_part.split("-")
        (hour, minutes, rest1, zone) = time_part.split(":")
        item.set_date(year, month, day, hour, minutes, 00)
        broadcast_date = datetime.datetime(int(year), int(month), int(day),
                                           int(hour), int(minutes))

        thumb_url = result_set.get("image", result_set.get("program_image"))
        # some images need to come via a proxy:
        if thumb_url and "://img.b17g.net/" in thumb_url:
            item.thumb = "https://imageproxy.b17g.services/?format=jpg&shape=cut" \
                         "&quality=90&resize=520x293&source={}"\
                .format(HtmlEntityHelper.url_encode(thumb_url))
        else:
            item.thumb = thumb_url

        availability = result_set["availability"]
        # noinspection PyTypeChecker
        free_period = availability["availability_group_free"]
        # noinspection PyTypeChecker
        premium_period = availability["availability_group_premium"]

        now = datetime.datetime.now()
        if False and not premium_period == "0":
            # always premium
            free_expired = now - datetime.timedelta(days=99 * 365)
        elif free_period == "30+" or free_period is None:
            free_expired = broadcast_date + datetime.timedelta(days=99 * 365)
        else:
            free_expired = broadcast_date + datetime.timedelta(
                days=int(free_period))
        Logger.trace(
            "Premium info for: %s\nPremium state: %s\nFree State:    %s\nBroadcast %s vs Expired %s",
            name, premium_period, free_period, broadcast_date, free_expired)

        if now > free_expired:
            item.isPaid = True

        item.type = "video"
        item.complete = False
        item.icon = self.icon
        item.isGeoLocked = result_set["is_geo_restricted"]
        item.isDrmProtected = result_set["is_drm_protected"]
        item.isLive = result_set.get("is_live", False)
        if item.isLive:
            item.url = "{0}&is_live=true".format(item.url)

        return item
コード例 #14
0
    def update_video_for_mzid(self, item, mzid, live=False):  # NOSONAR
        """ Updates a video item based on the MZID

        :param MediaItem item: the parent item
        :param str mzid:       the MZID

        """

        # region New URL retrieval with DRM protection
        # We need a player token
        token_data = UriHandler.open(
            "https://media-services-public.vrt.be/"
            "vualto-video-aggregator-web/rest/external/v1/tokens",
            data="",
            additional_headers={"Content-Type": "application/json"})

        token = JsonHelper(token_data).get_value("vrtPlayerToken")

        asset_url = "https://media-services-public.vrt.be/vualto-video-aggregator-web/rest/" \
                    "external/v1/videos/{0}?vrtPlayerToken={1}&client=vrtvideo"\
            .format(HtmlEntityHelper.url_encode(mzid), HtmlEntityHelper.url_encode(token))
        asset_data = UriHandler.open(asset_url,
                                     proxy=self.proxy,
                                     no_cache=True)
        asset_data = JsonHelper(asset_data)

        drm_key = asset_data.get_value("drm")
        drm_protected = drm_key is not None
        adaptive_available = AddonSettings.use_adaptive_stream_add_on(
            with_encryption=drm_protected, channel=self)
        part = item.create_new_empty_media_part()
        srt = None

        # see if we prefer hls over dash
        hls_prio = 2 if self._get_setting("hls_over_dash",
                                          'false') == 'true' else 0

        for target_url in asset_data.get_value("targetUrls"):
            video_type = target_url["type"]
            video_url = target_url["url"]

            if video_type == "hls_aes" and drm_protected and adaptive_available:
                # no difference in encrypted or not.
                Logger.debug("Found HLS AES encrypted stream and a DRM key")
                stream = part.append_media_stream(video_url, hls_prio)
                M3u8.set_input_stream_addon_input(stream, self.proxy)

            elif video_type == "hls" and not drm_protected:
                # no difference in encrypted or not.
                if adaptive_available:
                    Logger.debug(
                        "Found standard HLS stream and without DRM protection")
                    stream = part.append_media_stream(video_url, hls_prio)
                    M3u8.set_input_stream_addon_input(stream, self.proxy)
                else:
                    m3u8_data = UriHandler.open(video_url, self.proxy)
                    for s, b, a in M3u8.get_streams_from_m3u8(
                            video_url,
                            self.proxy,
                            play_list_data=m3u8_data,
                            map_audio=True):
                        item.complete = True
                        if a:
                            audio_part = a.rsplit("-", 1)[-1]
                            audio_part = "-%s" % (audio_part, )
                            s = s.replace(".m3u8", audio_part)
                        part.append_media_stream(s, b)

                    srt = M3u8.get_subtitle(video_url,
                                            play_list_data=m3u8_data,
                                            proxy=self.proxy)
                    if not srt or live:
                        # If there is not SRT don't download it. If it a live stream with subs,
                        # don't use it as it is not supported by Kodi
                        continue

                    srt = srt.replace(".m3u8", ".vtt")
                    part.Subtitle = SubtitleHelper.download_subtitle(
                        srt, format="webvtt")

            elif video_type == "mpeg_dash" and adaptive_available:
                if not drm_protected:
                    Logger.debug(
                        "Found standard MPD stream and without DRM protection")
                    stream = part.append_media_stream(video_url, 1)
                    Mpd.set_input_stream_addon_input(stream, self.proxy)
                else:
                    stream = part.append_media_stream(video_url, 1)
                    encryption_json = '{{"token":"{0}","drm_info":[D{{SSM}}],"kid":"{{KID}}"}}'\
                        .format(drm_key)
                    encryption_key = Mpd.get_license_key(
                        key_url="https://widevine-proxy.drm.technology/proxy",
                        key_type="D",
                        key_value=encryption_json,
                        key_headers={
                            "Content-Type": "text/plain;charset=UTF-8"
                        })
                    Mpd.set_input_stream_addon_input(
                        stream, self.proxy, license_key=encryption_key)

            if video_type.startswith("hls") and srt is None:
                srt = M3u8.get_subtitle(video_url, proxy=self.proxy)
                if not srt or live:
                    # If there is not SRT don't download it. If it a live stream with subs,
                    # don't use it as it is not supported by Kodi
                    continue

                srt = srt.replace(".m3u8", ".vtt")
                part.Subtitle = SubtitleHelper.download_subtitle(
                    srt, format="webvtt")

            item.complete = True
        # endregion
        return item
コード例 #15
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

        """

        Logger.debug('Starting update_video_item for %s (%s)', item.name,
                     self.channelName)

        # 1 - get the overal config file
        guid_regex = 'http://[^:]+/mgid:[^"]+:([0-9a-f-]+)"'
        rtmp_regex = r'type="video/([^"]+)" bitrate="(\d+)">\W+<src>([^<]+)</src>'

        data = UriHandler.open(item.url, proxy=self.proxy)
        guids = Regexer.do_regex(guid_regex, data)

        item.MediaItemParts = []
        for guid in guids:
            # get the info for this part
            Logger.debug("Processing part with GUID: %s", guid)

            # reset stuff
            part = None

            # http://www.southpark.nl/feeds/video-player/mediagen?uri=mgid%3Aarc%3Aepisode%3Acomedycentral.com%3Aeb2a53f7-e370-4049-a6a9-57c195367a92&suppressRegisterBeacon=true
            guid = HtmlEntityHelper.url_encode(
                "mgid:arc:episode:comedycentral.com:%s" % (guid, ))
            info_url = "%s/feeds/video-player/mediagen?uri=%s&suppressRegisterBeacon=true" % (
                self.baseUrl, guid)

            # 2- Get the GUIDS for the different ACTS
            info_data = UriHandler.open(info_url, proxy=self.proxy)
            rtmp_streams = Regexer.do_regex(rtmp_regex, info_data)

            for rtmp_stream in rtmp_streams:
                # if this is the first stream for the part, create an new part
                if part is None:
                    part = item.create_new_empty_media_part()

                part.append_media_stream(
                    self.get_verifiable_video_url(rtmp_stream[2]),
                    rtmp_stream[1])

        item.complete = True
        Logger.trace("Media item updated: %s", item)
        return item