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)