teoha commited on
Commit
7419e5c
·
1 Parent(s): dfb51ff

Alter logic to serve video on static endpoint

Browse files
Files changed (3) hide show
  1. Dockerfile +1 -1
  2. main.py +24 -16
  3. youtubeaudio.py +23 -19
Dockerfile CHANGED
@@ -29,7 +29,7 @@ COPY --chown=user youtubeaudio.py $HOME/root
29
  ARG model=base
30
  ENV model=$model
31
  RUN mkdir $HOME/root/models
32
- RUN mkdir -p -m 777 /tmp/holosubs/results
33
  COPY --from=build "/whisper/models/ggml-${model}.bin" "${HOME}/root/models/ggml-${model}.bin"
34
  COPY --from=build /whisper/main /usr/local/bin/whisper
35
 
 
29
  ARG model=base
30
  ENV model=$model
31
  RUN mkdir $HOME/root/models
32
+ RUN mkdir -p -m 775 /tmp/holosubs/tracks /tmp/holosubs/videos
33
  COPY --from=build "/whisper/models/ggml-${model}.bin" "${HOME}/root/models/ggml-${model}.bin"
34
  COPY --from=build /whisper/main /usr/local/bin/whisper
35
 
main.py CHANGED
@@ -1,12 +1,13 @@
1
- from fastapi import FastAPI, HTTPException
2
  from pydantic import BaseModel
3
- from youtubeaudio import YoutubeAudio
4
  import subprocess
5
  import os
6
  import uuid
7
  import logging
8
  from fastapi.staticfiles import StaticFiles
9
  from fastapi.middleware.cors import CORSMiddleware
 
10
 
11
  class Url(BaseModel):
12
  url: str
@@ -15,9 +16,11 @@ format = "%(asctime)s: %(message)s"
15
  logging.basicConfig(format=format, level=logging.DEBUG,
16
  datefmt="%H:%M:%S")
17
  MODEL=os.environ['model']
 
 
18
 
19
  app = FastAPI()
20
- app.mount("/tracks", StaticFiles(directory="/tmp/holosubs/results"), name="tracks")
21
 
22
  # CORS
23
  origins = ["*"]
@@ -30,27 +33,32 @@ app.add_middleware(
30
  )
31
 
32
  @app.post("/captions/")
33
- def read_root(url: Url):
 
34
  # Download wav file and get filename
35
- ytaudio=YoutubeAudio(url)
36
- ytaudio.download_audio()
37
- filename=ytaudio.filename
38
  # Resample file
39
  ytaudio.resample('16k')
 
40
  # Generate subtitles
41
- captionFilename=str(uuid.uuid4())
42
- output_file=os.path.join("/tmp/holosubs/results", captionFilename)
43
- logging.info(f'Writing to file {output_file}.vtt')
44
  cmd=['/usr/local/bin/whisper','-m',f'/home/user/root/models/ggml-{MODEL}.bin'
45
- ,'-f',filename, '-di', '-of', output_file, '-tr', '-ovtt', '-t', '8']
46
  p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
47
  (output, err) = p.communicate()
48
  p_status = p.wait()
49
- logging.info(output)
50
  if err:
51
- logging.error("Whisper translation failed with error",err)
52
  raise HTTPException(status_code=500, detail="Whisper translation failed")
53
- with open(output_file+".vtt", 'r') as f:
54
- raw_vtt=f.read()
55
- return {"captions": f"{captionFilename}.vtt"}
 
 
 
 
 
56
 
 
1
+ from fastapi import FastAPI, HTTPException, Request
2
  from pydantic import BaseModel
3
+ from youtubeaudio import YoutubeMedia
4
  import subprocess
5
  import os
6
  import uuid
7
  import logging
8
  from fastapi.staticfiles import StaticFiles
9
  from fastapi.middleware.cors import CORSMiddleware
10
+ import ffmpeg
11
 
12
  class Url(BaseModel):
13
  url: str
 
16
  logging.basicConfig(format=format, level=logging.DEBUG,
17
  datefmt="%H:%M:%S")
18
  MODEL=os.environ['model']
19
+ BASE_DIR="/tmp/holosubs"
20
+ VIDEO_DIR=os.path.join(BASE_DIR,"videos")
21
 
22
  app = FastAPI()
23
+ app.mount("/videos", StaticFiles(directory=VIDEO_DIR), name="videos")
24
 
25
  # CORS
26
  origins = ["*"]
 
33
  )
34
 
35
  @app.post("/captions/")
36
+ def read_root(url: Url, request: Request):
37
+ requestID=str(uuid.uuid4())
38
  # Download wav file and get filename
39
+ ytaudio=YoutubeMedia(url, os.path.join(BASE_DIR,requestID))
40
+ ytaudio.download()
41
+ audio_filename, video_filename = ytaudio.audio_filename, ytaudio.video_filename
42
  # Resample file
43
  ytaudio.resample('16k')
44
+
45
  # Generate subtitles
46
+ subtitle_filename=os.path.join("/tmp/holosubs/tracks", requestID)
47
+ logging.info(f'Output will be writen to {subtitle_filename}.srt')
 
48
  cmd=['/usr/local/bin/whisper','-m',f'/home/user/root/models/ggml-{MODEL}.bin'
49
+ ,'-f',audio_filename, '-of', subtitle_filename, '-tr', '-osrt', '-t', '8']
50
  p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
51
  (output, err) = p.communicate()
52
  p_status = p.wait()
 
53
  if err:
54
+ logging.error("Translation failed with error",err)
55
  raise HTTPException(status_code=500, detail="Whisper translation failed")
56
+ media=ffmpeg.input(video_filename)
57
+ ffmpeg.concat(media.video. \
58
+ filter('subtitles', subtitle_filename+".srt"), \
59
+ media.audio, v=1, a=1). \
60
+ output(os.path.join(VIDEO_DIR,requestID+".mp4")).run()
61
+
62
+ # Return video url delivered
63
+ return {"video": f'/videos/{requestID+".mp4"}'}
64
 
youtubeaudio.py CHANGED
@@ -14,45 +14,49 @@ format = "%(asctime)s: %(message)s"
14
  logging.basicConfig(format=format, level=logging.DEBUG,
15
  datefmt="%H:%M:%S")
16
 
17
- class YoutubeAudio:
18
- def __init__(self, url, dir="/tmp/holosubs/audio"):
19
  self.url=url
20
  self.dir=dir
 
 
21
 
22
- def download_audio(self):
23
  ydl_opts = {
24
  'outtmpl': os.path.join(self.dir, "%(id)s_%(epoch)s.%(ext)s"),
25
  'logger': logging,
26
  'progress_hooks': [self.progress_hook],
27
- 'format': 'm4a/bestaudio/best',
 
28
  'postprocessors': [{ # Extract audio using ffmpeg
29
  'key': 'FFmpegExtractAudio',
30
- 'preferredcodec': 'wav'
31
  }]
32
  }
33
  with YoutubeDL(ydl_opts) as ydl:
34
  error_code = ydl.download([self.url.url])
35
 
36
- def resample(self,sr='16k'):
37
- tmp_filename=os.path.join(self.dir,str(uuid.uuid4()))+".wav"
38
- ffmpeg.input(self.filename).output(tmp_filename,ar=sr).run()
39
- shutil.move(tmp_filename, self.filename)
40
- logging.info(f"Succesfuly resampled {self.filename}")
41
 
42
  def clean(self):
43
- if not self.filename:
44
  logging.error("Audio not downloaded")
45
  return
46
- location=os.path.join(self.dir, self.filename)
47
- if os.path.exists(self.filename):
48
- os.remove(self.filename)
49
- logging.info(f"File {self.filename} successfully removed")
50
- self.filename=None
51
  else:
52
- print(f"File {self.filename} does not exist")
53
 
54
  def progress_hook(self, d):
55
  if d['status'] == 'finished':
56
- self.filename=os.path.join(self.dir, Path(d.get('info_dict').get('_filename')).stem + ".wav")
57
- print(f'Done downloading {self.filename}, now post-processing ...')
 
58
 
 
14
  logging.basicConfig(format=format, level=logging.DEBUG,
15
  datefmt="%H:%M:%S")
16
 
17
+ class YoutubeMedia:
18
+ def __init__(self, url, dir, audio_format="wav", video_format="mp4"):
19
  self.url=url
20
  self.dir=dir
21
+ self.audio_format=audio_format
22
+ self.video_format=video_format
23
 
24
+ def download(self):
25
  ydl_opts = {
26
  'outtmpl': os.path.join(self.dir, "%(id)s_%(epoch)s.%(ext)s"),
27
  'logger': logging,
28
  'progress_hooks': [self.progress_hook],
29
+ 'format': f'{self.video_format}/bestvideo',
30
+ 'keepvideo': True,
31
  'postprocessors': [{ # Extract audio using ffmpeg
32
  'key': 'FFmpegExtractAudio',
33
+ 'preferredcodec': self.audio_format
34
  }]
35
  }
36
  with YoutubeDL(ydl_opts) as ydl:
37
  error_code = ydl.download([self.url.url])
38
 
39
+ def resample(self,sr='16k'): #defaults to 16k sampling rate
40
+ tmp_filename=os.path.join(self.dir,str(uuid.uuid4()))+"."+self.audio_format
41
+ ffmpeg.input(self.audio_filename).output(tmp_filename,ar=sr).run()
42
+ shutil.move(tmp_filename, self.audio_filename)
43
+ logging.info(f"Succesfuly resampled {self.audio_filename}")
44
 
45
  def clean(self):
46
+ if not self.audio_filename:
47
  logging.error("Audio not downloaded")
48
  return
49
+ location=os.path.join(self.dir, self.audio_filename)
50
+ if os.path.exists(self.audio_filename):
51
+ os.remove(self.audio_filename)
52
+ logging.info(f"File {self.audio_filename} successfully removed")
53
+ self.audio_filename=None
54
  else:
55
+ print(f"File {self.audio_filename} does not exist")
56
 
57
  def progress_hook(self, d):
58
  if d['status'] == 'finished':
59
+ self.audio_filename=os.path.join(self.dir, Path(d.get('info_dict').get('_filename')).stem + "."+self.audio_format)
60
+ self.video_filename=os.path.join(self.dir, Path(d.get('info_dict').get('_filename')).stem + "."+self.video_format)
61
+ print(f'Done downloading {self.audio_filename}, now post-processing ...')
62