Update: Refactored the code

This commit is contained in:
Izaya Orihara
2023-10-06 15:45:33 +02:00
parent 0b1c24de2a
commit 57cc73a439
4 changed files with 165 additions and 82 deletions
-5
View File
@@ -4,13 +4,8 @@
Transcriber code Transcriber code
Currently very unorganized
## to-do ## to-do
- clean the code
- organize the stuff
- add functions for other data sources
- seperate data production form code - seperate data production form code
``` ```
+46 -14
View File
@@ -1,41 +1,73 @@
import logging
import os import os
from pydub import AudioSegment from pydub import AudioSegment
from pytube import YouTube from pytube import YouTube, exceptions as pytube_exceptions
logging.basicConfig(level=logging.INFO)
class FileManager: class FileManager:
"""
FileManager class that handles operations such as downloading and converting audio.
"""
def __init__(self): def __init__(self):
pass pass
def download_youtube_audio(self, youtube_link, filename): @staticmethod
def download_youtube_audio(youtube_link: str, filename: str):
"""
Downloads audio from a YouTube link and saves it in the location specified by filename.
"""
try:
yt = YouTube(youtube_link) yt = YouTube(youtube_link)
except pytube_exceptions.PytubeError as e:
logging.error("An Error occurred while fetching the YouTube video: " + str(e))
return
# Get the highest quality audio stream
audio_stream = yt.streams.get_audio_only() audio_stream = yt.streams.get_audio_only()
# Download if the file doesn't already exist
if not os.path.exists(filename): if not os.path.exists(filename):
try:
audio_stream.download(filename=filename) audio_stream.download(filename=filename)
logging.info(f"Downloaded {youtube_link} successfully.")
except Exception as e:
logging.error("An Error occurred while downloading the YouTube video: " + str(e))
return
def convert_to_wav(self, mp4_file, wav_file): @staticmethod
def convert_to_wav(mp4_file: str, wav_file: str):
"""
Converts a mp4 audio file into wav format.
"""
try:
sound = AudioSegment.from_file(mp4_file, format="mp4") sound = AudioSegment.from_file(mp4_file, format="mp4")
sound.export(wav_file, format="wav") sound.export(wav_file, format="wav")
except Exception as e:
logging.error(f"An error occurred while converting {mp4_file} to wav: {e}")
def load_local_file(self, local_file_path): @staticmethod
# Check the file type def load_local_file(local_file_path: str) -> str:
"""
Loads a local file and converts it to wav format.
Return wav file path.
"""
ext = os.path.splitext(local_file_path)[-1].lower() ext = os.path.splitext(local_file_path)[-1].lower()
if ext == ".mp4": if ext in [".mp4", ".mp3"]:
wav_file = local_file_path.replace(".mp4", ".wav") wav_file = local_file_path.replace(ext, ".wav")
self.convert_to_wav(local_file_path, wav_file) try:
return wav_file audio = AudioSegment.from_file(local_file_path, format=ext[1:])
elif ext == ".mp3":
wav_file = local_file_path.replace(".mp3", ".wav")
audio = AudioSegment.from_file(local_file_path, format="mp3")
audio.export(wav_file, format="wav") audio.export(wav_file, format="wav")
except Exception as e:
logging.error(f"An error occurred while converting {local_file_path} to wav: {e}")
return wav_file return wav_file
elif ext == ".wav": elif ext == ".wav":
return local_file_path return local_file_path
else: else:
logging.error(f"Unsupported file format: {ext}.")
raise ValueError("Unsupported file format.") raise ValueError("Unsupported file format.")
+50 -13
View File
@@ -1,25 +1,44 @@
import gc import gc
import json import json
import logging
import torch import torch
import whisperx import whisperx
# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class Transcriber: class Transcriber:
"""A class to transcribe audio files using whisperx."""
def __init__(self, language_code="en", device="cuda", compute_type="float16"): def __init__(self, language_code: str = "en", device: str = "cuda", compute_type: str = "float16"):
"""
Init method.
:param language_code: str: Language code for whisperx transcription (default is "en").
:param device: str: Device to use for transcription (default is "cuda" if available, else "cpu").
:param compute_type: str: Compute type for whisperx transcription (default is "float16").
"""
self.device = device if torch.cuda.is_available() else "cpu" self.device = device if torch.cuda.is_available() else "cpu"
# Initialize the transcription and alignment models
# Initialize the transcription model try:
self.transcription_model = whisperx.load_model( self.transcription_model = whisperx.load_model("large-v2", device=self.device, compute_type=compute_type)
"large-v2", device=self.device, compute_type=compute_type) self.alignment_model, self.metadata = whisperx.load_align_model(language_code=language_code,
device=self.device)
# Initialize the alignment model except Exception as e:
self.alignment_model, self.metadata = whisperx.load_align_model( logger.exception("Failed to initialize models: %s", e)
language_code=language_code, device=self.device) raise
self.transcript_output = None self.transcript_output = None
def transcribe(self, wav_filename, batch_size=16): def transcribe(self, wav_filename: str, batch_size: int = 16) -> dict:
"""
Transcribe the wav file.
:param wav_filename: str: Path to the wav file.
:param batch_size: int: Batch size for whisperx transcription (default is 16).
:return: dict: Transcribed and aligned result.
"""
try:
# Load the audio file # Load the audio file
audio = whisperx.load_audio(wav_filename) audio = whisperx.load_audio(wav_filename)
@@ -27,8 +46,7 @@ class Transcriber:
result = self.transcription_model.transcribe(audio, batch_size=batch_size) result = self.transcription_model.transcribe(audio, batch_size=batch_size)
# Use the alignment model # Use the alignment model
result = whisperx.align( result = whisperx.align(result["segments"], self.alignment_model, self.metadata, audio, self.device,
result["segments"], self.alignment_model, self.metadata, audio, self.device,
return_char_alignments=False) return_char_alignments=False)
# Clear GPU memory # Clear GPU memory
@@ -44,8 +62,17 @@ class Transcriber:
self._clear_memory() self._clear_memory()
self.transcript_output = result self.transcript_output = result
return result return result
except Exception as e:
logger.exception("Failed to transcribe: %s", e)
raise
def save_transcript(self, output_json_file, output_txt_file): def save_transcript(self, output_json_file: str, output_txt_file: str):
"""
Save the transcription result to a json file and a text file.
:param output_json_file: str: Path to the output json file.
:param output_txt_file: str: Path to the output text file.
"""
try:
# Save full results to a json file # Save full results to a json file
with open(output_json_file, 'w') as f: with open(output_json_file, 'w') as f:
json.dump(self.transcript_output, f) json.dump(self.transcript_output, f)
@@ -54,10 +81,20 @@ class Transcriber:
transcripts = [seg['value'] for seg in self.transcript_output['segments']] transcripts = [seg['value'] for seg in self.transcript_output['segments']]
with open(output_txt_file, 'w') as f: with open(output_txt_file, 'w') as f:
f.write('\n'.join(transcripts)) f.write('\n'.join(transcripts))
except Exception as e:
logger.exception("Failed to save transcript: %s", e)
raise
def _clear_memory(self): def _clear_memory(self):
"""
Clear GPU memory.
"""
try:
del self.transcription_model del self.transcription_model
del self.alignment_model del self.alignment_model
del self.metadata del self.metadata
gc.collect() gc.collect()
torch.cuda.empty_cache() torch.cuda.empty_cache()
except Exception as e:
logger.exception("Failed to clear GPU memory: %s", e)
raise
+25 -6
View File
@@ -1,27 +1,46 @@
import logging
from typing import List
from src.file_manager import FileManager from src.file_manager import FileManager
from src.transcriber import Transcriber from src.transcriber import Transcriber
# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class TranscriptGenerator: class TranscriptGenerator:
"""
TranscriptGenerator class that coordinates FileManager and Transcriber classes
to generate transcriptions from given input sources.
"""
def __init__(self): def __init__(self):
self.file_manager = FileManager() self.file_manager = FileManager()
self.transcriber = Transcriber() self.transcriber = Transcriber()
def generate(self, input_sources): def generate(self, input_sources: List[str]) -> None:
"""
Generates transcriptions for the given list of input sources.
Args:
input_sources (List[str]): List of input sources which could be URLs or local file paths.
"""
for i, source in enumerate(input_sources): for i, source in enumerate(input_sources):
try:
if source.startswith('http'): if source.startswith('http'):
mp4_filename = f"audio_{i + 1}.mp4" mp4_filename = f"audio_{i + 1}.mp4"
wav_filename = f"audio_{i + 1}.wav" wav_filename = f"audio_{i + 1}.wav"
self.file_manager.download_youtube_audio(source, mp4_filename) self.file_manager.download_youtube_audio(source, mp4_filename)
self.file_manager.convert_to_wav(mp4_filename, wav_filename) self.file_manager.convert_to_wav(mp4_filename, wav_filename)
else: else:
wav_filename = self.file_manager.load_local_file(source) wav_filename = self.file_manager.load_local_file(source)
self.transcriber.transcribe(wav_filename)
transcription = self.transcriber.transcribe(wav_filename)
json_output_file = f'result_{i + 1}.json' json_output_file = f'result_{i + 1}.json'
txt_output_file = f'transcripts_{i + 1}.txt' txt_output_file = f'transcripts_{i + 1}.txt'
self.transcriber.save_transcript(json_output_file, txt_output_file) self.transcriber.save_transcript(json_output_file, txt_output_file)
except Exception as e:
logger.exception(f"Failed to generate transcript for source {source}. Error: {str(e)}")
continue
print('Transcription generation completed.') logger.info('Transcription generation completed.')