diff --git a/account.py b/account.py new file mode 100644 index 0000000..9bc49c2 --- /dev/null +++ b/account.py @@ -0,0 +1,21 @@ +class Account: + id = '' + username = '' + password = '' + provider = '' + cookies_path = '' + pin_protected = True + + def __init__(self, id, username, password, provider, cookies_path, pin_protected=True): + self.id = id + self.username = username + self.password = password + self.provider = provider + self.cookies_path = cookies_path + self.pin_protected = pin_protected + + def is_valid(self): + if self.username and self.password and self.provider: + return True + else: + return False diff --git a/exports.py b/exports.py index f71b32b..f50df25 100644 --- a/exports.py +++ b/exports.py @@ -34,7 +34,7 @@ def create_m3u(channels, path, url): c['stationid'], url + logo_sl_location(c['title']) if url is not None else logo_id(c['title']), c['title'])) - file.write(u'plugin://plugin.video.sl/?action=play&id=%s&askpin=%s\n' % (c['id'], c['pin'])) + file.write(u'plugin://plugin.video.sl/?action=play&account=%s&channel=%s&askpin=%s\n' % (c['account'], c['id'], c['pin'])) def create_epg(channels, epg, path, addon=None, url='https://livetv.skylink.sk/'): diff --git a/main.py b/main.py index d6fee83..5a0e341 100644 --- a/main.py +++ b/main.py @@ -3,6 +3,7 @@ import logger import requests import skylink +import account import xbmc import xbmcaddon import xbmcgui @@ -13,15 +14,14 @@ _id = int(sys.argv[1]) _addon = xbmcaddon.Addon() -_profile = xbmc.translatePath(_addon.getAddonInfo('profile')).decode("utf-8") -_user_name = xbmcplugin.getSetting(_id, 'username') -_password = xbmcplugin.getSetting(_id, 'password') -_provider = 'skylink.sk' if int(xbmcplugin.getSetting(_id, 'provider')) == 0 else 'skylink.cz' -_pin_protected_content = 'false' != xbmcplugin.getSetting(_id, 'pin_protected_content') -def play(channel_id, askpin): - logger.log.info('play: ' + channel_id) - sl = skylink.Skylink(_user_name, _password, _profile, _provider) +def play(account_id, channel_id, askpin): + sl = utils.get_provider(account_id) + + if not sl: + return + + logger.log.info('Play channel %s from %s' % (channel_id, sl.account.provider)) if askpin != 'False': pin_ok = utils.ask_for_pin(sl) @@ -44,13 +44,12 @@ def play(channel_id, askpin): playitem.setProperty('inputstream.adaptive.license_key', info['key']) xbmcplugin.setResolvedUrl(_id, True, playitem) - if __name__ == '__main__': args = urlparse.parse_qs(sys.argv[2][1:]) - if 'id' in args: - play(str(args['id'][0]), str(args['askpin'][0]) if 'askpin' in args else 'False') + if 'account' in args and 'channel' in args: + play(str(args['account'][0]), str(args['channel'][0]), str(args['askpin'][0]) if 'askpin' in args else 'False') elif 'replay' in args: - replay.router(args, skylink.Skylink(_user_name, _password, _profile, _provider, _pin_protected_content)) + replay.router(args) else: xbmcplugin.setPluginCategory(_id, '') xbmcplugin.setContent(_id, 'videos') diff --git a/replay.py b/replay.py index e8728e3..e799103 100644 --- a/replay.py +++ b/replay.py @@ -45,20 +45,34 @@ def get_logo(title, sl): return os.path.join(_logos_folder, exports.logo_id(title)) -def channels(sl): - channels = utils.call(sl, lambda: sl.channels(True)) + +def channels(): + providers = utils.get_available_providers() + channels = [] + + for sl in providers: + channels = channels + utils.call(sl, lambda: sl.channels(True)) + + if bool(_addon.getSetting('playlist_unique')): + channels = utils.unique_channels(channels) + xbmcplugin.setPluginCategory(_handle, _addon.getLocalizedString(30600)) if channels: for channel in channels: list_item = xbmcgui.ListItem(label=channel['title']) list_item.setInfo('video', {'title': channel['title']}) #TODO - genre? list_item.setArt({'thumb': get_logo(channel['title'], sl)}) - link = get_url(replay='days', stationid=channel['stationid'], channel=channel['title'], askpin=channel['pin']) + link = get_url(replay='days', accountid=channel['account'], stationid=channel['stationid'], channel=channel['title'], askpin=channel['pin']) is_folder = True xbmcplugin.addDirectoryItem(_handle, link, list_item, is_folder) xbmcplugin.endOfDirectory(_handle) -def days(sl, stationid, channel, askpin): +def days(accountid, stationid, channel, askpin): + sl = utils.get_provider(accountid) + + if not sl: + return + now = datetime.datetime.now() xbmcplugin.setPluginCategory(_handle, _addon.getLocalizedString(30600) + ' / ' + channel) if askpin != 'False': @@ -72,12 +86,17 @@ def days(sl, stationid, channel, askpin): title = _addon.getLocalizedString(int('3061' + str(d.weekday()))) + ', ' + title list_item = xbmcgui.ListItem(label=title) list_item.setArt({'icon':'DefaultAddonPVRClient.png'}) - link = get_url(replay='programs', stationid=stationid, channel=channel, day=day, first=True) + link = get_url(replay='programs', accountid=accountid, stationid=stationid, channel=channel, day=day, first=True) is_folder = True xbmcplugin.addDirectoryItem(_handle, link, list_item, is_folder) xbmcplugin.endOfDirectory(_handle) -def programs(sl, stationid, channel, day=0, first=False): +def programs(accountid, stationid, channel, day=0, first=False): + sl = utils.get_provider(accountid) + + if not sl: + return + today = day == 0 if today: now = datetime.datetime.now() @@ -88,7 +107,7 @@ def programs(sl, stationid, channel, day=0, first=False): if day < 6: list_item = xbmcgui.ListItem(label=_addon.getLocalizedString(30604)) list_item.setArt({'icon':'DefaultVideoPlaylists.png'}) - link = get_url(replay='programs', stationid=stationid, channel=channel, day=day+1) + link = get_url(replay='programs', accountid=accountid, stationid=stationid, channel=channel, day=day+1) is_folder = True xbmcplugin.addDirectoryItem(_handle, link, list_item, is_folder) if epg: @@ -108,20 +127,25 @@ def programs(sl, stationid, channel, day=0, first=False): cover = sl.getUrl() + "/" + program['cover'] list_item.setArt({'thumb': cover, 'icon': cover}) - link = get_url(replay='replay', locId=program['locId']) + link = get_url(replay='replay', accountid=accountid, locId=program['locId']) is_folder = False list_item.setProperty('IsPlayable','true') xbmcplugin.addDirectoryItem(_handle, link, list_item, is_folder) if day > 0: list_item = xbmcgui.ListItem(label=_addon.getLocalizedString(30603)) list_item.setArt({'icon':'DefaultVideoPlaylists.png'}) - link = get_url(replay='programs', stationid=stationid, channel=channel, day=day-1) + link = get_url(replay='programs', accountid=accountid, stationid=stationid, channel=channel, day=day-1) is_folder = True xbmcplugin.addDirectoryItem(_handle, link, list_item, is_folder) xbmcplugin.endOfDirectory(_handle, updateListing=not first) -def replay(sl, locId): +def replay(accountid, locId): + sl = utils.get_provider(accountid) + + if not sl: + return + info = utils.call(sl, lambda: sl.replay_info(locId)) if info: @@ -135,15 +159,15 @@ def replay(sl, locId): xbmcplugin.setResolvedUrl(_handle, True, playitem) -def router(args, sl): +def router(args): if args: if args['replay'][0] == 'programs': - programs(sl, args['stationid'][0], args['channel'][0], int(args['day'][0]) if 'day' in args else 0, 'first' in args) + programs(args['accountid'][0], args['stationid'][0], args['channel'][0], int(args['day'][0]) if 'day' in args else 0, 'first' in args) elif args['replay'][0] == 'replay': - replay(sl, args['locId'][0]) + replay(args['accountid'][0], args['locId'][0]) elif args['replay'][0] == 'days': - days(sl, args['stationid'][0], args['channel'][0], args['askpin'][0]) + days(args['accountid'][0], args['stationid'][0], args['channel'][0], args['askpin'][0]) else: - channels(sl) + channels() else: - channels(sl) + channels() diff --git a/resources/language/Czech/strings.po b/resources/language/Czech/strings.po index 8ef58fc..239f7f0 100644 --- a/resources/language/Czech/strings.po +++ b/resources/language/Czech/strings.po @@ -8,37 +8,33 @@ msgid "Account" msgstr "Účet" msgctxt "#30101" -msgid "Provider" -msgstr "Poskytovatel" +msgid "Skylink SK" +msgstr "Skylink SK" msgctxt "#30102" -msgid "Email / Card number" -msgstr "Číslo karty/Emailová adresa" +msgid "Skylink CZ" +msgstr "Skylink CZ" msgctxt "#30103" +msgid "Email / Card number" +msgstr "Email / číslo karty" + +msgctxt "#30104" msgid "Password" msgstr "Heslo" -msgctxt "#30111" -msgid "Skylink SK" -msgstr "Skylink SK" - -msgctxt "#30112" -msgid "Skylink CZ" -msgstr "Skylink CZ" +msgctxt "#30105" +msgid "Generate and show pin protected channels" +msgstr "Generovat a zobrazovat obsah chráněný PIN-em" -msgctxt "#30104" +msgctxt "#30106" msgid "When no device is available use oldest" msgstr "Pokud není k dispozici žádné zařízení, použij nejstarší -msgctxt "#30105" +msgctxt "#30107" msgid "Limit devices list to web browser" msgstr "Omezit zařízení v seznamu pouze na prohlížeče" -msgctxt "#30106" -msgid "Generate and show pin protected channels" -msgstr "Generovat a zobrazovat obsah chráněný PIN-em" - msgctxt "#30200" msgid "Playlist" msgstr "Playlist" @@ -56,6 +52,10 @@ msgid "Filename" msgstr "Název souboru" msgctxt "#30204" +msgid "Eliminate duplicate channels" +msgstr "Odstranit duplicitní stanice" + +msgctxt "#30205" msgid "Logo urls directly from Skylink Live TV site" msgstr "Url adresy pro loga stanic přímo ze stránky Skylink Live TV" diff --git a/resources/language/English/strings.po b/resources/language/English/strings.po index db25d44..9d133a5 100644 --- a/resources/language/English/strings.po +++ b/resources/language/English/strings.po @@ -8,37 +8,33 @@ msgid "Account" msgstr "Account" msgctxt "#30101" -msgid "Provider" -msgstr "Provider" +msgid "Skylink SK" +msgstr "Skylink SK" msgctxt "#30102" +msgid "Skylink CZ" +msgstr "Skylink CZ" + +msgctxt "#30103" msgid "Email / Card number" msgstr "Email / Card number" -msgctxt "#30103" +msgctxt "#30104" msgid "Password" msgstr "Password" -msgctxt "#30111" -msgid "Skylink SK" -msgstr "Skylink SK" - -msgctxt "#30112" -msgid "Skylink CZ" -msgstr "Skylink CZ" +msgctxt "#30105" +msgid "Generate and show pin protected channels" +msgstr "Generate and show pin protected channels" -msgctxt "#30104" +msgctxt "#30106" msgid "When no device is available use oldest" msgstr "When no device is available use oldest" -msgctxt "#30105" +msgctxt "#30107" msgid "Limit devices list to web browser" msgstr "Limit devices list to web browser" -msgctxt "#30106" -msgid "Generate and show pin protected channels" -msgstr "Generate and show pin protected channels" - msgctxt "#30200" msgid "Playlist" msgstr "Playlist" @@ -56,6 +52,10 @@ msgid "Filename" msgstr "Filename" msgctxt "#30204" +msgid "Eliminate duplicate channels" +msgstr "Eliminate duplicate channels" + +msgctxt "#30205" msgid "Logo urls directly from Skylink Live TV site" msgstr "Logo urls directly from Skylink Live TV site" diff --git a/resources/language/Slovak/strings.po b/resources/language/Slovak/strings.po index 837cc31..49737ba 100644 --- a/resources/language/Slovak/strings.po +++ b/resources/language/Slovak/strings.po @@ -8,37 +8,33 @@ msgid "Account" msgstr "Účet" msgctxt "#30101" -msgid "Provider" -msgstr "Provider" +msgid "Skylink SK" +msgstr "Skylink SK" msgctxt "#30102" +msgid "Skylink CZ" +msgstr "Skylink CZ" + +msgctxt "#30103" msgid "Email / Card number" msgstr "Email / číslo karty" -msgctxt "#30103" +msgctxt "#30104" msgid "Password" msgstr "Heslo" -msgctxt "#30111" -msgid "Skylink SK" -msgstr "Skylink SK" - -msgctxt "#30112" -msgid "Skylink CZ" -msgstr "Skylink CZ" +msgctxt "#30105" +msgid "Generate and show pin protected channels" +msgstr "Generovať a zobrazovať obsah chránený PIN-om" -msgctxt "#30104" +msgctxt "#30106" msgid "When no device is available use oldest" msgstr "Ak žiadne zariadenie nie je dostupé, použi najstaršie" -msgctxt "#30105" +msgctxt "#30107" msgid "Limit devices list to web browser" msgstr "Obmedziť zariadenia v zozname len na prehliadače" -msgctxt "#30106" -msgid "Generate and show pin protected channels" -msgstr "Generovať a zobrazovať obsah chránený PIN-om" - msgctxt "#30200" msgid "Playlist" msgstr "Playlist" @@ -56,6 +52,10 @@ msgid "Filename" msgstr "Súbor" msgctxt "#30204" +msgid "Eliminate duplicate channels" +msgstr "Odstrániť duplikátne stanice" + +msgctxt "#30205" msgid "Logo urls directly from Skylink Live TV site" msgstr "Url adresy pre logá staníc priamo zo stránky Skylink Live TV" diff --git a/resources/settings.xml b/resources/settings.xml index b4e4f3f..bcbd4b2 100644 --- a/resources/settings.xml +++ b/resources/settings.xml @@ -1,20 +1,24 @@ - - - - - - - - + + + + + + + + + + + - + + diff --git a/service.py b/service.py index c3fbd50..efa3a4b 100644 --- a/service.py +++ b/service.py @@ -46,28 +46,31 @@ def shedule_next(self, seconds): self._next_update = dt def update(self, try_reconnect=False): - _username = self._addon.getSetting('username') - _password = self._addon.getSetting('password') - _profile = xbmc.translatePath(self._addon.getAddonInfo('profile')) - _provider = 'skylink.sk' if int(self._addon.getSetting('provider')) == 0 else 'skylink.cz' - _pin_protected_content = 'false' != self._addon.getSetting('pin_protected_content') - sl = skylink.Skylink(_username, _password, _profile, _provider, _pin_protected_content) - logger.log.info('SL created') + providers = utils.get_available_providers() + channels = [] - try: - channels = sl.channels() - except skylink.TooManyDevicesException as e: - if self._addon.getSetting('reuse_last_device') == 'true': - device = utils.get_last_used_device(e.devices) - else: - device = utils.select_device(e.devices) if try_reconnect else '' - - if device != '': - logger.log.info('reconnecting as: ' + device) - sl.reconnect(device) - channels = sl.channels() - else: - raise + for sl in providers: + logger.log.info('Getting channels for account: %s' % (sl.account.username)) + + try: + channels = channels + sl.channels() + except skylink.TooManyDevicesException as e: + if self._addon.getSetting('reuse_last_device') == 'true': + device = utils.get_last_used_device(e.devices) + else: + device = utils.select_device(e.devices) if try_reconnect else '' + + if device != '': + logger.log.info('reconnecting as: ' + device) + sl.reconnect(device) + channels = channels + sl.channels() + else: + raise + + # Remove duplicate channels (same channels provided by skylink.sk and skylink.cz) + if bool(self._addon.getSetting('playlist_unique')): + logger.log.info('Filtering channels to create unique list...') + channels = utils.unique_channels(channels) logger.log.info('Updating playlist [%d channels]' % len(channels)) try: diff --git a/skylink.py b/skylink.py index 5cfb564..8551faf 100644 --- a/skylink.py +++ b/skylink.py @@ -45,6 +45,8 @@ def is_valid(self): class Skylink: + account = None + _id = '' _username = '' _password = '' _cookies_path = '' @@ -56,14 +58,16 @@ class Skylink: _login_url = '' _show_pin_protected = True - def __init__(self, username, password, cookies_storage_dir, provider='skylink.sk', show_pin_protected=True): - self._usermane = username - self._password = password - self._cookies_path = cookies_storage_dir - self._cookies_file = os.path.join(self._cookies_path, '%s.cookie' % username.lower()) - self._url = 'https://livetv.' + provider - self._login_url = 'https://login.' + provider - self._show_pin_protected = show_pin_protected + def __init__(self, account): + self.account = account + self._id = account.id + self._username = account.username + self._password = account.password + self._cookies_path = account.cookies_path + self._cookies_file = os.path.join(self._cookies_path, '%s@%s.cookie' % (self._username.lower(), account.provider)) + self._url = 'https://livetv.' + account.provider + self._login_url = 'https://login.' + account.provider + self._show_pin_protected = account.pin_protected def _store_cookies(self): if not os.path.exists(self._cookies_path): @@ -87,13 +91,13 @@ def _clear_cookies(self): def _auth(self, device=''): - if (self._usermane == '') or (self._password == ''): + if (self._username == '') or (self._password == ''): raise UserNotDefinedException self._session.cookies.clear() self._session.get(self._url + '/sso.aspx', headers={'User-Agent': UA}) - resp = self._session.post(self._login_url, data={'Username': self._usermane, 'Password': self._password}, + resp = self._session.post(self._login_url, data={'Username': self._username, 'Password': self._password}, headers={'User-Agent': UA, 'Referer': self._url}) self._data, error = self._parse_cookies() @@ -187,6 +191,7 @@ def channels(self, replay = False): if (is_stream and is_live and (not replay or is_replayable) and (self._show_pin_protected or (not self._show_pin_protected and not is_pin_protected))): + c['account'] = self._id c['pin'] = is_pin_protected result.append(c) diff --git a/utils.py b/utils.py index ad8bcda..8426cfc 100644 --- a/utils.py +++ b/utils.py @@ -4,7 +4,9 @@ import xbmcaddon import xbmcgui +import xbmc import skylink +import account import requests import logger @@ -75,3 +77,55 @@ def ask_for_pin(sl): dialog.ok(_addon.getAddonInfo('name'), _addon.getLocalizedString(30509)) return False return True + +def unique_channels(channels): + names_list = [] + channels_list = [] + for channel in channels: + if not channel['stationid'] in names_list: + names_list.append(channel['stationid']) + channels_list.append(channel) + + return channels_list + +def get_accounts(): + accounts = [] + profile = _addon.getAddonInfo('profile') + cookies_path = xbmc.translatePath(profile).decode("utf-8") + + account_sk = account.Account('sk', _addon.getSetting('username_sk'), _addon.getSetting('password_sk'), 'skylink.sk', cookies_path, bool(_addon.getSetting('pin_protected_content_sk'))) + account_cz = account.Account('cz', _addon.getSetting('username_cz'), _addon.getSetting('password_cz'), 'skylink.cz', cookies_path, bool(_addon.getSetting('pin_protected_content_cz'))) + accounts.append(account_sk) + accounts.append(account_cz) + + return accounts + +def get_account(id): + profile = _addon.getAddonInfo('profile') + cookies_path = xbmc.translatePath(profile).decode("utf-8") + + if id == 'sk': + return account.Account('sk', _addon.getSetting('username_sk'), _addon.getSetting('password_sk'), 'skylink.sk', cookies_path, bool(_addon.getSetting('pin_protected_content_sk'))) + if id == 'cz': + return account.Account('cz', _addon.getSetting('username_cz'), _addon.getSetting('password_cz'), 'skylink.cz', cookies_path, bool(_addon.getSetting('pin_protected_content_cz'))) + + return None + +def get_available_providers(): + accounts = get_accounts() + providers = [] + + for account in accounts: + if not account.is_valid(): + continue + providers.append(skylink.Skylink(account)) + + return providers + +def get_provider(accountid): + account = get_account(accountid) + + if not account or not account.is_valid(): + return None + + return skylink.Skylink(account) \ No newline at end of file