katospiegel commited on
Commit
f84745b
·
1 Parent(s): 5aed115

feat: Improving CLI with Verbose Quantize parameters

Browse files
Files changed (5) hide show
  1. .env.dist +5 -1
  2. app/app.py +20 -15
  3. app/app.sh +8 -6
  4. app/gradio_app.py +85 -17
  5. requirements.txt +2 -1
.env.dist CHANGED
@@ -19,4 +19,8 @@ ODTP_DIGITAL_TWIN=
19
  ODTP_EXCUTION=
20
  ODTP_STEP=
21
  ODTP_COMPONENT=
22
- ODTP_COMPONENT_VERSION=
 
 
 
 
 
19
  ODTP_EXCUTION=
20
  ODTP_STEP=
21
  ODTP_COMPONENT=
22
+ ODTP_COMPONENT_VERSION=
23
+
24
+ #ODTP_API_MODE=TRUE
25
+ #ODTP_GRADIO_SHARE=TRUE
26
+ TODO: User and password
app/app.py CHANGED
@@ -28,6 +28,8 @@ import paragraphsCreator
28
  from pydub import AudioSegment
29
  import yt_dlp
30
 
 
 
31
 
32
 
33
 
@@ -59,8 +61,8 @@ class WhisperFacade(ASRFacade):
59
  def __init__(self, model: str, *, quantize=False) -> None:
60
  print("Initialize Whisper")
61
  whisper_model = whisper.load_model(model)
62
- if quantize:
63
- print("Quantize")
64
  DTYPE = torch.qint8
65
  qmodel: Whisper = torch.quantization.quantize_dynamic(
66
  whisper_model, {torch.nn.Linear}, dtype=DTYPE
@@ -68,6 +70,7 @@ class WhisperFacade(ASRFacade):
68
  del whisper_model
69
  self.wmodel = qmodel
70
  else:
 
71
  self.wmodel = whisper_model
72
 
73
  def load_audio(self, file_path: str):
@@ -451,7 +454,8 @@ def convert_mpx_to_wav(file_path):
451
  def download_youtube_video(url, output_path='/tmp'):
452
  ydl_opts = {
453
  'format': 'bestaudio/best',
454
- 'outtmpl': os.path.join(output_path, '%(title)s.%(ext)s'),
 
455
  'postprocessors': [{
456
  'key': 'FFmpegExtractAudio',
457
  'preferredcodec': 'wav',
@@ -465,28 +469,29 @@ def download_youtube_video(url, output_path='/tmp'):
465
  with yt_dlp.YoutubeDL(ydl_opts) as ydl:
466
  info_dict = ydl.extract_info(url, download=True)
467
  output_file = ydl.prepare_filename(info_dict)
468
-
469
  base, ext = os.path.splitext(output_file)
 
470
  new_file = base + '.wav'
471
- print(new_file)
472
  return new_file
473
 
474
 
475
 
476
  def main(args):
477
- # Really dirty way to handle youtube links
478
- # We should refactor this to be more robust
479
- # with different input sources
480
- possible_link = args.input_file.replace("/odtp/odtp-input/", "")
481
- if possible_link.startswith('http://') or possible_link.startswith('https://'):
482
- file_path = download_youtube_video(possible_link, output_path=os.path.dirname(args.output_file))
483
  #file_path = convert_mpx_to_wav(file_path)
484
  elif args.input_file.lower().endswith('.mp3'):
485
  file_path = convert_mpx_to_wav(args.input_file)
 
486
  elif args.input_file.lower().endswith('.wav'):
487
  file_path = args.input_file
 
488
  elif args.input_file.lower().endswith('.mp4'):
489
  file_path = convert_mpx_to_wav(args.input_file)
 
490
  else:
491
  raise ValueError("Input file must be an MP3, WAV or MP4 file")
492
 
@@ -517,7 +522,7 @@ def main(args):
517
  "word_timestamps": False
518
  }
519
 
520
- if args.verbose == "True":
521
  print("Process diarized blocks")
522
 
523
 
@@ -527,7 +532,7 @@ def main(args):
527
  current_end = None
528
 
529
  for turn, _, speaker in diarization.itertracks(yield_label=True):
530
- if args.verbose=="True":
531
  print(speaker)
532
  if turn.end - turn.start < 0.5:
533
  # ignore short utterances
@@ -553,7 +558,7 @@ def main(args):
553
  result = asr_model.transcribe(start=start, end=end, options=whisper_options)
554
  language = result.get('language', args.language or 'unknown')
555
 
556
- if args.verbose=="True":
557
  print(f"start={start:.1f}s stop={end:.1f}s lang={language} {speaker}")
558
 
559
  # Use your existing logic to write SRT, JSON, etc.
@@ -588,7 +593,7 @@ if __name__ == '__main__':
588
  parser.add_argument('--output-paragraphs-json-file', type=str, required=True, help="Output paragraphs file")
589
  parser.add_argument('--output-md-file', type=str, required=True, help="Output markdown file")
590
  parser.add_argument('--output-pdf-file', type=str, required=True, help="Output pdf file")
591
- parser.add_argument('--verbose', type=str, required=False, help="Printing status")
592
 
593
  args = parser.parse_args()
594
  main(args)
 
28
  from pydub import AudioSegment
29
  import yt_dlp
30
 
31
+ from slugify import slugify
32
+ import uuid
33
 
34
 
35
 
 
61
  def __init__(self, model: str, *, quantize=False) -> None:
62
  print("Initialize Whisper")
63
  whisper_model = whisper.load_model(model)
64
+ if quantize == True:
65
+ print("Quantize active")
66
  DTYPE = torch.qint8
67
  qmodel: Whisper = torch.quantization.quantize_dynamic(
68
  whisper_model, {torch.nn.Linear}, dtype=DTYPE
 
70
  del whisper_model
71
  self.wmodel = qmodel
72
  else:
73
+ print("Quantize non active")
74
  self.wmodel = whisper_model
75
 
76
  def load_audio(self, file_path: str):
 
454
  def download_youtube_video(url, output_path='/tmp'):
455
  ydl_opts = {
456
  'format': 'bestaudio/best',
457
+ #'outtmpl': os.path.join(output_path, '%(title)s.%(ext)s'),
458
+ 'outtmpl': os.path.join(output_path, f"{str(uuid.uuid4())}.%(ext)s"),
459
  'postprocessors': [{
460
  'key': 'FFmpegExtractAudio',
461
  'preferredcodec': 'wav',
 
469
  with yt_dlp.YoutubeDL(ydl_opts) as ydl:
470
  info_dict = ydl.extract_info(url, download=True)
471
  output_file = ydl.prepare_filename(info_dict)
472
+ print(output_file)
473
  base, ext = os.path.splitext(output_file)
474
+
475
  new_file = base + '.wav'
 
476
  return new_file
477
 
478
 
479
 
480
  def main(args):
481
+ # TODO: Take out the file_path from ODTP here
482
+ if args.input_file.startswith('http://') or args.input_file.startswith('https://'):
483
+ file_path = download_youtube_video(args.input_file, output_path=os.path.dirname(args.output_file))
484
+ base_slug = slugify(file_path, separator='_')
 
 
485
  #file_path = convert_mpx_to_wav(file_path)
486
  elif args.input_file.lower().endswith('.mp3'):
487
  file_path = convert_mpx_to_wav(args.input_file)
488
+ file_path = "/odtp/odtp-input/" + file_path
489
  elif args.input_file.lower().endswith('.wav'):
490
  file_path = args.input_file
491
+ file_path = "/odtp/odtp-input/" + file_path
492
  elif args.input_file.lower().endswith('.mp4'):
493
  file_path = convert_mpx_to_wav(args.input_file)
494
+ file_path = "/odtp/odtp-input/" + file_path
495
  else:
496
  raise ValueError("Input file must be an MP3, WAV or MP4 file")
497
 
 
522
  "word_timestamps": False
523
  }
524
 
525
+ if args.verbose:
526
  print("Process diarized blocks")
527
 
528
 
 
532
  current_end = None
533
 
534
  for turn, _, speaker in diarization.itertracks(yield_label=True):
535
+ if args.verbose:
536
  print(speaker)
537
  if turn.end - turn.start < 0.5:
538
  # ignore short utterances
 
558
  result = asr_model.transcribe(start=start, end=end, options=whisper_options)
559
  language = result.get('language', args.language or 'unknown')
560
 
561
+ if args.verbose:
562
  print(f"start={start:.1f}s stop={end:.1f}s lang={language} {speaker}")
563
 
564
  # Use your existing logic to write SRT, JSON, etc.
 
593
  parser.add_argument('--output-paragraphs-json-file', type=str, required=True, help="Output paragraphs file")
594
  parser.add_argument('--output-md-file', type=str, required=True, help="Output markdown file")
595
  parser.add_argument('--output-pdf-file', type=str, required=True, help="Output pdf file")
596
+ parser.add_argument('--verbose', action='store_true', help="Printing status")
597
 
598
  args = parser.parse_args()
599
  main(args)
app/app.sh CHANGED
@@ -3,26 +3,28 @@
3
  if [ -n "$LANGUAGE" ]; then
4
  python3 /odtp/odtp-app/app.py \
5
  --model $MODEL \
6
- --quantize \
7
  --hf-token $HF_TOKEN \
8
  --task $TASK \
9
  --language $LANGUAGE \
10
- --input-file /odtp/odtp-input/$INPUT_FILE \
11
  --output-file /odtp/odtp-output/$OUTPUT_FILE.srt \
12
  --output-json-file /odtp/odtp-output/$OUTPUT_FILE.json \
13
  --output-paragraphs-json-file /odtp/odtp-output/$OUTPUT_FILE-paragraphs.json \
14
  --output-md-file /odtp/odtp-output/$OUTPUT_FILE.md \
15
- --output-pdf-file /odtp/odtp-output/$OUTPUT_FILE.pdf
 
16
  else
17
  python3 /odtp/odtp-app/app.py \
18
  --model $MODEL \
19
- --quantize \
20
  --hf-token $HF_TOKEN \
21
  --task $TASK \
22
- --input-file /odtp/odtp-input/$INPUT_FILE \
23
  --output-file /odtp/odtp-output/$OUTPUT_FILE.srt \
24
  --output-json-file /odtp/odtp-output/$OUTPUT_FILE.json \
25
  --output-paragraphs-json-file /odtp/odtp-output/$OUTPUT_FILE-paragraphs.json \
26
  --output-md-file /odtp/odtp-output/$OUTPUT_FILE.md \
27
- --output-pdf-file /odtp/odtp-output/$OUTPUT_FILE.pdf
 
28
  fi
 
3
  if [ -n "$LANGUAGE" ]; then
4
  python3 /odtp/odtp-app/app.py \
5
  --model $MODEL \
6
+ $( [ "$QUANTIZE" = "TRUE" ] && echo "--quantize" ) \
7
  --hf-token $HF_TOKEN \
8
  --task $TASK \
9
  --language $LANGUAGE \
10
+ --input-file $INPUT_FILE \
11
  --output-file /odtp/odtp-output/$OUTPUT_FILE.srt \
12
  --output-json-file /odtp/odtp-output/$OUTPUT_FILE.json \
13
  --output-paragraphs-json-file /odtp/odtp-output/$OUTPUT_FILE-paragraphs.json \
14
  --output-md-file /odtp/odtp-output/$OUTPUT_FILE.md \
15
+ --output-pdf-file /odtp/odtp-output/$OUTPUT_FILE.pdf \
16
+ $( [ "$VERBOSE" = "TRUE" ] && echo "--verbose" )
17
  else
18
  python3 /odtp/odtp-app/app.py \
19
  --model $MODEL \
20
+ $( [ "$QUANTIZE" = "TRUE" ] && echo "--quantize" ) \
21
  --hf-token $HF_TOKEN \
22
  --task $TASK \
23
+ --input-file $INPUT_FILE \
24
  --output-file /odtp/odtp-output/$OUTPUT_FILE.srt \
25
  --output-json-file /odtp/odtp-output/$OUTPUT_FILE.json \
26
  --output-paragraphs-json-file /odtp/odtp-output/$OUTPUT_FILE-paragraphs.json \
27
  --output-md-file /odtp/odtp-output/$OUTPUT_FILE.md \
28
+ --output-pdf-file /odtp/odtp-output/$OUTPUT_FILE.pdf \
29
+ $( [ "$VERBOSE" = "TRUE" ] && echo "--verbose" )
30
  fi
app/gradio_app.py CHANGED
@@ -5,6 +5,8 @@ import shutil
5
  import subprocess
6
  import threading
7
  import time
 
 
8
 
9
  def create_temp_structure():
10
  """Create temporary ODTP folder structure"""
@@ -22,7 +24,13 @@ def cleanup_temp(temp_dir):
22
  """Remove temporary folder structure"""
23
  shutil.rmtree(temp_dir)
24
 
25
- def process_audio(audio_file, model, task, language, hf_token=None):
 
 
 
 
 
 
26
  """Process audio file with Whisper and Pyannote"""
27
  # Create temp structure
28
  temp_dir = create_temp_structure()
@@ -30,16 +38,28 @@ def process_audio(audio_file, model, task, language, hf_token=None):
30
  start_time = time.time()
31
  print(f"Processing started at: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(start_time))}")
32
 
33
- # Copy input file
34
- input_path = os.path.join(temp_dir, "odtp-input", "input.wav")
35
- shutil.copy2(audio_file, input_path)
36
-
37
- # Prepare output paths #TODO: Add uuid to output file names
38
- output_base = audio_file.split("/")[-1].replace(".wav", "")
39
- output_srt = os.path.join(temp_dir, "odtp-output", #temp_dir
40
- f"{output_base}_{task}.srt")
41
- output_json = os.path.join(temp_dir, "odtp-output",
42
- f"{output_base}_{task}.json")
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
  # Use HF_TOKEN from environment if not provided
45
  if not hf_token:
@@ -51,14 +71,19 @@ def process_audio(audio_file, model, task, language, hf_token=None):
51
  cmd = [
52
  "python3", "/odtp/odtp-app/app.py",
53
  "--model", model,
54
- "--quantize",
55
  "--hf-token", hf_token,
56
  "--task", task,
57
  "--input-file", input_path,
58
  "--output-file", output_srt,
59
  "--output-json-file", output_json,
 
 
 
60
  "--verbose", "False"
61
  ]
 
 
 
62
 
63
  if language != "auto":
64
  cmd.extend(["--language", language])
@@ -84,7 +109,7 @@ def process_audio(audio_file, model, task, language, hf_token=None):
84
  total_duration_str = f"{int(hours)}h {int(minutes)}m {int(seconds)}s"
85
  print(f"Total processing time: {total_duration_str}")
86
 
87
- return total_duration_str, srt_content, json_content, output_srt, output_json
88
 
89
  # Define Gradio interface
90
  with gr.Blocks() as demo:
@@ -97,10 +122,23 @@ with gr.Blocks() as demo:
97
  label="Upload Audio File (WAV format)"
98
  )
99
  model = gr.Dropdown(
100
- choices=["tiny", "base", "small", "medium", "large", "large-v2", "large-v3", "large-v3-turbo", "softcatala/whisper-base-ca", "projecte-aina/whisper-large-v3-ca-3catparla"],
 
 
 
 
 
 
 
 
 
101
  value="base",
102
  label="Whisper Model"
103
  )
 
 
 
 
104
  task = gr.Dropdown(
105
  choices=["transcribe", "translate"],
106
  value="transcribe",
@@ -116,6 +154,11 @@ with gr.Blocks() as demo:
116
  type="password",
117
  placeholder="Leave blank if not applicable"
118
  )
 
 
 
 
 
119
  submit_btn = gr.Button("Process Audio")
120
 
121
  with gr.Column():
@@ -139,14 +182,39 @@ with gr.Blocks() as demo:
139
  label="Download JSON File",
140
  type="binary"
141
  )
 
 
 
 
 
 
 
 
 
 
 
 
142
 
143
  submit_btn.click(
144
  fn=process_audio,
145
- inputs=[audio_input, model, task, language, hf_token],
146
- outputs=[information, srt_output, json_output, srt_download, json_download]
 
 
 
 
 
 
 
 
 
 
 
 
 
147
  )
148
 
149
- import argparse
150
 
151
  if __name__ == "__main__":
152
  parser = argparse.ArgumentParser(description="Launch Gradio app with optional sharing.")
 
5
  import subprocess
6
  import threading
7
  import time
8
+ import argparse
9
+ import uuid
10
 
11
  def create_temp_structure():
12
  """Create temporary ODTP folder structure"""
 
24
  """Remove temporary folder structure"""
25
  shutil.rmtree(temp_dir)
26
 
27
+ def process_audio(audio_file,
28
+ youtube_link,
29
+ model,
30
+ task,
31
+ language,
32
+ hf_token=None,
33
+ quantize=False):
34
  """Process audio file with Whisper and Pyannote"""
35
  # Create temp structure
36
  temp_dir = create_temp_structure()
 
38
  start_time = time.time()
39
  print(f"Processing started at: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(start_time))}")
40
 
41
+ if youtube_link:
42
+ input_path = youtube_link
43
+
44
+ id = str(uuid.uuid4())
45
+ output_base = f"{id}"
46
+ output_srt = os.path.join(temp_dir, "odtp-output", f"{id}.srt")
47
+ output_json = os.path.join(temp_dir, "odtp-output", f"{id}.json")
48
+ output_paragraphs_json = os.path.join(temp_dir, "odtp-output", f"{id}_paragraphs.json")
49
+ output_md_file = os.path.join(temp_dir, "odtp-output", f"{id}.md")
50
+ output_pdf_file = os.path.join(temp_dir, "odtp-output", f"{id}.pdf")
51
+
52
+ else:
53
+ # Copy input file
54
+ input_path = os.path.join(temp_dir, "odtp-input", "input.wav")
55
+ shutil.copy2(audio_file, input_path)
56
+
57
+ output_base = audio_file.split("/")[-1].replace(".wav", "")
58
+ output_srt = os.path.join(temp_dir, "odtp-output", f"{output_base}_{task}.srt")
59
+ output_json = os.path.join(temp_dir, "odtp-output", f"{output_base}_{task}.json")
60
+ output_paragraphs_json = os.path.join(temp_dir, "odtp-output", f"{output_base}_{task}_paragraphs.json")
61
+ output_md_file = os.path.join(temp_dir, "odtp-output", f"{output_base}_{task}.md")
62
+ output_pdf_file = os.path.join(temp_dir, "odtp-output", f"{output_base}_{task}.pdf")
63
 
64
  # Use HF_TOKEN from environment if not provided
65
  if not hf_token:
 
71
  cmd = [
72
  "python3", "/odtp/odtp-app/app.py",
73
  "--model", model,
 
74
  "--hf-token", hf_token,
75
  "--task", task,
76
  "--input-file", input_path,
77
  "--output-file", output_srt,
78
  "--output-json-file", output_json,
79
+ "--output-paragraphs-json-file", output_paragraphs_json,
80
+ "--output-md-file", output_md_file,
81
+ "--output-pdf-file", output_pdf_file,
82
  "--verbose", "False"
83
  ]
84
+
85
+ if quantize:
86
+ cmd.append("--quantize")
87
 
88
  if language != "auto":
89
  cmd.extend(["--language", language])
 
109
  total_duration_str = f"{int(hours)}h {int(minutes)}m {int(seconds)}s"
110
  print(f"Total processing time: {total_duration_str}")
111
 
112
+ return total_duration_str, srt_content, json_content, output_srt, output_json, output_paragraphs_json, output_md_file, output_pdf_file
113
 
114
  # Define Gradio interface
115
  with gr.Blocks() as demo:
 
122
  label="Upload Audio File (WAV format)"
123
  )
124
  model = gr.Dropdown(
125
+ choices=["tiny",
126
+ "base",
127
+ "small",
128
+ "medium",
129
+ "large",
130
+ "large-v2",
131
+ "large-v3",
132
+ "large-v3-turbo",
133
+ "softcatala/whisper-base-ca",
134
+ "projecte-aina/whisper-large-v3-ca-3catparla"],
135
  value="base",
136
  label="Whisper Model"
137
  )
138
+ youtube_link = gr.Textbox(
139
+ type="text",
140
+ label="Youtube Link (optional)",
141
+ )
142
  task = gr.Dropdown(
143
  choices=["transcribe", "translate"],
144
  value="transcribe",
 
154
  type="password",
155
  placeholder="Leave blank if not applicable"
156
  )
157
+ quantize = gr.Checkbox(
158
+ label="Quantize Model",
159
+ value=False
160
+ )
161
+
162
  submit_btn = gr.Button("Process Audio")
163
 
164
  with gr.Column():
 
182
  label="Download JSON File",
183
  type="binary"
184
  )
185
+ json_paragraphs_download = gr.File(
186
+ label="Download JSON Paragraphs File",
187
+ type="binary"
188
+ )
189
+ md_download = gr.File(
190
+ label="Download MD File",
191
+ type="binary"
192
+ )
193
+ pdf_download = gr.File(
194
+ label="Download PDF File",
195
+ type="binary"
196
+ )
197
 
198
  submit_btn.click(
199
  fn=process_audio,
200
+ inputs=[audio_input,
201
+ youtube_link,
202
+ model,
203
+ task,
204
+ language,
205
+ hf_token,
206
+ quantize],
207
+ outputs=[information,
208
+ srt_output,
209
+ json_output,
210
+ srt_download,
211
+ json_download,
212
+ json_paragraphs_download,
213
+ md_download,
214
+ pdf_download]
215
  )
216
 
217
+
218
 
219
  if __name__ == "__main__":
220
  parser = argparse.ArgumentParser(description="Launch Gradio app with optional sharing.")
requirements.txt CHANGED
@@ -10,4 +10,5 @@ gradio==5.5.0
10
  numpy==1.24.4
11
  md2pdf==1.0.1
12
  transformers==4.48.0
13
- yt-dlp
 
 
10
  numpy==1.24.4
11
  md2pdf==1.0.1
12
  transformers==4.48.0
13
+ yt-dlp
14
+ python-slugify