The branch, frodo has been updated
       via  92ac31a1ae7443966f8b3f0f6ac1bd4154ec5817 (commit)
       via  5c0976bb52b8a40c38c7798eb80977a82641b549 (commit)
       via  dfd50157d8fde1702a193dc5ac953c2868cfb89f (commit)
      from  06748c4826fe4ccde04e703e688abc589d432c8d (commit)

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

commit 92ac31a1ae7443966f8b3f0f6ac1bd4154ec5817
Author: beenje <[email protected]>
Date:   Tue Mar 19 21:48:04 2013 +0100

    [plugin.video.synopsi] updated to version 1.0.5

diff --git a/plugin.video.synopsi/_src/usefull_queries.sql 
b/plugin.video.synopsi/_src/usefull_queries.sql
index 3db2ceb..66771bc 100644
--- a/plugin.video.synopsi/_src/usefull_queries.sql
+++ b/plugin.video.synopsi/_src/usefull_queries.sql
@@ -6,7 +6,9 @@ select user_id, email, min(inserted) from api_apilog left join 
auth_user as AU o
 select user_id, email, min(inserted), date_joined from api_apilog left join 
auth_user as AU on AU.id=user_id group by user_id, date_joined, email order by 
min(date_joined) desc;
 
 /* api users with api request counts */
-select user_id, email, min(inserted), date_joined from api_apilog left join 
auth_user as AU on AU.id=user_id group by user_id, date_joined, email order by 
min(date_joined) desc;
 
 /* checkins whith software info filled-in      */
 select * from actions_checkin where data like '%software_info":"{%' and 
data!='{}' and data!='';
+
+/* fail responses percentil by method */
+select method, count(id), sum(case when response_status=0 then 1 else 0 end) 
as cnt_ok, sum(case when response_status=0 then 0 else 1 end) as cnt_fail, 
round(sum(case when response_status=0 then 0 else 1 end)::real/count(id)*100)  
as fail_percentil from api_apilog group by method order by fail_percentil;
diff --git a/plugin.video.synopsi/addon.xml b/plugin.video.synopsi/addon.xml
index 505a308..fc9779e 100644
--- a/plugin.video.synopsi/addon.xml
+++ b/plugin.video.synopsi/addon.xml
@@ -1,12 +1,12 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <addon id="plugin.video.synopsi"
        name="SynopsiTV"
-       version="1.0.4"
+       version="1.0.5"
        provider-name="Synopsi.TV">
        <requires>
                <import addon="xbmc.python" version="2.1.0" />
                <import addon="xbmc.addon" version="12.0.0" />
-               <import addon="xbmc.metadata" version="2.0.0" />
+               <import addon="xbmc.metadata" version="2.1.0" />
                <import addon="xbmc.gui" version="4.0.0" />
                
                <import addon="script.module.parsedom" version="1.5.1" />
diff --git a/plugin.video.synopsi/apiclient.py 
b/plugin.video.synopsi/apiclient.py
index 7f92119..ac60356 100644
--- a/plugin.video.synopsi/apiclient.py
+++ b/plugin.video.synopsi/apiclient.py
@@ -29,7 +29,7 @@ defaultIdentifyProps = commonTitleProps + ['tvshow_id']
 watchableTitleProps = commonTitleProps + ['watched']
 defaultTVShowProps = commonTitleProps + ['seasons']
 smallListProps = ['id', 'cover_medium', 'name', 'watched', 'type']
-defaultEpisodeProps = smallListProps + ['season_number', 'episode_number']
+defaultEpisodeProps = smallListProps + ['season_number', 'episode_number', 
'cover_large', 'tvshow_id', 'tvshow_name']
 allSeasonProps = ['id', 'cover_full', 'cover_large', 'cover_medium', 
'cover_small', 'cover_thumbnail', 'season_number', 'episodes_count', 
'watched_count']
 defaultSeasonProps = ['id', 'cover_medium', 'season_number', 'episodes_count', 
'watched_count']
 defaultSeasonProps2 = ['id', 'episodes']
@@ -462,7 +462,7 @@ class ApiClient(loggable.Loggable):
 
                return self.execute(req)
 
-       def unwatchedEpisodes(self, props=watchableTitleProps):
+       def unwatchedEpisodes(self, props=defaultEpisodeProps):
                req = {
                        'methodPath': 'profile/unwatched_episodes/',
                        'method': 'get',
diff --git a/plugin.video.synopsi/app_apiclient.py 
b/plugin.video.synopsi/app_apiclient.py
index aef8e50..5a1712e 100644
--- a/plugin.video.synopsi/app_apiclient.py
+++ b/plugin.video.synopsi/app_apiclient.py
@@ -160,13 +160,7 @@ class AppApiClient(ApiClient):
        def get_tvshow_season(self, season_id):
                season = self.season(season_id)
                
-               # fix names
-               for i in season['episodes']:
-                       episident = 'S%sE%s' % (i['season_number'], 
i['episode_number'])
-                       i['name'] = '%s - %s' % (episident, i['name'])
-                       top.stvList.updateTitle(i)
-
-               return season['episodes']
+               return season
 
        def get_title(self, stv_id, detailProps=defaultDetailProps, 
castProps=defaultCastProps):
                return self.title(stv_id, detailProps, castProps)
diff --git a/plugin.video.synopsi/cache.py b/plugin.video.synopsi/cache.py
index 7e6ec53..8a51626 100644
--- a/plugin.video.synopsi/cache.py
+++ b/plugin.video.synopsi/cache.py
@@ -1,5 +1,5 @@
 # xbmc
-import xbmc
+import xbmc, xbmcvfs
 
 # python standart lib
 import base64
@@ -47,7 +47,6 @@ class OfflineStvList(object):
                self.filePath = filePath or self.__class__.getDefaultFilePath()
                self.clear()
                self.uuid = uuid
-               #~ self.list()
 
        @classmethod
        def getDefaultFilePath(cls):
@@ -78,7 +77,6 @@ class OfflineStvList(object):
 
        def deserialize(self, _string):
                self.items, self.byType, self.byTypeId, self.byFilename, 
self.byStvId = pickle.loads(base64.b64decode(_string))
-               self.dump()
 
        def log(self, msg):
                log('CACHE / ' + msg)
@@ -132,7 +130,7 @@ class OfflineStvList(object):
                                movie['stvId'] = title['id']
                                self.log('identified: ' + title['name'])
                        else:
-                               self.log('File NOT identified %s' % 
movie['file'])
+                               self.log('NOT identified %s' % movie['file'])
 
                        # current block could raise ApiCallError, when there is 
not a real problem
                        try:
@@ -161,7 +159,7 @@ class OfflineStvList(object):
                        self.put(stv_title)
 
        def put(self, item):
-               " Put a new record in the list "
+               """ Put a new record in the list """
                self.log('PUT ' + dump(filtertitles(item)))
                # check if an item with this stvId is not already there
                if item.has_key('stvId') and self.hasStvId(item['stvId']):
@@ -311,10 +309,10 @@ class OfflineStvList(object):
 
        def clear(self):
                self.items = []
-               self.byType = { 'movie': {}, 'tvshow': {}, 'episode': {}, 
'season': {}}
-               self.byTypeId = {}
+               self.byType = { 'movie': {}, 'tvshow': {}, 'episode': {}, 
'season': {}}         # ids here are xbmc_ids, except tvshow_ids!
+               self.byTypeId = {}                                              
                                                                        # ids 
here are xbmc_ids
                self.byFilename = {}
-               self.byStvId = {}
+               self.byStvId = {}                                               
                                                                        # ids 
here are stv_ids
 
        def getItems(self):
                return self.items
@@ -403,12 +401,12 @@ class OfflineStvList(object):
 
        def save(self):
                self.log('SAVING / ' + self.filePath)
-               f = open(self.filePath, 'w')
+               f = xbmcvfs.File(self.filePath, 'w')
                f.write(self.serialize())
                f.close()
 
        def load(self):
-               f = open(self.filePath, 'r')
+               f = xbmcvfs.File(self.filePath, 'r')
                self.deserialize(f.read())
                f.close()
 
@@ -496,7 +494,7 @@ class OnlineStvList(OfflineStvList):
 class AppStvList(OnlineStvList):
        def get_local_tvshows(self):
                local_tvshows = self.getAllByType('tvshow')
-               log('local tvshows ' + dump(local_tvshows))
+                               
                return local_tvshows.values()
 
        def get_tvshow_local_seasons(self, stv_id):
diff --git a/plugin.video.synopsi/changelog.txt 
b/plugin.video.synopsi/changelog.txt
index 9e36bac..38a7ce0 100644
--- a/plugin.video.synopsi/changelog.txt
+++ b/plugin.video.synopsi/changelog.txt
@@ -1,3 +1,11 @@
+[B]Version 1.0.5[/B]
+
++ Added: display year after movie name
++ Added: display episode identification in every episode listings
++ Added: breadcrumbs for listings
++ Added: display tvshow name in episode detail
+* Fixed: show correct titles on home screen (recently added)
+
 [B]Version 1.0.4[/B]
 
 - Fixed: labels in movie details
diff --git a/plugin.video.synopsi/dialog.py b/plugin.video.synopsi/dialog.py
index c224ff0..9df5d42 100644
--- a/plugin.video.synopsi/dialog.py
+++ b/plugin.video.synopsi/dialog.py
@@ -20,6 +20,9 @@ from cache import StvList, DuplicateStvIdException
 import top
 from threading import Thread
 
+# temporary
+import random
+
 ACTIONS_CLICK = [7, 100]
 LIST_ITEM_CONTROL_ID = 500
 HACK_GO_BACK = -2
@@ -111,23 +114,37 @@ class ListDialog(MyDialog):
                self.selectedMovie = None
                self.listControl = None
 
-       def onInit(self):
+
+       def onInit(self):               
+               win = xbmcgui.Window(xbmcgui.getCurrentWindowDialogId())
+               
                self.listControl = self.getControl(LIST_ITEM_CONTROL_ID)
                self.listControl.reset()
-               
+
+               # asynchronous initialization
                if self.__dict__.get('_async_init'):
                        result = {}
                        kwargs = self._async_init.get('kwargs', {})
                        kwargs['result'] = result
                        try:
-                               self._async_init['method'](**kwargs)
+                               self._async_init['method'](**kwargs)    # 
method(result=result, +kwargs)
                        except (AuthenticationError, ListEmptyException) as e:
                                self.close()
                                return
                                
-                       self.data = result
+                       self.data.update(result['result'])
+
+               # exception of incoming data format
+               if self.data.has_key('episodes'):
+                       self.data['items'] = self.data['episodes']
+                       first_episode = self.data['items'][0]
+                       self.data['tvshow_name'] = first_episode['tvshow_name']
+                       self.data['_categoryName'] = self.data['tvshow_name'] + 
' - Season ' + first_episode['season_number']
                        
+               win.setProperty('ContainerCategory', 
self.data.get('_categoryName', ''))
+               
                self.updateItems()
+               
                                
        def updateItems(self):
                items = []
@@ -156,9 +173,27 @@ class ListDialog(MyDialog):
 
        def _getListItem(self, item):
                #~ itemPath = 'mode=' + str(ActionCode.VideoDialogShowById) + 
'&amp;stv_id=' + str(item['id'])
-               li = xbmcgui.ListItem(item['name'], 
iconImage=item['cover_medium'])
+               itemName = item['name']
+               
+               # add year after name
+               if item.has_key('year'):
+                       itemName += ' (' + str(item['year']) + ')'
+
+               # for episodes, add epis-ident
+               if item['type'] == 'episode':
+                       episident = get_episode_identifier(item)
+                       itemName = '%s - %s' % (episident, itemName)
+               
+               # create listitem with basic properties 
+               li = xbmcgui.ListItem(itemName, iconImage=item['cover_medium'])
                li.setProperty('id', str(item['id']))
                li.setProperty('type', str(item['type']))
+
+               if item['type'] == 'episode':
+                       li.setProperty('episode_number', 
str(item['episode_number']))
+                       li.setProperty('season_number', 
str(item['season_number']))
+                       li.setProperty('tvshow_name', str(item['tvshow_name']))
+                       
                #~ li.setProperty('path', str(itemPath))                
                        
                # prefer already set custom_overlay, if N/A set custom overlay
@@ -197,7 +232,14 @@ class ListDialog(MyDialog):
                        elif stv_id == HACK_SHOW_ALL_LOCAL_MOVIES:
                                show_submenu(ActionCode.LocalMovies)
                        else:
-                               show_video_dialog({'type': 
item.getProperty('type'), 'id': stv_id}, close=False)
+                               data = {'type': item.getProperty('type'), 'id': 
stv_id}
+                               if data['type'] == 'episode':
+                                       data['season_number'] = 
item.getProperty('season_number')
+                                       data['episode_number'] = 
item.getProperty('episode_number')
+                                       data['tvshow_name'] = 
item.getProperty('tvshow_name')
+                                       #~ data['tvshow_name'] = 
self.data['tvshow_name']
+
+                               show_video_dialog(data, close=False)
 
                
 
@@ -208,11 +250,10 @@ def show_movie_list(item_list):
        open_list_dialog({ 'items': item_list })
 
 def show_tvshows_episodes(stv_id):
-       def init_data(result, **kwargs):
-               log('asyn handler show_tvshows_episodes: ' + str(kwargs))
-               result['items'] = top.apiClient.get_tvshow_season(stv_id)
+       def init_data(result):
+               result['result'] = top.apiClient.get_tvshow_season(stv_id)
        
-       tpl_data = { '_async_init': { 'method': init_data, 'kwargs': {} }}
+       tpl_data = { '_async_init': { 'method': init_data }}
 
        open_list_dialog(tpl_data)
 
@@ -225,9 +266,10 @@ class VideoDialog(MyDialog):
                super(VideoDialog, self).__init__()
                self.data = kwargs['data']
                self.controlId = None
-               
+
        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:
@@ -280,7 +322,12 @@ class VideoDialog(MyDialog):
                
                # fill-in the form
                win = xbmcgui.Window(xbmcgui.getCurrentWindowDialogId())
-               win.setProperty("Movie.Title", self.data["name"] + 
'[COLOR=gray] (' + unicode(self.data.get('year')) + ')[/COLOR]')
+               str_title = self.data['name'] + '[COLOR=gray] (' + 
unicode(self.data.get('year')) + ')[/COLOR]'
+               if self.data['type'] == 'episode':
+                       episident = get_episode_identifier(self.data)           
        
+                       str_title = self.data['tvshow_name'] + ' - 
[COLOR=gray]' + episident + ' -[/COLOR] ' + str_title
+                       
+               win.setProperty("Movie.Title", str_title)
                win.setProperty("Movie.Plot", self.data["plot"])
                win.setProperty("Movie.Cover", self.data["cover_full"])
 
@@ -550,9 +597,9 @@ def get_submenu_item_list(action_code, **kwargs):
 
 def show_submenu(action_code, **kwargs):
        def init_data(result, **kwargs):
-               log('init_data kwargs: ' + str(kwargs))
-               result['items'] = get_submenu_item_list(**kwargs)
+               result['result'] = {'items': get_submenu_item_list(**kwargs)}
        
-       kwargs['action_code'] = action_code     
-       tpl_data = { '_async_init': { 'method': init_data, 'kwargs': kwargs }}
+       categoryName = submenu_categories_dict[action_code]
+       kwargs['action_code'] = action_code
+       tpl_data = { '_categoryName': categoryName, '_async_init': { 'method': 
init_data, 'kwargs': kwargs }}
        open_list_dialog(tpl_data)
diff --git a/plugin.video.synopsi/loggable.py b/plugin.video.synopsi/loggable.py
index a5ea521..17c9d7b 100644
--- a/plugin.video.synopsi/loggable.py
+++ b/plugin.video.synopsi/loggable.py
@@ -18,7 +18,7 @@ class Loggable(object):
                # assure that log dir exists
                logdir = self.get_log_dir()
 
-               if not os.path.exists(logdir):
+               if not xbmcvfs.exists(logdir):
                        xbmcvfs.mkdir(logdir)
 
                fh = logging.handlers.RotatingFileHandler(os.path.join(logdir, 
self.name + '.log'), mode='w', backupCount=2)
diff --git 
a/plugin.video.synopsi/resources/skins/Default/720p/custom_MyVideoNav.xml 
b/plugin.video.synopsi/resources/skins/Default/720p/custom_MyVideoNav.xml
index 710f4b6..51c2fcd 100644
--- a/plugin.video.synopsi/resources/skins/Default/720p/custom_MyVideoNav.xml
+++ b/plugin.video.synopsi/resources/skins/Default/720p/custom_MyVideoNav.xml
@@ -8,9 +8,34 @@
                <include>ContentPanelBackgrounds</include>
                <control type="group">
                        <include>Window_OpenClose_Animation</include>
-<!--           <include>CommonRootView</include> <!-- view id = 50 -->
-<!--           <include>FullWidthList</include> <!-- view id = 51 -->
-<!--           <include>ThumbnailView</include> <!-- view id = 500 -->
+                       
+                       <control type="image">
+                               <description>Section header image</description>
+                               <posx>20</posx>
+                               <posy>3</posy>
+                               <width>35</width>
+                               <height>35</height>
+                               <aspectratio>keep</aspectratio>
+                               <texture>icon_video.png</texture>
+                       </control>
+                       <control type="grouplist">
+                               <posx>65</posx>
+                               <posy>5</posy>
+                               <width>1000</width>
+                               <height>30</height>
+                               <orientation>horizontal</orientation>
+                               <align>left</align>
+                               <itemgap>5</itemgap>
+                               <control type="label">
+                                       <include>WindowTitleCommons</include>
+                                       <label>$LOCALIZE[3]</label>
+                               </control>
+                               <control type="label">
+                                       <include>WindowTitleCommons</include>
+                                       <label>[COLOR=blue] - 
[/COLOR]$INFO[Window.Property(ContainerCategory)]</label>
+                                       
<visible>!IsEmpty(Container.FolderName)</visible>
+                               </control>
+                       </control>
                        <control type="group">
                                <visible>Control.IsVisible(500)</visible>
                                <include>VisibleFadeEffect</include>
@@ -112,6 +137,7 @@
                                                        <align>center</align>
                                                        <aligny>center</aligny>
                                                        
<info>ListItem.Label</info>
+                                                       <scrollsuffix>       ~  
     </scrollsuffix>
                                                </control>
                                                <control type="image">
                                                        <posx>180</posx>
diff --git a/plugin.video.synopsi/tests/apiclient.unittest.py 
b/plugin.video.synopsi/tests/apiclient.unittest.py
index 5ad2284..49f1795 100644
--- a/plugin.video.synopsi/tests/apiclient.unittest.py
+++ b/plugin.video.synopsi/tests/apiclient.unittest.py
@@ -4,6 +4,8 @@ from unittest import *
 import logging
 import json
 from copy import copy
+import random
+import string
 
 # test helper
 from common import connection
@@ -92,10 +94,58 @@ class ApiTest(TestCase):
                stv_title = client.titleIdentify(**ident2)
                self.assertTrue(stv_title.has_key('type'))
 
+               ident3 = {
+                       'file_name': 
'_videne/Notorious/Notorious.[2009self.Eng].TELESYNC.DivX-LTT.avi',
+               }
+
+               stv_title = client.titleIdentify(**ident3)
+
+               self.assertTrue(stv_title.has_key('type'))
+
+               ident = {
+                       'file_name': 
'_videne/Notorious/Notorious.[2009self.Eng].TELESYNC.DivX-LTT.avi',
+                       'stv_title_hash': None,
+                       'os_title_hash': None
+               }
+
+               stv_title = client.titleIdentify(**ident)
+
+               self.assertTrue(stv_title.has_key('type'))
+               
+               ident = {
+                       'file_name': 
'_videne/Notorious/Notorious.[2009self.Eng].TELESYNC.DivX-LTT.avi',
+                       'stv_title_hash': ''
+               }
+
+               stv_title = client.titleIdentify(**ident)
+
+               self.assertTrue(stv_title.has_key('type'))
+
+               ident = {
+                       'file_name': 'Avatar.avi',
+                       'stv_title_hash': ''
+               }
+
+               stv_title = client.titleIdentify(**ident)
+
+               self.assertTrue(stv_title.has_key('type'))
+
+               ident = {
+                       'file_name': 'Rambo.avi',
+               }
+
+               stv_title = client.titleIdentify(**ident)
+
+               self.assertTrue(stv_title.has_key('type'))
+
 
        def test_library_add(self):
                client.getAccessToken()
-
+               
+               # change the device id, to use empty library
+               randstr = ''.join(random.choice(string.ascii_uppercase + 
string.digits) for x in range(10))
+               client.device_id = 'testing_' + randstr
+               
                ident = {
                        'file_name': 
'/Volumes/FLOAT/Film/_videne/Notorious/Notorious.[2009self.Eng].TELESYNC.DivX-LTT.avi',
                        'stv_title_hash': 
'8b05ff1ad4865480e4705a42b413115db2bf94db',
@@ -142,21 +192,55 @@ class ApiTest(TestCase):
                self.assertTrue(data.has_key('titles'))
                self.assertTrue(len(data['titles']) > 0)
 
-       def test_profile_recco_local(self):
 
+       def test_profile_recco_local(self):
+               """ 
+                       To test local recco, we have to prepare a scenario for 
it:
+                       - create new client with origin library
+                       - get global recco
+                       - add some random titles from global recco to library
+                       - add titles not in global recco to library
+                       - test that first title is in local recco, and second 
not
+               """
+               props = [ 'id', 'name', 'year', 'cover_small' ]
+
+               device_id = ''.join([random.choice(string.hexdigits) for n in 
xrange(32)])
+               new_client = ApiClient(c['base_url'], c['key'], c['secret'], 
c['username'], c['password'], device_id, debugLvl = logging.DEBUG, 
rel_api_url=c['rel_api_url'])
+               
+               # get global recco
+               reco_global = new_client.profileRecco('movie', False, 50, props)
+               reco_global_ids = [i['id'] for i in reco_global['titles']]
+               
+               # pick five titles
+               random_pick = list(set([random.choice(reco_global_ids) for i in 
xrange(0,5)]))
+               
+               # add them to library
+               for i in random_pick:
+                       new_client.libraryTitleAdd(i)
+               
+               # wait a little !
+               time.sleep(1)
+               
+               # get local recco
+               reco_local = new_client.profileRecco('movie', True, 50, props)
 
-               props = [ 'year', 'cover_small' ]
-               data = client.profileRecco('movie', True, 5, props)
-
-               self.assertTrue(data.has_key('recco_id'))
-               self.assertTrue(data.has_key('titles'))
-               self.assertTrue(len(data['titles']) > 0)
+               reco_local_ids = [i['id'] for i in reco_local['titles']]
+               
+               # check local recco             
+               self.assertTrue(reco_local.has_key('recco_id'))
+               self.assertTrue(reco_local.has_key('titles'))
+               self.assertTrue(len(reco_local['titles']) > 0)
+               
+               # every picked movie should be in local recco
+               for i in random_pick:
+                       self.assertTrue(i in reco_local_ids)
+               
 
        def test_profile_recco_watched(self):
                props = [ 'id', 'year', 'cover_small' ]
                data = client.profileRecco('movie', False, 5, props)
                all_ids = [ i['id'] for i in data['titles'] ]
-               print dump(all_ids)
+
                self.assertTrue(data.has_key('recco_id'))
                self.assertTrue(data.has_key('titles'))
                self.assertTrue(len(data['titles']) > 0)
@@ -166,7 +250,7 @@ class ApiTest(TestCase):
 
                new_data = client.profileRecco('movie', False, 5, props)
                all_ids = [ i['id'] for i in new_data['titles'] ]
-               print dump(all_ids)
+
                self.assertFalse(check_id in all_ids)
 
 
@@ -196,12 +280,14 @@ class ApiTest(TestCase):
        def test_season(self):
                title = client.season(14376)
 
-               # print dump(title)
+               self.assertTrue(title.has_key('episodes'))
+               es = title['episodes']
+               self.assertTrue(es[0].has_key('season_number'))
+               self.assertTrue(es[0].has_key('episode_number'))
+               self.assertTrue(es[0].has_key('watched'))
+               self.assertTrue(es[0].has_key('id'))
+
 
-               self.assertTrue(title.has_key('cover_full'))
-               self.assertTrue(title.get('type')=='tvshow')
-               self.assertTrue(title.get('year')==2005)
-               self.assertTrue(title['cast'][0]['name']=='Josh Radnor')
 
        def test_unicode_input(self):
                data = {
@@ -216,8 +302,12 @@ class ApiTest(TestCase):
 
        def test_search(self):
                result = client.search('Adams aebler', 13)
-               # print dump(result)
+               
                self.assertTrue(result.has_key('search_result'))
+               self.assertTrue(result['search_result'][0]['id'] == 514461)
+
+
+               result = client.search('Love', 13)
                self.assertTrue(len(result['search_result']) == 13)
 
        def test_identify_correct(self):
@@ -225,6 +315,7 @@ class ApiTest(TestCase):
                # print dump(result)
                self.assertTrue(result['status']=='ok')
 
+       @skip('this needs deeper work')
        def test_identify_correct_library(self):
                TITLE_CORRECTION_TARGET = 1947362
                CORRECTION_FILE_HASH = 
'52b6f00222cdb3631d9914aee6b662961e924aa5'       # hash of my "three times" file
@@ -298,7 +389,7 @@ class ApiTest(TestCase):
                self.assertTrue(TITLE_CORRECTION_TARGET not in lib_ids)
                self.assertTrue(SOME_ID_IN_LIBRARY in lib_ids)
 
-
+       @skip('this needs deeper work')
        def test_correction_recco(self):
                TITLE_CORRECTION_TARGET = 1947362
                CORRECTION_FILE_HASH = 
'52b6f00222cdb3631d9914aee6b662961e924aa5'       # hash of my "three times" file
@@ -327,15 +418,25 @@ class ApiTest(TestCase):
                        print 'removing %d from library' % 
TITLE_CORRECTION_TARGET
                        client.libraryTitleRemove(TITLE_CORRECTION_TARGET)
 
-
-
+    
        def test_library(self):
                result = client.library(['date', 'genres', 'cover_small'])
-               print dump(result)
+               self.assertTrue(result.get('created'))
+               self.assertTrue(result.get('device_id'))
+               self.assertTrue(result.get('name'))
+               self.assertTrue(result.get('titles'))
+               self.assertTrue(type(result['titles']) is list)
+               
+               result2 = client.library(['id', 'cover_full', 'cover_large', 
'cover_medium', 'cover_small', 'cover_thumbnail', 'date', 'genres', 'url', 
'name', 'plot', 'released', 'trailer', 'type', 'year', 'runtime', 'directors', 
'writers', 'cast', 'watched'])
+               self.assertTrue(result2.get('created'))
+               self.assertTrue(result2.get('device_id'))
+               self.assertTrue(result2.get('name'))
+               self.assertTrue(result2.get('titles'))
+               self.assertTrue(type(result2['titles']) is list)
 
 if __name__ == '__main__':
        c = connection
-       client = ApiClient(c['base_url'], c['key'], c['secret'], c['username'], 
c['password'], c['device_id'], debugLvl = logging.WARNING, 
rel_api_url=c['rel_api_url'])
+       client = ApiClient(c['base_url'], c['key'], c['secret'], c['username'], 
c['password'], c['device_id'], debugLvl = logging.DEBUG, 
rel_api_url=c['rel_api_url'])
 
        logger = logging.getLogger()
 
diff --git a/plugin.video.synopsi/tests/fakeenv/xbmcvfs.py 
b/plugin.video.synopsi/tests/fakeenv/xbmcvfs.py
index 5724774..c9803ba 100644
--- a/plugin.video.synopsi/tests/fakeenv/xbmcvfs.py
+++ b/plugin.video.synopsi/tests/fakeenv/xbmcvfs.py
@@ -1,3 +1,7 @@
 import os
 
 mkdir = os.mkdir
+
+exists = os.path.exists
+
+File = open
diff --git a/plugin.video.synopsi/tests/utilities.unittest.py 
b/plugin.video.synopsi/tests/utilities.unittest.py
index 5430fd0..025bc8c 100644
--- a/plugin.video.synopsi/tests/utilities.unittest.py
+++ b/plugin.video.synopsi/tests/utilities.unittest.py
@@ -22,13 +22,32 @@ class UtilitiesTest(TestCase):
                        movie['os_title_hash'] = hash_opensubtitle(path)
                        return movie
                
-               print gethash(hash_path)
+               #~ print gethash(hash_path)
+                               
+               nonex_hash = gethash('x://non-existent')
+               self.assertEqual(nonex_hash['stv_title_hash'], None)
+               self.assertEqual(nonex_hash['os_title_hash'], None)
+               
+               
+       @skip('this is not supposed to work yet')       
+       def test_samba_hash(self):      
+               # test samba share hash (assumes local samba share)
+               # cannot run this test outside xbmc
+               local_path = 
'~/Videos/Movies/Intouchables/Intouchables.2011.FRENCH.720p.BluRay.x264.mkv'
+               local_hash = gethash(local_path)
+       
+               samba_path = 
'smb://crux/movies/Intouchables/Intouchables.2011.FRENCH.720p.BluRay.x264.mkv'
+               samba_hash = gethash(local_path)
+               
+               self.assertEqual(local_hash, samba_hash)
+               
        
        def test_xml_sources(self):
                sources = get_movie_sources()
                sources.sort(key=len, reverse=True)
                print sources
-               
+
+       
        def test_rel_path(self):
                path1 = 
'/home/smid/Videos/_testset/TVShows/xxx/the/movie/file.avi'
                rel1 = rel_path(path1)
diff --git a/plugin.video.synopsi/utilities.py 
b/plugin.video.synopsi/utilities.py
index a44bec2..ad8c8c5 100644
--- a/plugin.video.synopsi/utilities.py
+++ b/plugin.video.synopsi/utilities.py
@@ -1,5 +1,5 @@
 # xbmc
-import xbmc, xbmcgui, xbmcaddon, xbmcplugin
+import xbmc, xbmcgui, xbmcaddon, xbmcplugin, xbmcvfs
 import CommonFunctions
 
 # python standart lib
@@ -45,7 +45,7 @@ SEARCH_RESULT_LIMIT = 15
 
 
 # api request title properties
-reccoDefaultProps = ['id', 'cover_medium', 'name', 'type', 'watched']
+reccoDefaultProps = ['id', 'cover_medium', 'name', 'type', 'watched', 'year']
 defaultDetailProps = ['id', 'cover_full', 'cover_large', 'cover_medium', 
'cover_small', 'cover_thumbnail', 'date', 'genres', 'url', 'name', 'plot', 
'released', 'trailer', 'type', 'year', 'directors', 'writers', 'runtime', 
'cast']
 tvshowdefaultDetailProps = defaultDetailProps + ['seasons']
 defaultCastProps = ['name']
@@ -82,6 +82,21 @@ class ActionCode:
        VideoDialogShow = 900
        VideoDialogShowById = 910
 
+submenu_categories = [
+       (ActionCode.MovieRecco, "Movie Recommendations"),
+       (ActionCode.TVShows, "Popular TV Shows"),
+       (ActionCode.LocalMovieRecco, "Local Movie Recommendations"),
+       (ActionCode.LocalTVShows, "Local TV Shows"),
+       (ActionCode.UnwatchedEpisodes, "Unwatched TV Show Episodes"),
+       (ActionCode.UpcomingEpisodes, "Upcoming TV Episodes"),
+       (ActionCode.LoginAndSettings, "Login and Settings")
+]
+
+submenu_categories_dict = dict(submenu_categories)
+
+# we do not want the all local movies have listed in main menu, so this is an 
easy fix
+submenu_categories_dict[ActionCode.LocalMovies] = 'All Your Local Movies'
+
 # texts
 t_noupcoming = 'There are no upcoming episodes from your tracked TV shows.'
 t_nounwatched = 'There are no unwatched episodes in your TV Show tracking'
@@ -270,8 +285,8 @@ def dialog_yesno(msg):
 def clear_setting_cache():
        "Clear cached addon setting. Useful after update"
        settingsPath = os.path.join(__profile__, 'settings.xml')
-       if os.path.exists(settingsPath):
-               os.remove(settingsPath)
+       if xbmcvfs.exists(settingsPath):
+               xbmcvfs.delete(settingsPath)
 
 def setting_cache_append_string(string):
        settingsPath = os.path.join(__profile__, 'settings.xml')
@@ -307,7 +322,7 @@ class XMLRatingDialog(xbmcgui.WindowXMLDialog):
 
        def onClick(self, controlId):
                """
-               For controlID see: <control id="11" type="button"> in 
SynopsiDialog.xml
+               For controlID see: <control id="11" type="button"> in Rating.xml
                """
                if controlId == 11:
                        self.response = 1
@@ -345,7 +360,7 @@ class XMLLoginDialog(xbmcgui.WindowXMLDialog):
 
        def onClick(self, controlId):
                """
-               For controlID see: <control id="11" type="button"> in 
SynopsiDialog.xml
+               For controlID see: <control id="11" type="button"> in Rating.xml
                """
                # log(str('onClick:'+str(controlId)))
 
@@ -396,19 +411,40 @@ def is_protected(path):
        return False
 
 
+def textfilter(bytestring):
+       import string,re
+
+       norm = string.maketrans('', '') #builds list of all characters
+       non_alnum = string.translate(norm, norm, string.letters+string.digits) 
+       
+       trans_nontext=string.maketrans(non_alnum,'?'*len(non_alnum))
+       cleaned=string.translate(bytestring, trans_nontext)
+       
+       return cleaned
+
 def stv_hash(filepath):
        """
        New synopsi hash. Hashing the sedond 512 kB of a file using SHA1.
        """
 
+       chunk_offset = 524288
+       chunk_length = 524288
+       
        sha1 = hashlib.sha1()
 
        try:
-               with open(filepath, 'rb') as f:
-                       f.seek(524288, 0)
-                       sha1.update(f.read(524288))
+               f = xbmcvfs.File(filepath, 'r')
+               f.seek(chunk_offset, 0)
+               fcontent = f.read(chunk_length)
+               if len(fcontent) != chunk_length:
+                       raise IOError()
+                       
+               sha1.update(fcontent)
+               f.close()
+       
        except (IOError) as e:
-               raise HashError('Unable to hash file [%s]' % filepath)
+               log('Unable to hash file [%s]' % filepath)
+               return None
 
        return sha1.hexdigest()
 
@@ -421,10 +457,11 @@ def old_stv_hash(filepath):
        sha1 = hashlib.sha1()
 
        try:
-               with open(filepath, 'rb') as f:
-                       sha1.update(f.read(256))
-                       f.seek(-256, 2)
-                       sha1.update(f.read(256))
+               f = xbmcvfs.File(filepath, 'rb')
+               sha1.update(f.read(256))
+               f.seek(-256, 2)
+               sha1.update(f.read(256))
+               f.close()
        except (IOError) as e:
                return None
 
@@ -439,9 +476,9 @@ def hash_opensubtitle(name):
                longlongformat = 'q'  # long long
                bytesize = struct.calcsize(longlongformat)
 
-               _file = open(name, "rb")
+               _file = xbmcvfs.File(name, 'rb')
 
-               filesize = os.path.getsize(name)
+               filesize = _file.size()
                hash = filesize
 
                if filesize < 65536 * 2:
@@ -467,7 +504,8 @@ def hash_opensubtitle(name):
                return returnedhash
 
        except(IOError):
-               raise HashError('Unable to hash file [%s]' % name)
+               log('Unable to hash file [%s]' % name)
+               return None
 
 
 def generate_deviceid():
@@ -522,14 +560,17 @@ def get_api_port():
        If nothing is changed return default 9090.
        """
 
-       path = os.path.join('special://profile', 'advancedsettings.xml')
-       path = xbmc.translatePath(path)
-
        try:
-               tree = ET.parse(path)
-               root = tree.getroot()
+               path = os.path.join('special://profile', 'advancedsettings.xml')
+                               
+               f = xbmcvfs.File(path, 'r')
+               fcontent = f.read()
+               f.close()
+
+               root = et.fromstring(fcontent)
                nodes = root.findall('.//tcpport')
                value = int(nodes[0].text)
+
        except:
                value = 9090
 
@@ -579,15 +620,14 @@ def home_screen_fill(apiClient, cache):
 
                        # recco could return less than 5 items
                        if i < len(episode_recco):
-                               e = episode_recco[i]
-                               lib_item = cache.getByStvId(e['id'])
-                               log('episode %d %s' % (i, e['name']))
-                               log('lib_item %s' % (str(lib_item)))
-                               
WINDOW.setProperty("LatestEpisode.{0}.EpisodeTitle".format(i+1), e['name'])
-                               
WINDOW.setProperty("LatestEpisode.{0}.ShowTitle".format(i+1), e['name'])
-                               
WINDOW.setProperty("LatestEpisode.{0}.EpisodeNo".format(i+1), str(i))
-                               if lib_item:
-                                       
WINDOW.setProperty("LatestEpisode.{0}.Path".format(i+1), e['cover_large'])
+                               e = episode_recco[i]                            
+                               c_episode = cache.getByStvId(e['id'])
+                                       
+                                                               
+                               
WINDOW.setProperty("LatestEpisode.{0}.EpisodeTitle".format(i+1), 
e['tvshow_name'])                              # tv show name
+                               
WINDOW.setProperty("LatestEpisode.{0}.ShowTitle".format(i+1), e['name'])        
                                        # episode name
+                               
WINDOW.setProperty("LatestEpisode.{0}.EpisodeNo".format(i+1), 
get_episode_identifier(e))                # episode id string
+                               
WINDOW.setProperty("LatestEpisode.{0}.Path".format(i+1), c_episode['file'] if 
c_episode else '')
                                
WINDOW.setProperty("LatestEpisode.{0}.Thumb".format(i+1), e['cover_large'])
 
 
@@ -639,7 +679,7 @@ def get_rating():
        Get rating from user:
        1 = Amazing, 2 = OK, 3 = Terrible, 4 = Not rated
        """
-       ui = XMLRatingDialog("SynopsiDialog.xml", __addonpath__, "Default")
+       ui = XMLRatingDialog("Rating.xml", __addonpath__, "Default")
        ui.doModal()
        _response = ui.response
        del ui
@@ -691,26 +731,25 @@ def add_movie(movie, mode, iconimage):
        new_li = (u, li, isFolder)
 
        return new_li
-
-
+       
+       
 def show_categories():
        """
        Shows initial categories on home screen.
        """
        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 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")
-       add_directory("Login and Settings", "url", ActionCode.LoginAndSettings, 
"list.png")
+       for categoryCode, categoryName in submenu_categories:
+               add_directory(categoryName, "url", categoryCode, "list.png")
 
 def get_movie_sources():               
        userdata = xbmc.translatePath('special://userdata')
        sourceFilePath = os.path.join(userdata, 'sources.xml')
-       tree = et.parse(sourceFilePath)
-       root = tree.getroot()
+       
+       f = xbmcvfs.File(sourceFilePath, 'r')
+       fcontent = f.read()
+       f.close()
+
+       root = et.fromstring(fcontent)
        el = root.findall('video/source/path')
        return sorted([i.text for i in el], key=len, reverse=True)
 
@@ -722,4 +761,5 @@ def rel_path(realpath):
        
        return realpath
                
-       
+def get_episode_identifier(item):
+       return 'S%sE%s' % (item.get('season_number', '??'), 
item.get('episode_number', '??'))

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

commit 5c0976bb52b8a40c38c7798eb80977a82641b549
Author: beenje <[email protected]>
Date:   Tue Mar 19 21:47:54 2013 +0100

    [plugin.video.vidstatsx_com] updated to version 2.0.4

diff --git a/plugin.video.vidstatsx_com/addon.xml 
b/plugin.video.vidstatsx_com/addon.xml
index ecc7357..d206b62 100644
--- a/plugin.video.vidstatsx_com/addon.xml
+++ b/plugin.video.vidstatsx_com/addon.xml
@@ -1,17 +1,17 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<addon id="plugin.video.vidstatsx_com" name="VidStatsX.com" version="2.0.3" 
provider-name="AddonScriptorDE">
+<addon id="plugin.video.vidstatsx_com" name="VidStatsX.com" version="2.0.4" 
provider-name="AddonScriptorDE">
     <requires>
         <import addon="xbmc.python" version="2.1.0"/>
         <import addon="plugin.video.youtube" version="3.0.0"/>
+        <import addon="plugin.video.youtube.channels" version="1.0.0"/>
     </requires>
     <extension point="xbmc.python.pluginsource" library="default.py">
         <provides>video</provides>
     </extension>
     <extension point="xbmc.addon.metadata">
         <platform>all</platform>
-        <summary lang="en">View various YouTube channel and video charts (for 
example by category/country) from VidStatsX.com and organize your own favourite 
channels</summary>
+        <summary lang="en">View various YouTube channel and video charts from 
VidStatsX.com (for example by category/country)</summary>
         <language></language>
-        <description lang="en">View various YouTube channel and video charts 
(for example by category/country) from VidStatsX.com and organize your own 
favourite channels</description>
-    <platform>all</platform>
+        <description lang="en">View various YouTube channel and video charts 
from VidStatsX.com (for example by category/country)</description>
     </extension>
 </addon>
diff --git a/plugin.video.vidstatsx_com/changelog.txt 
b/plugin.video.vidstatsx_com/changelog.txt
index bb91eab..7166477 100644
--- a/plugin.video.vidstatsx_com/changelog.txt
+++ b/plugin.video.vidstatsx_com/changelog.txt
@@ -7,3 +7,5 @@
 1.0.3 / 2.0.3
 - Fixed site changes
 - Added context menu to add channel manually
+1.0.4 / 2.0.4
+- Integrated "YouTube Channels" support
diff --git a/plugin.video.vidstatsx_com/default.py 
b/plugin.video.vidstatsx_com/default.py
index 5f552f3..f5a8597 100644
--- a/plugin.video.vidstatsx_com/default.py
+++ b/plugin.video.vidstatsx_com/default.py
@@ -20,27 +20,10 @@ forceViewMode=addon.getSetting("forceViewMode")
 viewMode=str(addon.getSetting("viewMode"))
 
 def index():
-        addFDir(translation(30027),"","favoriteChannels","")
         addDir(translation(30001),"","mostSubscribedMain","")
         addDir(translation(30002),"","mostViewedMain","")
         addDir(translation(30003),"","videoChartsMain","")
         xbmcplugin.endOfDirectory(pluginhandle)
-        if forceViewMode=="true":
-          xbmc.executebuiltin('Container.SetViewMode('+viewMode+')')
-
-def favoriteChannels():
-        xbmcplugin.addSortMethod(pluginhandle, xbmcplugin.SORT_METHOD_LABEL)
-        if os.path.exists(channelFavsFile):
-          fh = open(channelFavsFile, 'r')
-          all_lines = fh.readlines()
-          for line in all_lines:
-            user=line[line.find("###USER###=")+11:]
-            user=user[:user.find("###END###")]
-            addChannelFavDir(user,user+"#1",'showYoutubeOrderBy',"")
-          fh.close()
-        xbmcplugin.endOfDirectory(pluginhandle)
-        if forceViewMode=="true":
-          xbmc.executebuiltin('Container.SetViewMode('+viewMode+')')
 
 def mostSubscribedMain():
         url="http://vidstatsx.com/youtube-top-100-most-subscribed-channels";
@@ -49,8 +32,6 @@ def mostSubscribedMain():
         addDir(translation(30006),url,"showLanguages","")
         
addDir("VEVO","http://vidstatsx.com/vevo-most-subscribed","listChannels","";)
         xbmcplugin.endOfDirectory(pluginhandle)
-        if forceViewMode=="true":
-          xbmc.executebuiltin('Container.SetViewMode('+viewMode+')')
 
 def mostViewedMain():
         url="http://vidstatsx.com/youtube-top-100-most-viewed";
@@ -58,22 +39,16 @@ def mostViewedMain():
         addDir(translation(30005),url,"showCategories","")
         addDir(translation(30006),url,"showLanguages","")
         xbmcplugin.endOfDirectory(pluginhandle)
-        if forceViewMode=="true":
-          xbmc.executebuiltin('Container.SetViewMode('+viewMode+')')
 
 def videoChartsMain():
         
addDir(translation(30004),"http://vidstatsx.com/most-popular-videos-today","videoChartsOrderBy","";)
         showCategories("http://vidstatsx.com/most-popular-videos-today";)
-        if forceViewMode=="true":
-          xbmc.executebuiltin('Container.SetViewMode('+viewMode+')')
 
 def topGainersMain():
         
addDir(translation(30021),"http://vidstatsx.com/top-100-1h-sub-gains","listChannels","";)
         
addDir(translation(30023),"http://vidstatsx.com/top-100-24h-sub-gains","listChannels","";)
         
addDir(translation(30024),"http://vidstatsx.com/top-100-7d-sub-gains","listChannels","";)
         xbmcplugin.endOfDirectory(pluginhandle)
-        if forceViewMode=="true":
-          xbmc.executebuiltin('Container.SetViewMode('+viewMode+')')
 
 def listChannels(url):
         content = getUrlVSX(url)
@@ -93,11 +68,9 @@ def listChannels(url):
           count=count.replace('<span class="gray">K</span>','K')
           match=re.compile('href="//www.youtube.com/user/(.+?)"', 
re.DOTALL).findall(entry)
           user=match[0]
-          title=user+"  -  "+count
+          title="[B]"+user+"[/B]  -  "+count
           addChannelDir(title,user+"#1",'showYoutubeOrderBy',"")
         xbmcplugin.endOfDirectory(pluginhandle)
-        if forceViewMode=="true":
-          xbmc.executebuiltin('Container.SetViewMode('+viewMode+')')
 
 def showCategories(url):
         if url=="http://vidstatsx.com/most-popular-videos-today":
@@ -108,24 +81,18 @@ def showCategories(url):
         for cat in cats:
           
addDir(cat[0],url.replace("-most-subscribed-channels","-most-subscribed-"+cat[1]+"-channels").replace("-most-viewed","-most-viewed-"+cat[1]).replace("most-popular-videos-","most-popular-"+cat[1]+"-videos-"),type,"")
         xbmcplugin.endOfDirectory(pluginhandle)
-        if forceViewMode=="true":
-          xbmc.executebuiltin('Container.SetViewMode('+viewMode+')')
 
 def showLanguages(url):
         cats=[[translation(30050), "argentina-ar"],[translation(30051), 
"australia-au"],[translation(30052), "brazil-br"],[translation(30053), 
"canada-ca"],[translation(30054), "czech-republic-cz"],[translation(30055), 
"france-fr"],[translation(30056), "germany-de"],[translation(30057), 
"great-britain-gb"],[translation(30058), "hong-kong-hk"],[translation(30059), 
"india-in"],[translation(30060), "ireland-ie"],[translation(30061), 
"israel-il"],[translation(30062), "italy-it"],[translation(30063), 
"japan-jp"],[translation(30064), "mexico-mx"],[translation(30065), 
"netherlands-nl"],[translation(30066), "new-zealand-nz"],[translation(30067), 
"poland-pl"],[translation(30068), "russia-ru"],[translation(30069), 
"south-africa-za"],[translation(30070), "south-korea-kr"],[translation(30071), 
"spain-es"],[translation(30072), "sweden-se"],[translation(30073), 
"taiwan-tw"],[translation(30074), "united-states-us"]]
         for cat in cats:
           
addDir(cat[0],url.replace("-most-subscribed-channels","-most-subscribed-"+cat[1]+"-channels").replace("-most-viewed","-most-viewed-"+cat[1]),"listChannels","")
         xbmcplugin.endOfDirectory(pluginhandle)
-        if forceViewMode=="true":
-          xbmc.executebuiltin('Container.SetViewMode('+viewMode+')')
 
 def showYoutubeOrderBy(url):
         addDir(translation(30009),url+"#published","listVideos","")
         addDir(translation(30010),url+"#viewCount","listVideos","")
         addDir(translation(30011),url+"#rating","listVideos","")
         xbmcplugin.endOfDirectory(pluginhandle)
-        if forceViewMode=="true":
-          xbmc.executebuiltin('Container.SetViewMode('+viewMode+')')
 
 def videoChartsOrderBy(url):
         if url=="http://vidstatsx.com/recently-featured-videos":
@@ -138,8 +105,6 @@ def videoChartsOrderBy(url):
             
addDir(translation(30015),url.replace("/most-popular-","/most-commented-"),"videoChartsOrderBy2","")
             
addDir(translation(30016),url.replace("/most-popular-","/most-responded-"),"videoChartsOrderBy2","")
             xbmcplugin.endOfDirectory(pluginhandle)
-            if forceViewMode=="true":
-              xbmc.executebuiltin('Container.SetViewMode('+viewMode+')')
           elif videoChartsSortType==translation(30010):
             videoChartsOrderBy2(url)
           elif videoChartsSortType==translation(30013):
@@ -157,8 +122,6 @@ def videoChartsOrderBy2(url):
           
addDir(translation(30018),url.replace("-today","-this-week"),"listVideoCharts","")
           
addDir(translation(30019),url.replace("-today","-this-month"),"listVideoCharts","")
           xbmcplugin.endOfDirectory(pluginhandle)
-          if forceViewMode=="true":
-            xbmc.executebuiltin('Container.SetViewMode('+viewMode+')')
         elif videoChartsSortTime==translation(30017):
             listVideoCharts(url)
         elif videoChartsSortTime==translation(30018):
@@ -171,41 +134,40 @@ def listVideos(params):
         user=spl[0]
         index=spl[1]
         orderby=spl[2]
-        content = 
getUrl("http://gdata.youtube.com/feeds/api/videos?author="+user+"&racy=include&max_results=25&start-index="+index+"&orderby="+orderby)
+        content = 
getUrl("http://gdata.youtube.com/feeds/api/videos?author="+user+"&racy=include&max-results=50&start-index="+index+"&orderby="+orderby+"&v=2";)
         
match=re.compile("<openSearch:totalResults>(.+?)</openSearch:totalResults><openSearch:startIndex>(.+?)</openSearch:startIndex>",
 re.DOTALL).findall(content)
         maxIndex=int(match[0][0])
         startIndex=int(match[0][1])
-        spl=content.split('<entry>')
+        spl=content.split('<entry')
         for i in range(1,len(spl),1):
-          try:
-            entry=spl[i]
-            
match=re.compile('<id>http://gdata.youtube.com/feeds/api/videos/(.+?)</id>', 
re.DOTALL).findall(entry)
-            id=match[0]
-            match=re.compile("viewCount='(.+?)'", re.DOTALL).findall(entry)
+          entry=spl[i]
+          match=re.compile('<yt:videoid>(.+?)</yt:videoid>', 
re.DOTALL).findall(entry)
+          id=match[0]
+          match=re.compile("viewCount='(.+?)'", re.DOTALL).findall(entry)
+          viewCount="0"
+          if len(match)>0:
             viewCount=match[0]
-            match=re.compile("duration='(.+?)'", re.DOTALL).findall(entry)
-            durationTemp=int(match[0])
-            min=(durationTemp/60)+1
-            sec=durationTemp%60
-            duration=str(min)+":"+str(sec)
-            match=re.compile("<author><name>(.+?)</name>", 
re.DOTALL).findall(entry)
-            author=match[0]
-            match=re.compile("<title type='text'>(.+?)</title>", 
re.DOTALL).findall(entry)
-            title=match[0]
-            title=cleanTitle(title)
-            match=re.compile("<content type='text'>(.+?)</content>", 
re.DOTALL).findall(entry)
-            desc=""
-            if len(match)>0:
-              desc=match[0]
-              desc=cleanTitle(desc)
-            match=re.compile("<published>(.+?)T", re.DOTALL).findall(entry)
-            date=match[0]
-            thumb="http://img.youtube.com/vi/"+id+"/0.jpg";
-            addLink(title,id,'playVideo',thumb,"Date: "+date+"; Views: 
"+viewCount+"\n"+desc,duration,author)
-          except:
-            pass
-        if startIndex+25<=maxIndex:
-          
addDir(translation(30075),user+"#"+str(int(index)+25)+"#"+orderby,'listVideos',"")
+          match=re.compile("duration='(.+?)'", re.DOTALL).findall(entry)
+          durationTemp=int(match[0])
+          min=(durationTemp/60)+1
+          sec=durationTemp%60
+          duration=str(min)+":"+str(sec)
+          match=re.compile("<author><name>(.+?)</name>", 
re.DOTALL).findall(entry)
+          author=match[0]
+          match=re.compile("<media:title type='plain'>(.+?)</media:title>", 
re.DOTALL).findall(entry)
+          title=match[0]
+          title=cleanTitle(title)
+          match=re.compile("<media:description 
type='plain'>(.+?)</media:title>", re.DOTALL).findall(entry)
+          desc=""
+          if len(match)>0:
+            desc=match[0]
+            desc=cleanTitle(desc)
+          match=re.compile("<published>(.+?)T", re.DOTALL).findall(entry)
+          date=match[0]
+          thumb="http://img.youtube.com/vi/"+id+"/0.jpg";
+          addLink(title,id,'playVideo',thumb,"Date: "+date+"; Views: 
"+viewCount+"\n"+desc,duration,author)
+        if startIndex+50<=maxIndex:
+          
addDir(translation(30007),user+"#"+str(int(index)+50)+"#"+orderby,'listVideos',"")
         xbmcplugin.endOfDirectory(pluginhandle)
         if forceViewMode=="true":
           xbmc.executebuiltin('Container.SetViewMode('+viewMode+')')
@@ -232,65 +194,36 @@ def listVideoCharts(url):
           xbmc.executebuiltin('Container.SetViewMode('+viewMode+')')
 
 def playVideo(youtubeID):
+        url = getYoutubeUrl(youtubeID)
+        listitem = xbmcgui.ListItem(path=url)
+        return xbmcplugin.setResolvedUrl(pluginhandle, True, listitem)
+
+def getYoutubeUrl(youtubeID):
         if xbox==True:
           url = 
"plugin://video/YouTube/?path=/root/video&action=play_video&videoid=" + 
youtubeID
         else:
           url = 
"plugin://plugin.video.youtube/?path=/root/video&action=play_video&videoid=" + 
youtubeID
-        listitem = xbmcgui.ListItem(path=url)
-        return xbmcplugin.setResolvedUrl(pluginhandle, True, listitem)
-
-def favourites(param):
-        mode=param[param.find("###MODE###=")+11:]
-        mode=mode[:mode.find("###")]
-        channelEntry=param[param.find("###USER###="):]
-
-        if mode=="ADD":
-          if os.path.exists(channelFavsFile):
-            fh = open(channelFavsFile, 'r')
-            content=fh.read()
-            fh.close()
-            if content.find(channelEntry)==-1:
-              fh=open(channelFavsFile, 'a')
-              fh.write(channelEntry+"\n")
-              fh.close()
-          else:
-            fh=open(channelFavsFile, 'a')
-            fh.write(channelEntry+"\n")
-            fh.close()
-          
xbmc.executebuiltin('XBMC.Notification(Info:,'+translation(30077)+',2000)')
-        elif mode=="REMOVE":
-          refresh=param[param.find("###REFRESH###=")+14:]
-          refresh=refresh[:refresh.find("###USER###=")]
-          fh = open(channelFavsFile, 'r')
-          content=fh.read()
-          fh.close()
-          entry=content[content.find(channelEntry):]
-          fh=open(channelFavsFile, 'w')
-          fh.write(content.replace(channelEntry+"\n",""))
-          fh.close()
-          if refresh=="TRUE":
-            xbmc.executebuiltin("Container.Refresh")
-          
xbmc.executebuiltin('XBMC.Notification(Info:,'+translation(30078)+',2000)')
+        return url
 
-def addFav():
-        keyboard = xbmc.Keyboard('', translation(30076))
-        keyboard.doModal()
-        if keyboard.isConfirmed() and keyboard.getText():
-          name = keyboard.getText()
-          channelEntry="###USER###="+name+"###END###"
-          if os.path.exists(channelFavsFile):
-            fh = open(channelFavsFile, 'r')
-            content=fh.read()
-            fh.close()
-            if content.find(channelEntry)==-1:
-              fh=open(channelFavsFile, 'a')
-              fh.write(channelEntry+"\n")
-              fh.close()
-          else:
-            fh=open(channelFavsFile, 'a')
-            fh.write(channelEntry+"\n")
-            fh.close()
-          
xbmc.executebuiltin('XBMC.Notification(Info:,'+translation(30077)+',2000)')
+def playChannel(id):
+        content = 
getUrl("http://gdata.youtube.com/feeds/api/videos?author="+id+"&racy=include&max_results=25&start-index=1&orderby=published";)
+        spl=content.split('<entry>')
+        playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
+        playlist.clear()
+        for i in range(1,len(spl),1):
+          try:
+            entry=spl[i]
+            
match=re.compile('<id>http://gdata.youtube.com/feeds/api/videos/(.+?)</id>', 
re.DOTALL).findall(entry)
+            id=match[0]
+            url = getYoutubeUrl(id)
+            match=re.compile("<title type='text'>(.+?)</title>", 
re.DOTALL).findall(entry)
+            title=match[0]
+            title=cleanTitle(title)
+            listitem = xbmcgui.ListItem(title)
+            playlist.add(url,listitem)
+          except:
+            pass
+        xbmc.Player().play(playlist)
 
 def getUrl(url):
         req = urllib2.Request(url)
@@ -332,32 +265,15 @@ def addDir(name,url,mode,iconimage):
         
ok=xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=u,listitem=liz,isFolder=True)
         return ok
 
-def addFDir(name,url,mode,iconimage):
-        u=sys.argv[0]+"?url="+urllib.quote_plus(url)+"&mode="+str(mode)
-        ok=True
-        liz=xbmcgui.ListItem(name, iconImage="DefaultFolder.png", 
thumbnailImage=iconimage)
-        liz.setInfo( type="Video", infoLabels={ "Title": name } )
-        liz.addContextMenuItems([(translation(30076), 
'XBMC.RunPlugin(plugin://'+addonID+'/?mode=addFav)',)])
-        
ok=xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=u,listitem=liz,isFolder=True)
-        return ok
-
 def addChannelDir(name,url,mode,iconimage):
         u=sys.argv[0]+"?url="+urllib.quote_plus(url)+"&mode="+str(mode)
         ok=True
-        liz=xbmcgui.ListItem(name, iconImage="DefaultFolder.png", 
thumbnailImage=iconimage)
-        liz.setInfo( type="Video", infoLabels={ "Title": name } )
-        playListInfos="###MODE###=ADD###USER###="+name[:name.find("  
-")]+"###END###"
-        liz.addContextMenuItems([(translation(30028), 
'XBMC.RunPlugin(plugin://'+addonID+'/?mode=favourites&url='+urllib.quote_plus(playListInfos)+')',)])
-        
ok=xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=u,listitem=liz,isFolder=True)
-        return ok
-
-def addChannelFavDir(name,url,mode,iconimage):
-        u=sys.argv[0]+"?url="+urllib.quote_plus(url)+"&mode="+str(mode)
-        ok=True
-        liz=xbmcgui.ListItem(name, iconImage="DefaultFolder.png", 
thumbnailImage=iconimage)
+        if iconimage=="": iconimage="DefaultFolder.png"
+        liz=xbmcgui.ListItem(name, iconImage=iconimage, 
thumbnailImage=iconimage)
         liz.setInfo( type="Video", infoLabels={ "Title": name } )
-        
playListInfos="###MODE###=REMOVE###REFRESH###=TRUE###USER###="+name+"###END###"
-        liz.addContextMenuItems([(translation(30029), 
'XBMC.RunPlugin(plugin://'+addonID+'/?mode=favourites&url='+urllib.quote_plus(playListInfos)+')',)])
+        user=name[:name.find("  -")].replace("[B]","").replace("[/B]","")
+        playListInfos="ADD#"+user+"#"+user+"#"+iconimage+"#"
+        liz.addContextMenuItems([(translation(30079), 
'XBMC.RunPlugin(plugin://'+addonID+'/?mode=playChannel&url='+user+')',),(translation(30076),
 
'RunPlugin(plugin://plugin.video.youtube.channels/?mode=addChannel&url='+urllib.quote_plus(name+"#"+name+"#"+iconimage+"#")+')',)])
         
ok=xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=u,listitem=liz,isFolder=True)
         return ok
 
@@ -406,9 +322,7 @@ elif mode == 'listVideoCharts':
     listVideoCharts(url)
 elif mode == 'playVideo':
     playVideo(url)
-elif mode == 'favourites':
-    favourites(url)
-elif mode == 'addFav':
-    addFav()
+elif mode == 'playChannel':
+    playChannel(url)
 else:
     index()
diff --git a/plugin.video.vidstatsx_com/resources/language/English/strings.xml 
b/plugin.video.vidstatsx_com/resources/language/English/strings.xml
index 82a8280..1d81a40 100644
--- a/plugin.video.vidstatsx_com/resources/language/English/strings.xml
+++ b/plugin.video.vidstatsx_com/resources/language/English/strings.xml
@@ -69,8 +69,7 @@
   <string id="30074">United States</string>
   <string id="30075">Next Page</string>
   <string id="30076">Add channel</string>
-  <string id="30077">Added channel to favourites</string>
-  <string id="30078">Removed channel from favourites</string>
+  <string id="30079">Play channel</string>
   <string id="30101">Force View</string>
   <string id="30102">ViewID</string>
   <string id="30103">Video Charts Order (Type)</string>
diff --git a/plugin.video.vidstatsx_com/resources/language/German/strings.xml 
b/plugin.video.vidstatsx_com/resources/language/German/strings.xml
index 5ae1b42..c75a4da 100644
--- a/plugin.video.vidstatsx_com/resources/language/German/strings.xml
+++ b/plugin.video.vidstatsx_com/resources/language/German/strings.xml
@@ -6,4 +6,5 @@
   <string id="30029">Aus Addon Favs entfernen</string>
   <string id="30075">Nächste Seite</string>
   <string id="30076">Channel hinzufügen</string>
+  <string id="30079">Channel abspielen</string>
 </strings>

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


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

Summary of changes:
 plugin.video.synopsi/_src/usefull_queries.sql      |    4 +-
 plugin.video.synopsi/addon.xml                     |    4 +-
 plugin.video.synopsi/apiclient.py                  |    4 +-
 plugin.video.synopsi/app_apiclient.py              |    8 +-
 plugin.video.synopsi/cache.py                      |   20 +-
 plugin.video.synopsi/changelog.txt                 |    8 +
 plugin.video.synopsi/dialog.py                     |   79 +-
 plugin.video.synopsi/loggable.py                   |    2 +-
 .../Default/720p/{SynopsiDialog.xml => Rating.xml} |    0
 .../resources/skins/Default/720p/ViewsFileMode.xml |  613 --------
 .../skins/Default/720p/ViewsVideoLibrary.xml       | 1545 --------------------
 .../skins/Default/720p/customLoginDialog.xml       |  155 --
 .../skins/Default/720p/custom_MyVideoNav.xml       |   32 +-
 .../resources/skins/Default/720p/includes.xml      |    3 -
 plugin.video.synopsi/tests/apiclient.unittest.py   |  143 ++-
 plugin.video.synopsi/tests/fakeenv/xbmcvfs.py      |    4 +
 plugin.video.synopsi/tests/utilities.unittest.py   |   23 +-
 plugin.video.synopsi/utilities.py                  |  128 ++-
 plugin.video.vidstatsx_com/addon.xml               |    8 +-
 plugin.video.vidstatsx_com/changelog.txt           |    2 +
 plugin.video.vidstatsx_com/default.py              |  210 +--
 .../resources/language/English/strings.xml         |    3 +-
 .../resources/language/German/strings.xml          |    1 +
 .../LICENSE.txt                                    |    0
 plugin.video.youtube.channels/addon.xml            |   17 +
 .../changelog.txt                                  |    4 +-
 plugin.video.youtube.channels/default.py           |  526 +++++++
 .../icon.png                                       |  Bin 38254 -> 38254 bytes
 .../iconVSX.png                                    |  Bin 42752 -> 42752 bytes
 .../resources/language/English/strings.xml         |   48 +
 .../resources/language/German/strings.xml          |   41 +
 .../resources/settings.xml                         |   29 +
 32 files changed, 1082 insertions(+), 2582 deletions(-)
 rename plugin.video.synopsi/resources/skins/Default/720p/{SynopsiDialog.xml => 
Rating.xml} (100%)
 delete mode 100644 
plugin.video.synopsi/resources/skins/Default/720p/ViewsFileMode.xml
 delete mode 100644 
plugin.video.synopsi/resources/skins/Default/720p/ViewsVideoLibrary.xml
 delete mode 100644 
plugin.video.synopsi/resources/skins/Default/720p/customLoginDialog.xml
 delete mode 100644 
plugin.video.synopsi/resources/skins/Default/720p/includes.xml
 copy {plugin.audio.booksshouldbefree_com => 
plugin.video.youtube.channels}/LICENSE.txt (100%)
 create mode 100644 plugin.video.youtube.channels/addon.xml
 copy {plugin.video.euronews_com => 
plugin.video.youtube.channels}/changelog.txt (52%)
 create mode 100644 plugin.video.youtube.channels/default.py
 copy {plugin.video.youtube => plugin.video.youtube.channels}/icon.png (100%)
 copy plugin.video.vidstatsx_com/icon.png => 
plugin.video.youtube.channels/iconVSX.png (100%)
 create mode 100644 
plugin.video.youtube.channels/resources/language/English/strings.xml
 create mode 100644 
plugin.video.youtube.channels/resources/language/German/strings.xml
 create mode 100644 plugin.video.youtube.channels/resources/settings.xml


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_mar
_______________________________________________
Xbmc-addons mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/xbmc-addons

Reply via email to