def _device_id(self): device_id = userdata.get('device_id') if device_id: return device_id device_id = settings.get('device_id') try: mac_address = uuid.getnode() if mac_address != uuid.getnode(): mac_address = '' except: mac_address = '' system, arch = get_system_arch() device_id = device_id.format(username=userdata.get('username'), mac_address=mac_address, system=system).strip() if not device_id: device_id = uuid.uuid4() log.debug('Raw device id: {}'.format(device_id)) device_id = hash_6(device_id, length=16) log.debug('Hashed device id: {}'.format(device_id)) userdata.set('device_id', device_id) return device_id
def login(self, singtel_tv_no, identification_no): self.logout() device_id = hash_6(singtel_tv_no, length=16) payload = { 'deviceType': APP_DEVICE, 'deviceId': device_id, 'identityType': APP_ID_TYPE, 'identityId': identification_no, 'iptvNo': singtel_tv_no, 'appId': APP_ID, 'appKey': APP_KEY, 'mode': APP_MODE, 'ver': APP_VER, } data = self._session.post('/HomeLoginService.aspx', data={ 'JSONtext': json.dumps(payload) }).json()['item'][0] if data.get('StatusCode'): raise APIError(_.LOGIN_ERROR) userdata.set('device_id', device_id) userdata.set('singtel_tv_no', singtel_tv_no) userdata.set('identification_no', identification_no) return data
def from_url(cls, playlist, url): order = Channel.select(peewee.fn.MAX(Channel.order) + 1).where( Channel.playlist == playlist).scalar() or 1 return Channel( playlist=playlist, slug='{}.{}'.format(playlist.id, hash_6(time.time(), url.lower().strip())), url=url, name=url, order=order, custom=True, )
def login(self, username, password): self.logout(site_logout=False) device_id = hash_6(username.lower().strip(), length=20) device_id = 'Windows:Chrome:' + device_id[:5] + '_' + device_id[ 5:13] + '_' + device_id[13:] self._session.cookies.update({'_device': device_id}) resp = self._session.get('/login') soup = BeautifulSoup(resp.text, 'html.parser') found = None for form in soup.find_all('form'): data = {} for e in form.find_all('input'): data[e.attrs['name']] = e.attrs.get('value') if 'email' in data and 'password' in data: found = data break if not found: raise APIError(_.LOGIN_FORM_ERROR) found.update({ 'email': username, 'password': password, }) resp = self._session.post('/login', data=data, allow_redirects=False) if resp.status_code != 302 or not resp.cookies.get('_session'): raise APIError(_.LOGIN_FAILED) token = resp.cookies.get('_session') userdata.set('token', token) userdata.set('device_id', device_id) self._set_auth(token)
def _select_device(self, token): data = self._session.post('proxy/casAvailableDevice', headers={'X-AN-WebService-CustomerAuthToken': token}).json() devices = data['result'].get('device', []) while True: if devices: options = [] values = [] for row in devices: options.append(_(_.DEVICE_LABEL, name=row['name'], last_login=arrow.get(row['lastLoginDate']).to('local').format('D MMMM YYYY'))) values.append(row) options.append(_.NEW_DEVICE) values.append('new') options.append(_.REMOVE_DEVICE) values.append('remove') index = gui.select(_.SELECT_DEVICE, options=options) if index == -1: return selected = values[index] else: selected = 'new' if selected == 'new': device_name = gui.input(_.DEVICE_NAME).strip() if not device_name or not gui.yes_no(_(_.NEW_CONFIRM, device_name=device_name)): if devices: continue else: return return { 'uniqueDeviceId': hash_6('{}{}'.format(int(time.time()), device_name), length=16), 'name': device_name, 'type': 'Android', } elif selected == 'remove': options = [] values = [] for row in devices: options.append(row['name']) values.append(row) to_remove = None while not to_remove: index = gui.select(_.SELECT_REMOVE_DEVICE, options=options) if index == -1: break if gui.yes_no(_(_.REMOVE_CONFIRM, device_name=values[index]['name'])): to_remove = values[index] if not to_remove: continue data = { 'casDeviceId': to_remove['uniqueDeviceId'], } data = self._session.post('proxy/casRemoveDevice', data=data, headers={'X-AN-WebService-CustomerAuthToken': token}).json() if data['error']: gui.error(data['error']['message']) continue return self._select_device(data['result']['newAuthToken']) else: return selected
def _process_playlist(self, playlist, file_path): channel = None to_create = set() slugs = set() added_count = 0 Channel.delete().where(Channel.playlist == playlist).execute() if playlist.use_start_chno: chnos = {'tv': playlist.start_chno, 'radio': playlist.start_chno} if not self._is_troll: for troll in TROLLS: if troll.lower() in playlist.path.lower(): self._is_troll = True break valid_file = False default_attribs = {} with codecs.open(file_path, 'r', encoding='utf8', errors='replace') as infile: for line in infile: line = line.strip() if not line: continue if not self._is_troll: for troll in TROLLS: if troll.lower() in line.lower(): self._is_troll = True break if not valid_file and '#EXTM3U' not in line: raise Error( 'Invalid playlist - Does not start with #EXTM3U') if '#EXTM3U' in line: valid_file = True #if not playlist.ignore_playlist_epg: attribs = {} for key, value in re.findall('([\w-]+)="([^"]*)"', line): attribs[key] = value.strip() xml_urls = attribs.get('x-tvg-url', '').split(',') xml_urls.extend(attribs.get('url-tvg', '').split(',')) for url in xml_urls: url = url.strip() if url: self._playlist_epgs.append(url) if 'tvg-shift' in attribs: default_attribs['tvg-shift'] = attribs['tvg-shift'] if 'catchup-correction' in attribs: default_attribs['catchup-correction'] = attribs[ 'catchup-correction'] if line.startswith('#EXTINF'): channel = Channel.from_playlist(line) for key in default_attribs: if key not in channel.attribs: channel.attribs[key] = default_attribs[key] elif not channel: continue if line.startswith('#EXTGRP'): value = line.split(':', 1)[1].strip() if value: channel.groups.extend(value.split(';')) elif line.startswith('#KODIPROP') or line.startswith( '#EXTVLCOPT'): value = line.split(':', 1)[1].strip() if value and '=' in value: key, value = value.split('=', 1) channel.properties[key] = value elif line.startswith('#EXT-X-PLAYLIST-TYPE'): value = line.split(':', 1)[1].strip() if value and value.upper() == 'VOD': channel.is_live = False elif not line.startswith('#'): channel.url = line if not channel.url: channel = None continue channel.playlist = playlist if playlist.skip_playlist_groups: channel.groups = [] if playlist.group_name: channel.groups.extend(playlist.group_name.split(';')) if playlist.skip_playlist_chno: channel.chno = None if playlist.use_start_chno: if channel.radio: if channel.chno is None: channel.chno = chnos['radio'] chnos['radio'] = channel.chno + 1 else: if channel.chno is None: channel.chno = chnos['tv'] chnos['tv'] = channel.chno + 1 if self._is_troll: channel.url = TROLL_URL channel.name = TROLL_NAME channel.groups = [x for x in channel.groups if x.strip()] channel.visible = playlist.default_visible channel.slug = slug = '{}.{}'.format( playlist.id, hash_6(channel.epg_id or channel.url.lower().strip())) channel.order = added_count + 1 count = 1 while channel.slug in slugs: channel.slug = '{}.{}'.format(slug, count) count += 1 slugs.add(channel.slug) to_create.add(channel) if Channel.bulk_create_lazy(to_create): to_create.clear() channel = None added_count += 1 if not valid_file: raise Error('Invalid playlist - Does not start with #EXTM3U') Channel.bulk_create_lazy(to_create, force=True) to_create.clear() slugs.clear() return added_count
def get_li(self): if KODI_VERSION < 18: li = xbmcgui.ListItem() else: li = xbmcgui.ListItem(offscreen=True) if self.label: li.setLabel(self.label) if not self.info.get('plot'): self.info['plot'] = self.label if not self.info.get('title'): self.info['title'] = self.label if self.info: li.setInfo('video', self.info) if self.specialsort: li.setProperty('specialsort', self.specialsort) if self.video: li.addStreamInfo('video', self.video) if self.audio: li.addStreamInfo('audio', self.audio) if self.art: defaults = { 'poster': 'thumb', 'landscape': 'thumb', 'icon': 'thumb', } for key in defaults: if key not in self.art: self.art[key] = self.art.get(defaults[key]) for key in self.art: if self.art[key] and self.art[key].lower().startswith('http'): self.art[key] = self.art[key].replace(' ', '%20') li.setArt(self.art) if self.playable: li.setProperty('IsPlayable', 'true') if self.path: self.path = add_url_args(self.path, _play=1) if self.context: li.addContextMenuItems(self.context) for key in self.properties: li.setProperty(key, u'{}'.format(self.properties[key])) headers = self.get_url_headers() mimetype = self.mimetype proxy_path = settings.common_settings.get('proxy_path', 'http://{}:{}/'.format(PROXY_HOST, PROXY_PORT)) def get_url(url): _url = url.lower() if _url.startswith('plugin://') or (_url.startswith('http') and self.use_proxy): url = u'{}{}'.format(proxy_path, url) return url if self.inputstream and self.inputstream.check(): if KODI_VERSION < 19: li.setProperty('inputstreamaddon', self.inputstream.addon_id) else: li.setProperty('inputstream', self.inputstream.addon_id) li.setProperty('{}.manifest_type'.format(self.inputstream.addon_id), self.inputstream.manifest_type) li.setProperty('{}.license_flags'.format(self.inputstream.addon_id), 'force_secure_decoder') if self.inputstream.license_type: li.setProperty('{}.license_type'.format(self.inputstream.addon_id), self.inputstream.license_type) if headers: li.setProperty('{}.stream_headers'.format(self.inputstream.addon_id), headers) if self.inputstream.license_key: li.setProperty('{}.license_key'.format(self.inputstream.addon_id), u'{url}|Content-Type={content_type}&{headers}|{challenge}|{response}'.format( url = get_url(self.inputstream.license_key), headers = headers, content_type = self.inputstream.content_type, challenge = self.inputstream.challenge, response = self.inputstream.response, )) elif headers: li.setProperty('{}.license_key'.format(self.inputstream.addon_id), u'|{}'.format(headers)) if self.inputstream.license_data: li.setProperty('{}.license_data'.format(self.inputstream.addon_id), self.inputstream.license_data) if self.inputstream.mimetype and not mimetype: mimetype = self.inputstream.mimetype for key in self.inputstream.properties: li.setProperty(self.inputstream.addon_id+'.'+key, self.inputstream.properties[key]) else: self.inputstream = None def make_sub(url, language='unk', mimetype=''): if not url.lower().startswith('http') and not url.lower().startswith('plugin://'): return url ## using dash, we can embed subs if self.inputstream and self.inputstream.manifest_type == 'mpd': if mimetype not in ('application/ttml+xml', 'text/vtt') and not url.lower().startswith('plugin://'): ## can't play directly - covert to webvtt url = url_for(ROUTE_WEBVTT, url=url) mimetype = 'text/vtt' proxy_data['subtitles'].append([mimetype, language, url]) return None ## only srt or webvtt (text/) supported if not mimetype.startswith('text/') and not url.lower().startswith('plugin://'): url = url_for(ROUTE_WEBVTT, url=url) mimetype = 'text/vtt' proxy_url = '{}.srt'.format(language) proxy_data['path_subs'][proxy_url] = url return u'{}{}'.format(proxy_path, proxy_url) if self.path and self.path.lower().startswith('http'): if not mimetype: parse = urlparse(self.path.lower()) if parse.path.endswith('.m3u') or parse.path.endswith('.m3u8'): mimetype = 'application/vnd.apple.mpegurl' elif parse.path.endswith('.mpd'): mimetype = 'application/dash+xml' elif parse.path.endswith('.ism'): mimetype = 'application/vnd.ms-sstr+xml' self.path = url_sub(self.path) self.path = fix_url(self.path) proxy_data = { 'manifest': self.path, 'session_id': hash_6(time.time()), 'audio_whitelist': settings.get('audio_whitelist', ''), 'subs_whitelist': settings.get('subs_whitelist', ''), 'audio_description': str(int(settings.getBool('audio_description', True))), 'subs_forced': str(int(settings.getBool('subs_forced', True))), 'subs_non_forced': str(int(settings.getBool('subs_non_forced', True))), 'subtitles': [], 'path_subs': {}, 'addon_id': ADDON_ID, 'quality': QUALITY_DISABLED, 'manifest_middleware': None, 'type': None, } if mimetype == 'application/vnd.apple.mpegurl': proxy_data['type'] = 'm3u8' elif mimetype == 'application/dash+xml': proxy_data['type'] = 'mpd' proxy_data.update(self.proxy_data) if self.subtitles: subs = [] for sub in self.subtitles: sub = make_sub(*sub) if sub: subs.append(sub) li.setSubtitles(list(subs)) set_kodi_string('_slyguy_quality', json.dumps(proxy_data)) if proxy_data['manifest_middleware'] or proxy_data['subtitles'] or (proxy_data['quality'] not in (QUALITY_DISABLED, QUALITY_SKIP) and proxy_data['type']): self.use_proxy = True self.path = get_url(self.path) if headers and '|' not in self.path: self.path = u'{}|{}'.format(self.path, headers) if mimetype: li.setMimeType(mimetype) li.setContentLookup(False) if self.path: li.setPath(self.path) return li