The branch, eden has been updated
via f4f7af96f6a0e2b07531027b6c6e13a6ec814823 (commit)
via 5a6c2fc09fe7ee5a1bfd434e05b1db035588a108 (commit)
from 6709f4fc36a728bf4d0a2f77b85c714cab280cdb (commit)
- Log -----------------------------------------------------------------
http://xbmc.git.sourceforge.net/git/gitweb.cgi?p=xbmc/plugins;a=commit;h=f4f7af96f6a0e2b07531027b6c6e13a6ec814823
commit f4f7af96f6a0e2b07531027b6c6e13a6ec814823
Author: spiff <[email protected]>
Date: Mon May 14 09:22:26 2012 +0200
[plugin.video.ted.talks] updated to version 3.1.0
diff --git a/plugin.video.ted.talks/addon.xml b/plugin.video.ted.talks/addon.xml
index 5de73f5..a17f757 100644
--- a/plugin.video.ted.talks/addon.xml
+++ b/plugin.video.ted.talks/addon.xml
@@ -1,19 +1,19 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<addon id="plugin.video.ted.talks"
- name="TED Talks"
- version="3.0.1"
- provider-name="rwparris2, moreginger">
+<addon id="plugin.video.ted.talks" name="TED Talks" version="3.1.0"
provider-name="rwparris2, moreginger">
<requires>
<import addon="xbmc.python" version="2.0"/>
<import addon="script.module.simplejson" version="2.0.10"/>
<import addon="script.module.beautifulsoup" version="3.0.8"/>
<import addon="script.module.elementtree" version="1.2.7"/>
</requires>
- <extension point="xbmc.python.pluginsource"
- library="default.py">
- <provides>video</provides>
+ <extension point="xbmc.python.pluginsource" library="default.py">
+ <provides>video</provides>
</extension>
<extension point="xbmc.addon.metadata">
+ <!-- There are subtitles on TED in many languages,
+ and this plugin is (partially) internationalized in several,
+ but it seems better to claim the one we are sure of for now. -->
+ <language>en</language>
<summary lang="en">Watch talks from TED.com, a site dedicated to 'Ideas
Worth Spreading'</summary>
<summary lang="hu">Beszédek megtekintése a TED.com-ról</summary>
<summary lang="pt">Veja debates de TED.com, um site dedicado a 'Ideias com
Valor'</summary>
diff --git a/plugin.video.ted.talks/changelog.txt
b/plugin.video.ted.talks/changelog.txt
index e6d62b4..2fb428b 100644
--- a/plugin.video.ted.talks/changelog.txt
+++ b/plugin.video.ted.talks/changelog.txt
@@ -1,5 +1,10 @@
+[B]Version 3.1.0[/B]
+Subtitle support (issue#10)
+Fix favorites (issue#17)
+Speakers list divided by first letter of last name (issue#18)
+
[B]Version 3.0.1[/B]
-Full speaker list restored
(https://github.com/moreginger/xbmc-plugin.video.ted.talks/issues/14)
+Full speaker list restored (issue#14)
[B]Version 3.0.0[/B]
Bump major version for Eden repository
diff --git a/plugin.video.ted.talks/default.py
b/plugin.video.ted.talks/default.py
index 87e21f7..710d679 100644
--- a/plugin.video.ted.talks/default.py
+++ b/plugin.video.ted.talks/default.py
@@ -4,14 +4,16 @@
"""
import sys
import resources.lib.plugin as plugin
+import resources.lib.settings as settings
import resources.lib.model.arguments as arguments
if __name__ == "__main__":
plugin.init()
+ settings.init()
import resources.lib.ted_talks as ted_talks
args_map = arguments.parse_arguments(sys.argv[2])
- ted_talks.Main(logger = plugin.log, args_map = args_map).run()
+ ted_talks.Main(args_map=args_map).run()
sys.modules.clear()
diff --git a/plugin.video.ted.talks/resources/language/English/strings.xml
b/plugin.video.ted.talks/resources/language/English/strings.xml
index 436a0b9..eac4e58 100644
--- a/plugin.video.ted.talks/resources/language/English/strings.xml
+++ b/plugin.video.ted.talks/resources/language/English/strings.xml
@@ -2,9 +2,10 @@
<strings>
<string id="30000">TED Talks</string>
- <!--Categories-->
+ <!--Categories and sub-folders -->
<string id="30001">Newest Talks</string>
<string id="30002">Speakers</string>
+ <string id="30006">Last Name: </string>
<string id="30003">Themes</string>
<string id="30004">Search</string>
<string id="30005">Favorites</string>
@@ -30,7 +31,10 @@
<string id="30051">User could not be authenticated</string>
<!--Settings-->
- <string id="30075">Login Information</string>
+ <string id="30110">Subtitles</string>
+ <string id="30111">Enable subtitles</string>
+ <string id="30113">Custom language codes (optional)</string>
+ <string id="30075">Login</string>
<string id="30070">username</string>
<string id="30071">password</string>
<string id="30076">Downloading</string>
diff --git a/plugin.video.ted.talks/resources/lib/menu_util.py
b/plugin.video.ted.talks/resources/lib/menu_util.py
index 72ba174..6584418 100644
--- a/plugin.video.ted.talks/resources/lib/menu_util.py
+++ b/plugin.video.ted.talks/resources/lib/menu_util.py
@@ -4,9 +4,9 @@ def create_context_menu(getLS, url = None, favorites_action =
None, talkID = Non
context_menu = []
context_menu += [(getLS(30097), 'Action(queue)')]
if url != None:
- context_menu += [(getLS(30096), 'RunPlugin(%s?downloadVideo=%s)' %
(sys.argv[0], url))]
+ context_menu += [(getLS(30096),
'RunPlugin(%s?mode=downloadVideo&url=%s)' % (sys.argv[0], url))]
if favorites_action == "add":
- context_menu += [(getLS(30090), 'RunPlugin(%s?addToFavorites=%s)' %
(sys.argv[0], talkID))]
+ context_menu += [(getLS(30090),
'RunPlugin(%s?mode=addToFavorites&talkID=%s)' % (sys.argv[0], talkID))]
elif favorites_action == "remove":
- context_menu += [(getLS(30093), 'RunPlugin(%s?removeFromFavorites=%s)'
% (sys.argv[0], talkID))]
+ context_menu += [(getLS(30093),
'RunPlugin(%s?mode=removeFromFavorites&talkID=%s)' % (sys.argv[0], talkID))]
return context_menu
diff --git a/plugin.video.ted.talks/resources/lib/menu_util_test.py
b/plugin.video.ted.talks/resources/lib/menu_util_test.py
index aa40ffe..c1d16b3 100644
--- a/plugin.video.ted.talks/resources/lib/menu_util_test.py
+++ b/plugin.video.ted.talks/resources/lib/menu_util_test.py
@@ -5,25 +5,25 @@ import re
getLS = lambda x: x
class TestNewTalksRss(unittest.TestCase):
-
+
def test_create_context_menu_fallback(self):
context_menu = menu_util.create_context_menu(getLS)
self.assertEquals([(30097, 'Action(queue)')], context_menu)
-
+
def test_create_context_menu_url(self):
context_menu = menu_util.create_context_menu(getLS,
'invalid://nowhere/nothing.mp4')
self.assertTrue(len(context_menu) == 2)
self.assertEquals((30097, 'Action(queue)'), context_menu[0])
self.assertEquals(30096, context_menu[1][0])
- download_re =
re.compile('RunPlugin\(.+?downloadVideo=invalid://nowhere/nothing\.mp4\)')
+ download_re =
re.compile('RunPlugin\(.+?mode=downloadVideo&url=invalid://nowhere/nothing\.mp4\)')
self.assertTrue(download_re.match(context_menu[1][1]))
-
+
def test_create_context_menu_add_favorite(self):
context_menu = menu_util.create_context_menu(getLS, favorites_action =
"add", talkID = "42")
self.assertTrue(len(context_menu) == 2)
self.assertEquals((30097, 'Action(queue)'), context_menu[0])
self.assertEquals(30090, context_menu[1][0])
- download_re = re.compile('RunPlugin\(.+?addToFavorites=42\)')
+ download_re =
re.compile('RunPlugin\(.+?mode=addToFavorites&talkID=42\)')
self.assertTrue(download_re.match(context_menu[1][1]))
-
+
\ No newline at end of file
diff --git a/plugin.video.ted.talks/resources/lib/model/favorites_scraper.py
b/plugin.video.ted.talks/resources/lib/model/favorites_scraper.py
index 57812c5..05ebda8 100644
--- a/plugin.video.ted.talks/resources/lib/model/favorites_scraper.py
+++ b/plugin.video.ted.talks/resources/lib/model/favorites_scraper.py
@@ -1,6 +1,7 @@
-from url_constants import URLTED, URLFAVORITES, URLADDFAV, URLREMFAV
+from url_constants import URLTED, URLFAVORITES, URLADDREMFAV
from BeautifulSoup import SoupStrainer, MinimalSoup as BeautifulSoup
from util import resizeImage
+import simplejson
import re
class Favorites:
@@ -9,26 +10,34 @@ class Favorites:
self.logger = logger
self.get_HTML = get_HTML
- def getFavoriteTalks(self, userID, url = URLFAVORITES):
+ def getFavoriteTalks(self, userID, url=URLFAVORITES):
if userID:
html = self.get_HTML(url + userID)
- talkContainer = SoupStrainer(attrs = {'class':re.compile('box
clearfix')})
- for talk in BeautifulSoup(html, parseOnlyThese = talkContainer):
- title = talk.ul.a.string
- link = URLTED+talk.dt.a['href']
- pic = resizeImage(talk.find('img', attrs =
{'src':re.compile('.+?\.jpg')})['src'])
- yield {'url':link, 'Title':title, 'Thumb':pic}
+ talkContainer = SoupStrainer(attrs={'class':re.compile('col
clearfix')})
+ for talk in BeautifulSoup(html, parseOnlyThese=talkContainer):
+ title = talk.a['title']
+ link = URLTED + talk.a['href']
+ img = resizeImage(talk.a.img['src'])
+ yield title, link, img
else:
self.logger('invalid user object')
def addToFavorites(self, talkID):
- response = self.get_HTML(URLADDFAV % (talkID))
- if not response:
- self.logger('failed to add favorite with id: %s' % (talkID))
- return response != None
+ return self.toggle_favorite('add', talkID)
def removeFromFavorites(self, talkID):
- response = self.get_HTML(URLREMFAV % (id))
+ return self.toggle_favorite('remove', talkID)
+
+ def toggle_favorite(self, verb, talkID):
+ url = URLADDREMFAV % (verb)
+ response = self.get_HTML(url, 'id=%s&type=talks' % (talkID))
if not response:
- self.logger('failed to remove favorite with id: %s' % (talkID))
- return response != None
\ No newline at end of file
+ msg = 'Failed to %s favorite with id: %s' % (verb, talkID)
+ self.logger(msg)
+ return False
+ parsed_response = simplejson.loads(response)
+ if parsed_response['status'] != 'success':
+ self.logger('Failed to %s favorite with id: %s\nReponse was: %s' %
(verb, talkID, response))
+ return False
+ return True
+
diff --git a/plugin.video.ted.talks/resources/lib/model/fetcher.py
b/plugin.video.ted.talks/resources/lib/model/fetcher.py
index 4e3407f..84b0e28 100644
--- a/plugin.video.ted.talks/resources/lib/model/fetcher.py
+++ b/plugin.video.ted.talks/resources/lib/model/fetcher.py
@@ -8,7 +8,7 @@ class Fetcher:
self.logger = logger
self.getTranslatedPath = getTranslatedPath
- def getHTML(self, url, headers = [('User-Agent', 'Mozilla/5.0 (Windows; U;
Windows NT 5.1; en-US; rv:1.8.1.14) Gecko/20080404 Firefox/2.0.0.14')]):
+ def getHTML(self, url, data=None):
"""
url Might be a real URL object or a String-like thing.
"""
@@ -18,6 +18,12 @@ class Fetcher:
url_string = url
self.logger('%s attempting to open %s with data' % (__name__,
url_string))
+ headers = [
+ ('User-Agent', 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US;
rv:1.8.1.14) Gecko/20080404 Firefox/2.0.0.14'),
+ ]
+ if data:
+ headers = headers + [('Content-type',
'application/x-www-form-urlencoded')]
+
#create cookiejar
cj = cookielib.LWPCookieJar()
cookiefile = self.getTranslatedPath('special://temp/ted-cookies.lwp')
@@ -26,13 +32,13 @@ class Fetcher:
cj.load(cookiefile)
#log what cookies were loaded
for index, cookie in enumerate(cj):
- self.logger('loaded cookie : %s\nfrom %s' % (cookie,
cookiefile))
+ self.logger('loaded cookie : %s from %s' % (cookie,
cookiefile))
#build opener with automagic cookie handling abilities.
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
opener.addheaders = headers
try:
- usock = opener.open(url)
+ usock = opener.open(url, data)
response = usock.read()
usock.close()
cj.save(cookiefile)
diff --git a/plugin.video.ted.talks/resources/lib/model/rss_scraper.py
b/plugin.video.ted.talks/resources/lib/model/rss_scraper.py
index 1c1108e..2045944 100644
--- a/plugin.video.ted.talks/resources/lib/model/rss_scraper.py
+++ b/plugin.video.ted.talks/resources/lib/model/rss_scraper.py
@@ -6,15 +6,6 @@ so keep it for now.
import urllib2
import time
-# multiprocessing module isn't working on Win32:
http://forum.xbmc.org/showthread.php?p=1023960
-#try:
-# # >=Eden
-# from multiprocessing import Pool
-# do_multi_threading = True
-#except ImportError:
-# # < Eden
-# do_multi_threading = False
-
try:
from elementtree.ElementTree import fromstring
except ImportError:
@@ -48,7 +39,7 @@ class NewTalksRss:
pic =
item.find('./{http://search.yahoo.com/mrss/}thumbnail').get('url')
duration =
item.find('./{http://www.itunes.com/dtds/podcast-1.0.dtd}duration').text
plot =
item.find('./{http://www.itunes.com/dtds/podcast-1.0.dtd}summary').text
- link = item.find('./enclosure').get('url')
+ link = item.find('./link').text
talkID = item.find('./guid').text.split(':')[-1]
# Get date as XBMC wants it
@@ -73,15 +64,6 @@ class NewTalksRss:
rss_urls = [sd_rss_url, hd_rss_url] # Prefer HD, but get SD if that's
all there is (my friends)
document_fetchers = []
-# if do_multi_threading:
-# pool = Pool(processes=1) # Maybe it would be better to fetch them
simultaneously?
-# for url in rss_urls:
-# result = pool.apply_async(get_document, [url])
-# document_fetchers.append(lambda x: result.get(30))
-# else:
-# for url in rss_urls:
-# document_fetchers.append(lambda x: get_document(url))
-
for url in rss_urls:
document_fetchers.append(lambda x: get_document(url))
@@ -92,15 +74,5 @@ class NewTalksRss:
talk = self.get_talk_details(item)
talksByTitle[talk['title']] = talk
-# if do_multi_threading:
-# # pool.close()
-# # pool.join()
-# # If I close Pool using close/join, then it logs
-# # ERROR: Error Type: <type 'exceptions.OSError'>
-# # ERROR: Error Contents: [Errno 3] No such process
-# # when the app exits (i.e. finalization occurs).
-# # Whereas this seems to be OK.
-# pool._terminate()
-
return talksByTitle.itervalues()
diff --git a/plugin.video.ted.talks/resources/lib/model/rss_scraper_test.py
b/plugin.video.ted.talks/resources/lib/model/rss_scraper_test.py
index 3135298..25f9579 100644
--- a/plugin.video.ted.talks/resources/lib/model/rss_scraper_test.py
+++ b/plugin.video.ted.talks/resources/lib/model/rss_scraper_test.py
@@ -1,6 +1,7 @@
import unittest
from rss_scraper import NewTalksRss
from datetime import datetime
+import sys
try:
from elementtree.ElementTree import fromstring
except ImportError:
@@ -15,20 +16,20 @@ minimal_item = """
<guid isPermaLink="false">eng.video.talk.ted.com:830</guid>
<pubDate>Sat, 04 Feb 2012 08:14:00 +0000</pubDate>
<media:thumbnail url="invalid://nowhere/nothing.jpg" width="42" height="42"
/>
- <enclosure url="invalid://nowhere/nothing.mp4" length="42" type="video/mp4"
/>
+ <link>invalid://nowhere/nothing.html</link>
</item>"""
class TestNewTalksRss(unittest.TestCase):
def setUp(self):
- self.talks = NewTalksRss(lambda x: x)
+ self.talks = NewTalksRss(lambda x: sys.stdout.write(x))
def test_get_talk_details_minimal(self):
details = self.talks.get_talk_details(fromstring(minimal_item))
expected_details = {
'author':'Dovahkiin',
'date':'04.02.2012',
- 'link':'invalid://nowhere/nothing.mp4',
+ 'link':'invalid://nowhere/nothing.html',
'thumb':'invalid://nowhere/nothing.jpg',
'title':'fus ro dah',
'plot':'Unrelenting Force',
diff --git a/plugin.video.ted.talks/resources/lib/model/url_constants.py
b/plugin.video.ted.talks/resources/lib/model/url_constants.py
index 9ed46e2..9aa98fb 100644
--- a/plugin.video.ted.talks/resources/lib/model/url_constants.py
+++ b/plugin.video.ted.talks/resources/lib/model/url_constants.py
@@ -1,5 +1,5 @@
URLTED = 'http://www.ted.com'
URLPROFILES = URLTED + '/profiles'
URLFAVORITES = URLPROFILES + '/favorites/id/'
-URLADDFAV = URLPROFILES + '/addfavorites?id=%s&modulename=talks'
-URLREMFAV = URLPROFILES + '/removefavorites?id=%s&modulename=talks'
\ No newline at end of file
+URLADDREMFAV = URLPROFILES + '/%sfavorites'
+URLSPEAKERS = URLTED +
'/speakers?orderedby=TALKPOSTED&page=%s&alphabylastname=%s'
diff --git a/plugin.video.ted.talks/resources/lib/model/user_test.py
b/plugin.video.ted.talks/resources/lib/model/user_test.py
index b6740b7..37c2e04 100644
--- a/plugin.video.ted.talks/resources/lib/model/user_test.py
+++ b/plugin.video.ted.talks/resources/lib/model/user_test.py
@@ -28,9 +28,8 @@ class TestUser(unittest.TestCase):
os.remove(cookieFile)
def test_login_failure(self):
- cookieFile = tempfile.mkstemp()[1]
+ cookieFile = tempfile.mktemp()[1]
try:
- os.remove(cookieFile)
get_HTML = fetcher.Fetcher(lambda x: sys.stdout.write(x + '\n'),
lambda x: cookieFile).getHTML
user = User(get_HTML)
userID, real_name = user.login(self.username, self.password +
"not")
diff --git a/plugin.video.ted.talks/resources/lib/model/util.py
b/plugin.video.ted.talks/resources/lib/model/util.py
index b352e12..266abf2 100644
--- a/plugin.video.ted.talks/resources/lib/model/util.py
+++ b/plugin.video.ted.talks/resources/lib/model/util.py
@@ -29,4 +29,7 @@ def cleanHTML(s, noBS=False):
def resizeImage(imagePath):
- return imagePath.replace('132x99', '389x292').replace('113x85', '389x292')
+ '''
+ Turn paths for small thumbnails into higher resolution versions.
+ '''
+ return re.sub('_\d+x\d+.jpg', '_389x292.jpg', imagePath)
diff --git a/plugin.video.ted.talks/resources/lib/plugin.py
b/plugin.video.ted.talks/resources/lib/plugin.py
index a669835..298ea1c 100644
--- a/plugin.video.ted.talks/resources/lib/plugin.py
+++ b/plugin.video.ted.talks/resources/lib/plugin.py
@@ -2,6 +2,8 @@
Contains constants that we initialize to the correct values at runtime.
"""
__plugin__ = "TED Talks Uninitialized Plugin"
+getLS = lambda x: x
+__pluginLS__ = __plugin__
__author__ = "XXX"
__version__ = "X.X.X"
@@ -9,11 +11,16 @@ __version__ = "X.X.X"
def init():
import xbmcaddon
addon = xbmcaddon.Addon(id='plugin.video.ted.talks')
- global __plugin__, __author__, __version__
+ global __plugin__, getLS, __author__, __version__, __pluginLS__
__plugin__ = addon.getAddonInfo('name')
+ getLS = addon.getLocalizedString
+ __pluginLS__ = getLS(30000)
__author__ = addon.getAddonInfo('author')
__version__ = addon.getAddonInfo('version')
print "[PLUGIN] '%s: version %s' initialized!" % (__plugin__, __version__)
-def log(message):
- print "[%s] %s" % (__plugin__, message)
\ No newline at end of file
+def report(gnarly_message, friendly_message=None):
+ import xbmc
+ print "[%s] %s" % (__plugin__, gnarly_message)
+ if friendly_message:
+ xbmc.executebuiltin('Notification(%s,%s,)' % (__pluginLS__,
friendly_message))
diff --git a/plugin.video.ted.talks/resources/lib/talkDownloader.py
b/plugin.video.ted.talks/resources/lib/talkDownloader.py
index 8f829c6..7ae66a0 100644
--- a/plugin.video.ted.talks/resources/lib/talkDownloader.py
+++ b/plugin.video.ted.talks/resources/lib/talkDownloader.py
@@ -7,15 +7,13 @@ import xbmcgui
import xbmcaddon
import plugin
-__settings__ = xbmcaddon.Addon(id='plugin.video.ted.talks')
-getLS = __settings__.getLocalizedString
-
class Download:
- def __init__(self, title, videoURL, downloadPath):
+ def __init__(self, getLS, title, videoURL, downloadPath):
+ self.getLS = getLS
self.pDialog = xbmcgui.DialogProgress()
- self.pDialog.create(getLS(30000))
+ self.pDialog.create(self.getLS(30000))
downloadPath = xbmc.translatePath(downloadPath)
self.url = videoURL
@@ -27,21 +25,21 @@ class Download:
if self.checkPath(downloadPath, self.filename):
try:
- re = urllib.urlretrieve(self.url, self.fullDownloadPath,
reporthook = self.showdlProgress)
+ re = urllib.urlretrieve(self.url, self.fullDownloadPath,
reporthook=self.showdlProgress)
print '[%s] Download Success!' % (plugin.__plugin__)
except IOError, e:
print e
self.pDialog.close()
dialog = xbmcgui.Dialog()
- dialog.ok('Error', str(i)+' of
'+str(len(photos))+'\n'+self.url, e.__str__())
+ dialog.ok('Error', str(i) + ' of ' + str(len(photos)) + '\n' +
self.url, e.__str__())
if self.pDialog.iscanceled():
self.pDialog.close()
#close the progress dialog
self.pDialog.close()
def showdlProgress(self, count, blockSize, totalSize):
- percent = int(count*blockSize*100/totalSize)
- self.pDialog.update(percent, '%s %s' % (getLS(32023), self.url), '%s
%s' % (getLS(32024), self.fullDownloadPath))
+ percent = int(count * blockSize * 100 / totalSize)
+ self.pDialog.update(percent, '%s %s' % (self.getLS(32023), self.url),
'%s %s' % (self.getLS(32024), self.fullDownloadPath))
print percent, '% ',
if self.pDialog.iscanceled():
self.pDialog.close()
@@ -49,12 +47,12 @@ class Download:
def checkPath(self, path, filename):
if os.path.isdir(path):
if os.path.isfile(os.path.join(path, filename)):
- if not os.path.getsize(os.path.join(path, filename))>0:
+ if not os.path.getsize(os.path.join(path, filename)) > 0:
return True #overwrite empty files, #skip others.
else:
return True
- def getLegalFilename(self, filename, illegalChars = '=?:;\'"*+,/|\\'):
+ def getLegalFilename(self, filename, illegalChars='=?:;\'"*+,/|\\'):
for c in illegalChars:
filename = filename.replace(c, '')
#xbox needs file name trunicated to 42 characters including extension.
diff --git a/plugin.video.ted.talks/resources/lib/ted_talks.py
b/plugin.video.ted.talks/resources/lib/ted_talks.py
index 7c039ee..5b11487 100644
--- a/plugin.video.ted.talks/resources/lib/ted_talks.py
+++ b/plugin.video.ted.talks/resources/lib/ted_talks.py
@@ -1,40 +1,40 @@
import sys
import urllib
import ted_talks_scraper
+import plugin
+import settings
from talkDownloader import Download
from model.fetcher import Fetcher
from model.user import User
from model.rss_scraper import NewTalksRss
from model.favorites_scraper import Favorites
+from model.speakers_scraper import Speakers
import menu_util
+import os
+import time
import xbmc
import xbmcplugin
import xbmcgui
import xbmcaddon
-
-__settings__ = xbmcaddon.Addon(id='plugin.video.ted.talks')
-getLS = __settings__.getLocalizedString
+import itertools
def login(user_scraper, username, password):
user_details = user_scraper.login(username, password)
if not user_scraper:
- xbmcgui.Dialog().ok(getLS(30050), getLS(30051))
+ xbmcgui.Dialog().ok(plugin.getLS(30050), plugin.getLS(30051))
return user_details
class UI:
- def __init__(self, logger, get_HTML, ted_talks, user, settings, args):
- self.logger = logger
+ def __init__(self, get_HTML, ted_talks, user):
self.get_HTML = get_HTML
self.ted_talks = ted_talks
self.user = user
- self.settings = settings
- self.args = args
xbmcplugin.setContent(int(sys.argv[1]), 'movies')
- def endofdirectory(self, sortMethod = 'title'):
+ def endofdirectory(self, sortMethod='title'):
# set sortmethod to something xbmc can use
if sortMethod == 'title':
sortMethod = xbmcplugin.SORT_METHOD_LABEL
@@ -43,196 +43,307 @@ class UI:
#Sort methods are required in library mode.
xbmcplugin.addSortMethod(int(sys.argv[1]), sortMethod)
#let xbmc know the script is done adding items to the list.
- xbmcplugin.endOfDirectory(handle = int(sys.argv[1]), updateListing =
False)
-
- def addItem(self, info, isFolder = True):
- #Defaults in dict. Use 'None' instead of None so it is compatible for
quote_plus in parseArgs
- #create params for xbmcplugin module
- args = {}
- for key1, key2 in {'url': 'url', 'mode': 'mode', 'Title': 'name',
'Thumb': 'icon'}.iteritems():
- if key1 in info:
- if info[key1] is None:
- self.logger("'None' in item dict: " + info)
- else:
- args[key2] = urllib.quote_plus(info[key1].encode('ascii',
'ignore'))
-
- u = sys.argv[0] + '?' + "&".join(key + '=' + value for key, value in
args.iteritems())
-
- info.setdefault('url', 'None')
- info.setdefault('Thumb', 'None')
- info.setdefault('Icon', info['Thumb'])
- #create list item
- if info['Title'].startswith(" "):
- title = info['Title'][1:]
- else:
- title = info['Title']
- li = xbmcgui.ListItem(label = title, iconImage = info['Icon'],
thumbnailImage = info['Thumb'])
- li.setInfo(type='Video', infoLabels = info)
- #for videos, replace context menu with queue and add to favorites
+ xbmcplugin.endOfDirectory(handle=int(sys.argv[1]), updateListing=False)
+
+ def addItem(self, title, mode, url=None, img=None, video_info={},
talkID=None, isFolder=True, total_items=0):
+ # Create action url
+ args = {'mode': mode}
+ if url:
+ args['url'] = url
+ if img:
+ args['icon'] = img
+ args = [k + '=' + urllib.quote_plus(v.encode('ascii', 'ignore')) for
k, v in args.iteritems()]
+ action_url = sys.argv[0] + '?' + "&".join(args)
+
+ li = xbmcgui.ListItem(label=title, iconImage=img, thumbnailImage=img)
+ video_info = dict((k, v) for k, v in video_info.iteritems() if k in
['date', 'duration', 'plot'])
+ if len(video_info) > 0:
+ li.setInfo('video', video_info)
if not isFolder:
- li.setProperty("IsPlayable", "true")#let xbmc know this can be
played, unlike a folder.
- context_menu = menu_util.create_context_menu(getLS = getLS)
- li.addContextMenuItems(context_menu, replaceItems = True)
+ li.setProperty("IsPlayable", "true") #let xbmc know this can be
played, unlike a folder.
+ context_menu = menu_util.create_context_menu(getLS=plugin.getLS)
+ li.addContextMenuItems(context_menu, replaceItems=True)
else:
- #for folders, completely remove contextmenu, as it is totally
useless.
- li.addContextMenuItems([], replaceItems = True)
- #add item to list
- xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=u,
listitem=li, isFolder=isFolder)
-
- def playVideo(self):
- video = self.ted_talks.getVideoDetails(self.args['url'])
- li=xbmcgui.ListItem(video['Title'],
- iconImage = self.args['icon'],
- thumbnailImage = self.args['icon'],
- path = video['url'])
- li.setInfo(type='Video', infoLabels=video)
+ li.addContextMenuItems([], replaceItems=True)
+ xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=action_url,
listitem=li, isFolder=isFolder, totalItems=total_items)
+
+ def playVideo(self, url, icon):
+ subs_language = settings.get_subtitle_languages()
+ title, url, subs, info_labels = self.ted_talks.getVideoDetails(url,
subs_language)
+ li = xbmcgui.ListItem(title, iconImage=icon, thumbnailImage=icon,
path=url)
+ li.setInfo(type='Video', infoLabels=info_labels)
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, li)
+ if subs:
+ # If not we either don't want them, or should have displayed a
notification.
+ subs_file = os.path.join(xbmc.translatePath("special://temp"),
'ted_talks_subs.srt')
+ fh = open(subs_file, 'w')
+ try:
+ fh.write(subs.encode('utf-8'))
+ finally:
+ fh.close()
+ player = xbmc.Player()
+ # Up to 30s to start
+ start_time = time.time()
+ while not player.isPlaying() and time.time() - start_time < 30:
+ time.sleep(1)
+ if player.isPlaying():
+ xbmc.Player().setSubtitles(subs_file);
+ else:
+ # No user message: user was probably already notified of a
problem with the stream.
+ plugin.report('Could not show subtitles: timed out waiting for
player to start.')
def navItems(self, navItems, mode):
if navItems['next']:
- self.addItem({'Title': getLS(30020), 'url':navItems['next'],
'mode':mode})
+ self.addItem(plugin.getLS(30020), mode, navItems['next'])
if navItems['previous']:
- self.addItem({'Title': getLS(30021), 'url':navItems['previous'],
'mode':mode})
+ self.addItem(plugin.getLS(30021), mode, navItems['previous'])
def showCategories(self):
- self.addItem({'Title':getLS(30001), 'mode':'newTalksRss',
'Plot':getLS(30031)})#new RSS
- self.addItem({'Title':getLS(30002), 'mode':'speakers',
'Plot':getLS(30032)})#speakers
- self.addItem({'Title':getLS(30003), 'mode':'themes',
'Plot':getLS(30033)})#themes
- #self.addItem({'Title':getLS(30004), 'mode':'search',
'Plot':getLS(30034)})#search
- if self.settings['username']:
- self.addItem({'Title':getLS(30005), 'mode':'favorites',
'Plot':getLS(30035)})#favorites
+ self.addItem(plugin.getLS(30001), 'newTalksRss',
video_info={'Plot':plugin.getLS(30031)})
+ self.addItem(plugin.getLS(30002), 'speakers',
video_info={'Plot':plugin.getLS(30032)})
+ self.addItem(plugin.getLS(30003), 'themes',
video_info={'Plot':plugin.getLS(30033)})
+ #self.addItemplugin.({'Title':getLS(30004), 'mode':'search',
'Plot':getLS(30034)})
+ if settings.username:
+ self.addItem(plugin.getLS(30005), 'favorites',
video_info={'Plot':plugin.getLS(30035)})
self.endofdirectory()
-
+
def newTalksRss(self):
- newTalks = NewTalksRss(self.logger)
+ newTalks = NewTalksRss(plugin.report)
for talk in newTalks.get_new_talks():
- li = xbmcgui.ListItem(label = talk['title'], iconImage =
talk['thumb'], thumbnailImage = talk['thumb'])
- li.setProperty("IsPlayable", "true")
- li.setInfo('video', {'date':talk['date'],
'duration':talk['duration'], 'plot':talk['plot']})
- favorites_action = None
- if self.settings['username'] != None:
- favorites_action = "add"
- context_menu = menu_util.create_context_menu(getLS = getLS, url =
talk['link'], favorites_action = favorites_action, talkID = talk['id'])
- li.addContextMenuItems(context_menu, replaceItems = True)
- xbmcplugin.addDirectoryItem(handle = int(sys.argv[1]), url =
talk['link'], listitem = li)
- self.endofdirectory(sortMethod = 'date')
-
- def speakers(self):
- newMode = 'speakerVids'
- speakers = ted_talks_scraper.Speakers(self.get_HTML,
self.args.get('url'))
- #add speakers to the list
- for speaker in speakers.getAllSpeakers():
- speaker['mode'] = newMode
- self.addItem(speaker, isFolder = True)
- #add nav items to the list
- self.navItems(speakers.navItems, self.args['mode'])
- #end the list
+ self.addItem(talk['title'], 'playVideo', talk['link'],
talk['thumb'], talk, talk['id'], isFolder=False)
+ self.endofdirectory(sortMethod='date')
+
+ def speakerGroups(self):
+ for i in range(65, 91):
+ letter = chr(i)
+ self.addItem(plugin.getLS(30006) + letter, 'speakerGroup', letter,
isFolder=True)
+ self.endofdirectory()
+
+ def speakers(self, letter):
+ speakers_generator =
Speakers(self.get_HTML).get_speakers_for_letter(letter)
+ speaker_count = itertools.islice(speakers_generator, 1).next()
+ for title, link, img in speakers_generator:
+ self.addItem(title, 'speakerVids', link, img, isFolder=True,
total_items=speaker_count)
self.endofdirectory()
- def speakerVids(self):
- newMode = 'playVideo'
- speakers = ted_talks_scraper.Speakers(self.get_HTML,
self.args.get('url'))
- for talk in speakers.getTalks():
- talk['mode'] = newMode
- self.addItem(talk, isFolder = False)
+ def speakerVids(self, url):
+ speakers = ted_talks_scraper.Speakers(self.get_HTML)
+ for title, link, img in speakers.getTalks(url):
+ self.addItem(title, 'playVideo', link, img, isFolder=False)
#end the list
self.endofdirectory()
def themes(self):
- newMode = 'themeVids'
- themes = self.ted_talks.Themes(self.get_HTML, self.args.get('url'))
+ themes = self.ted_talks.Themes(self.get_HTML, None)
#add themes to the list
- for theme in themes.getThemes():
- theme['mode'] = newMode
- self.addItem(theme, isFolder = True)
+ for title, link, img in themes.getThemes():
+ self.addItem(title, 'themeVids', link, img, isFolder=True)
#end the list
self.endofdirectory()
- def themeVids(self):
- newMode = 'playVideo'
- themes = self.ted_talks.Themes(self.get_HTML, self.args.get('url'))
- for talk in themes.getTalks():
- talk['mode'] = newMode
- self.addItem(talk, isFolder = False)
+ def themeVids(self, url):
+ themes = self.ted_talks.Themes(self.get_HTML, url)
+ for title, link, img in themes.getTalks():
+ self.addItem(title, 'playVideo', link, img, isFolder=False)
self.endofdirectory()
def favorites(self):
- newMode = 'playVideo'
#attempt to login
- userID, realname = login(self.user, self.settings['username'],
self.settings['password'])
+ userID, realname = login(self.user, settings.username,
settings.password)
if userID:
- for talk in Favorites(self.logger,
self.get_HTML).getFavoriteTalks(userID):
- talk['mode'] = newMode
- self.addItem(talk, isFolder = False)
+ for title, url, img in Favorites(plugin.report,
self.get_HTML).getFavoriteTalks(userID):
+ self.addItem(title, 'playVideo', url=url, img=img,
isFolder=False)
self.endofdirectory()
-class Main:
+class Action(object):
+ '''
+ Some action that can be executed by the user.
+ '''
- def __init__(self, logger, args_map):
+ def __init__(self, mode, required_args, logger):
+ self.mode = mode
+ self.required_args = set(required_args)
self.logger = logger
+
+ def run(self, args):
+ good = self.required_args.issubset(args.keys())
+ if good:
+ self.run_internal(args)
+ else:
+ self.report_problem(args)
+
+ def report_problem(self, args):
+ # The theory is that this might happen for a favorite from another
version;
+ # though we can't be sure about the cause hence vagueness in friendly
message.
+ friendly_message = "Action '%s' failed. Try re-creating the item." %
(self.mode)
+ self.logger("%s\nBad arguments: %s" % (friendly_message, args),
friendly_message)
+
+
+class PlayVideoAction(Action):
+
+ def __init__(self, logger, ui):
+ super(PlayVideoAction, self).__init__('playVideo', ['url', 'icon'],
logger)
+ self.ui = ui
+
+ def run_internal(self, args):
+ self.ui.playVideo(args['url'], args['icon'])
+
+
+class NewTalksAction(Action):
+
+ def __init__(self, logger, ui):
+ super(NewTalksAction, self).__init__('newTalksRss', [], logger)
+ self.ui = ui
+
+ def run_internal(self, args):
+ self.ui.newTalksRss()
+
+
+class SpeakersAction(Action):
+
+ def __init__(self, logger, ui):
+ super(SpeakersAction, self).__init__('speakers', [], logger)
+ self.ui = ui
+
+ def run_internal(self, args):
+ self.ui.speakerGroups()
+
+
+class SpeakerGroupAction(Action):
+
+ def __init__(self, logger, ui):
+ super(SpeakerGroupAction, self).__init__('speakerGroup', ['url'],
logger)
+ self.ui = ui
+
+ def run_internal(self, args):
+ self.ui.speakers(args['url'])
+
+
+class SpeakerVideosAction(Action):
+
+ def __init__(self, logger, ui):
+ super(SpeakerVideosAction, self).__init__('speakerVids', ['url'],
logger)
+ self.ui = ui
+
+ def run_internal(self, args):
+ self.ui.speakerVids(args['url'])
+
+
+class ThemesAction(Action):
+
+ def __init__(self, logger, ui):
+ super(ThemesAction, self).__init__('themes', [], logger)
+ self.ui = ui
+
+ def run_internal(self, args):
+ self.ui.themes()
+
+
+class ThemeVideosAction(Action):
+
+ def __init__(self, logger, ui):
+ super(ThemeVideosAction, self).__init__('themeVids', ['url'], logger)
+ self.ui = ui
+
+ def run_internal(self, args):
+ self.ui.themeVids(args['url'])
+
+
+class FavoritesAction(Action):
+
+ def __init__(self, logger, ui):
+ super(FavoritesAction, self).__init__('favorites', [], logger)
+ self.ui = ui
+
+ def run_internal(self, args):
+ self.ui.favorites()
+
+
+class SetFavoriteAction(Action):
+
+ def __init__(self, logger, main):
+ super(SetFavoriteAction, self).__init__('addToFavorites', ['talkID'],
logger)
+ self.main = main
+
+ def run_internal(self, args):
+ self.main.set_favorite(args['talkID'], True)
+
+
+class RemoveFavoriteAction(Action):
+
+ def __init__(self, logger, main):
+ super(RemoveFavoriteAction, self).__init__('removeFromFavorites',
['talkID'], logger)
+ self.main = main
+
+ def run_internal(self, args):
+ self.main.set_favorite(args['talkID'], False)
+
+
+class DownloadVideoAction(Action):
+
+ def __init__(self, logger, main):
+ super(DownloadVideoAction, self).__init__('downloadVideo', ['url'],
logger)
+ self.main = main
+
+ def run_internal(self, args):
+ self.main.downloadVid(args['url'], False)
+
+
+class Main:
+
+ def __init__(self, args_map):
self.args_map = args_map
- self.getSettings()
- self.get_HTML = Fetcher(logger, xbmc.translatePath).getHTML
+ self.get_HTML = Fetcher(plugin.report, xbmc.translatePath).getHTML
self.user = User(self.get_HTML)
- self.ted_talks = ted_talks_scraper.TedTalks(self.get_HTML)
-
- def getSettings(self):
- self.settings = dict()
- self.settings['username'] = __settings__.getSetting('username')
- self.settings['password'] = __settings__.getSetting('password')
- self.settings['downloadMode'] = __settings__.getSetting('downloadMode')
- self.settings['downloadPath'] = __settings__.getSetting('downloadPath')
+ self.ted_talks = ted_talks_scraper.TedTalks(self.get_HTML,
plugin.report)
def set_favorite(self, talkID, is_favorite):
"""
talkID ID for the talk.
is_favorite True to set as a favorite, False to unset.
"""
- if login(self.user, self.settings['username'],
self.settings['password']):
- favorites = Favorites(self.logger, self.fetcher.getHTML)
+ if login(self.user, settings.username, settings.password):
+ favorites = Favorites(plugin.report, self.get_HTML)
if is_favorite:
successful = favorites.addToFavorites(talkID)
else:
successful = favorites.removeFromFavorites(talkID)
notification_messages = {(True, True): 30091, (True, False):
30092, (False, True): 30094, (False, False): 30095}
notification_message = notification_messages[(is_favorite,
successful)]
- xbmc.executebuiltin('Notification(%s,%s,)' % (getLS(30000),
getLS(notification_message)))
+ xbmc.executebuiltin('Notification(%s,%s,)' % (plugin.getLS(30000),
plugin.getLS(notification_message)))
def downloadVid(self, url):
video = self.ted_talks.getVideoDetails(url)
- if self.settings['downloadMode'] == 'true':
- downloadPath = xbmcgui.Dialog().browse(3, getLS(30096), 'files')
+ if settings.download_mode == 'true':
+ downloadPath = xbmcgui.Dialog().browse(3, plugin.getLS(30096),
'files')
else:
- downloadPath = self.settings['downloadPath']
+ downloadPath = settings.download_path
if downloadPath:
- Download(video['Title'], video['url'], downloadPath)
+ Download(plugin.getLS, video['Title'], video['url'], downloadPath)
def run(self):
- # TODO Make these all modes for consistency
- if 'addToFavorites' in self.args_map:
- self.set_favorite(self.args_map['addToFavorites'], True)
- if 'removeFromFavorites' in self.args_map:
- self.set_favorite(self.args_map['removeFromFavorites'], False)
- if 'downloadVideo' in self.args_map:
- self.downloadVid(self.args_map('downloadVideo'))
-
- ui = UI(self.logger, self.get_HTML, self.ted_talks, self.user,
self.settings, self.args_map)
+ ui = UI(self.get_HTML, self.ted_talks, self.user)
if 'mode' not in self.args_map:
ui.showCategories()
else:
+ modes = [
+ PlayVideoAction(plugin.report, ui),
+ NewTalksAction(plugin.report, ui),
+ SpeakersAction(plugin.report, ui),
+ SpeakerGroupAction(plugin.report, ui),
+ SpeakerVideosAction(plugin.report, ui),
+ ThemesAction(plugin.report, ui),
+ ThemeVideosAction(plugin.report, ui),
+ FavoritesAction(plugin.report, ui),
+ SetFavoriteAction(plugin.report, self),
+ RemoveFavoriteAction(plugin.report, self),
+ DownloadVideoAction(plugin.report, self),
+ ]
+ modes = dict([(m.mode, m) for m in modes])
mode = self.args_map['mode']
- if mode == 'playVideo':
- ui.playVideo()
- elif mode == 'newTalksRss':
- ui.newTalksRss()
- elif mode == 'speakers':
- ui.speakers()
- elif mode == 'speakerVids':
- ui.speakerVids()
- elif mode == 'themes':
- ui.themes()
- elif mode == 'themeVids':
- ui.themeVids()
- elif mode == 'favorites':
- ui.favorites()
+ if mode in modes:
+ modes[mode].run(self.args_map)
+ else:
+ # Bit of a hack (cough)
+ Action(mode, [], plugin.report).report_problem(self.args_map)
diff --git a/plugin.video.ted.talks/resources/lib/ted_talks_scraper.py
b/plugin.video.ted.talks/resources/lib/ted_talks_scraper.py
index 539041d..7e016ff 100644
--- a/plugin.video.ted.talks/resources/lib/ted_talks_scraper.py
+++ b/plugin.video.ted.talks/resources/lib/ted_talks_scraper.py
@@ -2,6 +2,7 @@ import re
from model.util import cleanHTML, resizeImage
from BeautifulSoup import SoupStrainer, MinimalSoup as BeautifulSoup
from model.url_constants import URLTED
+import model.subtitles_scraper as subtitles_scraper
#MAIN URLS
URLTHEMES = 'http://www.ted.com/themes/atoz/page/'
@@ -12,13 +13,13 @@ URLSEARCH = 'http://www.ted.com/search?q=%s/page/'
def getNavItems(html):
"""self.navItems={'next':url, 'previous':url,
'selected':pagenumberasaninteger}"""
navItems = {'next':None, 'previous':None, 'selected':1}
- paginationContainer = SoupStrainer(attrs =
{'class':re.compile('pagination')})
- for aTag in BeautifulSoup(html, parseOnlyThese =
paginationContainer).findAll('a'):
+ paginationContainer =
SoupStrainer(attrs={'class':re.compile('pagination')})
+ for aTag in BeautifulSoup(html,
parseOnlyThese=paginationContainer).findAll('a'):
if aTag.has_key('class'):
if aTag['class'] == 'next':
- navItems['next'] = URLTED+aTag['href']
+ navItems['next'] = URLTED + aTag['href']
elif aTag['class'] == 'prev':
- navItems['previous'] = URLTED+aTag['href']
+ navItems['previous'] = URLTED + aTag['href']
elif aTag['class'] == 'selected':
navItems['selected'] = int(aTag.string)
return navItems
@@ -26,45 +27,26 @@ def getNavItems(html):
class Speakers:
- def __init__(self, get_HTML, url):
- # adding 9999 to the url takes the script to the very last page of the
list, providing the total # of pages.
- if url is None:
- url = URLSPEAKERS + '9999'
+ def __init__(self, get_HTML):
self.get_HTML = get_HTML
- self.html = self.get_HTML(url)
- # only bother with navItems where they have a chance to appear.
- if URLSPEAKERS in url:
- self.navItems = getNavItems(self.html)
-
- def getAllSpeakers(self):
- speakerContainers = SoupStrainer(attrs =
{'href':re.compile('/speakers/\S.+?.html')})
- pages_count = self.navItems['selected']
- for i in range(pages_count):
- # don't parse the last page twice.
- if i is not pages_count:
- html = self.get_HTML(URLSPEAKERS+str(i+1))
- else:
- html = self.html
- for speaker in BeautifulSoup(html, parseOnlyThese =
speakerContainers):
- title = speaker.string
- link = URLTED+speaker['href']
- yield {'url':link, 'Title':title}
-
- def getTalks(self):
- talkContainer = SoupStrainer(attrs = {'class':re.compile('box
clearfix')})
- for talk in BeautifulSoup(self.html, parseOnlyThese = talkContainer):
+
+ def getTalks(self, url):
+ html = self.get_HTML(url)
+ talkContainer = SoupStrainer(attrs={'class':re.compile('box
clearfix')})
+ for talk in BeautifulSoup(html, parseOnlyThese=talkContainer):
title = talk.h4.a.string
- link = URLTED+talk.dt.a['href']
- pic = resizeImage(talk.find('img', attrs =
{'src':re.compile('.+?\.jpg')})['src'])
- yield {'url':link, 'Title':title, 'Thumb':pic}
+ link = URLTED + talk.dt.a['href']
+ pic = resizeImage(talk.find('img',
attrs={'src':re.compile('.+?\.jpg')})['src'])
+ yield title, link, pic
class TedTalks:
- def __init__(self, getHTML):
+ def __init__(self, getHTML, logger):
self.getHTML = getHTML
+ self.logger = logger
- def getVideoDetails(self, url):
+ def getVideoDetails(self, url, subs_language=None):
"""self.videoDetails={Title, Director, Genre, Plot, id, url}"""
#TODO: get 'related tags' and list them under genre
html = self.getHTML(url)
@@ -93,15 +75,18 @@ class TedTalks:
# look for utub link
utublinks =
re.compile('http://(?:www.)?youtube.com/v/([^\&]*)\&').findall(html)
for link in utublinks:
- url =
'plugin://plugin.video.youtube/?action=play_video&videoid=%s' %(link)
- #get id from url
- id = url.split('/')[-1]
- return {'Title':title, 'Director':speaker, 'Genre':'TED', 'Plot':plot,
'PlotOutline':plot, 'id':id, 'url':url}
+ url =
'plugin://plugin.video.youtube/?action=play_video&videoid=%s' % (link)
+
+ subs = None
+ if subs_language:
+ subs = subtitles_scraper.get_subtitles_for_talk(soup,
subs_language, self.logger)
+
+ return title, url, subs, {'Director':speaker, 'Genre':'TED',
'Plot':plot, 'PlotOutline':plot}
class Themes:
- def __init__(self, get_HTML, url=None):
+ def __init__(self, get_HTML, url):
if url == None:
url = URLTHEMES
self.get_HTML = get_HTML
@@ -110,31 +95,31 @@ class TedTalks:
# self.navItems = TedTalks().getNavItems(html)
def getThemes(self):
- themeContainers = SoupStrainer(name = 'a', attrs =
{'href':re.compile('/themes/\S.+?.html')})
+ themeContainers = SoupStrainer(name='a',
attrs={'href':re.compile('/themes/\S.+?.html')})
seen_titles = set()
- for theme in BeautifulSoup(self.html, parseOnlyThese =
themeContainers):
+ for theme in BeautifulSoup(self.html,
parseOnlyThese=themeContainers):
if theme.img:
title = theme['title']
if title not in seen_titles:
seen_titles.add(title)
link = URLTED + theme['href']
thumb = theme.img['src']
- yield {'url':link, 'Title':title, 'Thumb':thumb}
+ yield title, link, thumb
def getTalks(self):
# themes loaded with a json call. Why are they not more consistant?
from simplejson import loads
# search HTML for the link to tedtalk's "api". It is easier to
use regex here than BS.
- jsonUrl = URLTED+re.findall('DataSource\("(.+?)"', self.html)[0]
+ jsonUrl = URLTED + re.findall('DataSource\("(.+?)"', self.html)[0]
# make a dict from the json formatted string from above url
talksMarkup = loads(self.get_HTML(jsonUrl))
# parse through said dict for all the metadata
for markup in talksMarkup['resultSet']['result']:
talk = BeautifulSoup(markup['markup'])
- link = URLTED+talk.dt.a['href']
+ link = URLTED + talk.dt.a['href']
title = cleanHTML(talk.dt.a['title'])
- pic = resizeImage(talk.find('img', attrs =
{'src':re.compile('.+?\.jpg')})['src'])
- yield {'url':link, 'Title':title, 'Thumb':pic}
+ pic = resizeImage(talk.find('img',
attrs={'src':re.compile('.+?\.jpg')})['src'])
+ yield title, link, pic
class Search:
diff --git a/plugin.video.ted.talks/resources/lib/ted_talks_scraper_test.py
b/plugin.video.ted.talks/resources/lib/ted_talks_scraper_test.py
index 25567ff..093cc21 100644
--- a/plugin.video.ted.talks/resources/lib/ted_talks_scraper_test.py
+++ b/plugin.video.ted.talks/resources/lib/ted_talks_scraper_test.py
@@ -14,37 +14,11 @@ def getHTML(url):
usock.close()
-def assertTalk(test_case, talk):
- """
- Assert the given talk has vaguely sane properties.
- """
- test_case.assertEqual(False, talk[0])
- test_case.assertEqual(4, len(talk[1]))
- test_case.assertEqual('playVideo', talk[1]['mode'])
- test_case.assertIsNotNone(talk[1]['Title'])
- test_case.assertIsNotNone(talk[1]['url'])
- test_case.assertIsNotNone(talk[1]['Thumb'])
-
-
class TestTedTalks(unittest.TestCase):
-
+
def setUp(self):
- self.ted_talks = ted_talks_scraper.TedTalks(getHTML)
+ self.ted_talks = ted_talks_scraper.TedTalks(getHTML, None)
def test_smoke(self):
self.ted_talks.getVideoDetails("http://www.ted.com/talks/ariel_garten_know_thyself_with_a_brain_scanner.html")
self.ted_talks.getVideoDetails("http://www.ted.com/talks/bjarke_ingels_hedonistic_sustainability.html")
-
-
-class TestSpeakers(unittest.TestCase):
-
- def setUp(self):
- self.speakers = ted_talks_scraper.Speakers(getHTML, None)
-
- def test_smoke(self):
- speakers = list(self.speakers.getAllSpeakers())
- # 1027 at time of writing, feel free to update
- self.assertTrue(len(speakers) >= 1027)
- # See
https://github.com/moreginger/xbmc-plugin.video.ted.talks/issues/14 for the
chosen speaker :)
- self.assertTrue('Clifford Stoll' in [s['Title'] for s in speakers])
-
diff --git a/plugin.video.ted.talks/resources/settings.xml
b/plugin.video.ted.talks/resources/settings.xml
index e1b2992..2a14429 100644
--- a/plugin.video.ted.talks/resources/settings.xml
+++ b/plugin.video.ted.talks/resources/settings.xml
@@ -1,5 +1,11 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<settings>
+ <!--Subtitles-->
+ <category label="30110">
+ <setting id="enable_subtitles" type="bool" default="true"
label="30111" />
+ <setting type="sep"/>
+ <setting id="subtitle_language" type="text" default="" label="30113"
enable="eq(-2,true)"/>
+ </category>
<!--Login-->
<category label="30075">
<setting id="username" type="text" label="30070" default="" />
http://xbmc.git.sourceforge.net/git/gitweb.cgi?p=xbmc/plugins;a=commit;h=5a6c2fc09fe7ee5a1bfd434e05b1db035588a108
-----------------------------------------------------------------------
Summary of changes:
.../LICENSE.txt | 0
plugin.video.bild_de/addon.xml | 18 +
.../changelog.txt | 0
plugin.video.bild_de/default.py | 115 +++++
plugin.video.bild_de/icon.png | Bin 0 -> 36995 bytes
.../resources/language/English/strings.xml | 8 +
.../resources/language/German/strings.xml | 8 +
plugin.video.ted.talks/README.md | 23 +
plugin.video.ted.talks/addon.xml | 14 +-
plugin.video.ted.talks/changelog.txt | 7 +-
plugin.video.ted.talks/default.py | 4 +-
.../resources/language/English/strings.xml | 8 +-
plugin.video.ted.talks/resources/lib/menu_util.py | 6 +-
.../resources/lib/menu_util_test.py | 12 +-
.../resources/lib/model/ISO-639-2_utf-8.txt | 485 ++++++++++++++++++++
.../resources/lib/model/favorites_scraper.py | 39 +-
.../resources/lib/model/favorites_scraper_test.py | 56 +++
.../resources/lib/model/fetcher.py | 12 +-
.../resources/lib/model/language_mapping.py | 26 +
.../resources/lib/model/language_mapping_test.py | 10 +
.../resources/lib/model/rss_scraper.py | 30 +--
.../resources/lib/model/rss_scraper_test.py | 7 +-
.../resources/lib/model/speakers_scraper.py | 52 +++
.../resources/lib/model/speakers_scraper_test.py | 19 +
.../resources/lib/model/subtitles_scraper.py | 101 ++++
.../resources/lib/model/subtitles_scraper_test.py | 135 ++++++
.../resources/lib/model/url_constants.py | 4 +-
.../resources/lib/model/user_test.py | 3 +-
plugin.video.ted.talks/resources/lib/model/util.py | 5 +-
.../resources/lib/model/util_test.py | 8 +
plugin.video.ted.talks/resources/lib/plugin.py | 13 +-
plugin.video.ted.talks/resources/lib/settings.py | 37 ++
.../resources/lib/settings_test.py | 31 ++
.../resources/lib/talkDownloader.py | 20 +-
plugin.video.ted.talks/resources/lib/ted_talks.py | 401 +++++++++++------
.../resources/lib/ted_talks_scraper.py | 79 ++--
.../resources/lib/ted_talks_scraper_test.py | 30 +--
plugin.video.ted.talks/resources/settings.xml | 6 +
38 files changed, 1523 insertions(+), 309 deletions(-)
copy {plugin.audio.radio_de => plugin.video.bild_de}/LICENSE.txt (100%)
create mode 100644 plugin.video.bild_de/addon.xml
copy {plugin.video.dtm_tv => plugin.video.bild_de}/changelog.txt (100%)
create mode 100644 plugin.video.bild_de/default.py
create mode 100644 plugin.video.bild_de/icon.png
create mode 100644 plugin.video.bild_de/resources/language/English/strings.xml
create mode 100644 plugin.video.bild_de/resources/language/German/strings.xml
create mode 100644 plugin.video.ted.talks/README.md
create mode 100644
plugin.video.ted.talks/resources/lib/model/ISO-639-2_utf-8.txt
create mode 100644
plugin.video.ted.talks/resources/lib/model/favorites_scraper_test.py
create mode 100644
plugin.video.ted.talks/resources/lib/model/language_mapping.py
create mode 100644
plugin.video.ted.talks/resources/lib/model/language_mapping_test.py
create mode 100644
plugin.video.ted.talks/resources/lib/model/speakers_scraper.py
create mode 100644
plugin.video.ted.talks/resources/lib/model/speakers_scraper_test.py
create mode 100644
plugin.video.ted.talks/resources/lib/model/subtitles_scraper.py
create mode 100644
plugin.video.ted.talks/resources/lib/model/subtitles_scraper_test.py
create mode 100644 plugin.video.ted.talks/resources/lib/model/util_test.py
create mode 100644 plugin.video.ted.talks/resources/lib/settings.py
create mode 100644 plugin.video.ted.talks/resources/lib/settings_test.py
hooks/post-receive
--
Plugins
------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and
threat landscape has changed and how IT managers can respond. Discussions
will include endpoint security, mobile security and the latest in malware
threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
_______________________________________________
Xbmc-addons mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/xbmc-addons