C36-VCT commited on
Commit
041c20e
·
verified ·
1 Parent(s): 3bf9624

Update static/script.js

Browse files
Files changed (1) hide show
  1. static/script.js +45 -36
static/script.js CHANGED
@@ -7,8 +7,8 @@ document.addEventListener('DOMContentLoaded', () => {
7
  const subtitleContainer = document.getElementById('subtitle-container');
8
  const historyList = document.getElementById('history-list');
9
 
10
- // 話者データキャッシュ (letに変更して上書き可能に)
11
- let speakerData = {};
12
 
13
  // UI要素
14
  const hamburger = document.querySelector('.hamburger');
@@ -22,7 +22,6 @@ document.addEventListener('DOMContentLoaded', () => {
22
  const transparency = document.getElementById('textBackground_transparency');
23
  const theaterBtn = document.getElementById('theaterToggle');
24
  const bgSelect = document.getElementById('backgroundColor');
25
- const resetSpeakersBtn = document.getElementById('resetSpeakersBtn');
26
 
27
  let currentFontSize = '24px';
28
 
@@ -47,31 +46,34 @@ document.addEventListener('DOMContentLoaded', () => {
47
  }
48
  }
49
  if (bgSelect) bgSelect.addEventListener('change', updateTheme);
50
- window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', updateTheme);
51
  updateTheme();
52
 
53
  // --- Socketイベント ---
54
  socket.on('connect', () => {
55
- socket.emit('request_speaker_profiles');
 
 
56
  });
57
 
 
58
  socket.on('initial_speaker_profiles', (profiles) => {
59
- // 既存データを上書き
60
- speakerData = profiles || {};
61
  updateSpeakerColorUI();
62
  });
63
 
 
64
  socket.on('new_subtitle', (data) => {
65
  if (!data || !subtitleContainer) return;
66
 
67
- // サーバーから送られてきた話者が手元にない場合、リロード要求
68
  if (!speakerData[data.speaker_id]) {
69
- socket.emit('request_speaker_profiles');
 
70
  }
71
 
72
- const info = speakerData[data.speaker_id] || { name: data.speaker, color: data.color };
73
- const displayName = info.name;
74
- const displayColor = info.color;
75
 
76
  // 履歴
77
  if (historyList) {
@@ -81,7 +83,7 @@ document.addEventListener('DOMContentLoaded', () => {
81
  if (historyList.children.length > 30) historyList.removeChild(historyList.lastChild);
82
  }
83
 
84
- // 字幕
85
  const p = document.createElement('p');
86
  p.className = 'subtitle-line';
87
  p.style.fontSize = currentFontSize;
@@ -95,19 +97,25 @@ document.addEventListener('DOMContentLoaded', () => {
95
  setTimeout(() => p.remove(), 5000);
96
  });
97
 
 
98
  socket.on("speaker_name_changed", data => {
99
  const { id, new_name } = data;
100
  if (speakerData[id]) {
101
  speakerData[id].name = new_name;
 
102
  const input = document.getElementById(`name-input-${id}`);
103
- if (input && input.value !== new_name) input.value = new_name;
 
 
104
  }
105
  });
106
 
 
107
  socket.on("speaker_color_changed", data => {
108
  const { id, color } = data;
109
  if (speakerData[id]) {
110
  speakerData[id].color = color;
 
111
  const bgDiv = document.getElementById(`color-box-${id}`);
112
  const input = document.getElementById(`color-input-${id}`);
113
  if (bgDiv) bgDiv.style.backgroundColor = color;
@@ -122,6 +130,7 @@ document.addEventListener('DOMContentLoaded', () => {
122
  const container = document.getElementById("speakerColorSettings");
123
  if (!container) return;
124
 
 
125
  const existingIds = new Set();
126
  container.querySelectorAll('.speaker-setting-row').forEach(el => {
127
  existingIds.add(el.dataset.speakerId);
@@ -129,44 +138,53 @@ document.addEventListener('DOMContentLoaded', () => {
129
 
130
  Object.keys(speakerData).forEach(speakerId => {
131
  const info = speakerData[speakerId];
 
 
132
  if (existingIds.has(speakerId)) {
133
- existingIds.delete(speakerId);
134
  return;
135
  }
136
 
 
137
  const row = document.createElement("div");
138
  row.className = "speaker-setting-row";
139
- row.dataset.speakerId = speakerId;
140
 
 
141
  const nameInput = document.createElement("input");
142
  nameInput.type = "text";
143
  nameInput.value = info.name;
144
  nameInput.id = `name-input-${speakerId}`;
145
  nameInput.className = "speaker-name-input";
146
 
 
147
  nameInput.addEventListener("change", () => {
148
  const newName = nameInput.value.trim();
149
  if (newName) {
150
  socket.emit("update_speaker_name", { id: speakerId, new_name: newName });
151
  } else {
152
- nameInput.value = speakerData[speakerId].name;
153
  }
154
  });
155
 
 
156
  const colorBox = document.createElement("div");
157
  colorBox.className = "color-preview-box";
158
  colorBox.id = `color-box-${speakerId}`;
159
  colorBox.style.backgroundColor = info.color;
160
 
 
161
  const colorInput = document.createElement("input");
162
  colorInput.type = "color";
163
  colorInput.value = info.color;
164
  colorInput.id = `color-input-${speakerId}`;
165
  colorInput.className = "color-picker-hidden";
166
 
 
167
  colorInput.addEventListener("input", (e) => {
168
  colorBox.style.backgroundColor = e.target.value;
169
  });
 
170
  colorInput.addEventListener("change", (e) => {
171
  const newColor = e.target.value;
172
  speakerData[speakerId].color = newColor;
@@ -179,7 +197,7 @@ document.addEventListener('DOMContentLoaded', () => {
179
  container.appendChild(row);
180
  });
181
 
182
- // 存在しなくなった要素を削除
183
  existingIds.forEach(oldId => {
184
  const el = container.querySelector(`.speaker-setting-row[data-speaker-id="${oldId}"]`);
185
  if (el) el.remove();
@@ -187,16 +205,6 @@ document.addEventListener('DOMContentLoaded', () => {
187
  }
188
 
189
  // --- ボタン操作 ---
190
- if (resetSpeakersBtn) {
191
- resetSpeakersBtn.addEventListener('click', () => {
192
- if(confirm("話者設定をリセットしますか?")) {
193
- speakerData = {}; // ローカルクリア
194
- document.getElementById("speakerColorSettings").innerHTML = "";
195
- socket.emit('reset_speakers');
196
- }
197
- });
198
- }
199
-
200
  if (startForm) {
201
  startForm.addEventListener('submit', (e) => {
202
  e.preventDefault();
@@ -233,6 +241,7 @@ document.addEventListener('DOMContentLoaded', () => {
233
  });
234
  }
235
 
 
236
  function sendSettings() {
237
  socket.emit('update_settings', {
238
  target_lang: subtitleLangSelect.value,
@@ -240,14 +249,9 @@ document.addEventListener('DOMContentLoaded', () => {
240
  });
241
  }
242
  if (subtitleLangSelect) subtitleLangSelect.addEventListener('change', sendSettings);
243
-
244
- // 話者数変更時はリセットするか確認してもいいが、ここでは設定送信のみ
245
- // もし人数を減らして即反映させたいなら、サーバー側でトリミングが必要
246
- if (maxSpeakersSelect) maxSpeakersSelect.addEventListener('change', () => {
247
- sendSettings();
248
- });
249
 
250
- // デザイン反映
251
  function hexToRgba(hex, alpha) {
252
  hex = hex.replace(/^#/, '');
253
  if (hex.length === 3) hex = hex.split('').map(c => c + c).join('');
@@ -269,7 +273,12 @@ document.addEventListener('DOMContentLoaded', () => {
269
  const updatePreview = () => { if(textBgPreview) textBgPreview.style.backgroundColor = textBackground.value; };
270
 
271
  textBackgroundCheck.addEventListener('change', updateAll);
272
- textBackground.addEventListener('input', () => { updateAll(); updatePreview(); });
 
 
 
 
 
273
  transparency.addEventListener('input', updateAll);
274
  updatePreview();
275
  }
 
7
  const subtitleContainer = document.getElementById('subtitle-container');
8
  const historyList = document.getElementById('history-list');
9
 
10
+ // 話者データキャッシュ: { "Speaker 1": { name: "Aさん", color: "#..." }, ... }
11
+ const speakerData = {};
12
 
13
  // UI要素
14
  const hamburger = document.querySelector('.hamburger');
 
22
  const transparency = document.getElementById('textBackground_transparency');
23
  const theaterBtn = document.getElementById('theaterToggle');
24
  const bgSelect = document.getElementById('backgroundColor');
 
25
 
26
  let currentFontSize = '24px';
27
 
 
46
  }
47
  }
48
  if (bgSelect) bgSelect.addEventListener('change', updateTheme);
 
49
  updateTheme();
50
 
51
  // --- Socketイベント ---
52
  socket.on('connect', () => {
53
+ // 再接続時にリセット
54
+ subtitleContainer.innerHTML = '';
55
+ historyList.innerHTML = '';
56
  });
57
 
58
+ // 初期プロファイル受信 (IDベースで管理)
59
  socket.on('initial_speaker_profiles', (profiles) => {
60
+ // profiles = { "Speaker 1": { name: "Aさん", color: "..." }, ... }
61
+ Object.assign(speakerData, profiles);
62
  updateSpeakerColorUI();
63
  });
64
 
65
+ // 字幕受信
66
  socket.on('new_subtitle', (data) => {
67
  if (!data || !subtitleContainer) return;
68
 
69
+ // ローカルデータも最新化(念のため)
70
  if (!speakerData[data.speaker_id]) {
71
+ speakerData[data.speaker_id] = { name: data.speaker, color: data.color };
72
+ updateSpeakerColorUI();
73
  }
74
 
75
+ const displayName = speakerData[data.speaker_id].name;
76
+ const displayColor = speakerData[data.speaker_id].color;
 
77
 
78
  // 履歴
79
  if (historyList) {
 
83
  if (historyList.children.length > 30) historyList.removeChild(historyList.lastChild);
84
  }
85
 
86
+ // 字幕表示
87
  const p = document.createElement('p');
88
  p.className = 'subtitle-line';
89
  p.style.fontSize = currentFontSize;
 
97
  setTimeout(() => p.remove(), 5000);
98
  });
99
 
100
+ // 名前変更反映
101
  socket.on("speaker_name_changed", data => {
102
  const { id, new_name } = data;
103
  if (speakerData[id]) {
104
  speakerData[id].name = new_name;
105
+ // UI上の名前入力欄だけ更新
106
  const input = document.getElementById(`name-input-${id}`);
107
+ if (input && input.value !== new_name) {
108
+ input.value = new_name;
109
+ }
110
  }
111
  });
112
 
113
+ // 色変更反映
114
  socket.on("speaker_color_changed", data => {
115
  const { id, color } = data;
116
  if (speakerData[id]) {
117
  speakerData[id].color = color;
118
+ // UI上の色入力欄だけ更新
119
  const bgDiv = document.getElementById(`color-box-${id}`);
120
  const input = document.getElementById(`color-input-${id}`);
121
  if (bgDiv) bgDiv.style.backgroundColor = color;
 
130
  const container = document.getElementById("speakerColorSettings");
131
  if (!container) return;
132
 
133
+ // 既存のIDセット
134
  const existingIds = new Set();
135
  container.querySelectorAll('.speaker-setting-row').forEach(el => {
136
  existingIds.add(el.dataset.speakerId);
 
138
 
139
  Object.keys(speakerData).forEach(speakerId => {
140
  const info = speakerData[speakerId];
141
+
142
+ // 既にUIが存在する場合はスキップ(再描画するとフォーカスが外れるため)
143
  if (existingIds.has(speakerId)) {
144
+ existingIds.delete(speakerId); // 処理済みとしてマーク
145
  return;
146
  }
147
 
148
+ // 新���作成
149
  const row = document.createElement("div");
150
  row.className = "speaker-setting-row";
151
+ row.dataset.speakerId = speakerId; // IDを埋め込む
152
 
153
+ // 名前入力
154
  const nameInput = document.createElement("input");
155
  nameInput.type = "text";
156
  nameInput.value = info.name;
157
  nameInput.id = `name-input-${speakerId}`;
158
  nameInput.className = "speaker-name-input";
159
 
160
+ // 変更確定時のみ送信 (Enter or FocusOut)
161
  nameInput.addEventListener("change", () => {
162
  const newName = nameInput.value.trim();
163
  if (newName) {
164
  socket.emit("update_speaker_name", { id: speakerId, new_name: newName });
165
  } else {
166
+ nameInput.value = speakerData[speakerId].name; // 空なら戻す
167
  }
168
  });
169
 
170
+ // カラーボックス
171
  const colorBox = document.createElement("div");
172
  colorBox.className = "color-preview-box";
173
  colorBox.id = `color-box-${speakerId}`;
174
  colorBox.style.backgroundColor = info.color;
175
 
176
+ // カラーピッカー
177
  const colorInput = document.createElement("input");
178
  colorInput.type = "color";
179
  colorInput.value = info.color;
180
  colorInput.id = `color-input-${speakerId}`;
181
  colorInput.className = "color-picker-hidden";
182
 
183
+ // ★重要: inputイベントでローカル反映(ドラッグ中)、changeで送信(決定)
184
  colorInput.addEventListener("input", (e) => {
185
  colorBox.style.backgroundColor = e.target.value;
186
  });
187
+
188
  colorInput.addEventListener("change", (e) => {
189
  const newColor = e.target.value;
190
  speakerData[speakerId].color = newColor;
 
197
  container.appendChild(row);
198
  });
199
 
200
+ // 存在しなくなったIDの要素を削除(もしあれば)
201
  existingIds.forEach(oldId => {
202
  const el = container.querySelector(`.speaker-setting-row[data-speaker-id="${oldId}"]`);
203
  if (el) el.remove();
 
205
  }
206
 
207
  // --- ボタン操作 ---
 
 
 
 
 
 
 
 
 
 
208
  if (startForm) {
209
  startForm.addEventListener('submit', (e) => {
210
  e.preventDefault();
 
241
  });
242
  }
243
 
244
+ // --- 設定変更 ---
245
  function sendSettings() {
246
  socket.emit('update_settings', {
247
  target_lang: subtitleLangSelect.value,
 
249
  });
250
  }
251
  if (subtitleLangSelect) subtitleLangSelect.addEventListener('change', sendSettings);
252
+ if (maxSpeakersSelect) maxSpeakersSelect.addEventListener('change', sendSettings);
 
 
 
 
 
253
 
254
+ // --- デザイン反映 ---
255
  function hexToRgba(hex, alpha) {
256
  hex = hex.replace(/^#/, '');
257
  if (hex.length === 3) hex = hex.split('').map(c => c + c).join('');
 
273
  const updatePreview = () => { if(textBgPreview) textBgPreview.style.backgroundColor = textBackground.value; };
274
 
275
  textBackgroundCheck.addEventListener('change', updateAll);
276
+
277
+ textBackground.addEventListener('input', () => {
278
+ updateAll();
279
+ updatePreview();
280
+ });
281
+
282
  transparency.addEventListener('input', updateAll);
283
  updatePreview();
284
  }