From ced9fda885c4be1a748d30122545703224d5f283 Mon Sep 17 00:00:00 2001 From: Florian Liers Date: Mon, 13 Jan 2014 21:15:19 +0100 Subject: [PATCH 01/11] Create README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..675bacc --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +plugin.video.ardmediathek_de +============================ + +Fork for implementing video download capabilities. From 37ee88efc85500ad31fcfad9e83a2868355856cf Mon Sep 17 00:00:00 2001 From: Florian Liers Date: Tue, 14 Jan 2014 14:50:46 +0100 Subject: [PATCH 02/11] Adds download option for videos This commit adds a context menu entry for downloading video streams to a folder specified in the plug-in configuration. It was inspired by the YouTube plug-in (http://wiki.xbmc.org/index.php?title=Add-on:YouTube), which uses the SimpleDownloader plug-in (http://wiki.xbmc.org/index.php?title=Add-on:Simple_Downloader_for_xbmc_plugins) for downloading video streams. The new dependency to the SimpleDownloader is an optional one. The code reports an error to the user, if the dependency is not installed/activated. Translations for the new menu entries are given in German and English. Changes tested with XBMC 12.2 on Windows and Linux. --- addon.xml | 1 + default.py | 121 +++++++++++++++++++++++-- resources/language/English/strings.xml | 8 ++ resources/language/German/strings.xml | 8 ++ resources/settings.xml | 1 + 5 files changed, 132 insertions(+), 7 deletions(-) diff --git a/addon.xml b/addon.xml index fb80859..0a57c27 100644 --- a/addon.xml +++ b/addon.xml @@ -2,6 +2,7 @@ + video diff --git a/default.py b/default.py index 7074fab..2fe1e09 100644 --- a/default.py +++ b/default.py @@ -19,6 +19,9 @@ forceViewMode = addon.getSetting("forceViewMode") == "true" useThumbAsFanart=addon.getSetting("useThumbAsFanart") == "true" viewMode = str(addon.getSetting("viewMode")) +errorMessageDurationSec = 20 +characterEncoding = "iso-8859-15" + baseUrl = "http://www.ardmediathek.de" defaultThumb = baseUrl+"/ard/static/pics/default/16_9/default_webM_16_9.jpg" defaultBackground = "http://www.ard.de/pool/img/ard/background/base_xl.jpg" @@ -273,12 +276,24 @@ def listVideosDossier(url): xbmc.executebuiltin('Container.SetViewMode('+viewMode+')') -def playVideo(url): +# +# Extracts the stream URL from a web page +# +# Returns two results: +# 1. stream URL or None on error +# 2. error message in case of error +# +def extractStreamURL(url): + # get web page content = getUrl(url) + # analyse the web page matchFSK = re.compile('
(.+?)
', re.DOTALL).findall(content) if matchFSK: fsk = matchFSK[0].strip() - xbmc.executebuiltin('XBMC.Notification(Info:,'+fsk+',15000)') + if fsk: + return None, fsk + else: + return None, content else: match5 = re.compile('addMediaStream\\(1, 2, "", "(.+?)"', re.DOTALL).findall(content) match6 = re.compile('addMediaStream\\(1, 1, "", "(.+?)"', re.DOTALL).findall(content) @@ -287,7 +302,7 @@ def playVideo(url): match3 = re.compile('addMediaStream\\(0, 1, "(.+?)", "(.+?)"', re.DOTALL).findall(content) match4 = re.compile('addMediaStream\\(0, 1, "", "(.+?)"', re.DOTALL).findall(content) matchUT = re.compile('setSubtitleUrl\\("(.+?)"', re.DOTALL).findall(content) - url = "" + url = None if match5: url = match5[0] elif match6: @@ -309,10 +324,83 @@ def playVideo(url): if url: if "?" in url: url = url[:url.find("?")] - listitem = xbmcgui.ListItem(path=url) - xbmcplugin.setResolvedUrl(pluginhandle, True, listitem) - if showSubtitles and matchUT: - setSubtitle(baseUrl+matchUT[0]) + return url, None + else: + return None, content + + +# +# Start playing a stream by extracting the stream URL from a web page URL +# +def playVideo(url): + streamURL, errorMsg = extractStreamURL(url) + print("Playing " +streamURL +" (web page = " +url +")") + if streamURL: + listitem = xbmcgui.ListItem(path=streamURL) + xbmcplugin.setResolvedUrl(pluginhandle, True, listitem) + if showSubtitles and matchUT: + setSubtitle(baseUrl+matchUT[0]) + else: + reportError(translation(30200), translation(30202) +" " +errorMsg) + + +# +# Enqueues a video to the download list of the plugin SimpleDownloader +# +def downloadVideo(url, name): + # determine stream URL from web page URL + streamURL, errorMsg = extractStreamURL(url) + + # handle error case + if not streamURL: + # avoid encoding problems within string operation in call to reportError + if isinstance(errorMsg, unicode): + errorMsg = errorMsg.encode(characterEncoding, "replace") + baseMsg = translation(30202) + if isinstance(baseMsg, unicode): + baseMsg = baseMsg.encode(characterEncoding, "replace") + reportError(translation(30200), baseMsg +" " +errorMsg) + return + + print("Download '" +name +"' from '" +streamURL +"' (web page url = '" +url +"')") + + # use original file name as suffix in order to (a) have correct extension + # for file and (b) to enable multiple files with same base name (e.g. + # different format/resolutions) + nameClean = name +" - " +os.path.basename(streamURL) + # clean up name to ASCII characters, because it is used as file name later on + # remove any problematic characters (e.g., 'ü') + regex = re.compile('[^a-zA-Z0-9_\.\- ]+') + nameClean = regex.sub('_', nameClean).strip() + + # check name of destination folder (from plug-in configuration) + downloadFolder = addon.getSetting("downloadFolder") + if downloadFolder: + print("Download '" +nameClean +"' to folder '" +downloadFolder +"'") + else: + reportError(translation(30200), translation(30203)) + return + + # init SimpleDownloader plugin + downloader = None + try: + import SimpleDownloader + downloader = SimpleDownloader.SimpleDownloader() + except: + reportError(translation(30200), translation(30201)) + return + + # debugging: remove invalid old entries from persistent queue + #downloader.cleanQueue() + + # set parameters for download + dparams = {} + dparams["Title"] = name + dparams["url"] = streamURL + dparams["download_path"] = downloadFolder + + # start download itself + downloader.download(nameClean, dparams) def setSubtitle(url): @@ -478,6 +566,7 @@ def addLink(name, url, mode, iconimage, duration="", desc=""): else: liz.setProperty("fanart_image", defaultBackground) liz.addContextMenuItems([(translation(30012), 'RunPlugin(plugin://'+addonID+'/?mode=queueVideo&url='+urllib.quote_plus(u)+'&name='+urllib.quote_plus(name)+')',)]) + liz.addContextMenuItems([(translation(30040), 'RunPlugin(plugin://'+addonID+'/?mode=downloadVideo&url='+urllib.quote_plus(url)+'&name='+urllib.quote_plus(name)+')',)]) ok = xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=u, listitem=liz) return ok @@ -530,6 +619,22 @@ def addShowFavDir(name, url, mode, iconimage): ok = xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=u, listitem=liz, isFolder=True) return ok + +# +# Saves an error message in the log file and outputs it to the user on the screen +# +def reportError(title, msg): + # convert to unicode in order to avoid later exceptions + if isinstance(title, unicode): + title = title.encode(characterEncoding, "replace") + if isinstance(msg, unicode): + msg = msg.encode(characterEncoding, "replace") + # save it in log file + print(title +": " +msg) + # display it + xbmc.executebuiltin('XBMC.Notification(' +title +',' +msg +',' +str(errorMessageDurationSec *1000) +')') + + params = parameters_string_to_dict(sys.argv[2]) mode = urllib.unquote_plus(params.get('mode', '')) url = urllib.unquote_plus(params.get('url', '')) @@ -561,6 +666,8 @@ def addShowFavDir(name, url, mode, iconimage): listShowVideos(url) elif mode == 'playVideo': playVideo(url) +elif mode == 'downloadVideo': + downloadVideo(url, name) elif mode == "queueVideo": queueVideo(url, name) elif mode == 'playLive': diff --git a/resources/language/English/strings.xml b/resources/language/English/strings.xml index e15f651..b66c3ea 100644 --- a/resources/language/English/strings.xml +++ b/resources/language/English/strings.xml @@ -22,8 +22,16 @@ Add to addon favs Remove from addon favs Play alternative Stream-URL + Download video + Force View ViewID Use thumb as fanart Activate subtitles (if available) + Destination folder for downloaded videos + + Error + Plug-in SimpleDownloader required for downloading not found. Maybe it has to be installed first. + Stream URL can not be determined. Web page: + Destination folder not defined. Please define it in the configuration section of this plug-in. diff --git a/resources/language/German/strings.xml b/resources/language/German/strings.xml index 055e193..09c8ba1 100644 --- a/resources/language/German/strings.xml +++ b/resources/language/German/strings.xml @@ -22,7 +22,15 @@ Zu Addon Favs hinzufügen Aus Addon Favs entfernen Alternative Stream-Url abspielen + Video herunterladen + View erzwingen Thumb als Fanart nutzen Untertitel aktivieren (falls verfügbar) + Zielordner für heruntergeladene Videos + + Fehler + Zum Herunterladen notwendiges Plugin SimpleDownloader nicht gefunden. Es muss möglicherweise erst installiert werden. + Stream-URL kann nicht bestimmt werden. Webseite: + Zielverzeichnis ist nicht definiert. Bitte Konfiguration des Plug-ins anpassen. diff --git a/resources/settings.xml b/resources/settings.xml index 8132dea..6837ae7 100644 --- a/resources/settings.xml +++ b/resources/settings.xml @@ -3,4 +3,5 @@ + From cfc650a433a87cb05fc844c9695277c08103a6b4 Mon Sep 17 00:00:00 2001 From: Florian Liers Date: Tue, 14 Jan 2014 15:02:25 +0100 Subject: [PATCH 03/11] Rewritten in a more general way for main repo --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 675bacc..acd995a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ plugin.video.ardmediathek_de ============================ -Fork for implementing video download capabilities. +Information: http://wiki.xbmc.org/index.php?title=Add-on:ARD_Mediathek +Features: Streaming and downloading videos from http://www.ardmediathek.de/ From fa10527101e5c99b89e6b5de710f7525a2fb1895 Mon Sep 17 00:00:00 2001 From: Florian Liers Date: Wed, 15 Jan 2014 15:16:15 +0100 Subject: [PATCH 04/11] Fixed regular expression (fixes #3) --- default.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/default.py b/default.py index 2fe1e09..8f9aea0 100644 --- a/default.py +++ b/default.py @@ -88,7 +88,7 @@ def listDossiers(): url = baseUrl+match[0] id = url[url.find("documentId=")+11:] url = baseUrl+"/ard/servlet/ajax-cache/3517004/view=list/documentId="+id+"/goto=1/index.html" - match = re.compile('\n (.+?)\n', re.DOTALL).findall(entry) + match = re.compile('\n (.+?)\n', re.DOTALL).findall(entry) title = cleanTitle(match[0]) match = re.compile('src="(.+?)"', re.DOTALL).findall(entry) thumb = getBetterThumb(baseUrl+match[0]) From 1221d7ddc3c01e075555faeca0f8a362128621e1 Mon Sep 17 00:00:00 2001 From: Florian Liers Date: Wed, 15 Jan 2014 15:32:38 +0100 Subject: [PATCH 05/11] Refactoring for having a single paring function To fix some of the parsing bugs like in #2, a single parsing function for determining the attributes of a video would be easier. Thus, this changes move all the parsing from the "list*" methods to an "iterateContent" (parsing list of content elements) and "extractVideoDescription" (attributes of a single content element). --- default.py | 237 ++++++++++++++++++++++++----------------------------- 1 file changed, 106 insertions(+), 131 deletions(-) diff --git a/default.py b/default.py index 8f9aea0..76d6242 100644 --- a/default.py +++ b/default.py @@ -99,33 +99,7 @@ def listDossiers(): def listShowVideos(url): - content = getUrl(url) - spl = content.split('
') - for i in range(1, len(spl), 1): - entry = spl[i] - if "mt-icon_video" in entry: - match = re.compile('(.+?)', re.DOTALL).findall(entry) - url = baseUrl+match[0][0] - title = cleanTitle(match[0][2]) - match = re.compile('\n (.+?)\n (.+?) min\n ', re.DOTALL).findall(entry) - duration = "" - if match: - date = match[0][0] - duration = match[0][1] - title = date[:5]+" - "+title - if "00:" in duration: - duration = 1 - match = re.compile('src="(.+?)"', re.DOTALL).findall(entry) - thumb = getBetterThumb(baseUrl+match[0]) - if "Livestream" not in title: - addLink(title, url, 'playVideo', thumb, duration) - match = re.compile('(.+?)', re.DOTALL).findall(content) - for url, title in match: - if title == "Weiter": - addDir(translation(30009), baseUrl+url, 'listShowVideos', "", "") - xbmcplugin.endOfDirectory(pluginhandle) - if forceViewMode: - xbmc.executebuiltin('Container.SetViewMode('+viewMode+')') + iterateContent(url, True, 'listShowVideos', createVideoEntry) def listShowsAZMain(): @@ -153,6 +127,10 @@ def listShowsAZ(letter): if forceViewMode: xbmc.executebuiltin('Container.SetViewMode('+viewMode+')') + +# +# Determines URL of better thumb for a video +# def getBetterThumb(url): if baseUrl+"/ard/static/pics/default/16_9/default" in url: url = defaultThumb @@ -180,7 +158,90 @@ def getBetterThumb(url): id = str(id) url = baseUrl+"/ard/servlet/contentblob/"+newID[0:2]+"/"+newID[2:4]+"/"+newID[4:6]+"/"+newID[6:8]+"/"+id+"/bild/1" return url + + +# +# Iterates over a web page and identifies all listed content elements (e.g. video/audio) +# +def iterateContent(url, videoOnly, nextPageAction, callbackForVideo, separator='
'): + print("list content for " +url) + content = getUrl(url) + # find relevant parts of web page + spl = content.split(separator) + for i in range(1, len(spl), 1): + entry = spl[i] + useEntry = True; + if videoOnly: + # there are also "mt-icon_audio" entries, which does not contain the following marker + if "mt-icon_video" not in entry: + useEntry = False + if useEntry: + title, streamurl, thumb, duration, channel, show, desc, date = extractVideoDescription(entry) + # was analysis successful? + if title and streamurl: + callbackForVideo(title, streamurl, thumb, duration, channel, show, desc, date) + else: + print("Ignoring entry without title and URL.") + # have a look for some "next page" indicator + if nextPageAction: + # searching for something like + # 'Weiter' + match = re.compile(']*rel="[0-9]+"[^<>]*>(.+?)', re.DOTALL).findall(content) + for url, title in match: + if title == "Weiter": + addDir(translation(30009), baseUrl+url, nextPageAction, "", "") + # end list + xbmcplugin.endOfDirectory(pluginhandle) + if forceViewMode: + xbmc.executebuiltin('Container.SetViewMode('+viewMode+')') + + +# +# Analyzes the content of a web page and try to extract important information about a video +# Returns multiple variables (equal to empty string if not found in the web page): +# 1. title +# 2. pageurl +# 3. thumb +# 4. duration +# 5. channel +# 6. show +# 7. desc +# 8. date +# +def extractVideoDescription(entry): + # init result + title = pageurl = thumb = duration = channel = show = desc = date = "" + # base information + match = re.compile('(.+?)', re.DOTALL).findall(entry) + if match: + pageurl = baseUrl+match[0][0] + title = cleanTitle(match[0][2]) + # show + match = re.compile('

aus: (.+?)

', re.DOTALL).findall(entry) + show = "" + if match: + show = match[0] + # channel + match = re.compile('(.+?)', re.DOTALL).findall(entry) + channel = "" + if match: + channel = match[0] + # duration and date + match = re.compile('\n (.+?)\n (.+?) min\n ', re.DOTALL).findall(entry) + if match: + date = match[0][0] + duration = match[0][1] + if "00:" in duration: + # XBMC will round this duration to zero. Thus, we set it to at least one minute + # (do not return an int, because it is normally a string) + duration = "01:00 min" + # thumbs + match = re.compile('src="(.+?)"', re.DOTALL).findall(entry) + thumb = getBetterThumb(baseUrl+match[0]) + + return title, pageurl, thumb, duration, channel, show, desc, date + def listCats(): content = getUrl(baseUrl) @@ -201,79 +262,11 @@ def listVideosMain(id): def listVideos(url): - content = getUrl(url) - spl = content.split('
') - for i in range(1, len(spl), 1): - entry = spl[i] - if "mt-icon_video" in entry: - match = re.compile('(.+?)', re.DOTALL).findall(entry) - url = baseUrl+match[0][0] - title = cleanTitle(match[0][2]) - match = re.compile('

aus: (.+?)

', re.DOTALL).findall(entry) - show = "" - if match: - show = match[0] - match = re.compile('(.+?)', re.DOTALL).findall(entry) - channel = "" - if match: - channel = match[0] - match = re.compile('\n (.+?)\n (.+?) min\n ', re.DOTALL).findall(entry) - duration = "" - date = "" - if match: - date = match[0][0] - duration = match[0][1] - title = date[:5]+" - "+title - if "00:" in duration: - duration = 1 - match = re.compile('src="(.+?)"', re.DOTALL).findall(entry) - thumb = getBetterThumb(baseUrl+match[0]) - desc = cleanTitle(date+" - "+show+" ("+channel+")") - if "Livestream" not in title: - addLink(title, url, 'playVideo', thumb, duration, desc) - xbmcplugin.endOfDirectory(pluginhandle) - if forceViewMode: - xbmc.executebuiltin('Container.SetViewMode('+viewMode+')') + iterateContent(url, True, None, createVideoEntry) def listVideosDossier(url): - content = getUrl(url) - spl = content.split('
') - for i in range(1, len(spl), 1): - entry = spl[i] - if 'class="mt-fo_source"' in entry: - match = re.compile('(.+?)', re.DOTALL).findall(entry) - url = baseUrl+match[0][0] - title = cleanTitle(match[0][2]) - match = re.compile('

aus: (.+?)

', re.DOTALL).findall(entry) - show = "" - if match: - show = match[0] - match = re.compile('(.+?)', re.DOTALL).findall(entry) - channel = "" - if match: - channel = match[0] - match = re.compile('\n (.+?)\n (.+?) min\n ', re.DOTALL).findall(entry) - duration = "" - date = "" - if match: - date = match[0][0] - duration = match[0][1] - title = date[:5]+" - "+title - if "00:" in duration: - duration = 1 - match = re.compile('src="(.+?)"', re.DOTALL).findall(entry) - thumb = getBetterThumb(baseUrl+match[0]) - desc = cleanTitle(date+" - "+show+" ("+channel+")") - if "Livestream" not in title: - addLink(title, url, 'playVideo', thumb, duration, desc) - match = re.compile('(.+?)', re.DOTALL).findall(content) - for url, title in match: - if title == "Weiter": - addDir(translation(30009), baseUrl+url, 'listVideosDossier', "", "") - xbmcplugin.endOfDirectory(pluginhandle) - if forceViewMode: - xbmc.executebuiltin('Container.SetViewMode('+viewMode+')') + iterateContent(url, False, 'listVideosDossier', createVideoEntry) # @@ -454,40 +447,7 @@ def search(): def listVideosSearch(url): - content = getUrl(url) - spl = content.split('
') - for i in range(1, len(spl), 1): - entry = spl[i] - match = re.compile('(.+?)', re.DOTALL).findall(entry) - url = baseUrl+match[0][0] - title = cleanTitle(match[0][2]) - match = re.compile('

aus: (.+?)

', re.DOTALL).findall(entry) - show = "" - if match: - show = match[0] - match = re.compile('(.+?)', re.DOTALL).findall(entry) - channel = "" - if match: - channel = match[0] - match = re.compile('(.+?) · (.+?) min', re.DOTALL).findall(entry) - duration = "" - date = "" - if match: - date = match[0][0] - duration = match[0][1] - title = date[:5]+" - "+title - match = re.compile('src="(.+?)"', re.DOTALL).findall(entry) - thumb = getBetterThumb(baseUrl+match[0]) - desc = cleanTitle(date+" - "+show+" ("+channel+")") - if "Livestream" not in title: - addLink(title, url, 'playVideo', thumb, duration, desc) - match = re.compile('(.+?)', re.DOTALL).findall(content) - for url, title in match: - if title == "Weiter": - addDir(translation(30009), baseUrl+url.replace("&", "&"), 'listVideosSearch', "", "") - xbmcplugin.endOfDirectory(pluginhandle) - if forceViewMode == True: - xbmc.executebuiltin('Container.SetViewMode('+viewMode+')') + iterateContent(url, False, 'listVideosSearch', createVideoEntry, separator='
') def cleanTitle(title): @@ -552,6 +512,21 @@ def parameters_string_to_dict(parameters): paramDict[paramSplits[0]] = paramSplits[1] return paramDict +# +# Callback per video for list function +# +def createVideoEntry(title, pageurl, thumb, duration, channel, show, desc, date): + # error handling for web page without any information + if not title: + title = "?" + if "Livestream" not in title: + if not desc: + desc = cleanTitle(date+" - "+show+" ("+channel+")") + if date: + # add date to title + title = date[:5]+" - "+title + addLink(title, pageurl, 'playVideo', thumb, duration) + def addLink(name, url, mode, iconimage, duration="", desc=""): u = sys.argv[0]+"?url="+urllib.quote_plus(url)+"&mode="+str(mode) From 5aa97566f52a7dc945030e9ab7efe0b396ded75b Mon Sep 17 00:00:00 2001 From: Florian Liers Date: Wed, 15 Jan 2014 15:37:08 +0100 Subject: [PATCH 06/11] Fixed issues determining date and duration (fixes #2) --- default.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/default.py b/default.py index 76d6242..feea24a 100644 --- a/default.py +++ b/default.py @@ -228,7 +228,8 @@ def extractVideoDescription(entry): if match: channel = match[0] # duration and date - match = re.compile('\n (.+?)\n (.+?) min\n ', re.DOTALL).findall(entry) + # (encoded as "15.01.14 16:46 min" or in the search results as "15.01.14 - 16:46 min" + match = re.compile('([0-9\.]+)[^0-9]*([0-9:]+ min)?', re.DOTALL).findall(entry) if match: date = match[0][0] duration = match[0][1] @@ -524,7 +525,7 @@ def createVideoEntry(title, pageurl, thumb, duration, channel, show, desc, date) desc = cleanTitle(date+" - "+show+" ("+channel+")") if date: # add date to title - title = date[:5]+" - "+title + title = date[:6]+" - "+title addLink(title, pageurl, 'playVideo', thumb, duration) From b85f7161d9440939f4e4a92a994cdc1a10ae3cef Mon Sep 17 00:00:00 2001 From: Florian Liers Date: Wed, 15 Jan 2014 15:45:55 +0100 Subject: [PATCH 07/11] Fixed misleading name of a variable --- default.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/default.py b/default.py index feea24a..03e09f2 100644 --- a/default.py +++ b/default.py @@ -176,10 +176,10 @@ def iterateContent(url, videoOnly, nextPageAction, callbackForVideo, separator=' if "mt-icon_video" not in entry: useEntry = False if useEntry: - title, streamurl, thumb, duration, channel, show, desc, date = extractVideoDescription(entry) + title, pageurl, thumb, duration, channel, show, desc, date = extractVideoDescription(entry) # was analysis successful? - if title and streamurl: - callbackForVideo(title, streamurl, thumb, duration, channel, show, desc, date) + if title and pageurl: + callbackForVideo(title, pageurl, thumb, duration, channel, show, desc, date) else: print("Ignoring entry without title and URL.") From 6492bb41d7bc6351d28d47c942c19230f1897c6c Mon Sep 17 00:00:00 2001 From: Florian Liers Date: Wed, 15 Jan 2014 15:58:15 +0100 Subject: [PATCH 08/11] Removed long error message Very long error messages (e.g., content of whole web page) are not corretly displayed in XMBC 12.2. Anyhow, no user wants to read them. Thus, it just displays a short message of the URL of the faulty web page. --- default.py | 14 +++++++------- resources/language/English/strings.xml | 2 +- resources/language/German/strings.xml | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/default.py b/default.py index 03e09f2..03b0f82 100644 --- a/default.py +++ b/default.py @@ -164,7 +164,7 @@ def getBetterThumb(url): # Iterates over a web page and identifies all listed content elements (e.g. video/audio) # def iterateContent(url, videoOnly, nextPageAction, callbackForVideo, separator='
'): - print("list content for " +url) + #print("list content for " +url) content = getUrl(url) # find relevant parts of web page spl = content.split(separator) @@ -179,6 +179,7 @@ def iterateContent(url, videoOnly, nextPageAction, callbackForVideo, separator=' title, pageurl, thumb, duration, channel, show, desc, date = extractVideoDescription(entry) # was analysis successful? if title and pageurl: + #print("Content entry: " +title +" (" +pageurl +") " +duration +" - " +channel +" - " +show +" - descr:" +desc +" - date:" +date) callbackForVideo(title, pageurl, thumb, duration, channel, show, desc, date) else: print("Ignoring entry without title and URL.") @@ -284,10 +285,7 @@ def extractStreamURL(url): matchFSK = re.compile('
(.+?)
', re.DOTALL).findall(content) if matchFSK: fsk = matchFSK[0].strip() - if fsk: - return None, fsk - else: - return None, content + return None, fsk else: match5 = re.compile('addMediaStream\\(1, 2, "", "(.+?)"', re.DOTALL).findall(content) match6 = re.compile('addMediaStream\\(1, 1, "", "(.+?)"', re.DOTALL).findall(content) @@ -320,7 +318,7 @@ def extractStreamURL(url): url = url[:url.find("?")] return url, None else: - return None, content + return None, None # @@ -328,13 +326,15 @@ def extractStreamURL(url): # def playVideo(url): streamURL, errorMsg = extractStreamURL(url) - print("Playing " +streamURL +" (web page = " +url +")") if streamURL: + print("Playing " +streamURL +" (web page = " +url +")") listitem = xbmcgui.ListItem(path=streamURL) xbmcplugin.setResolvedUrl(pluginhandle, True, listitem) if showSubtitles and matchUT: setSubtitle(baseUrl+matchUT[0]) else: + if not errorMsg: + errorMsg = url; reportError(translation(30200), translation(30202) +" " +errorMsg) diff --git a/resources/language/English/strings.xml b/resources/language/English/strings.xml index b66c3ea..4b7356e 100644 --- a/resources/language/English/strings.xml +++ b/resources/language/English/strings.xml @@ -32,6 +32,6 @@ Error Plug-in SimpleDownloader required for downloading not found. Maybe it has to be installed first. - Stream URL can not be determined. Web page: + Stream URL can not be determined. Error: Destination folder not defined. Please define it in the configuration section of this plug-in. diff --git a/resources/language/German/strings.xml b/resources/language/German/strings.xml index 09c8ba1..c018668 100644 --- a/resources/language/German/strings.xml +++ b/resources/language/German/strings.xml @@ -31,6 +31,6 @@ Fehler Zum Herunterladen notwendiges Plugin SimpleDownloader nicht gefunden. Es muss möglicherweise erst installiert werden. - Stream-URL kann nicht bestimmt werden. Webseite: + Stream-URL kann nicht bestimmt werden. Fehler: Zielverzeichnis ist nicht definiert. Bitte Konfiguration des Plug-ins anpassen. From 0ca1f2394f076aae8afb0ea62b812516ffcded85 Mon Sep 17 00:00:00 2001 From: Florian Liers Date: Thu, 16 Jan 2014 10:47:25 +0100 Subject: [PATCH 09/11] Added configuration for parts of video title (fixes #5) --- default.py | 13 +++++++------ resources/language/English/strings.xml | 4 +++- resources/language/German/strings.xml | 2 ++ resources/settings.xml | 2 ++ 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/default.py b/default.py index 03b0f82..efe3bcd 100644 --- a/default.py +++ b/default.py @@ -182,6 +182,7 @@ def iterateContent(url, videoOnly, nextPageAction, callbackForVideo, separator=' #print("Content entry: " +title +" (" +pageurl +") " +duration +" - " +channel +" - " +show +" - descr:" +desc +" - date:" +date) callbackForVideo(title, pageurl, thumb, duration, channel, show, desc, date) else: + # error handling for web page without any information print("Ignoring entry without title and URL.") # have a look for some "next page" indicator @@ -515,18 +516,18 @@ def parameters_string_to_dict(parameters): # # Callback per video for list function +# (at least title and pageurl have to be valid) # def createVideoEntry(title, pageurl, thumb, duration, channel, show, desc, date): - # error handling for web page without any information - if not title: - title = "?" if "Livestream" not in title: if not desc: desc = cleanTitle(date+" - "+show+" ("+channel+")") - if date: + if show and addon.getSetting("showSeriesInTitle") == "true": + title = show +": " +title + if date and addon.getSetting("showDateInTitle") == "true": # add date to title - title = date[:6]+" - "+title - addLink(title, pageurl, 'playVideo', thumb, duration) + title = date[:6]+" "+title + addLink(cleanTitle(title), pageurl, 'playVideo', thumb, duration, desc) def addLink(name, url, mode, iconimage, duration="", desc=""): diff --git a/resources/language/English/strings.xml b/resources/language/English/strings.xml index 4b7356e..476a876 100644 --- a/resources/language/English/strings.xml +++ b/resources/language/English/strings.xml @@ -29,7 +29,9 @@ Use thumb as fanart Activate subtitles (if available) Destination folder for downloaded videos - + Show date in video title + Show name of series in video title + Error Plug-in SimpleDownloader required for downloading not found. Maybe it has to be installed first. Stream URL can not be determined. Error: diff --git a/resources/language/German/strings.xml b/resources/language/German/strings.xml index c018668..7f5d2d1 100644 --- a/resources/language/German/strings.xml +++ b/resources/language/German/strings.xml @@ -28,6 +28,8 @@ Thumb als Fanart nutzen Untertitel aktivieren (falls verfügbar) Zielordner für heruntergeladene Videos + Zeige Datum im Videotitel + Zeige Serienname im Videotitel Fehler Zum Herunterladen notwendiges Plugin SimpleDownloader nicht gefunden. Es muss möglicherweise erst installiert werden. diff --git a/resources/settings.xml b/resources/settings.xml index 6837ae7..f2e1445 100644 --- a/resources/settings.xml +++ b/resources/settings.xml @@ -4,4 +4,6 @@ + + From 852bb6a95a322dd5d3fd47ca8e92530f13077cc8 Mon Sep 17 00:00:00 2001 From: Florian Liers Date: Thu, 16 Jan 2014 10:56:26 +0100 Subject: [PATCH 10/11] Avoiding some XBMC warnings regarding the video duration format --- default.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/default.py b/default.py index efe3bcd..a887f1e 100644 --- a/default.py +++ b/default.py @@ -235,10 +235,11 @@ def extractVideoDescription(entry): if match: date = match[0][0] duration = match[0][1] + duration = duration.replace("min", "").strip() if "00:" in duration: # XBMC will round this duration to zero. Thus, we set it to at least one minute # (do not return an int, because it is normally a string) - duration = "01:00 min" + duration = "01:00" # thumbs match = re.compile('src="(.+?)"', re.DOTALL).findall(entry) thumb = getBetterThumb(baseUrl+match[0]) @@ -534,7 +535,12 @@ def addLink(name, url, mode, iconimage, duration="", desc=""): u = sys.argv[0]+"?url="+urllib.quote_plus(url)+"&mode="+str(mode) ok = True liz = xbmcgui.ListItem(name, iconImage=defaultThumb, thumbnailImage=iconimage) - liz.setInfo(type="Video", infoLabels={"Title": name, "Duration": duration, "Plot": desc}) + infos = {"Title": name} + if duration: + infos["Duration"] = duration + if desc: + infos["Plot"] = desc + liz.setInfo(type="Video", infoLabels=infos) liz.setProperty('IsPlayable', 'true') if useThumbAsFanart: if not iconimage or iconimage==icon: From 37baccbac417ed8df7cc5867cf0f57154ba24910 Mon Sep 17 00:00:00 2001 From: Florian Liers Date: Sun, 26 Jan 2014 13:40:37 +0100 Subject: [PATCH 11/11] Added configuration flag for download feature Disabled by default due to unclear legal issues. See discussion in #1. --- default.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/default.py b/default.py index a887f1e..03fcb99 100644 --- a/default.py +++ b/default.py @@ -21,6 +21,7 @@ viewMode = str(addon.getSetting("viewMode")) errorMessageDurationSec = 20 characterEncoding = "iso-8859-15" +configEnableDownloadFeature = False baseUrl = "http://www.ardmediathek.de" defaultThumb = baseUrl+"/ard/static/pics/default/16_9/default_webM_16_9.jpg" @@ -549,7 +550,9 @@ def addLink(name, url, mode, iconimage, duration="", desc=""): else: liz.setProperty("fanart_image", defaultBackground) liz.addContextMenuItems([(translation(30012), 'RunPlugin(plugin://'+addonID+'/?mode=queueVideo&url='+urllib.quote_plus(u)+'&name='+urllib.quote_plus(name)+')',)]) - liz.addContextMenuItems([(translation(30040), 'RunPlugin(plugin://'+addonID+'/?mode=downloadVideo&url='+urllib.quote_plus(url)+'&name='+urllib.quote_plus(name)+')',)]) + if configEnableDownloadFeature: + liz.addContextMenuItems([(translation(30040), 'RunPlugin(plugin://'+addonID+'/?mode=downloadVideo&url='+urllib.quote_plus(url)+'&name='+urllib.quote_plus(name)+')',)]) + # else: downloading is not allowed ok = xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=u, listitem=liz) return ok