You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
205 lines
8.5 KiB
205 lines
8.5 KiB
import sys |
|
import os |
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "src"))) |
|
|
|
import streamlit as st |
|
import xml.etree.ElementTree as ET |
|
from hindenburg_api.transcription import Transcription, transcribe |
|
from hindenburg_api.project import Project |
|
|
|
# Remove hardcoded paths, we'll select them in the UI |
|
# PROJECT_PATH = "tests/demo_project/demo.nhsx" |
|
# AUDIOPOOL_PATH = "tests/demo_project/" |
|
|
|
# Remove the get_audio_files_from_project function since it's now in the Project class |
|
|
|
def main(): |
|
st.title("Hindenburg Project Transcription Tool") |
|
|
|
# File uploader for project file |
|
uploaded_file = st.file_uploader("Choose a Hindenburg project file (.nhsx)", type="nhsx") |
|
|
|
if uploaded_file is None: |
|
st.info("Please upload a project file to continue") |
|
return |
|
|
|
# Save the uploaded file to a temporary location |
|
temp_project_path = f"temp_{uploaded_file.name}" |
|
with open(temp_project_path, "wb") as f: |
|
f.write(uploaded_file.getbuffer()) |
|
|
|
st.success(f"Project file loaded: {uploaded_file.name}") |
|
|
|
# Get the project directory (parent directory of the project file) |
|
project_dir = os.path.dirname(temp_project_path) |
|
|
|
# Load the project |
|
project = Project(temp_project_path) |
|
project.load_project() |
|
|
|
# Get the audio pool path from the project XML |
|
audio_pool_path = "" |
|
tree = ET.parse(temp_project_path) |
|
root = tree.getroot() |
|
audio_pool_elem = root.find("AudioPool") |
|
if audio_pool_elem is not None: |
|
pool_path = audio_pool_elem.get("Path", "") |
|
pool_location = audio_pool_elem.get("Location", "") |
|
if pool_location and os.path.exists(pool_location): |
|
audio_pool_path = os.path.join(pool_location, pool_path) |
|
else: |
|
# Try different strategies to find the audio files |
|
potential_paths = [ |
|
os.path.join(os.path.dirname(temp_project_path), pool_path), # Look in same dir as project |
|
pool_location, # Use location directly |
|
os.path.join(project_dir, os.path.basename(pool_location)) # Use basename |
|
] |
|
|
|
for path in potential_paths: |
|
if path and os.path.exists(path): |
|
audio_pool_path = path |
|
break |
|
|
|
if not audio_pool_path or not os.path.exists(audio_pool_path): |
|
# Allow user to select the audio files directory |
|
st.warning("Could not automatically locate audio files directory.") |
|
audio_dir = st.text_input("Enter the path to your audio files directory:") |
|
if audio_dir and os.path.exists(audio_dir): |
|
audio_pool_path = audio_dir |
|
else: |
|
st.error("Please provide a valid audio files directory path.") |
|
return |
|
|
|
st.success(f"Audio files directory found: {audio_pool_path}") |
|
|
|
# Get audio files from the project |
|
audio_files = project.get_audio_files() |
|
|
|
if not audio_files: |
|
st.warning("No audio files found in the project") |
|
return |
|
|
|
st.write("Select files to transcribe:") |
|
|
|
# Create a container for the file list |
|
file_container = st.container() |
|
|
|
selected = [] |
|
with file_container: |
|
for f in audio_files: |
|
# Add a unique key for each file's state |
|
file_key = f"file_{f['id']}" |
|
if file_key not in st.session_state: |
|
st.session_state[file_key] = { |
|
"selected": False, |
|
"min_speakers": 2, |
|
"max_speakers": 2 |
|
} |
|
|
|
# Display file info |
|
col1, col2, col3, col4 = st.columns([3, 2, 2, 1]) |
|
with col1: |
|
# Use display_name if available, otherwise fall back to name |
|
display_name = f.get("display_name", f["name"]) |
|
st.write(display_name) |
|
with col2: |
|
st.write(f["duration"]) |
|
with col3: |
|
st.write("Yes" if f["has_transcription"] else "No") |
|
with col4: |
|
# Use a label to avoid accessibility warnings |
|
checked = st.checkbox("Select", key=f"chk_{f['id']}", value=False, |
|
disabled=f["has_transcription"], |
|
label_visibility="collapsed") |
|
|
|
if checked and not f["has_transcription"]: |
|
# Expand settings for selected files |
|
with st.expander(f"Settings for {display_name}", expanded=False): |
|
col_min, col_max = st.columns(2) |
|
with col_min: |
|
min_speakers = st.number_input("Min Speakers", |
|
min_value=1, |
|
max_value=5, |
|
value=2, |
|
key=f"min_{f['id']}") |
|
with col_max: |
|
max_speakers = st.number_input("Max Speakers", |
|
min_value=min_speakers, |
|
max_value=5, |
|
value=2, |
|
key=f"max_{f['id']}") |
|
|
|
# Add to selected files with speaker settings |
|
f_with_settings = f.copy() |
|
f_with_settings["min_speakers"] = min_speakers |
|
f_with_settings["max_speakers"] = max_speakers |
|
selected.append(f_with_settings) |
|
|
|
if st.button("Transcribe Selected"): |
|
if not selected: |
|
st.warning("Please select at least one file to transcribe") |
|
return |
|
|
|
progress_bar = st.progress(0) |
|
status_text = st.empty() |
|
transcription_successful = False |
|
|
|
for i, f in enumerate(selected): |
|
status_text.write(f"Transcribing {f['name']}...") |
|
|
|
# Try different places to find the audio file |
|
audio_file_found = False |
|
potential_audio_paths = [ |
|
os.path.join(audio_pool_path, f["name"]), |
|
os.path.join(audio_pool_path, "demo Files", f["name"]), |
|
os.path.join(audio_pool_path, "..", "demo Files", f["name"]), |
|
os.path.join(pool_location, f["name"]) if 'pool_location' in locals() else None |
|
] |
|
|
|
for audio_path in potential_audio_paths: |
|
if audio_path and os.path.exists(audio_path): |
|
audio_file_found = True |
|
break |
|
|
|
if not audio_file_found: |
|
st.error(f"Audio file not found: {f['name']}") |
|
continue |
|
|
|
try: |
|
# Get min and max speakers from the file settings |
|
min_speakers = f.get("min_speakers", 2) |
|
max_speakers = f.get("max_speakers", 3) |
|
|
|
st.info(f"Using {min_speakers} min and {max_speakers} max speakers for {f['name']}") |
|
|
|
segments = transcribe(audio_path, min_speakers=min_speakers, max_speakers=max_speakers) |
|
transcription = Transcription() |
|
transcription.add_segments(segments) |
|
xml_str = transcription.to_xml() |
|
project.add_transcription(f["id"], xml_str) |
|
project.save_project() |
|
st.success(f"Transcribed and saved: {f['name']}") |
|
transcription_successful = True |
|
except Exception as e: |
|
st.error(f"Error transcribing {f['name']}: {str(e)}") |
|
|
|
# Update progress |
|
progress_bar.progress((i + 1) / len(selected)) |
|
|
|
status_text.write("Transcription complete!") |
|
|
|
# Offer download if transcription was successful |
|
if transcription_successful: |
|
with open(temp_project_path, "rb") as file: |
|
btn = st.download_button( |
|
label="Download transcribed project", |
|
data=file, |
|
file_name=uploaded_file.name, |
|
mime="application/xml" |
|
) |
|
|
|
# Don't delete the temp file yet as the user might want to download it |
|
# We could add a cleanup button or do it on session end |
|
|
|
if __name__ == "__main__": |
|
main()
|
|
|