79 |
80 |
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/src/main.py:
--------------------------------------------------------------------------------
1 | from datetime import timedelta
2 | from typing import Optional
3 |
4 | from fastapi import FastAPI, Request, File, Form
5 | from fastapi.responses import HTMLResponse, StreamingResponse
6 | from fastapi.staticfiles import StaticFiles
7 | from fastapi.templating import Jinja2Templates
8 | import ffmpeg
9 | import numpy as np
10 | import srt as srt
11 | import stable_whisper
12 | from deep_translator import GoogleTranslator
13 |
14 | DEFAULT_MAX_CHARACTERS = 80
15 |
16 |
17 | def get_audio_buffer(filename: str, start: int, length: int):
18 | """
19 | input: filename of the audio file, start time in seconds, length of the audio in seconds
20 | output: np array of the audio data which the model's transcribe function can take as input
21 | """
22 | out, _ = (
23 | ffmpeg.input(filename, threads=0)
24 | .output("-", format="s16le", acodec="pcm_s16le", ac=1, ar=16000, ss=start, t=length)
25 | .run(cmd=["ffmpeg", "-nostdin"], capture_stdout=True, capture_stderr=True)
26 | )
27 |
28 | return np.frombuffer(out, np.int16).flatten().astype(np.float32) / 32768.0
29 |
30 |
31 | def transcribe_time_stamps(segments: list):
32 | """
33 | input: a list of segments from the model's transcribe function
34 | output: a string of the timestamps and the text of each segment
35 | """
36 | string = ""
37 | for seg in segments:
38 | string += " ".join([str(seg.start), "->", str(seg.end), ": ", seg.text.strip(), "\n"])
39 | return string
40 |
41 |
42 | def split_text_by_punctuation(text: str, max_length: int):
43 | chunks = []
44 | while len(text) > max_length:
45 |
46 | split_pos = max(
47 | text.rfind(p, 0, max_length) for p in [",", ".", "?", "!"," "] if p in text[:max_length]
48 | )
49 |
50 |
51 | if split_pos == -1:
52 | split_pos = max_length
53 |
54 |
55 | chunks.append(text[:split_pos + 1].strip())
56 | text = text[split_pos + 1:].strip()
57 |
58 | if text:
59 | chunks.append(text)
60 |
61 | return chunks
62 |
63 |
64 | def translate_text(text: str, translate_to: str):
65 | return GoogleTranslator(source='auto', target=translate_to).translate(text=text)
66 |
67 |
68 | def make_srt_subtitles(segments: list,translate_to: str, max_chars: int):
69 | subtitles = []
70 | for i, seg in enumerate(segments, start=1):
71 | start_time = seg.start
72 | end_time = seg.end
73 |
74 | text = (
75 | translate_text(seg.text.strip(), translate_to)
76 | if translate_to != "no_translation"
77 | else seg.text.strip()
78 | )
79 |
80 | text_chunks = split_text_by_punctuation(text, max_chars)
81 |
82 | duration = (end_time - start_time) / len(text_chunks)
83 |
84 | for j, chunk in enumerate(text_chunks):
85 | chunk_start = start_time + j * duration
86 | chunk_end = chunk_start + duration
87 |
88 | subtitle = srt.Subtitle(
89 | index=len(subtitles) + 1,
90 | start=timedelta(seconds=chunk_start),
91 | end=timedelta(seconds=chunk_end),
92 | content=chunk
93 | )
94 | subtitles.append(subtitle)
95 |
96 | return srt.compose(subtitles)
97 |
98 |
99 | app = FastAPI(debug=True)
100 |
101 | app.mount('/static', StaticFiles(directory='static'), name='static')
102 | template = Jinja2Templates(directory='templates')
103 |
104 |
105 | @app.get('/', response_class=HTMLResponse)
106 | def index(request: Request):
107 | return template.TemplateResponse('index.html', {"request": request, "text": None})
108 |
109 |
110 | @app.post('/download/')
111 | async def download_subtitle(
112 | request: Request,
113 | file: bytes = File(),
114 | model_type: str = Form("tiny"),
115 | timestamps: Optional[str] = Form("False"),
116 | filename: str = Form("subtitles"),
117 | file_type: str = Form("srt"),
118 | max_characters: int = Form(DEFAULT_MAX_CHARACTERS),
119 | translate_to: str = Form('no_translation'),
120 | ):
121 |
122 | with open('audio.mp3', 'wb') as f:
123 | f.write(file)
124 |
125 | model = stable_whisper.load_model(model_type)
126 | result = model.transcribe("audio.mp3", regroup=False)
127 |
128 | subtitle_file = "subtitle.srt"
129 |
130 | if file_type == "srt":
131 | subtitle_file = f"{filename}.srt"
132 | with open(subtitle_file, "w") as f:
133 | if timestamps:
134 | f.write(make_srt_subtitles(result.segments, translate_to, max_characters))
135 | else:
136 | f.write(result.text)
137 | elif file_type == "vtt":
138 | subtitle_file = f"{filename}.vtt"
139 | with open(subtitle_file, "w") as f:
140 | if timestamps:
141 | f.write(result.to_vtt())
142 | else:
143 | f.write(result.text)
144 |
145 |
146 | media_type = "application/octet-stream"
147 | response = StreamingResponse(
148 | open(subtitle_file, 'rb'),
149 | media_type=media_type,
150 | headers={'Content-Disposition': f'attachment;filename={subtitle_file}'}
151 | )
152 |
153 | return response
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity
10 | and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | * Demonstrating empathy and kindness toward other people
21 | * Being respectful of differing opinions, viewpoints, and experiences
22 | * Giving and gracefully accepting constructive feedback
23 | * Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | * Focusing on what is best not just for us as individuals, but for the
26 | overall community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | * The use of sexualized language or imagery, and sexual attention or
31 | advances of any kind
32 | * Trolling, insulting or derogatory comments, and personal or political attacks
33 | * Public or private harassment
34 | * Publishing others' private information, such as a physical or email
35 | address, without their explicit permission
36 | * Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | .
64 | All complaints will be reviewed and investigated promptly and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series
86 | of actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or
93 | permanent ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within
113 | the community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.0, available at
119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120 |
121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
122 | enforcement ladder](https://github.com/mozilla/diversity).
123 |
124 | [homepage]: https://www.contributor-covenant.org
125 |
126 | For answers to common questions about this code of conduct, see the FAQ at
127 | https://www.contributor-covenant.org/faq. Translations are available at
128 | https://www.contributor-covenant.org/translations.
129 |
--------------------------------------------------------------------------------