From 31a07ccca5a8b2d5a38bc20069b2d885a7355bda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dagur=20P=C3=A1ll=20Ammendrup?= Date: Wed, 8 Oct 2014 20:28:31 +0000 Subject: [PATCH] Still learning to use git --- plugin.video.sarpur/addon.xml | 4 +- plugin.video.sarpur/changelog.txt | 5 + plugin.video.sarpur/default.py | 11 +- plugin.video.sarpur/sarpur/__init__.py | 3 +- plugin.video.sarpur/sarpur/actions.py | 116 ++++++++++--------- plugin.video.sarpur/sarpur/logger.py | 7 ++ plugin.video.sarpur/sarpur/scraper.py | 150 +++++++++++-------------- plugin.video.sarpur/util/gui.py | 53 ++++++++- plugin.video.sarpur/util/player.py | 21 +++- 9 files changed, 215 insertions(+), 155 deletions(-) diff --git a/plugin.video.sarpur/addon.xml b/plugin.video.sarpur/addon.xml index 2f5e52f..0767fde 100644 --- a/plugin.video.sarpur/addon.xml +++ b/plugin.video.sarpur/addon.xml @@ -1,11 +1,11 @@ - + diff --git a/plugin.video.sarpur/changelog.txt b/plugin.video.sarpur/changelog.txt index c8823dc..29089c9 100644 --- a/plugin.video.sarpur/changelog.txt +++ b/plugin.video.sarpur/changelog.txt @@ -1,3 +1,8 @@ +[B]Version 3.2.0[/B] +- Better cross platform support (switched to BeautifulSoup 4 from html5lib from lxml) +- Removed the show index (since RÚV removed the recordings). (Added groups instead) +- Last Frodo release. Added Gotham support. + [B]Version 3.1.3[/B] - Added requests to imports diff --git a/plugin.video.sarpur/default.py b/plugin.video.sarpur/default.py index e937394..38d71a3 100644 --- a/plugin.video.sarpur/default.py +++ b/plugin.video.sarpur/default.py @@ -19,17 +19,10 @@ try: if action_key is None: actions.index() - elif action_key == 'view_channel_index': - actions.channel_index(int(action_value)) - elif action_key == 'view_channel_category': - channel, category = action_value.split(';') - actions.channel_category(int(channel), int(category)) - elif action_key == 'view_channel_category_show': - actions.channel_category_show(action_value, name) + elif action_key == 'view_group': + actions.view_group(action_value) elif action_key == 'play': actions.play_video(action_value, name) - elif action_key == 'view_tab': - actions.tab_index(action_value) elif action_key == 'view_podcast_index': actions.podcast_index() elif action_key == 'view_podcast_show': diff --git a/plugin.video.sarpur/sarpur/__init__.py b/plugin.video.sarpur/sarpur/__init__.py index c331c7a..b543adf 100644 --- a/plugin.video.sarpur/sarpur/__init__.py +++ b/plugin.video.sarpur/sarpur/__init__.py @@ -4,7 +4,7 @@ import sys import xbmcaddon -ALWAYS_REFRESH = True +ALWAYS_REFRESH = False #True LOGGING_ENABLED = True BASE_URL = sys.argv[0] @@ -18,3 +18,4 @@ 'rondo': 'http://sip-live.hds.adaptive.level3.net/hls-live/ruv-ras3/_definst_/live.m3u8' } +PODCAST_URL = 'http://www.ruv.is/hladvarp' diff --git a/plugin.video.sarpur/sarpur/actions.py b/plugin.video.sarpur/sarpur/actions.py index 993a443..7325953 100644 --- a/plugin.video.sarpur/sarpur/actions.py +++ b/plugin.video.sarpur/sarpur/actions.py @@ -5,89 +5,99 @@ import sarpur import sarpur.scraper as scraper import util.player as player -from sarpur.cached import Categories from util.gui import GUI INTERFACE = GUI(sarpur.ADDON_HANDLE, sarpur.BASE_URL) -CATS = Categories() def index(): """ + .. py:function:: index() + The front page (i.e. the first one the user sees when opening the plugin) """ - for tab in CATS.tabs: - title = tab[0].encode('utf-8') - url = tab[1].encode('utf-8') - INTERFACE.addDir(title, 'view_tab', url) - - for i, channel in enumerate(CATS.showtree): - title = '{0} Þættir'.format(channel['name'].encode('utf-8')) - INTERFACE.addDir(title, 'view_channel_index', i) - + INTERFACE.addItem('Bein útsending RÚV', 'play_live', 'ruv') + INTERFACE.addItem('Bein útsending RÁS 1', 'play_live', 'ras1') + INTERFACE.addItem('Bein útsending RÁS 2', 'play_live', 'ras2') + INTERFACE.addItem('Bein útsending Rondó', 'play_live', 'rondo') + INTERFACE.addDir('Nýtt', 'view_group', '/') + INTERFACE.addDir('Fréttir', 'view_group', '/flokkar/frettir') + INTERFACE.addDir('Íþróttir', 'view_group', '/flokkar/ithrottir') + INTERFACE.addDir('Barnaefni', 'view_group', '/flokkar/born') + INTERFACE.addDir('Rás 1', 'view_group', '/ras1') + INTERFACE.addDir('Rás 2', 'view_group', '/ras2') + INTERFACE.addDir('Sjónvarpsefni', 'view_group', '/flokkar/1') + INTERFACE.addDir('Íþróttarás', 'view_group', '/flokkar/10') INTERFACE.addDir('Hlaðvarp', 'view_podcast_index', '') - INTERFACE.addItem('Bein úttsending: RÚV', 'play_live', 'ruv') - INTERFACE.addItem('Bein úttsending: RÁS 1', 'play_live', 'ras1') - INTERFACE.addItem('Bein úttsending: RÁS 2', 'play_live', 'ras2') - INTERFACE.addItem('Bein úttsending: RONDÓ', 'play_live', 'rondo') - -def channel_index(channel): - for i, category in enumerate(CATS.showtree[channel].get('categories')): - INTERFACE.addDir(category['name'].encode('utf-8'), - 'view_channel_category', - "{0};{1}".format(channel, i)) - -def channel_category(channel, category): - for show in CATS.showtree[channel]['categories'][category]['shows']: - name, url = show - if url[0] == '/': - url = 'http://dagskra.ruv.is%s' % url - INTERFACE.addDir(name.encode('utf-8'), - 'view_channel_category_show', - url.encode('utf-8')) - -def channel_category_show(url, show_name): - episodes = scraper.get_episodes(url) - if not episodes: - INTERFACE.infobox("Engar upptökur", "Engin upptaka fannst") - else: - for episode in episodes: - episode_name, url = episode - name = "{0} - {1}".format(show_name, episode_name.encode('utf-8')) - INTERFACE.addItem(name, 'play', url) def play_video(url, name): + """ + .. py:function:: play_video(url, name) + + Plays videos (and audio) other than live streams and podcasts. + + :param url: The page url + :param name: The name of the item to play + """ (playpath, rtmp_url, swfplayer) = scraper.get_stream_info(url) player.play_stream(playpath, swfplayer, rtmp_url, url, name) def play_podcast(url): + """ + .. py:function:: play_podcast(url) + + Plays podcast + + :param url: The file url (this can be any file that xbmc can play) + """ + player.play(url) def play_live_stream(name): - #url = scraper.get_live_url(name) + """ + .. py:function:: play_live_stream(name) + + Play one of the live streams. + + :param name: The name of the stream (defined in LIVE_URLS in __init__.py) + """ url = sarpur.LIVE_URLS.get(name) player.play(url) -def tab_index(url): - pageurl = 'http://www.ruv.is{0}'.format(url) +def view_group(rel_url): + """ + .. py:function:: view_group(rel_url) - episodes = scraper.get_tab_items(pageurl) - if not episodes: - INTERFACE.infobox("Engar upptökur", "Engin upptaka fannst") - else: - for episode in episodes: - episode_name, url = episode - INTERFACE.addItem(episode_name.encode('utf-8'), - 'play', - url.encode('utf-8')) + List items on one of the groups (flokkur tab) on sarpurinn. + + :param rel_url: Relative url to the flokkur + + """ + full_url = 'http://www.ruv.is/sarpurinn{0}'.format(rel_url) + for video in scraper.get_videos(full_url): + name, url = video + INTERFACE.addItem(name.encode('utf-8'), 'play', url.encode('utf-8')) def podcast_index(): - for show in scraper.get_podcast_shows(): + """ + .. py:function:: podcast_index() + + List all the podcasts. + """ + for show in scraper.get_podcast_shows(sarpur.PODCAST_URL): name, url = show INTERFACE.addDir(name.encode('utf-8'), 'view_podcast_show', url) def podcast_show(url, name): + """ + .. py:function:: podcast_show(url, name) + + List all the recordings for a podcast show. + + :param url: The podcast url (xml file) + :param name: The name of the show + """ for recording in scraper.get_podcast_episodes(url): date, url = recording title = "{0} - {1}".format(name, date.encode('utf-8')) diff --git a/plugin.video.sarpur/sarpur/logger.py b/plugin.video.sarpur/sarpur/logger.py index 48ed2c1..52d49cd 100644 --- a/plugin.video.sarpur/sarpur/logger.py +++ b/plugin.video.sarpur/sarpur/logger.py @@ -5,5 +5,12 @@ import sarpur def log(message): + """ + .. py:function:: log(message) + + This is the preferred way to log things in XBMC (rahter than using print) + + :param message to log + """ if sarpur.LOGGING_ENABLED: xbmc.log(message) diff --git a/plugin.video.sarpur/sarpur/scraper.py b/plugin.video.sarpur/sarpur/scraper.py index 334ca6e..417b2c9 100644 --- a/plugin.video.sarpur/sarpur/scraper.py +++ b/plugin.video.sarpur/sarpur/scraper.py @@ -7,143 +7,119 @@ def get_document(url): - req = requests.get(url) - doc = BeautifulSoup(req.content) - - return doc - -def get_episodes(url): - "Find playable files on a shows page" - episodes = [] - doc = get_document(url) - - #Generic look - for episode in doc.findall(".//a[contains(@title, 'Spila')]"): - episodes.append((episode.text, episode.get('href'))) - - if episodes: - return episodes + """ + .. py:function:: get_document(url) - #"Special" page - for episode in doc.findall(".//div[contains(@class,'mm-mynd')]"): - episode_date = episode.getparent().find('span').text - url = u'http://www.ruv.is{0}'.format(episode.find('a').attrib.get('href')) - episodes.append((episode_date, url)) + Downloads url and returns a BeautifulSoup object - return episodes - -def get_tabs(): - doc = get_document("http://www.ruv.is/sarpurinn") - findallstring = ".//div[@class='menu-block-ctools-menu-sarpsmynd-1 menu-name-menu-sarpsmynd parent-mlid-_active:0 menu-level-2']/ul/li/a" - tabs = [] - - for hyperlink in doc.findall(findallstring): - tabs.append((hyperlink.text, hyperlink.get('href'))) - - return tabs - -def get_showtree(): - doc = get_document('http://dagskra.ruv.is/thaettir/') - showtree = [] + :param url: An url + :return BeautifulSoup "document" + """ + req = requests.get(url) + doc = BeautifulSoup(req.content, "html.parser") + return doc - #.//div[@style] - for i, channel in enumerate(doc.findall("div", style=True)): - channel_name = channel.find("h1").text - showtree.append({"name": channel_name, "categories": []}) +def get_stream_info(page_url): + """ + .. py:function:: get_stream_info(page_url) - for group in channel.find("div"): - if group.tag == 'h2': - showtree[i]["categories"].append( - {"name":group.text, "shows":[]}) - elif group.tag == 'div': - for hyperlink in group.findall("div/a"): - show_info = (hyperlink.text, hyperlink.get('href')) - showtree[i]["categories"][-1]['shows'].append(show_info) - return showtree + Get a page url and finds the url of the rtmp stream -def get_stream_info(page_url): - "Get a page url and finds the url of the rtmp stream" + :param page_url: An url to a page with a media player + :return a 3-tuple of paths useful for playing videos + """ doc = get_document(page_url) #Get urls for the swf player and playpath - params = doc.findall('.//param') + params = doc.find_all('param') swfplayer = 'http://ruv.is{0}'.format(params[0].get('value')) details = params[1].get('value') playpath = re.search('streamer=(.*?)&(file=.*?)&stre', details).group(2) # Get the url of the actual rtmp stream - source_tags = doc.findall('.//source') - if source_tags and source_tags[0].attrib.get('src'): #RÚV + source_tags = doc.find_all('source') + if source_tags and source_tags[0].get('src'): #RÚV rtmp_url = source_tags[0].get('src') else: #RÁS 1 & 2 # The ip address of the stream server is returned in another page - cache_url = doc.findall(".//script[contains(@src, 'load.cache.is')]")[0].get('src') + cache_url = doc.select('script[src*="load.cache.is"]')[0].get('src') res = requests.get(cache_url) cache_ip = re.search('"([^"]+)"', res.content).group(1) # Now that we have the ip address we can insert it into the URL - source_js = doc.findall(".//script[contains(., 'tengipunktur')]")[0].text + source_js = doc.find('script', text=re.compile(r'tengipunktur')).text source_url = re.search(r"'file': '(http://' \+ tengipunktur \+ '[^']+)", source_js).group(1) rtmp_url = source_url.replace("' + tengipunktur + '", cache_ip) return (playpath, rtmp_url, swfplayer) -def get_tab_items(url): - "Find playable items on the 'recent' pages" +def get_videos(url): + """ + .. py:function:: get_videos(url) + + Find playable items in a group (like fréttir or barnaefni) + + :param url: The url to the group + :return A list of of 2-tuples with item name and page url + """ doc = get_document(url) episodes = [] #Every tab has a player with the newest/featured item. Get the name of it. - featured_item = doc.findall(".//div[@class='kubbur sarpefst']/div/h1") + featured_item = doc.select('.sarpefst div h1') if featured_item: featured_name = featured_item[0].text - episodes.append((featured_name, url)) + featured_date = doc.select('.sarpur-date')[0].text.split(' | ')[0] + title = u"{0} - {1}".format(featured_name, featured_date) + episodes.append((title, url)) #Get the listed items - for item in doc.findall(".//ul[@class='sarplisti']/li"): - item_link = item.findall("a")[0].attrib - item_date = item.findall("em")[0].text + for item in doc.select('.sarplisti li'): + item_link = item.find_all("a")[0] + item_date = item.find_all("em")[0].text page_url = u"http://www.ruv.is{0}".format(item_link['href']) title = u"{0} - {1}".format(item_link.get('title'), item_date) episodes.append((title, page_url)) return episodes -def get_podcast_shows(): - """Gets the names and rss urls of all the Podcasts""" - doc = get_document("http://www.ruv.is/podcast") +def get_podcast_shows(url): + """ + .. py:function:: get_podcast_shows(url) + + Gets the names and rss urls of all the podcasts (shows) + + :param url: The url to the podcast index + :return A list of 2-tuples with show name and rss url + + """ + doc = get_document(url) shows = [] - for show in doc.findall(".//ul[@class='hladvarp-info']"): - title = show.findall('li/h4')[0].text - url = show.findall("li/a[contains(@href,'http')]")[0].attrib.get('href') - shows.append((title, url)) + for show in doc.select("ul .hladvarp-info"): + title = show.select('li h4')[0].text + show_url = show.select("li a[href*=http]")[0].get('href') + shows.append((title, show_url)) return shows def get_podcast_episodes(url): - """Gets the items from the rss feed""" + """ + .. py:function:: get_podcast_episodes(url) + + Gets the items from the rss feed + + :param url: Get all the playable items in podcast rss + :return a list of 2-tuples with airdate and media url + + """ doc = get_document(url) episodes = [] - for item in doc.findall(".//guid"): + for item in doc.find_all("guid"): url = item.text - for element in item.itersiblings(): - if element.tag == 'pubdate': - date = element.text - - #date = item.findall('pubdate')[0].text - #url = item.findall('guid')[0].text + date = item.select('~ pubdate')[0].text episodes.append((date, url)) return episodes - -def get_live_url(channel='ruv'): - page_urls = { - 'ruv': "http://ruv.is/ruv" - } - - doc = get_document(page_urls.get(channel)) - return doc.findall(".//div[@id='spilarinn']/video/source")[0].attrib['src'] - diff --git a/plugin.video.sarpur/util/gui.py b/plugin.video.sarpur/util/gui.py index e2df3f6..1c548f5 100644 --- a/plugin.video.sarpur/util/gui.py +++ b/plugin.video.sarpur/util/gui.py @@ -5,11 +5,34 @@ from urllib import quote_plus as quote class GUI(object): + """ + A very simple class that wraps the interface functions in XBMC + """ def __init__(self, addon_handle, base_url): + """ + .. py_function:: __init__(self, addon_handle, base_url) + + :param addon_handle: An identifier that XBMC uses to identify the addon + (created in default.py) + :param base_url: The root internal url used in all calls in the addon + """ self.addon_handle = addon_handle self.base_url = base_url def _addDir(self, name, action_key, action_value, iconimage, is_folder): + """ + .. py_function:: _addDir(self, name, action_key, action_value, + iconimage, is_folder) + + Creates a link in xbmc. + + :param name: Name of the link + :param action_key: Name of the action to take when link selected + :param action_value: Parameter to use with the action + :param iconimage: Icon to use for the link + :param is_folder: Does the link lead to a folder or playable item + + """ formatparams = { "base_url": self.base_url, "key": quote(str(action_key)), @@ -32,6 +55,17 @@ def _addDir(self, name, action_key, action_value, iconimage, is_folder): def addDir(self, name, action_key, action_value, iconimage='DefaultFolder.png'): + """ + .. py_function:: addDir(self, name, action_key, action_value[, + iconimage='DefaultFolder.png']) + + Create folder (wrapper function for _addDir). + + :param name: The name of the folder + :param action_key: Action to take + :param action_value: Parameter to action + :iconimage: Image to use with the folder + """ self._addDir(name, action_key, action_value, @@ -40,6 +74,17 @@ def addDir(self, name, action_key, action_value, def addItem(self, name, action_key, action_value, iconimage='DefaultMovies.png'): + """ + .. py_function:: addItem(self, name, action_key, action_value, + iconimage='DefaultMovies.png') + + Create link to playable item (wrapper function for _addDir). + + :param name: The name of the folder + :param action_key: Action to take + :param action_value: Parameter to action + :iconimage: Image to use for the item + """ self._addDir(name, action_key, action_value, @@ -47,6 +92,12 @@ def addItem(self, name, action_key, action_value, is_folder=False) def infobox(self, title, message): - xbmcgui.Dialog().ok(title, message) + """ + .. py_function:: infobox(self, title, message) + Display a pop up message. + :param title: The title of the pop up window + :param message: Message you want to display to the user + """ + xbmcgui.Dialog().ok(title, message) diff --git a/plugin.video.sarpur/util/player.py b/plugin.video.sarpur/util/player.py index 1b0e14b..6b25b75 100644 --- a/plugin.video.sarpur/util/player.py +++ b/plugin.video.sarpur/util/player.py @@ -4,11 +4,28 @@ import xbmc, xbmcgui def play(url): - "Play audio or video on a given url" + """ + .. py:function:: play(url) + + Play audio or video on a given url" + + :param url: + """ + xbmc.Player().play(url) def play_stream(playpath, sfwplayer, rtmp_url, url, name): - "Play flash videos" + """ + .. py:function:: play_stream(playpath, sfwplayer, rtmp_url, url, name) + + Play flash videos in XBMC + + :param playpath: + :param sfwplayer: Path to the player on the page + :param rtmp_url: URL to the actual video + :param url: URL of the page where the flash player is + :param name: Name of the video + """ item = xbmcgui.ListItem(name) item.setProperty("PlayPath", playpath) item.setProperty("SWFPlayer", sfwplayer)