マスター編集とプレビュー再生を実装

This commit is contained in:
hina ntki 2024-08-16 02:09:16 +09:00
parent f6dc93a9ab
commit 1afeaa6022
4 changed files with 167 additions and 50 deletions

View File

@ -138,7 +138,7 @@ async def send_quiz_message(channel, intro_audio_name, volume):
await disable_button_after_timeout(message)
async def disable_button_after_timeout(message):
await asyncio.sleep(20)
await asyncio.sleep(30)
view = discord.ui.View()
for action_row in message.components:
for item in action_row.children:

View File

@ -4,7 +4,7 @@ import traceback
import secrets
import random
import requests
from flask import Flask, session, redirect, url_for, g, request, render_template, jsonify, flash
from flask import Flask, session, redirect, url_for, g, request, render_template, jsonify, flash, send_from_directory
import logging
from datetime import timedelta
from db import get_db, close_connection, initialize_db, reset_active_sessions
@ -331,6 +331,16 @@ def process_csv(file_path):
except Exception as e:
logger.error(f'Error processing CSV file: {str(e)}')
# プレビュー用のエンドポイントを追加
@app.route('/preview_audio/<filename>', methods=['GET'])
@login_required
def preview_audio_route(filename):
try:
# ファイルがアップロードされているディレクトリからファイルを返す
return send_from_directory(app.config['UPLOAD_FOLDER'], filename)
except Exception as e:
return jsonify({'error': 'ファイルのプレビュー中にエラーが発生しました'}), 500
if __name__ == "__main__":
try:
app.run(debug=True, host='0.0.0.0', port=80)
@ -340,4 +350,3 @@ if __name__ == "__main__":
except Exception as e:
logger.error(f"例外が発生しました: {e}")
traceback.print_exc(file=sys.stderr)

View File

@ -90,14 +90,33 @@ def update_quiz_master():
db = get_db()
quiz_master = db.query(QuizMaster).filter_by(id=data['id']).first()
if quiz_master:
old_full_audio_name = quiz_master.full_audio_name
old_intro_audio_name = quiz_master.intro_audio_name
# フル音源名とイントロ音源名を更新
quiz_master.title = data['title']
quiz_master.full_audio_name = data['full_audio_name']
quiz_master.intro_audio_name = data['intro_audio_name']
quiz_master.keyword1 = data['keyword1']
quiz_master.keyword2 = data['keyword2']
quiz_master.keyword3 = data['keyword3']
# ファイル名を変更
upload_folder = current_app.config['UPLOAD_FOLDER']
if old_full_audio_name != data['full_audio_name']:
os.rename(
os.path.join(upload_folder, old_full_audio_name),
os.path.join(upload_folder, data['full_audio_name'])
)
if old_intro_audio_name != data['intro_audio_name']:
os.rename(
os.path.join(upload_folder, old_intro_audio_name),
os.path.join(upload_folder, data['intro_audio_name'])
)
db.commit()
return jsonify({'message': '更新が完了しました'})
return jsonify({'message': '更新が完了しました'})
return jsonify({'error': 'クイズマスタが見つかりません'})
def add_quiz_master():
data = request.json

View File

@ -36,10 +36,28 @@
th {
background-color: #f2f2f2;
}
#updateModal {
display: none;
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
background-color: white;
padding: 20px;
border: 1px solid #ddd;
box-shadow: 0px 0px 10px rgba(0,0,0,0.1);
}
/* 音量スライダーのスタイル */
#volumeSlider {
width: 100%;
}
</style>
<script>
let files = [];
let usedFiles = [];
let audio = new Audio();
async function fetchFiles() {
try {
@ -63,7 +81,6 @@
function filterFiles() {
const fullAudioInput = document.getElementById('full_audio_name_input').value.toLowerCase();
const fullAudioSelect = document.getElementById('full_audio_name_select');
const introAudioSelect = document.getElementById('intro_audio_name_select');
fullAudioSelect.innerHTML = '';
@ -83,11 +100,9 @@
introAudioSelect.appendChild(optionIntro);
});
// タイトルフィールドを更新(拡張子を削除)
const title = fullAudioInput.split('.').slice(0, -1).join('.');
document.getElementById('title').value = title;
// イントロ音源名を自動入力
const extension = fullAudioInput.split('.').pop();
const introAudioName = `${title}_intro.${extension}`;
if (files.includes(introAudioName)) {
@ -95,7 +110,6 @@
document.getElementById('intro_audio_name_hidden').value = introAudioName;
}
// キーワード1を自動入力
const keyword1 = fullAudioInput.split(' - ')[0];
document.getElementById('keyword1').value = keyword1;
}
@ -107,12 +121,10 @@
input.value = select.value;
hidden.value = select.value;
// タイトルフィールドを更新(拡張子を削除)
if (inputId === 'full_audio_name_input') {
const title = select.value.split('.').slice(0, -1).join('.');
document.getElementById('title').value = title;
// イントロ音源名を自動入力
const extension = select.value.split('.').pop();
const introAudioName = `${title}_intro.${extension}`;
if (files.includes(introAudioName)) {
@ -120,12 +132,84 @@
document.getElementById('intro_audio_name_hidden').value = introAudioName;
}
// キーワード1を自動入力
const keyword1 = select.value.split(' - ')[0];
document.getElementById('keyword1').value = keyword1;
}
}
async function fetchQuizMasters() {
try {
const response = await fetch('/quiz_master');
const quizMasters = await response.json();
const quizMasterList = document.getElementById('quizMasterList');
quizMasterList.innerHTML = '';
quizMasters.forEach(qm => {
const tr = document.createElement('tr');
tr.innerHTML = `
<td>${qm.title}</td>
<td>${qm.full_audio_name} <button onclick="previewAudio('${qm.full_audio_name}')">再生</button></td>
<td>${qm.intro_audio_name} <button onclick="previewAudio('${qm.intro_audio_name}')">再生</button></td>
<td>${qm.keyword1}</td>
<td>${qm.keyword2}</td>
<td>${qm.keyword3}</td>
<td>
<button onclick="editQuizMaster('${qm.id}')">編集</button>
<form onsubmit="handleFormSubmit(event, '/delete_quiz_master')">
<input type="hidden" name="id" value="${qm.id}">
<button type="submit">削除</button>
</form>
</td>
`;
quizMasterList.appendChild(tr);
});
} catch (error) {
console.error('Error fetching quiz masters:', error);
}
}
function previewAudio(filename) {
const audioUrl = `/preview_audio/${encodeURIComponent(filename)}`;
audio.src = audioUrl;
audio.volume = document.getElementById('volumeSlider').value / 100; // スライダーから音量を設定
audio.play().catch(error => {
console.error('Audio playback failed:', error);
});
}
function stopAudio() {
audio.pause();
audio.currentTime = 0; // 再生位置をリセット
}
function changeVolume(value) {
audio.volume = value / 100; // 音量を変更
}
async function editQuizMaster(id) {
try {
const response = await fetch('/select_quiz', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ quiz_id: id })
});
const data = await response.json();
document.getElementById('edit_title').value = data.title;
document.getElementById('edit_full_audio_name_input').value = data.full_audio_name;
document.getElementById('edit_intro_audio_name_input').value = data.intro_audio_name;
document.getElementById('edit_keyword1').value = data.keyword1;
document.getElementById('edit_keyword2').value = data.keyword2;
document.getElementById('edit_keyword3').value = data.keyword3;
document.getElementById('edit_quiz_id').value = data.id;
document.getElementById('addQuizMasterForm').style.display = 'none';
document.getElementById('updateModal').style.display = 'block';
} catch (error) {
console.error('Error fetching quiz master:', error);
}
}
async function handleFormSubmit(event, endpoint) {
event.preventDefault();
const form = event.target;
@ -150,39 +234,19 @@
fetchQuizMasters();
await fetchUsedFiles();
filterFiles();
if (endpoint === '/update_quiz_master') {
document.getElementById('updateModal').style.display = 'none';
document.getElementById('addQuizMasterForm').style.display = 'block';
}
}
} catch (error) {
document.getElementById('message').innerText = 'エラーが発生しました: ' + error.message;
}
}
async function fetchQuizMasters() {
try {
const response = await fetch('/quiz_master');
const quizMasters = await response.json();
const quizMasterList = document.getElementById('quizMasterList');
quizMasterList.innerHTML = '';
quizMasters.forEach(qm => {
const tr = document.createElement('tr');
tr.innerHTML = `
<td>${qm.title}</td>
<td>${qm.full_audio_name}</td>
<td>${qm.intro_audio_name}</td>
<td>${qm.keyword1}</td>
<td>${qm.keyword2}</td>
<td>${qm.keyword3}</td>
<td>
<form onsubmit="handleFormSubmit(event, '/delete_quiz_master')">
<input type="hidden" name="id" value="${qm.id}">
<button type="submit">削除</button>
</form>
</td>
`;
quizMasterList.appendChild(tr);
});
} catch (error) {
console.error('Error fetching quiz masters:', error);
}
function closeUpdateModal() {
document.getElementById('updateModal').style.display = 'none';
document.getElementById('addQuizMasterForm').style.display = 'block';
}
document.addEventListener('DOMContentLoaded', async () => {
@ -196,6 +260,8 @@
<h1>クイズマスタデータ管理</h1>
<div id="message" style="color: red; margin-top: 20px;"></div>
<button onclick="location.href='/'">トップページに戻る</button>
<!-- 新規追加フォーム -->
<form id="addQuizMasterForm" onsubmit="handleFormSubmit(event, '/add_quiz_master')">
<div class="form-group">
<label for="title">タイトル:</label>
@ -231,24 +297,48 @@
</form>
</div>
<div class="form-container">
<h2>CSVアップロード</h2>
<form action="/upload_quiz_master" method="post" enctype="multipart/form-data">
<!-- 編集用モーダル -->
<div id="updateModal">
<form id="updateQuizMasterForm" onsubmit="handleFormSubmit(event, '/update_quiz_master')">
<input type="hidden" name="id" id="edit_quiz_id">
<div class="form-group">
<label for="file">CSVファイル:</label>
<input type="file" name="file" id="file" accept=".csv" required>
<label for="edit_title">タイトル:</label>
<input type="text" name="title" id="edit_title" required>
</div>
<div class="form-group">
<label for="edit_full_audio_name_input">フル音源名:</label>
<input type="text" id="edit_full_audio_name_input" name="full_audio_name" required>
</div>
<div class="form-group">
<label for="edit_intro_audio_name_input">イントロ音源名:</label>
<input type="text" id="edit_intro_audio_name_input" name="intro_audio_name" required>
</div>
<div class="form-group">
<label for="edit_keyword1">キーワード1:</label>
<input type="text" name="keyword1" id="edit_keyword1">
</div>
<div class="form-group">
<label for="edit_keyword2">キーワード2:</label>
<input type="text" name="keyword2" id="edit_keyword2">
</div>
<div class="form-group">
<label for="edit_keyword3">キーワード3:</label>
<input type="text" name="keyword3" id="edit_keyword3">
</div>
<div class="button-group">
<button type="submit">アップロード</button>
</div>
</form>
<form action="/import_quiz_master" method="post">
<div class="button-group">
<button type="submit">インポート</button>
<button type="submit">更新</button>
<button type="button" onclick="closeUpdateModal()">キャンセル</button>
</div>
</form>
</div>
<!-- 音量調整と停止ボタン -->
<div style="margin-top: 20px;">
<label for="volumeSlider">プレビュー音量:</label>
<input type="range" id="volumeSlider" min="0" max="100" value="10" oninput="changeVolume(this.value)">
<button onclick="stopAudio()">レビュー再生停止</button>
</div>
<div class="table-container">
<h2>クイズマスタ一覧</h2>
<table>
@ -270,4 +360,3 @@
</div>
</body>
</html>