Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -3,70 +3,119 @@ import re
|
|
| 3 |
import gradio as gr
|
| 4 |
from datetime import datetime
|
| 5 |
import os
|
|
|
|
| 6 |
|
| 7 |
-
#
|
| 8 |
TIMESTAMP_RE = re.compile(r'(\[\d{2}:\d{2}:\d{2}(?:[.,]\d{1,3})?\])')
|
| 9 |
|
|
|
|
|
|
|
|
|
|
| 10 |
def format_transcript(text: str) -> str:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
if not text:
|
| 12 |
return ""
|
|
|
|
| 13 |
text = text.replace('\r\n', '\n').replace('\r', '\n')
|
| 14 |
parts = TIMESTAMP_RE.split(text)
|
| 15 |
out_lines = []
|
| 16 |
i = 0
|
|
|
|
| 17 |
if parts and not TIMESTAMP_RE.match(parts[0]):
|
| 18 |
if len(parts) > 1:
|
| 19 |
parts[1] = parts[0] + parts[1]
|
| 20 |
i = 1
|
| 21 |
else:
|
|
|
|
| 22 |
return re.sub(r'\n+', ' ', text).strip()
|
| 23 |
|
|
|
|
| 24 |
while i < len(parts) - 1:
|
| 25 |
ts = parts[i].strip()
|
| 26 |
body = parts[i+1]
|
| 27 |
-
#
|
| 28 |
body = re.sub(r'\n+', ' ', body).strip()
|
| 29 |
body = re.sub(r' {2,}', ' ', body)
|
| 30 |
line = f"{ts} {body}".strip()
|
| 31 |
out_lines.append(line)
|
| 32 |
i += 2
|
| 33 |
|
|
|
|
| 34 |
if i < len(parts):
|
| 35 |
tail = parts[i].strip()
|
| 36 |
if tail:
|
| 37 |
tail = re.sub(r'\n+', ' ', tail).strip()
|
| 38 |
out_lines.append(tail)
|
| 39 |
|
|
|
|
| 40 |
return "\n\n".join(out_lines)
|
| 41 |
|
| 42 |
-
def download_filename():
|
| 43 |
now = datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")
|
| 44 |
-
return f"
|
| 45 |
|
| 46 |
-
def
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
path = os.path.join("/tmp/hf_transcript", fname)
|
| 51 |
with open(path, "wb") as f:
|
| 52 |
-
f.write(
|
| 53 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
|
| 55 |
-
with gr.Blocks(title="Transkript-formatter") as demo:
|
| 56 |
-
gr.Markdown(
|
|
|
|
|
|
|
|
|
|
| 57 |
with gr.Row():
|
| 58 |
inp = gr.Textbox(lines=18, label="Råtekst (lim inn her)", placeholder="Lim inn hele transkriptet her...")
|
| 59 |
-
out = gr.Textbox(lines=18, label="Formatert tekst")
|
| 60 |
with gr.Row():
|
| 61 |
-
btn = gr.Button("Formatér")
|
| 62 |
-
|
| 63 |
-
|
|
|
|
|
|
|
|
|
|
| 64 |
btn.click(lambda t: format_transcript(t), inputs=inp, outputs=out)
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
|
|
|
|
|
|
| 68 |
return text
|
| 69 |
-
copy_btn.click(
|
| 70 |
|
| 71 |
if __name__ == "__main__":
|
| 72 |
demo.launch(server_name="0.0.0.0", server_port=7860)
|
|
|
|
| 3 |
import gradio as gr
|
| 4 |
from datetime import datetime
|
| 5 |
import os
|
| 6 |
+
from docx import Document # krever python-docx i requirements
|
| 7 |
|
| 8 |
+
# Aksepterer både punktum og komma i desimaldelen
|
| 9 |
TIMESTAMP_RE = re.compile(r'(\[\d{2}:\d{2}:\d{2}(?:[.,]\d{1,3})?\])')
|
| 10 |
|
| 11 |
+
TMP_DIR = "/tmp/hf_transcript"
|
| 12 |
+
os.makedirs(TMP_DIR, exist_ok=True)
|
| 13 |
+
|
| 14 |
def format_transcript(text: str) -> str:
|
| 15 |
+
"""
|
| 16 |
+
Bevarer tidsstempler og slår sammen alle linjer under hvert
|
| 17 |
+
tidsstempel til én linje. Returnerer en lesbar tekst med ett
|
| 18 |
+
blankt linjeskift mellom hvert segment.
|
| 19 |
+
"""
|
| 20 |
if not text:
|
| 21 |
return ""
|
| 22 |
+
# Normaliser linjeskift
|
| 23 |
text = text.replace('\r\n', '\n').replace('\r', '\n')
|
| 24 |
parts = TIMESTAMP_RE.split(text)
|
| 25 |
out_lines = []
|
| 26 |
i = 0
|
| 27 |
+
# Håndter eventuell tekst før første timestamp
|
| 28 |
if parts and not TIMESTAMP_RE.match(parts[0]):
|
| 29 |
if len(parts) > 1:
|
| 30 |
parts[1] = parts[0] + parts[1]
|
| 31 |
i = 1
|
| 32 |
else:
|
| 33 |
+
# ingen tidsstempel funnet - kollaps nye linjer til ett avsnitt
|
| 34 |
return re.sub(r'\n+', ' ', text).strip()
|
| 35 |
|
| 36 |
+
# Bygg segmenter: [timestamp, body, timestamp, body, ...]
|
| 37 |
while i < len(parts) - 1:
|
| 38 |
ts = parts[i].strip()
|
| 39 |
body = parts[i+1]
|
| 40 |
+
# Slå sammen interne linjeskift til mellomrom
|
| 41 |
body = re.sub(r'\n+', ' ', body).strip()
|
| 42 |
body = re.sub(r' {2,}', ' ', body)
|
| 43 |
line = f"{ts} {body}".strip()
|
| 44 |
out_lines.append(line)
|
| 45 |
i += 2
|
| 46 |
|
| 47 |
+
# Trailing tekst uten timestamp
|
| 48 |
if i < len(parts):
|
| 49 |
tail = parts[i].strip()
|
| 50 |
if tail:
|
| 51 |
tail = re.sub(r'\n+', ' ', tail).strip()
|
| 52 |
out_lines.append(tail)
|
| 53 |
|
| 54 |
+
# Ett tomt linjeskift mellom segmentene for lesbarhet
|
| 55 |
return "\n\n".join(out_lines)
|
| 56 |
|
| 57 |
+
def download_filename(prefix="formatted_transcript"):
|
| 58 |
now = datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")
|
| 59 |
+
return f"{prefix}_{now}"
|
| 60 |
|
| 61 |
+
def make_txt_file(text: str) -> str:
|
| 62 |
+
"""Lagre tekst som .txt og returner filsti."""
|
| 63 |
+
fname = download_filename() + ".txt"
|
| 64 |
+
path = os.path.join(TMP_DIR, fname)
|
|
|
|
| 65 |
with open(path, "wb") as f:
|
| 66 |
+
f.write(text.encode("utf-8"))
|
| 67 |
+
return path
|
| 68 |
+
|
| 69 |
+
def make_docx_file(text: str) -> str:
|
| 70 |
+
"""
|
| 71 |
+
Lag en enkel .docx hvor hver segment (separert av tom linje i text)
|
| 72 |
+
blir egen paragraf. Returner filsti.
|
| 73 |
+
"""
|
| 74 |
+
fname = download_filename() + ".docx"
|
| 75 |
+
path = os.path.join(TMP_DIR, fname)
|
| 76 |
+
doc = Document()
|
| 77 |
+
# Del opp på doble linjeskift som vi brukte i format_transcript
|
| 78 |
+
segments = text.split("\n\n")
|
| 79 |
+
for seg in segments:
|
| 80 |
+
# legg til som egen paragraf
|
| 81 |
+
p = doc.add_paragraph(seg)
|
| 82 |
+
# Lagre dokumentet
|
| 83 |
+
doc.save(path)
|
| 84 |
+
return path
|
| 85 |
+
|
| 86 |
+
def format_and_prepare_files(text: str):
|
| 87 |
+
"""
|
| 88 |
+
Returnerer: formatert tekst (string), sti_til_txt, sti_til_docx
|
| 89 |
+
som bindes til gr.File-outputkomponenter.
|
| 90 |
+
"""
|
| 91 |
+
formatted = format_transcript(text)
|
| 92 |
+
txt_path = make_txt_file(formatted)
|
| 93 |
+
docx_path = make_docx_file(formatted)
|
| 94 |
+
return formatted, txt_path, docx_path
|
| 95 |
|
| 96 |
+
with gr.Blocks(title="Transkript-formatter (TXT + DOCX)") as demo:
|
| 97 |
+
gr.Markdown(
|
| 98 |
+
"### Lim inn transkriptet ditt (f.eks. `[00:00:23.850]` eller `[00:00:23,850]`) og trykk **Formatér**. "
|
| 99 |
+
"Du kan også generere nedlastbare `.txt` og `.docx` filer."
|
| 100 |
+
)
|
| 101 |
with gr.Row():
|
| 102 |
inp = gr.Textbox(lines=18, label="Råtekst (lim inn her)", placeholder="Lim inn hele transkriptet her...")
|
| 103 |
+
out = gr.Textbox(lines=18, label="Formatert tekst (kopier herfra eller generer filer)")
|
| 104 |
with gr.Row():
|
| 105 |
+
btn = gr.Button("Formatér (vis i felt)")
|
| 106 |
+
gen_btn = gr.Button("Generer .txt og .docx (nedlasting)")
|
| 107 |
+
txt_file = gr.File(label="Last ned .txt")
|
| 108 |
+
docx_file = gr.File(label="Last ned .docx")
|
| 109 |
+
copy_btn = gr.Button("Oppdater utdata (klar for kopiering)")
|
| 110 |
+
# Vis formatert tekst i tekstboksen
|
| 111 |
btn.click(lambda t: format_transcript(t), inputs=inp, outputs=out)
|
| 112 |
+
# Generer filer og bind dem til gr.File-komponentene
|
| 113 |
+
gen_btn.click(format_and_prepare_files, inputs=inp, outputs=[out, txt_file, docx_file])
|
| 114 |
+
# "Kopier"-knapp: server-side kan ikke direkte kopiere til klientens clipboard,
|
| 115 |
+
# men denne oppdaterer utdatafeltet slik at bruker lett kan markere og kopiere manuelt.
|
| 116 |
+
def copy_to_out(text):
|
| 117 |
return text
|
| 118 |
+
copy_btn.click(copy_to_out, inputs=out, outputs=out)
|
| 119 |
|
| 120 |
if __name__ == "__main__":
|
| 121 |
demo.launch(server_name="0.0.0.0", server_port=7860)
|