Spaces:
Sleeping
Sleeping
File size: 6,098 Bytes
5a14485 05c6f5f d974f1d 5a14485 d974f1d 0c42c4f 5a14485 d974f1d 5a14485 c1022a7 5a14485 05c6f5f 5a14485 c1022a7 d974f1d 5a14485 d974f1d 5a14485 d974f1d 05c6f5f d974f1d 7e03b02 d974f1d c1022a7 d974f1d 5a14485 c1022a7 7e03b02 c1022a7 7e03b02 c1022a7 7e03b02 d974f1d 7d26a50 c1022a7 d974f1d 5a14485 05c6f5f d974f1d 5a14485 c1022a7 d974f1d 7e03b02 c1022a7 7e03b02 5a14485 c1022a7 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# app.py
import re
import gradio as gr
from datetime import datetime
import os
from docx import Document # krever python-docx i requirements
# Aksepterer både punktum og komma i desimaldelen
TIMESTAMP_RE = re.compile(r'(\[\d{2}:\d{2}:\d{2}(?:[.,]\d{1,3})?\])')
TMP_DIR = "/tmp/hf_transcript"
os.makedirs(TMP_DIR, exist_ok=True)
def format_transcript(text: str) -> str:
"""Bevarer tidsstempler og slår sammen linjer per tidsstempel."""
if not text:
return ""
text = text.replace('\r\n', '\n').replace('\r', '\n')
parts = TIMESTAMP_RE.split(text)
out_lines = []
i = 0
if parts and not TIMESTAMP_RE.match(parts[0]):
if len(parts) > 1:
parts[1] = parts[0] + parts[1]
i = 1
else:
return re.sub(r'\n+', ' ', text).strip()
while i < len(parts) - 1:
ts = parts[i].strip()
body = parts[i+1]
body = re.sub(r'\n+', ' ', body).strip()
body = re.sub(r' {2,}', ' ', body)
line = f"{ts} {body}".strip()
out_lines.append(line)
i += 2
if i < len(parts):
tail = parts[i].strip()
if tail:
tail = re.sub(r'\n+', ' ', tail).strip()
out_lines.append(tail)
return "\n\n".join(out_lines)
def format_without_timestamps(text: str, sentences_per_paragraph: int = 5) -> str:
"""
Tar tekst uten tidskoder og formaterer den til avsnitt etter hver N setninger.
- splitter på setningsendelser (., !, ?), forsøker å beholde forkortelser rimelig.
- samler setninger i grupper på sentences_per_paragraph og separerer gruppene med et tomt linjeskift.
"""
if not text:
return ""
# Normaliser whitespace og newlines
txt = text.replace('\r\n', '\n').replace('\r', '\n')
txt = re.sub(r'\n+', ' ', txt) # fjern nye linjer internt
txt = re.sub(r'\s+', ' ', txt).strip() # collapse whitespace
# Split på setningsavslutning: behold skilletegn
# Dette splitter på (?<=[.!?]) etterfulgt av ett eller flere mellomrom
raw_sentences = re.split(r'(?<=[.!?])\s+', txt)
# Hvis veldig korte "setninger" eller tomme, filter dem ut
sentences = [s.strip() for s in raw_sentences if s.strip()]
if not sentences:
return txt # fallback
# Gruppér setninger og lag avsnitt
paragraphs = []
for i in range(0, len(sentences), sentences_per_paragraph):
group = sentences[i:i+sentences_per_paragraph]
paragraph = ' '.join(group).strip()
paragraphs.append(paragraph)
return "\n\n".join(paragraphs)
def download_filename(prefix="formatted_transcript"):
now = datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")
return f"{prefix}_{now}"
def make_txt_file(text: str) -> str:
fname = download_filename() + ".txt"
path = os.path.join(TMP_DIR, fname)
with open(path, "wb") as f:
f.write(text.encode("utf-8"))
return path
def make_docx_file(text: str) -> str:
fname = download_filename() + ".docx"
path = os.path.join(TMP_DIR, fname)
doc = Document()
segments = text.split("\n\n")
for seg in segments:
doc.add_paragraph(seg)
doc.save(path)
return path
def format_and_prepare_files(preferred_text: str) -> tuple:
"""
Lager txt og docx fra gitt tekst (forventet allerede formatert).
Returnerer: (tekst, txt_path, docx_path)
"""
formatted = preferred_text if preferred_text is not None else ""
txt_path = make_txt_file(formatted)
docx_path = make_docx_file(formatted)
return formatted, txt_path, docx_path
def generate_from_preferred(inp_text: str, out_text: str):
"""
Velger utdatafeltets tekst hvis det finnes (prioritet), ellers bruker original input.
Deretter lager filer fra valgt tekst.
"""
preferred = out_text.strip() if out_text and out_text.strip() else inp_text
if not preferred:
return "", None, None
return format_and_prepare_files(preferred)
def clear_input_and_outputs():
return gr.update(value=""), gr.update(value=""), None, None
with gr.Blocks(title="Transkript-formatter (TXT + DOCX)") as demo:
gr.Markdown(
"<b>Transkript-formatering</b><br>"
"Lim inn transkriptet ditt (med tidskoder eller uten).<br>"
"- Bruk <i>Formatér</i> for å slå sammen tekst under tidskoder. <br>"
"- Bruk <i>Uten tidskoder</i> for å formatere tekst uten tidskoder: avsnitt etter hver 5. setning. <br>"
"- Når utdata er i tekstfeltet, kan du trykke <i>Generer .txt og .docx</i> for å laste ned filene basert på utdata."
)
with gr.Row():
inp = gr.Textbox(lines=18, label="Råtekst (lim inn her)", placeholder="Lim inn hele transkriptet her...")
out = gr.Textbox(lines=18, label="Formatert tekst (kopier herfra eller generer filer)")
with gr.Row():
fmt_btn = gr.Button("Formatér (vis i felt)")
no_ts_btn = gr.Button("Uten tidskoder") # <-- ny knapp
gen_btn = gr.Button("Generer .txt og .docx (nedlasting)")
txt_file = gr.File(label="Last ned .txt")
docx_file = gr.File(label="Last ned .docx")
with gr.Row():
copy_btn = gr.Button("Oppdater utdata (klar for kopiering)")
clear_btn = gr.Button("Clear input")
# Normalt format (bevarer tidskoder)
fmt_btn.click(lambda t: format_transcript(t), inputs=inp, outputs=out)
# Uten tidskoder: formaterer tekst ved å gruppere hver 5 setning til ett avsnitt
# (Returnerer oppdatert out-tekst)
def without_timestamps_and_show(text):
return format_without_timestamps(text, sentences_per_paragraph=5)
no_ts_btn.click(without_timestamps_and_show, inputs=inp, outputs=out)
# Generer bruker preferert tekst (out hvis ikke tom, ellers inp)
gen_btn.click(generate_from_preferred, inputs=[inp, out], outputs=[out, txt_file, docx_file])
copy_btn.click(lambda t: t, inputs=out, outputs=out)
clear_btn.click(clear_input_and_outputs, inputs=None, outputs=[inp, out, txt_file, docx_file])
if __name__ == "__main__":
demo.launch(server_name="0.0.0.0", server_port=7860)
|