async def handle_callback(event): choice = event.data msg = await event.get_message() sender = await get_sender_info(event) message = await msg.get_reply_message() if choice == NORMAL: resize_func = normal_resize elif choice == SEAM_CARVING: resize_func = seam_carving_resize else: logger.warning(f'Get wrong resize func: {choice} from {sender!r}') return await event.edit('Request accepted, downloading...') with BytesIO() as in_f, BytesIO() as out_f: success = await client.download_media(message, file=in_f) await event.edit('File received, processing') if success is None: logger.info('Fail to download media') await event.edit('Fail to download media') return in_f.flush() in_f.seek(0) if not is_image(in_f): logger.debug("Media is not image") await event.edit('Media is not image') return in_f.seek(0) with ProcessPoolExecutor() as pool: new_img = await client.loop.run_in_executor( pool, resize_func, in_f) file_name, ext = splitext(get_media_filename(message.media)) out_f.name = f'Resized_{file_name}.png' is_success, im_buf_arr = cv2.imencode('.png', new_img) if not is_success: logger.debug("OpenCV can't transform img to bytes") return out_f.write(im_buf_arr.tobytes()) out_f.flush() out_f.seek(0) prompt = f'Sending Resized File:{out_f.name!r}' logger.debug(f'{prompt} to {sender!r}') await event.edit(text=prompt) await message.reply(file=out_f, force_document=True) await event.edit('Finished')
def check_output_exists(data_dir, ytid, ts_start, ts_end, audio_only=False, video_format='mp4', audio_format='flac'): # Skip files that already have been downloaded media_filename = get_media_filename(ytid, ts_start, ts_end) video_filepath = os.path.join(data_dir, 'video', media_filename + '.' + video_format) audio_filepath = os.path.join(data_dir, 'audio', media_filename + '.' + audio_format) output_exists = False audio_exists = os.path.exists(audio_filepath) if audio_only: output_exists = audio_exists else: output_exists = audio_exists and os.path.exists(video_filepath) return output_exists
def download_subset_videos(subset_path, data_dir, ffmpeg_path, ffprobe_path, num_workers, **ffmpeg_cfg): """ Download subset segment file and videos Args: subset_path: Path to subset segments file (Type: str) data_dir: Directory where dataset files will be saved (Type: str) ffmpeg_path: Path to ffmpeg executable (Type: str) ffprobe_path: Path to ffprobe executable (Type: str) num_workers: Number of multiprocessing workers used to download videos (Type: int) Keyword Args: **ffmpeg_cfg: Configuration for audio and video downloading and decoding done by ffmpeg (Type: dict[str, *]) """ subset_name = get_subset_name(subset_path) LOGGER.info('Starting download jobs for subset "{}"'.format(subset_name)) with open(subset_path, 'r') as f: subset_data = csv.reader(f) # Set up multiprocessing pool pool = mp.Pool(num_workers) try: for row_idx, row in enumerate(subset_data): # Skip commented lines if row[0][0] == '#': continue ytid, ts_start, ts_end = row[0], float(row[1]), float(row[2]) # Skip files that already have been downloaded media_filename = get_media_filename(ytid, ts_start, ts_end) video_filepath = os.path.join( data_dir, 'video', media_filename + '.' + ffmpeg_cfg.get('video_format', 'mp4')) audio_filepath = os.path.join( data_dir, 'audio', media_filename + '.' + ffmpeg_cfg.get('audio_format', 'flac')) if os.path.exists(video_filepath) and os.path.exists( audio_filepath): info_msg = 'Already downloaded video {} ({} - {}). Skipping.' LOGGER.info(info_msg.format(ytid, ts_start, ts_end)) continue # Skip files that are neither Applause nor Speech with open('filemove/both_id.txt', 'r+') as f: if ytid in f.read(): print("downloaded sth meaningful!" + ytid) else: # print("skip" + ytid) continue worker_args = [ ytid, ts_start, ts_end, data_dir, ffmpeg_path, ffprobe_path ] pool.apply_async(partial(segment_mp_worker, **ffmpeg_cfg), worker_args) # Run serially #segment_mp_worker(*worker_args, **ffmpeg_cfg) except csv.Error as e: err_msg = 'Encountered error in {} at line {}: {}' LOGGER.error(err_msg) sys.exit(err_msg.format(subset_path, row_idx + 1, e)) except KeyboardInterrupt: LOGGER.info("Forcing exit.") exit() finally: try: pool.close() pool.join() except KeyboardInterrupt: LOGGER.info("Forcing exit.") exit() LOGGER.info('Finished download jobs for subset "{}"'.format(subset_name))
def download_yt_video(ytid, ts_start, ts_end, output_dir, ffmpeg_path, ffprobe_path, audio_codec='flac', audio_format='flac', audio_sample_rate=48000, audio_bit_depth=16, video_codec='h264', video_format='mp4', video_mode='bestvideoaudio', video_frame_rate=30, num_retries=10): """ Download a Youtube video (with the audio and video separated). The audio will be saved in <output_dir>/audio and the video will be saved in <output_dir>/video. The output filename is of the format: <YouTube ID>_<start time in ms>_<end time in ms>.<extension> Args: ytid: Youtube ID string (Type: str) ts_start: Segment start time (in seconds) (Type: float) ts_start: Segment end time (in seconds) (Type: float) output_dir: Output directory where video will be saved (Type: str) ffmpeg_path: Path to ffmpeg executable (Type: str) ffprobe_path: Path to ffprobe executable (Type: str) Keyword Args: audio_codec: Name of audio codec used by ffmpeg to encode output audio (Type: str) audio_format: Name of audio container format used for output audio (Type: str) audio_sample_rate: Target audio sample rate (in Hz) (Type: int) audio_bit_depth: Target audio sample bit depth (Type: int) video_codec: Name of video codec used by ffmpeg to encode output video (Type: str) video_format: Name of video container format used for output video (Type: str) video_mode: Name of the method in which video is downloaded. 'bestvideo' obtains the best quality video that does not contain an audio stream. 'bestvideoaudio' obtains the best quality video that contains an audio stream. 'bestvideowithaudio' obtains the best quality video without an audio stream and merges it with audio stream. (Type: bool) video_frame_rate: Target video frame rate (in fps) (Type: int) num_retries: Number of attempts to download and process an audio or video file with ffmpeg (Type: int) Returns: video_filepath: Filepath to video file (Type: str) audio_filepath: Filepath to audio file (Type: str) """ # Compute some things from the segment boundaries duration = ts_end - ts_start # Make the output format and video URL # Output format is in the format: # <YouTube ID>_<start time in ms>_<end time in ms>.<extension> media_filename = get_media_filename(ytid, ts_start, ts_end) video_filepath = os.path.join(output_dir, 'video', media_filename + '.' + video_format) audio_filepath = os.path.join(output_dir, 'audio', media_filename + '.' + audio_format) video_page_url = 'https://www.youtube.com/watch?v={}'.format(ytid) # Get the direct URLs to the videos with best audio and with best video (with audio) video = pafy.new(video_page_url) video_duration = video.length end_past_video_end = False if ts_end > video_duration: warn_msg = "End time for segment ({} - {}) of video {} extends past end of video (length {} sec)" LOGGER.warning(warn_msg.format(ts_start, ts_end, ytid, video_duration)) duration = video_duration - ts_start ts_end = ts_start + duration end_past_video_end = True if video_mode in ('bestvideo', 'bestvideowithaudio'): best_video = video.getbestvideo() # If there isn't a video only option, go with best video with audio if best_video is None: best_video = video.getbest() elif video_mode in ('bestvideoaudio', 'bestvideoaudionoaudio'): best_video = video.getbest() else: raise ValueError('Invalid video mode: {}'.format(video_mode)) best_audio = video.getbestaudio() best_video_url = best_video.url best_audio_url = best_audio.url audio_info = { 'sample_rate': audio_sample_rate, 'channels': 2, 'bitrate': audio_bit_depth, 'encoding': audio_codec.upper(), 'duration': duration } video_info = { "r_frame_rate": "{}/1".format(video_frame_rate), "avg_frame_rate": "{}/1".format(video_frame_rate), 'codec_name': video_codec.lower(), 'duration': duration } # Download the audio audio_input_args = ['-n', '-ss', str(ts_start)] audio_output_args = [ '-t', str(duration), '-ar', str(audio_sample_rate), '-vn', '-ac', str(audio_info['channels']), '-sample_fmt', 's{}'.format(audio_bit_depth), '-f', audio_format, '-acodec', audio_codec ] ffmpeg(ffmpeg_path, best_audio_url, audio_filepath, input_args=audio_input_args, output_args=audio_output_args, num_retries=num_retries, validation_callback=validate_audio, validation_args={ 'audio_info': audio_info, 'end_past_video_end': end_past_video_end }) if video_mode != 'bestvideowithaudio': # Download the video video_input_args = ['-n', '-ss', str(ts_start)] video_output_args = [ '-t', str(duration), '-f', video_format, '-r', str(video_frame_rate), '-vcodec', video_codec ] # Suppress audio stream if we don't want to audio in the video if video_mode in ('bestvideo', 'bestvideoaudionoaudio'): video_output_args.append('-an') ffmpeg(ffmpeg_path, best_video_url, video_filepath, input_args=video_input_args, output_args=video_output_args, num_retries=num_retries, validation_callback=validate_video, validation_args={ 'ffprobe_path': ffprobe_path, 'video_info': video_info, 'end_past_video_end': end_past_video_end }) else: # Download the best quality video, in lossless encoding if video_codec != 'h264': error_msg = 'Not currently supporting merging of best quality video with video for codec: {}' raise NotImplementedError(error_msg.format(video_codec)) video_input_args = ['-n', '-ss', str(ts_start)] video_output_args = [ '-t', str(duration), '-f', video_format, '-crf', '0', '-preset', 'medium', '-r', str(video_frame_rate), '-an', '-vcodec', video_codec ] ffmpeg(ffmpeg_path, best_video_url, video_filepath, input_args=video_input_args, output_args=video_output_args, num_retries=num_retries) # Merge the best lossless video with the lossless audio, and compress merge_video_filepath = os.path.splitext(video_filepath)[0] \ + '_merge.' + video_format video_input_args = ['-n'] video_output_args = [ '-f', video_format, '-r', str(video_frame_rate), '-vcodec', video_codec, '-acodec', 'aac', '-ar', str(audio_sample_rate), '-ac', str(audio_info['channels']), '-strict', 'experimental' ] ffmpeg(ffmpeg_path, [video_filepath, audio_filepath], merge_video_filepath, input_args=video_input_args, output_args=video_output_args, num_retries=num_retries, validation_callback=validate_video, validation_args={ 'ffprobe_path': ffprobe_path, 'video_info': video_info, 'end_past_video_end': end_past_video_end }) # Remove the original video file and replace with the merged version if os.path.exists(merge_video_filepath): os.remove(video_filepath) shutil.move(merge_video_filepath, video_filepath) else: error_msg = 'Cannot find merged video for {} ({} - {}) at {}' LOGGER.error( error_msg.format(ytid, ts_start, ts_end, merge_video_filepath)) LOGGER.info('Downloaded video {} ({} - {})'.format(ytid, ts_start, ts_end)) return video_filepath, audio_filepath
def download_yt_video(ytid, ts_start, ts_end, output_dir, ffmpeg_path, ffprobe_path, audio_codec='flac', audio_format='flac', audio_sample_rate=48000, audio_bit_depth=16, video_codec='h264', video_format='mp4', video_mode='bestvideoaudio', video_frame_rate=30, num_retries=10): """ Download a Youtube video (with the audio and video separated). The audio will be saved in <output_dir>/audio and the video will be saved in <output_dir>/video. The output filename is of the format: <YouTube ID>_<start time in ms>_<end time in ms>.<extension> Args: ytid: Youtube ID string (Type: str) ts_start: Segment start time (in seconds) (Type: float) ts_start: Segment end time (in seconds) (Type: float) output_dir: Output directory where video will be saved (Type: str) ffmpeg_path: Path to ffmpeg executable (Type: str) ffprobe_path: Path to ffprobe executable (Type: str) Keyword Args: audio_codec: Name of audio codec used by ffmpeg to encode output audio (Type: str) audio_format: Name of audio container format used for output audio (Type: str) audio_sample_rate: Target audio sample rate (in Hz) (Type: int) audio_bit_depth: Target audio sample bit depth (Type: int) video_codec: Name of video codec used by ffmpeg to encode output video (Type: str) video_format: Name of video container format used for output video (Type: str) video_mode: Name of the method in which video is downloaded. 'bestvideo' obtains the best quality video that does not contain an audio stream. 'bestvideoaudio' obtains the best quality video that contains an audio stream. 'bestvideowithaudio' obtains the best quality video without an audio stream and merges it with audio stream. (Type: bool) video_frame_rate: Target video frame rate (in fps) (Type: int) num_retries: Number of attempts to download and process an audio or video file with ffmpeg (Type: int) Returns: video_filepath: Filepath to video file (Type: str) audio_filepath: Filepath to audio file (Type: str) """ # Compute some things from the segment boundaries duration = ts_end - ts_start # Make the output format and video URL # Output format is in the format: # <YouTube ID>_<start time in ms>_<end time in ms>.<extension> media_filename = get_media_filename(ytid, ts_start, ts_end) video_filepath = os.path.join(output_dir, 'video', media_filename + '.' + video_format) audio_filepath = os.path.join(output_dir, 'audio', media_filename + '.' + audio_format) video_page_url = 'https://www.youtube.com/watch?v={}'.format(ytid) # Get the direct URLs to the videos with best audio and with best video (with audio) video = YouTube(video_page_url) video_duration = video.length end_past_video_end = True if ts_end > video_duration: warn_msg = "End time for segment ({} - {}) of video {} extends past end of video (length {} sec)" LOGGER.warning(warn_msg.format(ts_start, ts_end, ytid, video_duration)) duration = video_duration - ts_start ts_end = ts_start + duration end_past_video_end = True if video_mode in ('bestvideo', 'bestvideowithaudio'): best_video = video.streams.get_highest_resolution() elif video_mode == 'audioonly': best_video = video.streams.get_audio_only() else: best_video = video.streams.get_first() audio_info = { 'sample_rate': audio_sample_rate, 'channels': 2, 'duration': duration } video_info = { "r_frame_rate": "{}/1".format(video_frame_rate), "avg_frame_rate": "{}/1".format(video_frame_rate), 'codec_name': video_codec.lower(), 'duration': duration } # Download the audio download_tmp_file = f"audio_{ytid}.mp4" downloaded_file_path = best_video.download(filename=download_tmp_file) audio_input_args = ['-n', '-ss', str(ts_start)] audio_output_args = [ '-t', str(duration), '-ar', str(audio_sample_rate), '-vn', '-ac', str(audio_info['channels']), #'-sample_fmt', 's{}'.format(audio_bit_depth), '-f', audio_format ] #, #'-acodec', audio_codec] ffmpeg(ffmpeg_path, downloaded_file_path, audio_filepath, input_args=audio_input_args, output_args=audio_output_args, num_retries=num_retries, validation_callback=validate_audio, validation_args={ 'audio_info': audio_info, 'end_past_video_end': end_past_video_end }) # Remove the original file, probably bulkier, we really just want our cropped-down MP3 if os.path.exists(downloaded_file_path): os.remove(downloaded_file_path) else: error_msg = f'Cannot find original download file for {ytid} ({ts_start} - {ts_start}) at {downloaded_file_path}' LOGGER.error( error_msg.format(ytid, ts_start, ts_end, downloaded_file_path)) LOGGER.info('Downloaded video {} ({} - {})'.format(ytid, ts_start, ts_end)) return audio_filepath
def download_yt_video(ytid, ts_start, ts_end, output_dir, ffmpeg_path, ffprobe_path, audio_codec='flac', audio_format='flac', audio_sample_rate=48000, audio_bit_depth=16, num_retries=10): """ Download a Youtube video (with the audio and video separated). The audio will be saved in <output_dir>/audio and the video will be saved in <output_dir>/video. The output filename is of the format: <YouTube ID>_<start time in ms>_<end time in ms>.<extension> Args: ytid: Youtube ID string (Type: str) ts_start: Segment start time (in seconds) (Type: float) ts_start: Segment end time (in seconds) (Type: float) output_dir: Output directory where video will be saved (Type: str) ffmpeg_path: Path to ffmpeg executable (Type: str) ffprobe_path: Path to ffprobe executable (Type: str) Keyword Args: audio_codec: Name of audio codec used by ffmpeg to encode output audio (Type: str) audio_format: Name of audio container format used for output audio (Type: str) audio_sample_rate: Target audio sample rate (in Hz) (Type: int) audio_bit_depth: Target audio sample bit depth (Type: int) video_codec: Name of video codec used by ffmpeg to encode output video (Type: str) video_format: Name of video container format used for output video (Type: str) video_mode: Name of the method in which video is downloaded. 'bestvideo' obtains the best quality video that does not contain an audio stream. 'bestvideoaudio' obtains the best quality video that contains an audio stream. 'bestvideowithaudio' obtains the best quality video without an audio stream and merges it with audio stream. (Type: bool) video_frame_rate: Target video frame rate (in fps) (Type: int) num_retries: Number of attempts to download and process an audio or video file with ffmpeg (Type: int) Returns: video_filepath: Filepath to video file (Type: str) audio_filepath: Filepath to audio file (Type: str) """ # Compute some things from the segment boundaries duration = ts_end - ts_start # Make the output format and video URL # Output format is in the format: # <YouTube ID>_<start time in ms>_<end time in ms>.<extension> media_filename = get_media_filename(ytid, ts_start, ts_end) audio_filepath = os.path.join(output_dir, media_filename + '.' + audio_format) video_page_url = 'https://www.youtube.com/watch?v={}'.format(ytid) # Get the direct URLs to the videos with best audio and with best video (with audio) video = pafy.new(video_page_url) video_duration = video.length end_past_video_end = False if ts_end > video_duration: warn_msg = "End time for segment ({} - {}) of video {} extends past end of video (length {} sec)" LOGGER.warning(warn_msg.format(ts_start, ts_end, ytid, video_duration)) duration = video_duration - ts_start ts_end = ts_start + duration end_past_video_end = True best_audio = video.getbestaudio() best_audio_url = best_audio.url audio_info = { 'sample_rate': audio_sample_rate, 'channels': 2, 'bitrate': audio_bit_depth, 'encoding': audio_codec.upper(), 'duration': duration } # Download the audio audio_input_args = ['-n', '-ss', str(ts_start)] audio_output_args = [ '-t', str(duration), '-ar', str(audio_sample_rate), '-vn', '-ac', str(audio_info['channels']), '-sample_fmt', 's{}'.format(audio_bit_depth), '-f', audio_format, '-acodec', audio_codec ] ffmpeg(ffmpeg_path, best_audio_url, audio_filepath, input_args=audio_input_args, output_args=audio_output_args, num_retries=num_retries, validation_callback=validate_audio, validation_args={ 'audio_info': audio_info, 'end_past_video_end': end_past_video_end }) LOGGER.info('Downloaded audio {} ({} - {})'.format(ytid, ts_start, ts_end)) return audio_filepath