4 from tempfile import NamedTemporaryFile
5 from django.conf import settings
8 FILE_CACHE = getattr(settings, 'FILE_CACHE', 'file_cache/')
11 def link_or_copy(src, dst):
12 dstdir = os.path.dirname(dst)
13 if not os.path.exists(dstdir):
15 if os.path.exists(dst):
17 # FIXME: tiny window here when the temp path is not taken.
21 shutil.copyfile(src, dst)
24 def process_to_file(cmdline, prefix='', suffix='', cache_key=None, output_path=None):
26 tmp = NamedTemporaryFile(prefix=prefix, suffix=suffix, delete=False)
28 output_path = tmp.name
31 cache_path = FILE_CACHE + cache_key.replace('/', '__')
33 if cache_key and os.path.exists(cache_path):
34 link_or_copy(cache_path, output_path)
36 # Actually run the processing.
37 subprocess.run(cmdline + [output_path], check=True)
39 link_or_copy(output_path, cache_path)
44 def video_from_image(img_path, duration, fps=25, cache=True):
45 return process_to_file(
46 ['ffmpeg', '-y', '-loop', '1', '-t', str(duration), '-i', img_path, '-c:v', 'libx264', '-vf', f'fps={fps},format=yuv420p'],
49 f'video_from_image:{img_path}:{duration}:{fps}.mkv' if cache else None
53 def cut_video(video_path, duration):
54 return process_to_file(
55 ['ffmpeg', '-y', '-i', video_path, '-t', str(duration)],
61 def ffmpeg_concat(paths, suffix):
62 filelist = NamedTemporaryFile(prefix='concat-', suffix='.txt')
64 filelist.write(f"file '{path}'\n".encode('utf-8'))
67 outname = process_to_file(
68 ['ffmpeg', '-y', '-safe', '0', '-f', 'concat', '-i', filelist.name],
76 def concat_videos(paths):
77 return ffmpeg_concat(paths, '.mkv')
80 def concat_audio(paths):
81 return ffmpeg_concat(paths, '.flac')
84 def standardize_audio(p, cache=True):
85 return process_to_file(
86 ['ffmpeg', '-y', '-i', p, '-sample_fmt', 's16', '-acodec', 'flac', '-ac', '2', '-ar', '44100'],
87 'standardize-', '.flac',
88 f'standardize_audio:{p}.flac' if cache else None
92 def standardize_video(p, cache=True):
93 return process_to_file(
94 ['ffmpeg', '-y', '-i', p],
95 'standardize-', '.mkv',
96 f'standardize_video:{p}.mkv' if cache else None
100 def mux(channels, output_path=None):
101 args = ['ffmpeg', '-y']
103 args.extend(['-i', c])
104 return process_to_file(args, 'mux-', '.mkv', output_path=output_path)
107 def get_duration(path):
128 def get_framerate(path):
129 rates = subprocess.run(
135 "stream=r_frame_rate",
144 ).stdout.strip().split('\n')
146 a, b = rate.split('/')