# -*- coding: utf-8 -*- import sys import os import re from operator import itemgetter from ipwww_common import translation, AddMenuEntry, OpenURL, \ CheckLogin, CreateBaseDirectory import xbmc import xbmcgui import xbmcplugin import xbmcaddon import random ADDON = xbmcaddon.Addon(id='plugin.video.iplayerwww') def GetPage(page_url, just_episodes=False): """ Generic Radio page scraper. """ pDialog = xbmcgui.DialogProgressBG() pDialog.create(translation(30319)) html = OpenURL(page_url) total_pages = 1 current_page = 1 page_range = range(1) paginate = re.search(r'',html) next_page = 1 if paginate: if int(ADDON.getSetting('radio_paginate_episodes')) == 0: current_page_match = re.search(r'page=(\d*)', page_url) if current_page_match: current_page = int(current_page_match.group(1)) page_range = range(current_page, current_page+1) next_page_match = re.search(r'
  • ', paginate.group(0)) if next_page_match: page_base_url = next_page_match.group(1) next_page = int(next_page_match.group(2)) else: next_page = current_page page_range = range(current_page, current_page+1) else: pages = re.findall(r'',paginate.group(0)) if pages: last = pages[-1] last_page = re.search(r' current_page: page_url = 'http://www.bbc.co.uk' + page_base_url + str(page) html = OpenURL(page_url) masthead_title = '' masthead_title_match = re.search(r'(.+?)', html) if masthead_title_match: masthead_title = masthead_title_match.group(1) list_item_num = 1 programmes = html.split('
    (.+?)', programme) if name_match: name = name_match.group(1) subtitle = '' subtitle_match = re.search(r'(.*?)(.*?property="name">(.*?))?', programme) if subtitle_match: series = subtitle_match.group(1) episode = subtitle_match.group(3) if episode: subtitle = "(%s, %s)" % (series, episode) else: if series.strip(): subtitle = "(%s)" % series image = '' image_match = re.search(r'', programme) if image_match: image = image_match.group(1) synopsis = '' synopsis_match = re.search(r'(.+?)', programme) if synopsis_match: synopsis = synopsis_match.group(1) station = '' station_match = re.search(r'

    >[/COLOR]" % translation(30320), page_url, 136, '', '', '') #BUG: this should sort by original order but it doesn't (see http://trac.kodi.tv/ticket/10252) xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_UNSORTED) xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_VIDEO_TITLE) pDialog.close() def GetEpisodes(url): new_url = 'http://www.bbc.co.uk/programmes/%s/episodes/player' % url GetPage(new_url,True) def AddAvailableLiveStreamItem(name, channelname, iconimage): """Play a live stream based on settings for preferred live source and bitrate.""" providers = [('ak', 'Akamai'), ('llnw', 'Limelight')] location_qualities = {'uk' : ['sbr_vlow', 'sbr_low', 'sbr_med', 'sbr_high'], 'nonuk': ['sbr_vlow', 'sbr_low'] } location_settings = ['uk', 'nonuk'] location = location_settings[int(ADDON.getSetting('radio_location'))] for provider_url, provider_name in providers: qualities = location_qualities[location] max_quality = int(ADDON.getSetting('radio_live_bitrate')) + 1 max_quality = min(len(qualities),max_quality) qualities = qualities[0:max_quality] qualities.reverse() for quality in qualities: url = 'http://a.files.bbci.co.uk/media/live/manifesto/audio/simulcast/hls/%s/%s/%s/%s.m3u8' % (location, quality, provider_url, channelname) PlayStream(name, url, iconimage, '', '') def AddAvailableLiveStreamsDirectory(name, channelname, iconimage): """Retrieves the available live streams for a channel Args: name: only used for displaying the channel. iconimage: only used for displaying the channel. channelname: determines which channel is queried. """ providers = [('ak', 'Akamai'), ('llnw', 'Limelight')] location_qualities = { 'uk' : ['sbr_vlow', 'sbr_low', 'sbr_med', 'sbr_high'], 'nonuk': ['sbr_vlow', 'sbr_low'] } location_names = {'uk': 'UK', 'nonuk': 'International'} quality_colours = { 'sbr_vlow': 'ffff0000', 'sbr_low': 'ffffa500', 'sbr_med': 'ffffff00', 'sbr_high': 'ff008000' } quality_bitrates = { 'sbr_vlow': '48', 'sbr_low': '96', 'sbr_med': '128', 'sbr_high': '320' } for location in location_qualities.keys(): qualities = location_qualities[location] qualities.reverse() for quality in qualities: for provider_url, provider_name in providers: url = 'http://a.files.bbci.co.uk/media/live/manifesto/audio/simulcast/hls/%s/%s/%s/%s.m3u8' % (location, quality, provider_url, channelname) title = name + ' - [I][COLOR %s]%s Kbps[/COLOR] [COLOR fff1f1f1]%s[/COLOR] [COLOR ffb4b4b4]%s[/COLOR][/I]' % ( quality_colours[quality], quality_bitrates[quality] , location_names[location], provider_name) AddMenuEntry(title, url, 201, '', '', '') def PlayStream(name, url, iconimage, description, subtitles_url): html = OpenURL(url) check_geo = re.search( '

    Access Denied

    ', html) if check_geo or not html: # print "Geoblock detected, raising error message" dialog = xbmcgui.Dialog() dialog.ok(translation(30400), translation(30401)) raise liz = xbmcgui.ListItem(name, iconImage='DefaultVideo.png', thumbnailImage=iconimage) liz.setInfo(type='Audio', infoLabels={'Title': name}) liz.setProperty("IsPlayable", "true") liz.setPath(url) xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, liz) def AddAvailableStreamsDirectory(name, stream_id, iconimage, description): """Will create one menu entry for each available stream of a particular stream_id""" streams = ParseStreams(stream_id) for supplier, bitrate, url, encoding in sorted(streams[0], key=itemgetter(1), reverse=True): bitrate = int(bitrate) if bitrate >= 320: color = 'ff008000' elif bitrate >= 128: color = 'ffffff00' elif bitrate >= 96: color = 'ffffa500' else: color = 'ffff0000' title = name + ' - [I][COLOR %s]%d Kbps %s[/COLOR] [COLOR ffd3d3d3]%s[/COLOR][/I]' % ( color, bitrate, encoding, supplier) AddMenuEntry(title, url, 201, iconimage, description, '', '') def AddAvailableStreamItem(name, url, iconimage, description): """Play a streamm based on settings for preferred catchup source and bitrate.""" stream_ids = ScrapeAvailableStreams(url) if len(stream_ids) < 1: #TODO check CBeeBies for special cases xbmcgui.Dialog().ok(translation(30403), translation(30404)) return streams_all = ParseStreams(stream_ids) streams = streams_all[0] source = int(ADDON.getSetting('radio_source')) if source > 0: # Case 1: Selected source match = [x for x in streams if (x[0] == source)] if len(match) == 0: # Fallback: Use any source and any bitrate match = streams match.sort(key=lambda x: x[1], reverse=True) else: # Case 3: Any source # Play highest available bitrate match = streams match.sort(key=lambda x: x[1], reverse=True) PlayStream(name, match[0][2], iconimage, description, '') def ListAtoZ(): """List programmes based on alphabetical order. Only creates the corresponding directories for each character. """ characters = [ ('A', 'a'), ('B', 'b'), ('C', 'c'), ('D', 'd'), ('E', 'e'), ('F', 'f'), ('G', 'g'), ('H', 'h'), ('I', 'i'), ('J', 'j'), ('K', 'k'), ('L', 'l'), ('M', 'm'), ('N', 'n'), ('O', 'o'), ('P', 'p'), ('Q', 'q'), ('R', 'r'), ('S', 's'), ('T', 't'), ('U', 'u'), ('V', 'v'), ('W', 'w'), ('X', 'x'), ('Y', 'y'), ('Z', 'z'), ('0-9', '@')] for name, url in characters: url = 'http://www.bbc.co.uk/radio/programmes/a-z/by/%s/current' % url AddMenuEntry(name, url, 136, '', '', '') def ListGenres(): """List programmes based on alphabetical order. Only creates the corresponding directories for each character. """ genres = [] html = OpenURL('http://www.bbc.co.uk/radio/programmes/genres') mains = html.split('
  • ') for main in mains: current_main_match = re.search(r'(.+?)',main) if current_main_match: genres.append((current_main_match.group(1), current_main_match.group(2), True)) current_sub_match = re.findall(r'(.+?)',main) for sub_match_url, sub_match_name in current_sub_match: genres.append((sub_match_url, current_main_match.group(2)+' - '+sub_match_name, False)) for url, name, group in genres: new_url = 'http://www.bbc.co.uk%s/player/episodes' % url if group: AddMenuEntry("[B]%s[/B]" % name, new_url, 136, '', '', '') else: AddMenuEntry("%s" % name, new_url, 136, '', '', '') #BUG: this should sort by original order but it doesn't (see http://trac.kodi.tv/ticket/10252) xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_UNSORTED) xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_VIDEO_TITLE) def ListLive(): channel_list = [ ('bbc_radio_one', 'BBC Radio 1'), ('bbc_1xtra', 'BBC Radio 1Xtra'), ('bbc_radio_two', 'BBC Radio 2'), ('bbc_radio_three', 'BBC Radio 3'), ('bbc_radio_fourfm', 'BBC Radio 4 FM'), ('bbc_radio_fourlw', 'BBC Radio 4 LW'), ('bbc_radio_four_extra', 'BBC Radio 4 Extra'), ('bbc_radio_five_live', 'BBC Radio 5 live'), ('bbc_radio_five_live_sports_extra', 'BBC Radio 5 live sports extra'), ('bbc_6music', 'BBC Radio 6 Music'), ('bbc_asian_network', 'BBC Asian Network'), ('bbc_radio_scotland_fm', 'BBC Radio Scotland'), ('bbc_radio_nan_gaidheal', u'BBC Radio nan GĂ idheal'), ('bbc_radio_ulster', 'BBC Radio Ulster'), ('bbc_radio_foyle', 'BBC Radio Foyle'), ('bbc_radio_wales_fm', 'BBC Radio Wales'), ('bbc_radio_cymru', 'BBC Radio Cymru'), ('bbc_radio_berkshire', 'BBC Radio Berkshire'), ('bbc_radio_bristol', 'BBC Radio Bristol'), ('bbc_radio_cambridge', 'BBC Radio Cambridgeshire'), ('bbc_radio_cornwall', 'BBC Radio Cornwall'), ('bbc_radio_coventry_warwickshire', 'BBC Coventry & Warwickshire'), ('bbc_radio_cumbria', 'BBC Radio Cumbria'), ('bbc_radio_derby', 'BBC Radio Derby'), ('bbc_radio_devon', 'BBC Radio Devon'), ('bbc_radio_essex', 'BBC Essex'), ('bbc_radio_gloucestershire', 'BBC Radio Gloucestershire'), ('bbc_radio_guernsey', 'BBC Radio Guernsey'), ('bbc_radio_hereford_worcester', 'BBC Hereford & Worcester'), ('bbc_radio_humberside', 'BBC Radio Humberside'), ('bbc_radio_jersey', 'BBC Radio Jersey'), ('bbc_radio_kent', 'BBC Radio Kent'), ('bbc_radio_lancashire', 'BBC Radio Lancashire'), ('bbc_radio_leeds', 'BBC Radio Leeds'), ('bbc_radio_leicester', 'BBC Radio Leicester'), ('bbc_radio_lincolnshire', 'BBC Radio Lincolnshire'), ('bbc_london', 'BBC Radio London'), ('bbc_radio_manchester', 'BBC Radio Manchester'), ('bbc_radio_merseyside', 'BBC Radio Merseyside'), ('bbc_radio_newcastle', 'BBC Newcastle'), ('bbc_radio_norfolk', 'BBC Radio Norfolk'), ('bbc_radio_northampton', 'BBC Radio Northampton'), ('bbc_radio_nottingham', 'BBC Radio Nottingham'), ('bbc_radio_oxford', 'BBC Radio Oxford'), ('bbc_radio_sheffield', 'BBC Radio Sheffield'), ('bbc_radio_shropshire', 'BBC Radio Shropshire'), ('bbc_radio_solent', 'BBC Radio Solent'), ('bbc_radio_somerset_sound', 'BBC Somerset'), ('bbc_radio_stoke', 'BBC Radio Stoke'), ('bbc_radio_suffolk', 'BBC Radio Suffolk'), ('bbc_radio_surrey', 'BBC Surrey'), ('bbc_radio_sussex', 'BBC Sussex'), ('bbc_tees', 'BBC Tees'), ('bbc_three_counties_radio', 'BBC Three Counties Radio'), ('bbc_radio_wiltshire', 'BBC Wiltshire'), ('bbc_wm', 'BBC WM 95.6'), ('bbc_radio_york', 'BBC Radio York'), ] for id, name in channel_list: iconimage = xbmc.translatePath( os.path.join('special://home/addons/plugin.video.iplayerwww/media', id + '.png')) if ADDON.getSetting('streams_autoplay') == 'true': AddMenuEntry(name, id, 213, iconimage, '', '') else: AddMenuEntry(name, id, 133, iconimage, '', '') def ListListenList(logged_in): if(CheckLogin(logged_in) == False): CreateBaseDirectory('audio') return """Scrapes all episodes of the favourites page.""" html = OpenURL('http://www.bbc.co.uk/radio/favourites/episodesandclips') programmes = html.split('
  • \s*(.+?)\s*', programme) if subtitle_match: if subtitle_match.group(1).strip(): subtitle = "(%s)" % subtitle_match.group(1) image = '' image_match = re.search(r'\s*(.+?)\s*', programme) if station_match: station = station_match.group(1) title = "[B]%s[/B] - %s %s" % (station, name, subtitle) if programme_id and title and image: url = "http://www.bbc.co.uk/programmes/%s" % programme_id CheckAutoplay(title, url, image, ' ', '') #BUG: this should sort by original order but it doesn't (see http://trac.kodi.tv/ticket/10252) xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_VIDEO_TITLE) xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_UNSORTED) def Search(search_entered): """Simply calls the online search function. The search is then evaluated in EvaluateSearch.""" if search_entered is None: keyboard = xbmc.Keyboard('', 'Search iPlayer') keyboard.doModal() if keyboard.isConfirmed(): search_entered = keyboard.getText() if search_entered is None: return False url = 'http://www.bbc.co.uk/radio/programmes/a-z/by/%s/current' % search_entered GetPage(url) def GetAvailableStreams(name, url, iconimage, description): """Calls AddAvailableStreamsDirectory based on user settings""" stream_ids = ScrapeAvailableStreams(url) if stream_ids: AddAvailableStreamsDirectory(name, stream_ids, iconimage, description) def ParseStreams(stream_id): retlist = [] # print "Parsing streams for PID: %s"%stream_id[0] # Open the page with the actual strem information and display the various available streams. NEW_URL = "http://open.live.bbc.co.uk/mediaselector/5/select/version/2.0/mediaset/apple-ipad-hls/vpid/%s/proto/http?cb=%d" % (stream_id[0], random.randrange(10000,99999)) #NOTE magic from get_iplayer html = OpenURL(NEW_URL) # Parse the different streams and add them as new directory entries. match = re.compile( 'media.+?bitrate="(.+?)".+?encoding="(.+?)".+?connection.+?href="(.+?)".+?supplier="(.+?)".+?transferFormat="(.+?)"' ).findall(html) for bitrate, encoding, url, supplier, transfer_format in match: retlist.append((supplier, bitrate, url, encoding)) return retlist, match def ScrapeAvailableStreams(url): # Open page and retrieve the stream ID html = OpenURL(url) # Search for standard programmes. stream_id_st = re.compile('"vpid":"(.+?)"').findall(html) return stream_id_st def CheckAutoplay(name, url, iconimage, plot, aired=None): if ADDON.getSetting('streams_autoplay') == 'true': AddMenuEntry(name, url, 212, iconimage, plot, '', aired=aired) else: AddMenuEntry(name, url, 132, iconimage, plot, '', aired=aired)