summaryrefslogtreecommitdiff
path: root/plugin.video.psvue
diff options
context:
space:
mode:
authorEric <eracknaphobia@hotmail.com>2017-06-07 14:37:23 -0400
committerEric <eracknaphobia@hotmail.com>2017-06-07 14:37:23 -0400
commit7fe9b1533ed06526a7d66a770c87f6927a550e02 (patch)
tree535b325f5c1196a5f6cbaad51134cfec795d15fd /plugin.video.psvue
parent5f55ca760019d7f296c60e8035fe58d553d24423 (diff)
[plugin.video.psvue] 2017.6.7
Diffstat (limited to 'plugin.video.psvue')
-rw-r--r--plugin.video.psvue/addon.xml6
-rw-r--r--plugin.video.psvue/main.py24
-rw-r--r--plugin.video.psvue/resources/language/resource.language.en_gb/strings.po25
-rw-r--r--plugin.video.psvue/resources/lib/ps_vue.py (renamed from plugin.video.psvue/resources/lib/globals.py)315
-rw-r--r--plugin.video.psvue/resources/lib/sony.py334
-rw-r--r--plugin.video.psvue/resources/settings.xml3
6 files changed, 389 insertions, 318 deletions
diff --git a/plugin.video.psvue/addon.xml b/plugin.video.psvue/addon.xml
index 56e57af..6c79e1f 100644
--- a/plugin.video.psvue/addon.xml
+++ b/plugin.video.psvue/addon.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<addon id="plugin.video.psvue" name="PS Vue" version="2017.6.1" provider-name="eracknaphobia">
+<addon id="plugin.video.psvue" name="PS Vue" version="2017.6.7" provider-name="eracknaphobia">
<requires>
<import addon="xbmc.python" version="2.25.0"/>
<import addon="script.module.requests" version="2.9.1"/>
@@ -13,8 +13,8 @@
<description lang="en_GB">PlayStation™Vue is a TV service that streams live TV, movies, and sports on a variety of your favorite devices without a cable or satellite subscription. With powerful features that allow you to save thousands of hours of your favorite shows without recording conflicts, and Premium channels that can be purchased individually or with a multi-channel plan, TV has never been the same. No annual contracts, no conflicts, no problems.</description>
<disclaimer lang="en_GB">Requires a valid subscription to PS Vue which is currently only available in the US</disclaimer>
<news>
-- Fix for special characters in username and password
-- Improved error notification descriptions
+- Fixed streams not launching
+- Code Clean Up
</news>
<language>en</language>
<platform>all</platform>
diff --git a/plugin.video.psvue/main.py b/plugin.video.psvue/main.py
index b204a62..f49787d 100644
--- a/plugin.video.psvue/main.py
+++ b/plugin.video.psvue/main.py
@@ -1,4 +1,4 @@
-from resources.lib.globals import *
+from resources.lib.ps_vue import *
params=get_params()
url=None
@@ -23,13 +23,17 @@ try:
except:
pass
-if mode != 900: check_reqpayload()
-if mode == None:
- if ADDON.getSetting(id='default_profile') == '':
- get_profiles()
-
- main_menu()
+sony = SONY()
+if ADDON.getSetting(id='last_auth') != '':
+ last_auth = stringToDate(ADDON.getSetting(id='last_auth'), "%Y-%m-%dT%H:%M:%S.%fZ")
+ if (datetime.now() - last_auth).total_seconds() >= 5400: sony.check_auth()
+else:
+ sony.check_auth()
+
+if mode == None:
+ if ADDON.getSetting(id='default_profile') == '': sony.get_profiles()
+ main_menu()
elif mode == 50:
timeline()
@@ -59,7 +63,7 @@ elif mode == 700:
featured()
elif mode == 800:
- get_profiles()
+ sony.get_profiles()
main_menu()
elif mode == 900:
@@ -69,7 +73,7 @@ elif mode == 998:
sys.exit()
elif mode == 999:
- logout()
+ sony.logout()
main_menu()
@@ -78,4 +82,4 @@ if mode != None and mode != 800:
elif mode == 800:
xbmcplugin.endOfDirectory(addon_handle, updateListing=True)
else:
- xbmcplugin.endOfDirectory(addon_handle) \ No newline at end of file
+ xbmcplugin.endOfDirectory(addon_handle)
diff --git a/plugin.video.psvue/resources/language/resource.language.en_gb/strings.po b/plugin.video.psvue/resources/language/resource.language.en_gb/strings.po
index b2a483d..a636d27 100644
--- a/plugin.video.psvue/resources/language/resource.language.en_gb/strings.po
+++ b/plugin.video.psvue/resources/language/resource.language.en_gb/strings.po
@@ -88,3 +88,28 @@ msgstr ""
msgctxt "#30204"
msgid "Please enter your verification code"
msgstr ""
+
+msgctxt "#30205"
+msgid "Profile Not Found"
+msgstr ""
+
+msgctxt "#30206"
+msgid "Your profile list could not be retrieved"
+msgstr ""
+
+msgctxt "#30207"
+msgid "Authorization Failed"
+msgstr ""
+
+msgctxt "#30208"
+msgid "Could not retrieve grant code"
+msgstr ""
+
+msgctxt "#30209"
+msgid "Could not retrieve the reqpayload"
+msgstr ""
+
+
+msgctxt "#30210"
+msgid "Choose Profile"
+msgstr ""
diff --git a/plugin.video.psvue/resources/lib/globals.py b/plugin.video.psvue/resources/lib/ps_vue.py
index 5a1221a..333d652 100644
--- a/plugin.video.psvue/resources/lib/globals.py
+++ b/plugin.video.psvue/resources/lib/ps_vue.py
@@ -1,11 +1,12 @@
import sys, os
import xbmc, xbmcplugin, xbmcgui, xbmcaddon
import random
-import cookielib, urllib, urlparse
+import cookielib, urllib
import json
import requests
import time, calendar
from datetime import date, datetime, timedelta
+from sony import SONY
def main_menu():
@@ -144,12 +145,12 @@ def list_episode(show):
title = show['display_episode_title']
airing_id = str(show['airings'][0]['airing_id'])
airing_date = show['airing_date']
- airing_date = stringToDate(airing_date, "%Y-%m-%dT%H:%M:%S.000Z")
+ airing_date = stringToDate(airing_date, "%Y-%m-%dT%H:%M:%S.%fZ")
airing_date = UTCToLocal(airing_date)
broadcast_date = ''
if 'broadcast_date' in show:
broadcast_date = show['broadcast_date']
- broadcast_date = stringToDate(broadcast_date, "%Y-%m-%dT%H:%M:%S.000Z")
+ broadcast_date = stringToDate(broadcast_date, "%Y-%m-%dT%H:%M:%S.%fZ")
broadcast_date = UTCToLocal(broadcast_date)
genre = ''
@@ -236,8 +237,6 @@ def get_stream(url):
r = requests.get(url, headers=headers, cookies=load_cookies(), verify=VERIFY)
json_source = r.json()
- # save_cookies(r.cookies)
-
stream_url = json_source['body']['video']
stream_url = stream_url + '|User-Agent=Dalvik/2.1.0 (Linux; U; Android 6.0.1 Build/MOB31H)&Cookie=reqPayload=' + urllib.quote('"' + ADDON.getSetting(id='reqPayload') + '"')
@@ -256,213 +255,6 @@ def get_stream(url):
'''
-def put_resume_time():
- """
- PUT https://sentv-user-action.totsuko.tv/sentv_user_action/ws/v2/watch_history HTTP/1.1
- Host: sentv-user-action.totsuko.tv
- Connection: keep-alive
- Content-Length: 247
- Accept: */*
- reqPayload: redacted
- User-Agent: Mozilla/5.0 (Linux; Android 6.0.1; Build/MOB31H; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/44.0.2403.119 Safari/537.36
- Origin: https://themis.dl.playstation.net
- Content-Type: application/json
- Referer: https://themis.dl.playstation.net/themis/zartan/2.2.2b/
- Accept-Encoding: gzip, deflate
- Accept-Language: en-US
- X-Requested-With: com.snei.vue.android
-
- {"series_id":21188,"program_id":1320750,"channel_id":25039,"tms_id":"EP005544655496","airing_id":14626670,"last_watch_date":"2017-04-28T00:40:43Z","last_timecode":"01:46:29","start_timecode":"00:00:00:00","fully_watched":false,"stream_type":"dvr"}
- """
- url = 'https://sentv-user-action.totsuko.tv/sentv_user_action/ws/v2/watch_history'
- headers = {"Accept": "*/*",
- "Content-type": "application/json",
- "Origin": "https://themis.dl.playstation.net",
- "Accept-Language": "en-US",
- "Referer": "https://themis.dl.playstation.net/themis/zartan/2.2.2b/",
- "Accept-Encoding": "gzip, deflate",
- "User-Agent": UA_ANDROID,
- "Connection": "Keep-Alive",
- 'reqPayload': ADDON.getSetting(id='reqPayload'),
- 'X-Requested-With': 'com.snei.vue.android'
- }
-
- payload = '{"series_id":21188,'
- payload += '"program_id":1320750,'
- payload += '"channel_id":25039,'
- payload += '"tms_id":"EP005544655496",'
- payload += '"airing_id":14626670,'
- payload += '"last_watch_date":"2017-04-28T00:40:43Z",'
- payload += '"last_timecode":"01:46:29",'
- payload += '"start_timecode":"00:00:00:00",'
- payload += '"fully_watched":false,'
- payload += '"stream_type":"dvr"}'
-
- r = requests.put(url, headers=headers, data=payload, verify=VERIFY)
-
-
-def login():
- global USERNAME
- if USERNAME == '':
- dialog = xbmcgui.Dialog()
- USERNAME = dialog.input(LOCAL_STRING(30202), type=xbmcgui.INPUT_ALPHANUM)
- if USERNAME != '':
- ADDON.setSetting(id='username', value=USERNAME)
- else:
- sys.exit()
-
- global PASSWORD
- if PASSWORD == '':
- dialog = xbmcgui.Dialog()
- PASSWORD = dialog.input(LOCAL_STRING(30203), type=xbmcgui.INPUT_ALPHANUM,
- option=xbmcgui.ALPHANUM_HIDE_INPUT)
- if PASSWORD != '':
- ADDON.setSetting(id='password', value=PASSWORD)
- else:
- sys.exit()
-
- if USERNAME != '' and PASSWORD != '':
- url = 'https://auth.api.sonyentertainmentnetwork.com/2.0/ssocookie'
- headers = {"Accept": "*/*",
- "Content-type": "application/x-www-form-urlencoded",
- "Origin": "https://id.sonyentertainmentnetwork.com",
- "Accept-Language": "en-US,en;q=0.8",
- "Accept-Encoding": "deflate",
- "User-Agent": UA_ANDROID,
- "Connection": "Keep-Alive"
- }
-
- payload = 'authentication_type=password&username='+urllib.quote_plus(USERNAME)+'&password='+urllib.quote_plus(PASSWORD)+'&client_id='+LOGIN_CLIENT_ID
- r = requests.post(url, headers=headers, cookies=load_cookies(), data=payload, verify=VERIFY)
- json_source = r.json()
- save_cookies(r.cookies)
-
- if 'npsso' in json_source:
- npsso = json_source['npsso']
- ADDON.setSetting(id='npsso', value=npsso)
- elif 'authentication_type' in json_source:
- if json_source['authentication_type'] == 'two_step':
- ticket_uuid = json_source['ticket_uuid']
- two_step_verification(ticket_uuid)
- elif 'error_description' in json_source:
- msg = json_source['error_description']
- dialog = xbmcgui.Dialog()
- ok = dialog.ok(LOCAL_STRING(30200), msg)
- sys.exit()
- else:
- # Something went wrong during login
- dialog = xbmcgui.Dialog()
- ok = dialog.ok(LOCAL_STRING(30200), LOCAL_STRING(30201))
- sys.exit()
-
-
-def two_step_verification(ticket_uuid):
- dialog = xbmcgui.Dialog()
- code = dialog.input(LOCAL_STRING(30204), type=xbmcgui.INPUT_ALPHANUM)
- if code == '': sys.exit()
-
- url = 'https://auth.api.sonyentertainmentnetwork.com/2.0/ssocookie'
- headers = {
- "Accept": "*/*",
- "Content-type": "application/x-www-form-urlencoded",
- "Origin": "https://id.sonyentertainmentnetwork.com",
- "Accept-Language": "en-US,en;q=0.8",
- "Accept-Encoding": "deflate",
- "User-Agent": UA_ANDROID,
- "Connection": "Keep-Alive",
- "Referer": "https://id.sonyentertainmentnetwork.com/signin/?service_entity=urn:service-entity:psn&ui=pr&service_logo=ps&response_type=code&scope=psn:s2s&client_id="+REQ_CLIENT_ID+"&request_locale=en_US&redirect_uri=https://io.playstation.com/playstation/psn/acceptLogin&error=login_required&error_code=4165&error_description=User+is+not+authenticated"
- }
-
- payload = 'authentication_type=two_step&ticket_uuid='+ticket_uuid+'&code='+code+'&client_id='+LOGIN_CLIENT_ID
- r = requests.post(url, headers=headers, cookies=load_cookies(), data=payload, verify=VERIFY)
- json_source = r.json()
- save_cookies(r.cookies)
-
- if 'npsso' in json_source:
- npsso = json_source['npsso']
- ADDON.setSetting(id='npsso', value=npsso)
- elif 'error_description' in json_source:
- msg = json_source['error_description']
- dialog = xbmcgui.Dialog()
- ok = dialog.ok(LOCAL_STRING(30200), msg)
- sys.exit()
- else:
- # Something went wrong during login
- dialog = xbmcgui.Dialog()
- ok = dialog.ok(LOCAL_STRING(30200), LOCAL_STRING(30201))
- sys.exit()
-
-
-def logout():
- url = 'https://auth.api.sonyentertainmentnetwork.com/2.0/ssocookie'
- headers = {
- "User-Agent": "com.sony.snei.np.android.sso.share.oauth.versa.USER_AGENT",
- "Content-Type": "application/x-www-form-urlencoded",
- "Connection": "Keep-Alive",
- "Accept-Encoding": "gzip"
- }
-
- r = requests.delete(url, headers=headers, cookies=load_cookies(), verify=VERIFY)
- # Clear addon settings
- ADDON.setSetting(id='reqPayload', value='')
- ADDON.setSetting(id='npsso', value='')
- ADDON.setSetting(id='default_profile', value='')
-
-
-def get_reqpayload():
- url = 'https://auth.api.sonyentertainmentnetwork.com/2.0/oauth/authorize'
- url += '?state=209189737'
- url += '&duid=' + DEVICE_ID
- url += '&ui=pr'
- url += '&animation=enable'
- url += '&client_id=' + REQ_CLIENT_ID
- url += '&device_base_font_size=10'
- url += '&device_profile=tablet'
- url += '&hidePageElements=noAccountSection'
- url += '&redirect_uri=https%3A%2F%2Fthemis.dl.playstation.net%2Fthemis%2Fzartan%2Fredirect.html'
- url += '&response_type=code'
- url += '&scope=psn%3As2s'
- url += '&service_entity=urn%3Aservice-entity%3Anp'
- url += '&service_logo=ps'
- url += '&smcid=android%3Apsvue'
- url += '&support_scheme=sneiprls'
-
- headers = {"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
- "X-Requested-With": "com.snei.vue.android",
- "Accept-Language": "en-US",
- "Accept-Encoding": "deflate",
- "User-Agent": UA_ANDROID,
- "Connection": "Keep-Alive",
- "Upgrade-Insecure-Requests": "1",
- "Referer": "https://id.sonyentertainmentnetwork.com/signin/?hidePageElements=noAccountSection&smcid=android%3Apsvue&client_id=" + REQ_CLIENT_ID + "&response_type=code&scope=psn%3As2s&redirect_uri=https%3A%2F%2Fthemis.dl.playstation.net%2Fthemis%2Fzartan%2Fredirect.html&state=209189737&service_entity=urn%3Aservice-entity%3Anp&duid=" + DEVICE_ID + "&ui=pr&support_scheme=sneiprls&device_profile=tablet&device_base_font_size=10&animation=enable&service_logo=ps&error=login_required&error_code=4165&error_description=User+is+not+authenticated"
- }
-
- r = requests.get(url, headers=headers, cookies=load_cookies(), verify=VERIFY)
- last_url = r.url
- parsed = urlparse.urlparse(last_url)
- code = urlparse.parse_qs(parsed.query)['code'][0]
-
- # Get reqPayload
- url = 'https://sentv-user-auth.totsuko.tv/sentv_user_auth/ws/oauth2/token'
- url += '?device_type_id=android_tablet'
- url += '&device_id=' + DEVICE_ID
- url += '&code=' + code
- url += '&redirect_uri=https%3A%2F%2Fthemis.dl.playstation.net%2Fthemis%2Fzartan%2Fredirect.html'
-
- headers = {"Accept": "*/*",
- "Origin": "https://themis.dl.playstation.net",
- "Connection": "Keep-Alive",
- "Accept-Encoding": "gzip"
- }
-
- r = requests.get(url, headers=headers, verify=VERIFY)
- if 'reqPayload' in r.headers:
- req_payload = str(r.headers['reqPayload'])
- ADDON.setSetting(id='reqPayload', value=req_payload)
- else:
- sys.exit()
-
-
def get_json(url):
headers = {'Accept': '*/*',
'reqPayload': ADDON.getSetting(id='reqPayload'),
@@ -488,92 +280,6 @@ def get_json(url):
return r.json()
-def check_reqpayload():
- check_login()
-
- if ADDON.getSetting(id='reqPayload') == '':
- get_reqpayload()
-
-
-def check_login():
- expired_cookies = True
-
- try:
- cj = cookielib.LWPCookieJar()
- cj.load(os.path.join(ADDON_PATH_PROFILE, 'cookies.lwp'), ignore_discard=True)
- if ADDON.getSetting(id='npsso') != '':
- for cookie in cj:
- if cookie.name == 'npsso':
- xbmc.log(str(cookie.name))
- xbmc.log(str(cookie.expires))
- xbmc.log(str(cookie.is_expired()))
- expired_cookies = cookie.is_expired()
- except:
- pass
-
- if expired_cookies:
- login()
-
-
-def get_profiles():
- url = 'https://sentv-user-ext.totsuko.tv/sentv_user_ext/ws/v2/profile/ids'
- headers = {
- 'User-Agent': UA_ANDROID,
- 'reqPayload': ADDON.getSetting(id='reqPayload'),
- 'Accept': '*/*',
- 'Origin': 'https://themis.dl.playstation.net',
- 'Host': 'sentv-user-ext.totsuko.tv',
- 'Connection': 'Keep-Alive',
- 'Accept-Encoding': 'gzip'
- }
-
- r = requests.get(url, headers=headers, verify=VERIFY)
- profiles = r.json()['body']['profiles']
- prof_dict = {}
- prof_list = []
- for profile in profiles:
- xbmc.log(str(profile['profile_id']) + ' ' + str(profile['profile_name']))
- prof_dict[str(profile['profile_name'])] = str(profile['profile_id'])
- prof_list.append(str(profile['profile_name']))
-
- dialog = xbmcgui.Dialog()
- ret = dialog.select('Choose Profile', prof_list)
- if ret >= 0:
- set_profile(prof_dict[prof_list[ret]])
- else:
- sys.exit()
-
-
-def set_profile(profile_id):
- url = 'https://sentv-user-ext.totsuko.tv/sentv_user_ext/ws/v2/profile/' + profile_id
- headers = {
- 'User-Agent': UA_ANDROID,
- 'reqPayload': ADDON.getSetting(id='reqPayload'),
- 'Accept': '*/*',
- 'Origin': 'https://themis.dl.playstation.net',
- 'Host': 'sentv-user-ext.totsuko.tv',
- 'Connection': 'Keep-Alive',
- 'Accept-Encoding': 'gzip'
- }
-
- r = requests.get(url, headers=headers, verify=VERIFY)
- req_payload = str(r.headers['reqPayload'])
- ADDON.setSetting(id='reqPayload', value=req_payload)
- ADDON.setSetting(id='default_profile', value=profile_id)
-
-
-def save_cookies(cookiejar):
- filename = os.path.join(ADDON_PATH_PROFILE, 'cookies.lwp')
- lwp_cookiejar = cookielib.LWPCookieJar()
- for c in cookiejar:
- args = dict(vars(c).items())
- args['rest'] = args['_rest']
- del args['_rest']
- c = cookielib.Cookie(**args)
- lwp_cookiejar.set_cookie(c)
- lwp_cookiejar.save(filename, ignore_discard=True)
-
-
def load_cookies():
filename = os.path.join(ADDON_PATH_PROFILE, 'cookies.lwp')
lwp_cookiejar = cookielib.LWPCookieJar()
@@ -597,8 +303,8 @@ def stringToDate(string, date_format):
def create_device_id():
android_id = ''.join(random.choice('0123456789abcdef') for i in range(16))
android_id = android_id.rjust(30, '0')
- manufacturer = 'Amazon'
- model = 'AFTS' # fire tv gen 2
+ manufacturer = 'ASUS'
+ model = 'Nexus 7'
manf_model = ":%s:%s" % (manufacturer.rjust(10, ' '), model.rjust(10, ' '))
manf_model = manf_model.encode("hex")
zero = '0'
@@ -681,16 +387,17 @@ FANART = os.path.join(ROOTDIR, "resources", "fanart.jpg")
ICON = os.path.join(ROOTDIR, "resources", "icon.png")
ADDON_PATH_PROFILE = xbmc.translatePath(ADDON.getAddonInfo('profile'))
-USERNAME = ADDON.getSetting(id='username')
-PASSWORD = ADDON.getSetting(id='password')
DEVICE_ID = ADDON.getSetting(id='deviceId')
+
+amazon_device = 'Amazon'
+amazon_device = amazon_device.encode("hex")
+if amazon_device in DEVICE_ID: DEVICE_ID = ''
+
if DEVICE_ID == '':
create_device_id()
DEVICE_ID = ADDON.getSetting(id='deviceId')
UA_ANDROID = 'Mozilla/5.0 (Linux; Android 6.0.1; Build/MOB31H; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/44.0.2403.119 Safari/537.36'
-LOGIN_CLIENT_ID = '71a7beb8-f21a-47d9-a604-2e71bee24fe0'
-REQ_CLIENT_ID = 'dee6a88d-c3be-4e17-aec5-1018514cee40'
CHANNEL_URL = 'https://media-framework.totsuko.tv/media-framework/media/v2.1/stream/channel'
EPG_URL = 'https://epg-service.totsuko.tv/epg_service_sony/service/v2'
VERIFY = False
diff --git a/plugin.video.psvue/resources/lib/sony.py b/plugin.video.psvue/resources/lib/sony.py
new file mode 100644
index 0000000..968169f
--- /dev/null
+++ b/plugin.video.psvue/resources/lib/sony.py
@@ -0,0 +1,334 @@
+import os, xbmc, xbmcaddon, xbmcgui
+import cookielib, requests, urllib
+from datetime import datetime
+
+
+class SONY():
+ addon = xbmcaddon.Addon()
+ api_url = 'https://auth.api.sonyentertainmentnetwork.com/2.0'
+ device_id = ''
+ localized = addon.getLocalizedString
+ login_client_id = '71a7beb8-f21a-47d9-a604-2e71bee24fe0'
+ npsso = ''
+ password = ''
+ req_client_id = 'dee6a88d-c3be-4e17-aec5-1018514cee40'
+ ua_android = 'Mozilla/5.0 (Linux; Android 6.0.1; Build/MOB31H; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/44.0.2403.119 Safari/537.36'
+ ua_sony = 'com.sony.snei.np.android.sso.share.oauth.versa.USER_AGENT'
+ username = ''
+ verify = False
+
+
+ def __init__(self):
+ self.device_id = self.addon.getSetting('deviceId')
+ self.npsso = self.addon.getSetting('npsso')
+ self.password = self.addon.getSetting('password')
+ self.username = self.addon.getSetting('username')
+
+
+ def check_auth(self):
+ self.check_login()
+ self.authorize_device()
+
+
+ def check_login(self):
+ expired_cookies = True
+ addon_profile_path = xbmc.translatePath(self.addon.getAddonInfo('profile'))
+ try:
+ cj = cookielib.LWPCookieJar()
+ cj.load(os.path.join(addon_profile_path, 'cookies.lwp'), ignore_discard=True)
+ if self.npsso != '':
+ for cookie in cj:
+ if cookie.name == 'npsso':
+ expired_cookies = cookie.is_expired()
+ break
+ except:
+ pass
+
+ if expired_cookies:
+ self.login()
+
+
+ def login(self):
+ if self.username == '':
+ dialog = xbmcgui.Dialog()
+ self.username = dialog.input(self.localized(30202), type=xbmcgui.INPUT_ALPHANUM)
+ if self.username != '':
+ self.addon.setSetting(id='username', value=self.username)
+ else:
+ sys.exit()
+
+ if self.password == '':
+ dialog = xbmcgui.Dialog()
+ self.password = dialog.input(self.localized(30203), type=xbmcgui.INPUT_ALPHANUM, option=xbmcgui.ALPHANUM_HIDE_INPUT)
+ if self.password != '':
+ self.addon.setSetting(id='password', value=self.password)
+ else:
+ sys.exit()
+
+ if self.username != '' and self.password != '':
+ url = self.api_url + '/ssocookie'
+ headers = {"Accept": "*/*",
+ "Content-type": "application/x-www-form-urlencoded",
+ "Origin": "https://id.sonyentertainmentnetwork.com",
+ "Accept-Language": "en-US,en;q=0.8",
+ "Accept-Encoding": "deflate",
+ "User-Agent": self.ua_android,
+ "Connection": "Keep-Alive"
+ }
+
+ payload = 'authentication_type=password&username='+urllib.quote_plus(self.username)+'&password='+urllib.quote_plus(self.password)+'&client_id='+self.login_client_id
+ r = requests.post(url, headers=headers, cookies=self.load_cookies(), data=payload, verify=self.verify)
+ json_source = r.json()
+ self.save_cookies(r.cookies)
+
+ if 'npsso' in json_source:
+ npsso = json_source['npsso']
+ self.addon.setSetting(id='npsso', value=npsso)
+ elif 'authentication_type' in json_source:
+ if json_source['authentication_type'] == 'two_step':
+ ticket_uuid = json_source['ticket_uuid']
+ self.two_step_verification(ticket_uuid)
+ elif 'error_description' in json_source:
+ msg = json_source['error_description']
+ self.error_msg(self.localized(30200), msg)
+ sys.exit()
+ else:
+ # Something went wrong during login
+ self.error_msg(self.localized(30200), self.localized(30201))
+ sys.exit()
+
+
+ def two_step_verification(self, ticket_uuid):
+ dialog = xbmcgui.Dialog()
+ code = dialog.input(self.localized(30204), type=xbmcgui.INPUT_ALPHANUM)
+ if code == '': sys.exit()
+
+ url = self.api_url + '/ssocookie'
+ headers = {
+ "Accept": "*/*",
+ "Content-type": "application/x-www-form-urlencoded",
+ "Origin": "https://id.sonyentertainmentnetwork.com",
+ "Accept-Language": "en-US,en;q=0.8",
+ "Accept-Encoding": "deflate",
+ "User-Agent": self.ua_android,
+ "Connection": "Keep-Alive",
+ "Referer": "https://id.sonyentertainmentnetwork.com/signin/?service_entity=urn:service-entity:psn&ui=pr&service_logo=ps&response_type=code&scope=psn:s2s&client_id="+self.req_client_id+"&request_locale=en_US&redirect_uri=https://io.playstation.com/playstation/psn/acceptLogin&error=login_required&error_code=4165&error_description=User+is+not+authenticated"
+ }
+
+ payload = 'authentication_type=two_step&ticket_uuid='+ticket_uuid+'&code='+code+'&client_id='+self.login_client_id
+ r = requests.post(url, headers=headers, cookies=self.load_cookies(), data=payload, verify=self.verify)
+ json_source = r.json()
+ self.save_cookies(r.cookies)
+
+ if 'npsso' in json_source:
+ npsso = json_source['npsso']
+ self.addon.setSetting(id='npsso', value=npsso)
+ elif 'error_description' in json_source:
+ msg = json_source['error_description']
+ self.error_msg(self.localized(30200), msg)
+ sys.exit()
+ else:
+ # Something went wrong during login
+ self.error_msg(self.localized(30200), self.localized(30201))
+ sys.exit()
+
+
+ def logout(self):
+ url = self.api_url + '/ssocookie'
+ headers = {
+ "User-Agent": self.ua_sony,
+ "Content-Type": "application/x-www-form-urlencoded",
+ "Connection": "Keep-Alive",
+ "Accept-Encoding": "gzip"
+ }
+
+ r = requests.delete(url, headers=headers, cookies=self.load_cookies(), verify=self.verify)
+ # Clear addon settings
+ self.addon.setSetting(id='reqPayload', value='')
+ self.addon.setSetting(id='last_auth', value='')
+ self.addon.setSetting(id='npsso', value='')
+ self.addon.setSetting(id='default_profile', value='')
+
+
+ def get_grant_code(self):
+ url = self.api_url + '/oauth/authorize'
+ url += '?response_type=code'
+ url += '&client_id=' + self.req_client_id
+ url += '&redirect_uri=https%3A%2F%2Fthemis.dl.playstation.net%2Fthemis%2Fzartan%2Fredirect.html'
+ url += '&scope=psn%3As2s'
+ url += '&signInOnly=true'
+ url += '&service_entity=urn%3Aservice-entity%3Anp'
+ url += '&prompt=none'
+ url += '&duid=' + self.device_id
+
+ headers = {
+ "Accept-Encoding": "gzip",
+ "User-Agent": self.ua_sony,
+ "Connection": "Keep-Alive",
+ }
+
+ code = ''
+ r = requests.get(url, headers=headers, allow_redirects=False, cookies=self.load_cookies(), verify=self.verify)
+ if 'X-NP-GRANT-CODE' in r.headers:
+ code = r.headers['X-NP-GRANT-CODE']
+ else:
+ self.error_msg(self.localized(30207), self.localized(30208))
+ sys.exit()
+
+ return code
+
+
+ def authorize_device(self):
+ url = 'https://sentv-user-auth.totsuko.tv/sentv_user_auth/ws/oauth2/token'
+ url += '?device_type_id=android_tablet'
+ url += '&device_id=' + self.device_id
+ url += '&code=' + self.get_grant_code()
+ url += '&redirect_uri=https%3A%2F%2Fthemis.dl.playstation.net%2Fthemis%2Fzartan%2Fredirect.html'
+
+ headers = {
+ 'Origin': 'https://themis.dl.playstation.net',
+ 'User-Agent': self.ua_android,
+ 'Accept': '*/*',
+ 'Connection': 'Keep-Alive',
+ 'Accept-Encoding': 'gzip'
+ }
+ if self.addon.getSetting(id='reqPayload') != '':
+ headers['reauth'] = '1'
+ headers['reqPayload'] = self.addon.getSetting(id='reqPayload')
+
+ r = requests.get(url, headers=headers, verify=self.verify)
+ if 'reqPayload' in r.headers:
+ req_payload = str(r.headers['reqPayload'])
+ self.addon.setSetting(id='reqPayload', value=req_payload)
+ auth_time = r.json()['header']['time_stamp']
+ self.addon.setSetting(id='last_auth', value=auth_time)
+ else:
+ self.error_msg(self.localized(30207), self.localized(30209))
+ sys.exit()
+
+
+ def get_profiles(self):
+ url = 'https://sentv-user-ext.totsuko.tv/sentv_user_ext/ws/v2/profile/ids'
+ headers = {
+ 'User-Agent': self.ua_android,
+ 'reqPayload': self.addon.getSetting(id='reqPayload'),
+ 'Accept': '*/*',
+ 'Origin': 'https://themis.dl.playstation.net',
+ 'Connection': 'Keep-Alive',
+ 'Accept-Encoding': 'gzip'
+ }
+
+ r = requests.get(url, headers=headers, verify=self.verify)
+ if 'body' in r.json() and 'profiles' in r.json()['body']:
+ profiles = r.json()['body']['profiles']
+ prof_dict = {}
+ prof_list = []
+ for profile in profiles:
+ xbmc.log(str(profile['profile_id']) + ' ' + str(profile['profile_name']))
+ prof_dict[str(profile['profile_name'])] = str(profile['profile_id'])
+ prof_list.append(str(profile['profile_name']))
+
+ dialog = xbmcgui.Dialog()
+ ret = dialog.select(self.localized(30210), prof_list)
+ if ret >= 0:
+ self.set_profile(prof_dict[prof_list[ret]])
+ else:
+ sys.exit()
+ else:
+ self.error_msg(self.localized(30205), self.localized(30206))
+ sys.exit()
+
+
+ def set_profile(self, profile_id):
+ url = 'https://sentv-user-ext.totsuko.tv/sentv_user_ext/ws/v2/profile/' + profile_id
+ headers = {
+ 'User-Agent': self.ua_android,
+ 'reqPayload': self.addon.getSetting(id='reqPayload'),
+ 'Accept': '*/*',
+ 'Origin': 'https://themis.dl.playstation.net',
+ 'Host': 'sentv-user-ext.totsuko.tv',
+ 'Connection': 'Keep-Alive',
+ 'Accept-Encoding': 'gzip'
+ }
+
+ r = requests.get(url, headers=headers, verify=self.verify)
+ req_payload = str(r.headers['reqPayload'])
+ self.addon.setSetting(id='reqPayload', value=req_payload)
+ auth_time = r.json()['header']['time_stamp']
+ self.addon.setSetting(id='last_auth', value=auth_time)
+ self.addon.setSetting(id='default_profile', value=profile_id)
+
+
+ def put_resume_time(self):
+ """
+ PUT https://sentv-user-action.totsuko.tv/sentv_user_action/ws/v2/watch_history HTTP/1.1
+ Host: sentv-user-action.totsuko.tv
+ Connection: keep-alive
+ Content-Length: 247
+ Accept: */*
+ reqPayload: redacted
+ User-Agent: Mozilla/5.0 (Linux; Android 6.0.1; Build/MOB31H; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/44.0.2403.119 Safari/537.36
+ Origin: https://themis.dl.playstation.net
+ Content-Type: application/json
+ Referer: https://themis.dl.playstation.net/themis/zartan/2.2.2b/
+ Accept-Encoding: gzip, deflate
+ Accept-Language: en-US
+ X-Requested-With: com.snei.vue.android
+
+ {"series_id":redacted,"program_id":redacted,"channel_id":redacted,"tms_id":"EP005544655496","airing_id":redacted,"last_watch_date":"2017-04-28T00:40:43Z","last_timecode":"01:46:29","start_timecode":"00:00:00:00","fully_watched":false,"stream_type":"dvr"}
+ """
+ url = 'https://sentv-user-action.totsuko.tv/sentv_user_action/ws/v2/watch_history'
+ headers = {"Accept": "*/*",
+ "Content-type": "application/json",
+ "Origin": "https://themis.dl.playstation.net",
+ "Accept-Language": "en-US",
+ "Referer": "https://themis.dl.playstation.net/themis/zartan/2.2.2b/",
+ "Accept-Encoding": "gzip, deflate",
+ "User-Agent": self.ua_android,
+ "Connection": "Keep-Alive",
+ 'reqPayload': self.addon.getSetting(id='reqPayload'),
+ 'X-Requested-With': 'com.snei.vue.android'
+ }
+
+ payload = '{"series_id":redacted,'
+ payload += '"program_id":redacted,'
+ payload += '"channel_id":redacted,'
+ payload += '"tms_id":"redacted",'
+ payload += '"airing_id":redacted,'
+ payload += '"last_watch_date":"2017-04-28T00:40:43Z",'
+ payload += '"last_timecode":"01:46:29",'
+ payload += '"start_timecode":"00:00:00:00",'
+ payload += '"fully_watched":false,'
+ payload += '"stream_type":"dvr"}'
+
+ r = requests.put(url, headers=headers, data=payload, verify=self.verify)
+
+
+ def save_cookies(self, cookiejar):
+ addon_profile_path = xbmc.translatePath(self.addon.getAddonInfo('profile'))
+ filename = os.path.join(addon_profile_path, 'cookies.lwp')
+ lwp_cookiejar = cookielib.LWPCookieJar()
+ for c in cookiejar:
+ args = dict(vars(c).items())
+ args['rest'] = args['_rest']
+ del args['_rest']
+ c = cookielib.Cookie(**args)
+ lwp_cookiejar.set_cookie(c)
+ lwp_cookiejar.save(filename, ignore_discard=True)
+
+
+ def load_cookies(self):
+ addon_profile_path = xbmc.translatePath(self.addon.getAddonInfo('profile'))
+ filename = os.path.join(addon_profile_path, 'cookies.lwp')
+ lwp_cookiejar = cookielib.LWPCookieJar()
+ try:
+ lwp_cookiejar.load(filename, ignore_discard=True)
+ except:
+ pass
+
+ return lwp_cookiejar
+
+
+ def error_msg(self, title, msg):
+ dialog = xbmcgui.Dialog()
+ dialog.notification(title, msg, xbmcgui.NOTIFICATION_INFO, 5000)
diff --git a/plugin.video.psvue/resources/settings.xml b/plugin.video.psvue/resources/settings.xml
index d54ae70..2628d20 100644
--- a/plugin.video.psvue/resources/settings.xml
+++ b/plugin.video.psvue/resources/settings.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<settings>
<!--Login-->
- <category label="30000">
+ <category label="30000">
<setting id="username" type="text" label="30001" default=""/>
<setting id="password" type="text" label="30002" option="hidden" default=""/>
<setting id="logout" type="action" label="30003" action="RunPlugin(plugin://plugin.video.psvue/?mode=999)" option="close" />
@@ -10,6 +10,7 @@
<!-- Hidden -->
<setting id="reqPayload" type="text" default="" visible="false"/>
+ <setting id="last_auth" type="text" default="" visible="false"/>
<setting id="deviceId" type="text" default="" visible="false"/>
<setting id="npsso" type="text" default="" visible="false"/>
<setting id="default_profile" type="text" default="" visible="false"/>