hiDenorIYamano commited on
Commit
e8c79a2
·
1 Parent(s): ee54cf7

intiial commit

Browse files
Files changed (3) hide show
  1. .gitignore +4 -0
  2. app.py +229 -0
  3. requirements.txt +5 -0
.gitignore ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ /*.mp4
2
+ /*.srt
3
+ /*.zip
4
+ .idea
app.py ADDED
@@ -0,0 +1,229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import pathlib
3
+ import subprocess
4
+ import math
5
+ from pydub import AudioSegment
6
+ import openai
7
+ import re
8
+ import MeCab
9
+ import deepl
10
+ import os
11
+ import zipfile
12
+
13
+ def split_japanese_line(line, max_length):
14
+ """Split a Japanese line into multiple lines using MeCab for morphological analysis."""
15
+ if len(line) < max_length:
16
+ return line
17
+
18
+ max_line = 2
19
+ line = re.sub(r'[、。]', '', line)
20
+
21
+ m = MeCab.Tagger()
22
+ nodes = m.parse(line).split("\n")
23
+
24
+ words = [node.split("\t")[0] for node in nodes if node and not node.startswith("EOS")]
25
+ word_features = [re.split('[\t,]', node)[4] if len(re.split('[\t,]', node)) > 3 else None for node in nodes if
26
+ node and not node.startswith("EOS")]
27
+
28
+ lines = []
29
+ current_line = ""
30
+
31
+ idx = 0
32
+ allowed_features = ["助詞-格助詞", "助詞-副助詞", "助詞-終助詞", "助詞-係助詞", "補助記号-句点"]
33
+ whitelisted_words = ["という", "っていう", "になって", "みたいな", "として", "に対して", "とする", "というか", "ていう", "になって", "について", "にかけて", "とか", "とかで", "にも", "には", "について",
34
+ "がある", "があって", "でも", "では", "のような", "のように", "のいる", "のある", "がします", "がする"]
35
+
36
+ while idx < len(words):
37
+ word = words[idx]
38
+ # If adding the next word exceeds the max_length or if the word is a particle, break the line
39
+ if idx + 1 < len(words) and any(
40
+ whitelisted_word == word + words[idx + 1] for whitelisted_word in whitelisted_words):
41
+ current_line += word + words[idx + 1]
42
+ idx += 2 # Increment to skip next word
43
+ if len(word_features) > idx:
44
+ if word_features[idx] not in allowed_features:
45
+ lines.append(current_line)
46
+ current_line = ""
47
+ elif len(word_features) > idx:
48
+ if word_features[idx - 1] in allowed_features and word_features[idx] not in allowed_features:
49
+ lines.append(current_line)
50
+ current_line = ""
51
+ current_line += word
52
+ idx += 1
53
+ else:
54
+ current_line += word
55
+ idx += 1
56
+
57
+
58
+ # Append the last line if it exists
59
+ if current_line:
60
+ lines.append(current_line)
61
+
62
+ # Merge lines to ensure each line is less than or equal to max_length
63
+ merged_lines = []
64
+ temp_line = ''
65
+ for line in lines:
66
+ if len(temp_line) <= max_length / 2:
67
+ temp_line += line
68
+ elif len(temp_line + line) <= max_length:
69
+ temp_line += line
70
+ elif len(merged_lines) >= max_line - 1:
71
+ temp_line += line
72
+ else:
73
+ if temp_line == '':
74
+ merged_lines.append(line)
75
+ else:
76
+ merged_lines.append(temp_line)
77
+ temp_line = line
78
+ if temp_line:
79
+ merged_lines.append(temp_line)
80
+
81
+ return "\n".join(merged_lines)
82
+
83
+
84
+ def split_japanese_srt_text(srt_content, max_length):
85
+ """Split the lines of the srt content for Japanese text."""
86
+ srt_lines = srt_content.split("\n")
87
+ modified_lines = []
88
+
89
+ for line in srt_lines:
90
+ # Check if the line looks like a subtitle text (not an index or a timestamp)
91
+ if re.match(r"^[0-9]{1,2}:[0-9]{2}:[0-9]{2},[0-9]{3} --> [0-9]{1,2}:[0-9]{2}:[0-9]{2},[0-9]{3}$", line):
92
+ modified_lines.append(line)
93
+ elif line.isdigit():
94
+ modified_lines.append(line)
95
+ elif line == "":
96
+ modified_lines.append(line)
97
+ else:
98
+ # Split the Japanese subtitle text line
99
+ modified_lines.extend(split_japanese_line(line, max_length).split("\n"))
100
+
101
+ return "\n".join(modified_lines)
102
+
103
+ def format_transcription_for_japanese(file_path, max_length):
104
+ with open(file_path, "r", encoding="utf-8") as file:
105
+ srt_content = file.read()
106
+ modified_japanese_srt_content = split_japanese_srt_text(srt_content, max_length)
107
+ with open("formatted_transcription.srt", "w", encoding="utf-8") as file:
108
+ file.write(modified_japanese_srt_content)
109
+
110
+ def mp4ToSrt(api_key, sampletext_path):
111
+ openai.api_key = api_key # Set the API key
112
+
113
+ # Read the provided text file and replace actual newline characters with "\n" string
114
+ with open(sampletext_path, "r", encoding="utf-8") as file:
115
+ content = file.read()
116
+
117
+ # Replace newline characters with "\n" string
118
+ modified_content = content.replace('\n', '\\n')
119
+
120
+ with open(str('converted.mp4'), "rb") as f:
121
+ response = openai.Audio.transcribe(
122
+ "whisper-1",
123
+ f,
124
+ temperature=0,
125
+ language="ja",
126
+ prompt=modified_content,
127
+ response_format="srt")
128
+ transcription = response
129
+
130
+ with open("transcription.srt", "wt", encoding="utf-8") as f:
131
+ f.writelines([transcription])
132
+
133
+ def compress_mp4(path):
134
+ original_file = pathlib.Path(path)
135
+ audio_file = pathlib.Path("./audio").with_suffix(original_file.suffix)
136
+
137
+ # If the audio file already exists, delete it
138
+ if audio_file.exists():
139
+ audio_file.unlink()
140
+
141
+ subprocess.run(["ffmpeg", "-i", str(original_file)
142
+ , "-codec:a", "copy", "-vn", str(audio_file)])
143
+
144
+ TARGET_FILE_SIZE = 25000000
145
+
146
+ print(f"{audio_file.stat().st_size=}")
147
+
148
+ if audio_file.stat().st_size > TARGET_FILE_SIZE:
149
+ print("This file needs to be converted.")
150
+
151
+ audio_segment = AudioSegment.from_file(str(audio_file))
152
+
153
+ audio_length_sec = len(audio_segment) / 1000
154
+
155
+ target_kbps = int(math.floor(TARGET_FILE_SIZE * 8 / audio_length_sec / 1000 * 0.95))
156
+
157
+ if target_kbps < 8:
158
+ assert f"{target_kbps=} is not supported."
159
+
160
+ converted_file = pathlib.Path("./converted").with_suffix(".mp4")
161
+
162
+ # If the converted file already exists, delete it
163
+ if converted_file.exists():
164
+ converted_file.unlink()
165
+
166
+ subprocess.run(["ffmpeg", "-i", str(audio_file)
167
+ , "-codec:a", "aac", "-ar", "16000", "-ac", "1", "-b:a", f"{target_kbps}k"
168
+ , str(converted_file)])
169
+
170
+ print(f"{converted_file.stat().st_size=}")
171
+
172
+ def videoToSrt(video_obj, sampletext_obj, max_length, api_key):
173
+ compress_mp4(video_obj.name)
174
+ mp4ToSrt(api_key, sampletext_obj.name)
175
+ format_transcription_for_japanese("transcription.srt", max_length)
176
+ with open("formatted_transcription.srt", "r", encoding="utf-8") as file:
177
+ content = file.read()
178
+ return content
179
+
180
+ def translateSrt(text, languages, api_key):
181
+ translator = deepl.Translator(api_key)
182
+
183
+ # 出力ファイルを保存するための一時ディレクトリを作成
184
+ if not os.path.exists('translated_srt_files'):
185
+ os.makedirs('translated_srt_files')
186
+
187
+ # 各言語での翻訳をループ処理
188
+ for lang in languages:
189
+ result = translator.translate_text(text, target_lang=lang, split_sentences=0).text
190
+ with open(f'translated_srt_files/{lang}.srt', 'w') as f:
191
+ f.write(result)
192
+
193
+ # srtファイルをまとめたzipを作成
194
+ zip_filename = "translated_srt_files.zip"
195
+ with zipfile.ZipFile(zip_filename, 'w') as zipf:
196
+ for root, _, files in os.walk('translated_srt_files'):
197
+ for file in files:
198
+ zipf.write(os.path.join(root, file))
199
+
200
+ return zip_filename # zipファイルのパスを返す
201
+
202
+
203
+ with gr.Blocks() as app:
204
+ with gr.Tab("音声ファイルからsrt生成"):
205
+ with gr.Row():
206
+ text_input = [
207
+ gr.File(file_types=[".mp4"], label="動画ファイル(mp4)"), # File input for the video
208
+ gr.File(file_types=[".txt"], label="書き出したい文章の例"), # File input for the text file
209
+ gr.Number(20, label="1行あたりの最大文字数"),
210
+ gr.Textbox(placeholder="openAI API key", type="password", label="openAI API keyを入力してください")
211
+ ]
212
+ text_output = gr.Textbox(label="SRTの出力結果", max_lines=30)
213
+ text_button = gr.Button("SRTに変換")
214
+ with gr.Tab("srt自動翻訳"):
215
+ with gr.Row():
216
+ translate_input = [
217
+ gr.Textbox(placeholder="srt本文", label="srt本文を貼り付けてください", max_lines=10),
218
+ gr.Dropdown(["EN-US", "ZH"], multiselect=True, label="翻訳したい言語を選択"),
219
+ gr.Textbox(placeholder="DeepL API key", type="password", label="DeepL API keyを入力してください")
220
+ ]
221
+ translate_output = gr.File(label="翻訳結果")
222
+ translate_button = gr.Button("SRTを翻訳")
223
+
224
+
225
+ text_button.click(videoToSrt, inputs=text_input, outputs=text_output)
226
+ translate_button.click(translateSrt, inputs=translate_input, outputs=translate_output)
227
+
228
+ if __name__ == "__main__":
229
+ app.launch()
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ openai~=0.27.2
2
+ deepl~=1.14.0
3
+ pydub~=0.25.1
4
+ mecab-python3==1.0.6
5
+ unidic-lite==1.0.8