The branch, eden has been updated
via 174a7ce5021c6b8133424570e7d53e8146275932 (commit)
via 44b8ad032ddaea135abb601fc71ad60d9208adcf (commit)
via b49eb090d0f77a2a2590f70a08560a2473987754 (commit)
from 7f83291b59ee3bef0e8421e7d494ed3be16974bf (commit)
- Log -----------------------------------------------------------------
http://xbmc.git.sourceforge.net/git/gitweb.cgi?p=xbmc/plugins;a=commit;h=174a7ce5021c6b8133424570e7d53e8146275932
commit 174a7ce5021c6b8133424570e7d53e8146275932
Author: beenje <[email protected]>
Date: Wed Jan 23 22:13:56 2013 +0100
[plugin.video.synopsi] updated to version 0.4.9
diff --git a/plugin.video.synopsi/_src/TODO b/plugin.video.synopsi/_src/TODO
index cd3c3b0..10dde4a 100644
--- a/plugin.video.synopsi/_src/TODO
+++ b/plugin.video.synopsi/_src/TODO
@@ -16,10 +16,11 @@ x video detail
x handle HACK_SHOW_ALL_LOCAL_MOVIES
x handle AuthenticationError
x remove obsolete Container.* calls
- - paskovanie k tvshows - zistit v ktorych episodach su
already-watched episody
+ x paskovanie k tvshows - zistit v ktorych seasons su
already-watched episody
+ - paskovanie k tvshows - opaskovat seasons 'on-disk' (aspon jedna
episoda)
x local movies / tvshows / episodes - paska watched nefunguje
x bug: similars v epizodach
-- "show all local movies" fails on empty list
+x "show all local movies" fails on empty list
- icon vs thhumbnail v listingoch
x 512kB hash (synopsi hash)
- cache
@@ -28,7 +29,11 @@ x 512kB hash (synopsi hash)
n corrections between episode/movie types
- possible only with scraper
- test how do the profiles work, and their options about sharing media
sources/information
-- frodo version
+
+x apiclient - send timestamp with checkin/watched
+n apiclient - update library/list method
+x remove scrobbler, move xbmc.player to main (service) thread
+x rpc listener - remove scrobbler dependency, use global player
x You need xbmc.python 2.0 for eden. See
http://wiki.xbmc.org/index.php?title=Addon.xml
diff --git a/plugin.video.synopsi/addon.xml b/plugin.video.synopsi/addon.xml
index f56ccf4..76c0793 100644
--- a/plugin.video.synopsi/addon.xml
+++ b/plugin.video.synopsi/addon.xml
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.synopsi"
name="SynopsiTV"
- version="0.4.7"
- provider-name="Synopsi s.r.o.">
+ version="0.4.9"
+ provider-name="synopsi.tv">
<requires>
<import addon="xbmc.python" version="2.0"/>
<import addon="plugin.video.youtube" version="2.9.1"/> <!-- We
play trailers through this plugin -->
diff --git a/plugin.video.synopsi/apiclient.py
b/plugin.video.synopsi/apiclient.py
index 9dc70c9..1afc276 100644
--- a/plugin.video.synopsi/apiclient.py
+++ b/plugin.video.synopsi/apiclient.py
@@ -64,7 +64,6 @@ class ApiClient(loggable.Loggable):
self._log.addHandler(logging.StreamHandler(sys.stdout))
#~ self._log.setLevel(debugLvl)
- self._log.debug('apiclient __init__ (%s, %s)' % (self.username,
self.password))
self.accessTokenTimeout = accessTokenTimeout #
[minutes] how long is stv accessToken valid ?
self.accessTokenSessionStart = None
self.failedRequest = []
@@ -215,6 +214,7 @@ class ApiClient(loggable.Loggable):
if not self.isAuthenticated():
self.getAccessToken()
+ self._log.debug('-' * 20)
url = self.apiUrl + requestData['methodPath']
method = requestData['method']
data = None
@@ -288,7 +288,7 @@ class ApiClient(loggable.Loggable):
'method': 'post',
'data': data
}
-
+
self.execute(req)
def titleIdentify(self, props=defaultIdentifyProps, **data):
diff --git a/plugin.video.synopsi/changelog.txt
b/plugin.video.synopsi/changelog.txt
index 2d2ac40..27b455b 100644
--- a/plugin.video.synopsi/changelog.txt
+++ b/plugin.video.synopsi/changelog.txt
@@ -1,5 +1,10 @@
+[B]Version 0.4.9[/B]
+- Fixed: moved scrobbler/player to main thread, removing one thread
+- Added: send timestamp for checkin
+
[B]Version 0.4.7[/B]
- Added: send checkin w/o rating for a played file stopped before 70% of
progress
+- Fixed: Empty list exception for "Local movies"
[B]Version 0.4.6[/B]
- Fixed: addon.xml to get into official repository
diff --git a/plugin.video.synopsi/dialog.py b/plugin.video.synopsi/dialog.py
index 9bbf202..0505277 100644
--- a/plugin.video.synopsi/dialog.py
+++ b/plugin.video.synopsi/dialog.py
@@ -499,7 +499,9 @@ def open_video_dialog(tpl_data, close=False):
ui = VideoDialog("VideoInfo.xml", __cwd__, "Default",
data=tpl_data)
ui.doModal()
del ui
-
+
+
+
def show_submenu(action_code, **kwargs):
try:
item_list =
top.apiClient.get_item_list(action_code=action_code, **kwargs)
@@ -509,17 +511,17 @@ def show_submenu(action_code, **kwargs):
item_list.append(item_show_all_movies_hack)
if not item_list:
- dialog_ok(exc_text_by_mode(action_code))
- return
-
+ raise ListEmptyException()
+
except AuthenticationError as e:
if dialog_check_login_correct():
show_submenu(action_code, **kwargs)
-
+
return
except ListEmptyException:
- dialog_ok(exc_text_by_mode(p['mode']))
+ dialog_ok(exc_text_by_mode(action_code))
+ return
except:
log(traceback.format_exc())
diff --git a/plugin.video.synopsi/library.py b/plugin.video.synopsi/library.py
index ad3d401..04aa195 100644
--- a/plugin.video.synopsi/library.py
+++ b/plugin.video.synopsi/library.py
@@ -1,10 +1,16 @@
+# xbmc
import xbmc, xbmcgui, xbmcaddon
-from mythread import MyThread
+
+# python standart library
import time
import socket
import json
import re
import traceback
+import top
+
+# application
+from mythread import MyThread
from utilities import *
from cache import *
@@ -13,11 +19,10 @@ ABORT_REQUESTED = False
class RPCListener(MyThread):
- def __init__(self, cache, scrobbler):
+ def __init__(self, cache):
super(RPCListener, self).__init__()
self.cache = cache
- self.scrobbler = scrobbler
self.sock = socket.socket()
self.sock.settimeout(5)
self.connected = False
@@ -96,8 +101,8 @@ class RPCListenerHandler(RPCListener):
"""
RPCListenerHandler defines event handler methods that are autotically
called from parent class's RPCListener
"""
- def __init__(self, cache, scrobbler):
- super(RPCListenerHandler, self).__init__(cache, scrobbler)
+ def __init__(self, cache):
+ super(RPCListenerHandler, self).__init__(cache)
def _xbmc_time2sec(self, time):
return time["hours"] * 3600 + time["minutes"] * 60 +
time["seconds"] + time["milliseconds"] / 1000
@@ -122,7 +127,7 @@ class RPCListenerHandler(RPCListener):
def Player_OnSeek(self, data):
position =
self._xbmc_time2sec(data['params']['data']['player']['time'])
- self.scrobbler.player.playerEventSeek(position)
+ top.player.playerEventSeek(position)
def Player_OnPause(self, data):
self.playerEvent(data)
diff --git a/plugin.video.synopsi/scrobbler.py
b/plugin.video.synopsi/scrobbler.py
index 73de05a..aa16885 100644
--- a/plugin.video.synopsi/scrobbler.py
+++ b/plugin.video.synopsi/scrobbler.py
@@ -41,7 +41,7 @@ class SynopsiPlayer(xbmc.Player):
def __init__(self):
- super(SynopsiPlayer, self).__init__()
+ super(SynopsiPlayer, self).__init__() # this will probably
not call the xbmc.Player's init, but it is ok
self.log('INIT')
self.current_time = 0
@@ -201,9 +201,15 @@ class SynopsiPlayerDecor(SynopsiPlayer):
if not detail.has_key('stvId'):
return False
- # prepare the data
+ ## prepare the data
data = { 'player_events': json.dumps(self.playerEvents) }
+
+ # prepare timestamp if avail
+ pe = self.playerEvents
+ if len(pe) > 0 and pe[0]['event'] == 'start':
+ data['timestamp'] = pe[0]['timestamp']
+ # prepare rating if requested
if rate:
rating = get_rating()
# if user rated the title
@@ -217,34 +223,3 @@ class SynopsiPlayerDecor(SynopsiPlayer):
def send_checkin(self, filename):
self.rate_file(filename, rate=False)
-
-class Scrobbler(MyThread):
- """
- Thread creates SynopsiPlayer to receive events and waits for ABORT
request.
- """
- def __init__(self, xcache):
- super(Scrobbler, self).__init__()
- self.log('Created Scrobbler thread')
- self.cache = xcache
- self.player = None
-
- def log(self, msg):
- self._log.debug('Scrobbler: ' + msg)
-
- def run(self):
- self.log('thread run start')
-
- self.player = SynopsiPlayerDecor()
- self.player.setStvList(self.cache)
-
- # wait for abort flag
- while not library.ABORT_REQUESTED and not xbmc.abortRequested:
- self.player.update_current_time()
- xbmc.sleep(500)
-
- dbg = ''
- if library.ABORT_REQUESTED: dbg += "library.ABORT_REQUESTED "
- if xbmc.abortRequested: dbg += "xbmc.abortRequested "
-
- self.log("thread run end " + dbg)
-
diff --git a/plugin.video.synopsi/service.py b/plugin.video.synopsi/service.py
index 9d3dd81..53d7e26 100644
--- a/plugin.video.synopsi/service.py
+++ b/plugin.video.synopsi/service.py
@@ -11,7 +11,7 @@ import sys
import time
# application
-from scrobbler import Scrobbler
+from scrobbler import SynopsiPlayerDecor
from library import RPCListenerHandler
from cache import *
from utilities import home_screen_fill, login_screen, log
@@ -26,7 +26,6 @@ __addon__ = get_current_addon()
__cwd__ = __addon__.getAddonInfo('path')
__addon__.setSetting('ADDON_SERVICE_FIRSTRUN', "false")
-
DEFAULT_SERVICE_PORT=int(__addon__.getSetting('ADDON_SERVICE_PORT'))
def main():
@@ -43,6 +42,10 @@ def main():
# try to restore cache
cache = StvList(iuid, apiclient1)
top.stvList = cache
+
+ top.player = SynopsiPlayerDecor()
+ top.player.setStvList(cache)
+
try:
cache.load()
@@ -57,11 +60,11 @@ def main():
thread.start_new_thread(cache_rebuild_hp_update, ())
- s = Scrobbler(cache)
- l = RPCListenerHandler(cache, s)
+ threads = []
+ l = RPCListenerHandler(cache)
+ threads.append(l)
aos = AddonService('localhost', DEFAULT_SERVICE_PORT, apiclient1, cache)
-
- threads = [s, l, aos]
+ threads.append(aos)
for t in threads:
t.start()
@@ -70,7 +73,7 @@ def main():
while True:
xbmc.sleep(500)
- if not l.isAlive() and not s.isAlive() and not aos.isAlive():
+ if not [t for t in threads if t.isAlive()]:
log('All threads are dead. Exiting loop')
break
@@ -79,6 +82,9 @@ def main():
log('waiting for: ' + str(','.join([i.name for i in
threads if i.isAlive()])))
aos.stop()
+ top.player.update_current_time()
+
+
log('Service loop END')
cache.save()
diff --git a/plugin.video.synopsi/top.py b/plugin.video.synopsi/top.py
index 19925cc..0992017 100644
--- a/plugin.video.synopsi/top.py
+++ b/plugin.video.synopsi/top.py
@@ -1,2 +1,3 @@
apiClient = None
stvList = None
+player = None
diff --git a/plugin.video.synopsi/utilities.py
b/plugin.video.synopsi/utilities.py
index 0ada8aa..04156a0 100644
--- a/plugin.video.synopsi/utilities.py
+++ b/plugin.video.synopsi/utilities.py
@@ -93,6 +93,7 @@ t_emptylist_by_mode = {
ActionCode.UnwatchedEpisodes: t_nounwatched,
ActionCode.UpcomingEpisodes: t_noupcoming,
ActionCode.LocalMovieRecco: t_nolocalrecco,
+ ActionCode.LocalMovies: t_nolocalrecco,
ActionCode.LocalTVShows: t_nolocaltvshow
}
http://xbmc.git.sourceforge.net/git/gitweb.cgi?p=xbmc/plugins;a=commit;h=44b8ad032ddaea135abb601fc71ad60d9208adcf
commit 44b8ad032ddaea135abb601fc71ad60d9208adcf
Author: beenje <[email protected]>
Date: Wed Jan 23 22:13:52 2013 +0100
[plugin.video.khanacademy] updated to version 1.5.0
diff --git a/plugin.video.khanacademy/addon.py
b/plugin.video.khanacademy/addon.py
index 0e851e8..03ee24f 100755
--- a/plugin.video.khanacademy/addon.py
+++ b/plugin.video.khanacademy/addon.py
@@ -1,105 +1,60 @@
-#!/usr/bin/env python
-# Copyright 2011 Jonathan Beluch.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import os
-import time
-try:
- import json
-except ImportError:
- import simplejson as json
-from urlparse import urljoin
-from xbmcswift import Plugin, download_page, xbmc
-from BeautifulSoup import BeautifulSoup as BS
-from resources.lib.khan import KhanData, download_playlists_json
-from resources.lib.cache import get_cached_data, put_cached_data
+'''
+ Khan Academy XBMC Addon
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ Watch videos from http://www.khanacademy.org in XBMC.
-__plugin_name__ = 'Khan Academy'
-__plugin_id__ = 'plugin.video.khanacademy'
-plugin = Plugin(__plugin_name__, __plugin_id__, __file__)
-BASE_URL = 'http://www.khanacademy.org'
+ :copyright: (c) 2013 by Jonathan Beluch
+ :license: GPLv3, see LICENSE.txt for more details.
+'''
+import xbmcswift2
+from resources.lib import khan
-# Ugly temporary hack until xbmcswift is fixed. Need to ensure the basedirs
-# for the cache already exist before we attempt to use it. Also, in pyton 2.4
-# we don't get the lovely os.makedirs :(
-def make_cache_dirs():
- '''Make plugin_id and .cache dirs for the current plugin.'''
- def make_if_not_exist(path):
- print path
- if not os.path.exists(path):
- os.mkdir(path)
- cache_root = xbmc.translatePath('special://profile/addon_data')
- make_if_not_exist(os.path.join(cache_root, __plugin_id__))
- make_if_not_exist(os.path.join(cache_root, __plugin_id__, '.cache'))
-make_cache_dirs()
-
-
-def full_url(path):
- '''Returns the full url for the given path. Uses BASE_URL.'''
- return urljoin(BASE_URL, path)
-
-
-def htmlify(url):
- '''Returns a BeautifulSoup object for a give url's response.'''
- return BS(download_page(url))
+ONE_HOUR_IN_MINUTES = 60
+YOUTUBE_URL = 'plugin://plugin.video.youtube/?action=play_video&videoid=%s'
+plugin = xbmcswift2.Plugin()
[email protected](TTL=ONE_HOUR_IN_MINUTES)
def get_khan_data():
- '''Returns a KhanData instance containg playlist data.
-
- Behind the scenes, it checks for a local cached copy first. If the cached
- copy's lifetime has not expired it will use the local copy. Otherwise, it
- will fetch fresh data from the API and cache it locally.
+ '''A wrapper method that exists to cache the results of the remote API
+ calls.
'''
- json_fn = plugin.cache_fn('playlists.json')
- timestamp_fn = plugin.cache_fn('playlists.json.ts')
-
- _json = get_cached_data(json_fn, timestamp_fn)
- if _json is None:
- _json = download_playlists_json()
- put_cached_data(json.dumps(_json), json_fn, timestamp_fn)
+ return khan.load_topic_tree()
- return KhanData(_json)
-
-KHAN_DATA = get_khan_data()
-
-
[email protected]('/')
[email protected]('/<category>/', name='show_category')
-def main_menu(category='_root'):
- '''This view displays Categories or Playlists.
-
- This method does a lookup based on the passed category/playlist name to get
- the members. The "root" or base category is name "_root"
+def get_playable_url(video):
+ '''Returns a the direct mp4 url if present otherwise returns a URL for the
+ youtube addon.
'''
- items = [item.to_listitem(plugin)
- for item in KHAN_DATA.get_items(category)]
- return plugin.add_items(items)
+ return video['mp4_url'] or YOUTUBE_URL % video['youtube_id']
+
+
+def to_listitem(item):
+ '''Converts a khan academy dict to an xbmcswift2 item dict.'''
+ if 'mp4_url' in item.keys():
+ return {
+ 'label': item['title'],
+ 'path': get_playable_url(item),
+ 'is_playable': True,
+ 'thumbnail': item['thumbnail'],
+ 'info': {
+ 'plot': item['description'],
+ },
+ }
+ else:
+ return {
+ 'label': item['title'],
+ 'path': plugin.url_for('show_topic', topic=item['id']),
+ }
[email protected]('/play/<video_slug>/')
-def play_video(video_slug):
- '''Resolves a video page's url to a playable video url.'''
- # Videos are both in .mp4 format and youtube. For simplicity's sake just
- # use mp4 for now.
- url = 'http://www.khanacademy.org/video/%s' % video_slug
- html = htmlify(url)
- a = html.find('a', {'title': 'Download this lesson'})
- plugin.set_resolved_url(a['href'])
[email protected]('/')
[email protected]('/<topic>/', name='show_topic')
+def main_menu(topic='root'):
+ '''The one and only view which displays topics hierarchically.'''
+ return [to_listitem(item) for item in get_khan_data()[topic]]
if __name__ == '__main__':
diff --git a/plugin.video.khanacademy/addon.xml
b/plugin.video.khanacademy/addon.xml
index 30a01ff..e0d6ce6 100644
--- a/plugin.video.khanacademy/addon.xml
+++ b/plugin.video.khanacademy/addon.xml
@@ -1,18 +1,17 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<addon id="plugin.video.khanacademy" name="Khan Academy" version="1.4.2"
provider-name="Jonathan Beluch (jbel)">
+<addon id="plugin.video.khanacademy" name="Khan Academy"
provider-name="Jonathan Beluch (jbel)" version="1.5.0">
<requires>
- <import addon="xbmc.python" version="2.0"/>
- <import addon="script.module.beautifulsoup" version="3.0.8"/>
- <import addon="script.module.xbmcswift" version="0.2.0"/>
- <import addon="script.module.simplejson" version="2.0.10"/>
- <import addon="plugin.video.youtube" version="2.9.1"/>
+ <import addon="xbmc.python" version="2.0" />
+ <import addon="script.module.xbmcswift2" version="1.3.1" />
+ <import addon="script.module.requests" version="0.13.2" />
+ <import addon="plugin.video.youtube" version="3.4.1" />
</requires>
- <extension point="xbmc.python.pluginsource" library="addon.py">
+ <extension library="addon.py" point="xbmc.python.pluginsource">
<provides>video</provides>
</extension>
<extension point="xbmc.addon.metadata">
+ <language />
<platform>all</platform>
<summary>View lessons from http://www.khanacademy.org</summary>
<description>From http://www.khanacademy.org/about: "The Khan Academy is
an organization on a mission. We're a not-for-profit with the goal of changing
education for the better by providing a free world-class education to anyone
anywhere.[CR][CR]All of the site's resources are available to anyone. It
doesn't matter if you are a student, teacher, home-schooler, principal, adult
returning to the classroom after 20 years, or a friendly alien just trying to
get a leg up in earthly biology. The Khan Academy's materials and resources are
available to you completely free of charge."</description>
</extension>
-</addon>
+</addon>
\ No newline at end of file
diff --git a/plugin.video.khanacademy/changelog.txt
b/plugin.video.khanacademy/changelog.txt
index 400b802..58e2935 100644
--- a/plugin.video.khanacademy/changelog.txt
+++ b/plugin.video.khanacademy/changelog.txt
@@ -1,3 +1,7 @@
+Version 1.5.0
+* Upgrade to xbmcswift2.
+* Update API interaction.
+
Version 1.4.2
* Update plugin to reflect Khan API changes
* Use youtube for playback when an mp4 isn't available
diff --git a/plugin.video.khanacademy/resources/lib/khan.py
b/plugin.video.khanacademy/resources/lib/khan.py
index cf70413..6883ddb 100644
--- a/plugin.video.khanacademy/resources/lib/khan.py
+++ b/plugin.video.khanacademy/resources/lib/khan.py
@@ -1,134 +1,95 @@
-try:
- import json
-except ImportError:
- import simplejson as json
-import time
-import urllib
-
-
-# Hierarchical list
-API_URL = 'http://www.khanacademy.org/api/v1/playlists/library'
-YOUTUBE_PLUGIN_PTN =
'plugin://plugin.video.youtube/?action=play_video&videoid=%s'
-
-
-def download_playlists_json():
- '''Fetches the playlist JSON from the Khan Academy API and returns the
- parsed JSON object.
+'''
+ resources.lib.khan
+ ~~~~~~~~~~~~~~~~~~~
+
+ This module interfaces with the remote Khan Academy API.
+
+ :copyright: (c) 2013 by Jonathan Beluch
+ :license: GPLv3, see LICENSE.txt for more details.
+'''
+
+import requests
+import collections
+
+
+API_URL = 'http://www.khanacademy.org/api/v1/topictree'
+
+
+def _video(item):
+ '''Returns a video dict of values parsed from the json dict.'''
+ return {
+ 'title': item['title'],
+ 'description': item['description'],
+ 'thumbnail': (item['download_urls'] or {}).get('png'),
+ 'youtube_id': item['youtube_id'],
+ 'mp4_url': (item['download_urls'] or {}).get('mp4'),
+ }
+
+
+def _topic(item):
+ '''Returns a topic dict of values parsed from the json dict.'''
+ return {
+ 'id': item['id'],
+ 'title': item['title'],
+ }
+
+
+_KINDS = {
+ 'Topic': _topic,
+ 'Video': _video,
+}
+
+
+def _flatten(item):
+ '''Returns a new dict which is a flattened version of the provided item.
+ The provided item can have an arbitrary depth, since each item can possibly
+ have a 'children' entry. Since all items have unique ids, this method
+ creates a flat dictionary, where the key is each item's unique id and the
+ value is the item's children if present.
+
+ >>> item = {
+ ... 'id': 'root',
+ ... 'children': [
+ ... {
+ ... 'id': 'Algebra',
+ ... 'children': [
+ ... { 'id': 'Lesson 1'},
+ ... { 'id': 'Lesson 2'},
+ ... ],
+ ... },
+ ... {
+ ... 'id': 'Calculus',
+ ... },
+ ... ]
+ ... }
+ >>> _flatten(item).keys()
+ ['root', 'Algebra']
'''
- conn = urllib.urlopen(API_URL)
- resp = json.load(conn)
- conn.close()
- return resp
-
-
-def _try_parse(_json, *args):
- '''Returns the parsed value or None if doesn't exist. The provided args
- correspond to dictionary keys.
-
- >>> _json = {'foo': {'bar': 'baz'}}
- >>> _try_parse(_json, 'foo', 'bar')
- baz
- >>> _try_parse(_json, 'foo', 'missingkey')
- None
+ tree = collections.defaultdict(list)
+ queue = [(child, 'root') for child in item['children']]
+ while queue:
+ item, parent_id = queue.pop(0)
+ tree[parent_id].append(item)
+ if 'children' in item.keys():
+ current_id = item['id']
+ queue.extend((child, current_id) for child in item['children'])
+ return tree
+
+
+def load_topic_tree():
+ '''The main entry point for this module. Returns a dict keyed by Topic id.
+ The value of the dict is the topic's children which are either topic dicts
+ or video dicts. To hierarchichally descend through topics, start with the
+ key 'root', and then look up each child topic's 'id' in this tree to get
+ its children.
'''
- try:
- for key in args:
- _json = _json[key]
- return _json
- except TypeError:
- return None
-
-
-class Video(object):
-
- def __init__(self, _json):
- #self.key_id = _json['key_id']
- self.title = _json['title']
- self.readable_id = _json['readable_id']
- self.youtube_url = YOUTUBE_PLUGIN_PTN % _json['youtube_id']
- self.mp4_url = _try_parse(_json, 'download_urls', 'mp4')
- self.thumbnail = _try_parse(_json, 'download_urls', 'png')
-
-
- def to_listitem(self, plugin):
- '''Returns a dict suitable for passing to xbmcswift.plugin.add_items'''
- item = {
- 'label': self.title,
- 'url': self.mp4_url or self.youtube_url,
- 'is_playable': True,
- 'is_folder': False,
- }
- if self.thumbnail:
- item['thumbnail'] = self.thumbnail
- return item
+ # Eden has older version of requests where json is an attr not a method
+ _json = requests.get(API_URL).json
+ flattened = _flatten(_json)
+ tree = {}
+ for item_id, children in flattened.items():
+ tree[item_id] = [_KINDS[child['kind']](child) for child in children
+ if child['kind'] in _KINDS.keys()]
-class Playlist(object):
-
- def __init__(self, _json):
- self.title = _json['title']
- self.description = _json['description']
- self.videos = [Video(item) for item in _json['videos']]
-
- def __iter__(self):
- return iter(self.videos)
-
- def to_listitem(self, plugin):
- '''Returns a dict suitable for passing to xbmcswift.plugin.add_items'''
- return {
- 'label': self.title,
- 'url': plugin.url_for('show_category', category=self.title),
- 'info': {'plot': self.description},
- }
-
-
-class Category(object):
-
- def __init__(self, _json):
- self.name = _json['name']
-
- def to_listitem(self, plugin):
- '''Returns a dict suitable for passing to xbmcswift.plugin.add_items'''
- return {
- 'label': self.name,
- 'url': plugin.url_for('show_category', category=self.name),
- }
-
-
-class KhanData(object):
- '''This class repurposes the Khan Academy playlist JSON into more
- convenient data structures.
- '''
-
- def __init__(self, _json):
- self._items = {}
- self._parse(_json, '_root')
-
- def _is_playlist(self, item):
- return 'playlist' in item.keys()
-
- def _create_keys(self, key):
- if key not in self._items.keys():
- self._items[key] = []
-
- def _parse(self, _json, current_key):
- '''Recursive parsing function to format the _json input in a single
- dict keyable on item['name']
- '''
- self._create_keys(current_key)
-
- for item in _json:
- if self._is_playlist(item):
- playlist = Playlist(item['playlist'])
- self._items[current_key].append(playlist)
- self._items[item['name']] = playlist
- else:
- category = Category(item)
- self._items[current_key].append(category)
- self._parse(item['items'], item['name'])
-
- def get_items(self, key):
- '''For a given key, returns a list of items (Category or Playlist) or
- if the key is a playlist name, returns a Playlist object.
- '''
- return self._items[key]
+ return tree
http://xbmc.git.sourceforge.net/git/gitweb.cgi?p=xbmc/plugins;a=commit;h=b49eb090d0f77a2a2590f70a08560a2473987754
commit b49eb090d0f77a2a2590f70a08560a2473987754
Author: beenje <[email protected]>
Date: Wed Jan 23 22:13:48 2013 +0100
[plugin.video.ted.talks] updated to version 3.1.7
diff --git a/plugin.video.ted.talks/addon.xml b/plugin.video.ted.talks/addon.xml
index 1d81401..198d745 100644
--- a/plugin.video.ted.talks/addon.xml
+++ b/plugin.video.ted.talks/addon.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<addon id="plugin.video.ted.talks" name="TED Talks" version="3.1.6"
provider-name="rwparris2, moreginger">
+<addon id="plugin.video.ted.talks" name="TED Talks" version="3.1.7"
provider-name="rwparris2, moreginger">
<requires>
<import addon="xbmc.python" version="2.0"/>
<import addon="script.module.simplejson" version="2.0.10"/>
diff --git a/plugin.video.ted.talks/changelog.txt
b/plugin.video.ted.talks/changelog.txt
index 8e3c822..f398be7 100644
--- a/plugin.video.ted.talks/changelog.txt
+++ b/plugin.video.ted.talks/changelog.txt
@@ -1,3 +1,6 @@
+[B]Version 3.1.7[/B]
+Fix subtitles (issue#35) thanks to omega32
+
[B]Version 3.1.6[/B]
Fix getting subtitles using default xbmc language (issue#30)
diff --git a/plugin.video.ted.talks/resources/lib/model/subtitles_scraper.py
b/plugin.video.ted.talks/resources/lib/model/subtitles_scraper.py
index c2b8aa4..efee063 100644
--- a/plugin.video.ted.talks/resources/lib/model/subtitles_scraper.py
+++ b/plugin.video.ted.talks/resources/lib/model/subtitles_scraper.py
@@ -9,7 +9,7 @@ import urllib
import re
__friendly_message__ = 'Error showing subtitles'
-__talkIdKey__ = 'talkId'
+__talkIdKey__ = 'ti'
__introDurationKey__ = 'introDuration'
def format_time(time):
@@ -53,15 +53,28 @@ def get_flashvars(soup):
if not flashvar_match:
raise Exception('Could not get flashVars')
- talkId_re = re.compile('"%s":(\d+)' % (__talkIdKey__))
- introDuration_re = re.compile('"%s":(\d+)' % (__introDurationKey__))
+ talkId_re = re.compile('"?%s"?:"?(\d+)"?' % (__talkIdKey__))
flashvars = urllib.unquote(flashvar_match.group(1).encode('ascii'))
talkId_match = talkId_re.search(flashvars)
if not talkId_match:
raise Exception('Could not get talk ID')
- introDuration_match = introDuration_re.search(flashvars)
+ input_tag2 = soup.find('script', type='text/javascript',
text=re.compile('var talkDetails'))
+ if not input_tag2:
+ raise Exception('Could not find the talkDetails container')
+
+ talkDetails_re = re.compile('var talkDetails = (\{.*\})');
+ talkDetails_match = talkDetails_re.search(input_tag2.string)
+
+ if not talkDetails_match:
+ raise Exception('Could not get talkDetails')
+
+ talkDetails = urllib.unquote(talkDetails_match.group(1).encode('ascii'))
+
+ introDuration_re = re.compile('"%s":(\d+)' % (__introDurationKey__))
+ introDuration_match = introDuration_re.search(talkDetails)
+
if not introDuration_match:
raise Exception('Could not get intro duration')
diff --git
a/plugin.video.ted.talks/resources/lib/model/subtitles_scraper_test.py
b/plugin.video.ted.talks/resources/lib/model/subtitles_scraper_test.py
index 5af783c..12a7416 100644
--- a/plugin.video.ted.talks/resources/lib/model/subtitles_scraper_test.py
+++ b/plugin.video.ted.talks/resources/lib/model/subtitles_scraper_test.py
@@ -49,7 +49,7 @@ World
self.assertEquals('15', flashvars['introDuration'])
# Talk ID, need this to request subtitles.
- self.assertEquals('1253', flashvars['talkId'])
+ self.assertEquals('1253', flashvars['ti'])
expected = set(['sq', 'ar', 'hy', 'bg', 'ca', 'zh-cn', 'zh-tw', 'hr',
'cs', 'da', 'nl', 'en', 'fr', 'ka', 'de', 'el', 'he', 'hu', 'id', 'it', 'ja',
'ko', 'fa', 'mk', 'pl', 'pt', 'pt-br', 'ro', 'ru', 'sr', 'sk', 'es', 'th',
'tr', 'uk', 'vi'])
self.assertEquals(expected, set(subtitles_scraper.get_languages(soup)))
diff --git a/plugin.video.ted.talks/resources/lib/model/user.py
b/plugin.video.ted.talks/resources/lib/model/user.py
index 60a9092..07e5ead 100644
--- a/plugin.video.ted.talks/resources/lib/model/user.py
+++ b/plugin.video.ted.talks/resources/lib/model/user.py
@@ -44,9 +44,9 @@ class User:
finally:
response.close()
#set username & password in the sign in form
- form = forms[1]
- form["users[username]"] = username
- form["users[password]"] = password
- form["users[rememberme]"] = ["on"]
+ form = forms[0]
+ form["user[email]"] = username
+ form["user[password]"] = password
+ form.find_control(name="user[remember_me]", type="checkbox",
id="user_remember_me").selected = True
#click submit
return self.get_HTML(form.click())
-----------------------------------------------------------------------
Summary of changes:
plugin.video.khanacademy/addon.py | 133 ++++--------
plugin.video.khanacademy/addon.xml | 17 +-
plugin.video.khanacademy/changelog.txt | 4 +
plugin.video.khanacademy/requirements.txt | 3 +
plugin.video.khanacademy/resources/lib/cache.py | 46 ----
plugin.video.khanacademy/resources/lib/khan.py | 221 ++++++++------------
plugin.video.synopsi/_src/TODO | 11 +-
plugin.video.synopsi/addon.xml | 4 +-
plugin.video.synopsi/apiclient.py | 4 +-
plugin.video.synopsi/changelog.txt | 5 +
plugin.video.synopsi/dialog.py | 14 +-
plugin.video.synopsi/library.py | 17 +-
plugin.video.synopsi/scrobbler.py | 41 +---
plugin.video.synopsi/service.py | 20 ++-
plugin.video.synopsi/top.py | 1 +
plugin.video.synopsi/utilities.py | 1 +
plugin.video.ted.talks/addon.xml | 2 +-
plugin.video.ted.talks/changelog.txt | 3 +
.../resources/lib/model/subtitles_scraper.py | 21 ++-
.../resources/lib/model/subtitles_scraper_test.py | 2 +-
plugin.video.ted.talks/resources/lib/model/user.py | 8 +-
21 files changed, 235 insertions(+), 343 deletions(-)
create mode 100644 plugin.video.khanacademy/requirements.txt
delete mode 100644 plugin.video.khanacademy/resources/lib/cache.py
hooks/post-receive
--
Plugins
------------------------------------------------------------------------------
Master Visual Studio, SharePoint, SQL, ASP.NET, C# 2012, HTML5, CSS,
MVC, Windows 8 Apps, JavaScript and much more. Keep your skills current
with LearnDevNow - 3,200 step-by-step video tutorials by Microsoft
MVPs and experts. ON SALE this month only -- learn more at:
http://p.sf.net/sfu/learnnow-d2d
_______________________________________________
Xbmc-addons mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/xbmc-addons