def convert_video_to_16_9(in_path: str, out_path: str, debug: bool = False) -> bool: w, h = ffprobe.video_resolution(in_path) if h is None or w is None: return False if h == 1080 and w == 1920: sh.cp(in_path, out_path) return True if w / h < 16 / 9: sh.sh( 'ffmpeg -y -i ' + sh.path(in_path) + " -vf 'split[original][copy];[copy]scale=1920:-1,setsar=1:1,crop=h=1080,gblur=sigma=75[blurred];[original]scale=-1:1080[original_resized];[blurred][original_resized]overlay=(main_w-overlay_w)/2:(main_h-overlay_h)/2' " + sh.path(out_path), debug=debug) else: sh.sh( 'ffmpeg -y -i ' + sh.path(in_path) + " -vf 'split[original][copy];[copy]scale=-1:1080,setsar=1:1,crop=w=1920,gblur=sigma=75[blurred];[original]scale=1920:-1[original_resized];[blurred][original_resized]overlay=(main_w-overlay_w)/2:(main_h-overlay_h)/2' " + sh.path(out_path), debug=debug) return path.exists(out_path)
def concat_videos_reencode(in_paths: List[str], out_path: str, debug: bool = False) -> bool: if len(in_paths) == 0: return False elif len(in_paths) == 1: sh.cp(in_paths[0], out_path) return path.exists(out_path) cmd = 'ffmpeg -y' for path_ in in_paths: cmd += ' -i ' + sh.path(path_) cmd += ' -filter_complex "' for i in range(len(in_paths)): cmd += '[{}:v] [{}:a] '.format(i, i) cmd += 'concat=n={}:v=1:a=1 [v] [a]" -map "[v]" -map "[a]" '.format( len(in_paths)) + sh.path(out_path) sh.sh(cmd, debug=debug) return path.exists(out_path)
def concat_videos_copy(in_paths: List[str], out_path: str, debug: bool = False) -> bool: if len(in_paths) == 0: return False elif len(in_paths) == 1: sh.cp(in_paths[0], out_path) return True temp_txt_path = path.join(kpath.folder_path_of_file(out_path), '__temp_list.txt') temp_txt_content = '' for in_path in in_paths: if len(temp_txt_content) > 0: temp_txt_content += '\n' temp_txt_content += 'file \'' + in_path + '\'' with open(temp_txt_path, 'w') as f: f.write(temp_txt_content) sh.sh('ffmpeg -y -f concat -safe 0 -i ' + sh.path(temp_txt_path) + ' -c copy ' + sh.path(out_path), debug=debug) kpath.remove(temp_txt_path) return path.exists(out_path)
def add_silence_to_video(input: str, output: str, debug: bool = False) -> bool: sh.sh( 'ffmpeg -f lavfi -y -i anullsrc=channel_layout=stereo:sample_rate=48000 -i ' + sh.path(input) + ' -c:v copy -c:a aac -shortest ' + sh.path(output), debug=debug) return path.exists(output)
def loop(in_path: str, out_path: str, length_seconds: float, debug: bool = False): sh.sh('ffmpeg -y -stream_loop -1 -i ' + sh.path(in_path) + ' -t ' + str(length_seconds) + ' ' + sh.path(out_path), debug=debug) return path.exists(out_path)
def remove_audio( input: str, output: str, debug: bool = False ) -> bool: sh.sh( 'ffmpeg -y -i ' + sh.path(input) + ' -c copy -an ' + sh.path(output), debug=debug ) return path.exists(output)
def concat(in_paths: List[str], out_path: str, debug: bool = False) -> Tuple[Optional[List[ConcatSongElement]], Optional[int]]: song_elements = [] total_len = 0 for p in in_paths: len_s = soxi.get_len(p) song_elements.append(ConcatSongElement(p, total_len, total_len + len_s)) total_len += len_s sh.sh('sox {} {}'.format(' '.join([sh.path(p) for p in in_paths]), sh.path(out_path)), debug=debug) return (song_elements, total_len) if os.path.exists(out_path) else (None, None)
def add_silence_to_video(input: str, output: str, sample_rate: int = 48000, duration: str = 'shortest', debug: bool = False) -> bool: cmd = 'ffmpeg -f lavfi -y -i anullsrc=channel_layout=stereo:sample_rate={} -i {} -c:v copy -c:a aac '.format( sample_rate, sh.path(input)) if duration == 'shortest': cmd += '-shortest ' cmd += sh.path(output) sh.sh(cmd, debug=debug) return path.exists(output)
def add_audio_to_video(input_a: str, input_v: str, output: str, reencode: bool = False, debug: bool = False) -> bool: cmd = 'ffmpeg -y -i ' + sh.path(input_v) + ' -i ' + sh.path(input_a) if not reencode: cmd += ' -c:v copy -map 0:v:0 -map 1:a:0' cmd += ' -shortest ' + sh.path(output) sh.sh(cmd, debug=debug) return path.exists(output)
def get_duration(video_path: str) -> float: res = sh.sh( 'ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 ' + sh.path(video_path)) try: return float(res.strip()) except: return 0
def rotate_video(path_in: str, path_out: str, times_90_degrees: int, debug: bool = False) -> bool: times_90_degrees = times_90_degrees % 4 rot_val = 1 if times_90_degrees > 0 else 2 if times_90_degrees == 0: sh.cp(path_in, path_out) else: sh.sh('ffmpeg -y -i {} -vf "{}" {}'.format( sh.path(path_in), ','.join([ 'transpose={}'.format(rot_val) for _ in range(times_90_degrees) ]), sh.path(path_out)), debug=debug) return path.exists(path_out)
def concat_videos_loop(in_paths: List[str], out_path: str, video_duration: float, randomize_videos: Optional[bool] = False, debug: bool = False) -> bool: if len(in_paths) == 0: return False elif len(in_paths) == 1: sh.cp(in_paths[0], out_path) return True temp_txt_path = path.join(kpath.folder_path_of_file(out_path), '__temp_video_paths.txt') current_video_duration = 0 final_text = '' while current_video_duration < video_duration: if randomize_videos: random.shuffle(in_paths) for vid_path in in_paths: final_text += 'file ' + '\'' + vid_path + '\'\n' subvid_duration = ffprobe.get_duration(vid_path) current_video_duration += subvid_duration if current_video_duration >= video_duration: with open(temp_txt_path, 'w') as f: f.write(final_text) break sh.sh('ffmpeg -y -f concat -safe 0 -i ' + sh.path(temp_txt_path) + ' -c copy ' + sh.path(out_path), debug=debug) kpath.remove(temp_txt_path) return path.exists(out_path)
def video_resolution(path: str) -> (Optional[float], Optional[float]): res = sh.sh( 'ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=s=x:p=0 ' + sh.path(path)).strip() try: comps = res.split('x') if len(comps) == 2: return float(comps[0]), float(comps[1]) except: pass return None, None
def mix_multiple_audios(audio_paths: List[str], path_out: str, duration: Optional[str] = 'longest', debug: bool = False) -> bool: cmd = 'ffmpeg -y' for audio_path in audio_paths: cmd += ' -i ' + sh.path(audio_path) cmd += ' -filter_complex amix=inputs=' + str(len(audio_paths)) if duration: cmd += ':duration={}'.format(duration) cmd += ' ' + path_out sh.sh(cmd, debug=debug) return path.exists(path_out)
def fade(in_path: str, out_path: str, fade_in_s: float = 0, fade_out_s: float = 0, shape: str = 'q', debug: bool = False) -> bool: '''shape can be: 'q', 'h', 't', 'p', 'l' ''' sh.sh('sox {} {} fade {} {} -0 {}'.format(sh.path(in_path), sh.path(out_path), shape, fade_in_s, fade_out_s), debug=debug) return os.path.exists(out_path)
def trim(in_path: str, out_path: str, len_s: float, start_s: float = 0, debug: bool = False) -> bool: '''if start_s is negative, it will record from the end-start_s''' sh.sh('sox {} {} trim {} {}'.format(sh.path(in_path), sh.path(out_path), start_s, len_s), debug=debug) return os.path.exists(out_path)
def adjust_volume(in_path: str, out_path: str, multi: float, debug: bool = False) -> bool: sh.sh('sox -v {} {} {}'.format(multi, sh.path(in_path), sh.path(out_path)), debug=debug) return os.path.exists(out_path)
def mix(in_paths: List[str], out_path: str, debug: bool = False) -> bool: sh.sh('sox -m {} {}'.format(' '.join([sh.path(p) for p in in_paths]), sh.path(out_path)), debug=debug) return os.path.exists(out_path)
def change_sample_rate(in_path: str, out_path: str, sample_rate: int, debug: bool = False) -> bool: sh.sh('sox {} -r {} {}'.format(sh.path(in_path), sample_rate, sh.path(out_path)), debug=debug) return os.path.exists(out_path)
def trim( in_path: str, out_path: str, start_seconds: float = 0, stop_seconds: Optional[float] = None, duration: Optional[float] = None, reencode: bool = False, debug: bool = False ) -> bool: """PASS EITHER 'stop_seconds' OR 'duration' IF BOTH PASSED, 'duration' will be taken as preference """ if stop_seconds is None and duration is None: print('Either, \'stop_seconds\' should e passed or \'duration\'') return False if duration is None: duration = stop_seconds - start_seconds sh.sh( 'ffmpeg -y -ss {} -i {} -t {} {} {}'.format(__seconds_to_time_str(start_seconds), sh.path(in_path), __seconds_to_time_str(duration), '-c copy -avoid_negative_ts make_zero' if reencode else '-async 1', sh.path(out_path)), debug=debug ) return path.exists(out_path)
def has_video(path: str) -> bool: return len( sh.sh( 'ffprobe -i ' + sh.path(path) + ' -show_streams -select_streams v -loglevel error' ).strip() ) > 0
def get_video_fps( path: str ) -> Optional[float]: res = sh.sh( 'ffprobe -v error -select_streams v -of default=noprint_wrappers=1:nokey=1 -show_entries stream=r_frame_rate ' + sh.path(path) ).strip() try: fps = res.split('/') if len(fps) == 2: return float(fps[0]) / float(fps[1]) except: pass return None
def __audio_data(path: str) -> str: return sh.sh( 'ffprobe -i ' + sh.path(path) + ' -show_streams 2>&1 | grep \'Stream #0:1\'' ).strip()