Exemplo n.º 1
0
    def _request_fog_channels_json(cls):
        url = '{0}{1}'.format(
            SmoothStreamsConstants.FOG_EPG_BASE_URL,
            SmoothStreamsConstants.FOG_CHANNELS_JSON_FILE_NAME)

        logger.debug('Downloading {0}\n'
                     'URL => {1}'.format(
                         SmoothStreamsConstants.FOG_CHANNELS_JSON_FILE_NAME,
                         url))

        requests_session = requests.Session()
        response = Utility.make_http_request(requests_session.get,
                                             url,
                                             headers=requests_session.headers,
                                             stream=True)

        if response.status_code == requests.codes.OK:
            response.raw.decode_content = True

            logger.trace(Utility.assemble_response_from_log_message(response))

            return response.raw
        else:
            logger.error(Utility.assemble_response_from_log_message(response))

            response.raise_for_status()
Exemplo n.º 2
0
    def _request_fog_epg_xml(cls):
        url = '{0}{1}'.format(
            SmoothStreamsConstants.FOG_EPG_BASE_URL,
            SmoothStreamsConstants.FOG_EPG_XML_FILE_NAME,
        )

        logger.debug(
            'Downloading %s\nURL => %s',
            SmoothStreamsConstants.FOG_EPG_XML_FILE_NAME,
            url,
        )

        requests_session = requests.Session()
        response = Utility.make_http_request(requests_session.get,
                                             url,
                                             headers=requests_session.headers,
                                             stream=True)

        if response.status_code == requests.codes.OK:
            response.raw.decode_content = True

            logger.trace(Utility.assemble_response_from_log_message(response))

            return response.raw

        logger.error(Utility.assemble_response_from_log_message(response))

        response.raise_for_status()
Exemplo n.º 3
0
    def download_ts_file(cls, client_ip_address, client_uuid, requested_path,
                         requested_query_string_parameters):
        authorization_token = requested_query_string_parameters.get(
            'wmsAuthSign')
        channel_number = requested_query_string_parameters.get(
            'channel_number')
        nimble_session_id = requested_query_string_parameters.get(
            'nimblesessionid')

        IPTVProxy.refresh_serviceable_clients(client_uuid, client_ip_address)
        IPTVProxy.set_serviceable_client_parameter(
            client_uuid, 'last_request_date_time_in_utc',
            datetime.now(pytz.utc))
        IPTVProxy.set_serviceable_client_parameter(
            client_uuid, 'last_requested_channel_number', channel_number)

        requests_session = cls._get_session_parameter('requests_session')

        target_url = 'https://{0}.smoothstreams.tv/{1}/ch{2}q1.stream{3}'.format(
            Configuration.get_configuration_parameter('SMOOTHSTREAMS_SERVER'),
            Configuration.get_configuration_parameter('SMOOTHSTREAMS_SERVICE'),
            channel_number, re.sub(r'(/.*)?(/.*\.ts)', r'\2', requested_path))

        logger.debug('Proxying request\n'
                     'Source IP      => {0}\n'
                     'Requested path => {1}\n'
                     '  Parameters\n'
                     '    channel_number  => {2}\n'
                     '    client_uuid     => {3}\n'
                     'Target path    => {4}\n'
                     '  Parameters\n'
                     '    nimblesessionid => {5}\n'
                     '    wmsAuthSign     => {6}'.format(
                         client_ip_address, requested_path, channel_number,
                         client_uuid, target_url, nimble_session_id,
                         authorization_token))

        response = Utility.make_http_request(
            requests_session.get,
            target_url,
            params={
                'nimblesessionid': nimble_session_id,
                'wmsAuthSign': authorization_token
            },
            headers=requests_session.headers,
            cookies=requests_session.cookies.get_dict())

        if response.status_code == requests.codes.OK:
            logger.trace(
                Utility.assemble_response_from_log_message(
                    response, is_content_binary=True))

            return response.content
        else:
            logger.error(Utility.assemble_response_from_log_message(response))

            response.raise_for_status()
Exemplo n.º 4
0
    def _request_epg_json(cls, epg_json_path, epg_json_file_name,
                          request_parameters):
        username = Configuration.get_configuration_parameter(
            'VADERSTREAMS_USERNAME')
        password = SecurityManager.decrypt_password(
            Configuration.get_configuration_parameter(
                'VADERSTREAMS_PASSWORD')).decode()

        url = '{0}{1}'.format(VaderStreamsConstants.BASE_URL, epg_json_path)

        logger.debug(
            'Downloading %s\n'
            'URL => %s\n'
            '  Parameters\n'
            '    username => %s\n'
            '    password => %s%s',
            epg_json_file_name,
            url,
            username,
            '\u2022' * len(password),
            '' if request_parameters is None
            or 'category_id' not in request_parameters else
            '\n    category => {0}'.format(request_parameters['category_id']),
        )

        requests_session = requests.Session()
        response = Utility.make_http_request(
            requests_session.get,
            url,
            params={
                'username': username,
                'password': password,
                **request_parameters
            },
            headers=requests_session.headers,
            cookies=requests_session.cookies.get_dict(),
            stream=True,
        )

        if response.status_code == requests.codes.OK:
            response.raw.decode_content = True

            logger.trace(Utility.assemble_response_from_log_message(response))

            return response.raw

        logger.error(Utility.assemble_response_from_log_message(response))

        response.raise_for_status()
Exemplo n.º 5
0
def main():
    try:
        Privilege.initialize()
        Privilege.become_unprivileged_user()

        (configuration_file_path, optional_settings_file_path, db_file_path,
         log_file_path, recordings_directory_path, certificate_file_path,
         key_file_path) = Utility.parse_command_line_arguments()

        Logging.initialize_logging(log_file_path)

        logger.info('Starting IPTV Proxy {0}\n'
                    'Configuration file path     => {1}\n'
                    'Optional settings file path => {2}\n'
                    'Database file path          => {3}\n'
                    'Log file path               => {4}\n'
                    'Recordings directory path   => {5}\n'
                    'SSL certificate file path   => {6}\n'
                    'SSL key file path           => {7}'.format(
                        VERSION, configuration_file_path,
                        optional_settings_file_path, db_file_path,
                        log_file_path, recordings_directory_path,
                        certificate_file_path, key_file_path))

        Controller.start_proxy(configuration_file_path,
                               optional_settings_file_path, db_file_path,
                               log_file_path, recordings_directory_path,
                               certificate_file_path, key_file_path)

        logger.info('Shutting down IPTV Proxy {0}'.format(VERSION))
    except Exception:
        (type_, value_, traceback_) = sys.exc_info()
        logger.error('\n'.join(
            traceback.format_exception(type_, value_, traceback_)))
Exemplo n.º 6
0
    def render_guide_div_template(
        self,
        is_server_secure,
        authorization_required,
        client_ip_address,
        client_uuid,
        guide_number_of_days,
        guide_provider,
        guide_group,
        active_providers_map_class,
    ):
        client_ip_address_type = Utility.determine_ip_address_type(
            client_ip_address)

        yield self._environment.get_template('guide_div_header.html').render()

        for rendered_guide_li_template in self._render_guide_lis_template(
                is_server_secure,
                authorization_required,
                client_ip_address_type,
                client_uuid,
                guide_number_of_days,
                guide_provider,
                guide_group,
                active_providers_map_class,
        ):
            yield rendered_guide_li_template

        yield self._environment.get_template('guide_div_footer.html').render()
Exemplo n.º 7
0
    def generate_self_signed_certificate(cls):
        ip_address_location = Utility.determine_ip_address_location()

        if ip_address_location is not None:
            private_key = rsa.generate_private_key(public_exponent=65537,
                                                   key_size=2048,
                                                   backend=default_backend())

            with open(DEFAULT_SSL_KEY_FILE_PATH, 'wb') as output_file:
                output_file.write(
                    private_key.private_bytes(
                        encoding=serialization.Encoding.PEM,
                        format=serialization.PrivateFormat.TraditionalOpenSSL,
                        encryption_algorithm=serialization.NoEncryption(),
                    ))

            current_date_time_in_utc = datetime.now(pytz.utc)

            subject = issuer = x509.Name([
                x509.NameAttribute(NameOID.COUNTRY_NAME,
                                   ip_address_location['countryCode']),
                x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME,
                                   ip_address_location['region']),
                x509.NameAttribute(NameOID.LOCALITY_NAME,
                                   ip_address_location['city']),
                x509.NameAttribute(NameOID.ORGANIZATION_NAME, 'IPTVProxy'),
                x509.NameAttribute(
                    NameOID.COMMON_NAME,
                    Configuration.get_configuration_parameter(
                        'SERVER_HOSTNAME_PUBLIC'),
                ),
            ])

            certificate = (x509.CertificateBuilder(
            ).subject_name(subject).issuer_name(issuer).public_key(
                private_key.public_key()).serial_number(
                    x509.random_serial_number()
                ).not_valid_before(current_date_time_in_utc).not_valid_after(
                    current_date_time_in_utc +
                    timedelta(days=10 * 365)).add_extension(
                        x509.SubjectAlternativeName([
                            x509.DNSName(
                                Configuration.get_configuration_parameter(
                                    'SERVER_HOSTNAME_LOOPBACK')),
                            x509.DNSName(
                                Configuration.get_configuration_parameter(
                                    'SERVER_HOSTNAME_PRIVATE')),
                            x509.DNSName(
                                Configuration.get_configuration_parameter(
                                    'SERVER_HOSTNAME_PUBLIC')),
                        ]),
                        critical=False,
                    ).sign(private_key, hashes.SHA256(), default_backend()))

            with open(DEFAULT_SSL_CERTIFICATE_FILE_PATH, 'wb') as output_file:
                output_file.write(
                    certificate.public_bytes(serialization.Encoding.PEM))
        else:
            logger.error('Failed to generate self signed certificate')
Exemplo n.º 8
0
    def _request_epg_xml(cls):
        url = '{0}{1}'.format(VaderStreamsConstants.EPG_BASE_URL, VaderStreamsConstants.XML_EPG_FILE_NAME)

        logger.debug('Downloading {0}\n'
                     'URL => {1}'.format(VaderStreamsConstants.XML_EPG_FILE_NAME, url))

        requests_session = requests.Session()
        response = Utility.make_http_request(requests_session.get, url, headers=requests_session.headers, stream=True)

        if response.status_code == requests.codes.OK:
            logger.trace(Utility.assemble_response_from_log_message(response))

            return response.raw
        else:
            logger.error(Utility.assemble_response_from_log_message(response))

            response.raise_for_status()
Exemplo n.º 9
0
    def generate_playlist_m3u8(cls, is_server_secure, client_ip_address,
                               client_uuid, requested_query_string_parameters,
                               providers):
        http_token = requested_query_string_parameters.get('http_token')
        playlist_protocol = requested_query_string_parameters.get('protocol')
        playlist_type = requested_query_string_parameters.get('type')

        try:
            client_ip_address_type = Utility.determine_ip_address_type(
                client_ip_address)
            server_hostname = Configuration.get_configuration_parameter(
                'SERVER_HOSTNAME_{0}'.format(client_ip_address_type.value))
            server_port = Configuration.get_configuration_parameter(
                'SERVER_HTTP{0}_PORT'.format('S' if is_server_secure else ''))

            playlist_m3u8 = [
                '#EXTM3U x-tvg-url="{0}://{1}:{2}/live/epg.xml"\n'.format(
                    'https' if is_server_secure else 'http', server_hostname,
                    server_port)
            ]

            for (provider_name, provider) in sorted(list(providers.items())):
                provider_protocol = playlist_protocol
                provider_type = playlist_type

                try:
                    provider_protocol = requested_query_string_parameters[
                        '{0}_protocol'.format(provider_name)]
                except KeyError:
                    pass

                try:
                    provider_type = requested_query_string_parameters[
                        '{0}_type'.format(provider_name)]
                except KeyError:
                    pass

                generate_playlist_m3u8_tracks_mapping = dict(
                    client_uuid=client_uuid,
                    http_token=http_token,
                    is_server_secure=is_server_secure,
                    playlist_protocol=provider_protocol,
                    playlist_type=provider_type,
                    server_hostname=server_hostname,
                    server_port=server_port)

                playlist_m3u8.append(''.join(
                    provider.api_class().generate_playlist_m3u8_tracks(
                        generate_playlist_m3u8_tracks_mapping)))

            logger.debug('Generated live IPTVProxy playlist.m3u8')

            return ''.join(playlist_m3u8)
        except (KeyError, ValueError):
            (status, value_, traceback_) = sys.exc_info()

            logger.error('\n'.join(
                traceback.format_exception(status, value_, traceback_)))
Exemplo n.º 10
0
    def generate_xmltv(cls,
                       is_server_secure,
                       authorization_required,
                       client_ip_address,
                       providers_map_class,
                       number_of_days,
                       style):
        current_date_time_in_utc = datetime.now(pytz.utc)

        yield '<?xml version="1.0" encoding="utf-8"?>\n<tv date="{0}" generator-info-name="IPTVProxy {1}">\n'.format(
            current_date_time_in_utc.strftime('%Y%m%d%H%M%S %z'),
            VERSION)

        client_ip_address_type = Utility.determine_ip_address_type(client_ip_address)
        server_password = Configuration.get_configuration_parameter('SERVER_PASSWORD')
        server_hostname = Configuration.get_configuration_parameter(
            'SERVER_HOSTNAME_{0}'.format(client_ip_address_type.value))
        server_port = Configuration.get_configuration_parameter(
            'SERVER_HTTP{0}_PORT'.format('S' if is_server_secure
                                         else ''))

        cutoff_date_time_in_local = datetime.now(tzlocal.get_localzone()).replace(
            hour=0,
            minute=0,
            second=0,
            microsecond=0) + timedelta(days=int(number_of_days) + 1)
        cutoff_date_time_in_utc = cutoff_date_time_in_local.astimezone(pytz.utc)

        for provider_map_class in providers_map_class.values():
            with provider_map_class.database_class().get_access_lock().shared_lock:
                db_session = provider_map_class.database_class().create_session()

                try:
                    if style.capitalize() == EPGStyle.COMPLETE.value:
                        query_channels_xmltv = provider_map_class.database_access_class().query_channels_complete_xmltv
                        query_programs_xmltv = provider_map_class.database_access_class().query_programs_complete_xmltv
                    else:
                        query_channels_xmltv = provider_map_class.database_access_class().query_channels_minimal_xmltv
                        query_programs_xmltv = provider_map_class.database_access_class().query_programs_minimal_xmltv

                    for channel_row in query_channels_xmltv(db_session):
                        yield channel_row.xmltv.format(
                            's' if is_server_secure
                            else '',
                            server_hostname,
                            server_port,
                            '?http_token={0}'.format(server_password) if authorization_required
                            else '')

                    for program_row in query_programs_xmltv(db_session, cutoff_date_time_in_utc):
                        yield program_row.xmltv
                finally:
                    db_session.close()

        yield '</tv>\n'
Exemplo n.º 11
0
    def _do_process_on_modified_event(self, event):
        do_process_on_modified_event = False

        if os.path.normpath(event.src_path) == os.path.normpath(
                self._file_path):
            if not self._last_file_version_md5_checksum:
                do_process_on_modified_event = True

                self._last_file_version_md5_checksum = hashlib.md5(
                    Utility.read_file(self._file_path,
                                      in_binary=True)).hexdigest()
            else:
                configuration_md5_checksum = hashlib.md5(
                    Utility.read_file(self._file_path,
                                      in_binary=True)).hexdigest()
                if configuration_md5_checksum != self._last_file_version_md5_checksum:
                    do_process_on_modified_event = True

                    self._last_file_version_md5_checksum = configuration_md5_checksum

        return do_process_on_modified_event
Exemplo n.º 12
0
    def validate_update_configuration_request(cls, configuration):
        errors = {}

        # <editor-fold desc="Validate Server options">
        if not Utility.is_valid_server_password(
                configuration['SERVER_PASSWORD']):
            errors['serverPassword'] = '******'

        if not Utility.is_valid_loopback_hostname(
                configuration['SERVER_HOSTNAME_LOOPBACK']):
            errors['serverHostnameLoopback'] = 'Must be a valid loopback IP address or hostname\n' \
                                               'Recommended value => {0}'.format(DEFAULT_HOSTNAME_LOOPBACK)

        if not Utility.is_valid_private_hostname(
                configuration['SERVER_HOSTNAME_PRIVATE']):
            if not Utility.is_valid_public_hostname(
                    configuration['SERVER_HOSTNAME_PRIVATE']):
                private_ip_address = Utility.determine_private_ip_address()

                errors[
                    'serverHostnamePrivate'] = 'Must be a valid private IP address, public IP address, or hostname'

                if private_ip_address:
                    errors[
                        'serverHostnamePrivate'] += '\nRecommended value => {0}'.format(
                            private_ip_address)

        if not Utility.is_valid_public_hostname(
                configuration['SERVER_HOSTNAME_PUBLIC']):
            public_ip_address = Utility.determine_public_ip_address()

            errors[
                'serverHostnamePublic'] = 'Must be a valid public IP address or hostname'

            if public_ip_address:
                errors[
                    'serverHostnamePublic'] += '\nRecommended value => {0}'.format(
                        public_ip_address)

        if not Utility.is_valid_port_number(configuration['SERVER_HTTP_PORT']):
            errors['serverPort'] = 'Must be a number between 0 and 65535'
        # </editor-fold>

        for provider_name in sorted(
                ProvidersController.get_providers_map_class()):
            ProvidersController.get_provider_map_class(
                provider_name).configuration_class(
                ).validate_update_configuration_request(configuration, errors)

        return errors
Exemplo n.º 13
0
    def on_created(self, event):
        with self._lock:
            if os.path.normpath(event.src_path) == os.path.normpath(
                    self._file_path):
                self._last_file_version_md5_checksum = hashlib.md5(
                    Utility.read_file(self._file_path,
                                      in_binary=True)).hexdigest()

                logger.debug(
                    'Detected creation of logging configuration file\n'
                    'Logging configuration file path => {0}'.format(
                        self._file_path))

                Logging.set_logging_configuration()
Exemplo n.º 14
0
    def generate_vod_index_playlist_m3u8(cls, is_server_secure,
                                         client_ip_address, client_uuid,
                                         http_token):
        playlist_m3u8 = []

        client_ip_address_type = Utility.determine_ip_address_type(
            client_ip_address)
        server_hostname = Configuration.get_configuration_parameter(
            'SERVER_HOSTNAME_{0}'.format(client_ip_address_type.value))
        server_port = Configuration.get_configuration_parameter(
            'SERVER_HTTP{0}_PORT'.format('S' if is_server_secure else ''))

        db_session = Database.create_session()

        try:
            for persistent_recording in DatabaseAccess.query_persisted_recordings(
                    db_session):
                playlist_m3u8.append(
                    '#EXTINF:-1,{0} - [{1} - {2}]\n'
                    '{3}\n'.format(
                        persistent_recording.program_title,
                        persistent_recording.start_date_time_in_utc.astimezone(
                            tzlocal.get_localzone()).strftime(
                                '%Y-%m-%d %H:%M:%S%z'),
                        persistent_recording.end_date_time_in_utc.astimezone(
                            tzlocal.get_localzone()).strftime(
                                '%Y-%m-%d %H:%M:%S%z'),
                        cls.generate_vod_recording_playlist_url(
                            is_server_secure,
                            server_hostname,
                            server_port,
                            client_uuid,
                            persistent_recording.id,
                            http_token,
                        ),
                    ))
        finally:
            db_session.close()

        if playlist_m3u8:
            playlist_m3u8 = '#EXTM3U\n{0}'.format(''.join(playlist_m3u8))

            logger.debug('Generated VOD playlist.m3u8')
        else:
            logger.debug(
                'No persistent recordings found. VOD playlist.m3u8 will not be generated'
            )

        return playlist_m3u8
Exemplo n.º 15
0
    def load_ts_file(cls, path, recording_id):
        db_session = Database.create_session()

        try:
            segment_name = re.sub(r'/vod/(.*)\?.*', r'\1', path)

            segment_row = DatabaseAccess.query_segment_directory_path(
                db_session, segment_name, recording_id)

            if segment_row is not None:
                return Utility.read_file(os.path.join(
                    segment_row.directory_path, segment_name),
                                         in_binary=True)
            else:
                raise SegmentNotFoundError
        finally:
            db_session.close()
Exemplo n.º 16
0
    def read_optional_settings_file(cls):
        with cls._lock.writer_lock:
            cls._backup_optional_settings()

            try:
                optional_settings_file_content = Utility.read_file(
                    cls._optional_settings_file_path)
                cls._set_optional_settings(
                    json.loads(optional_settings_file_content,
                               object_pairs_hook=OrderedDict))
            except OSError:
                logger.error('Failed to read optional settings file\n'
                             'Optional settings file path => {0}'.format(
                                 cls._optional_settings_file_path))
            except JSONDecodeError:
                logger.error('Invalid optional settings file syntax\n'
                             'Optional settings file path => {0}'.format(
                                 cls._optional_settings_file_path))
Exemplo n.º 17
0
    def read_configuration_file(cls, initial_read=True):
        with cls._lock.writer_lock:
            cls._backup_configuration()

            try:
                configuration_object = ConfigObj(cls._configuration_file_path,
                                                 file_error=True,
                                                 indent_type='',
                                                 interpolation=False,
                                                 raise_errors=True,
                                                 write_empty_values=True)

                configuration_object_md5 = hashlib.md5(
                    '{0}'.format(configuration_object).encode()).hexdigest()

                configuration = {}
                providers = []

                non_defaultable_error = False
                error_message_to_log = []
                message_to_log = []

                password = None
                hostname_loopback = DEFAULT_HOSTNAME_LOOPBACK
                hostname_private = None
                hostname_public = None
                http_port = None
                https_port = None

                # <editor-fold desc="Read Server section">
                try:
                    server_section = configuration_object['Server']

                    try:
                        password = server_section['password']
                    except KeyError:
                        non_defaultable_error = True

                        error_message_to_log.append(
                            'Could not find a password option in the [Server] section\n'
                        )

                    try:
                        server_hostnames_section = server_section['Hostnames']

                        # <editor-fold desc="Read loopback option">
                        try:
                            hostname_loopback = server_hostnames_section[
                                'loopback']

                            if not Utility.is_valid_loopback_hostname(
                                    hostname_loopback):
                                error_message_to_log.append(
                                    'The loopback option in the [Hostnames] section has an invalid loopback hostname\n'
                                    'Defaulting to {0}\n'.format(
                                        DEFAULT_HOSTNAME_LOOPBACK))
                        except KeyError:
                            hostname_loopback = DEFAULT_HOSTNAME_LOOPBACK

                            error_message_to_log.append(
                                'The loopback option in the [Hostnames] section is missing\n'
                                'Defaulting to {0}\n'.format(
                                    DEFAULT_HOSTNAME_LOOPBACK))
                        # </editor-fold>

                        # <editor-fold desc="Read private option">
                        do_determine_private_ip_address = False

                        try:
                            hostname_private = server_hostnames_section[
                                'private']

                            if not Utility.is_valid_private_hostname(
                                    hostname_private):
                                if Utility.is_valid_public_hostname(
                                        hostname_private):
                                    error_message_to_log.append(
                                        'The private option in the [Hostnames] section has a public IP address\n'
                                    )
                                else:
                                    do_determine_private_ip_address = True
                        except KeyError:
                            do_determine_private_ip_address = True
                        # </editor-fold>

                        # <editor-fold desc="Read public option">
                        do_determine_public_ip_address = False

                        try:
                            hostname_public = server_hostnames_section[
                                'public']
                            if not Utility.is_valid_public_hostname(
                                    hostname_public):
                                do_determine_public_ip_address = True
                        except KeyError:
                            do_determine_public_ip_address = True
                        # </editor-fold>
                    except KeyError:
                        error_message_to_log.append(
                            'Could not find a [Hostnames] section in the [Server] section\n'
                        )

                        hostname_loopback = DEFAULT_HOSTNAME_LOOPBACK

                        do_determine_private_ip_address = True
                        do_determine_public_ip_address = True

                    if do_determine_private_ip_address:
                        hostname_private = Utility.determine_private_ip_address(
                        )

                        if hostname_private:
                            error_message_to_log.append(
                                'The private option in the [Hostnames] section has an invalid private IP address\n'
                                'Reverting to {0}\n'.format(hostname_private))

                    if do_determine_public_ip_address:
                        hostname_public = Utility.determine_public_ip_address()

                        if hostname_public:
                            error_message_to_log.append(
                                'The public option in the [Hostnames] section has an invalid public IP address\n'
                                'Reverting to {0}\n'.format(hostname_public))

                    try:
                        server_ports_section = server_section['Ports']

                        # <editor-fold desc="Read http option">
                        try:
                            http_port = server_ports_section['http']
                            if not Utility.is_valid_port_number(http_port):
                                non_defaultable_error = True

                                error_message_to_log.append(
                                    'The http option in the [Ports] section must be a number between 0 and 65535\n'
                                )
                        except KeyError:
                            non_defaultable_error = True

                            error_message_to_log.append(
                                'Could not find an http option in the [Ports] section\n'
                                'The http option in the [Ports] section must be a number between 0 and 65535\n'
                            )
                        # </editor-fold>

                        # <editor-fold desc="Read https option">
                        try:
                            https_port = server_ports_section['https']
                            if not Utility.is_valid_port_number(https_port):
                                non_defaultable_error = True

                                error_message_to_log.append(
                                    'The https option in the [Ports] section must be a number between 0 and 65535\n'
                                )
                        except KeyError:
                            non_defaultable_error = True

                            error_message_to_log.append(
                                'Could not find an https option in the [Ports] section\n'
                                'The https option in the [Ports] section must be a number between 0 and 65535\n'
                            )
                        # </editor-fold>
                    except KeyError:
                        non_defaultable_error = True

                        error_message_to_log.append(
                            'Could not find a [Ports] section in the [Server] section\n'
                        )
                except KeyError:
                    non_defaultable_error = True

                    error_message_to_log.append(
                        'Could not find a [Server] section\n')
                # </editor-fold>

                if not non_defaultable_error:
                    configuration = {
                        'SERVER_PASSWORD': password,
                        'SERVER_HOSTNAME_LOOPBACK': hostname_loopback,
                        'SERVER_HOSTNAME_PRIVATE': hostname_private,
                        'SERVER_HOSTNAME_PUBLIC': hostname_public,
                        'SERVER_HTTP_PORT': http_port,
                        'SERVER_HTTPS_PORT': https_port
                    }

                    message_to_log = [
                        '{0}ead configuration file\n'
                        'Configuration file path          => {1}\n\n'
                        'SERVER_PASSWORD                  => {2}\n'
                        'SERVER_HOSTNAME_LOOPBACK         => {3}\n'
                        'SERVER_HOSTNAME_PRIVATE          => {4}\n'
                        'SERVER_HOSTNAME_PUBLIC           => {5}\n'
                        'SERVER_HTTP_PORT                 => {6}\n'
                        'SERVER_HTTPS_PORT                => {7}'.format(
                            'R' if initial_read else 'Rer',
                            cls._configuration_file_path, password,
                            hostname_loopback, hostname_private,
                            hostname_public, http_port, https_port)
                    ]

                for provider_name in sorted(
                        ProvidersController.get_providers_map_class()):
                    ProvidersController.get_provider_map_class(
                        provider_name).configuration_class(
                        ).read_configuration_file(configuration_object,
                                                  configuration, providers,
                                                  message_to_log,
                                                  error_message_to_log)

                if not non_defaultable_error:
                    logger.info('\n'.join(message_to_log))

                    cls._set_configuration(configuration)

                    if configuration_object_md5 != hashlib.md5('{0}'.format(
                            configuration_object).encode()).hexdigest():
                        cls._update_configuration_file(configuration_object)

                    if initial_read:
                        ProvidersController.initialize_providers(providers)

                if error_message_to_log:
                    error_message_to_log.insert(
                        0, '{0} configuration file values\n'
                        'Configuration file path => {1}\n'.format(
                            'Invalid'
                            if non_defaultable_error else 'Warnings regarding',
                            cls._configuration_file_path))

                    if initial_read and non_defaultable_error:
                        error_message_to_log.append('Exiting...')
                    elif non_defaultable_error:
                        error_message_to_log.append(
                            'Configuration file skipped')
                    else:
                        error_message_to_log.append(
                            'Configuration file processed')

                    logger.error('\n'.join(error_message_to_log))

                    if initial_read and non_defaultable_error:
                        sys.exit()
            except OSError:
                logger.error(
                    'Could not open the specified configuration file for reading\n'
                    'Configuration file path => {0}'
                    '{1}'.format(cls._configuration_file_path,
                                 '\n\nExiting...' if initial_read else ''))

                if initial_read:
                    sys.exit()
            except SyntaxError as e:
                logger.error('Invalid configuration file syntax\n'
                             'Configuration file path => {0}\n'
                             '{1}'
                             '{2}'.format(
                                 cls._configuration_file_path, '{0}'.format(e),
                                 '\n\nExiting...' if initial_read else ''))

                if initial_read:
                    sys.exit()
Exemplo n.º 18
0
    def download_ts_file(cls, client_ip_address, client_uuid, requested_path, requested_query_string_parameters):
        authorization_token = requested_query_string_parameters.get('authorization_token')
        channel_name = requested_query_string_parameters.get('channel_name')
        channel_number = requested_query_string_parameters.get('channel_number')
        port = requested_query_string_parameters.get('port')
        server = requested_query_string_parameters.get('server')

        IPTVProxy.refresh_serviceable_clients(client_uuid, client_ip_address)
        IPTVProxy.set_serviceable_client_parameter(client_uuid, 'last_request_date_time_in_utc', datetime.now(pytz.utc))

        requests_session = requests.Session()

        target_url = 'http://{0}{1}/{2}{3}'.format(server
                                                   if re.match(r"\A\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\Z", server)
                                                   else '{0}.vaders.tv'.format(server),
                                                   port if port != ':80'
                                                   else '',
                                                   channel_name,
                                                   re.sub(r'(/.*)?(/.*\.ts)',
                                                          r'\2',
                                                          requested_path).replace('_', '/'))

        logger.debug('Proxying request\n'
                     'Source IP      => {0}\n'
                     'Requested path => {1}\n'
                     '  Parameters\n'
                     '    channel_name   => {2}\n'
                     '    channel_number => {3}\n'
                     '    client_uuid    => {4}\n'
                     '    port           => {5}\n'
                     '    server         => {6}\n'
                     'Target path    => {7}\n'
                     '  Parameters\n'
                     '    token          => {8}'.format(client_ip_address,
                                                        requested_path,
                                                        channel_name,
                                                        channel_number,
                                                        client_uuid,
                                                        port,
                                                        server,
                                                        target_url,
                                                        authorization_token))

        response = Utility.make_http_request(requests_session.get,
                                             target_url,
                                             params={
                                                 'token': authorization_token
                                             },
                                             headers=requests_session.headers,
                                             cookies=requests_session.cookies.get_dict())

        if response.status_code == requests.codes.OK:
            logger.trace(Utility.assemble_response_from_log_message(response,
                                                                    is_content_binary=True))

            IPTVProxy.set_serviceable_client_parameter(client_uuid,
                                                       'last_requested_ts_file_path',
                                                       re.sub(r'(/.*)?(/.*\.ts)',
                                                              r'\2',
                                                              requested_path).replace('_', '/')[1:])

            return response.content
        else:
            logger.error(Utility.assemble_response_from_log_message(response))

            response.raise_for_status()
Exemplo n.º 19
0
    def download_playlist_m3u8(cls, client_ip_address, client_uuid,
                               requested_path,
                               requested_query_string_parameters):
        channel_number = requested_query_string_parameters.get(
            'channel_number')
        http_token = requested_query_string_parameters.get('http_token')
        protocol = requested_query_string_parameters.get('protocol')

        IPTVProxy.refresh_serviceable_clients(client_uuid, client_ip_address)
        IPTVProxy.set_serviceable_client_parameter(
            client_uuid, 'last_request_date_time_in_utc',
            datetime.now(pytz.utc))
        IPTVProxy.set_serviceable_client_parameter(
            client_uuid, 'last_requested_channel_number', channel_number)

        cls.refresh_session()

        if protocol == 'hls':
            authorization_token = cls._get_session_parameter(
                'authorization_token')
            requests_session = cls._get_session_parameter('requests_session')

            target_url = 'https://{0}.smoothstreams.tv/{1}/ch{2}q1.stream{3}'.format(
                Configuration.get_configuration_parameter(
                    'SMOOTHSTREAMS_SERVER'),
                Configuration.get_configuration_parameter(
                    'SMOOTHSTREAMS_SERVICE'), channel_number,
                re.sub(r'(/.*)?(/.*\.m3u8)', r'\2', requested_path))

            logger.debug('Proxying request\n'
                         'Source IP      => {0}\n'
                         'Requested path => {1}\n'
                         '  Parameters\n'
                         '    channel_number => {2}\n'
                         '    client_uuid    => {3}\n'
                         '    protocol       => {4}\n'
                         'Target path    => {5}\n'
                         '  Parameters\n'
                         '    wmsAuthSign    => {6}'.format(
                             client_ip_address, requested_path, channel_number,
                             client_uuid, protocol, target_url,
                             authorization_token))

            response = Utility.make_http_request(
                requests_session.get,
                target_url,
                params={'wmsAuthSign': authorization_token},
                headers=requests_session.headers,
                cookies=requests_session.cookies.get_dict())

            if response.status_code == requests.codes.OK:
                logger.trace(
                    Utility.assemble_response_from_log_message(
                        response, is_content_text=True, do_print_content=True))

                return response.text.replace(
                    'chunks.m3u8?',
                    'chunks.m3u8?channel_number={0}&client_uuid={1}&http_token={2}&'
                    .format(
                        channel_number, client_uuid,
                        urllib.parse.quote(http_token) if http_token else ''))
            else:
                logger.error(
                    Utility.assemble_response_from_log_message(response))

                response.raise_for_status()
        elif protocol == 'mpegts':
            authorization_token = cls._get_session_parameter(
                'authorization_token')

            return '#EXTM3U\n' \
                   '#EXTINF:-1 ,{0}\n' \
                   'https://{1}.smoothstreams.tv:443/{2}/ch{3}q1.stream/mpeg.2ts?' \
                   'wmsAuthSign={4}'.format(SmoothStreamsEPG.get_channel_name(int(channel_number)),
                                            Configuration.get_configuration_parameter(
                                                'SMOOTHSTREAMS_SERVER'),
                                            Configuration.get_configuration_parameter(
                                                'SMOOTHSTREAMS_SERVICE'),
                                            channel_number,
                                            authorization_token)
        elif protocol == 'rtmp':
            authorization_token = cls._get_session_parameter(
                'authorization_token')

            return '#EXTM3U\n' \
                   '#EXTINF:-1 ,{0}\n' \
                   'rtmp://{1}.smoothstreams.tv:3635/{2}/ch{3}q1.stream?' \
                   'wmsAuthSign={4}'.format(SmoothStreamsEPG.get_channel_name(int(channel_number)),
                                            Configuration.get_configuration_parameter(
                                                'SMOOTHSTREAMS_SERVER'),
                                            Configuration.get_configuration_parameter(
                                                'SMOOTHSTREAMS_SERVICE'),
                                            channel_number,
                                            authorization_token)
Exemplo n.º 20
0
    def _refresh_session(cls):
        requests_session = requests.Session()

        if Configuration.get_configuration_parameter(
                'SMOOTHSTREAMS_SERVICE') == 'viewmmasr':
            url = 'https://www.mma-tv.net/loginForm.php'
        else:
            url = 'https://auth.smoothstreams.tv/hash_api.php'

        username = Configuration.get_configuration_parameter(
            'SMOOTHSTREAMS_USERNAME')
        password = SecurityManager.decrypt_password(
            Configuration.get_configuration_parameter(
                'SMOOTHSTREAMS_PASSWORD')).decode()
        site = Configuration.get_configuration_parameter(
            'SMOOTHSTREAMS_SERVICE')

        logger.debug('Retrieving SmoothStreams authorization token\n'
                     'URL => {0}\n'
                     '  Parameters\n'
                     '    username => {0}\n'
                     '    password => {1}\n'
                     '    site     => {2}'.format(url, username,
                                                  '\u2022' * len(password),
                                                  site))

        response = Utility.make_http_request(
            requests_session.get,
            url,
            params={
                'username': username,
                'password': password,
                'site': site
            },
            headers=requests_session.headers,
            cookies=requests_session.cookies.get_dict())

        response_status_code = response.status_code
        if response_status_code != requests.codes.OK and response_status_code != requests.codes.NOT_FOUND:
            logger.error(Utility.assemble_response_from_log_message(response))

            response.raise_for_status()

        logger.trace(
            Utility.assemble_response_from_log_message(response,
                                                       is_content_json=True,
                                                       do_print_content=True))

        authorization_token_response = response.json()
        session = {}

        if 'code' in authorization_token_response:
            if authorization_token_response['code'] == '0':
                logger.error(
                    'Failed to retrieve SmoothStreams authorization token\n'
                    'Error => {0}'.format(
                        authorization_token_response['error']))
            elif authorization_token_response['code'] == '1':
                session['authorization_token'] = authorization_token_response[
                    'hash']
                session['expires_on'] = datetime.now(pytz.utc) + timedelta(
                    seconds=(authorization_token_response['valid'] * 60))
                session['requests_session'] = requests_session

                logger.info('Retrieved SmoothStreams authorization token\n'
                            'Hash       => {0}\n'
                            'Expires On => {1}'.format(
                                session['authorization_token'],
                                session['expires_on'].astimezone(
                                    tzlocal.get_localzone()).strftime(
                                        '%Y-%m-%d %H:%M:%S%z')))
        else:
            logger.error(
                'Failed to retrieve SmoothStreams authorization token\n'
                'Error => JSON response contains no [\'code\'] field')

        if response_status_code != requests.codes.OK:
            response.raise_for_status()

        return session
Exemplo n.º 21
0
    def render_index_template(
        self,
        is_server_secure,
        authorization_required,
        client_ip_address,
        client_uuid,
        guide_number_of_days,
        guide_provider,
        guide_group,
        streaming_protocol,
        active_providers_map_class,
    ):
        client_ip_address_type = Utility.determine_ip_address_type(
            client_ip_address)
        server_hostname = self._configuration['SERVER_HOSTNAME_{0}'.format(
            client_ip_address_type.value)]
        server_http_port = self._configuration['SERVER_HTTP_PORT']
        server_https_port = self._configuration['SERVER_HTTPS_PORT']

        yield self._environment.get_template('index_header.html').render()

        yield self._render_head_template(authorization_required,
                                         guide_number_of_days,
                                         streaming_protocol)

        yield self._environment.get_template('body_header.html').render()

        yield self._render_navigation_bar_div_template(
            guide_provider, guide_group, active_providers_map_class)

        yield self._render_settings_div_template(guide_number_of_days,
                                                 streaming_protocol)

        yield self._environment.get_template('loading_div.html').render()

        yield self._environment.get_template(
            'content_div_header.html').render()

        yield self._environment.get_template('guide_div_header.html').render()

        for rendered_guide_li_template in self._render_guide_lis_template(
                is_server_secure,
                authorization_required,
                client_ip_address_type,
                client_uuid,
                guide_number_of_days,
                guide_provider,
                guide_group,
                active_providers_map_class,
        ):
            yield rendered_guide_li_template

        yield self._environment.get_template('guide_div_footer.html').render()

        yield self._environment.get_template('video_div.html').render()

        yield self._render_recordings_div_template(
            is_server_secure,
            authorization_required,
            client_uuid,
            server_hostname,
            server_https_port if is_server_secure else server_http_port,
        )

        yield self._render_configuration_div_template(
            active_providers_map_class)

        yield self._environment.get_template('monitor_div.html').render()

        yield self._render_about_div_template()

        yield self._environment.get_template(
            'content_div_footer.html').render()

        yield self._environment.get_template('body_footer.html').render()

        yield self._environment.get_template('index_footer.html').render()
Exemplo n.º 22
0
    def download_ts_file(
        cls,
        client_ip_address,
        client_uuid,
        requested_path,
        requested_query_string_parameters,
    ):
        authorization_token = requested_query_string_parameters.get(
            'authorization_token')
        channel_number = requested_query_string_parameters.get(
            'channel_number')
        hostname = requested_query_string_parameters.get('hostname')
        leaf_directory = requested_query_string_parameters.get(
            'leaf_directory')
        port = requested_query_string_parameters.get('port')
        scheme = requested_query_string_parameters.get('scheme')

        IPTVProxy.refresh_serviceable_clients(client_uuid, client_ip_address)
        IPTVProxy.set_serviceable_client_parameter(
            client_uuid, 'last_request_date_time_in_utc',
            datetime.now(pytz.utc))

        username = Configuration.get_configuration_parameter(
            '{0}_USERNAME'.format(cls._provider_name.upper()))
        password = SecurityManager.decrypt_password(
            Configuration.get_configuration_parameter('{0}_PASSWORD'.format(
                cls._provider_name.upper()))).decode()

        requests_session = requests.Session()

        target_url = '{0}://{1}{2}/hls{3}{4}/{5}/{6}/{7}/{8}{9}'.format(
            scheme,
            hostname,
            port,
            'r' if authorization_token else '',
            '/{0}'.format(authorization_token) if authorization_token else '',
            username,
            password,
            channel_number,
            leaf_directory,
            re.sub(r'(/.*)?(/.*\.ts)', r'\2', requested_path),
        )

        logger.debug(
            'Proxying request\n'
            'Source IP      => %s\n'
            'Requested path => %s\n'
            '  Parameters\n'
            '    channel_number => %s\n'
            '    client_uuid    => %s\n'
            '    hostname       => %s\n'
            '    port           => %s\n'
            '    scheme         => %s\n'
            'Target path    => %s',
            client_ip_address,
            requested_path,
            channel_number,
            client_uuid,
            hostname,
            port,
            scheme,
            target_url,
        )

        response = Utility.make_http_request(
            requests_session.get,
            target_url,
            headers=requests_session.headers,
            cookies=requests_session.cookies.get_dict(),
        )

        if response.status_code == requests.codes.OK:
            logger.trace(
                Utility.assemble_response_from_log_message(
                    response, is_content_binary=True))

            IPTVProxy.set_serviceable_client_parameter(
                client_uuid,
                'last_requested_ts_file_path',
                re.sub(r'(/.*)?(/.*\.ts)', r'\2', requested_path)[1:],
            )

            return response.content

        logger.error(Utility.assemble_response_from_log_message(response))

        response.raise_for_status()
Exemplo n.º 23
0
    def download_chunks_m3u8(cls, client_ip_address, client_uuid,
                             requested_path,
                             requested_query_string_parameters):
        authorization_token = requested_query_string_parameters.get(
            'wmsAuthSign')
        channel_number = requested_query_string_parameters.get(
            'channel_number')
        http_token = requested_query_string_parameters.get('http_token')
        nimble_session_id = requested_query_string_parameters.get(
            'nimblesessionid')

        nimble_session_id = cls._map_nimble_session_id(client_ip_address,
                                                       channel_number,
                                                       client_uuid,
                                                       nimble_session_id,
                                                       authorization_token)

        IPTVProxy.refresh_serviceable_clients(client_uuid, client_ip_address)

        authorization_token = cls._get_session_parameter('authorization_token')
        requests_session = cls._get_session_parameter('requests_session')

        target_url = 'https://{0}.smoothstreams.tv/{1}/ch{2}q1.stream{3}'.format(
            Configuration.get_configuration_parameter('SMOOTHSTREAMS_SERVER'),
            Configuration.get_configuration_parameter('SMOOTHSTREAMS_SERVICE'),
            channel_number, re.sub(r'(/.*)?(/.*\.m3u8)', r'\2',
                                   requested_path))

        logger.debug('Proxying request\n'
                     'Source IP      => {0}\n'
                     'Requested path => {1}\n'
                     '  Parameters\n'
                     '    channel_number  => {2}\n'
                     '    client_uuid     => {3}\n'
                     'Target path    => {4}\n'
                     '  Parameters\n'
                     '    nimblesessionid => {5}\n'
                     '    wmsAuthSign     => {6}'.format(
                         client_ip_address, requested_path, channel_number,
                         client_uuid, target_url, nimble_session_id,
                         authorization_token))

        response = Utility.make_http_request(
            requests_session.get,
            target_url,
            params={
                'nimblesessionid': nimble_session_id,
                'wmsAuthSign': authorization_token
            },
            headers=requests_session.headers,
            cookies=requests_session.cookies.get_dict())

        if response.status_code == requests.codes.OK:
            logger.trace(
                Utility.assemble_response_from_log_message(
                    response, is_content_text=True, do_print_content=True))

            return response.text.replace(
                '.ts?',
                '.ts?channel_number={0}&client_uuid={1}&http_token={2}&'.
                format(channel_number, client_uuid,
                       urllib.parse.quote(http_token) if http_token else ''))
        else:
            logger.error(Utility.assemble_response_from_log_message(response))

            response.raise_for_status()
Exemplo n.º 24
0
    def download_chunks_m3u8(cls, client_ip_address, client_uuid, requested_path, requested_query_string_parameters):
        authorization_token = requested_query_string_parameters.get('authorization_token')
        channel_name = requested_query_string_parameters.get('channel_name')
        channel_number = requested_query_string_parameters.get('channel_number')
        http_token = requested_query_string_parameters.get('http_token')
        port = requested_query_string_parameters.get('port')
        server = requested_query_string_parameters.get('server')

        IPTVProxy.refresh_serviceable_clients(client_uuid, client_ip_address)
        IPTVProxy.set_serviceable_client_parameter(client_uuid, 'last_request_date_time_in_utc', datetime.now(pytz.utc))

        requests_session = requests.Session()

        target_url = 'http://{0}{1}/{2}/tracks-v1a1/mono.m3u8'.format(
            server if re.match(r"\A\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\Z", server)
            else '{0}.vaders.tv'.format(server),
            port if port != ':80'
            else '',
            channel_name)

        logger.debug('Proxying request\n'
                     'Source IP      => {0}\n'
                     'Requested path => {1}\n'
                     '  Parameters\n'
                     '    channel_name   => {2}\n'
                     '    channel_number => {3}\n'
                     '    client_uuid    => {4}\n'
                     '    port           => {5}\n'
                     '    server         => {6}\n'
                     'Target path    => {7}\n'
                     '  Parameters\n'
                     '    token          => {8}'.format(client_ip_address,
                                                        requested_path,
                                                        channel_name,
                                                        channel_number,
                                                        client_uuid,
                                                        port,
                                                        server,
                                                        target_url,
                                                        authorization_token))

        response = Utility.make_http_request(requests_session.get,
                                             target_url,
                                             params={
                                                 'token': authorization_token
                                             },
                                             headers=requests_session.headers,
                                             cookies=requests_session.cookies.get_dict())

        if response.status_code == requests.codes.OK:
            logger.trace(Utility.assemble_response_from_log_message(response,
                                                                    is_content_text=True,
                                                                    do_print_content=True))

            with cls._do_reduce_hls_stream_delay_lock.reader_lock:
                if cls._do_reduce_hls_stream_delay:
                    chunks_m3u8 = cls._reduce_hls_stream_delay(response.text,
                                                               client_uuid,
                                                               channel_number,
                                                               number_of_segments_to_keep=3)
                else:
                    chunks_m3u8 = response.text

            IPTVProxy.set_serviceable_client_parameter(client_uuid, 'last_requested_channel_number', channel_number)

            match = re.search(r'http://(.*)\.vaders\.tv(:\d+)?/(.*)/tracks-v1a1/.*', chunks_m3u8)
            if match is None:
                match = re.search(r'http://(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(:\d+)?/(.*)/tracks-v1a1/.*',
                                  chunks_m3u8)
            if match is not None:
                server = match.group(1)
                port = match.group(2) if match.group(2) is not None and len(match.groups()) == 3 else ':80'
                channel_name = match.group(3) if len(match.groups()) == 3 else match.group(2)

            chunks_m3u8 = re.sub('.*/tracks-v1a1/', '', chunks_m3u8)

            return re.sub(r'.ts\?token=(.*)',
                          r'.ts?'
                          r'authorization_token=\1&'
                          'channel_name={0}&'
                          'channel_number={1}&'
                          'client_uuid={2}&'
                          'http_token={3}&'
                          'port={4}&'
                          'server={5}'.format(urllib.parse.quote(channel_name),
                                              channel_number,
                                              client_uuid,
                                              urllib.parse.quote(http_token) if http_token
                                              else '',
                                              urllib.parse.quote(port),
                                              urllib.parse.quote(server)),
                          chunks_m3u8.replace('/', '_'))
        else:
            logger.error(Utility.assemble_response_from_log_message(response))

            response.raise_for_status()
Exemplo n.º 25
0
    def download_playlist_m3u8(
        cls,
        client_ip_address,
        client_uuid,
        requested_path,
        requested_query_string_parameters,
    ):
        channel_number = requested_query_string_parameters.get(
            'channel_number')
        http_token = requested_query_string_parameters.get('http_token')
        protocol = requested_query_string_parameters.get('protocol')

        IPTVProxy.refresh_serviceable_clients(client_uuid, client_ip_address)
        IPTVProxy.set_serviceable_client_parameter(
            client_uuid, 'last_request_date_time_in_utc',
            datetime.now(pytz.utc))
        IPTVProxy.set_serviceable_client_parameter(
            client_uuid, 'last_requested_channel_number', channel_number)

        url = Configuration.get_configuration_parameter('{0}_URL'.format(
            cls._provider_name.upper()))
        username = Configuration.get_configuration_parameter(
            '{0}_USERNAME'.format(cls._provider_name.upper()))
        password = SecurityManager.decrypt_password(
            Configuration.get_configuration_parameter('{0}_PASSWORD'.format(
                cls._provider_name.upper()))).decode()

        if protocol == 'hls':
            requests_session = requests.Session()

            target_url = '{0}live/{1}/{2}/{3}.m3u8'.format(
                url, username, password, channel_number)

            logger.debug(
                'Proxying request\n'
                'Source IP      => %s\n'
                'Requested path => %s\n'
                '  Parameters\n'
                '    channel_number => %s\n'
                '    client_uuid    => %s\n'
                '    protocol       => %s\n'
                'Target path    => %s',
                client_ip_address,
                requested_path,
                channel_number,
                client_uuid,
                protocol,
                target_url,
            )

            response = Utility.make_http_request(
                requests_session.get,
                target_url,
                headers=requests_session.headers,
                cookies=requests_session.cookies.get_dict(),
            )

            if response.status_code == requests.codes.OK:
                logger.trace(
                    Utility.assemble_response_from_log_message(
                        response, is_content_text=True, do_print_content=True))

                with cls._do_reduce_hls_stream_delay_lock.reader_lock:
                    if cls._do_reduce_hls_stream_delay:
                        chunks_m3u8 = cls._reduce_hls_stream_delay(
                            response.text,
                            client_uuid,
                            channel_number,
                            number_of_segments_to_keep=2,
                        )
                    else:
                        chunks_m3u8 = response.text

                parsed_url = urllib.parse.urlparse(response.request.url)
                scheme = parsed_url.scheme
                hostname = parsed_url.hostname
                port = (':{0}'.format(parsed_url.port)
                        if parsed_url.port is not None else '')

                return re.sub(
                    r'/hls/(.*)/(.*)/(.*)/(.*)/(.*).ts',
                    r'\5.ts?'
                    'authorization_token=&'
                    'channel_number={0}&'
                    'client_uuid={1}&'
                    'hostname={2}&'
                    'http_token={3}&'
                    r'leaf_directory=\4&'
                    'port={4}&'
                    'scheme={5}'.format(
                        channel_number,
                        client_uuid,
                        urllib.parse.quote(hostname),
                        urllib.parse.quote(http_token) if http_token else '',
                        urllib.parse.quote(port),
                        scheme,
                    ),
                    chunks_m3u8,
                )
            elif response.status_code == requests.codes.FOUND:
                logger.trace(
                    Utility.assemble_response_from_log_message(
                        response,
                        is_content_text=False,
                        do_print_content=False))

                parsed_url = urllib.parse.urlparse(
                    response.headers['Location'])
                scheme = parsed_url.scheme
                hostname = parsed_url.hostname
                port = (':{0}'.format(parsed_url.port)
                        if parsed_url.port is not None else '')

                return ('#EXTM3U\n'
                        '#EXT-X-VERSION:3\n'
                        '#EXT-X-STREAM-INF:BANDWIDTH=8388608\n'
                        'chunks.m3u8?authorization_token={0}&'
                        'channel_number={1}&'
                        'client_uuid={2}&'
                        'hostname={3}&'
                        'http_token={4}&'
                        'port={5}&'
                        'scheme={6}'.format(
                            dict(urllib.parse.parse_qsl(
                                parsed_url.query))['token'],
                            channel_number,
                            client_uuid,
                            urllib.parse.quote(hostname),
                            urllib.parse.quote(http_token)
                            if http_token else '',
                            urllib.parse.quote(port),
                            scheme,
                        ))

            logger.error(Utility.assemble_response_from_log_message(response))

            response.raise_for_status()
        elif protocol == 'mpegts':
            provider_map_class = ProvidersController.get_provider_map_class(
                cls._provider_name)

            return ('#EXTM3U\n'
                    '#EXTINF:-1 ,{0}\n'
                    '{1}live/{2}/{3}/{4}.ts'
                    ''.format(
                        provider_map_class.epg_class().get_channel_name(
                            int(channel_number)),
                        url,
                        username,
                        password,
                        channel_number,
                    ))
Exemplo n.º 26
0
    def download_chunks_m3u8(cls, client_ip_address, client_uuid,
                             requested_path,
                             requested_query_string_parameters):
        channel_number = requested_query_string_parameters.get(
            'channel_number')
        http_token = requested_query_string_parameters.get('http_token')
        protocol = requested_query_string_parameters.get('protocol')

        IPTVProxy.refresh_serviceable_clients(client_uuid, client_ip_address)
        IPTVProxy.set_serviceable_client_parameter(
            client_uuid, 'last_request_date_time_in_utc',
            datetime.now(pytz.utc))

        username = Configuration.get_configuration_parameter(
            '{0}_USERNAME'.format(cls._provider_name.upper()))
        password = SecurityManager.decrypt_password(
            Configuration.get_configuration_parameter('{0}_PASSWORD'.format(
                cls._provider_name.upper()))).decode()

        if protocol == 'hls':
            requests_session = requests.Session()

            target_url = '{0}live/{1}/{2}/{3}.m3u8'.format(
                ProvidersController.get_provider_map_class(
                    cls._provider_name).constants_class().BASE_URL, username,
                password, channel_number)

            logger.debug('Proxying request\n'
                         'Source IP      => {0}\n'
                         'Requested path => {1}\n'
                         '  Parameters\n'
                         '    channel_number => {2}\n'
                         '    client_uuid    => {3}\n'
                         '    protocol       => {4}\n'
                         'Target path    => {5}'.format(
                             client_ip_address, requested_path, channel_number,
                             client_uuid, protocol, target_url))

            response = Utility.make_http_request(
                requests_session.get,
                target_url,
                headers=requests_session.headers,
                cookies=requests_session.cookies.get_dict())

            if response.status_code == requests.codes.OK:
                logger.trace(
                    Utility.assemble_response_from_log_message(
                        response, is_content_text=True, do_print_content=True))

                with cls._do_reduce_hls_stream_delay_lock.reader_lock:
                    if cls._do_reduce_hls_stream_delay:
                        chunks_m3u8 = cls._reduce_hls_stream_delay(
                            response.text,
                            client_uuid,
                            channel_number,
                            number_of_segments_to_keep=2)
                    else:
                        chunks_m3u8 = response.text

                IPTVProxy.set_serviceable_client_parameter(
                    client_uuid, 'last_requested_channel_number',
                    channel_number)

                match = re.search(
                    r'http://(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(:\d+)?',
                    response.request.url)

                server = match.group(1)
                port = match.group(2) if len(match.groups()) == 2 else ':80'

                return re.sub(
                    r'/hlsr/(.*)/(.*)/(.*)/(.*)/(.*)/(.*).ts', r'\6.ts?'
                    r'authorization_token=\1&'
                    'channel_number={0}&'
                    'client_uuid={1}&'
                    'http_token={2}&'
                    r'leaf_directory=\5&'
                    'port={3}&'
                    'server={4}'.format(
                        channel_number, client_uuid,
                        urllib.parse.quote(http_token) if http_token else '',
                        urllib.parse.quote(port), urllib.parse.quote(server)),
                    chunks_m3u8)
            else:
                logger.error(
                    Utility.assemble_response_from_log_message(response))

                response.raise_for_status()
        elif protocol == 'mpegts':
            provider_map_class = ProvidersController.get_provider_map_class(
                cls._provider_name)

            return '#EXTM3U\n' \
                   '#EXTINF:-1 ,{0}\n' \
                   '{1}{2}/{3}/{4}' \
                   ''.format(provider_map_class.constants_class().BASE_URL,
                             provider_map_class.epg_class().get_channel_name(int(channel_number)),
                             username,
                             password,
                             channel_number)
Exemplo n.º 27
0
    def download_playlist_m3u8(cls, client_ip_address, client_uuid, requested_path, requested_query_string_parameters):
        channel_number = requested_query_string_parameters.get('channel_number')
        http_token = requested_query_string_parameters.get('http_token')
        protocol = requested_query_string_parameters.get('protocol')

        IPTVProxy.refresh_serviceable_clients(client_uuid, client_ip_address)
        IPTVProxy.set_serviceable_client_parameter(client_uuid, 'last_request_date_time_in_utc', datetime.now(pytz.utc))

        authorization_token = cls._calculate_token()

        if protocol == 'hls':
            requests_session = requests.Session()

            target_url = 'http://vapi.vaders.tv/play/{0}.m3u8'.format(channel_number)

            logger.debug('Proxying request\n'
                         'Source IP      => {0}\n'
                         'Requested path => {1}\n'
                         '  Parameters\n'
                         '    channel_number => {2}\n'
                         '    client_uuid    => {3}\n'
                         '    protocol       => {4}\n'
                         'Target path    => {5}\n'
                         '  Parameters\n'
                         '    token          => {6}'.format(client_ip_address,
                                                            requested_path,
                                                            channel_number,
                                                            client_uuid,
                                                            protocol,
                                                            target_url,
                                                            authorization_token))

            response = Utility.make_http_request(requests_session.get,
                                                 target_url,
                                                 params={
                                                     'token': authorization_token
                                                 },
                                                 headers=requests_session.headers,
                                                 cookies=requests_session.cookies.get_dict())

            if response.status_code == requests.codes.OK:
                logger.trace(Utility.assemble_response_from_log_message(response,
                                                                        is_content_text=True,
                                                                        do_print_content=True))

                match = re.search(r'http://(.*)\.vaders\.tv(:\d+)?/(.*)/tracks-v1a1/.*', response.text)
                if match is None:
                    match = re.search(r'http://(.*)\.vaders\.tv(:\d+)?/(.*)/index.m3u8.*', response.request.url)
                    if match is None:
                        match = re.search(r'http://(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(:\d+)?/(.*)/index.m3u8.*',
                                          response.request.url)

                server = match.group(1)
                port = match.group(2) if match.group(2) is not None and len(match.groups()) == 3 else ':80'
                channel_name = match.group(3) if len(match.groups()) == 3 else match.group(2)

                return re.sub(r'tracks-v1a1/mono.m3u8\?token=(.*)',
                              'chunks.m3u8?'
                              r'authorization_token=\1&'
                              'channel_name={0}&'
                              'channel_number={1}&'
                              'client_uuid={2}&'
                              'http_token={3}&'
                              'port={4}&'
                              'server={5}'.format(urllib.parse.quote(channel_name),
                                                  channel_number,
                                                  client_uuid,
                                                  urllib.parse.quote(http_token) if http_token
                                                  else '',
                                                  urllib.parse.quote(port),
                                                  urllib.parse.quote(server)),
                              re.sub('.*/tracks-v1a1/', 'tracks-v1a1/', response.text))
            else:
                logger.error(Utility.assemble_response_from_log_message(response))

                response.raise_for_status()
        elif protocol == 'mpegts':
            return '#EXTM3U\n' \
                   '#EXTINF:-1 ,{0}\n' \
                   'http://vapi.vaders.tv/play/{1}.ts?' \
                   'token={2}'.format(VaderStreamsEPG.get_channel_name(int(channel_number)),
                                      channel_number,
                                      authorization_token)
Exemplo n.º 28
0
    def run(self):
        logger.info(
            'Starting recording\n'
            'Provider          => {0}\n'
            'Channel number    => {1}\n'
            'Channel name      => {2}\n'
            'Program title     => {3}\n'
            'Start date & time => {4}\n'
            'End date & time   => {5}'.format(
                self._recording.provider, self._recording.channel_number,
                self._recording.channel_name, self._recording.program_title,
                self._recording.start_date_time_in_utc.astimezone(
                    tzlocal.get_localzone()).strftime('%Y-%m-%d %H:%M:%S'),
                self._recording.end_date_time_in_utc.astimezone(
                    tzlocal.get_localzone()).strftime('%Y-%m-%d %H:%M:%S')))

        self._create_recording_directory_tree()

        try:
            hls_client = HLSClient(self._id, self._recording.provider.lower(),
                                   self._recording.channel_number)

            playlist_m3u8_object = m3u8.loads(
                hls_client.download_playlist_m3u8())
            chunks_m3u8_object = None

            try:
                chunks_url = '/live/{0}/{1}'.format(
                    self._recording.provider.lower(),
                    playlist_m3u8_object.data['playlists'][0]['uri'])
            except IndexError:
                chunks_m3u8_object = playlist_m3u8_object

            downloaded_segment_file_names = []

            while not self._stop_recording_event.is_set():
                try:
                    chunks_m3u8_object = m3u8.loads(
                        hls_client.download_chunks_m3u8(chunks_url))
                except NameError:
                    if chunks_m3u8_object is None:
                        chunks_m3u8_object = m3u8.loads(
                            hls_client.download_playlist_m3u8())

                chunks_m3u8_download_date_time_in_utc = datetime.now(pytz.utc)
                chunks_m3u8_total_duration = 0

                for (segment_index,
                     segment) in enumerate(chunks_m3u8_object.segments):
                    segment_url = '/live/{0}'.format(segment.uri)
                    segment_url_components = urllib.parse.urlparse(segment_url)
                    segment_file_name = re.sub(r'(/.*)?(/)(.*\.ts)', r'\3',
                                               segment_url_components.path)

                    if segment_file_name not in downloaded_segment_file_names:
                        try:
                            ts_file_content = CacheManager.query_cache(
                                self._recording.channel_number,
                                segment_file_name.lower())
                            if ts_file_content is None:
                                ts_file_content = hls_client.download_ts_file(
                                    segment_url)

                                CacheManager.update_cache(
                                    self._recording.channel_number,
                                    segment_file_name.lower(), ts_file_content)

                                logger.debug(
                                    'Downloaded segment\n'
                                    'Segment => {0}'.format(segment_file_name))

                            segment.uri = '{0}?recording_id={1}'.format(
                                segment_file_name,
                                urllib.parse.quote(self._recording.id))
                            downloaded_segment_file_names.append(
                                segment_file_name)

                            Utility.write_file(
                                ts_file_content,
                                os.path.join(self._recording_directory_path,
                                             segment_file_name),
                                in_binary=True)

                            with Database.get_write_lock():
                                db_session = Database.create_session()

                                try:
                                    db_session.add(
                                        Segment(
                                            segment_file_name,
                                            self._recording.id,
                                            pickle.dumps(segment,
                                                         protocol=pickle.
                                                         HIGHEST_PROTOCOL),
                                            self._recording_directory_path))
                                    db_session.commit()
                                except Exception:
                                    (type_, value_,
                                     traceback_) = sys.exc_info()
                                    logger.error('\n'.join(
                                        traceback.format_exception(
                                            type_, value_, traceback_)))

                                    db_session.rollback()
                                finally:
                                    db_session.close()
                        except requests.exceptions.HTTPError:
                            logger.error(
                                'Failed to download segment\n'
                                'Segment => {0}'.format(segment_file_name))
                    else:
                        logger.debug(
                            'Skipped segment since it was already downloaded\n'
                            'Segment => {0} '.format(segment_file_name))

                    chunks_m3u8_total_duration += segment.duration

                current_date_time_in_utc = datetime.now(pytz.utc)
                wait_duration = chunks_m3u8_total_duration - (
                    current_date_time_in_utc -
                    chunks_m3u8_download_date_time_in_utc).total_seconds()
                if wait_duration > 0:
                    self._stop_recording_event.wait(wait_duration)

                chunks_m3u8_object = None

            self._recording.status = RecordingStatus.PERSISTED.value

            db_session.merge(self._recording)
            db_session.commit()

            logger.info(
                'Finished recording\n'
                'Provider          => {0}\n'
                'Channel number    => {1}\n'
                'Channel name      => {2}\n'
                'Program title     => {3}\n'
                'Start date & time => {4}\n'
                'End date & time   => {5}'.format(
                    self._recording.provider, self._recording.channel_number,
                    self._recording.channel_name,
                    self._recording.program_title,
                    self._recording.start_date_time_in_utc.astimezone(
                        tzlocal.get_localzone()).strftime('%Y-%m-%d %H:%M:%S'),
                    self._recording.end_date_time_in_utc.astimezone(
                        tzlocal.get_localzone()).strftime(
                            '%Y-%m-%d %H:%M:%S')))
        except (HLSPlaylistDownloadError, ProviderNotFoundError):
            if self._stop_recording_event.is_set():
                self._recording.status = RecordingStatus.PERSISTED.value

                db_session.merge(self._recording)
                db_session.commit()

                logger.info(
                    'Finished recording\n'
                    'Provider          => {0}\n'
                    'Channel number    => {1}\n'
                    'Channel name      => {2}\n'
                    'Program title     => {3}\n'
                    'Start date & time => {4}\n'
                    'End date & time   => {5}'.format(
                        self._recording.provider,
                        self._recording.channel_number,
                        self._recording.channel_name,
                        self._recording.program_title,
                        self._recording.start_date_time_in_utc.astimezone(
                            tzlocal.get_localzone()).strftime(
                                '%Y-%m-%d %H:%M:%S'),
                        self._recording.end_date_time_in_utc.astimezone(
                            tzlocal.get_localzone()).strftime(
                                '%Y-%m-%d %H:%M:%S')))
            else:
                logger.info(
                    'Canceling recording\n'
                    'Provider          => {0}\n'
                    'Channel number    => {1}\n'
                    'Channel name      => {2}\n'
                    'Program title     => {3}\n'
                    'Start date & time => {4}\n'
                    'End date & time   => {5}'.format(
                        self._recording.provider,
                        self._recording.channel_number,
                        self._recording.channel_name,
                        self._recording.program_title,
                        self._recording.start_date_time_in_utc.astimezone(
                            tzlocal.get_localzone()).strftime(
                                '%Y-%m-%d %H:%M:%S'),
                        self._recording.end_date_time_in_utc.astimezone(
                            tzlocal.get_localzone()).strftime(
                                '%Y-%m-%d %H:%M:%S')))
        finally:
            PVR.cleanup_live_recording(self._recording)
Exemplo n.º 29
0
    def download_chunks_m3u8(
        cls,
        client_ip_address,
        client_uuid,
        requested_path,
        requested_query_string_parameters,
    ):
        authorization_token = requested_query_string_parameters.get(
            'authorization_token')
        channel_number = requested_query_string_parameters.get(
            'channel_number')
        client_uuid = requested_query_string_parameters.get('client_uuid')
        hostname = requested_query_string_parameters.get('hostname')
        http_token = requested_query_string_parameters.get('http_token')
        port = requested_query_string_parameters.get('port')
        scheme = requested_query_string_parameters.get('scheme')

        IPTVProxy.refresh_serviceable_clients(client_uuid, client_ip_address)
        IPTVProxy.set_serviceable_client_parameter(
            client_uuid, 'last_request_date_time_in_utc',
            datetime.now(pytz.utc))
        IPTVProxy.set_serviceable_client_parameter(
            client_uuid, 'last_requested_channel_number', channel_number)

        username = Configuration.get_configuration_parameter(
            '{0}_USERNAME'.format(cls._provider_name.upper()))
        password = SecurityManager.decrypt_password(
            Configuration.get_configuration_parameter('{0}_PASSWORD'.format(
                cls._provider_name.upper()))).decode()

        requests_session = requests.Session()

        target_url = '{0}://{1}{2}/live/{3}/{4}/{5}.m3u8'.format(
            scheme, hostname, port, username, password, channel_number)

        logger.debug(
            'Proxying request\n'
            'Source IP      => %s\n'
            'Requested path => %s\n'
            '  Parameters\n'
            '    authorization_token => %s\n'
            '    channel_number  => %s\n'
            '    client_uuid     => %s\n'
            '    hostname        => %s\n'
            '    port            => %s\n'
            '    scheme          => %s\n'
            'Target path    => %s\n'
            '  Parameters\n'
            '    token => %s',
            client_ip_address,
            requested_path,
            authorization_token,
            channel_number,
            client_uuid,
            hostname,
            port,
            scheme,
            target_url,
            authorization_token,
        )

        response = Utility.make_http_request(
            requests_session.get,
            target_url,
            params={'token': authorization_token},
            headers=requests_session.headers,
            cookies=requests_session.cookies.get_dict(),
        )

        if response.status_code == requests.codes.OK:
            logger.trace(
                Utility.assemble_response_from_log_message(
                    response, is_content_text=True, do_print_content=True))

            with cls._do_reduce_hls_stream_delay_lock.reader_lock:
                if cls._do_reduce_hls_stream_delay:
                    chunks_m3u8 = cls._reduce_hls_stream_delay(
                        response.text,
                        client_uuid,
                        channel_number,
                        number_of_segments_to_keep=2,
                    )
                else:
                    chunks_m3u8 = response.text

                return re.sub(
                    r'/hlsr/(.*)/(.*)/(.*)/(.*)/(.*)/(.*).ts',
                    r'\6.ts?'
                    r'authorization_token=\1&'
                    'channel_number={0}&'
                    'client_uuid={1}&'
                    'hostname={2}&'
                    'http_token={3}&'
                    r'leaf_directory=\5&'
                    'port={4}&'
                    'scheme={5}'.format(
                        channel_number,
                        client_uuid,
                        urllib.parse.quote(hostname),
                        urllib.parse.quote(http_token) if http_token else '',
                        urllib.parse.quote(port),
                        scheme,
                    ),
                    chunks_m3u8,
                )
        else:
            logger.error(Utility.assemble_response_from_log_message(response))

            response.raise_for_status()