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

Reply via email to