Example #1
0
    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
Example #2
0
    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,
        )
Example #4
0
    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)
Example #5
0
    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
Example #7
0
    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