def __UpdateVideoItem(self, item, episodeId):
        """Updates an existing MediaItem with more data.

        Arguments:
        item      : MediaItem - the MediaItem that needs to be updated
        episodeId : String    - The episodeId, e.g.: VARA_xxxxxx

        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.Trace("Using Generic UpdateVideoItem method")

        # get the subtitle
        subTitleUrl = "http://tt888.omroep.nl/tt888/%s" % (episodeId,)
        subTitlePath = subtitlehelper.SubtitleHelper.DownloadSubtitle(subTitleUrl,
                                                                      episodeId + ".srt",
                                                                      format='srt',
                                                                      proxy=self.proxy)

        item.MediaItemParts = []
        part = item.CreateNewEmptyMediaPart()
        part.Subtitle = subTitlePath

        for s, b in NpoStream.GetStreamsFromNpo(None, episodeId, proxy=self.proxy):
            item.complete = True
            # s = self.GetVerifiableVideoUrl(s)
            part.AppendMediaStream(s, b)

        if AddonSettings.IsMinVersion(18):
            for s, b, p in NpoStream.GetMpdStreamFromNpo(None, episodeId, proxy=self.proxy):
                item.complete = True
                # s = self.GetVerifiableVideoUrl(s)
                stream = part.AppendMediaStream(s, b)
                for k, v in p.iteritems():
                    stream.AddProperty(k, v)

        return item
    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 ===============

        # setup the urls
        self.__api = None
        self.__sso = None
        if self.channelCode == "vtm":
            self.noImage = "vtmbeimage.jpg"
            self.mainListUri = "https://vtm.be/feed/programs?format=json&type=all&only_with_video=true"
            self.mainListUri = "https://vtm.be/video/?f[0]=sm_field_video_origin_cms_longform%3AVolledige%20afleveringen"
            self.baseUrl = "https://vtm.be"
            self.__app = "vtm_watch"
            self.__sso = "vtm-sso"
            self.__apiKey = "vtm-b7sJGrKwMJj0VhdZvqLDFvgkJF5NLjNY"

            # setup the main parsing data in case of HTML
            htmlVideoRegex = '<img[^>]+class="media-object"[^>]+src="(?<thumburl>[^"]+)[^>]*>[\w\W]{0,1000}?<a[^>]+href="/(?<url>[^"]+)"[^>]*>(?<title>[^<]+)'
            htmlVideoRegex = Regexer.FromExpresso(htmlVideoRegex)
            self._AddDataParser(
                "https://vtm.be/video/?f[0]=sm_field_video_origin_cms_longform%3AVolledige%20afleveringen&",
                name="HTML Page Video Parser for VTM",
                # preprocessor=self.AddMoreRecentVideos,
                parser=htmlVideoRegex, creator=self.CreateVideoItemHtml)

            recentRegex = '<a href="/(?<url>[^"]+)"[^>]*>\W+(?:<div[^>]+>\W+)+<img[^>]+src="(?<thumburl>[^"]+)"[^>]+>\W*<span[^>]*>\W*(?<subtitle>[^<]+)[\w\W]{0,300}?(?:<div[^>]+class="item-caption-program"[^>]*>(?<title>[^<]+)</div>\W*)</div>\W*</div>\W*</div>\W*</a'
            # recentRegex = 'data-video-id="(?<url>\d+)"[^>]*>\W+<[^>]+>\W+<img[^>]*src="(?<thumburl>[^"]+)[^>]*>[\W\w]{0,1000}?class="item-caption-title"[^>]*>(?<subtitle>[^<]+)<[^>]+>\W*<[^>]+>\W*<a[^>]+>(?<title>[^<]+)'
            recentRegex = Regexer.FromExpresso(recentRegex)
            self._AddDataParser(
                "https://vtm.be/video/volledige-afleveringen/id",
                matchType=ParserData.MatchExact,
                name="Recent Items HTML Video Parser",
                parser=recentRegex,
                creator=self.CreateVideoItemHtml
            )

        elif self.channelCode == "q2":
            self.noImage = "q2beimage.jpg"
            self.mainListUri = "https://www.q2.be/feed/programs?format=json&type=all&only_with_video=true"
            self.mainListUri = "https://www.q2.be/video/?f[0]=sm_field_video_origin_cms_longform%3AVolledige%20afleveringen"
            self.baseUrl = "https://www.q2.be"
            self.__app = "q2"
            self.__sso = "q2-sso"
            self.__apiKey = "q2-html5-NNSMRSQSwGMDAjWKexV4e5Vm6eSPtupk"

            htmlVideoRegex = '<a[^>]+class="cta-full[^>]+href="/(?<url>[^"]+)"[^>]*>[^<]*</a>\W*<span[^>]*>[^<]*</[^>]*\W*<div[^>]*>\W*<img[^>]+src="(?<thumburl>[^"]+)[\w\W]{0,1000}?<h3[^>]*>(?<title>[^<]+)'
            htmlVideoRegex = Regexer.FromExpresso(htmlVideoRegex)
            self._AddDataParser(
                "https://www.q2.be/video/?f[0]=sm_field_video_origin_cms_longform%3AVolledige%20afleveringen&",
                name="HTML Page Video Parser for Q2",
                parser=htmlVideoRegex, creator=self.CreateVideoItemHtml)

        elif self.channelCode == "stievie":
            self.__app = "stievie"
            self.__sso = "stievie-sso"
            self.__apiKey = "stievie-web-2.8-yz4DSTPshescHUytkWwU9jDxQ28PKTGn"
            self.noImage = "stievieimage.jpg"
            self.httpHeaders["Authorization"] = "apikey=%s" % (self.__apiKey, )

            # self.mainListUri = "https://vod.medialaan.io/vod/v2/programs?offset=0&limit=0"
            self.mainListUri = "https://channels.medialaan.io/channels/v1/channels?preview=false"
            self._AddDataParser(self.mainListUri,
                                json=True,
                                preprocessor=self.StievieMenu,
                                name="JSON Channel overview",
                                parser=("response", "channels"),
                                creator=self.StievieCreateChannelItem)

            self._AddDataParser("#channel", name="Channel menu parser",
                                preprocessor=self.StievieChannelMenu)

            # main list parsing
            self._AddDataParser("https://vod.medialaan.io/vod/v2/programs?offset=0&limit=0",
                                json=True,
                                name="Main program list parsing for Stievie",
                                # preprocessor=self.AddLiveChannel,
                                creator=self.StievieCreateEpisode,
                                parser=("response", "videos"))

            self._AddDataParser("https://epg.medialaan.io/epg/v2/", json=True,
                                name="EPG Stievie parser",
                                creator=self.StievieCreateEpgItems,
                                parser=("channels", ))

            self._AddDataParser("https://vod.medialaan.io/vod/v2/programs?query=",
                                name="Stievie Search Parser", json=True,
                                creator=self.StievieCreateEpisode,
                                parser=("response", "videos"))

        else:
            raise NotImplementedError("%s not supported yet" % (self.channelCode, ))

        # generic to all channels
        htmlEpisodeRegex = '<a[^>]+href="(?<url>[^"]+im_field_program[^"]+)"[^>]+>(?<title>[^(<]+)'
        htmlEpisodeRegex = Regexer.FromExpresso(htmlEpisodeRegex)
        self._AddDataParser(
            "sm_field_video_origin_cms_longform%3AVolledige%20afleveringen",
            matchType=ParserData.MatchEnd,
            name="HTML Page Show Parser",
            preprocessor=self.AddLiveChannel,
            parser=htmlEpisodeRegex,
            creator=self.CreateEpisodeItemHtml)

        self._AddDataParser(
            "https://(?:vtm.be|www.q2.be)/video/?.+=sm_field_video_origin_cms_longform%3AVolledige%20afleveringen&.+id=\d+",
            matchType=ParserData.MatchRegex,
            name="HTML Page Video Updater",
            updater=self.UpdateVideoItem, requiresLogon=True)

        self._AddDataParser(
            "https://vtm.be/video/volledige-afleveringen/id/",
            name="HTML Page Video Updater New Style (AddMoreRecentVideos)",
            updater=self.UpdateVideoItem, requiresLogon=True)

        # setup the main parsing data in case of JSON
        self._AddDataParser("/feed/programs?format=json&type=all&only_with_video=true",
                            matchType=ParserData.MatchEnd,
                            name="JSON Feed Show Parser for Medialaan",
                            json=True, preprocessor=self.AddLiveChannelAndFetchAllData,
                            creator=self.CreateEpisodeItemJson, parser=("response", "items"))

        self._AddDataParser("https://vod.medialaan.io/api/1.0/list", json=True,
                            name="JSON Video Parser for Medialaan",
                            preprocessor=self.AddVideoPageItemsJson,
                            parser=("response", "items"), creator=self.CreateVideoItemJson)

        self._AddDataParser("https://vod.medialaan.io/vod/v2/videos/",
                            matchType=ParserData.MatchRegex,
                            name="JSON Video Updater for Medialaan",
                            updater=self.UpdateVideoItemJson, requiresLogon=True)

        self._AddDataParser("https://vod.medialaan.io/vod/v2/videos?",
                            name="JSON Video Updater for Medialaan with programOID",
                            updater=self.UpdateVideoEpgItemJson, requiresLogon=True)

        # self._AddDataParser("https://vtm.be/video?aid=", name="HTML Stream Updater",
        #                     requiresLogon=True, updater=self.UpdateVideoItem)

        self._AddDataParser("#livestream", name="Live Stream Updater for Q2, VTM and Stievie",
                            requiresLogon=True, updater=self.UpdateLiveStream)

        # ===============================================================================================================
        # non standard items
        self.__signature = None
        self.__signatureTimeStamp = None
        self.__userId = None
        self.__cleanRegex = re.compile("<[^>]+>")
        self.__dashStreamsSupported = AddonSettings.IsMinVersion(18)

        # Mappings from the normal URL (which has all shows with actual videos and very little
        # video-less shows) to the JSON ids. Loading can be done using:
        #     import json
        #     fp = file("c:\\temp\\ff.json")
        #     data = json.load(fp)
        #     fp.close()
        #     mapping = dict()
        #     for item in data["response"]["items"]:
        #         if not item["parent_series_oid"]:
        #             continue
        #         mapping[item["title"]] = item["parent_series_oid"]
        #     print json.dumps(mapping)
        #
        # TODO: perhap we can do this dynamically?

        self.__mappings = {
            "q2": {
                "Grimm": "256511352168527", "Homeland": "256467029990527",
                "Vikings": "256528439042527", "The Big Bang Theory": "256467024031527",
                "Brooklyn Nine-Nine": "256575703668527",
                "The Graham Norton Show": "256943055386527",
                "Person of Interest": "256467035258527", "Grounded for Life": "256575957717527",
                "Valemont": " 256433841939527", "Advocaat van de Duivel": "256816070531527",
                "Quantico": "256684804053527", "__The Middle": "256577035751527",
                "Life in Pieces": "256651006814527", "My Wife & Kids": "257045563302527",
                "Dawson's Creek": "256575773017527", "Dracula": "256588092015527",
                "__Two and a Half Men": "256490829206527", "Modern Family": "256467031756527",
                "Game of Thrones": "256588988771527", "Marvel's Agent Carter": "256576832916527",
                "Jo": "256576812799527", "Hit The Floor": "256594294622527",
                "Foute Vrienden": "256403630232527", "That '70s Show": "256467039034527",
                "Covert Affairs": "256467028271527", "__Champions League": "256584896142527",
                "24: Live Another Day": "256539021922527",
                "Het Beste van X-Factor Worldwide": "256575966527527",
                "Mr. Robot": "256757142789527", "Graceland": "256528403044527",
                "The Glades": "256467029451527", "Arrested Development": "256467009408527",
                "Dads": "256586859951527", "Marvel's Agents of S.H.I.E.L.D.": "256576835198527",
                "__The Muppets": "256684804375527", "The Voice USA": "256946841840527",
                "__Top Gear": "256528438369527", "Friends With Better Lives": "256588965013527",
                "New Girl": "256467032228527", "Tricked": "256573688559527",
                "Community": "256973035121527", "Salem": "256676854495527",
                "Rude Tube": "256433822255527", "Bones": "256404132799527",
                "Rosewood": "256650982517527", "The Crazy Ones": "256467028697527",
                "Married with Children": "256576831734527", "Crisis": "256511351727527"
            },
            "vtm": {
                "Helden van Hier: Door het Vuur": "256588089798527", "Aspe": "256382495645527",
                "Alloo bij ...": "256943106645527", "De Zonen van Van As": "256407562265527",
                "Heidi": "256463008600527", "Coppers": "256685693714527",
                "Chicago Med": "256722572301527", "Altijd Prijs": "256544288119527",
                "De Vetste Vakantie": "256676855101527", "Familie": "256383171504527",
                "Binnenstebuiten": "256575685561527", "Code 37": "256407560206527",
                "De Bunker": "256587035116527", "De Drone School": "256850916997527",
                "De 25": "256454876662527", "Het Grootste Licht": "256676855233527",
                "Axel Opgelicht": "256436933413527", "Het Lichaam van Coppens": "256402076016527",
                "America's Funniest Home Videos": "256547897482527",
                "Expeditie Paira Daiza": "256574614075527", "Cordon": "256407560819527",
                "Alloo in de Buitenlandse Gevangenis": "256454773025527",
                "De Wensboom": "256725880042527", "FAROEK": "256575897637527",
                "Cathérine": "256856277380527", "De Avonturen van K3": "256595788573527",
                "Ella": "256588631982527", "De Funnie Show": "256587019478527",
                "Cycling Cup": "256778013268527", "Dubbelspel": "256920728612527",
                "Allemaal Chris": "256936077540527", "Baas in Huis": "256463013040527",
                "De Keuken van Sofie": "256575862270527",
                "Helden van Hier: In de Lucht": "256996171054527",
                "Brandweerman Sam": "256475467798527", "Blind Getrouwd": "256589828137527",
                "Clan": "256407588081527", "De Waarzeggers": "256431811242527",
                "Alloo bij de Lokale Politie ": "256544207094527",
                "Het Furchester Hotel": "256831536569527",
                "Dynamo: Magician Impossible": "256676855209527",
                "Alloo bij de Wegpolitie": "256676855317527", "De Buurtpolitie": "256403648640527",
                "Amigo's": "256544290999527", "David": "256586890385527",
                "Amateurs": "256403567370527", "__Border Security": "256472727848527",
                "Alloo in de Psychiatrie": "256544231522527", "Danni Lowinski": "256404119004527",
                "De Disco Dans Show": "256798376480527",
                "Het Geheime Leven van 5-jarigen": "256611388403527",
                "__Belgium's Got Talent": "256462951774527", "De Kotmadam": "256403656559527",
                "Gezond Verstand": "257076990290527", "Beat da Bompaz": "256433651880527",
                "BK Sumo 2016": "256577054292527", "Beste Kijkers": "256407593035527",
                "Geert Hoste": "256573544524527", "Benidorm Bastards USA": "256472726922527",
                "Benidorm Bastards": "256575677508527", "De Kliniek": "256547901203527",
                "De Rodenburgs": "256407561643527", "__Deze Is Voor Jou": "256728833164527",
                "Grote Ster, Kleine Ster": "256575950955527",
                "Alloo bij Jambers": "256595573889527", "De Kroongetuigen": "256407561421527",
                "Groeten uit": "257040505722527", "Comedy Toppers": "256403657663527",
                "Little People": "256597844370527", "Rode Neuzen Dag": "257023165962527",
                "Stadion": "256573559156527", "Spitsbroers": "256471098273527",
                "Wat Als?": "256407582304527", "Til Death": "256676850133527",
                "Pac-Man en de Spook Avonturen": "256475476997527", "The Team": "256676855137527",
                "Vlaamse Streken": "256486551290527", "VTM Telefoneert": "256168188259527",
                "Sofie in de Keuken van": "256939967974527",
                "Patrouille Linkeroever": "256676854375527",
                "So You Think You Can Dance": "256464798482527", "Odd Squad": "256710652408527",
                "Vind Mijn Familie": "256384038011527", "Nicholas": "256454884349527",
                "Little Big Shots": "256897888122527",
                "Liefde voor Sterren tegen de Muziek op": "256597815326527",
                "LouisLouise": "256598003727527", "Jonas & Van Geel": "256595778045527",
                "Project K": "256611396531527", "Hollywood in 't echt": "256594308987527",
                "Maya de Bij": "256547919980527", "Zone Stad ": "15777992529",
                "Jill": "256853912126527", "Moerkerke en de mannen": "256676855221527",
                "Met Vier in Bed": "256547903459527", "Vinger Aan De Poot": "256547852448527",
                "Mijn Pop-uprestaurant!": "256477480591527", "Pak Ace": "256729926613527",
                "Vossenstreken": "256433821163527", "The Voice Kids": "256676855365527",
                "Wittekerke": "256403641361527", "Total Loss in het Bos": "256664909485527",
                "Telefacts": "256407577960527", "Royalty": "256407571395527",
                "Lang Leve...": "256403648386527", "Valkuil": "256676854981527",
                "Moerkerke en de Vrouwen": "257108815284527", "__Lotgenoten": "256586980503527",
                "Safety First": "256402022747527", "The Voice van Vlaanderen": "256577041480527",
                "Zuidflank": "256404868560527", "Rijker dan je Denkt?": "256403651256527",
                "Special Forces": "257001201929527", "Uit de Kast": "256611410411527",
                "VTM NIEUWS": "256547855317527", "Wild van Dieren": "256084514960527",
                "Shades of Blue": "256757101999527", "Het Weer": "256547857195527",
                "Tegen de Sterren op": "256407581965527", "K3 zoekt K3": "256576815747527",
                "Turbo FAST": "256676855125527", "The Band": "256996808958527",
                "Ligt er Flan op de Mont Blanc?": "257007717785527",
                "Zijn er nog Kroketten?": "256403645293527",
                "Liefde voor Muziek": "256480753274527",
                "Tom Boonen: My Ride, My Fight, My Life": "256797352477527",
                "McLeod's Daughters": "256472731081527", "Is er Wifi in Tahiti?": "256547902518527",
                "Moordvrouw": "256496106525527", "The Amazing Spiez!": "256882084522527",
                "Wij zijn K3": "256725887906527"
            }
        }

        # ===============================================================================================================
        # Test cases:

        # ====================================== Actual channel setup STOPS here =======================================
        return
    def GetXBMCPlayList(self, bitrate, updateItemUrls=False, proxy=None):
        """ Creates a XBMC Playlist containing the MediaItemParts in this MediaItem

        Keyword Arguments:
        bitrate        : integer         - The bitrate of the streams that should be in
                                           the playlist. Given in kbps

        updateItemUrls : [opt] boolean   - If specified, the Playlist items will
                                           have a path pointing to the actual stream
        proxy          : [opt] ProxyInfo - The proxy to set

        Returns:
        a XBMC Playlist for this MediaItem

        If the Bitrate keyword is omitted the the bitrate is retrieved using the
        default bitrate settings:

        """

        playList = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
        srt = None

        playListItems = []
        if not updateItemUrls:
            # if we are not using the resolveUrl method, we need to clear the playlist and set the index
            playList.clear()
            currentIndex = 0
        else:
            # copy into a list so we can add stuff in between (we can't do that in an
            # XBMC PlayList) and then create a new playlist item
            currentIndex = playList.getposition(
            )  # this is the location at which we are now.
            if currentIndex < 0:
                # no items where there, so we can just start at position 0
                currentIndex = 0

            Logger.Info(
                "Updating the playlist for item at position %s and trying to preserve other playlist items",
                currentIndex)
            for i in range(0, len(playList)):
                Logger.Trace("Copying playList item %s out of %s", i + 1,
                             len(playList))
                playListItems.append((playList[i].getfilename(), playList[i]))

            startList = reduce(
                lambda x, y: "%s\n%s" % (x, y[0]), playListItems,
                "Starting with Playlist Items (%s)" % (len(playListItems), ))
            Logger.Debug(startList)
            playList.clear()

        logText = "Creating playlist for Bitrate: %s kbps\n%s\nSelected Streams:\n" % (
            bitrate, self)

        # for each MediaItemPart get the URL, starting at the current index
        index = currentIndex
        for part in self.MediaItemParts:
            if len(part.MediaStreams) == 0:
                Logger.Warning("Ignoring empty MediaPart: %s", part)
                continue

            # get the playlist item
            (stream, xbmcItem) = part.GetXBMCPlayListItem(
                self, bitrate, updateItemUrls=updateItemUrls)
            logText = "%s\n + %s" % (logText, stream)

            streamUrl = stream.Url
            xbmcParams = dict()
            if proxy:
                if stream.Downloaded:
                    logText = "%s\n    + Not adding proxy as the stream is already downloaded" % (
                        logText, )
                elif proxy.Scheme.startswith(
                        "http") and not stream.Url.startswith("http"):
                    logText = "%s\n    + Not adding proxy due to scheme mismatch" % (
                        logText, )
                elif proxy.Scheme == "dns":
                    logText = "%s\n    + Not adding DNS proxy for Kodi streams" % (
                        logText, )
                elif not proxy.UseProxyForUrl(streamUrl):
                    logText = "%s\n    + Not adding proxy due to filter mismatch" % (
                        logText, )
                else:
                    if AddonSettings.IsMinVersion(17):
                        # See ffmpeg proxy in https://github.com/xbmc/xbmc/commit/60b21973060488febfdc562a415e11cb23eb9764
                        xbmcItem.setProperty("proxy.host", proxy.Proxy)
                        xbmcItem.setProperty("proxy.port", str(proxy.Port))
                        xbmcItem.setProperty("proxy.type", proxy.Scheme)
                        if proxy.Username:
                            xbmcItem.setProperty("proxy.user", proxy.Username)
                        if proxy.Password:
                            xbmcItem.setProperty("proxy.password",
                                                 proxy.Password)
                        logText = "%s\n    + Adding (Krypton) %s" % (logText,
                                                                     proxy)
                    else:
                        xbmcParams["HttpProxy"] = proxy.GetProxyAddress()
                        logText = "%s\n    + Adding (Pre-Krypton) %s" % (
                            logText, proxy)

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

            if xbmcParams:
                xbmcQueryString = reduce(
                    lambda x, y: "%s&%s=%s" % (x, y, xbmcParams[y]),
                    xbmcParams.keys(), "").lstrip("&")
                Logger.Debug("Adding Kodi Stream parameters: %s\n%s",
                             xbmcParams, xbmcQueryString)
                streamUrl = "%s|%s" % (stream.Url, xbmcQueryString)

            if index == currentIndex and index < len(playListItems):
                # We need to replace the current item.
                Logger.Trace(
                    "Replacing current Kodi ListItem at Playlist index %s (of %s)",
                    index, len(playListItems))
                playListItems[index] = (streamUrl, xbmcItem)
            else:
                # We need to add at the current index
                Logger.Trace("Inserting Kodi ListItem at Playlist index %s",
                             index)
                playListItems.insert(index, (streamUrl, xbmcItem))

            index += 1

            # for now we just add the last subtitle, this will not work if each
            # part has it's own subtitles.
            srt = part.Subtitle

        Logger.Info(logText)

        endList = reduce(
            lambda x, y: "%s\n%s" % (x, y[0]), playListItems,
            "Ended with Playlist Items (%s)" % (len(playListItems), ))
        Logger.Debug(endList)
        for playListItem in playListItems:
            playList.add(playListItem[0], playListItem[1])

        return playList, srt