summaryrefslogtreecommitdiff
path: root/plugin.video.ustvnow
diff options
context:
space:
mode:
authorLunatixz <Lunatixz@users.noreply.github.com>2017-07-16 09:44:54 -0400
committerenen92 <enen92@users.noreply.github.com>2017-07-16 14:44:54 +0100
commit77fc8fe3560fc946de7c3391b4ec04cf8c32f4d1 (patch)
tree44b1b6f4c7bd4cf68d16af10de361b9e6956eaa2 /plugin.video.ustvnow
parent9ce79969174c5db89f4aea414397408a2e154877 (diff)
[plugin.video.ustvnow] 2017.4.7 (#1321)
Watch All the Major American Television Networks Live in HD for Free!
Diffstat (limited to 'plugin.video.ustvnow')
-rw-r--r--plugin.video.ustvnow/addon.xml29
-rw-r--r--plugin.video.ustvnow/default.py584
-rw-r--r--plugin.video.ustvnow/net.py328
-rw-r--r--plugin.video.ustvnow/resources/images/fanart.jpgbin0 -> 120956 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/icon.pngbin0 -> 49124 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/ABC.pngbin0 -> 3706 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/AE.pngbin0 -> 2489 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/AMC.pngbin0 -> 7769 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/Animal Planet.pngbin0 -> 3652 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/BBC America.pngbin0 -> 5459 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/Bravo.pngbin0 -> 1366 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/CBS.pngbin0 -> 2081 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/CNBC.pngbin0 -> 6271 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/CNN.pngbin0 -> 2716 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/CW.pngbin0 -> 1255 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/Cartoon Network.pngbin0 -> 4494 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/Comedy Central.pngbin0 -> 1946 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/Discovery Channel.pngbin0 -> 16719 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/ESPN 2.pngbin0 -> 627 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/ESPN.pngbin0 -> 1368 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/FOX.pngbin0 -> 2779 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/FX.pngbin0 -> 1316 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/Fox News.pngbin0 -> 3953 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/Freeform.pngbin0 -> 5200 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/History.pngbin0 -> 3244 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/Lifetime.pngbin0 -> 5806 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/MSNBC.pngbin0 -> 1871 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/MY9.pngbin0 -> 2175 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/NBC.pngbin0 -> 6113 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/NBCSN.pngbin0 -> 4692 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/National Geographic.pngbin0 -> 2349 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/Nickelodeon.pngbin0 -> 1727 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/PBS.pngbin0 -> 2334 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/SPIKE TV.pngbin0 -> 1422 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/SundanceTV.pngbin0 -> 3611 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/Syfy.pngbin0 -> 1540 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/TBS.pngbin0 -> 2077 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/TLC.pngbin0 -> 922 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/TNT.pngbin0 -> 1335 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/USA Network.pngbin0 -> 3370 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/logos/Universal.pngbin0 -> 8698 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/screenshot01.pngbin0 -> 561123 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/screenshot02.pngbin0 -> 693317 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/screenshot03.pngbin0 -> 517590 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/screenshot04.pngbin0 -> 751334 bytes
-rw-r--r--plugin.video.ustvnow/resources/images/screenshot05.pngbin0 -> 379036 bytes
-rw-r--r--plugin.video.ustvnow/resources/language/resource.language.en_gb/strings.po117
-rw-r--r--plugin.video.ustvnow/resources/settings.xml11
48 files changed, 1069 insertions, 0 deletions
diff --git a/plugin.video.ustvnow/addon.xml b/plugin.video.ustvnow/addon.xml
new file mode 100644
index 0000000..77525a7
--- /dev/null
+++ b/plugin.video.ustvnow/addon.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<addon id="plugin.video.ustvnow" name="USTVnow" provider-name="Lunatixz" version="2017.4.7">
+ <requires>
+ <import addon="xbmc.python" version="2.25.0"/>
+ <import addon="script.module.simplecache" version="1.0.0"/>
+ </requires>
+ <extension point="xbmc.python.pluginsource" library="default.py">
+ <provides>video</provides>
+ </extension>
+ <extension point="xbmc.addon.metadata">
+ <summary lang="en_GB">Watch All the Major American Television Networks Live in HD for Free!</summary>
+ <description lang="en_GB">Watch All the Major American Television Networks Live in HD! Entirely Legal and Free! You can also record content through their web site, and there is an optional premium service available with all kinds of other channels. Signup for a free account through the following link today: http://watch.ustvnow.com</description>
+ <platform>all</platform>
+ <license>GNU GENERAL PUBLIC LICENSE. Version 3, June 2007</license>
+ <source>https://github.com/Lunatixz/XBMC_Addons/tree/master/plugin.video.ustvnow</source>
+ <website>http://ustvnow.com</website>
+ <forum>http://forum.kodi.tv/showthread.php?tid=233135</forum>
+ <news>2017.4.7[CR]-100% new code base, written from scratch for your enjoy!!, DVR Recording and Playback Untested.[CR]2017.4.7a[CR]- Added "Featured"[CR]- Fixed DVR support.</news>
+ <assets>
+ <icon>resources/images/icon.png</icon>
+ <fanart>resources/images/fanart.jpg</fanart>
+ <screenshot>resources/images/screenshot01.png</screenshot>
+ <screenshot>resources/images/screenshot02.png</screenshot>
+ <screenshot>resources/images/screenshot03.png</screenshot>
+ <screenshot>resources/images/screenshot04.png</screenshot>
+ <screenshot>resources/images/screenshot05.png</screenshot>
+ </assets>
+ </extension>
+</addon> \ No newline at end of file
diff --git a/plugin.video.ustvnow/default.py b/plugin.video.ustvnow/default.py
new file mode 100644
index 0000000..047196c
--- /dev/null
+++ b/plugin.video.ustvnow/default.py
@@ -0,0 +1,584 @@
+# Copyright (C) 2017 Lunatixz
+#
+#
+# This file is part of USTVnow
+#
+# USTVnow is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# USTVnow is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with USTVnow. If not, see <http://www.gnu.org/licenses/>.
+
+# -*- coding: utf-8 -*-
+import os, sys, datetime, re, traceback, HTMLParser, calendar
+import urllib, urllib2, socket, json, collections, net, random
+import xbmc, xbmcvfs, xbmcgui, xbmcplugin, xbmcaddon
+
+from simplecache import SimpleCache
+# Plugin Info
+ADDON_ID = 'plugin.video.ustvnow'
+REAL_SETTINGS = xbmcaddon.Addon(id=ADDON_ID)
+ADDON_NAME = REAL_SETTINGS.getAddonInfo('name')
+SETTINGS_LOC = REAL_SETTINGS.getAddonInfo('profile')
+ADDON_PATH = REAL_SETTINGS.getAddonInfo('path').decode('utf-8')
+ADDON_VERSION = REAL_SETTINGS.getAddonInfo('version')
+ICON = REAL_SETTINGS.getAddonInfo('icon')
+FANART = REAL_SETTINGS.getAddonInfo('fanart')
+LANGUAGE = REAL_SETTINGS.getLocalizedString
+
+## GLOBALS ##
+TIMEOUT = 30
+USER_EMAIL = REAL_SETTINGS.getSetting('User_Email')
+PASSWORD = REAL_SETTINGS.getSetting('User_Password')
+LAST_TOKEN = REAL_SETTINGS.getSetting('User_Token')
+LAST_PASSKEY = REAL_SETTINGS.getSetting('User_Paskey')
+DEBUG = REAL_SETTINGS.getSetting('Enable_Debugging') == 'true'
+PTVL_RUN = xbmcgui.Window(10000).getProperty('PseudoTVRunning') == 'True'
+BASEURL = 'http://m-api.ustvnow.com/'
+BASEMOB = 'http://mc.ustvnow.com/'
+IMG_PATH = os.path.join(ADDON_PATH,'resources','images')
+IMG_HTTP = BASEMOB + 'gtv/1/live/viewposter?srsid='
+IMG_MOVIE = 'http://tvdata.ustvnow.com/movieposters/'
+IMG_TV = 'http://tvdata.ustvnow.com/tvbanners/'
+IMG_POSTER = 'http://m.poster.static-ustvnow.com/'
+IMG_POSTER_RE= 'http://m.reimages.static-ustvnow.com/'
+IMG_SNAPSHOT = 'http://m.snapshot.static-ustvnow.com/%s/high'
+IMG_CHLOGO = 'http://m.ustvnow.com/images/%s.png'
+COOKIE_JAR = xbmc.translatePath(os.path.join(SETTINGS_LOC, "cookiejar.lwp"))
+MEDIA_TYPES = {'SP':'video','SH':'episode','EP':'episode','MV':'movie'}
+FREE_CHANS = ['CW','ABC','FOX','PBS','CBS','NBC','MY9']
+
+CHAN_NAMES = {'ABC':'ABC','AMC':'AMC','Animal Planet':'Animal Planet','Bravo':'Bravo','CBS':'CBS','CNBC':'CNBC','CW':'CW','Comedy Central':'Comedy Central','Discovery Channel':'Discovery Channel','ESPN':'ESPN',
+ 'FOX':'FOX','FX':'FX','Fox News Channel':'Fox News','Freeform':'Freeform','MSNBC':'MSNBC','NBC':'NBC','National Geographic Channel':'National Geographic','Nickelodeon':'Nickelodeon','PBS':'PBS',
+ 'SPIKE TV':'SPIKE TV','SundanceTV':'SundanceTV','Syfy':'Syfy',
+ 'AE':'A&E','My9':'MY9','BBCA':'BBC America','ESPN2':'ESPN 2','NBCSNHD':'NBCSN','The Learning Channel':'TLC','Universal HD':'Universal','USA':'USA Network','SUNDHD':'SundanceTV'}
+
+USTVNOW_MENU = [("Live" , '', 0),
+ ("Schedules" , '', 1),
+ ("Recordings", '', 2),
+ ("Guide" , '', 3),
+ ("Featured" , '', 5)]
+
+def unescape(string):
+ parser = HTMLParser.HTMLParser()
+ return parser.unescape(string)
+
+def log(msg, level=xbmc.LOGDEBUG):
+ if DEBUG == True:
+ if level == xbmc.LOGERROR:
+ msg += ' ,' + traceback.format_exc()
+ xbmc.log(ADDON_ID + '-' + ADDON_VERSION + '-' + msg, level)
+
+def inputDialog(heading=ADDON_NAME, default='', key=xbmcgui.INPUT_ALPHANUM, opt=0, close=0):
+ retval = xbmcgui.Dialog().input(heading, default, key, opt, close)
+ if len(retval) > 0:
+ return retval
+
+def okDialog(str1, str2='', str3='', header=ADDON_NAME):
+ xbmcgui.Dialog().ok(header, str1, str2, str3)
+
+def yesnoDialog(str1, str2='', str3='', header=ADDON_NAME, yes='', no='', autoclose=0):
+ return xbmcgui.Dialog().yesno(header, str1, str2, str3, no, yes, autoclose)
+
+def getParams():
+ param=[]
+ if len(sys.argv[2])>=2:
+ params=sys.argv[2]
+ cleanedparams=params.replace('?','')
+ if (params[len(params)-1]=='/'):
+ params=params[0:len(params)-2]
+ pairsofparams=cleanedparams.split('&')
+ param={}
+ for i in range(len(pairsofparams)):
+ splitparams={}
+ splitparams=pairsofparams[i].split('=')
+ if (len(splitparams))==2:
+ param[splitparams[0]]=splitparams[1]
+ return param
+
+socket.setdefaulttimeout(TIMEOUT)
+class USTVnow():
+ def __init__(self):
+ log('__init__')
+ self.net = net.Net()
+ self.cache = SimpleCache()
+ if self.login(USER_EMAIL, PASSWORD) == False:
+ raise SystemExit
+
+
+ def mainMenu(self):
+ log('mainMenu')
+ for item in USTVNOW_MENU:
+ self.addDir(*item)
+
+
+ def buildHeader(self):
+ header_dict = {}
+ header_dict['Accept'] = 'application/json, text/javascript, */*; q=0.01'
+ header_dict['Host'] = 'm-api.ustvnow.com'
+ header_dict['Connection'] = 'keep-alive'
+ header_dict['Referer'] = 'http://watch.ustvnow.com'
+ header_dict['Origin'] = 'http://watch.ustvnow.com'
+ header_dict['User-Agent'] = 'Mozilla/5.0 (Windows NT 6.2; rv:24.0) Gecko/20100101 Firefox/24.0'
+ return header_dict
+
+
+ def login(self, user, password):
+ log('login')
+ if len(user) > 0:
+ try:
+ #remove COOKIE_JAR Folder
+ xbmcvfs.rmdir(COOKIE_JAR)
+ except:
+ pass
+
+ if xbmcvfs.exists(COOKIE_JAR) == False:
+ try:
+ xbmcvfs.mkdirs(SETTINGS_LOC)
+ f = xbmcvfs.File(COOKIE_JAR, 'w')
+ f.close()
+ except:
+ log('login, Unable to create the storage directory', xbmc.LOGERROR)
+ header_dict = self.buildHeader()
+ self.net.set_cookies(COOKIE_JAR)
+
+ try:
+ #check token
+ dvrlink = None
+ custlink = json.loads(self.net.http_POST(BASEURL + 'gtv/1/live/getcustomerkey', form_data={'token':LAST_TOKEN}, headers=header_dict).content.encode("utf-8").rstrip())
+ '''{u'username': u'', u'ip': u'', u'customerkey': u''}'''
+ self.custkey = (custlink.get('customerkey','') or 0)
+ if custlink and 'username' in custlink and user.lower() == custlink['username'].lower():
+ log('login, using existing token, passkey')
+ self.token = LAST_TOKEN
+ self.passkey = LAST_PASSKEY
+ else:
+ #login
+ loginlink = json.loads(self.net.http_POST(BASEURL + 'gtv/1/live/login', form_data={'username':user,'password':password,'device':'gtv','redir':'0'}, headers=header_dict).content.encode("utf-8").rstrip())
+ '''{u'token': u'', u'result': u'success'}'''
+ if loginlink and 'token' in loginlink and loginlink['result'].lower() == 'success':
+ log('login, creating new token')
+ self.token = loginlink['token']
+ REAL_SETTINGS.setSetting('User_Token',self.token)
+
+ #passkey
+ dvrlink = json.loads(self.net.http_POST(BASEURL + 'gtv/1/live/viewdvrlist', form_data={'token':self.token}, headers=header_dict).content.encode("utf-8").rstrip())
+ '''{u'globalparams': {u'passkey': u''}, u'results': []}'''
+ if dvrlink and 'globalparams' in dvrlink:
+ log('login, creating new passkey')
+ self.passkey = dvrlink['globalparams']['passkey']
+ REAL_SETTINGS.setSetting('User_Paskey',self.passkey)
+ else:
+ raise Exception
+
+ #check user credentials
+ userlink = json.loads(self.net.http_POST(BASEURL + 'gtv/1/live/getuserbytoken', form_data={'token':self.token}, headers=header_dict).content.encode("utf-8").rstrip())
+ '''{u'status': u'success', u'data': {u'username': u'', u'need_account_activation': False, u'plan_id': 1, u'language': u'en', u'plan_free': 1, u'sub_id': u'7', u'lname': u'', u'currency': u'USD', u'points': 1, u'need_account_renew': False, u'fname': u'', u'plan_name': u'Free Plan'}}'''
+ log('login, checking user account')
+ if userlink and 'data' in userlink and userlink['status'].lower() == 'success':
+ REAL_SETTINGS.setSetting('User_Plan' ,userlink['data']['plan_name'])
+ expires = 'Never' if userlink['data']['plan_free'] == 1 else ''
+ REAL_SETTINGS.setSetting('User_Expires' ,'%s'%expires)
+ REAL_SETTINGS.setSetting('User_isFree' ,str(userlink['data']['plan_free'] == 1))
+ REAL_SETTINGS.setSetting('User_DVRpoints' ,str((userlink['data']['points']) or 0))
+ xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30006) + userlink['data']['fname'], ICON, 4000)
+
+ if userlink['data']['need_account_renew'] == True:
+ xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30016) + userlink['data']['fname'], ICON, 4000)
+ elif userlink['data']['need_account_activation'] == True:
+ xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30022) + userlink['data']['fname'], ICON, 4000)
+ else:
+ if REAL_SETTINGS.getSetting('User_DVRpoints') != REAL_SETTINGS.getSetting('Last_DVRpoints'):
+ REAL_SETTINGS.setSetting('Last_DVRpoints',REAL_SETTINGS.getSetting('User_DVRpoints'))
+ xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30021) + userlink['data']['fname'], ICON, 4000)
+
+ #check subscription
+ if self.custkey != 0:
+ sublink = json.loads(self.net.http_POST(BASEURL + 'gtv/1/live/getaccountsubscription', form_data={'username':user,'customerkey':self.custkey}, headers=header_dict).content.encode("utf-8").rstrip())
+ '''{u'username': u'', u'billingDatetime': u'', u'currency': u'USD', u'cgaccountstatusreason': u'', u'invoicehistory': u'', u'ocaccountstatus': u'Account active', u'ccLastFour': u'', u'lname': u'', u'x-cg-acnt-USD': False, u'fname': u'', u'sub_info': {u'cost': 0, u'plan': {u'sub_group': 4, u'plan_id': 1, u'name': u'Free Plan', u'language': u'en', u'date_expire': u'0000-00-00', u'sub_id': 7, u'price': 0, u'currency': u'USD', u'plan_code': u'7_FREETRIAL',
+ u'details': u'This plan lets you receive all major US terrestrial stations (ABC, CBS, CW, FOX, NBC, PBS). You can later upgrade to a paid plan with more channels and DVR.'}, u'packages': []}, u'pendinginvoices': u'', u'cgbillingstatus': u'', u'dvrpoints': 1, u'plans': {u'10': {u'price': 15, u'name': u'1 Week All Channel Plan $15 ($2.14/day)',
+ u'details': u'1 Week pass for all channels (No DVR)'}, u'23': {u'price': 19, u'name': u'All Channel Promo Plan $19/mo first 3 months',
+ u'details': u'Monthly subscription for all channels. This promotional price is for the first three months after which it will renew at $29. This plan automatically renews each month but you can cancel anytime and will not be billed again when your current 30 day period has expired. '}, u'31': {u'price': 29, u'name': u'All Channel Promo Plan w/DVR $29/mo first 3 months',
+ u'details': u'Monthly subscription for all channels. This promotional price is for the first three months after which it will renew at $39. This plan automatically renews each month but you can cancel anytime and will not be billed again when your current 30 day period has expired.'}, u'7': {u'price': 0, u'name': u'Free Plan',
+ u'details': u'This plan lets you receive all major US terrestrial stations (ABC, CBS, CW, FOX, NBC, PBS). You can later upgrade to a paid plan with more channels and DVR.'}, u'9': {u'price': 0.00, u'name': u'3 Day All Channel Plan $6.99 ($2.33/day)',
+ u'details': u'3 Day pass for all channels (No DVR)'}, u'8': {u'price': 2.99, u'name': u'1 Day All Channel Plan $2.99',
+ u'details': u'24 hour pass for all channels (No DVR)'}}, u'cgredirectUrl': u'', u'cgaccountstatus': False, u'isfacebookuser': False, u'cgkey': u'', u'ocaccountstatuscode': 0, u'packages': [], u'subscription': u'Free Plan', u'language': u'en', u'sub_id': u'7', u'dateopened': u'December 01, 2000', u'canceledDatetime': u'', u'cgbillingmethod': u''}'''
+ if sublink:
+ REAL_SETTINGS.setSetting('User_Expires' ,str(sublink['sub_info']['date_expire']))
+ REAL_SETTINGS.setSetting('User_Created' ,str(sublink['sub_info']['dateopened']))
+ else:
+ raise Exception
+ self.net.save_cookies(COOKIE_JAR)
+
+ self.channels = channels = self.cache.get(ADDON_NAME + '.channelguide')
+ if not self.channels:
+ log('login, refreshing channels')
+ channels = sorted(json.loads(self.net.http_POST(BASEURL + 'gtv/1/live/channelguide', form_data={'token':self.token}, headers=header_dict).content.encode("utf-8").rstrip())['results'], key=lambda x: x['displayorder'])
+ '''{u'app_name': u'preview', u'stream': u'00000WXYZustvnow', u'af': u'US', u'dvraction': u'add', u'callsign': u'WHTM', u'event_inprogress': 1,
+ u'srsid': 3560383, u'guideremainingtime': 3660, u'scheduleid': 9952642, u'favoriteaction': u'remove', u'event_time': u'00:00:00',u'title': u'Shark Tank',
+ u'timemark': 1498867200, u'recordedon': u'June 30, 2017 20:00', u'prg_img': u'h3/NowShowing/9977826/p9977826_b1t_h3_aa.jpg', u'title_10': u'',
+ u'xcdrappname': u'livehd', u'event_date': u'2017-07-01', u'has_img': 1, u'stream_code': u'ABC', u'updated': u'2017-06-29 11:49:08',
+ u'description': u'Durable bags made out of the material that protects on the front lines of firefighting; a vibrating mat that helps calm babies;
+ an ointment made from essential oils; a natural snack made with acai.', u'actualremainingtime': 3281, u'content_allowed': False,
+ u'dvrtimeraction': u'add', u'mediatype': u'EP', u'auth': None, u'stream_origin': u'dne.ustvnow.com', u'dvrtimertype': 0, u'scode': u'whtm',
+ u'aksid': 1, u'guideheight': 120, u'orig_air_date': u'2017-02-10', u'prgsvcid': 11534, u'runtime': 3660, u'img': u'images/WHTM.png',
+ u'connectorid': u'SH011581290000', u'ut_start': 1498867200, u'streamname': u'00000WXYZustvnow', u'episode_title': u'', u'synopsis':
+ u'A vibrating mat that helps calm babies.', u't': 1, u'edgetypes': 7, u'imgmark': u'live', u'content_id': u'EP011581290171', u'order': 1,
+ u'displayorder': 1}'''
+ self.cache.set(ADDON_NAME + '.channelguide', channels, expiration=datetime.timedelta(minutes=10))
+ self.channels = self.cache.get(ADDON_NAME + '.channelguide'),
+
+ self.upcoming = self.cache.get(ADDON_NAME + '.upcoming')
+ if not self.upcoming:
+ log('login, refreshing upcoming')
+ upcoming = json.loads(self.net.http_POST(BASEURL + 'gtv/1/live/upcoming', form_data={'token':self.token}, headers=header_dict).content.encode("utf-8").rstrip())
+ '''{u'prgschid': 19479379, u'dvraction': u'add', u'callsign': u'AMC', u'newtimecat': True, u'srsid': 14930, u'timecat': u'Today', u'scheduleid': 9946282,
+ u'img': u'images/AMC.png', u'title': u'The Fugitive', u'prg_img': u'v5/NowShowing/14930/p14930_p_v5_aa.jpg', u'has_img': 1, u't': 0, u'sname': u'AMC',
+ u'description': u'U.S. marshal (Tommy Lee Jones) hunts doctor (Harrison Ford) for murder of his wife (Sela Ward).', u'dvrtimeraction': u'add',
+ u'auth': None, u'orig_air_date': None, u'dvrtimertype': 0, u'scode': u'amchd', u'prgsvcid': 10021, u'runtime': 10800, u'connectorid': u'MV000371790000',
+ u'episode_title': u'', u'synopsis': u'An innocent man must evade the law as he pursues a killer.', u'event_inprogress': 1, u'ut_start': 1499009400,
+ u'content_allowed': False, u'imgmark': u'live', u'content_id': u'MV000371790000', u'displayorder': 100}'''
+ self.cache.set(ADDON_NAME + '.upcoming', upcoming, expiration=datetime.timedelta(minutes=10))
+ self.upcoming = self.cache.get(ADDON_NAME + '.upcoming')
+
+ self.recorded = self.cache.get(ADDON_NAME + '.recorded')
+ if not self.recorded:
+ log('login, refreshing recorded')
+ '''{u'fn1': u'20170702', u'episode_season': 0, u'filename_smil': u'20170702_213000_220000_utc_SH000000010000_11534_11534_ABC.smil', u'app_name': u'dvrrokuplay',
+ u'stream': u'2F1ECWHTMUSTVNOW', u'imgmark': u'rem', u'dvrtimeraction': u'add', u'mediatype': u'SH', u'orig_air_date': None, u'content_id': u'SH000000010000',
+ u'callsign': u'WHTM', u'ut_expires': 1500327000, u'filename_med': u'20170702_213000_220000_utc_SH000000010000_11534_11534_ABC_650.mp4',
+ u'filename_high': u'20170702_213000_220000_utc_SH000000010000_11534_11534_ABC_950.mp4', u'dvrlocation': u'dvr6', u'episode_number': 0, u'srsid': 459763,
+ u'runtime': 1800, u'description': u'Paid programming.', u'scheduleid': 9952747, u'has_img': 1, u'fn3': u'220000', u'fn2': u'213000',
+ u'event_time': u'21:30:00', u'title': u'Paid Programming', u'recordedonmmddyyyy': u'07/02/2017', u'connectorid': u'SH000000010000',
+ u'recordedon': u'July 2, 2017', u'prg_img': u'h3/NowShowing/459763/p459763_b_h3_ae.jpg', u'filename_low': u'20170702_213000_220000_utc_SH000000010000_11534_11534_ABC_350.mp4',
+ u'episode_title': u'', u'synopsis': u'Paid programming.', u'dvrtimertype': 0, u'content_allowed': True, u'xcdrappname': u'livehd', u'event_date': u'2017-07-02',
+ u'event_inprogress': 2, u'prgsvcid': 11534, u'ut_start': 1499031000, u'stream_code': u'ABC'}'''
+ if dvrlink:
+ recorded = dvrlink['results']
+ else:
+ recorded = (json.loads(self.net.http_POST(BASEURL + 'gtv/1/live/viewdvrlist', form_data={'token':self.token}, headers=header_dict).content.encode("utf-8").rstrip()))['results']
+ self.cache.set(ADDON_NAME + '.recorded', recorded, expiration=datetime.timedelta(minutes=10))
+ self.recorded = self.cache.get(ADDON_NAME + '.recorded')
+ return True
+ except Exception,e:
+ log('login, Unable to login ' + str(e), xbmc.LOGERROR)
+ REAL_SETTINGS.setSetting('LAST_TOKEN','')
+ REAL_SETTINGS.setSetting('LAST_PASSKEY','')
+ REAL_SETTINGS.setSetting('User_Activated',str(False))
+ xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30007), ICON, 4000)
+ return False
+ else:
+ #firstrun wizard
+ if yesnoDialog(LANGUAGE(30008),no=LANGUAGE(30009), yes=LANGUAGE(30010)):
+ user = inputDialog(LANGUAGE(30001))
+ password = inputDialog(LANGUAGE(30002),opt=xbmcgui.ALPHANUM_HIDE_INPUT)
+ REAL_SETTINGS.setSetting('User_Email' ,user)
+ REAL_SETTINGS.setSetting('User_Password',password)
+ return self.login(user, password)
+ else:
+ okDialog(LANGUAGE(30003))
+ return False
+
+
+ def browseLive(self):
+ log('browseLive')
+ if self.channels is None:
+ if self.login(USER_EMAIL, PASSWORD) == False:
+ raise SystemExit
+
+ d = datetime.datetime.utcnow()
+ now = datetime.datetime.fromtimestamp(calendar.timegm(d.utctimetuple()))
+ isFree = REAL_SETTINGS.getSetting('User_isFree') == "True"
+ for channel in self.channels:
+ name = CHAN_NAMES[channel['stream_code']]
+ if isFree == True and name not in FREE_CHANS:
+ continue
+ startime = datetime.datetime.fromtimestamp(channel['ut_start'])
+ endtime = startime + datetime.timedelta(seconds=channel['runtime'])
+ if endtime > now and startime <= now:
+ label, url, liz = self.buildChannelListItem(name, channel)
+ self.addLink(label, url, 9, liz, len(self.channels))
+
+
+ def browseRecordings(self, recorded=False):
+ log('browseRecordings')
+ if self.recorded is None:
+ if self.login(USER_EMAIL, PASSWORD) == False:
+ raise SystemExit
+
+ d = datetime.datetime.utcnow()
+ now = datetime.datetime.fromtimestamp(calendar.timegm(d.utctimetuple()))
+ for channel in self.recorded:
+ startime = datetime.datetime.fromtimestamp(channel['ut_start'])
+ endtime = startime + datetime.timedelta(seconds=channel['runtime'])
+ if endtime > now and (startime <= now or startime > now) and recorded == True:
+ continue
+ elif endtime < now and (startime <= now or startime > now) and recorded == False:
+ continue
+ label, url, liz = self.buildRecordedListItem(CHAN_NAMES[channel['stream_code']])
+ self.addLink(label, url, 10, liz, len(self.recorded))
+
+
+ def browseGuide(self, name=None, upcoming=False):
+ log('browseGuide')
+ if self.channels is None:
+ if self.login(USER_EMAIL, PASSWORD) == False:
+ raise SystemExit
+
+ d = datetime.datetime.utcnow()
+ now = datetime.datetime.fromtimestamp(calendar.timegm(d.utctimetuple()))
+ isFree = REAL_SETTINGS.getSetting('User_isFree') == "True"
+ if name is None and upcoming == False:
+ collect = []
+ for channel in self.channels:
+ name = CHAN_NAMES[channel['stream_code']]
+ if isFree == True and name not in FREE_CHANS:
+ continue
+ collect.append(name)
+ counter = collections.Counter(collect)
+ for key, value in sorted(counter.iteritems()):
+ icon = (os.path.join(IMG_PATH,'logos','%s.png'%key) or ICON)
+ infoArt = {"thumb":icon,"poster":icon,"icon":icon,"fanart":FANART}
+ self.addDir("%s"%(key), key, 4, infoArt=infoArt)
+ else:
+ for channel in self.channels:
+ if isFree == True and name not in FREE_CHANS:
+ continue
+ if name == CHAN_NAMES[channel['stream_code']]:
+ startime = datetime.datetime.fromtimestamp(channel['ut_start'])
+ endtime = startime + datetime.timedelta(seconds=channel['runtime'])
+ if endtime > now and (startime <= now or startime > now):
+ label, url, liz = self.buildChannelListItem(name, channel)
+ self.addLink(label, url, 9, liz, len(self.channels))
+
+
+ def browseFeatured(self):
+ log('browseFeatured')
+ if self.upcoming is None:
+ if self.login(USER_EMAIL, PASSWORD) == False:
+ raise SystemExit
+
+ d = datetime.datetime.utcnow()
+ now = datetime.datetime.fromtimestamp(calendar.timegm(d.utctimetuple()))
+ isFree = REAL_SETTINGS.getSetting('User_isFree') == "True"
+ for channel in self.upcoming:
+ name = CHAN_NAMES[channel['sname']]
+ startime = datetime.datetime.fromtimestamp(channel['ut_start'])
+ endtime = startime + datetime.timedelta(seconds=channel['runtime'])
+ if endtime > now and (startime <= now or startime > now):
+ label, url, liz = self.buildChannelListItem(name, channel, feat=True)
+ self.addLink(label, url, 9, liz, len(self.upcoming))
+
+
+ def buildChannelListItem(self, name, channel=None, feat=False):
+ if channel is None:
+ for channel in self.channels:
+ if name == CHAN_NAMES[channel['stream_code']]:
+ break
+ startime = datetime.datetime.fromtimestamp(channel['ut_start'])
+ endtime = startime + datetime.timedelta(seconds=channel['runtime'])
+ title = unescape(channel['title'])
+ mediatype = (channel.get('mediatype','') or (channel.get('connectorid',''))[:2] or (channel.get('content_id',''))[:2] or 'SP')
+ mtype = MEDIA_TYPES[mediatype]
+ if PTVL_RUN == True:
+ label = name
+ mtype = 'video'
+ elif feat == True:
+ label = '%s %s-%s: %s - %s'%(startime.strftime('%m/%d/%Y'),startime.strftime('%I:%M').lstrip('0'),endtime.strftime('%I:%M %p').lstrip('0'),name,title)
+ else:
+ label = '%s: %s - %s'%(startime.strftime('%I:%M %p').lstrip('0'),name,title)
+ label2 = '%s - %s'%(startime.strftime('%I:%M %p').lstrip('0'),endtime.strftime('%I:%M %p').lstrip('0'))
+ scode = (channel.get('stream_code','') or channel.get('sname','') or '')
+ url = CHAN_NAMES[scode]
+ liz = xbmcgui.ListItem(label)
+ infoList = {"mediatype":mtype,"label":label,"label2":label2,"title":label,
+ "studio":CHAN_NAMES[scode],"duration":channel['runtime'],"plotoutline":unescape(channel['synopsis']),
+ "plot":unescape(channel['description']),"aired":(channel['orig_air_date'] or startime.strftime('%Y-%m-%d'))}
+
+ if mediatype.startswith('MV'):
+ poster = IMG_MOVIE+channel['prg_img']
+ elif mediatype.startswith(('SH','EP')):
+ poster = IMG_TV+channel['prg_img']
+ else:
+ poster = IMG_HTTP + str(channel['srsid']) + '&cs=' + channel['callsign'] + '&tid=' + mediatype
+ thumb = IMG_HTTP + str(channel['srsid']) + '&cs=' + channel['callsign'] + '&tid=' + mediatype
+ logo = (os.path.join(IMG_PATH,'%s.png'%CHAN_NAMES[scode]) or ICON)
+ liz.setInfo(type="Video", infoLabels=infoList)
+ liz.setArt({"thumb":thumb,"poster":poster,"icon":logo,"fanart":FANART})
+ liz.setProperty("IsPlayable","true")
+ liz.setProperty("IsInternetStream","true")
+ if channel['dvrtimeraction'] == 'add':
+ opt = '@'.join([str(channel['prgsvcid']),(channel.get('event_time','') or str(channel.get('ut_start','')))])#lazy solution rather then create additional url parameters for this single function.
+ contextMenu = [('Set single recording' ,'XBMC.RunPlugin(%s)'%(sys.argv[0]+"?url="+urllib.quote_plus(str(channel['connectorid']))+"&mode="+str(6)+"&name="+urllib.quote_plus(opt))),
+ ('Set recurring recording','XBMC.RunPlugin(%s)'%(sys.argv[0]+"?url="+urllib.quote_plus(str(channel['connectorid']))+"&mode="+str(7)+"&name="+urllib.quote_plus(opt)))]
+ else:
+ contextMenu = [('Remove recording','XBMC.RunPlugin(%s)'%(sys.argv[0]+"?url="+urllib.quote_plus(str(channel['scheduleid']))+"&mode="+str(8)+"&name="+urllib.quote_plus(name)))]
+ liz.addContextMenuItems(contextMenu)
+ log('buildChannelListItem, label = ' + label + ', url = ' + url)
+ return label, url, liz
+
+
+ def buildRecordedListItem(self, name):
+ for channel in self.recorded:
+ if name == CHAN_NAMES[channel['stream_code']] or name == str(channel['scheduleid']):
+ startime = datetime.datetime.fromtimestamp(channel['ut_start'])
+ endtime = startime + datetime.timedelta(seconds=channel['runtime'])
+ title = unescape(channel['title'])
+ label = '%s %s-%s: %s - %s'%(startime.strftime('%m/%d/%Y'),startime.strftime('%I:%M').lstrip('0'),endtime.strftime('%I:%M %p').lstrip('0'),name,title)
+ label2 = '%s %s-%s'%(startime.strftime('%m/%d/%Y'),startime.strftime('%I:%M').lstrip('0'),endtime.strftime('%I:%M %p').lstrip('0'))
+ url = str(channel['scheduleid'])
+ liz = xbmcgui.ListItem(label)
+ mediatype = (channel.get('mediatype','') or (channel.get('connectorid',''))[:2] or (channel.get('content_id',''))[:2] or 'SP')
+ mtype = MEDIA_TYPES[mediatype]
+ infoList = {"mediatype":mtype,"label":label,"label2":label2,"title":label,"studio":CHAN_NAMES[channel['stream_code']],
+ "duration":channel['runtime'],"plotoutline":unescape(channel['synopsis']),"plot":unescape(channel['description']),
+ "aired":(channel['orig_air_date'] or startime.strftime('%Y-%m-%d'))}
+ if mediatype.startswith('MV'):
+ poster = IMG_MOVIE+channel['prg_img']
+ elif mediatype.startswith(('SH','EP')):
+ poster = IMG_TV+channel['prg_img']
+ else:
+ poster = IMG_HTTP + str(channel['srsid']) + '&cs=' + channel['callsign'] + '&tid=' + mediatype
+ thumb = IMG_HTTP + str(channel['srsid']) + '&cs=' + channel['callsign'] + '&tid=' + mediatype
+ logo = (os.path.join(IMG_PATH,'%s.png'%CHAN_NAMES[channel['stream_code']]) or ICON)
+ liz.setInfo(type="Video", infoLabels=infoList)
+ liz.setArt({"thumb":thumb,"poster":poster,"icon":logo,"fanart":FANART})
+ liz.setProperty("IsPlayable","true")
+ liz.setProperty("IsInternetStream","true")
+ if channel['dvrtimeraction'] == 'add':
+ opt = '@'.join([str(channel['prgsvcid']),channel['event_time']])#lazy solution rather then create additional url parameters for this single function.
+ contextMenu = [('Set single recording' ,'XBMC.RunPlugin(%s)'%(sys.argv[0]+"?url="+urllib.quote_plus(str(channel['connectorid']))+"&mode="+str(6)+"&name="+urllib.quote_plus(opt))),
+ ('Set recurring recording','XBMC.RunPlugin(%s)'%(sys.argv[0]+"?url="+urllib.quote_plus(str(channel['connectorid']))+"&mode="+str(7)+"&name="+urllib.quote_plus(opt)))]
+ else:
+ contextMenu = [('Remove recording','XBMC.RunPlugin(%s)'%(sys.argv[0]+"?url="+urllib.quote_plus(str(channel['scheduleid']))+"&mode="+str(8)+"&name="+urllib.quote_plus(name)))]
+ liz.addContextMenuItems(contextMenu)
+ log('buildRecordedListItem, label = ' + label + ', url = ' + url)
+ return label, url, liz
+
+
+ def resolveURL(self, url, dvr=False):
+ log('resolveURL, url = ' + url + ', dvr = ' + str(dvr))
+ isFree = REAL_SETTINGS.getSetting('User_isFree') == "True"
+ if dvr:
+ for channel in self.recorded:
+ if url == str(channel['scheduleid']):
+ try:
+ urllink = json.loads(self.net.http_POST(BASEURL + 'stream/1/dvr/play', form_data={'token':self.token,'key':self.passkey,'scheduleid':channel['scheduleid']}, headers=self.buildHeader()).content.encode("utf-8").rstrip())
+ '''{u'pr': u'll', u'domain': u'ilvc02.ll.ustvnow.com',u'stream': u'http://ilvc02.ll.ustvnow.com/ilv10/pr/xxl/smil:0B64AWHTMUSTVNOW/playlist.m3u8?',
+ u'streamname': u'0B64AWHTMUSTVNOW', u'tr': u'', u'up': 1, u'pd': 0, u'pl': u'vjs'}'''
+ if urllink and 'stream' in urllink:
+ return urllink['stream']
+ except Exception,e:
+ log('resolveURL, Unable to login ' + str(e), xbmc.LOGERROR)
+ xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30005), ICON, 4000)
+ raise SystemExit
+ else:
+ for channel in self.channels:
+ if url == CHAN_NAMES[channel['stream_code']]:
+ try:
+ urllink = json.loads(self.net.http_POST(BASEURL + 'stream/1/live/view', form_data={'token':self.token,'key':self.passkey,'scode':channel['scode']}, headers=self.buildHeader()).content.encode("utf-8").rstrip())
+ '''{u'pr': u'll', u'domain': u'ilvc02.ll.ustvnow.com',u'stream': u'http://ilvc02.ll.ustvnow.com/ilv10/pr/xxl/smil:0B64AWHTMUSTVNOW/playlist.m3u8?',
+ u'streamname': u'0B64AWHTMUSTVNOW', u'tr': u'', u'up': 1, u'pd': 0, u'pl': u'vjs'}'''
+ if urllink and 'stream' in urllink:
+ return urllink['stream']
+ except Exception,e:
+ log('resolveURL, Unable to login ' + str(e), xbmc.LOGERROR)
+ xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30005), ICON, 4000)
+ raise SystemExit
+
+
+ def setRecording(self, name, url, remove=False, recurring=False):
+ if remove == True:
+ setlink = (self.net.http_POST(BASEURL + 'gtv/1/dvr/updatedvr', form_data={'scheduleid':url,'token':self.token,'action':'add'}, headers=self.buildHeader()).content.encode("utf-8").rstrip())
+ else:
+ if int(REAL_SETTINGS.getSetting('User_DVRpoints')) <= 1:
+ xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30019), ICON, 4000)
+ return
+ opt = name.split('@')#lazy solution rather then create additional url parameters for this single function.
+ if recurring == True:
+ setlink = (self.net.http_POST(BASEURL + 'gtv/1/dvr/updatedvrtimer', form_data={'connectorid':url,'prgsvcid':opt[0],'eventtime':opt[1],'token':self.token,'action':'add'}, headers=self.buildHeader()).content.encode("utf-8").rstrip())
+ else:
+ setlink = (self.net.http_POST(BASEURL + 'gtv/1/dvr/updatedvr' , form_data={'scheduleid':url,'token':self.token,'action':'add'}, headers=self.buildHeader()).content.encode("utf-8").rstrip())
+ '''<result><status>failure</status><action>add</action></result>'''
+ action = re.findall(r'<action>(.*?)</action>', setlink, re.DOTALL)[0]
+ status = re.findall(r'<status>(.*?)</status>', setlink, re.DOTALL)[0]
+ log('setRecording, action = ' + action + ', status = ' + status)
+ if status == 'failure':
+ xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30023)%action.title(), ICON, 4000)
+ return
+ xbmcgui.Dialog().notification(ADDON_NAME, LANGUAGE(30024)%action.title(), ICON, 4000)
+
+
+ def playVideo(self, url, dvr=False):
+ log('playVideo, url = ' + url + ', dvr = ' + str(dvr))
+ if dvr:
+ label, path, liz = self.buildRecordedListItem(url)
+ else:
+ label, path, liz = self.buildChannelListItem(url)
+ liz.setPath(self.resolveURL(url,dvr))
+ xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, liz)
+
+
+ def addLink(self, name, u, mode, liz, total=0):
+ log('addLink, name = ' + name)
+ u=sys.argv[0]+"?url="+urllib.quote_plus(u)+"&mode="+str(mode)+"&name="+urllib.quote_plus(name)
+ xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=u,listitem=liz,totalItems=total)
+
+
+ def addDir(self, name, u, mode, infoList=False, infoArt=False):
+ log('addDir, name = ' + name)
+ liz=xbmcgui.ListItem(name)
+ liz.setProperty('IsPlayable', 'false')
+ if infoList == False:
+ liz.setInfo(type="Video", infoLabels={"mediatype":"video","label":name,"title":name} )
+ else:
+ liz.setInfo(type="Video", infoLabels=infoList)
+ if infoArt == False:
+ liz.setArt({'thumb':ICON,'fanart':FANART})
+ else:
+ liz.setArt(infoArt)
+ u=sys.argv[0]+"?url="+urllib.quote_plus(u)+"&mode="+str(mode)+"&name="+urllib.quote_plus(name)
+ xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=u,listitem=liz,isFolder=True)
+
+
+params=getParams()
+try:
+ url=urllib.unquote_plus(params["url"])
+except:
+ url=None
+try:
+ name=urllib.unquote_plus(params["name"])
+except:
+ name=None
+try:
+ mode=int(params["mode"])
+except:
+ mode=None
+
+log("Mode: "+str(mode))
+log("URL : "+str(url))
+log("Name: "+str(name))
+
+if mode==None: USTVnow().mainMenu()
+elif mode == 0: USTVnow().browseLive()
+elif mode == 1: USTVnow().browseRecordings()
+elif mode == 2: USTVnow().browseRecordings(recorded=True)
+elif mode == 3: USTVnow().browseGuide()
+elif mode == 4: USTVnow().browseGuide(name)
+elif mode == 5: USTVnow().browseFeatured()
+elif mode == 6: USTVnow().setRecording(name,url)
+elif mode == 7: USTVnow().setRecording(name,url,recurring=True)
+elif mode == 8: USTVnow().setRecording(name,url,remove=True)
+elif mode == 9: USTVnow().playVideo(url)
+elif mode == 10:USTVnow().playVideo(url,dvr=True)
+
+xbmcplugin.addSortMethod(int(sys.argv[1]) , xbmcplugin.SORT_METHOD_NONE )
+xbmcplugin.addSortMethod(int(sys.argv[1]) , xbmcplugin.SORT_METHOD_LABEL )
+xbmcplugin.endOfDirectory(int(sys.argv[1]), cacheToDisc=True) \ No newline at end of file
diff --git a/plugin.video.ustvnow/net.py b/plugin.video.ustvnow/net.py
new file mode 100644
index 0000000..769881c
--- /dev/null
+++ b/plugin.video.ustvnow/net.py
@@ -0,0 +1,328 @@
+'''
+ common XBMC Module
+ Copyright (C) 2011 t0mm0
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+'''
+
+import cookielib
+import gzip
+import re
+import StringIO
+import urllib
+import urllib2
+import socket
+
+#Set Global timeout - Useful for slow connections and Putlocker.
+socket.setdefaulttimeout(30)
+
+class HeadRequest(urllib2.Request):
+ '''A Request class that sends HEAD requests'''
+ def get_method(self):
+ return 'HEAD'
+
+class Net:
+ '''
+ This class wraps :mod:`urllib2` and provides an easy way to make http
+ requests while taking care of cookies, proxies, gzip compression and
+ character encoding.
+
+ Example::
+
+ from t0mm0.common.net import Net
+ net = Net()
+ response = net.http_GET('http://xbmc.org')
+ print response.content
+ '''
+
+ _cj = cookielib.LWPCookieJar()
+ _proxy = None
+ _user_agent = 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.1 ' + \
+ '(KHTML, like Gecko) Chrome/13.0.782.99 Safari/535.1'
+ _http_debug = False
+
+
+ def __init__(self, cookie_file='', proxy='', user_agent='',
+ http_debug=False):
+ '''
+ Kwargs:
+ cookie_file (str): Full path to a file to be used to load and save
+ cookies to.
+
+ proxy (str): Proxy setting (eg.
+ ``'http://user:pass@example.com:1234'``)
+
+ user_agent (str): String to use as the User Agent header. If not
+ supplied the class will use a default user agent (chrome)
+
+ http_debug (bool): Set ``True`` to have HTTP header info written to
+ the XBMC log for all requests.
+ '''
+ if cookie_file:
+ self.set_cookies(cookie_file)
+ if proxy:
+ self.set_proxy(proxy)
+ if user_agent:
+ self.set_user_agent(user_agent)
+ self._http_debug = http_debug
+ self._update_opener()
+
+
+ def set_cookies(self, cookie_file):
+ '''
+ Set the cookie file and try to load cookies from it if it exists.
+
+ Args:
+ cookie_file (str): Full path to a file to be used to load and save
+ cookies to.
+ '''
+ try:
+ self._cj.load(cookie_file, ignore_discard=True)
+ self._update_opener()
+ return True
+ except:
+ return False
+
+
+ def get_cookies(self):
+ '''Returns A dictionary containing all cookie information by domain.'''
+ return self._cj._cookies
+
+
+ def save_cookies(self, cookie_file):
+ '''
+ Saves cookies to a file.
+
+ Args:
+ cookie_file (str): Full path to a file to save cookies to.
+ '''
+ self._cj.save(cookie_file, ignore_discard=True)
+
+
+ def set_proxy(self, proxy):
+ '''
+ Args:
+ proxy (str): Proxy setting (eg.
+ ``'http://user:pass@example.com:1234'``)
+ '''
+ self._proxy = proxy
+ self._update_opener()
+
+
+ def get_proxy(self):
+ '''Returns string containing proxy details.'''
+ return self._proxy
+
+
+ def set_user_agent(self, user_agent):
+ '''
+ Args:
+ user_agent (str): String to use as the User Agent header.
+ '''
+ self._user_agent = user_agent
+
+
+ def get_user_agent(self):
+ '''Returns user agent string.'''
+ return self._user_agent
+
+
+ def _update_opener(self):
+ '''
+ Builds and installs a new opener to be used by all future calls to
+ :func:`urllib2.urlopen`.
+ '''
+ if self._http_debug:
+ http = urllib2.HTTPHandler(debuglevel=1)
+ else:
+ http = urllib2.HTTPHandler()
+
+ if self._proxy:
+ opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self._cj),
+ urllib2.ProxyHandler({'http':
+ self._proxy}),
+ urllib2.HTTPBasicAuthHandler(),
+ http)
+
+ else:
+ opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self._cj),
+ urllib2.HTTPBasicAuthHandler(),
+ http)
+ urllib2.install_opener(opener)
+
+
+ def http_GET(self, url, headers={}, compression=True):
+ '''
+ Perform an HTTP GET request.
+
+ Args:
+ url (str): The URL to GET.
+
+ Kwargs:
+ headers (dict): A dictionary describing any headers you would like
+ to add to the request. (eg. ``{'X-Test': 'testing'}``)
+
+ compression (bool): If ``True`` (default), try to use gzip
+ compression.
+
+ Returns:
+ An :class:`HttpResponse` object containing headers and other
+ meta-information about the page and the page content.
+ '''
+ return self._fetch(url, headers=headers, compression=compression)
+
+
+ def http_POST(self, url, form_data, headers={}, compression=True):
+ '''
+ Perform an HTTP POST request.
+
+ Args:
+ url (str): The URL to POST.
+
+ form_data (dict): A dictionary of form data to POST.
+
+ Kwargs:
+ headers (dict): A dictionary describing any headers you would like
+ to add to the request. (eg. ``{'X-Test': 'testing'}``)
+
+ compression (bool): If ``True`` (default), try to use gzip
+ compression.
+
+ Returns:
+ An :class:`HttpResponse` object containing headers and other
+ meta-information about the page and the page content.
+ '''
+ return self._fetch(url, form_data, headers=headers,
+ compression=compression)
+
+
+ def http_HEAD(self, url, headers={}):
+ '''
+ Perform an HTTP HEAD request.
+
+ Args:
+ url (str): The URL to GET.
+
+ Kwargs:
+ headers (dict): A dictionary describing any headers you would like
+ to add to the request. (eg. ``{'X-Test': 'testing'}``)
+
+ Returns:
+ An :class:`HttpResponse` object containing headers and other
+ meta-information about the page.
+ '''
+ req = HeadRequest(url)
+ req.add_header('User-Agent', self._user_agent)
+ for k, v in headers.items():
+ req.add_header(k, v)
+ response = urllib2.urlopen(req)
+ return HttpResponse(response)
+
+
+ def _fetch(self, url, form_data={}, headers={}, compression=True):
+ '''
+ Perform an HTTP GET or POST request.
+
+ Args:
+ url (str): The URL to GET or POST.
+
+ form_data (dict): A dictionary of form data to POST. If empty, the
+ request will be a GET, if it contains form data it will be a POST.
+
+ Kwargs:
+ headers (dict): A dictionary describing any headers you would like
+ to add to the request. (eg. ``{'X-Test': 'testing'}``)
+
+ compression (bool): If ``True`` (default), try to use gzip
+ compression.
+
+ Returns:
+ An :class:`HttpResponse` object containing headers and other
+ meta-information about the page and the page content.
+ '''
+ encoding = ''
+ req = urllib2.Request(url)
+ if form_data:
+ form_data = urllib.urlencode(form_data)
+ req = urllib2.Request(url, form_data)
+ req.add_header('User-Agent', self._user_agent)
+ for k, v in headers.items():
+ req.add_header(k, v)
+ if compression:
+ req.add_header('Accept-Encoding', 'gzip')
+ response = urllib2.urlopen(req)
+ return HttpResponse(response)
+
+
+
+class HttpResponse:
+ '''
+ This class represents a resoponse from an HTTP request.
+
+ The content is examined and every attempt is made to properly encode it to
+ Unicode.
+
+ .. seealso::
+ :meth:`Net.http_GET`, :meth:`Net.http_HEAD` and :meth:`Net.http_POST`
+ '''
+
+ content = ''
+ '''Unicode encoded string containing the body of the reposne.'''
+
+
+ def __init__(self, response):
+ '''
+ Args:
+ response (:class:`mimetools.Message`): The object returned by a call
+ to :func:`urllib2.urlopen`.
+ '''
+ self._response = response
+ html = response.read()
+ try:
+ if response.headers['content-encoding'].lower() == 'gzip':
+ html = gzip.GzipFile(fileobj=StringIO.StringIO(html)).read()
+ except:
+ pass
+
+ try:
+ content_type = response.headers['content-type']
+ if 'charset=' in content_type:
+ encoding = content_type.split('charset=')[-1]
+ except:
+ pass
+
+ r = re.search('<meta\s+http-equiv="Content-Type"\s+content="(?:.+?);' +
+ '\s+charset=(.+?)"', html, re.IGNORECASE)
+ if r:
+ encoding = r.group(1)
+
+ try:
+ html = unicode(html, encoding)
+ except:
+ pass
+
+ self.content = html
+
+
+ def get_headers(self):
+ '''Returns a List of headers returned by the server.'''
+ return self._response.info().headers
+
+
+ def get_url(self):
+ '''
+ Return the URL of the resource retrieved, commonly used to determine if
+ a redirect was followed.
+ '''
+ return self._response.geturl()
diff --git a/plugin.video.ustvnow/resources/images/fanart.jpg b/plugin.video.ustvnow/resources/images/fanart.jpg
new file mode 100644
index 0000000..ba7e104
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/fanart.jpg
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/icon.png b/plugin.video.ustvnow/resources/images/icon.png
new file mode 100644
index 0000000..65bc43e
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/icon.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/ABC.png b/plugin.video.ustvnow/resources/images/logos/ABC.png
new file mode 100644
index 0000000..0d55248
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/ABC.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/AE.png b/plugin.video.ustvnow/resources/images/logos/AE.png
new file mode 100644
index 0000000..3ba79b8
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/AE.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/AMC.png b/plugin.video.ustvnow/resources/images/logos/AMC.png
new file mode 100644
index 0000000..356645f
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/AMC.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/Animal Planet.png b/plugin.video.ustvnow/resources/images/logos/Animal Planet.png
new file mode 100644
index 0000000..8340877
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/Animal Planet.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/BBC America.png b/plugin.video.ustvnow/resources/images/logos/BBC America.png
new file mode 100644
index 0000000..2009a0b
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/BBC America.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/Bravo.png b/plugin.video.ustvnow/resources/images/logos/Bravo.png
new file mode 100644
index 0000000..af1082a
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/Bravo.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/CBS.png b/plugin.video.ustvnow/resources/images/logos/CBS.png
new file mode 100644
index 0000000..b641457
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/CBS.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/CNBC.png b/plugin.video.ustvnow/resources/images/logos/CNBC.png
new file mode 100644
index 0000000..2611e1d
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/CNBC.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/CNN.png b/plugin.video.ustvnow/resources/images/logos/CNN.png
new file mode 100644
index 0000000..eb2ba56
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/CNN.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/CW.png b/plugin.video.ustvnow/resources/images/logos/CW.png
new file mode 100644
index 0000000..2d6a358
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/CW.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/Cartoon Network.png b/plugin.video.ustvnow/resources/images/logos/Cartoon Network.png
new file mode 100644
index 0000000..51896f6
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/Cartoon Network.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/Comedy Central.png b/plugin.video.ustvnow/resources/images/logos/Comedy Central.png
new file mode 100644
index 0000000..5063094
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/Comedy Central.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/Discovery Channel.png b/plugin.video.ustvnow/resources/images/logos/Discovery Channel.png
new file mode 100644
index 0000000..ad56503
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/Discovery Channel.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/ESPN 2.png b/plugin.video.ustvnow/resources/images/logos/ESPN 2.png
new file mode 100644
index 0000000..819eb3a
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/ESPN 2.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/ESPN.png b/plugin.video.ustvnow/resources/images/logos/ESPN.png
new file mode 100644
index 0000000..6185006
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/ESPN.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/FOX.png b/plugin.video.ustvnow/resources/images/logos/FOX.png
new file mode 100644
index 0000000..88883c8
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/FOX.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/FX.png b/plugin.video.ustvnow/resources/images/logos/FX.png
new file mode 100644
index 0000000..ab18f1f
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/FX.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/Fox News.png b/plugin.video.ustvnow/resources/images/logos/Fox News.png
new file mode 100644
index 0000000..432db29
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/Fox News.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/Freeform.png b/plugin.video.ustvnow/resources/images/logos/Freeform.png
new file mode 100644
index 0000000..7ce5fe9
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/Freeform.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/History.png b/plugin.video.ustvnow/resources/images/logos/History.png
new file mode 100644
index 0000000..29299fc
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/History.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/Lifetime.png b/plugin.video.ustvnow/resources/images/logos/Lifetime.png
new file mode 100644
index 0000000..482d4eb
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/Lifetime.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/MSNBC.png b/plugin.video.ustvnow/resources/images/logos/MSNBC.png
new file mode 100644
index 0000000..2daff84
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/MSNBC.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/MY9.png b/plugin.video.ustvnow/resources/images/logos/MY9.png
new file mode 100644
index 0000000..e0dfea4
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/MY9.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/NBC.png b/plugin.video.ustvnow/resources/images/logos/NBC.png
new file mode 100644
index 0000000..8dc2969
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/NBC.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/NBCSN.png b/plugin.video.ustvnow/resources/images/logos/NBCSN.png
new file mode 100644
index 0000000..c40b4ea
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/NBCSN.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/National Geographic.png b/plugin.video.ustvnow/resources/images/logos/National Geographic.png
new file mode 100644
index 0000000..7472a79
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/National Geographic.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/Nickelodeon.png b/plugin.video.ustvnow/resources/images/logos/Nickelodeon.png
new file mode 100644
index 0000000..31a989e
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/Nickelodeon.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/PBS.png b/plugin.video.ustvnow/resources/images/logos/PBS.png
new file mode 100644
index 0000000..d7ca040
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/PBS.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/SPIKE TV.png b/plugin.video.ustvnow/resources/images/logos/SPIKE TV.png
new file mode 100644
index 0000000..b422602
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/SPIKE TV.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/SundanceTV.png b/plugin.video.ustvnow/resources/images/logos/SundanceTV.png
new file mode 100644
index 0000000..871d8a5
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/SundanceTV.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/Syfy.png b/plugin.video.ustvnow/resources/images/logos/Syfy.png
new file mode 100644
index 0000000..615fe82
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/Syfy.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/TBS.png b/plugin.video.ustvnow/resources/images/logos/TBS.png
new file mode 100644
index 0000000..6c5c0ff
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/TBS.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/TLC.png b/plugin.video.ustvnow/resources/images/logos/TLC.png
new file mode 100644
index 0000000..f468f8d
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/TLC.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/TNT.png b/plugin.video.ustvnow/resources/images/logos/TNT.png
new file mode 100644
index 0000000..f5d69ce
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/TNT.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/USA Network.png b/plugin.video.ustvnow/resources/images/logos/USA Network.png
new file mode 100644
index 0000000..942d808
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/USA Network.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/logos/Universal.png b/plugin.video.ustvnow/resources/images/logos/Universal.png
new file mode 100644
index 0000000..1951ae9
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/logos/Universal.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/screenshot01.png b/plugin.video.ustvnow/resources/images/screenshot01.png
new file mode 100644
index 0000000..9df9333
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/screenshot01.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/screenshot02.png b/plugin.video.ustvnow/resources/images/screenshot02.png
new file mode 100644
index 0000000..a795689
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/screenshot02.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/screenshot03.png b/plugin.video.ustvnow/resources/images/screenshot03.png
new file mode 100644
index 0000000..c8291b1
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/screenshot03.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/screenshot04.png b/plugin.video.ustvnow/resources/images/screenshot04.png
new file mode 100644
index 0000000..74d629c
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/screenshot04.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/images/screenshot05.png b/plugin.video.ustvnow/resources/images/screenshot05.png
new file mode 100644
index 0000000..e27c2b5
--- /dev/null
+++ b/plugin.video.ustvnow/resources/images/screenshot05.png
Binary files differ
diff --git a/plugin.video.ustvnow/resources/language/resource.language.en_gb/strings.po b/plugin.video.ustvnow/resources/language/resource.language.en_gb/strings.po
new file mode 100644
index 0000000..12d2ca5
--- /dev/null
+++ b/plugin.video.ustvnow/resources/language/resource.language.en_gb/strings.po
@@ -0,0 +1,117 @@
+# Kodi Media Center language file
+# Addon Name: USTVnow
+# Addon id: plugin.video.ustvnow
+# Addon Provider: Lunatixz
+msgid ""
+msgstr ""
+"Project-Id-Version: plugin.video.ustvnow\n"
+"Report-Msgid-Bugs-To: http://forum.kodi.tv/showthread.php?tid=315513\n"
+"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Lunatixz Translation Team\n"
+"Language-Team: English (http://www.transifex.com/projects/p/xbmc-addons/language/en/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: en\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+msgctxt "#30000"
+msgid "Enable Debugging [Log errors]"
+msgstr ""
+
+msgctxt "#30001"
+msgid "Enter User Email"
+msgstr ""
+
+msgctxt "#30002"
+msgid "Enter User Password"
+msgstr ""
+
+msgctxt "#30003"
+msgid "Please visit www.ustvnow.com to activate your account."
+msgstr ""
+
+msgctxt "#30004"
+msgid "Select Quality"
+msgstr ""
+
+msgctxt "#30005"
+msgid "Account Disabled, visit www.ustvnow.com for support."
+msgstr ""
+
+msgctxt "#30006"
+msgid "Welcome Back "
+msgstr ""
+
+msgctxt "#30007"
+msgid "Invalid User Credentials"
+msgstr ""
+
+msgctxt "#30008"
+msgid "Already have a USTVnow account?"
+msgstr ""
+
+msgctxt "#30009"
+msgid "Exit"
+msgstr ""
+
+msgctxt "#30010"
+msgid "Sign-In"
+msgstr ""
+
+msgctxt "#30011"
+msgid "Account"
+msgstr ""
+
+msgctxt "#30012"
+msgid "General"
+msgstr ""
+
+msgctxt "#30013"
+msgid "Email Activated:"
+msgstr ""
+
+msgctxt "#30014"
+msgid "Plan Type:"
+msgstr ""
+
+msgctxt "#30015"
+msgid "DVR Credit:"
+msgstr ""
+
+msgctxt "#30016"
+msgid "Your account has expired, visit www.ustvnow.com"
+msgstr ""
+
+msgctxt "#30017"
+msgid "Account Information:"
+msgstr ""
+
+msgctxt "#30018"
+msgid "Subscription expires - "
+msgstr ""
+
+msgctxt "#30019"
+msgid "Insufficient DVR credits, visit www.ustvnow.com"
+msgstr ""
+
+msgctxt "#30020"
+msgid "Subscription plan - "
+msgstr ""
+
+msgctxt "#30021"
+msgid "DVR credits updated"
+msgstr ""
+
+msgctxt "#30022"
+msgid "Email requires Activation, visit www.ustvnow.com"
+msgstr ""
+
+msgctxt "#30023"
+msgid "%s Recording Failed!"
+msgstr ""
+
+msgctxt "#30024"
+msgid "%s Recording Successful"
+msgstr "" \ No newline at end of file
diff --git a/plugin.video.ustvnow/resources/settings.xml b/plugin.video.ustvnow/resources/settings.xml
new file mode 100644
index 0000000..66f133f
--- /dev/null
+++ b/plugin.video.ustvnow/resources/settings.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<settings>
+ <setting id="Enable_Debugging" type="bool" label="30000" default="false" />
+ <setting id="User_Email" type="text" label="30001" default="" />
+ <setting id="User_Password" type="text" label="30002" default="" option="hidden"/>
+ <setting type="lsep" label="30017"/>
+ <setting id="User_Plan" type="text" label="30020" default="" enable="false"/>
+ <setting id="User_Expires" type="text" label="30018" default="" enable="false"/>
+ <setting id="User_Token" type="text" label="30002" default="" visible="false"/>
+ <setting id="User_Paskey" type="text" label="30002" default="" visible="false"/>
+</settings>