The branch, eden has been updated
       via  f78acccc9af85eba80a61bd08221a4897b5359de (commit)
       via  ccbfbf609d0be00e704a171b2d9863d37bb07b66 (commit)
      from  a4bb2095355917ba2971d20e9b3cf04e8427ecbd (commit)

- Log -----------------------------------------------------------------
http://xbmc.git.sourceforge.net/git/gitweb.cgi?p=xbmc/plugins;a=commit;h=f78acccc9af85eba80a61bd08221a4897b5359de

commit f78acccc9af85eba80a61bd08221a4897b5359de
Author: beenje <[email protected]>
Date:   Wed Feb 20 21:40:00 2013 +0100

    [plugin.video.rbk.no] updated to version 2.0.9

diff --git a/plugin.video.rbk.no/addon.xml b/plugin.video.rbk.no/addon.xml
index e7327cf..48f49b9 100644
--- a/plugin.video.rbk.no/addon.xml
+++ b/plugin.video.rbk.no/addon.xml
@@ -1,12 +1,11 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <addon id="plugin.video.rbk.no"
        name="RBK.no"
-       version="2.0.8"
+       version="2.0.9"
        provider-name="spiff">
   <requires>
     <import addon="xbmc.python" version="2.0"/>
     <import addon="script.module.beautifulsoup" version="3.0.8"/>
-    <import addon="plugin.video.youtube" version="2.0.5"/>
   </requires>
   <extension point="xbmc.python.pluginsource"
              provides="video"
diff --git a/plugin.video.rbk.no/changelog.txt 
b/plugin.video.rbk.no/changelog.txt
index ea934e7..4e82a7f 100644
--- a/plugin.video.rbk.no/changelog.txt
+++ b/plugin.video.rbk.no/changelog.txt
@@ -1,3 +1,6 @@
+[B]2.0.9:[/B]
+Update for site changes
+
 [B]2.0.8:[/B]
 Update for site changes. Lacks pagination support due to javascript
 
diff --git a/plugin.video.rbk.no/default.py b/plugin.video.rbk.no/default.py
index f638c20..797fd4d 100644
--- a/plugin.video.rbk.no/default.py
+++ b/plugin.video.rbk.no/default.py
@@ -27,12 +27,28 @@ def INDEX():
   response = urllib2.urlopen(req)
   link=response.read()
   response.close()
-  links = string.split(link,'<div class="vodList"')[1].split('<div 
class="vodItems"')
+  links = string.split(link,'<div class="vodList')[1].split('<div 
class="vodItems')
   del links[0]
   for link in links:
     url = 'http://www.rbk.no'+link.split('<a href="')[1].split('"')[0]
-    name = link.split('<a href="')[1].split('>')[1].split('<')[0].strip()
+    name = link.split('<a href="')[2].split('>')[1].split('<')[0].strip()
+    datetab = {" januar "    : ".01.",
+               " februar "   : ".02.",
+               " mars "      : ".03.",
+               " april "     : ".04.",
+               " mai "       : ".05.",
+               " juni "      : ".06.",
+               " juli "      : ".07.",
+               " august "    : ".08.",
+               " september " : ".09.",
+               " oktober"    : ".10.",
+               " november "  : ".11.",
+               " desember "  : ".12."}
     date = link.split('<div class="vodItemDate">')[1].split('<')[0].strip()
+    pattern = re.compile('(' + '|'.join(datetab.keys()) + ')')
+    date = pattern.sub(lambda x: datetab[x.group()], date)
+    if date[2] is not '.':
+      date = '0'+date
     thumb = 'http://www.rbk.no'+link.split('<img src="')[1].split('"')[0]
     addLink(name,url,thumb,date)
 
@@ -97,4 +113,6 @@ if len(url) > 0:
 else:
   INDEX()
   xbmcplugin.addSortMethod(int(sys.argv[1]),xbmcplugin.SORT_METHOD_DATE)
+  xbmcplugin.addSortMethod(int(sys.argv[1]),xbmcplugin.SORT_METHOD_LABEL)
+  xbmcplugin.addSortMethod(int(sys.argv[1]),xbmcplugin.SORT_METHOD_UNSORTED)
   xbmcplugin.endOfDirectory(int(sys.argv[1]))

http://xbmc.git.sourceforge.net/git/gitweb.cgi?p=xbmc/plugins;a=commit;h=ccbfbf609d0be00e704a171b2d9863d37bb07b66

commit ccbfbf609d0be00e704a171b2d9863d37bb07b66
Author: beenje <[email protected]>
Date:   Wed Feb 20 21:39:45 2013 +0100

    [plugin.video.synopsi] updated to version 0.5.1

diff --git a/plugin.video.synopsi/addon.py b/plugin.video.synopsi/addon.py
index e1b007c..739d226 100644
--- a/plugin.video.synopsi/addon.py
+++ b/plugin.video.synopsi/addon.py
@@ -29,6 +29,7 @@ import threading
 from utilities import *
 from cache import StvList
 from xbmcrpc import xbmc_rpc
+import resources.const as const
 
 threading.current_thread().name = 'addon.py'
 
@@ -49,7 +50,8 @@ class AddonClient(object):
                try:
                        json_data = {
                                'command': command,
-                               'arguments': arguments
+                               'arguments': arguments,
+                               'iface_version': const.SERVICE_IFACE_VERSION
                        }
 
                        response = None
@@ -71,7 +73,17 @@ class AddonClient(object):
                        response_json = json.loads(response)
                        #~ xbmc.log('CLIENT / JSON RESPONSE / ' + 
dump(response))
 
+                       # handle exceptions
+                       if response_json.get('exception'):
+                               exc = response_json['exception']
+                               if exc['type'] == 'VersionMismatch':
+                                       if 
dialog_need_restart(t_needrestart_update):
+                                               raise 
ShutdownRequestedException('User requested shutdown')
+                                       
+
                # TODO: some handling
+               except ShutdownRequestedException, e:
+                       raise
                except:
                        xbmc.log('CLIENT / ERROR / RESPONSE ' + str(response))
                        #~ raise
@@ -137,6 +149,7 @@ class AddonClient(object):
 try:
        dirhandle = int(sys.argv[1])
 
+       log('SYNOPSI ADDON (%s) START' % VERSION)
        log('SYS ARGV:' + str(sys.argv))
 
        url_parsed = urlparse.urlparse(sys.argv[2])
@@ -255,7 +268,7 @@ try:
         }
         ]
 
-               dialog.open_list_dialog({ 'items': items }, close=True)
+               dialog.open_list_dialog({ 'items': items }, close=False)
        
 
        elif p['mode']==972:
@@ -273,6 +286,7 @@ try:
 
 
        elif p['mode']==973:
+               #~ addonclient.show_video_dialog_byId(2406418)
                addonclient.debug_3()
        else:
                raise UnknownModeException('Unknown mode: %s' % p['mode'])
diff --git a/plugin.video.synopsi/addon.xml b/plugin.video.synopsi/addon.xml
index 5046d31..38fadf6 100644
--- a/plugin.video.synopsi/addon.xml
+++ b/plugin.video.synopsi/addon.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <addon id="plugin.video.synopsi"
        name="SynopsiTV"
-       version="0.5.0"
+       version="0.5.1"
        provider-name="Synopsi.TV">
        <requires>
                <import addon="xbmc.python" version="2.0"/>
diff --git a/plugin.video.synopsi/addonservice.py 
b/plugin.video.synopsi/addonservice.py
index e244759..2a6d0a0 100644
--- a/plugin.video.synopsi/addonservice.py
+++ b/plugin.video.synopsi/addonservice.py
@@ -7,6 +7,7 @@ import thread
 # application
 from utilities import *
 import dialog
+import resources.const as const
 
 class ServiceTCPHandler(SocketServer.StreamRequestHandler):
        def __init__(self, *args, **kwargs):
@@ -25,6 +26,14 @@ class ServiceTCPHandler(SocketServer.StreamRequestHandler):
                        return
 
                try:
+                       # check interface version
+                       iface_version = json_data['iface_version']
+                       if iface_version != const.SERVICE_IFACE_VERSION:
+                               exc = { 'type': 'VersionMismatch',
+                                               'message': 'plugin version (%s) 
doesn\'t match service version (%s). Please restart service' % (iface_version, 
const.SERVICE_IFACE_VERSION) }
+                               self.wfile.write(json.dumps({'exception': exc}))
+                               return
+                       
                        # handle requested method
                        methodName = json_data['command']
                        arguments = json_data.get('arguments', {})
@@ -33,10 +42,10 @@ class ServiceTCPHandler(SocketServer.StreamRequestHandler):
                        result = method(**arguments)
 
                        # convert non-string result to json string
-                       if not isinstance(result, str):
-                               result = json.dumps(result)
-                       elif not result:
+                       if result == None:
                                result = '{}'
+                       elif not isinstance(result, str):
+                               result = json.dumps(result)
 
                        self.server._log.debug('RESULT: ' + result)
 
@@ -100,10 +109,10 @@ class AddonHandler(ServiceTCPHandler):
                thread.start_new_thread(dialog.show_submenu, (), kwargs)
                                
        def show_video_dialog(self, json_data):
-               thread.start_new_thread(dialog.show_video_dialog, (json_data))
+               thread.start_new_thread(dialog.show_video_dialog, (json_data, ))
 
        def show_video_dialog_byId(self, stv_id):
-               thread.start_new_thread(dialog.show_video_dialog_byId, (stv_id))
+               thread.start_new_thread(dialog.show_video_dialog_byId, (stv_id, 
))
 
        def open_settings(self):
                __addon__ = get_current_addon()
diff --git a/plugin.video.synopsi/apiclient.py 
b/plugin.video.synopsi/apiclient.py
index 26be6f3..7f92119 100644
--- a/plugin.video.synopsi/apiclient.py
+++ b/plugin.video.synopsi/apiclient.py
@@ -14,6 +14,8 @@ import httplib
 # application
 from utilities import *
 import loggable
+import resources.const as const
+
 
 RATING_CODE = {
        1: 'like',
@@ -81,14 +83,14 @@ class ApiClient(loggable.Loggable):
 
                # get or generate install-unique ID
                ApiClient._instance = cls(
-                       __addon__.getSetting('BASE_URL'),
-                       __addon__.getSetting('KEY'),
-                       __addon__.getSetting('SECRET'),
+                       const.BASE_URL,
+                       const.KEY,
+                       const.SECRET,
                        __addon__.getSetting('USER'),
                        __addon__.getSetting('PASS'),
                        iuid,
                        debugLvl=logging.ERROR,
-                       rel_api_url=__addon__.getSetting('REL_API_URL'),
+                       rel_api_url=const.REL_API_URL
                )
 
                return ApiClient._instance
diff --git a/plugin.video.synopsi/cache.py b/plugin.video.synopsi/cache.py
index a275fd9..81d56f8 100644
--- a/plugin.video.synopsi/cache.py
+++ b/plugin.video.synopsi/cache.py
@@ -98,7 +98,7 @@ class OfflineStvList(object):
        def addorupdate(self, atype, aid):
                if not atype in playable_types:
                        return
-
+               
                # find out actual data about movie
                movie = xbmc_rpc.get_details(atype, aid)
                movie['type'] = atype
@@ -111,7 +111,7 @@ class OfflineStvList(object):
                        movie['stv_title_hash'] = stv_hash(path)
                        movie['os_title_hash'] = hash_opensubtitle(path)
 
-
+                       
                        # TODO: stv_subtitle_hash - hash of the subtitle file 
if present
                        ident = {}
                        self._translate_xbmc2stv_keys(ident, movie)
@@ -191,6 +191,7 @@ class OfflineStvList(object):
                                                                
 
        def update(self, item):
+               changed_keys = []
                typeIdStr = self._getKey(item['type'], item['id'])
                cacheItem = self.byTypeId[typeIdStr]
 
@@ -201,8 +202,10 @@ class OfflineStvList(object):
                        if not cacheItem.has_key(key) or not item[key] == 
cacheItem[key]:
                                updateStr += key + ': ' + 
str(getattr(cacheItem, key, None)) + ' -> ' + str(item[key]) + ' | '
                                cacheItem[key] = item[key]
+                               changed_keys.append(key)
 
                self.log('UPDATE / ' + typeIdStr + ' / ' + updateStr)
+               return (cacheItem, changed_keys)
 
        def remove(self, atype, aid):
                typeIdStr = self._getKey(atype, aid)
@@ -427,7 +430,8 @@ class OnlineStvList(OfflineStvList):
        def __init__(self, uuid, apiclient, filePath=None):
                super(OnlineStvList, self).__init__(uuid, filePath)
                self.apiClient = apiclient
-
+               self._block_rating = None
+               
        @classmethod
        def getDefaultList(cls, apiClient=None):
                if cls._instance:
@@ -448,7 +452,17 @@ class OnlineStvList(OfflineStvList):
                # if known by synopsi, add to list
                if item.has_key('stvId'):
                        self.apiClient.libraryTitleAdd(item['stvId'])
+                       # if already watched, check-in to title
+                       if item.get('lastplayed'):
+                               self.apiClient.titleWatched(item['stvId'], 
created_time=item.get('lastplayed'))
 
+       def update(self, item):
+               cacheItem, changed_keys = OfflineStvList.update(self, item)
+               
+               # update lastplayed only if it is not in the rating process, 
and it is not the 'unwatched' action
+               if 'lastplayed' in changed_keys and cacheItem.get('lastplayed') 
and not self.getBlockEvents(item['type'], item['id']):
+                       self.apiClient.titleWatched(cacheItem['stvId'], 
created_time=cacheItem.get('lastplayed'))
+               
        def remove(self, atype, aid):
                if self.hasTypeId(atype, aid):
                        item = self.getByTypeId(atype, aid)
@@ -466,6 +480,19 @@ class OnlineStvList(OfflineStvList):
 
                return new_item
 
+       def setBlockEvents(self, atype, aid):
+               self._block_rating = (atype, aid)
+
+       def resetBlockEvents(self):
+               self._block_rating = None
+               
+       def getBlockEvents(self, atype, aid):
+               if self._block_rating == (atype, aid):
+                       return True
+               
+               return False
+
+
 class AppStvList(OnlineStvList):
        def get_local_tvshows(self):
                local_tvshows = self.getAllByType('tvshow')
diff --git a/plugin.video.synopsi/changelog.txt 
b/plugin.video.synopsi/changelog.txt
index 0416ec2..d547882 100644
--- a/plugin.video.synopsi/changelog.txt
+++ b/plugin.video.synopsi/changelog.txt
@@ -1,3 +1,13 @@
+[B]Version 0.5.1 [/B]
+
+- Fixed: labels in movie details
+- Fixed: dialog navigation, back button, hide dialogs on movie play
+- Added: mark as already watched when adding files to library
+- Added: mark as watched from context menu is properly handled
+- Fixed: bug with movie time evaluation
+- Fixed: dialogs and windows are now opened immediately after user's requests, 
data are filled in afterwards
+- Fixed: few minor bugs
+
 [B]Version 0.5.0 [/B]
 
 - Added: send software info on checkin
diff --git a/plugin.video.synopsi/dialog.py b/plugin.video.synopsi/dialog.py
index c7a4e26..60959c1 100644
--- a/plugin.video.synopsi/dialog.py
+++ b/plugin.video.synopsi/dialog.py
@@ -18,6 +18,7 @@ from utilities import *
 from app_apiclient import AppApiClient, AuthenticationError
 from cache import StvList, DuplicateStvIdException
 import top
+from threading import Thread
 
 ACTIONS_CLICK = [7, 100]
 LIST_ITEM_CONTROL_ID = 500
@@ -36,11 +37,13 @@ __profile__      = __addon__.getAddonInfo('profile')
 
 itemFolderBack = {'name': '...', 'cover_medium': 'DefaultFolderBack.png', 
'id': HACK_GO_BACK, 'type': 'HACK'}
 
-opendialogs = []
+open_dialogs = []
+closed_dialogs = []
+stashed_dialogs = []
 
 def get_current_dialog():
-       if opendialogs:
-               return opendialogs[-1]
+       if open_dialogs:
+               return open_dialogs[-1]
        else:
                return None
 
@@ -49,33 +52,92 @@ def close_current_dialog():
        if d:
                d.close()
 
+       return d
+
 def close_all_dialogs():
-       for d in opendialogs:
-               d.close()
-               del d
+       while 1:
+               d = close_current_dialog()
+               if not d:
+                       break
+
+def stash_all_dialogs():
+       while 1:
+               d = close_current_dialog()
+               if not d:
+                       break
+
+               stashed_dialogs.append(d)
+
+def unstash_all_dialogs():
+       log('stashed_dialogs:' + str(stashed_dialogs))
+       for d in stashed_dialogs:
+               open_dialogs.append(d)
+               d.doModal()
+
+def open_dialog(dialogClass, xmlFile, tpl_data, close=False):          
+       if close:
+               close_current_dialog()
+                       
+       ui = dialogClass(xmlFile, __cwd__, "Default", data=tpl_data)
+       ui.doModal()
+               
+       result = ui.result
+       return result   
+
+class MyDialog(xbmcgui.WindowXMLDialog):
+       def __init__(self):
+               self.parentWindow = open_dialogs[-1] if open_dialogs else None
+               open_dialogs.append(self)
+               self.result = None
+       
+       def close(self):
+               # check if closing the currently opened dialog
+               if open_dialogs[-1] != self:
+                       log('WARNING: Dialog queue inconsistency. Non-top 
dialog close')
 
-class ListDialog(xbmcgui.WindowXMLDialog):
-       """ Dialog for choosing movie corrections """
-       def __init__(self, *args, **kwargs):
+               xbmcgui.WindowXMLDialog.close(self)
+               open_dialogs.remove(self)
+       
+class ListDialog(MyDialog):
+       """ Dialog for synopsi listings with custom cover overlays """
+       def __init__(self, strXMLname, strFallbackPath, strDefaultName, 
**kwargs):
+               super(ListDialog, self).__init__()
                self.data = kwargs['data']
+
+               if kwargs['data'].has_key('_async_init'):
+                       self._async_init = kwargs['data']['_async_init']
+                       
                self.controlId = None
                self.selectedMovie = None
-               opendialogs.append(self)
+               self.listControl = None
 
        def onInit(self):
-               self.updateItems()
+               self.listControl = self.getControl(LIST_ITEM_CONTROL_ID)
+               self.listControl.reset()
                
+               if self.__dict__.get('_async_init'):
+                       result = {}
+                       kwargs = self._async_init.get('kwargs', {})
+                       kwargs['result'] = result
+                       try:
+                               self._async_init['method'](**kwargs)
+                       except (AuthenticationError, ListEmptyException) as e:
+                               self.close()
+                               return
+                               
+                       self.data = result
+                       
+               self.updateItems()
+                               
        def updateItems(self):
                items = []
                items.append(self._getListItem(itemFolderBack))
                for item in self.data['items']:
                        li = self._getListItem(item)
                        items.append(li)
-
                try:
-                       listControl = self.getControl(LIST_ITEM_CONTROL_ID)
-                       listControl.addItems(items)
-                       self.setFocus(listControl)
+                       self.listControl.addItems(items)
+                       self.setFocus(self.listControl)
                except:
                        log('Adding items failed')
        
@@ -137,55 +199,86 @@ class ListDialog(xbmcgui.WindowXMLDialog):
                        else:
                                show_video_dialog({'type': 
item.getProperty('type'), 'id': stv_id}, close=False)
 
-       def close(self):                
-               # check if closing the currently opened dialog
-               if opendialogs[-1] != self:
-                       log('WARNING: Dialog queue inconsistency. Non-top 
dialog close')
-                       
-               opendialogs.remove(self)
-               xbmcgui.WindowXMLDialog.close(self)
-
                
 
-def open_list_dialog(tpl_data, close=True):
-       #~ path = '/home/smid/projects/XBMC/resources/skins/Default/720p/'
-       path = ''
-       
-       try:
-               win = xbmcgui.Window(xbmcgui.getCurrentWindowDialogId())
-       except ValueError, e:
-               log('Window ValueError')
-               ui = ListDialog(path + "custom_MyVideoNav.xml", __addonpath__, 
"Default", data=tpl_data)
-               ui.doModal()
-               del ui
-       else:
-               win = xbmcgui.WindowDialog(xbmcgui.getCurrentWindowDialogId())
-               if close:
-                       close_current_dialog()
-               ui = ListDialog(path + "custom_MyVideoNav.xml", __addonpath__, 
"Default", data=tpl_data)
-               ui.doModal()
-               del ui
+def open_list_dialog(tpl_data, close=False):
+       open_dialog(ListDialog, "custom_MyVideoNav.xml", tpl_data, close)
 
 def show_movie_list(item_list):
        open_list_dialog({ 'items': item_list })
 
-
 def show_tvshows_episodes(stv_id):
-       items = top.apiClient.get_tvshow_season(stv_id)
-       open_list_dialog({'items': items })
+       def init_data(result, **kwargs):
+               log('asyn handler show_tvshows_episodes: ' + str(kwargs))
+               result['items'] = top.apiClient.get_tvshow_season(stv_id)
+       
+       tpl_data = { '_async_init': { 'method': init_data, 'kwargs': {} }}
 
+       open_list_dialog(tpl_data)
 
-class VideoDialog(xbmcgui.WindowXMLDialog):
+
+class VideoDialog(MyDialog):
        """
        Dialog about video information.
        """
        def __init__(self, *args, **kwargs):
+               super(VideoDialog, self).__init__()
                self.data = kwargs['data']
-               self.parentWindow = opendialogs[-1]
                self.controlId = None
-               opendialogs.append(self)
+               
+       def _init_data(self):
+               json_data = self.data
+               if json_data.get('type') == 'tvshow':
+                       stv_details = top.apiClient.tvshow(json_data['id'], 
cast_props=defaultCastProps)
+               else:
+                       stv_details = top.apiClient.title(json_data['id'], 
defaultDetailProps, defaultCastProps)
+                       
+               top.stvList.updateTitle(stv_details)
+
+               # add xbmc id if available
+               if json_data.has_key('id') and 
top.stvList.hasStvId(json_data['id']):
+                       cacheItem = top.stvList.getByStvId(json_data['id'])
+                       json_data['xbmc_id'] = cacheItem['id']
+                       try:
+                               json_data['xbmc_movie_detail'] = 
xbmc_rpc.get_details('movie', json_data['xbmc_id'], True)
+                       except:
+                               pass
+
+               # add similars or seasons (bottom covers list)
+               if stv_details['type'] == 'movie':
+                       # get similar movies
+                       t1_similars = 
top.apiClient.titleSimilar(stv_details['id'])
+                       if t1_similars.has_key('titles'):
+                               stv_details['similars'] = t1_similars['titles']
+               elif stv_details['type'] == 'tvshow' and 
stv_details.has_key('seasons'):
+                       seasons = 
top.stvList.get_tvshow_local_seasons(stv_details['id'])
+                       log('seasons on disk:' + str(seasons))          
+                       stv_details['similars'] = [ {'id': i['id'], 'name': 
'Season %d' % i['season_number'], 'cover_medium': i['cover_medium'], 'watched': 
i['episodes_count'] == i['watched_count'], 'file': i['season_number'] in 
seasons} for i in stv_details['seasons'] ]
+
+               # similar overlays
+               if stv_details.has_key('similars'):
+                       for item in stv_details['similars']:
+                               top.stvList.updateTitle(item)
+
+                               oc = 0
+                               if item.get('file'):
+                                       oc |= OverlayCode.OnYourDisk
+                               if item.get('watched'):
+                                       oc |= OverlayCode.AlreadyWatched
+
+                               if oc:
+                                       item['overlay'] = overlay_image[oc]
+
+               self.data = video_dialog_template_fill(stv_details, json_data)
 
        def onInit(self):
+               # reset some default garbage
+               self.getControl(59).reset()
+               
+               # initialze data for the form
+               self._init_data()
+               
+               # fill-in the form
                win = xbmcgui.Window(xbmcgui.getCurrentWindowDialogId())
                win.setProperty("Movie.Title", self.data["name"] + 
'[COLOR=gray] (' + unicode(self.data.get('year')) + ')[/COLOR]')
                win.setProperty("Movie.Plot", self.data["plot"])
@@ -196,7 +289,7 @@ class VideoDialog(xbmcgui.WindowXMLDialog):
 
                # set available labels
                i = 1
-               for key, value in self.data['labels'].iteritems():
+               for key, value in self.data['labels']:
                        win.setProperty("Movie.Label.{0}.1".format(i), key)
                        win.setProperty("Movie.Label.{0}.2".format(i), value)
                        i = i + 1
@@ -246,11 +339,11 @@ class VideoDialog(xbmcgui.WindowXMLDialog):
 
                # play
                if controlId == 5:
-                       self.close()
+                       close_all_dialogs()
 
                # trailer
                elif controlId == 10:
-                       self.close()
+                       close_all_dialogs()
 
                # already watched
                elif controlId == 11:
@@ -267,7 +360,6 @@ class VideoDialog(xbmcgui.WindowXMLDialog):
                        stv_id = int(selected_item.getProperty('id'))
 
                        if self.data['type'] == 'tvshow':
-                               self.close()
                                show_tvshows_episodes(stv_id)                   
        
                        else:
                                show_video_dialog_byId(stv_id, close=True)
@@ -301,12 +393,8 @@ class VideoDialog(xbmcgui.WindowXMLDialog):
                try:
                        search_term = 
common.getUserInput(t_correct_search_title, "")
                        if search_term:
-                               results = top.apiClient.search(search_term, 
SEARCH_RESULT_LIMIT)
-                               if len(results['search_result']) == 0:
-                                       dialog_ok('No results')
-                               else:
-                                       data = { 'movies': 
results['search_result'] }
-                                       return open_select_movie_dialog(data)
+                               data = { 'search_term': search_term }
+                               return open_select_movie_dialog(data)           
                
                        else:
                                dialog_ok(t_enter_title_to_search)
                except:
@@ -314,45 +402,48 @@ class VideoDialog(xbmcgui.WindowXMLDialog):
 
                return
 
-       def close(self):                
-               # check if closing the currently opened dialog
-               if opendialogs[-1] != self:
-                       log('WARNING: Dialog queue inconsistency. Non-top 
dialog close')
-                       
-               opendialogs.remove(self)
-               xbmcgui.WindowXMLDialog.close(self)
-
 
-class SelectMovieDialog(xbmcgui.WindowXMLDialog):
+class SelectMovieDialog(MyDialog):
        """ Dialog for choosing movie corrections """
        def __init__(self, *args, **kwargs):
+               super(SelectMovieDialog, self).__init__()
                self.data = kwargs['data']
                self.controlId = None
                self.selectedMovie = None
-               opendialogs.append(self)
+
+       def _init_data(self):
+               results = top.apiClient.search(self.data['search_term'], 
SEARCH_RESULT_LIMIT)
+               if len(results['search_result']) == 0:
+                       dialog_ok('No results')
+                       self.close()
+                       return False
+               else:
+                       self.data.update({ 'movies': results['search_result'] 
})                
+                       return True
 
        def onInit(self):
-               items = []
-               for item in self.data['movies']:
-                       text = '%s [COLOR=gray](%s)[/COLOR]' % (item['name'], 
item.get('year', '?'))
+               if self._init_data():
+                       items = []
+                       for item in self.data['movies']:
+                               text = '%s [COLOR=gray](%s)[/COLOR]' % 
(item['name'], item.get('year', '?'))
 
-                       if item.get('type') == 'episode':
-                               text = 'S%sE%s - ' % (item.get('season_number', 
'??'), item.get('episode_number', '??')) + text
+                               if item.get('type') == 'episode':
+                                       text = 'S%sE%s - ' % 
(item.get('season_number', '??'), item.get('episode_number', '??')) + text
 
-                       li = xbmcgui.ListItem(text, 
iconImage=item['cover_medium'])
-                       li.setProperty('id', str(item['id']))
-                       li.setProperty('director', ', 
'.join(item.get('directors')) if item.has_key('directors') else t_unavail)
-                       cast = ', '.join([i['name'] for i in item['cast']]) if 
item.has_key('cast') else t_unavail                      
-                       li.setProperty('cast', cast)
-                       items.append(li)
+                               li = xbmcgui.ListItem(text, 
iconImage=item['cover_medium'])
+                               li.setProperty('id', str(item['id']))
+                               li.setProperty('director', ', 
'.join(item.get('directors')) if item.has_key('directors') else t_unavail)
+                               cast = ', '.join([i['name'] for i in 
item['cast']]) if item.has_key('cast') else t_unavail                      
+                               li.setProperty('cast', cast)
+                               items.append(li)
 
-                       self.getControl(59).addItems(items)
+                               self.getControl(59).addItems(items)
 
        def onClick(self, controlId):
                log('onClick: ' + str(controlId))
                if self.controlId == 59:
                        sel_index = self.getControl(59).getSelectedPosition()
-                       self.selectedMovie = self.data['movies'][sel_index]
+                       self.result = self.data['movies'][sel_index]
                        self.close()
 
 
@@ -364,77 +455,15 @@ class SelectMovieDialog(xbmcgui.WindowXMLDialog):
                if (action.getId() in CANCEL_DIALOG):
                        self.close()
 
-       def close(self):                
-               # check if closing the currently opened dialog
-               if opendialogs[-1] != self:
-                       log('WARNING: Dialog queue inconsistency. Non-top 
dialog close')
-                       
-               opendialogs.remove(self)
-               xbmcgui.WindowXMLDialog.close(self)
-
 
 def open_select_movie_dialog(tpl_data):
-       ui = SelectMovieDialog("SelectMovie.xml", __cwd__, "Default", 
data=tpl_data)
-       ui.doModal()
-       result = ui.selectedMovie
-       del ui
-       return result
+       return open_dialog(SelectMovieDialog, "SelectMovie.xml", tpl_data)
 
-def show_video_dialog_byId(stv_id, close=False):                               
-       stv_details = top.apiClient.title(stv_id, defaultDetailProps, 
defaultCastProps)
-       top.stvList.updateTitle(stv_details)
-       show_video_dialog_data(stv_details, close=close)
+def show_video_dialog_byId(stv_id, close=False):
+       show_video_dialog({'id': stv_id}, close)        
 
 def show_video_dialog(json_data, close=False): 
-       if json_data.get('type') == 'tvshow':
-               stv_details = top.apiClient.tvshow(json_data['id'], 
cast_props=defaultCastProps)
-       else:
-               stv_details = top.apiClient.title(json_data['id'], 
defaultDetailProps, defaultCastProps)
-               
-       top.stvList.updateTitle(stv_details)
-       show_video_dialog_data(stv_details, json_data, close)
-
-def show_video_dialog_data(stv_details, json_data={}, close=False):
-       log('stv_details:' + dump(stv_details))
-
-       # add xbmc id if available
-       if json_data.has_key('id') and top.stvList.hasStvId(json_data['id']):
-               cacheItem = top.stvList.getByStvId(json_data['id'])
-               json_data['xbmc_id'] = cacheItem['id']
-               try:
-                       json_data['xbmc_movie_detail'] = 
xbmc_rpc.get_details('movie', json_data['xbmc_id'], True)
-               except:
-                       pass
-
-       # add similars or seasons (bottom covers list)
-       if stv_details['type'] == 'movie':
-               # get similar movies
-               t1_similars = top.apiClient.titleSimilar(stv_details['id'])
-               if t1_similars.has_key('titles'):
-                       stv_details['similars'] = t1_similars['titles']
-       elif stv_details['type'] == 'tvshow' and stv_details.has_key('seasons'):
-               seasons = 
top.stvList.get_tvshow_local_seasons(stv_details['id'])
-               log('seasons on disk:' + str(seasons))          
-               stv_details['similars'] = [ {'id': i['id'], 'name': 'Season %d' 
% i['season_number'], 'cover_medium': i['cover_medium'], 'watched': 
i['episodes_count'] == i['watched_count'], 'file': i['season_number'] in 
seasons} for i in stv_details['seasons'] ]
-
-                               
-
-       # similar overlays
-       if stv_details.has_key('similars'):
-               for item in stv_details['similars']:
-                       top.stvList.updateTitle(item)
-
-                       oc = 0
-                       if item.get('file'):
-                               oc |= OverlayCode.OnYourDisk
-                       if item.get('watched'):
-                               oc |= OverlayCode.AlreadyWatched
-
-                       if oc:
-                               item['overlay'] = overlay_image[oc]
-
-       tpl_data = video_dialog_template_fill(stv_details, json_data)
-       open_video_dialog(tpl_data, close)
+       open_video_dialog(json_data, close)
 
 
 def video_dialog_template_fill(stv_details, json_data={}):
@@ -449,77 +478,50 @@ def video_dialog_template_fill(stv_details, json_data={}):
 
        tpl_data=stv_details
 
-       stv_labels = {}
-       if tpl_data.get('genres'):
-               stv_labels['Genre'] = ', '.join(tpl_data['genres'])     
-       if tpl_data.get('runtime'):
-               stv_labels['Runtime'] = '%d min' % tpl_data['runtime']
-       if tpl_data.get('directors'):
-               stv_labels['Director'] = ', '.join(tpl_data['directors'])
-       if tpl_data.get('cast'):
-               stv_labels['Cast'] = ', '.join(map(lambda x:x['name'], 
tpl_data['cast']))
-       #~ if tpl_data.get('writers'):
-               #~ stv_labels['Writer'] = ', '.join(tpl_data['writers'])
-       if tpl_data.get('date'):
-               tpl_data['release_date'] = 
datetime.fromtimestamp(tpl_data['date'])
-               stv_labels['Release date'] = 
tpl_data['release_date'].strftime('%x')
-
-       xbmclabels = {}
+       # store file in tpl
        if tpl_data.has_key('xbmc_movie_detail'):
-               d = tpl_data['xbmc_movie_detail']
-               if d.get('director'):
-                       xbmclabels["Director"] = ', '.join(d['director'])
-               #~ if d.get('writer'):
-                       #~ xbmclabels["Writer"] = d['writer']
-               if d.get('runtime'):
-                       xbmclabels["Runtime"] = d['runtime'] + ' min'
-               if d.get('premiered'):
-                       xbmclabels["Release date"] = d['premiered']
-                       if not tpl_data.get('release_date'):
-                               try:
-                                       tpl_data['release_date'] = 
datetime.strptime(d['premiered'], '%m/%d/%y')
-                               except:
-                                       pass
-
                if d.get('file'):
                        tpl_data['file'] = d.get('file')
 
-       labels = {}
-       labels.update(xbmclabels)
-       labels.update(stv_labels)
-
-       # set unavail labels
-       for label in ['Genre', 'Runtime', 'Director','Cast']:
-               if not labels.has_key(label):
-                       labels[label] = t_unavail
+       # store labels
+       labels = []     
 
+       # append tuple to labels, translated by trfn, or the N/A string
+       def append_tuple(stv_label, tpl_label, trfn):
+               if tpl_data.get(tpl_label):
+                       val = trfn(tpl_data[tpl_label])
+               else:
+                       val = t_unavail
+                               
+               labels.append((stv_label, val))
+       
+       # translate functions
+       def tr_genre(data): return ', '.join(data)
+       def tr_cast(data): return ', '.join(map(lambda x:x['name'], data))
+       def tr_runtime(data): return '%d min' % data
+
+       append_tuple('Genre', 'genres', tr_genre)
+       append_tuple('Cast', 'cast', tr_cast)
+       append_tuple('Director', 'directors', tr_genre)         # reuse 
tr_genre here   
+       append_tuple('Runtime', 'runtime', tr_runtime)
+               
+       if tpl_data.get('date'):
+               tpl_data['release_date'] = 
datetime.fromtimestamp(tpl_data['date'])
+               labels.append(('Release date', 
tpl_data['release_date'].strftime('%x')))
+               
        tpl_data['labels'] = labels
        tpl_data['BottomListingLabel'] = 
type2listinglabel.get(tpl_data['type'], '')
 
        return tpl_data
 
 def open_video_dialog(tpl_data, close=False):
-       try:
-               win = xbmcgui.Window(xbmcgui.getCurrentWindowDialogId())
-       except ValueError, e:
-               log('Window ValueError')
-               ui = VideoDialog("VideoInfo.xml", __cwd__, "Default", 
data=tpl_data)
-               ui.doModal()
-               del ui
-       else:
-               win = xbmcgui.WindowDialog(xbmcgui.getCurrentWindowDialogId())
-               if close:
-                       close_current_dialog()
-               ui = VideoDialog("VideoInfo.xml", __cwd__, "Default", 
data=tpl_data)
-               ui.doModal()
-               del ui
-
+       open_dialog(VideoDialog, "VideoInfo.xml", tpl_data, close)
 
 
-def show_submenu(action_code, **kwargs):
+def get_submenu_item_list(action_code, **kwargs):
        try:
                item_list = 
top.apiClient.get_item_list(action_code=action_code, **kwargs)
-               
+
                # hack HACK_SHOW_ALL_LOCAL_MOVIES
                if action_code==ActionCode.LocalMovieRecco:
                        item_list.append(item_show_all_movies_hack)
@@ -530,16 +532,26 @@ def show_submenu(action_code, **kwargs):
        except AuthenticationError as e:
                if dialog_check_login_correct():
                        show_submenu(action_code, **kwargs)
-                       
-               return
+
+               raise
 
        except ListEmptyException:
                dialog_ok(exc_text_by_mode(action_code))
-               return
+               raise
                
        except:
                log(traceback.format_exc())
                dialog_ok(t_listing_failed)
+               raise
                        
-       show_movie_list(item_list)
+       return item_list
 
+
+def show_submenu(action_code, **kwargs):
+       def init_data(result, **kwargs):
+               log('init_data kwargs: ' + str(kwargs))
+               result['items'] = get_submenu_item_list(**kwargs)
+       
+       kwargs['action_code'] = action_code     
+       tpl_data = { '_async_init': { 'method': init_data, 'kwargs': kwargs }}
+       open_list_dialog(tpl_data)
diff --git a/plugin.video.synopsi/resources/settings.xml 
b/plugin.video.synopsi/resources/settings.xml
index 5803bc9..5100da8 100644
--- a/plugin.video.synopsi/resources/settings.xml
+++ b/plugin.video.synopsi/resources/settings.xml
@@ -2,21 +2,13 @@
 
 <settings>
     <category label="69500">
-        <setting id="SETTINGS_VERSION" option="hidden" type="number" 
visible="false" default="1" />
-
         <!--Login-->
         <setting label="69700" type="lsep" />
                
         <setting id="USER" label="69503" type="text" default=""/>
        <setting id="PASS" label="69502" option="hidden" type="text" 
enable="!eq(-1,)" default=""/>
        
-       
        <!--TOKENS-->
-        <setting id="BASE_URL" option="hidden" type="text" visible="false" 
default="https://api.synopsi.tv/"; />
-        <setting id="REL_API_URL" option="hidden" type="text" visible="false" 
default="1.0/" />
-        <setting id="KEY" option="hidden" type="text" visible="false" 
default="59c53964b1013defcff0155f6e4d54a4" />
-        <setting id="SECRET" option="hidden" type="text" visible="false" 
default="487615d20b22cdce510fd3476ed84d924e2b0c45ce7c49dc621764e05fae0904" />
-
         <setting id="ADDON_SERVICE_PORT" option="hidden" type="number" 
visible="false" default="9091" />
 
        <setting id="ACCTOKEN" option="hidden" type="text" visible="false" 
default=""/>
diff --git a/plugin.video.synopsi/scrobbler.py 
b/plugin.video.synopsi/scrobbler.py
index 0d1cd2e..62162f2 100644
--- a/plugin.video.synopsi/scrobbler.py
+++ b/plugin.video.synopsi/scrobbler.py
@@ -12,7 +12,7 @@ import json
 # application
 from utilities import *
 import top
-
+import dialog
 
 TIME_UNKNOWN = 65535
 CANCEL_DIALOG = (9, 10, 92, 216, 247, 257, 275, 61467, 61448, )
@@ -31,7 +31,6 @@ class SynopsiPlayer(xbmc.Player):
        ended = False
        stopped = False
        paused = False
-       ended_without_rating = False
        apiclient = None
 
        playing = False
@@ -123,7 +122,7 @@ class SynopsiPlayer(xbmc.Player):
                if self.playing:
                        self.resumed()
 
-       def get_time(self, default=TIME_UNKNOWN):
+       def get_time(self, default=None):
                try:
                        if self.isPlayingVideo():
                                t = int(self.getTime())
@@ -131,7 +130,7 @@ class SynopsiPlayer(xbmc.Player):
                                raise Exception('fix: xbmc missing exception')
                except:
                        return default
-
+                       
                return t
 
        def get_media_info_tag(self):
@@ -156,11 +155,9 @@ class SynopsiPlayerDecor(SynopsiPlayer):
                        tries to update time while we are in the 
onPlayBackStopped method and handlers """
                
                t = self.get_time()
-                       
                if t or not self.playing:
                        self.current_time = t
                        
-                       
                #~ self.get_media_info_tag()
 
        def started(self):
@@ -173,12 +170,10 @@ class SynopsiPlayerDecor(SynopsiPlayer):
                # rate file
                self.rate_file(self.last_played_file)
 
-       def ended_without_rating(self):
-               self.playerEvent('end')
+               self.onAfterStop()
 
        def stopped(self):
                self.playerEvent('stop')
-               #~ self.log(dump(self.playerEvents))
                percent = self.current_time / self.total_time
                self.log('percent:' + str(self.current_time / self.total_time))
 
@@ -187,6 +182,14 @@ class SynopsiPlayerDecor(SynopsiPlayer):
                        self.rate_file(self.last_played_file)
                else:
                        self.send_checkin(self.last_played_file)
+               
+               self.onAfterStop()
+
+       def onAfterStop(self):
+               # unstash all dialogs, if any
+               #~ dialog.unstash_all_dialogs()
+               pass
+
 
        def paused(self):
                self.update_current_time()
@@ -204,12 +207,15 @@ class SynopsiPlayerDecor(SynopsiPlayer):
                # get stv id
                detail = self.cache.getByFilename(filename)
 
-               self.log('detail: ' + str(detail))
+               self.log('rating detail: ' + str(detail))
 
                # only for identified by synopsi
                if not detail.has_key('stvId'):
                        return False
-
+               
+               # disallow sending 'watched' event for this file from scrobbler
+               self.cache.setBlockEvents(detail['type'], detail['id'])
+                               
                ## prepare the data
                data = { 'player_events': json.dumps(self.playerEvents) }
        
@@ -225,10 +231,14 @@ class SynopsiPlayerDecor(SynopsiPlayer):
                        if rating < 4:
                                data['rating'] = rating
 
-               self.apiclient.titleWatched(detail['stvId'], **data)
-
-               # clear the player events
-               self.playerEvents = []
+               try:
+                       self.apiclient.titleWatched(detail['stvId'], **data)
+               finally:
+                       # allow sending 'watched' event for this file from 
scrobbler
+                       self.cache.resetBlockEvents()
+                       
+                       # clear the player events
+                       self.playerEvents = []
 
        def send_checkin(self, filename):
                self.rate_file(filename, rate=False)
diff --git a/plugin.video.synopsi/service.py b/plugin.video.synopsi/service.py
index 5b7c419..c8b81f5 100644
--- a/plugin.video.synopsi/service.py
+++ b/plugin.video.synopsi/service.py
@@ -14,7 +14,7 @@ import time
 from scrobbler import SynopsiPlayerDecor
 from library import RPCListenerHandler
 from cache import *
-from utilities import home_screen_fill, login_screen, log
+from utilities import home_screen_fill, login_screen, log, VERSION
 from app_apiclient import AppApiClient
 from addonservice import AddonService
 import top
@@ -30,6 +30,8 @@ __addon__.setSetting('ADDON_SERVICE_FIRSTRUN', "false")
 DEFAULT_SERVICE_PORT=int(__addon__.getSetting('ADDON_SERVICE_PORT'))
 
 def main():
+       log('SYNOPSI SERVICE (%s) START' % VERSION)
+       
        apiclient1 = AppApiClient.getDefaultClient()
        top.apiClient = apiclient1
 
diff --git a/plugin.video.synopsi/utilities.py 
b/plugin.video.synopsi/utilities.py
index 7335f02..b918ad2 100644
--- a/plugin.video.synopsi/utilities.py
+++ b/plugin.video.synopsi/utilities.py
@@ -21,6 +21,9 @@ import time
 import sys
 import xml.etree.ElementTree as et
 
+# application
+import resources.const
+
 common = CommonFunctions
 common.plugin = "SynopsiTV"
 
@@ -29,7 +32,7 @@ __addon__  = xbmcaddon.Addon()
 __addonname__ = __addon__.getAddonInfo('name')
 __addonpath__  = __addon__.getAddonInfo('path')
 __author__  = __addon__.getAddonInfo('author')
-__version__   = __addon__.getAddonInfo('version')
+VERSION   = __addon__.getAddonInfo('version')
 __profile__      = xbmc.translatePath(__addon__.getAddonInfo('profile'))
 __lockLoginScreen__ = threading.Lock()
 
@@ -85,6 +88,7 @@ t_nounwatched = 'There are no unwatched episodes in your TV 
Show tracking'
 t_nolocalrecco = 'There are no items in this list. Either you have no movies 
in your library or they have not been recognized by Synopsi'
 t_nolocaltvshow = 'There are no items in this list. Either you have no 
episodes in your library or they have not been recognized by Synopsi'
 t_needrestart = 'To start the SynopsiTV service, please turn off your media 
center then turn it back on again. Do this now?'
+t_needrestart_update = 'Addon service has been updated. For the plugin to work 
correctly, turn off your media center then turn it back on again. Do this now?'
 t_enter_title_to_search =  'Enter a title name to search for.'
 t_correct_search_title = 'Search for the correct title'
 
@@ -135,7 +139,7 @@ def os_info():
        return info
 
 def software_info():
-       i = { 'os_info': os_info() }
+       i = { 'plugin_version': VERSION, 'os_info': os_info() }
        i.update(player_info())
        return i 
 
@@ -269,21 +273,6 @@ def clear_setting_cache():
        if os.path.exists(settingsPath):
                os.remove(settingsPath)
 
-def get_settings_file_version():
-       path = os.path.join(__addonpath__, 'resources', 'settings.xml')
-
-       value = None
-       try:
-               with open(path, 'r') as _file:
-                       temp = _file.read()
-                       if "SETTINGS_VERSION" in temp:
-                               version = re.compile('\<setting 
id="SETTINGS_VERSION" option="hidden" type="number" visible="false" 
default="(\d+)" /\>').findall(temp)
-                               value = int(version[0])
-       except (IOError, IndexError):
-               pass
-
-       return value
-
 def setting_cache_append_string(string):
        settingsPath = os.path.join(__profile__, 'settings.xml')
 
@@ -671,9 +660,9 @@ def dialog_login_fail_yesno():
        return result
 
 
-def dialog_need_restart():
+def dialog_need_restart(reason=t_needrestart):
        dialog = xbmcgui.Dialog()
-       yes = dialog_yesno(t_needrestart)
+       yes = dialog_yesno(reason)
        return yes
 
 
@@ -711,7 +700,7 @@ def show_categories():
        xbmc.executebuiltin("Container.SetViewMode(503)")
        add_directory("Movie Recommendations", "url", ActionCode.MovieRecco, 
"list.png")
        add_directory("Popular TV Shows", "url", ActionCode.TVShows, "list.png")
-       add_directory("Local Movie recommendations", "url", 
ActionCode.LocalMovieRecco, "list.png")
+       add_directory("Local Movie Recommendations", "url", 
ActionCode.LocalMovieRecco, "list.png")
        add_directory("Local TV Shows", "url", ActionCode.LocalTVShows, 
"list.png")
        add_directory("Unwatched TV Show Episodes", "url", 
ActionCode.UnwatchedEpisodes, "list.png")
        add_directory("Upcoming TV Episodes", "url", 
ActionCode.UpcomingEpisodes, "list.png")

-----------------------------------------------------------------------

Summary of changes:
 plugin.video.rbk.no/addon.xml                      |    3 +-
 plugin.video.rbk.no/changelog.txt                  |    3 +
 plugin.video.rbk.no/default.py                     |   22 +-
 plugin.video.synopsi/_src/install_xbmc_version.sh  |    1 +
 plugin.video.synopsi/_src/pull_request.sh          |   15 +
 plugin.video.synopsi/_src/usefull_queries.sql      |   12 +
 plugin.video.synopsi/addon.py                      |   18 +-
 plugin.video.synopsi/addon.xml                     |    2 +-
 plugin.video.synopsi/addonservice.py               |   19 +-
 plugin.video.synopsi/apiclient.py                  |   10 +-
 plugin.video.synopsi/cache.py                      |   33 ++-
 plugin.video.synopsi/changelog.txt                 |   10 +
 plugin.video.synopsi/dialog.py                     |  424 ++++++++++----------
 .../CommonFunctions.py => resources/__init__.py}   |    0
 plugin.video.synopsi/resources/const.py            |    9 +
 plugin.video.synopsi/resources/settings.xml        |    8 -
 plugin.video.synopsi/scrobbler.py                  |   40 ++-
 plugin.video.synopsi/service.py                    |    4 +-
 plugin.video.synopsi/utilities.py                  |   29 +-
 19 files changed, 393 insertions(+), 269 deletions(-)
 create mode 100755 plugin.video.synopsi/_src/install_xbmc_version.sh
 create mode 100755 plugin.video.synopsi/_src/pull_request.sh
 create mode 100644 plugin.video.synopsi/_src/usefull_queries.sql
 copy plugin.video.synopsi/{tests/fakeenv/CommonFunctions.py => 
resources/__init__.py} (100%)
 create mode 100644 plugin.video.synopsi/resources/const.py


hooks/post-receive
-- 
Plugins

------------------------------------------------------------------------------
Everyone hates slow websites. So do we.
Make your web apps faster with AppDynamics
Download AppDynamics Lite for free today:
http://p.sf.net/sfu/appdyn_d2d_feb
_______________________________________________
Xbmc-addons mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/xbmc-addons

Reply via email to