radames commited on
Commit
c376f4f
·
1 Parent(s): 43da6b0

remove API

Browse files
Files changed (3) hide show
  1. README.md +1 -1
  2. app.py +92 -138
  3. requirements.txt +3 -2
README.md CHANGED
@@ -5,7 +5,7 @@ colorFrom: red
5
  colorTo: gray
6
  sdk: gradio
7
  app_file: app.py
8
- sdk_version: 4.26.0
9
  pinned: false
10
  disable_embedding: true
11
  short_description: Audio-based video editing using AI-generated transcription
 
5
  colorTo: gray
6
  sdk: gradio
7
  app_file: app.py
8
+ sdk_version: 5.49.1
9
  pinned: false
10
  disable_embedding: true
11
  short_description: Audio-based video editing using AI-generated transcription
app.py CHANGED
@@ -1,61 +1,49 @@
 
 
1
  import gradio as gr
2
  import json
3
  from difflib import Differ
4
  import ffmpeg
5
- import os
6
  from pathlib import Path
7
- import time
8
  import aiohttp
9
  import asyncio
10
-
11
 
12
  # Set true if you're using huggingface inference API API https://huggingface.co/inference-api
13
  API_BACKEND = True
14
  # MODEL = 'facebook/wav2vec2-large-960h-lv60-self'
15
- # MODEL = "facebook/wav2vec2-large-960h"
16
- MODEL = "facebook/wav2vec2-base-960h"
17
  # MODEL = "patrickvonplaten/wav2vec2-large-960h-lv60-self-4-gram"
18
- if API_BACKEND:
19
- from dotenv import load_dotenv
20
- import base64
21
- import asyncio
22
- load_dotenv(Path(".env"))
23
-
24
- HF_TOKEN = os.environ["HF_TOKEN"]
25
- headers = {"Authorization": f"Bearer {HF_TOKEN}"}
26
- API_URL = f'https://api-inference.huggingface.co/models/{MODEL}'
27
-
28
- else:
29
- import torch
30
- from transformers import pipeline
31
-
32
- # is cuda available?
33
- cuda = torch.device(
34
- 'cuda:0') if torch.cuda.is_available() else torch.device('cpu')
35
- device = 0 if torch.cuda.is_available() else -1
36
- speech_recognizer = pipeline(
37
- task="automatic-speech-recognition",
38
- model=f'{MODEL}',
39
- tokenizer=f'{MODEL}',
40
- framework="pt",
41
- device=device,
42
- )
43
 
44
  videos_out_path = Path("./videos_out")
45
  videos_out_path.mkdir(parents=True, exist_ok=True)
46
 
47
- samples_data = sorted(Path('examples').glob('*.json'))
48
  SAMPLES = []
49
  for file in samples_data:
50
  with open(file) as f:
51
  sample = json.load(f)
52
  SAMPLES.append(sample)
53
- VIDEOS = list(map(lambda x: [x['video']], SAMPLES))
54
 
55
  total_inferences_since_reboot = 415
56
  total_cuts_since_reboot = 1539
57
 
58
 
 
59
  async def speech_to_text(video_file_path):
60
  """
61
  Takes a video path to convert to audio, transcribe audio channel to text and char timestamps
@@ -63,68 +51,52 @@ async def speech_to_text(video_file_path):
63
  Using https://huggingface.co/tasks/automatic-speech-recognition pipeline
64
  """
65
  global total_inferences_since_reboot
66
- if (video_file_path == None):
67
  raise ValueError("Error no video input")
68
 
69
  video_path = Path(video_file_path)
70
  try:
71
  # convert video to audio 16k using PIPE to audio_memory
72
- audio_memory, _ = ffmpeg.input(video_path).output(
73
- '-', format="wav", ac=1, ar='16k').overwrite_output().global_args('-loglevel', 'quiet').run(capture_stdout=True)
 
 
 
 
 
74
  except Exception as e:
75
  raise RuntimeError("Error converting video to audio")
76
 
77
  ping("speech_to_text")
78
- last_time = time.time()
79
- if API_BACKEND:
80
- # Using Inference API https://huggingface.co/inference-api
81
- # try twice, because the model must be loaded
82
- for i in range(10):
83
- for tries in range(4):
84
- print(f'Transcribing from API attempt {tries}')
85
- try:
86
- inference_reponse = await query_api(audio_memory)
87
- print(inference_reponse)
88
- transcription = inference_reponse["text"].lower()
89
- timestamps = [[chunk["text"].lower(), chunk["timestamp"][0], chunk["timestamp"][1]]
90
- for chunk in inference_reponse['chunks']]
91
-
92
- total_inferences_since_reboot += 1
93
- print("\n\ntotal_inferences_since_reboot: ",
94
- total_inferences_since_reboot, "\n\n")
95
- return (transcription, transcription, timestamps)
96
- except Exception as e:
97
- print(e)
98
- if 'error' in inference_reponse and 'estimated_time' in inference_reponse:
99
- wait_time = inference_reponse['estimated_time']
100
- print("Waiting for model to load....", wait_time)
101
- # wait for loading model
102
- # 5 seconds plus for certanty
103
- await asyncio.sleep(wait_time + 5.0)
104
- elif 'error' in inference_reponse:
105
- raise RuntimeError("Error Fetching API",
106
- inference_reponse['error'])
107
- else:
108
- break
109
- else:
110
- raise RuntimeError(inference_reponse, "Error Fetching API")
111
- else:
112
-
113
- try:
114
- print(f'Transcribing via local model')
115
- output = speech_recognizer(
116
- audio_memory, return_timestamps="char", chunk_length_s=10, stride_length_s=(4, 2))
117
-
118
- transcription = output["text"].lower()
119
- timestamps = [[chunk["text"].lower(), chunk["timestamp"][0].tolist(), chunk["timestamp"][1].tolist()]
120
- for chunk in output['chunks']]
121
- total_inferences_since_reboot += 1
122
 
123
- print("\n\ntotal_inferences_since_reboot: ",
124
- total_inferences_since_reboot, "\n\n")
125
- return (transcription, transcription, timestamps)
126
- except Exception as e:
127
- raise RuntimeError("Error Running inference with local model", e)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
 
129
 
130
  async def cut_timestamps_to_video(video_in, transcription, text_in, timestamps):
@@ -136,14 +108,14 @@ async def cut_timestamps_to_video(video_in, transcription, text_in, timestamps):
136
 
137
  video_path = Path(video_in)
138
  video_file_name = video_path.stem
139
- if (video_in == None or text_in == None or transcription == None):
140
  raise ValueError("Inputs undefined")
141
 
142
  d = Differ()
143
  # compare original transcription with edit text
144
  diff_chars = d.compare(transcription, text_in)
145
  # remove all text aditions from diff
146
- filtered = list(filter(lambda x: x[0] != '+', diff_chars))
147
 
148
  # filter timestamps to be removed
149
  # timestamps_to_cut = [b for (a,b) in zip(filtered, timestamps_var) if a[0]== '-' ]
@@ -152,8 +124,8 @@ async def cut_timestamps_to_video(video_in, transcription, text_in, timestamps):
152
  # groupping character timestamps so there are less cuts
153
  idx = 0
154
  grouped = {}
155
- for (a, b) in zip(filtered, timestamps):
156
- if a[0] != '-':
157
  if idx in grouped:
158
  grouped[idx].append(b)
159
  else:
@@ -165,24 +137,27 @@ async def cut_timestamps_to_video(video_in, transcription, text_in, timestamps):
165
  # after grouping, gets the lower and upter start and time for each group
166
  timestamps_to_cut = [[v[0][1], v[-1][2]] for v in grouped.values()]
167
 
168
- between_str = '+'.join(
169
- map(lambda t: f'between(t,{t[0]},{t[1]})', timestamps_to_cut))
 
170
 
171
  if timestamps_to_cut:
172
  video_file = ffmpeg.input(video_in)
173
- video = video_file.video.filter(
174
- "select", f'({between_str})').filter("setpts", "N/FRAME_RATE/TB")
175
- audio = video_file.audio.filter(
176
- "aselect", f'({between_str})').filter("asetpts", "N/SR/TB")
177
-
178
- output_video = f'./videos_out/{video_file_name}.mp4'
 
 
179
  ffmpeg.concat(video, audio, v=1, a=1).output(
180
- output_video).overwrite_output().global_args('-loglevel', 'quiet').run()
 
181
  else:
182
  output_video = video_in
183
 
184
- tokens = [(token[2:], token[0] if token[0] != " " else None)
185
- for token in filtered]
186
 
187
  total_cuts_since_reboot += 1
188
  ping("video_cuts")
@@ -190,40 +165,15 @@ async def cut_timestamps_to_video(video_in, transcription, text_in, timestamps):
190
  return (tokens, output_video)
191
 
192
 
193
- async def query_api(audio_bytes: bytes):
194
- """
195
- Query for Huggingface Inference API for Automatic Speech Recognition task
196
- """
197
- payload = json.dumps({
198
- "inputs": base64.b64encode(audio_bytes).decode("utf-8"),
199
- "parameters": {
200
- "return_timestamps": "char",
201
- "chunk_length_s": 10,
202
- "stride_length_s": [4, 2]
203
- },
204
- "options": {"use_gpu": False}
205
- }).encode("utf-8")
206
- async with aiohttp.ClientSession() as session:
207
- async with session.post(API_URL, headers=headers, data=payload) as response:
208
- print("API Response: ", response.status)
209
- if response.headers['Content-Type'] == 'application/json':
210
- return await response.json()
211
- elif response.headers['Content-Type'] == 'application/octet-stream':
212
- return await response.read()
213
- elif response.headers['Content-Type'] == 'text/plain':
214
- return await response.text()
215
- else:
216
- raise RuntimeError("Error Fetching API")
217
-
218
-
219
  def ping(name):
220
- url = f'https://huggingface.co/api/telemetry/spaces/radames/edit-video-by-editing-text/{name}'
221
  print("ping: ", url)
222
 
223
  async def req():
224
  async with aiohttp.ClientSession() as session:
225
  async with session.get(url) as response:
226
  print("pong: ", response.status)
 
227
  asyncio.create_task(req())
228
 
229
 
@@ -257,13 +207,12 @@ with gr.Blocks(css=css) as demo:
257
  """)
258
 
259
  with gr.Row():
260
-
261
  examples.render()
262
 
263
  def load_example(id):
264
- video = SAMPLES[id]['video']
265
- transcription = SAMPLES[id]['transcription'].lower()
266
- timestamps = SAMPLES[id]['timestamps']
267
 
268
  return (video, transcription, transcription, timestamps)
269
 
@@ -271,13 +220,15 @@ with gr.Blocks(css=css) as demo:
271
  load_example,
272
  inputs=[examples],
273
  outputs=[video_in, text_in, transcription_var, timestamps_var],
274
- queue=False)
 
275
  with gr.Row():
276
  with gr.Column():
277
  video_in.render()
278
  transcribe_btn = gr.Button("Transcribe Audio")
279
- transcribe_btn.click(speech_to_text, [video_in], [
280
- text_in, transcription_var, timestamps_var])
 
281
 
282
  with gr.Row():
283
  gr.Markdown("""
@@ -290,13 +241,16 @@ with gr.Blocks(css=css) as demo:
290
  with gr.Row():
291
  cut_btn = gr.Button("Cut to video", elem_id="cut_btn")
292
  # send audio path and hidden variables
293
- cut_btn.click(cut_timestamps_to_video, [
294
- video_in, transcription_var, text_in, timestamps_var], [diff_out, video_out])
 
 
 
295
 
296
  reset_transcription = gr.Button(
297
- "Reset to last trascription", elem_id="reset_btn")
298
- reset_transcription.click(
299
- lambda x: x, transcription_var, text_in)
300
  with gr.Column():
301
  video_out.render()
302
  diff_out.render()
 
1
+ import torch
2
+ from transformers import pipeline
3
  import gradio as gr
4
  import json
5
  from difflib import Differ
6
  import ffmpeg
 
7
  from pathlib import Path
 
8
  import aiohttp
9
  import asyncio
10
+ import spaces
11
 
12
  # Set true if you're using huggingface inference API API https://huggingface.co/inference-api
13
  API_BACKEND = True
14
  # MODEL = 'facebook/wav2vec2-large-960h-lv60-self'
15
+ MODEL = "facebook/wav2vec2-large-960h"
16
+ # MODEL = "facebook/wav2vec2-base-960h"
17
  # MODEL = "patrickvonplaten/wav2vec2-large-960h-lv60-self-4-gram"
18
+
19
+
20
+ # is cuda available?
21
+ cuda = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
22
+ device = 0 if torch.cuda.is_available() else -1
23
+ speech_recognizer = pipeline(
24
+ task="automatic-speech-recognition",
25
+ model=f"{MODEL}",
26
+ tokenizer=f"{MODEL}",
27
+ framework="pt",
28
+ device=device,
29
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
  videos_out_path = Path("./videos_out")
32
  videos_out_path.mkdir(parents=True, exist_ok=True)
33
 
34
+ samples_data = sorted(Path("examples").glob("*.json"))
35
  SAMPLES = []
36
  for file in samples_data:
37
  with open(file) as f:
38
  sample = json.load(f)
39
  SAMPLES.append(sample)
40
+ VIDEOS = list(map(lambda x: [x["video"]], SAMPLES))
41
 
42
  total_inferences_since_reboot = 415
43
  total_cuts_since_reboot = 1539
44
 
45
 
46
+ @spaces.GPU(duration=120)
47
  async def speech_to_text(video_file_path):
48
  """
49
  Takes a video path to convert to audio, transcribe audio channel to text and char timestamps
 
51
  Using https://huggingface.co/tasks/automatic-speech-recognition pipeline
52
  """
53
  global total_inferences_since_reboot
54
+ if video_file_path == None:
55
  raise ValueError("Error no video input")
56
 
57
  video_path = Path(video_file_path)
58
  try:
59
  # convert video to audio 16k using PIPE to audio_memory
60
+ audio_memory, _ = (
61
+ ffmpeg.input(video_path)
62
+ .output("-", format="wav", ac=1, ar="16k")
63
+ .overwrite_output()
64
+ .global_args("-loglevel", "quiet")
65
+ .run(capture_stdout=True)
66
+ )
67
  except Exception as e:
68
  raise RuntimeError("Error converting video to audio")
69
 
70
  ping("speech_to_text")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
 
72
+ try:
73
+ print(f"Transcribing via local model")
74
+ output = speech_recognizer(
75
+ audio_memory,
76
+ return_timestamps="char",
77
+ chunk_length_s=10,
78
+ stride_length_s=(4, 2),
79
+ )
80
+
81
+ transcription = output["text"].lower()
82
+ timestamps = [
83
+ [
84
+ chunk["text"].lower(),
85
+ chunk["timestamp"][0].tolist(),
86
+ chunk["timestamp"][1].tolist(),
87
+ ]
88
+ for chunk in output["chunks"]
89
+ ]
90
+ total_inferences_since_reboot += 1
91
+
92
+ print(
93
+ "\n\ntotal_inferences_since_reboot: ",
94
+ total_inferences_since_reboot,
95
+ "\n\n",
96
+ )
97
+ return (transcription, transcription, timestamps)
98
+ except Exception as e:
99
+ raise RuntimeError("Error Running inference with local model", e)
100
 
101
 
102
  async def cut_timestamps_to_video(video_in, transcription, text_in, timestamps):
 
108
 
109
  video_path = Path(video_in)
110
  video_file_name = video_path.stem
111
+ if video_in == None or text_in == None or transcription == None:
112
  raise ValueError("Inputs undefined")
113
 
114
  d = Differ()
115
  # compare original transcription with edit text
116
  diff_chars = d.compare(transcription, text_in)
117
  # remove all text aditions from diff
118
+ filtered = list(filter(lambda x: x[0] != "+", diff_chars))
119
 
120
  # filter timestamps to be removed
121
  # timestamps_to_cut = [b for (a,b) in zip(filtered, timestamps_var) if a[0]== '-' ]
 
124
  # groupping character timestamps so there are less cuts
125
  idx = 0
126
  grouped = {}
127
+ for a, b in zip(filtered, timestamps):
128
+ if a[0] != "-":
129
  if idx in grouped:
130
  grouped[idx].append(b)
131
  else:
 
137
  # after grouping, gets the lower and upter start and time for each group
138
  timestamps_to_cut = [[v[0][1], v[-1][2]] for v in grouped.values()]
139
 
140
+ between_str = "+".join(
141
+ map(lambda t: f"between(t,{t[0]},{t[1]})", timestamps_to_cut)
142
+ )
143
 
144
  if timestamps_to_cut:
145
  video_file = ffmpeg.input(video_in)
146
+ video = video_file.video.filter("select", f"({between_str})").filter(
147
+ "setpts", "N/FRAME_RATE/TB"
148
+ )
149
+ audio = video_file.audio.filter("aselect", f"({between_str})").filter(
150
+ "asetpts", "N/SR/TB"
151
+ )
152
+
153
+ output_video = f"./videos_out/{video_file_name}.mp4"
154
  ffmpeg.concat(video, audio, v=1, a=1).output(
155
+ output_video
156
+ ).overwrite_output().global_args("-loglevel", "quiet").run()
157
  else:
158
  output_video = video_in
159
 
160
+ tokens = [(token[2:], token[0] if token[0] != " " else None) for token in filtered]
 
161
 
162
  total_cuts_since_reboot += 1
163
  ping("video_cuts")
 
165
  return (tokens, output_video)
166
 
167
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  def ping(name):
169
+ url = f"https://huggingface.co/api/telemetry/spaces/radames/edit-video-by-editing-text/{name}"
170
  print("ping: ", url)
171
 
172
  async def req():
173
  async with aiohttp.ClientSession() as session:
174
  async with session.get(url) as response:
175
  print("pong: ", response.status)
176
+
177
  asyncio.create_task(req())
178
 
179
 
 
207
  """)
208
 
209
  with gr.Row():
 
210
  examples.render()
211
 
212
  def load_example(id):
213
+ video = SAMPLES[id]["video"]
214
+ transcription = SAMPLES[id]["transcription"].lower()
215
+ timestamps = SAMPLES[id]["timestamps"]
216
 
217
  return (video, transcription, transcription, timestamps)
218
 
 
220
  load_example,
221
  inputs=[examples],
222
  outputs=[video_in, text_in, transcription_var, timestamps_var],
223
+ queue=False,
224
+ )
225
  with gr.Row():
226
  with gr.Column():
227
  video_in.render()
228
  transcribe_btn = gr.Button("Transcribe Audio")
229
+ transcribe_btn.click(
230
+ speech_to_text, [video_in], [text_in, transcription_var, timestamps_var]
231
+ )
232
 
233
  with gr.Row():
234
  gr.Markdown("""
 
241
  with gr.Row():
242
  cut_btn = gr.Button("Cut to video", elem_id="cut_btn")
243
  # send audio path and hidden variables
244
+ cut_btn.click(
245
+ cut_timestamps_to_video,
246
+ [video_in, transcription_var, text_in, timestamps_var],
247
+ [diff_out, video_out],
248
+ )
249
 
250
  reset_transcription = gr.Button(
251
+ "Reset to last trascription", elem_id="reset_btn"
252
+ )
253
+ reset_transcription.click(lambda x: x, transcription_var, text_in)
254
  with gr.Column():
255
  video_out.render()
256
  diff_out.render()
requirements.txt CHANGED
@@ -1,8 +1,9 @@
1
  torch
2
  transformers
3
- gradio==3.35.2
4
  datasets
5
  librosa
6
  ffmpeg-python
7
  python-dotenv
8
- aiohttp
 
 
1
  torch
2
  transformers
3
+ gradio==5.49.1
4
  datasets
5
  librosa
6
  ffmpeg-python
7
  python-dotenv
8
+ aiohttp
9
+ spaces