Spaces:
Running
Running
Upload app.py
Browse files
app.py
CHANGED
@@ -1,151 +1,29 @@
|
|
1 |
-
from flask import Flask, request, jsonify, render_template, send_from_directory
|
2 |
import base64
|
3 |
from pydub import AudioSegment # 変換用にpydubをインポート
|
4 |
import os
|
5 |
import shutil
|
6 |
-
|
7 |
-
import string
|
8 |
-
import random
|
9 |
-
from datetime import datetime, timedelta
|
10 |
-
from pyannote.audio import Model, Inference
|
11 |
-
from pydub import AudioSegment
|
12 |
-
from flask_sqlalchemy import SQLAlchemy
|
13 |
-
from dotenv import load_dotenv
|
14 |
-
from google.oauth2 import id_token
|
15 |
-
from google_auth_oauthlib.flow import Flow
|
16 |
-
from google.auth.transport import requests as google_requests
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
# Hugging Face のトークン取得(環境変数 HF に設定)
|
21 |
-
#hf_token = os.environ.get("HF")
|
22 |
-
load_dotenv()
|
23 |
-
hf_token = os.getenv("HF")
|
24 |
-
if hf_token is None:
|
25 |
-
raise ValueError("HUGGINGFACE_HUB_TOKEN が設定されていません。")
|
26 |
-
|
27 |
-
# キャッシュディレクトリの作成(書き込み可能な /tmp を利用)
|
28 |
-
cache_dir = "/tmp/hf_cache"
|
29 |
-
os.makedirs(cache_dir, exist_ok=True)
|
30 |
-
|
31 |
-
# pyannote モデルの読み込み
|
32 |
-
model = Model.from_pretrained("pyannote/embedding", use_auth_token=hf_token, cache_dir=cache_dir)
|
33 |
-
inference = Inference(model)
|
34 |
|
|
|
35 |
app = Flask(__name__)
|
36 |
|
37 |
-
app.config['SECRET_KEY'] = os.urandom(24)
|
38 |
-
|
39 |
-
def cosine_similarity(vec1, vec2):
|
40 |
-
vec1 = vec1 / np.linalg.norm(vec1)
|
41 |
-
vec2 = vec2 / np.linalg.norm(vec2)
|
42 |
-
return np.dot(vec1, vec2)
|
43 |
-
|
44 |
-
def segment_audio(path, target_path='/tmp/setup_voice', seg_duration=1.0):
|
45 |
-
"""
|
46 |
-
音声を指定秒数ごとに分割する。
|
47 |
-
target_path に分割したファイルを保存し、元の音声の総長(ミリ秒)を返す。
|
48 |
-
"""
|
49 |
-
os.makedirs(target_path, exist_ok=True)
|
50 |
-
base_sound = AudioSegment.from_file(path)
|
51 |
-
duration_ms = len(base_sound)
|
52 |
-
seg_duration_ms = int(seg_duration * 1000)
|
53 |
-
|
54 |
-
for i, start in enumerate(range(0, duration_ms, seg_duration_ms)):
|
55 |
-
end = min(start + seg_duration_ms, duration_ms)
|
56 |
-
segment = base_sound[start:end]
|
57 |
-
segment.export(os.path.join(target_path, f'{i}.wav'), format="wav")
|
58 |
-
|
59 |
-
return target_path, duration_ms
|
60 |
-
|
61 |
-
def calculate_similarity(path1, path2):
|
62 |
-
embedding1 = inference(path1)
|
63 |
-
embedding2 = inference(path2)
|
64 |
-
return float(cosine_similarity(embedding1.data.flatten(), embedding2.data.flatten()))
|
65 |
-
|
66 |
-
def process_audio(reference_path, input_path, output_folder='/tmp/data/matched_segments', seg_duration=1.0, threshold=0.5):
|
67 |
-
"""
|
68 |
-
入力音声ファイルを seg_duration 秒ごとに分割し、各セグメントと参照音声の類似度を計算。
|
69 |
-
類似度が threshold を超えたセグメントを output_folder にコピーし、マッチした時間(ms)と
|
70 |
-
マッチしなかった時間(ms)を返す。
|
71 |
-
"""
|
72 |
-
os.makedirs(output_folder, exist_ok=True)
|
73 |
-
segmented_path, total_duration_ms = segment_audio(input_path, seg_duration=seg_duration)
|
74 |
-
|
75 |
-
matched_time_ms = 0
|
76 |
-
for file in sorted(os.listdir(segmented_path)):
|
77 |
-
segment_file = os.path.join(segmented_path, file)
|
78 |
-
similarity = calculate_similarity(segment_file, reference_path)
|
79 |
-
if similarity > threshold:
|
80 |
-
shutil.copy(segment_file, output_folder)
|
81 |
-
matched_time_ms += len(AudioSegment.from_file(segment_file))
|
82 |
-
|
83 |
-
unmatched_time_ms = total_duration_ms - matched_time_ms
|
84 |
-
return matched_time_ms, unmatched_time_ms
|
85 |
-
|
86 |
-
def generate_random_string(length):
|
87 |
-
letters = string.ascii_letters + string.digits
|
88 |
-
return ''.join(random.choice(letters) for i in range(length))
|
89 |
-
|
90 |
-
def generate_filename(random_length):
|
91 |
-
random_string = generate_random_string(random_length)
|
92 |
-
current_time = datetime.now().strftime("%Y%m%d%H%M%S")
|
93 |
-
filename = f"{current_time}_{random_string}.wav"
|
94 |
-
return filename
|
95 |
-
|
96 |
# トップページ(テンプレート: index.html)
|
97 |
@app.route('/')
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
|
102 |
# フィードバック画面(テンプレート: feedback.html)
|
103 |
@app.route('/feedback', methods=['GET', 'POST'])
|
104 |
def feedback():
|
105 |
-
#ログイン問題解決しだい戻す
|
106 |
-
"""
|
107 |
-
if 'google_id' not in session:
|
108 |
-
return redirect(url_for('login'))
|
109 |
-
user_info = {
|
110 |
-
'name': session.get('name'),
|
111 |
-
'email': session.get('email')
|
112 |
-
}
|
113 |
-
"""
|
114 |
return render_template('feedback.html')
|
115 |
|
116 |
# 会話詳細画面(テンプレート: talkDetail.html)
|
117 |
@app.route('/talk_detail', methods=['GET', 'POST'])
|
118 |
def talk_detail():
|
119 |
-
"""
|
120 |
-
if 'google_id' not in session:
|
121 |
-
return redirect(url_for('login'))
|
122 |
-
user_info = {
|
123 |
-
'name': session.get('name'),
|
124 |
-
'email': session.get('email')
|
125 |
-
}
|
126 |
-
"""
|
127 |
return render_template('talkDetail.html')
|
128 |
|
129 |
-
# インデックス画面(テンプレート: index.html)
|
130 |
-
@app.route('/index', methods=['GET', 'POST'])
|
131 |
-
def index():
|
132 |
-
"""
|
133 |
-
if 'google_id' not in session:
|
134 |
-
return redirect(url_for('login'))
|
135 |
-
user_info = {
|
136 |
-
'name': session.get('name'),
|
137 |
-
'email': session.get('email')
|
138 |
-
}
|
139 |
-
"""
|
140 |
-
return render_template('index.html')
|
141 |
-
|
142 |
-
@app.before_request
|
143 |
-
def before_request():
|
144 |
-
# リクエストのたびにセッションの寿命を更新する
|
145 |
-
session.permanent = True
|
146 |
-
app.permanent_session_lifetime = timedelta(minutes=15)
|
147 |
-
session.modified = True
|
148 |
-
|
149 |
# 音声アップロード&解析エンドポイント
|
150 |
@app.route('/upload_audio', methods=['POST'])
|
151 |
def upload_audio():
|
@@ -215,8 +93,6 @@ def upload_base_audio():
|
|
215 |
print("Error in /upload_base_audio:", str(e))
|
216 |
return jsonify({"error": "サーバーエラー", "details": str(e)}), 500
|
217 |
|
218 |
-
|
219 |
-
|
220 |
if __name__ == '__main__':
|
221 |
port = int(os.environ.get("PORT", 7860))
|
222 |
app.run(debug=True, host="0.0.0.0", port=port)
|
|
|
1 |
+
from flask import Flask, request, jsonify, render_template, send_from_directory
|
2 |
import base64
|
3 |
from pydub import AudioSegment # 変換用にpydubをインポート
|
4 |
import os
|
5 |
import shutil
|
6 |
+
from process import AudioProcessor
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
|
8 |
+
process=AudioProcessor()
|
9 |
app = Flask(__name__)
|
10 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
# トップページ(テンプレート: index.html)
|
12 |
@app.route('/')
|
13 |
+
@app.route('/index', methods=['GET', 'POST'])
|
14 |
+
def index():
|
15 |
+
return render_template('index.html')
|
16 |
|
17 |
# フィードバック画面(テンプレート: feedback.html)
|
18 |
@app.route('/feedback', methods=['GET', 'POST'])
|
19 |
def feedback():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
return render_template('feedback.html')
|
21 |
|
22 |
# 会話詳細画面(テンプレート: talkDetail.html)
|
23 |
@app.route('/talk_detail', methods=['GET', 'POST'])
|
24 |
def talk_detail():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
return render_template('talkDetail.html')
|
26 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
# 音声アップロード&解析エンドポイント
|
28 |
@app.route('/upload_audio', methods=['POST'])
|
29 |
def upload_audio():
|
|
|
93 |
print("Error in /upload_base_audio:", str(e))
|
94 |
return jsonify({"error": "サーバーエラー", "details": str(e)}), 500
|
95 |
|
|
|
|
|
96 |
if __name__ == '__main__':
|
97 |
port = int(os.environ.get("PORT", 7860))
|
98 |
app.run(debug=True, host="0.0.0.0", port=port)
|