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) +
'&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