summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartijn Kaijser <martijn@xbmc.org>2017-11-16 19:55:18 +0100
committerGitHub <noreply@github.com>2017-11-16 19:55:18 +0100
commit8d32a9a1e964d84fc04ea2cf039c441037512b23 (patch)
treec4b83e6ac38c2d5728e39a87560249468e752b99
parent5694665bd5c2aacc3b67de0a3c6ea40fbc782e3e (diff)
parente1152fb41379879d6ed40669e53814d5cbf134cd (diff)
Merge pull request #1510 from LS80/krypton-ecbtv
[plugin.video.spurs-tv] 0.5.0
-rw-r--r--plugin.video.ecbtv/addon.py130
-rw-r--r--plugin.video.ecbtv/addon.xml3
-rw-r--r--plugin.video.ecbtv/resources/language/resource.language.en_gb/strings.po12
-rw-r--r--plugin.video.ecbtv/resources/lib/api.py152
4 files changed, 224 insertions, 73 deletions
diff --git a/plugin.video.ecbtv/addon.py b/plugin.video.ecbtv/addon.py
index fc87873..03f86f0 100644
--- a/plugin.video.ecbtv/addon.py
+++ b/plugin.video.ecbtv/addon.py
@@ -29,16 +29,60 @@ from kodiswift import Plugin
from resources.lib import api
-plugin = Plugin()
+PAGE_SIZE = 9
+plugin = Plugin(addon_id='plugin.video.ecbtv')
+
+
+def top_level_categories():
+ yield {'label': u'[B]{}[/B]'.format(plugin.get_string(30002)),
+ 'path': plugin.url_for('show_all_videos_first_page')}
+ yield {'label': u'[B]{}[/B]'.format(plugin.get_string(30001)),
+ 'path': plugin.url_for('search')}
+ yield {'label': 'England',
+ 'path': plugin.url_for('show_videos_by_reference_first_page',
+ reference=api.england().reference)}
+ yield {'label': 'Counties',
+ 'path': plugin.url_for('show_counties')}
+ yield {'label': 'Players',
+ 'path': plugin.url_for('show_player_categories')}
+
+
+def subcategories(categories, route):
+ for category in categories:
+ yield {'label': category.name,
+ 'thumbnail': category.thumbnail,
+ 'path': plugin.url_for(route, category=category.name)}
+
+
+def entity_items(entities):
+ for entity in entities:
+ yield {'label': entity.name,
+ 'thumbnail': entity.thumbnail,
+ 'path': plugin.url_for('show_videos_by_reference_first_page',
+ reference=entity.reference)}
+
+
+def items(func, route, page, **kwargs):
+ videos, npages = func(page=page, page_size=PAGE_SIZE, **kwargs)
+
+ if page > 1:
+ yield {
+ 'label': u'[B]<< {} ({})[/B]'.format(plugin.get_string(30003), page - 1),
+ 'path': plugin.url_for(route, page=page - 1, **kwargs)
+ }
+ if page < npages:
+ yield {
+ 'label': u'[B]{} ({}) >> [/B]'.format(plugin.get_string(30004), page + 1),
+ 'path': plugin.url_for(route, page=page + 1, **kwargs)
+ }
-def items(videos):
for video in videos:
yield {
+ 'label': video.title,
'thumbnail': video.thumbnail,
'path': video.url,
'info': {
- 'title': video.title,
'date': video.date.strftime('%d.%m.%Y'),
'duration': video.duration
},
@@ -46,41 +90,77 @@ def items(videos):
}
-def categories():
- yield {'label': "[B]{}[/B]".format(plugin.get_string(30001)),
- 'path': plugin.url_for('search')}
- for title, path in api.categories():
- yield {'label': title, 'path': plugin.url_for('show_videos', path=path)}
+def show_videos(func, route, page, update_listing, **kwargs):
+ return plugin.finish(
+ items(func, route, page, **kwargs),
+ sort_methods=['playlist_order', 'date', 'title', 'duration'],
+ update_listing=update_listing
+ )
+
+
+@plugin.cached()
+def counties():
+ return list(api.counties())
-@plugin.route('/')
+@plugin.cached()
+def player_categories():
+ return list(api.player_categories())
+
+
+@plugin.cached()
+def players(category):
+ return list(api.players(category))
+
+
+@plugin.cached_route('/')
def index():
- return plugin.finish(categories())
+ return list(top_level_categories())
+
+@plugin.route('/counties', name='show_counties', options={'func': counties})
+def show_entities(func):
+ return plugin.finish(entity_items(func()), sort_methods=['label'])
-@plugin.route('/category/<path>')
-def show_videos(path):
+
+@plugin.route('/players')
+def show_player_categories():
return plugin.finish(
- items(api.videos(path)),
- sort_methods=['playlist_order', 'date', 'title', 'duration']
+ subcategories(player_categories(), 'show_players'),
+ sort_methods=['label']
)
+@plugin.route('/players/<category>')
+def show_players(category):
+ return plugin.finish(entity_items(players(category)), sort_methods=['label'])
+
+
+@plugin.route('/videos/all', name='show_all_videos_first_page', options={'update_listing': False})
+@plugin.route('/videos/all/<page>')
+def show_all_videos(page='1', update_listing=True):
+ return show_videos(api.videos, 'show_all_videos', int(page), update_listing)
+
+
+@plugin.route('/videos/<reference>', name='show_videos_by_reference_first_page', options={'update_listing': False})
+@plugin.route('/videos/<reference>/<page>')
+def show_videos_by_reference(reference, page='1', update_listing=True):
+ return show_videos(api.videos, 'show_videos_by_reference', int(page), update_listing, reference=reference)
+
+
+@plugin.route('/search/<term>', name='show_search_results_first_page', options={'update_listing': False})
+@plugin.route('/search/<term>/<page>')
+def show_search_results(term, page='1', update_listing=True):
+ return show_videos(api.search_results, 'show_search_results', int(page), update_listing, term=term)
+
+
@plugin.route('/search')
def search():
- query = plugin.keyboard(heading=plugin.get_string(30001))
- if query:
- url = plugin.url_for('search_result', query=query)
+ term = plugin.keyboard(heading=plugin.get_string(30001))
+ if term:
+ url = plugin.url_for('show_search_results_first_page', term=term)
plugin.redirect(url)
-@plugin.route('/search/<query>')
-def search_result(query):
- return plugin.finish(
- items(api.search_results(query, size=11)),
- sort_methods=['playlist_order', 'date', 'title', 'duration']
- )
-
-
if __name__ == '__main__':
plugin.run()
diff --git a/plugin.video.ecbtv/addon.xml b/plugin.video.ecbtv/addon.xml
index d9a66c6..89e2c17 100644
--- a/plugin.video.ecbtv/addon.xml
+++ b/plugin.video.ecbtv/addon.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<addon id="plugin.video.ecbtv" name="ECB TV" version="0.3.0" provider-name="Leopold">
+<addon id="plugin.video.ecbtv" name="ECB TV" version="0.5.0" provider-name="Leopold">
<requires>
<import addon="xbmc.python" version="2.25.0"/>
<import addon="script.module.kodiswift" version="0.0.8" optional="false"/>
@@ -21,5 +21,6 @@
<icon>resources/icon.png</icon>
<fanart>resources/fanart.jpg</fanart>
</assets>
+ <news>Fixed and improved video browsing after website changes.</news>
</extension>
</addon>
diff --git a/plugin.video.ecbtv/resources/language/resource.language.en_gb/strings.po b/plugin.video.ecbtv/resources/language/resource.language.en_gb/strings.po
index f7b1d15..646d379 100644
--- a/plugin.video.ecbtv/resources/language/resource.language.en_gb/strings.po
+++ b/plugin.video.ecbtv/resources/language/resource.language.en_gb/strings.po
@@ -19,3 +19,15 @@ msgstr ""
msgctxt "#30001"
msgid "Search"
msgstr ""
+
+msgctxt "#30002"
+msgid "All"
+msgstr ""
+
+msgctxt "#30003"
+msgid "Previous Page"
+msgstr ""
+
+msgctxt "#30004"
+msgid "Next Page"
+msgstr ""
diff --git a/plugin.video.ecbtv/resources/lib/api.py b/plugin.video.ecbtv/resources/lib/api.py
index 6f57dcd..c8072c8 100644
--- a/plugin.video.ecbtv/resources/lib/api.py
+++ b/plugin.video.ecbtv/resources/lib/api.py
@@ -28,38 +28,55 @@
Module for extracting video links from the England and Wales Cricket Board website
'''
-import json
import os
+import re
from urlparse import urljoin, urlparse, urlunparse
from urllib import urlencode
from datetime import datetime
import time
from collections import namedtuple
+import math
import requests
from bs4 import BeautifulSoup
-HOST = 'http://www.ecb.co.uk'
-BASE_URL = urljoin(HOST, 'tv/')
+
+BASE_URL = 'http://www.ecb.co.uk/'
HLS_HOST = 'https://secure.brightcove.com/'
HLS_URL_FMT = urljoin(HLS_HOST, 'services/mobile/streaming/index/master.m3u8?videoId={}')
-SEARCH_URL = 'https://content-ecb.pulselive.com/search/ecb/'
+PLAYER_THUMB_URL_FMT = 'https://ecb-resources.s3.amazonaws.com/player-photos/{}/480x480/{}.png'
+SEARCH_URL = 'https://content-ecb.pulselive.com/search/ecb/'
+VIDEO_LIST_URL = 'https://content-ecb.pulselive.com/content/ecb/EN/'
Video = namedtuple('Video', 'title url thumbnail date duration')
+Entity = namedtuple('Entity', 'name reference thumbnail')
+
+
+def _video_list_url(reference, page, page_size=10):
+ '''Returns a URL for a list of videos'''
+ url_parts = list(urlparse(VIDEO_LIST_URL))
+ query_params = dict(
+ contentTypes='video',
+ references=reference if reference is not None else '',
+ page=page - 1,
+ pageSize=page_size
+ )
+ url_parts[4] = urlencode(query_params)
+ return urlunparse(url_parts)
-def _search_url(term, start, size):
+def _search_url(term, page, page_size=10):
'''Returns a URL for the JSON search api'''
url_parts = list(urlparse(SEARCH_URL))
query_params = dict(
type='VIDEO',
fullObjectResponse=True,
terms=term,
- size=size,
- start=start
+ size=page_size,
+ start=(page - 1) * page_size
)
url_parts[4] = urlencode(query_params)
return urlunparse(url_parts)
@@ -78,12 +95,6 @@ def _date_from_str(date_str, fmt='%d %B %Y'):
return datetime(*(time.strptime(date_str, fmt)[0:6])).date()
-def _date(media_item):
- '''Returns a date object from the HTML media item.'''
- date_str = media_item.find('time', 'media__sub-meta').string
- return _date_from_str(date_str)
-
-
def _date_json(json_item):
'''Returns a date object from the JSON item.
The date can be one of two formats'''
@@ -98,60 +109,107 @@ def _date_json(json_item):
raise exc
-def categories():
- '''Generator for category names and links, excluding all that appear before Home'''
- start = False
- for submenu_link in _soup()('a', 'submenu__link'):
- title = submenu_link.string.strip()
- if start and title != 'All Categories':
- yield title, os.path.basename(submenu_link['href'])
- if title == 'Home':
- start = True
+def _thumbnail_variant(video):
+ if video['thumbnail'] is None:
+ return
+ return (variant['url'] for variant in video['thumbnail']['variants']
+ if variant['tag']['id'] == 981).next()
-def videos(path):
- '''Generator for all videos from a particular page'''
- for media_item in _soup(path)('a', 'media__item'):
- video = json.loads(media_item['data-ui-args'])
- yield Video(
- title=media_item.find('span', 'media__title').string,
- url=HLS_URL_FMT.format(video['mediaId']),
- thumbnail=media_item.picture.img['data-highres-img'],
- date=_date(media_item),
- duration=int(video['duration'].replace(',', ''))
+def england():
+ return Entity(
+ name='England',
+ reference='cricket_team:11',
+ thumbnail=None
+ )
+
+
+def counties():
+ for county in _soup('/county-championship/teams')('div', 'partners__item'):
+ team_id = int(os.path.basename(county.a['href']))
+ yield Entity(
+ name=county.a.text,
+ reference='cricket_team:{}'.format(team_id),
+ thumbnail=county.img['src']
+ )
+
+
+def player_categories():
+ for tab in _soup('/england/men/players').find_all(
+ 'div', attrs={'data-ui-args': re.compile(r'{ "title": "\w+" }')}):
+ yield Entity(
+ name=tab['data-ui-tab'],
+ reference=None,
+ thumbnail=None
+ )
+
+
+def players(category='Test'):
+ soup = _soup('/england/men/players').find('div', attrs={'data-ui-tab': category})
+ for player in soup('section', 'profile-player-card'):
+ player_id = player.img['data-player']
+ yield Entity(
+ name=player.img['alt'],
+ reference='cricket_player:{}'.format(player_id),
+ thumbnail=PLAYER_THUMB_URL_FMT.format(category.lower(), player_id)
)
-def search_results(term, start=0, size=10):
+def _video(video):
+ return Video(
+ title=video['title'],
+ url=HLS_URL_FMT.format(video['mediaId']),
+ thumbnail=_thumbnail_variant(video),
+ date=_date_json(video),
+ duration=video['duration']
+ )
+
+
+def _videos(videos_json):
+ '''Generator for all videos from a particular page'''
+ for video in videos_json['content']:
+ yield _video(video)
+
+
+def videos(reference=None, page=1, page_size=10):
+ videos_json = requests.get(_video_list_url(reference, page, page_size)).json()
+ npages = videos_json['pageInfo']['numPages']
+ return _videos(videos_json), npages
+
+
+def _search_results(search_results_json):
'''Generator for videos matching a search term'''
- results = requests.get(_search_url(term, start, size)).json()['hits']['hit']
+ results = search_results_json['hits']['hit']
for result in results:
video = result['response']
- yield Video(
- title=video['title'],
- url=HLS_URL_FMT.format(video['mediaId']),
- thumbnail=video['imageUrl'],
- date=_date_json(video),
- duration=video['duration']
- )
+ yield _video(video)
+
+
+def search_results(term, page=1, page_size=10):
+ search_results_json = requests.get(_search_url(term, page, page_size)).json()
+ total = search_results_json['hits']['found']
+ npages = int(math.ceil(float(total) / page_size))
+ return _search_results(search_results_json), npages
-def _print_all_videos():
+def _print_team_videos():
'''Test function to print all categories and videos'''
- for title, path in categories():
- print '{} ({})'.format(title, path)
- for video in videos(path):
+ for team in [england()] + list(counties()):
+ print '{} ({})'.format(team.name, team.reference)
+ videos_page, _num_pages = videos(team.reference)
+ for video in videos_page:
print '\t', video.title
def _print_search_results(term):
'''Test function to print search results'''
print 'Search: {}'.format(term)
- for video in search_results(term):
+ videos_page, _num_pages = search_results(term)
+ for video in videos_page:
print '\t', video.title
if __name__ == '__main__':
- _print_all_videos()
+ _print_team_videos()
print
_print_search_results('test cricket')