def generate_vod_recording_playlist_m3u8(cls, client_uuid, recording_id, http_token): db_session = Database.create_session() try: vod_playlist_m3u8_object = M3U8() vod_playlist_m3u8_object.media_sequence = 0 vod_playlist_m3u8_object.version = '3' vod_playlist_m3u8_object.target_duration = 0 vod_playlist_m3u8_object.playlist_type = 'VOD' for segment_row in DatabaseAccess.query_segment_pickle( db_session, recording_id): segment = pickle.loads(segment_row.pickle) if segment.duration > vod_playlist_m3u8_object.target_duration: vod_playlist_m3u8_object.target_duration = math.ceil( segment.duration) vod_playlist_m3u8_object.add_segment(segment) return re.sub( r'(\.ts\?)(.*)', r'\1client_uuid={0}&http_token={1}&\2'.format( client_uuid, urllib.parse.quote(http_token) if http_token else ''), '{0}\n' '{1}'.format(vod_playlist_m3u8_object.dumps(), '#EXT-X-ENDLIST')) finally: db_session.close()
def get_recording(cls, db_session, recording_id): recording = DatabaseAccess.query_recording(db_session, recording_id) if recording is not None: return recording raise RecordingNotFoundError
def _start_recording(cls): current_date_time_in_utc = datetime.now(pytz.utc) with Database.get_write_lock(): db_session = Database.create_session() try: for scheduled_recording in DatabaseAccess.query_scheduled_recordings( db_session): scheduled_recording_start_date_time_in_utc = scheduled_recording.start_date_time_in_utc if current_date_time_in_utc > scheduled_recording_start_date_time_in_utc: scheduled_recording.status = RecordingStatus.LIVE.value with cls._live_recordings_to_recording_thread_lock: cls._live_recordings_to_recording_thread[ scheduled_recording.id] = RecordingThread( scheduled_recording) cls._live_recordings_to_recording_thread[ scheduled_recording.id].start() db_session.commit() cls._set_start_recording_timer(db_session) 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()
def delete_recording(cls, db_session, recording): DatabaseAccess.delete_recording(db_session, recording.id) for segment_row in DatabaseAccess.query_segments_directory_path( db_session, recording.id): try: shutil.rmtree(segment_row.directory_path) except OSError: pass DatabaseAccess.delete_segments(db_session, recording.id) db_session.flush() if not db_session.deleted: if recording.status == RecordingStatus.SCHEDULED.value: cls._set_start_recording_timer(db_session) else: raise RecordingNotFoundError
def stop_live_recording(cls, db_session, recording): with cls._live_recordings_to_recording_thread_lock: try: cls._live_recordings_to_recording_thread[ recording.id].force_stop() del cls._live_recordings_to_recording_thread[recording.id] except KeyError: recording = DatabaseAccess.query_recording( db_session, recording.id) if recording is not None: if recording.status == RecordingStatus.LIVE.value: segment_row = DatabaseAccess.query_segments_count( db_session, recording.id) if segment_row is not None and segment_row.count > 0: recording.status = RecordingStatus.PERSISTED.value else: raise RecordingNotFoundError
def get_recordings(cls): recordings = [] db_session = Database.create_session() try: for recording in DatabaseAccess.query_recordings(db_session): recordings.append(recording) finally: db_session.close() return recordings
def _initialize_fernet_key(cls): db_session = Database.create_session() try: password_encryption_key_setting = DatabaseAccess.query_setting( db_session, 'password_encryption_key') if password_encryption_key_setting is not None: fernet_key = password_encryption_key_setting.value.encode() cls._fernet = Fernet(fernet_key) finally: db_session.close()
def _restart_live_recordings(cls, db_session): current_date_time_in_utc = datetime.now(pytz.utc) live_recordings_to_recording_thread = {} for live_recording in DatabaseAccess.query_live_recordings(db_session): if live_recording.end_date_time_in_utc > current_date_time_in_utc: live_recordings_to_recording_thread[ live_recording.id] = RecordingThread(live_recording) live_recordings_to_recording_thread[live_recording.id].start() cls._set_live_recordings_to_recording_thread( live_recordings_to_recording_thread)
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
def get_recording_program_title(cls, recording_id): db_session = Database.create_session() try: recording = DatabaseAccess.query_recording(db_session, recording_id) if recording is not None: program_title = recording.program_title else: program_title = 'Recording {0}'.format(recording_id) return program_title finally: db_session.close()
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()
def _set_start_recording_timer(cls, db_session): with cls._start_recording_timer_lock: if cls._start_recording_timer: cls._start_recording_timer.cancel() soonest_scheduled_recording_start_date_time_in_utc = None current_date_time_in_utc = datetime.now(pytz.utc) for scheduled_recording in DatabaseAccess.query_scheduled_recordings( db_session): scheduled_recording_start_date_time_in_utc = ( scheduled_recording.start_date_time_in_utc) if (current_date_time_in_utc > scheduled_recording_start_date_time_in_utc): scheduled_recording.status = RecordingStatus.LIVE.value with cls._live_recordings_to_recording_thread_lock: cls._live_recordings_to_recording_thread[ scheduled_recording.id] = RecordingThread( scheduled_recording) cls._live_recordings_to_recording_thread[ scheduled_recording.id].start() elif not soonest_scheduled_recording_start_date_time_in_utc: soonest_scheduled_recording_start_date_time_in_utc = ( scheduled_recording_start_date_time_in_utc) elif (soonest_scheduled_recording_start_date_time_in_utc > scheduled_recording_start_date_time_in_utc): soonest_scheduled_recording_start_date_time_in_utc = ( scheduled_recording_start_date_time_in_utc) if soonest_scheduled_recording_start_date_time_in_utc: interval = ( soonest_scheduled_recording_start_date_time_in_utc - datetime.now(pytz.utc)).total_seconds() cls._start_recording_timer = Timer(interval, cls._start_recording) cls._start_recording_timer.daemon = True cls._start_recording_timer.start() logger.debug( 'Starting recording timer\nInterval => %s seconds', interval)
def _initialize_recordings(cls, db_session): deleted_recordings_log_message = [] loaded_recordings_log_message = [] unformatted_message_to_log = 'Provider => {0}\n' \ 'Channel number => {1}\n' \ 'Channel name => {2}\n' \ 'Program title => {3}\n' \ 'Start date & time => {4}\n' \ 'End date & time => {5}\n' \ 'Status => {6}\n' for recording in DatabaseAccess.query_recordings(db_session): current_date_time_in_utc = datetime.now(pytz.utc) formatted_message_to_log = unformatted_message_to_log.format( recording.provider, recording.channel_number, recording.channel_name, recording.program_title, recording.start_date_time_in_utc.astimezone( tzlocal.get_localzone()).strftime('%Y-%m-%d %H:%M:%S%z'), recording.end_date_time_in_utc.astimezone( tzlocal.get_localzone()).strftime('%Y-%m-%d %H:%M:%S%z'), recording.status) if current_date_time_in_utc >= recording.end_date_time_in_utc: if recording.status == RecordingStatus.LIVE.value: segment_row = DatabaseAccess.query_segments_count( db_session, recording.id) if segment_row is not None and segment_row.count > 0: recording.status = RecordingStatus.PERSISTED.value loaded_recordings_log_message.append( formatted_message_to_log) else: DatabaseAccess.delete_recording( db_session, recording.id) deleted_recordings_log_message.append( formatted_message_to_log) elif recording.status == RecordingStatus.SCHEDULED.value: DatabaseAccess.delete_recording(db_session, recording.id) deleted_recordings_log_message.append( formatted_message_to_log) else: loaded_recordings_log_message.append(formatted_message_to_log) if deleted_recordings_log_message: deleted_recordings_log_message.insert( 0, 'Deleted expired recording{0}\n'.format( 's' if len(deleted_recordings_log_message) > 1 else '')) logger.debug('\n'.join(deleted_recordings_log_message).strip()) if loaded_recordings_log_message: loaded_recordings_log_message.insert( 0, 'Loaded recording{0}\n'.format( 's' if len(loaded_recordings_log_message) > 1 else '')) logger.debug('\n'.join(loaded_recordings_log_message).strip())
def delete_setting(cls, db_session, setting_name): DatabaseAccess.delete_setting(db_session, setting_name)
def query_settings(cls, db_session): return DatabaseAccess.query_settings(db_session)
def query_setting(cls, db_session, setting_name): return DatabaseAccess.query_setting(db_session, setting_name)