Updated with code
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
__pycache__/
|
||||
*.pyc
|
||||
.env
|
||||
.DS_Store
|
||||
25
README.md
Normal file
25
README.md
Normal file
@ -0,0 +1,25 @@
|
||||
# OBS Recording Transcriber
|
||||
|
||||
Process OBS recordings with AI-based transcription and summarization.
|
||||
|
||||
|
||||
## Features
|
||||
- AI transcription using Whisper.
|
||||
- Summarization using Hugging Face Transformers.
|
||||
- File selection, resource validation, and error handling.
|
||||
|
||||
## Installation
|
||||
1. Clone the repo.
|
||||
git clone https://github.com/yourusername/OBS_Recording_Transcriber.git
|
||||
cd OBS_Recording_Transcriber
|
||||
2. Install dependencies:
|
||||
pip install -r requirements.txt
|
||||
|
||||
|
||||
Notes:
|
||||
Ensure that the versions align with the features you use and your system compatibility.
|
||||
torch version should match the capabilities of your hardware (e.g., CUDA support for GPUs).
|
||||
whisper might need to be installed from source or a GitHub repository if it's not available on PyPI.
|
||||
If you encounter any issues regarding compatibility, versions may need adjustments.
|
||||
|
||||
3. streamlit run app.py
|
||||
58
app.py
Normal file
58
app.py
Normal file
@ -0,0 +1,58 @@
|
||||
import streamlit as st
|
||||
from utils.audio_processing import extract_audio
|
||||
from utils.transcription import transcribe_audio
|
||||
from utils.summarization import summarize_text
|
||||
from utils.validation import validate_environment
|
||||
from pathlib import Path
|
||||
|
||||
def main():
|
||||
st.title("🎥 OBS Recording Transcriber")
|
||||
st.caption("Process your OBS recordings with AI transcription and summarization")
|
||||
|
||||
# Allow the user to select a base folder
|
||||
st.sidebar.header("Folder Selection")
|
||||
base_folder = st.sidebar.text_input(
|
||||
"Enter the base folder path:",
|
||||
value=str(Path.home())
|
||||
)
|
||||
|
||||
base_path = Path(base_folder)
|
||||
|
||||
# Validate environment
|
||||
env_errors = validate_environment(base_path)
|
||||
if env_errors:
|
||||
st.error("## Environment Issues")
|
||||
for error in env_errors:
|
||||
st.markdown(f"- {error}")
|
||||
return
|
||||
|
||||
# File selection
|
||||
recordings = list(base_path.glob("*.mp4"))
|
||||
if not recordings:
|
||||
st.warning(f"📂 No recordings found in the folder: {base_folder}!")
|
||||
return
|
||||
|
||||
selected_file = st.selectbox("Choose a recording", recordings)
|
||||
|
||||
if st.button("🚀 Start Processing"):
|
||||
try:
|
||||
transcript, summary = transcribe_audio(selected_file)
|
||||
if transcript:
|
||||
st.subheader("🖍 Summary")
|
||||
st.write(summary)
|
||||
st.subheader("📜 Full Transcript")
|
||||
with st.expander("View transcript content"):
|
||||
st.text(transcript)
|
||||
st.download_button(
|
||||
label="💾 Download Transcript",
|
||||
data=transcript,
|
||||
file_name=f"{Path(selected_file).stem}_transcript.txt",
|
||||
mime="text/plain"
|
||||
)
|
||||
else:
|
||||
st.error("❌ Failed to process recording")
|
||||
except Exception as e:
|
||||
st.error(f"An error occurred: {e}")
|
||||
st.write(e) # This will show the traceback in the Streamlit app
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
5
requirements.txt
Normal file
5
requirements.txt
Normal file
@ -0,0 +1,5 @@
|
||||
streamlit==1.26.0
|
||||
moviepy==1.0.3
|
||||
whisper
|
||||
transformers==4.21.1
|
||||
torch>=1.7.0
|
||||
12
utils/audio_processing.py
Normal file
12
utils/audio_processing.py
Normal file
@ -0,0 +1,12 @@
|
||||
from moviepy.editor import AudioFileClip
|
||||
from pathlib import Path
|
||||
|
||||
def extract_audio(video_path: Path):
|
||||
"""Extract audio from a video file."""
|
||||
try:
|
||||
audio = AudioFileClip(str(video_path))
|
||||
audio_path = video_path.parent / f"{video_path.stem}_audio.wav"
|
||||
audio.write_audiofile(str(audio_path), verbose=False, logger=None)
|
||||
return audio_path
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"Audio extraction failed: {e}")
|
||||
8
utils/summarization.py
Normal file
8
utils/summarization.py
Normal file
@ -0,0 +1,8 @@
|
||||
from transformers import pipeline
|
||||
|
||||
SUMMARY_MODEL = "Falconsai/text_summarization"
|
||||
|
||||
def summarize_text(text):
|
||||
"""Summarize text using a Hugging Face pipeline."""
|
||||
summarizer = pipeline("summarization", model=SUMMARY_MODEL)
|
||||
return summarizer(text, max_length=150, min_length=30, do_sample=False)[0]["summary_text"]
|
||||
60
utils/transcription.py
Normal file
60
utils/transcription.py
Normal file
@ -0,0 +1,60 @@
|
||||
import whisper
|
||||
from pathlib import Path
|
||||
from transformers import pipeline, AutoTokenizer
|
||||
|
||||
WHISPER_MODEL = "base"
|
||||
SUMMARIZATION_MODEL = "t5-base"
|
||||
|
||||
def transcribe_audio(audio_path: Path):
|
||||
"""Transcribe audio using Whisper."""
|
||||
model = whisper.load_model(WHISPER_MODEL)
|
||||
result = model.transcribe(str(audio_path))
|
||||
transcript = result["text"]
|
||||
summary = summarize_text(transcript)
|
||||
return transcript, summary
|
||||
|
||||
def summarize_text(text):
|
||||
"""Summarize text using a pre-trained T5 transformer model with chunking."""
|
||||
summarization_pipeline = pipeline("summarization", model=SUMMARIZATION_MODEL)
|
||||
tokenizer = AutoTokenizer.from_pretrained(SUMMARIZATION_MODEL)
|
||||
|
||||
max_tokens = 512
|
||||
|
||||
tokens = tokenizer(text, return_tensors='pt')
|
||||
num_tokens = len(tokens['input_ids'][0])
|
||||
|
||||
if num_tokens > max_tokens:
|
||||
chunks = chunk_text(text, max_tokens)
|
||||
summaries = []
|
||||
for chunk in chunks:
|
||||
summary_output = summarization_pipeline("summarize: " + chunk, max_length=150, min_length=30, do_sample=False)
|
||||
summaries.append(summary_output[0]['summary_text'])
|
||||
overall_summary = " ".join(summaries)
|
||||
else:
|
||||
overall_summary = summarization_pipeline("summarize: " + text, max_length=150, min_length=30, do_sample=False)[0]['summary_text']
|
||||
|
||||
return overall_summary
|
||||
|
||||
def chunk_text(text, max_tokens):
|
||||
"""Splits the text into a list of chunks based on token limits."""
|
||||
tokenizer = AutoTokenizer.from_pretrained(SUMMARIZATION_MODEL)
|
||||
words = text.split()
|
||||
|
||||
chunks = []
|
||||
current_chunk = []
|
||||
current_length = 0
|
||||
|
||||
for word in words:
|
||||
hypothetical_length = current_length + len(tokenizer(word, return_tensors='pt')['input_ids'][0]) - 2
|
||||
if hypothetical_length <= max_tokens:
|
||||
current_chunk.append(word)
|
||||
current_length = hypothetical_length
|
||||
else:
|
||||
chunks.append(' '.join(current_chunk))
|
||||
current_chunk = [word]
|
||||
current_length = len(tokenizer(word, return_tensors='pt')['input_ids'][0]) - 2
|
||||
|
||||
if current_chunk:
|
||||
chunks.append(' '.join(current_chunk))
|
||||
|
||||
return chunks
|
||||
8
utils/validation.py
Normal file
8
utils/validation.py
Normal file
@ -0,0 +1,8 @@
|
||||
from pathlib import Path
|
||||
|
||||
def validate_environment(obs_path: Path):
|
||||
"""Validate environment and prerequisites."""
|
||||
errors = []
|
||||
if not obs_path.exists():
|
||||
errors.append(f"OBS directory not found: {obs_path}")
|
||||
return errors
|
||||
Reference in New Issue
Block a user