The branch, eden has been updated
via b930921d4d8c303740616ae9385e0c5db9c53fb6 (commit)
via 5a31ca4317ec7eef67030bc732566a91cdcc21af (commit)
from 309de40e60c623ee50ecd0fd3beecfb7778cee4e (commit)
- Log -----------------------------------------------------------------
http://xbmc.git.sourceforge.net/git/gitweb.cgi?p=xbmc/plugins;a=commit;h=b930921d4d8c303740616ae9385e0c5db9c53fb6
commit b930921d4d8c303740616ae9385e0c5db9c53fb6
Author: spiff <[email protected]>
Date: Fri Oct 19 15:30:13 2012 +0200
[plugin.video.twitch] updated to version 0.2.2
diff --git a/plugin.video.twitch/addon.xml b/plugin.video.twitch/addon.xml
index 0ee85b6..17f304a 100644
--- a/plugin.video.twitch/addon.xml
+++ b/plugin.video.twitch/addon.xml
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
-<addon id='plugin.video.twitch' version='0.2.1' name='TwitchTV'
provider-name='StateOfTheArt'>
+<addon id='plugin.video.twitch' version='0.2.2' name='TwitchTV'
provider-name='StateOfTheArt'>
<requires>
<import addon='xbmc.python' version='2.0'/>
<import addon='script.module.simplejson' version='2.0.10'/>
diff --git a/plugin.video.twitch/changelog.txt
b/plugin.video.twitch/changelog.txt
index ca1a498..e383461 100644
--- a/plugin.video.twitch/changelog.txt
+++ b/plugin.video.twitch/changelog.txt
@@ -21,4 +21,6 @@
- added selection of prefered video settings
- added fallback-function, if selected video does not support video settings
0.2.1
-- added featured streams section
\ No newline at end of file
+- added featured streams section
+0.2.2
+- added teams section - thx to kokarn
\ No newline at end of file
diff --git a/plugin.video.twitch/default.py b/plugin.video.twitch/default.py
index b9080d3..d726d05 100644
--- a/plugin.video.twitch/default.py
+++ b/plugin.video.twitch/default.py
@@ -1,280 +1,355 @@
-import xbmcplugin
-import xbmcgui
-import sys
-import urllib2,urllib,re
-import xbmcaddon
-import os
-import xbmcvfs
-try:
- import json
-except:
- import simplejson as json
-
-thisPlugin = int(sys.argv[1])
-settings = xbmcaddon.Addon(id='plugin.video.twitch')
-httpHeaderUserAgent = 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0)
Gecko/20100101 Firefox/6.0'
-translation = settings.getLocalizedString
-ITEMS_PER_SITE=20
-
-def downloadWebData(url):
- try:
- req = urllib2.Request(url)
- req.add_header('User-Agent', httpHeaderUserAgent)
- response = urllib2.urlopen(req)
- data=response.read()
- response.close()
- return data
- except urllib2.URLError, e:
- xbmc.executebuiltin("XBMC.Notification("+translation(31000)+"," +
translation(32001) +")")
-
-def createMainListing():
- addDir(translation(30005),'','featured','')
- addDir(translation(30001),'','games','')
- addDir(translation(30002),'','following','')
- addDir(translation(30003),'','search','')
- addDir(translation(30004),'','settings','')
- xbmcplugin.endOfDirectory(thisPlugin)
-
-def createFollowingList():
- username = settings.getSetting('username').lower()
- if not username:
- settings.openSettings()
- username = settings.getSetting('username').lower()
- jsonString =
downloadWebData(url='http://api.justin.tv/api/user/favorites/'+username+'.json?limit=40&offset=0')
- xmlDataOnlineStreams =
downloadWebData(url='http://api.justin.tv/api/stream/list.xml')
- if jsonString is None or xmlDataOnlineStreams is None:
- return
- jsonData=json.loads(jsonString)
- for x in jsonData:
- name = x['status']
- image = x['image_url_huge']
- loginname = x['login']
- if len(name) <= 0:
- name = loginname
- if xmlDataOnlineStreams.count('<login>'+loginname+'</login>') > 0:
- addLink(name,loginname,'play',image,loginname)
- xbmcplugin.endOfDirectory(thisPlugin)
-
-def createListOfFeaturedStreams():
-
jsonString=downloadWebData(url='https://api.twitch.tv/kraken/streams/featured')
- jsonData=json.loads(jsonString)
- for x in jsonData['featured']:
- try:
- image = x['stream']['channel']['logo']
- image = image.replace("http://","",1)
- image = urllib.quote(image)
- image = 'http://' + image
- except:
- image = ""
- name = x['stream']['channel']['status']
- if name == '':
- name = x['stream']['channel']['display_name']
- channelname = x['stream']['channel']['name']
- addLink(name,'...','play',image,channelname)
- xbmcplugin.endOfDirectory(thisPlugin)
-
-def createListOfGames(index=0):
-
jsonString=downloadWebData(url='https://api.twitch.tv/kraken/games/top?limit='+str(ITEMS_PER_SITE)+'&offset='+str(index*ITEMS_PER_SITE))
- jsonData=json.loads(jsonString)
- for x in jsonData['top']:
- try:
- name = str(x['game']['name'])
- game = urllib.quote(name)
- image = ''
- except:
- continue
- try:
- image = x['game']['images']['super']
- image = image.replace("http://","",1)
- image = urllib.quote(image)
- image = 'http://' + image
- except:
- image = ''
- addDir(name,game,'channel',image)
- if len(jsonData['top']) >= ITEMS_PER_SITE:
- addDir(translation(31001),'','games','',index+1)
- xbmcplugin.endOfDirectory(thisPlugin)
-
-def search():
- keyboard = xbmc.Keyboard('', translation(30101))
- keyboard.doModal()
- if keyboard.isConfirmed() and keyboard.getText():
- search_string = urllib.quote_plus(keyboard.getText())
- sdata =
downloadWebData('http://api.swiftype.com/api/v1/public/engines/search.json?callback=jQuery1337&q='+search_string+'&engine_key=9NXQEpmQPwBEz43TM592&page=1&per_page='+str(ITEMS_PER_SITE))
- sdata = sdata.replace('jQuery1337','');
- sdata = sdata[1:len(sdata)-1]
- jdata = json.loads(sdata)
- records = jdata['records']['broadcasts']
- for x in records:
- addLink(x['title'],x['user'],'play',x['thumbnail'],x['user'])
- xbmcplugin.endOfDirectory(thisPlugin)
-
-def createListForGame(gameName, index=0):
-
jsonString=downloadWebData(url='https://api.twitch.tv/kraken/streams?game='+gameName+'&limit='+str(ITEMS_PER_SITE)+'&offset='+str(index*ITEMS_PER_SITE))
- jsonData=json.loads(jsonString)
- for x in jsonData['streams']:
- try:
- image = x['channel']['logo']
- image = image.replace("http://","",1)
- image = urllib.quote(image)
- image = 'http://' + image
- except:
- image = ""
- name = x['channel']['status']
- if name == '':
- name = x['channel']['display_name']
- channelname = x['channel']['name']
- addLink(name,'...','play',image,channelname)
- if len(jsonData['streams']) >= ITEMS_PER_SITE:
- addDir(translation(31001),url,'channel','',index+1)
- xbmcplugin.endOfDirectory(thisPlugin)
-
-def addLink(name,url,mode,iconimage,channelname):
-
u=sys.argv[0]+"?url="+urllib.quote_plus(url)+"&mode="+str(mode)+"&channelname="+channelname
- ok=True
- liz=xbmcgui.ListItem(name, iconImage="DefaultVideo.png",
thumbnailImage=iconimage)
- liz.setInfo( type="Video", infoLabels={ "Title": name } )
- liz.setProperty('IsPlayable', 'true')
-
ok=xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=u,listitem=liz)
- return ok
-
-def addDir(name,url,mode,iconimage,index=0):
-
u=sys.argv[0]+"?url="+urllib.quote_plus(url)+"&mode="+str(mode)+"&siteIndex="+str(index)
- ok=True
- liz=xbmcgui.ListItem(name, iconImage="DefaultFolder.png",
thumbnailImage=iconimage)
- liz.setInfo( type="Video", infoLabels={ "Title": name } )
-
ok=xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=u,listitem=liz,isFolder=True)
- return ok
-
-def get_request(url, headers=None):
- try:
- if headers is None:
- headers = {'User-agent' : httpHeaderUserAgent,
- 'Referer' : 'http://www.justin.tv/'}
- req = urllib2.Request(url,None,headers)
- response = urllib2.urlopen(req)
- link=response.read()
- response.close()
- return link
- except urllib2.URLError, e:
- errorStr = str(e.read())
- if hasattr(e, 'code'):
- if str(e.code) == '403':
- if 'archive' in url:
-
xbmc.executebuiltin("XBMC.Notification("+translation(31000)+","
+translation(32003)+ " " +name+")")
-
xbmc.executebuiltin("XBMC.Notification("+translation(31000)+"," +
translation(32001) +")")
-
-
-def parameters_string_to_dict(parameters):
- ''' Convert parameters encoded in a URL to a dict. '''
- paramDict = {}
- if parameters:
- paramPairs = parameters[1:].split("&")
- for paramsPair in paramPairs:
- paramSplits = paramsPair.split('=')
- if (len(paramSplits)) == 2:
- paramDict[paramSplits[0]] = paramSplits[1]
- return paramDict
-
-def getSwfUrl(channel_name):
- ''' Helper method to grab the swf url, resolving HTTP 301/302 along
the way'''
- base_url =
'http://www.justin.tv/widgets/live_embed_player.swf?channel=%s' % channel_name
- headers = {'User-agent' : httpHeaderUserAgent,
- 'Referer' : 'http://www.justin.tv/'+channel_name}
- req = urllib2.Request(base_url, None, headers)
- response = urllib2.urlopen(req)
- return response.geturl()
-
-def getBestJtvTokenPossible(name):
- '''Helper method to find another jtv token'''
- swf_url = getSwfUrl(name)
- headers = {'User-agent' : httpHeaderUserAgent,
- 'Referer' : swf_url}
- url = 'http://usher.justin.tv/find/'+name+'.json?type=any&group='
- data = json.loads(get_request(url,headers))
- bestVideoHeight = -1
- bestIndex = -1
- index = 0
- for x in data:
- value = x.get('token', '')
- videoHeight = int(x['video_height'])
- if (value != '') and (videoHeight > bestVideoHeight):
- bestVideoHeight = x['video_height']
- bestIndex = index
- index = index + 1
- return data[bestIndex]
-
-def playLive(name, play=False, password=None):
- swf_url = getSwfUrl(name)
- headers = {'User-agent' : httpHeaderUserAgent,
- 'Referer' : swf_url}
- chosenQuality = settings.getSetting('video')
- videoTypeName = 'any'
- if chosenQuality == '0':
- videoTypeName = 'any'
- elif chosenQuality == '1':
- videoTypeName = '720p'
- elif chosenQuality == '2':
- videoTypeName = '480p'
- elif chosenQuality == '3':
- videoTypeName = '360p'
- url =
'http://usher.justin.tv/find/'+name+'.json?type='+videoTypeName+'&private_code=null&group='
- data = json.loads(get_request(url,headers))
- tokenIndex = 0
- if data == []:
-
xbmc.executebuiltin("XBMC.Notification("+translation(31000)+","+translation(32002)+")")
- return
- try:
- '''trying to get a token in desired quality'''
- token = '
jtv='+data[tokenIndex]['token'].replace('\\','\\5c').replace('
','\\20').replace('"','\\22')
- rtmp = data[tokenIndex]['connect']+'/'+data[tokenIndex]['play']
- except:
-
xbmc.executebuiltin("XBMC.Notification("+translation(32005)+","+translation(32006)+")")
- jtvtoken = getBestJtvTokenPossible(name)
- if jtvtoken == '':
-
xbmc.executebuiltin("XBMC.Notification("+translation(31000)+","+translation(32004)+")")
- return
- token = ' jtv='+jtvtoken['token'].replace('\\','\\5c').replace('
','\\20').replace('"','\\22')
- rtmp = jtvtoken['connect']+'/'+jtvtoken['play']
-
- swf = ' swfUrl=%s swfVfy=1 live=1' % swf_url
- Pageurl = ' Pageurl=http://www.justin.tv/'+name
- url = rtmp+token+swf+Pageurl
- if play == True:
- info = xbmcgui.ListItem(name)
- playlist = xbmc.PlayList(1)
- playlist.clear()
- playlist.add(url, info)
- xbmc.executebuiltin('playlist.playoffset(video,0)')
- else:
- item = xbmcgui.ListItem(path=url)
- xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, item)
-
-params=parameters_string_to_dict(sys.argv[2])
-mode=params.get('mode')
-url=params.get('url')
-sIndex=params.get('siteIndex')
-try:
- index = int(sIndex)
-except Exception:
- index = 0
-channelname=params.get('channelname')
-if type(url)==type(str()):
- url=urllib.unquote_plus(url)
-if mode == 'games':
- createListOfGames(index)
-elif mode == 'featured':
- createListOfFeaturedStreams()
-elif mode == 'channel':
- createListForGame(url, index)
-elif mode == 'play':
- playLive(channelname)
-elif mode == 'following':
- createFollowingList()
-elif mode == 'settings':
- settings.openSettings()
-elif mode == 'search':
- search()
-else:
- createMainListing()
-
+import xbmcplugin
+import xbmcgui
+import sys
+import urllib2,urllib,re
+import xbmcaddon
+import os
+import xbmcvfs
+import socket
+try:
+ import json
+except:
+ import simplejson as json
+
+thisPlugin = int(sys.argv[1])
+settings = xbmcaddon.Addon(id='plugin.video.twitch')
+httpHeaderUserAgent = 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0)
Gecko/20100101 Firefox/6.0'
+translation = settings.getLocalizedString
+ITEMS_PER_SITE=20
+
+def downloadWebData(url):
+ try:
+ req = urllib2.Request(url)
+ req.add_header('User-Agent', httpHeaderUserAgent)
+ response = urllib2.urlopen(req)
+ data=response.read()
+ response.close()
+ return data
+ except urllib2.HTTPError, e:
+ # HTTP errors usualy contain error information in JSON Format
+ return e.fp.read()
+ except urllib2.URLError, e:
+ xbmc.executebuiltin("XBMC.Notification("+translation(32001)+"," +
translation(32010) +")")
+
+def getJsonFromTwitchApi(url):
+ jsonString=downloadWebData(url)
+ if jsonString is None:
+ return None
+ try:
+ jsonData=json.loads(jsonString)
+ except:
+
xbmc.executebuiltin('XBMC.Notification("'+translation(32008)+'","'+translation(32008)+'")')
+ return None
+ if type(jsonData) is dict and 'error' in jsonData.keys():
+
xbmc.executebuiltin('XBMC.Notification("'+translation(32007)+'","'+jsonData['error']+'")')
+ return None
+ return jsonData
+
+def createMainListing():
+ addDir(translation(30005),'','featured','')
+ addDir(translation(30001),'','games','')
+ addDir(translation(30002),'','following','')
+ addDir(translation(30006),'','teams','')
+ addDir(translation(30003),'','search','')
+ addDir(translation(30004),'','settings','')
+ xbmcplugin.endOfDirectory(thisPlugin)
+
+def createFollowingList():
+ username = settings.getSetting('username').lower()
+ if not username:
+ settings.openSettings()
+ username = settings.getSetting('username').lower()
+ #Using xml in this case, because it's alot faster than parsing throw the
big json result
+ xmlDataOnlineStreams =
downloadWebData(url='http://api.justin.tv/api/stream/list.xml')
+ jsonData =
getJsonFromTwitchApi(url='http://api.justin.tv/api/user/favorites/'+username+'.json')
+ if jsonData is None:
+ return
+ for x in jsonData:
+ name = x['status']
+ image = x['image_url_huge']
+ loginname = x['login']
+ if len(name) <= 0:
+ name = loginname
+ if xmlDataOnlineStreams.count('<login>'+loginname+'</login>') > 0:
+ addLink(name,loginname,'play',image,loginname)
+ xbmcplugin.endOfDirectory(thisPlugin)
+
+def createListOfFeaturedStreams():
+ jsonData =
getJsonFromTwitchApi(url='https://api.twitch.tv/kraken/streams/featured')
+ if jsonData is None:
+ return
+ for x in jsonData['featured']:
+ try:
+ image = x['stream']['channel']['logo']
+ image = image.replace("http://","",1)
+ image = urllib.quote(image)
+ image = 'http://' + image
+ except:
+ image = ""
+ name = x['stream']['channel']['status']
+ if name == '':
+ name = x['stream']['channel']['display_name']
+ channelname = x['stream']['channel']['name']
+ addLink(name,'...','play',image,channelname)
+ xbmcplugin.endOfDirectory(thisPlugin)
+
+def createListOfTeams():
+ #Temporary solution until twitch api method is available
+
jsonString=downloadWebData(url='https://spreadsheets.google.com/feeds/list/0ArmMFLQnLIp8dFJ5bW9aOW03VHY5aUhsUFNXSUl1SXc/od6/public/basic?alt=json')
+ if jsonString is None:
+ return
+ try:
+ jsonData=json.loads(jsonString)
+ except:
+
xbmc.executebuiltin('XBMC.Notification("'+translation(32008)+'","'+translation(32008)+'")')
+ return
+ for x in jsonData['feed']['entry']:
+ teamData = x['content']['$t'].split(',')
+ try:
+ image = teamData[1][7:]
+ image = image.replace("http://","",1)
+ image = urllib.quote(image)
+ image = 'http://' + image
+ except:
+ image = ""
+ name = x['title']['$t']
+ channelname = teamData[0][7:]
+ addDir(name,channelname,'team',image,)
+ xbmcplugin.endOfDirectory(thisPlugin)
+
+def createListOfTeamStreams(team=''):
+ jsonData =
getJsonFromTwitchApi(url='http://api.twitch.tv/api/team/'+team+'/live_channels.json')
+ if jsonData is None:
+ return
+ for x in jsonData['channels']:
+ try:
+ image = x['channel']['image']['size600']
+ image = image.replace("http://","",1)
+ image = urllib.quote(image)
+ image = 'http://' + image
+ except:
+ image = ""
+ if x['channel']['title'] is None:
+ name = x['channel']['display_name']
+ else:
+ name = x['channel']['display_name']+' - '+x['channel']['title']
+ channelname = x['channel']['name']
+ addLink(name,'...','play',image,channelname)
+ xbmcplugin.endOfDirectory(thisPlugin)
+
+def createListOfGames(index=0):
+ jsonData =
getJsonFromTwitchApi(url='https://api.twitch.tv/kraken/games/top?limit='+str(ITEMS_PER_SITE)+'&offset='+str(index*ITEMS_PER_SITE))
+ if jsonData is None:
+ return
+ for x in jsonData['top']:
+ try:
+ name = str(x['game']['name'])
+ game = urllib.quote(name)
+ image = ''
+ except:
+ continue
+ try:
+ image = x['game']['images']['super']
+ image = image.replace("http://","",1)
+ image = urllib.quote(image)
+ image = 'http://' + image
+ except:
+ image = ''
+ addDir(name,game,'channel',image)
+ if len(jsonData['top']) >= ITEMS_PER_SITE:
+ addDir(translation(31001),'','games','',index+1)
+ xbmcplugin.endOfDirectory(thisPlugin)
+
+def search():
+ keyboard = xbmc.Keyboard('', translation(30101))
+ keyboard.doModal()
+ if keyboard.isConfirmed() and keyboard.getText():
+ search_string = urllib.quote_plus(keyboard.getText())
+ sdata =
downloadWebData('http://api.swiftype.com/api/v1/public/engines/search.json?callback=jQuery1337&q='+search_string+'&engine_key=9NXQEpmQPwBEz43TM592&page=1&per_page='+str(ITEMS_PER_SITE))
+ sdata = sdata.replace('jQuery1337','');
+ sdata = sdata[1:len(sdata)-1]
+ jdata = json.loads(sdata)
+ records = jdata['records']['broadcasts']
+ for x in records:
+ addLink(x['title'],x['user'],'play',x['thumbnail'],x['user'])
+ xbmcplugin.endOfDirectory(thisPlugin)
+
+
+def createListForGame(gameName, index=0):
+
jsonString=downloadWebData(url='https://api.twitch.tv/kraken/streams?game='+gameName+'&limit='+str(ITEMS_PER_SITE)+'&offset='+str(index*ITEMS_PER_SITE))
+ if jsonString is None:
+ return
+ jsonData=json.loads(jsonString)
+ try:
+ jsonData=json.loads(jsonString)
+ except:
+
xbmc.executebuiltin('XBMC.Notification("'+translation(32008)+'","'+translation(32008)+'")')
+ return
+ for x in jsonData['streams']:
+ try:
+ image = x['channel']['logo']
+ image = image.replace("http://","",1)
+ image = urllib.quote(image)
+ image = 'http://' + image
+ except:
+ image = ""
+ name = x['channel']['status']
+ if name == '':
+ name = x['channel']['display_name']
+ channelname = x['channel']['name']
+ addLink(name,'...','play',image,channelname)
+ if len(jsonData['streams']) >= ITEMS_PER_SITE:
+ addDir(translation(31001),url,'channel','',index+1)
+ xbmcplugin.endOfDirectory(thisPlugin)
+
+def addLink(name,url,mode,iconimage,channelname):
+
u=sys.argv[0]+"?url="+urllib.quote_plus(url)+"&mode="+str(mode)+"&channelname="+channelname
+ ok=True
+ liz=xbmcgui.ListItem(name, iconImage="DefaultVideo.png",
thumbnailImage=iconimage)
+ liz.setInfo( type="Video", infoLabels={ "Title": name } )
+ liz.setProperty('IsPlayable', 'true')
+
ok=xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=u,listitem=liz)
+ return ok
+
+def addDir(name,url,mode,iconimage,index=0):
+
u=sys.argv[0]+"?url="+urllib.quote_plus(url)+"&mode="+str(mode)+"&siteIndex="+str(index)
+ ok=True
+ liz=xbmcgui.ListItem(name, iconImage="DefaultFolder.png",
thumbnailImage=iconimage)
+ liz.setInfo( type="Video", infoLabels={ "Title": name } )
+
ok=xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=u,listitem=liz,isFolder=True)
+ return ok
+
+def get_request(url, headers=None):
+ try:
+ if headers is None:
+ headers = {'User-agent' : httpHeaderUserAgent,
+ 'Referer' : 'http://www.twitch.tv/'}
+ req = urllib2.Request(url,None,headers)
+ response = urllib2.urlopen(req)
+ link=response.read()
+ response.close()
+ return link
+ except urllib2.URLError, e:
+ errorStr = str(e.read())
+ if hasattr(e, 'code'):
+ if str(e.code) == '403':
+ if 'archive' in url:
+
xbmc.executebuiltin("XBMC.Notification("+translation(31000)+","
+translation(32003)+ " " +name+")")
+
xbmc.executebuiltin("XBMC.Notification("+translation(31000)+"," +
translation(32001) +")")
+
+
+def parameters_string_to_dict(parameters):
+ # Convert parameters encoded in a URL to a dict.
+ paramDict = {}
+ if parameters:
+ paramPairs = parameters[1:].split("&")
+ for paramsPair in paramPairs:
+ paramSplits = paramsPair.split('=')
+ if (len(paramSplits)) == 2:
+ paramDict[paramSplits[0]] = paramSplits[1]
+ return paramDict
+
+def getSwfUrl(channel_name):
+ # Helper method to grab the swf url, resolving HTTP 301/302 along the
way
+ base_url =
'http://www.justin.tv/widgets/live_embed_player.swf?channel=%s' % channel_name
+ headers = {'User-agent' : httpHeaderUserAgent,
+ 'Referer' : 'http://www.justin.tv/'+channel_name}
+ req = urllib2.Request(base_url, None, headers)
+ response = urllib2.urlopen(req)
+ return response.geturl()
+
+def getBestJtvTokenPossible(name):
+ # Helper method to find another jtv token
+ swf_url = getSwfUrl(name)
+ headers = {'User-agent' : httpHeaderUserAgent,
+ 'Referer' : swf_url}
+ url = 'http://usher.justin.tv/find/'+name+'.json?type=any&group='
+ data = json.loads(get_request(url,headers))
+ bestVideoHeight = -1
+ bestIndex = 0
+ index = 0
+ for x in data:
+ value = x.get('token', '')
+ videoHeight = int(x['video_height'])
+ if (value != '') and (videoHeight > bestVideoHeight):
+ bestVideoHeight = x['video_height']
+ bestIndex = index
+ index = index + 1
+ return data[bestIndex]
+
+def playLive(name, play=False, password=None):
+ swf_url = getSwfUrl(name)
+ headers = {'User-agent' : httpHeaderUserAgent,
+ 'Referer' : swf_url}
+ chosenQuality = settings.getSetting('video')
+ videoTypeName = 'any'
+ if chosenQuality == '0':
+ videoTypeName = 'any'
+ elif chosenQuality == '1':
+ videoTypeName = '720p'
+ elif chosenQuality == '2':
+ videoTypeName = '480p'
+ elif chosenQuality == '3':
+ videoTypeName = '360p'
+ url =
'http://usher.justin.tv/find/'+name+'.json?type='+videoTypeName+'&private_code=null&group='
+ data = json.loads(get_request(url,headers))
+ tokenIndex = 0
+
+ try:
+ # trying to get a token in desired quality
+ token = '
jtv='+data[tokenIndex]['token'].replace('\\','\\5c').replace('
','\\20').replace('"','\\22')
+ rtmp = data[tokenIndex]['connect']+'/'+data[tokenIndex]['play']
+ except:
+
xbmc.executebuiltin("XBMC.Notification("+translation(32005)+","+translation(32006)+")")
+ jtvtoken = getBestJtvTokenPossible(name)
+ if jtvtoken == '':
+
xbmc.executebuiltin("XBMC.Notification("+translation(31000)+","+translation(32004)+")")
+ return
+ token = ' jtv='+jtvtoken['token'].replace('\\','\\5c').replace('
','\\20').replace('"','\\22')
+ rtmp = jtvtoken['connect']+'/'+jtvtoken['play']
+
+ swf = ' swfUrl=%s swfVfy=1 live=1' % swf_url
+ Pageurl = ' Pageurl=http://www.justin.tv/'+name
+ url = rtmp+token+swf+Pageurl
+ if play == True:
+ info = xbmcgui.ListItem(name)
+ playlist = xbmc.PlayList(1)
+ playlist.clear()
+ playlist.add(url, info)
+ xbmc.executebuiltin('playlist.playoffset(video,0)')
+ else:
+ item = xbmcgui.ListItem(path=url)
+ xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, item)
+
+params=parameters_string_to_dict(sys.argv[2])
+mode=params.get('mode')
+url=params.get('url')
+sIndex=params.get('siteIndex')
+try:
+ index = int(sIndex)
+except:
+ index = 0
+channelname=params.get('channelname')
+if type(url)==type(str()):
+ url=urllib.unquote_plus(url)
+if mode == 'games':
+ createListOfGames(index)
+elif mode == 'featured':
+ createListOfFeaturedStreams()
+elif mode == 'channel':
+ createListForGame(url, index)
+elif mode == 'play':
+ playLive(channelname)
+elif mode == 'following':
+ createFollowingList()
+elif mode == 'teams':
+ createListOfTeams()
+elif mode == 'team':
+ createListOfTeamStreams(url)
+elif mode == 'settings':
+ settings.openSettings()
+elif mode == 'search':
+ search()
+else:
+ createMainListing()
+
diff --git a/plugin.video.twitch/resources/language/English/strings.xml
b/plugin.video.twitch/resources/language/English/strings.xml
index 63a3a97..67751ba 100644
--- a/plugin.video.twitch/resources/language/English/strings.xml
+++ b/plugin.video.twitch/resources/language/English/strings.xml
@@ -1,32 +1,37 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
-<strings>
- <!-- main menu -->
- <string id="30001">Games</string>
- <string id="30002">Following</string>
- <string id="30003">Search</string>
- <string id="30004">Settings</string>
- <string id="30005">Featured Streams</string>
-
- <!--search -->
- <string id="30101">Search for channels</string>
-
- <!--general -->
- <string id="31000">TwitchTV</string>
- <string id="31001">next page...</string>
-
- <!--notifications -->
- <string id="32001">Http error</string>
- <string id="32002">No Live Data Found</string>
- <string id="32003">No archives found for</string>
- <string id="32004">User Token Error</string>
- <string id="32005">Changed video settings</string>
- <string id="32006">Selected video settings are not available</string>
-
- <!--settings -->
- <string id="33000">Video Quality</string>
- <string id="33001">Best possible</string>
- <string id="33002">720p</string>
- <string id="33003">480p</string>
- <string id="33004">360p</string>
-
-</strings>
\ No newline at end of file
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<strings>
+ <!-- main menu -->
+ <string id="30001">Games</string>
+ <string id="30002">Following</string>
+ <string id="30003">Search</string>
+ <string id="30004">Settings</string>
+ <string id="30005">Featured Streams</string>
+ <string id="30006">Teams</string>
+
+ <!--search -->
+ <string id="30101">Search for channels</string>
+
+ <!--general -->
+ <string id="31000">TwitchTV</string>
+ <string id="31001">next page...</string>
+
+ <!--notifications -->
+ <string id="32001">Http error</string>
+ <string id="32002">No Live Data Found</string>
+ <string id="32003">No archives found for</string>
+ <string id="32004">User Token Error</string>
+ <string id="32005">Changed video settings</string>
+ <string id="32006">Selected video settings are not available</string>
+ <string id="32007">TwitchApi error</string>
+ <string id="32008">JSON error</string>
+ <string id="32009">Failed to parse the result set</string>
+ <string id="32010">Please check your internet connection</string>
+
+ <!--settings -->
+ <string id="33000">Video Quality</string>
+ <string id="33001">Best possible</string>
+ <string id="33002">720p</string>
+ <string id="33003">480p</string>
+ <string id="33004">360p</string>
+
+</strings>
diff --git a/plugin.video.twitch/resources/language/German/strings.xml
b/plugin.video.twitch/resources/language/German/strings.xml
index d51c540..d3e8315 100644
--- a/plugin.video.twitch/resources/language/German/strings.xml
+++ b/plugin.video.twitch/resources/language/German/strings.xml
@@ -1,31 +1,36 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
-<strings>
- <!-- main menu -->
- <string id="30001">Games</string>
- <string id="30002">Folgend</string>
- <string id="30003">Suche</string>
- <string id="30004">Einstellungen</string>
- <string id="30005">Featured Streams</string>
-
- <!-- search -->
- <string id="30101">Suche nach Kanälen</string>
-
- <!--general -->
- <string id="31000">TwitchTV</string>
- <string id="31001">nächste Seite...</string>
-
- <!--notifications -->
- <string id="32001">Http Fehler</string>
- <string id="32002">Keine Live-Daten gefunden</string>
- <string id="32003">Keine Archive gefunden für</string>
- <string id="32004">User Token Fehler</string>
- <string id="32005">Geaenderte Video Einstellung</string>
- <string id="32006">Gewaehlte Qualitaet nicht verfuegbar...Eventuell Premium
Content?</string>
-
- <!--settings -->
- <string id="33000">Video Qualität</string>
- <string id="33001">Beste</string>
- <string id="33002">720p</string>
- <string id="33003">480p</string>
- <string id="33004">360p</string>
-</strings>
\ No newline at end of file
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<strings>
+ <!-- main menu -->
+ <string id="30001">Games</string>
+ <string id="30002">Folgend</string>
+ <string id="30003">Suche</string>
+ <string id="30004">Einstellungen</string>
+ <string id="30005">Featured Streams</string>
+ <string id="30006">Teams</string>
+
+ <!-- search -->
+ <string id="30101">Suche nach Kanälen</string>
+
+ <!--general -->
+ <string id="31000">TwitchTV</string>
+ <string id="31001">nächste Seite...</string>
+
+ <!--notifications -->
+ <string id="32001">Http Fehler</string>
+ <string id="32002">Keine Live-Daten gefunden</string>
+ <string id="32003">Keine Archive gefunden für</string>
+ <string id="32004">User Token Fehler</string>
+ <string id="32005">Geaenderte Video Einstellung</string>
+ <string id="32006">Gewaehlte Qualitaet nicht verfuegbar...Eventuell Premium
Content?</string>
+ <string id="32007">TwitchApi Fehler</string>
+ <string id="32008">JSON Fehler</string>
+ <string id="32009">Fehlerhaftes Datenformat</string>
+ <string id="32010">Bitte Internet-Verbindung pruefen</string>
+
+ <!--settings -->
+ <string id="33000">Video Qualität</string>
+ <string id="33001">Beste</string>
+ <string id="33002">720p</string>
+ <string id="33003">480p</string>
+ <string id="33004">360p</string>
+</strings>
http://xbmc.git.sourceforge.net/git/gitweb.cgi?p=xbmc/plugins;a=commit;h=5a31ca4317ec7eef67030bc732566a91cdcc21af
commit 5a31ca4317ec7eef67030bc732566a91cdcc21af
Author: spiff <[email protected]>
Date: Fri Oct 19 15:29:15 2012 +0200
[plugin.image.iphoto] updated to version 1.9.2
diff --git a/plugin.image.iphoto/addon.py b/plugin.image.iphoto/addon.py
index 89370ea..0b5c471 100644
--- a/plugin.image.iphoto/addon.py
+++ b/plugin.image.iphoto/addon.py
@@ -194,8 +194,6 @@ def list_albums(params):
albums = db.GetAlbums()
if (not albums):
- dialog = gui.Dialog()
- dialog.ok(addon.getLocalizedString(30240),
addon.getLocalizedString(30241))
return
commands = []
@@ -248,8 +246,6 @@ def list_events(params):
rolls = db.GetRolls()
if (not rolls):
- dialog = gui.Dialog()
- dialog.ok(addon.getLocalizedString(30240),
addon.getLocalizedString(30241))
return
commands = []
@@ -310,8 +306,6 @@ def list_faces(params):
faces = db.GetFaces()
if (not faces):
- dialog = gui.Dialog()
- dialog.ok(addon.getLocalizedString(30240),
addon.getLocalizedString(30241))
return
commands = []
@@ -378,8 +372,6 @@ def list_places(params):
places = db.GetPlaces()
if (not places):
- dialog = gui.Dialog()
- dialog.ok(addon.getLocalizedString(30240),
addon.getLocalizedString(30241))
return
commands = []
@@ -441,8 +433,6 @@ def list_keywords(params):
keywords = db.GetKeywords()
if (not keywords):
- dialog = gui.Dialog()
- dialog.ok(addon.getLocalizedString(30240),
addon.getLocalizedString(30241))
return
hidden_keywords = addon.getSetting('hidden_keywords')
@@ -542,12 +532,13 @@ def import_library(xmlpath, xmlfile, masterspath,
masters_realpath, enable_place
else:
gui.Window(10000).setProperty("iphoto_scanning", "True")
- # always ignore Books and currently selected album
+ # always ignore Books and all Event type albums
album_ign = []
album_ign.append("Book")
album_ign.append("Selected Event Album")
+ album_ign.append("Event")
- # ignore albums published to MobileMe if configured to do so
+ # ignore albums published to MobileMe/iCloud if configured to do so
album_ign_publ = addon.getSetting('album_ignore_published')
if (album_ign_publ == ""):
album_ign_publ = "true"
@@ -590,7 +581,7 @@ def import_library(xmlpath, xmlfile, masterspath,
masters_realpath, enable_place
except:
print traceback.print_exc()
else:
- iparser = IPhotoParser(xmlpath, xmlfile, masterspath, masters_realpath,
album_ign, enable_places, map_aspect, db.AddAlbumNew, db.AddRollNew,
db.AddFaceNew, db.AddKeywordNew, db.AddMediaNew, import_progress_callback,
progress_dialog)
+ iparser = IPhotoParser(xmlpath, xmlfile, masterspath, masters_realpath,
album_ign, enable_places, map_aspect, db.SetConfig, db.AddAlbumNew,
db.AddRollNew, db.AddFaceNew, db.AddKeywordNew, db.AddMediaNew,
import_progress_callback, progress_dialog)
try:
progress_dialog.update(0, addon.getLocalizedString(30219))
@@ -608,11 +599,6 @@ def import_library(xmlpath, xmlfile, masterspath,
masters_realpath, enable_place
print "iPhoto: Library imported successfully."
progress_dialog.close()
gui.Window(10000).setProperty("iphoto_scanning", "False")
- try:
- # this is non-critical
- db.UpdateLastImport()
- except:
- pass
def reset_db(params):
try:
diff --git a/plugin.image.iphoto/addon.xml b/plugin.image.iphoto/addon.xml
index dde6093..edabc96 100644
--- a/plugin.image.iphoto/addon.xml
+++ b/plugin.image.iphoto/addon.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<addon id="plugin.image.iphoto" name="iPhoto" version="1.9.1"
provider-name="jingai">
+<addon id="plugin.image.iphoto" name="iPhoto" version="1.9.2"
provider-name="jingai">
<requires>
<import addon="xbmc.python" version="2.0"/>
<import addon="script.module.simplejson" version="2.0.10"/>
diff --git a/plugin.image.iphoto/changelog.txt
b/plugin.image.iphoto/changelog.txt
index d5ba4d8..8c30d50 100644
--- a/plugin.image.iphoto/changelog.txt
+++ b/plugin.image.iphoto/changelog.txt
@@ -1,3 +1,7 @@
+1.9.2 - 20121017
+- Fixes for iPhoto 9.4.
+- Ignore Albums of type "Event".
+
1.9.1 - 20120803
- Support for Metropolis skin.
diff --git a/plugin.image.iphoto/resources/lib/iphoto_parser.py
b/plugin.image.iphoto/resources/lib/iphoto_parser.py
index 75f842b..a98f226 100644
--- a/plugin.image.iphoto/resources/lib/iphoto_parser.py
+++ b/plugin.image.iphoto/resources/lib/iphoto_parser.py
@@ -16,6 +16,7 @@ except:
import sys
import os
+import time
import locale
try:
@@ -172,7 +173,7 @@ class IPhotoDB:
id integer primary key,
name varchar,
thumbpath varchar,
- keyphotoid integer,
+ keyphotoid varchar,
keyphotoidx integer,
photocount integer,
faceorder integer
@@ -273,16 +274,13 @@ class IPhotoDB:
self.Commit()
- def UpdateLastImport(self):
- try:
- self.SetConfig('lastimport', 'dummy')
- self.dbconn.execute("""UPDATE config
- SET value = datetime('now')
- WHERE key = ?""",
- ('lastimport',))
- self.Commit()
- except Exception, e:
- print "iphoto.db: UpdateLastImport: " + to_str(e)
+ def GetIphotoVersion(self):
+ verstr = self.GetConfig('version')
+ if (verstr == None):
+ ver = 0.0
+ else:
+ ver = float('.'.join(verstr.split('.')[:2]))
+ return ver
def GetTableId(self, table, value, column='name', autoadd=False,
autoclean=True):
try:
@@ -383,11 +381,19 @@ class IPhotoDB:
faces = []
try:
cur = self.dbconn.cursor()
+
+ if (self.GetIphotoVersion() < 9.4):
+ idtype = "M.id"
+ else:
+ idtype = "M.guid"
+
cur.execute("""SELECT F.id, F.name, F.thumbpath, F.photocount
- FROM faces F LEFT JOIN media M ON F.keyphotoid = M.id
- ORDER BY F.faceorder""")
+ FROM faces F LEFT JOIN media M ON F.keyphotoid = %s
+ ORDER BY F.faceorder""" % (idtype))
+
for tuple in cur:
faces.append(tuple)
+
cur.close()
except Exception, e:
print "iphoto.db: GetFaces: " + to_str(e)
@@ -633,6 +639,11 @@ class IPhotoDB:
try:
cur = self.dbconn.cursor()
+ if (self.GetIphotoVersion() < 9.4):
+ cmpkey = 'MediaID'
+ else:
+ cmpkey = 'GUID'
+
faces = []
cur.execute("""SELECT id, keyphotoid, keyphotoidx FROM faces""")
for tuple in cur:
@@ -644,7 +655,7 @@ class IPhotoDB:
VALUES (?, ?)""", (faceid, mediaid))
for fid, fkey, fkeyidx in faces:
- if int(fkey) == int(mediaid):
+ if (fkey == media[cmpkey]):
fthumb = os.path.splitext(thumbpath)[0] + "_face%s.jpg"
% (fkeyidx)
self.dbconn.execute("""
UPDATE faces SET thumbpath = ?
@@ -791,6 +802,8 @@ class IPhotoParserState:
self.nphotos = 0
self.nphotostotal = 0
self.level = 0
+ self.appversion = False
+ self.inappversion = 0
self.archivepath = False
self.inarchivepath = 0
self.albums = False
@@ -812,6 +825,7 @@ class IPhotoParserState:
class IPhotoParser:
def __init__(self, library_path="", xmlfile="", masters_path="",
masters_real_path="",
album_ign=[], enable_places=False, map_aspect=0.0,
+ config_callback=None,
album_callback=None, roll_callback=None, face_callback=None,
keyword_callback=None, photo_callback=None,
progress_callback=None, progress_dialog=None):
self.libraryPath = library_path
@@ -820,10 +834,11 @@ class IPhotoParser:
self.mastersRealPath = masters_real_path
if (self.mastersPath and self.mastersRealPath):
try:
- print "Rewriting referenced masters path '%s'" %
(to_str(self.mastersPath))
- print "as '%s'" % (to_str(self.mastersRealPath))
+ print "iphoto.db: Rewriting referenced masters path '%s'" %
(to_str(self.mastersPath))
+ print "iphoto.db: as '%s'" % (to_str(self.mastersRealPath))
except:
pass
+ self.iphotoVersion = "0.0.0"
self.imagePath = ""
self.parser = xml.parsers.expat.ParserCreate()
self.parser.StartElementHandler = self.StartElement
@@ -844,6 +859,7 @@ class IPhotoParser:
self.albumIgn = album_ign
self.enablePlaces = enable_places
self.mapAspect = map_aspect
+ self.ConfigCallback = config_callback
self.AlbumCallback = album_callback
self.RollCallback = roll_callback
self.FaceCallback = face_callback
@@ -938,6 +954,15 @@ class IPhotoParser:
self.PhotoCallback(a, self.imagePath, self.libraryPath,
self.mastersPath, self.mastersRealPath, self.enablePlaces, self.mapAspect,
self.updateProgress)
state.nphotos += 1
self.updateProgress()
+
+ if (self.ConfigCallback):
+ print "iphoto.db: Writing configuration"
+ if (self.iphotoVersion != "0.0.0"):
+ self.ConfigCallback('version', self.iphotoVersion)
+ try:
+ self.ConfigCallback('lastimport', to_str(time.time()))
+ except:
+ pass
except ParseCanceled:
raise
except Exception, e:
@@ -967,7 +992,10 @@ class IPhotoParser:
def StartElement(self, name, attrs):
state = self.state
self.lastdata = False
- if (state.archivepath):
+ if (state.appversion):
+ state.inappversion += 1
+ state.key = name
+ elif (state.archivepath):
state.inarchivepath += 1
state.key = name
elif (state.albums):
@@ -1004,14 +1032,23 @@ class IPhotoParser:
self.lastdata = False
state = self.state
- if (state.archivepath):
+ # Application Version
+ if (state.appversion):
+ if (not state.key):
+ self.iphotoVersion = state.value
+ print "iphoto.db: Detected iPhoto Version %s" %
self.iphotoVersion
+ state.appversion = False
+ state.inappversion -= 1
+
+ # Archive Path
+ elif (state.archivepath):
if (not state.key):
self.imagePath = state.value
state.archivepath = False
if (self.imagePath != self.libraryPath):
try:
- print "Rewriting iPhoto archive path '%s'" %
(to_str(self.imagePath))
- print "as '%s'" % (to_str(self.libraryPath))
+ print "iPhoto.db: Rewriting iPhoto archive path '%s'" %
(to_str(self.imagePath))
+ print "iPhoto.db: as '%s'" % (to_str(self.libraryPath))
except:
pass
state.inarchivepath -= 1
@@ -1098,7 +1135,14 @@ class IPhotoParser:
# determine which section we are in
if (state.key and state.level == 3):
- if (data == "Archive Path"):
+ if (data == "Application Version"):
+ state.appversion = True
+ state.albums = False
+ state.rolls = False
+ state.faces = False
+ state.keywords = False
+ state.master = False
+ elif (data == "Archive Path"):
state.archivepath = True
state.albums = False
state.rolls = False
@@ -1106,6 +1150,7 @@ class IPhotoParser:
state.keywords = False
state.master = False
elif (data == "List of Albums"):
+ state.appversion = False
state.archivepath = False
state.albums = True
state.rolls = False
@@ -1113,6 +1158,7 @@ class IPhotoParser:
state.keywords = False
state.master = False
elif (data == "List of Rolls"):
+ state.appversion = False
state.archivepath = False
state.albums = False
state.rolls = True
@@ -1120,6 +1166,7 @@ class IPhotoParser:
state.keywords = False
state.master = False
elif (data == "List of Faces"):
+ state.appversion = False
state.archivepath = False
state.albums = False
state.rolls = False
@@ -1127,6 +1174,7 @@ class IPhotoParser:
state.keywords = False
state.master = False
elif (data == "List of Keywords"):
+ state.appversion = False
state.archivepath = False
state.albums = False
state.rolls = False
@@ -1134,6 +1182,7 @@ class IPhotoParser:
state.keywords = True
state.master = False
elif (data == "Master Image List"):
+ state.appversion = False
state.archivepath = False
state.albums = False
state.rolls = False
-----------------------------------------------------------------------
Summary of changes:
plugin.image.iphoto/addon.py | 22 +-
plugin.image.iphoto/addon.xml | 2 +-
plugin.image.iphoto/changelog.txt | 4 +
plugin.image.iphoto/resources/lib/iphoto_parser.py | 91 +++-
plugin.video.twitch/addon.xml | 2 +-
plugin.video.twitch/changelog.txt | 4 +-
plugin.video.twitch/default.py | 635 +++++++++++---------
.../resources/language/English/strings.xml | 69 ++-
.../resources/language/German/strings.xml | 67 ++-
9 files changed, 511 insertions(+), 385 deletions(-)
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_sfd2d_oct
_______________________________________________
Xbmc-addons mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/xbmc-addons