generated from dellevin/template
555
This commit is contained in:
8
flask-dev-api/.idea/.gitignore
generated
vendored
Normal file
8
flask-dev-api/.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
25
flask-dev-api/.idea/flask-dev-api.iml
generated
Normal file
25
flask-dev-api/.idea/flask-dev-api.iml
generated
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="Flask">
|
||||
<option name="enabled" value="true" />
|
||||
</component>
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Python 3.12" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="PyDocumentationSettings">
|
||||
<option name="format" value="PLAIN" />
|
||||
<option name="myDocStringFormat" value="Plain" />
|
||||
</component>
|
||||
<component name="TemplatesService">
|
||||
<option name="TEMPLATE_CONFIGURATION" value="Jinja2" />
|
||||
<option name="TEMPLATE_FOLDERS">
|
||||
<list>
|
||||
<option value="$MODULE_DIR$/templates" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
</module>
|
||||
23
flask-dev-api/.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
23
flask-dev-api/.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoredPackages">
|
||||
<value>
|
||||
<list size="9">
|
||||
<item index="0" class="java.lang.String" itemvalue="sentence_transformers" />
|
||||
<item index="1" class="java.lang.String" itemvalue="fast_langdetect" />
|
||||
<item index="2" class="java.lang.String" itemvalue="jieba_fast" />
|
||||
<item index="3" class="java.lang.String" itemvalue="rotary_embedding_torch" />
|
||||
<item index="4" class="java.lang.String" itemvalue="ko_pron" />
|
||||
<item index="5" class="java.lang.String" itemvalue="pyopenjtalk" />
|
||||
<item index="6" class="java.lang.String" itemvalue="g2p_en" />
|
||||
<item index="7" class="java.lang.String" itemvalue="huggingface_hub" />
|
||||
<item index="8" class="java.lang.String" itemvalue="x_transformers" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
6
flask-dev-api/.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
flask-dev-api/.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
||||
7
flask-dev-api/.idea/misc.xml
generated
Normal file
7
flask-dev-api/.idea/misc.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.12" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12" project-jdk-type="Python SDK" />
|
||||
</project>
|
||||
8
flask-dev-api/.idea/modules.xml
generated
Normal file
8
flask-dev-api/.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/flask-dev-api.iml" filepath="$PROJECT_DIR$/.idea/flask-dev-api.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
6
flask-dev-api/.idea/vcs.xml
generated
Normal file
6
flask-dev-api/.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
15
flask-dev-api/.idea/webResources.xml
generated
Normal file
15
flask-dev-api/.idea/webResources.xml
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="WebResourcesPaths">
|
||||
<contentEntries>
|
||||
<entry url="file://$PROJECT_DIR$">
|
||||
<entryData>
|
||||
<resourceRoots>
|
||||
<path value="file://$PROJECT_DIR$/config" />
|
||||
<path value="file://$PROJECT_DIR$/static" />
|
||||
</resourceRoots>
|
||||
</entryData>
|
||||
</entry>
|
||||
</contentEntries>
|
||||
</component>
|
||||
</project>
|
||||
@@ -15,7 +15,7 @@ from blueprints import pin_tu_bp, base64_bp, down_video_bp, fen_ci_bp, content_t
|
||||
from utils.stats_db import init_db, record_call, get_total_count, get_avg_duration, get_daily_counts, get_monthly_counts, get_available_years, get_available_months, get_daily_counts_by_month, get_hourly_counts, get_endpoint_stats, get_today_count, get_yesterday_count, get_month_count, get_success_rate, get_duration_distribution, get_slowest_endpoints, get_week_compare, get_recent_calls, get_active_days
|
||||
from utils.stats_db import ALLOWED_TABLES, get_table_info, get_table_rows, get_table_count, insert_row, update_row, delete_rows, clear_table
|
||||
|
||||
TOOL_COUNT = 19
|
||||
TOOL_COUNT = 18
|
||||
|
||||
|
||||
def create_app():
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"threshold": -41,
|
||||
"min_length": 5000,
|
||||
"min_interval": 300,
|
||||
"min_interval": 500,
|
||||
"hop_size": 10,
|
||||
"max_sil_kept": 1000
|
||||
}
|
||||
@@ -4,13 +4,13 @@
|
||||
"model_dir_mode": "absolute",
|
||||
"index_dir": "E:\\AI\\RVC\\rvc_con\\index",
|
||||
"index_dir_mode": "absolute",
|
||||
"last_model": "",
|
||||
"last_index": "",
|
||||
"last_model": "bubu-sing.pth",
|
||||
"last_index": "E:\\AI\\RVC\\rvc_con\\index\\bubu-sing_v2.index",
|
||||
"f0_up_key": 0,
|
||||
"f0_method": "rmvpe",
|
||||
"index_rate": 0.75,
|
||||
"index_rate": 1,
|
||||
"filter_radius": 3,
|
||||
"resample_sr": 22050,
|
||||
"rms_mix_rate": 0.25,
|
||||
"protect": 0.33
|
||||
"resample_sr": 32000,
|
||||
"rms_mix_rate": 0.5,
|
||||
"protect": 0.37
|
||||
}
|
||||
Binary file not shown.
@@ -280,6 +280,56 @@
|
||||
position: absolute; top: 0; left: 0; width: 100%; height: 100%;
|
||||
border-radius: 8px; pointer-events: none;
|
||||
}
|
||||
#segmentOverlay {
|
||||
position: absolute; top: 0; left: 0; width: 100%; height: 100%;
|
||||
border-radius: 8px; pointer-events: none; z-index: 1;
|
||||
}
|
||||
.seg-gray {
|
||||
position: absolute; top: 0; height: 100%;
|
||||
background: rgba(0,0,0,0.18); pointer-events: none;
|
||||
}
|
||||
#playCursor {
|
||||
position: absolute; top: 0; height: 100%; width: 10px;
|
||||
transform: translateX(-5px); z-index: 5;
|
||||
pointer-events: auto; cursor: col-resize;
|
||||
display: none;
|
||||
}
|
||||
#playCursor::after {
|
||||
content: ''; position: absolute; left: 50%; top: 0; bottom: 0;
|
||||
width: 2px; transform: translateX(-50%);
|
||||
background: #e53935; box-shadow: 0 0 4px rgba(229,57,53,0.4);
|
||||
pointer-events: none;
|
||||
}
|
||||
#cursorHandle {
|
||||
position: absolute; left: 50%; top: -6px; transform: translateX(-50%);
|
||||
width: 12px; height: 12px; border-radius: 50%;
|
||||
background: #e53935; border: 2px solid #fff;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,0.25);
|
||||
pointer-events: none;
|
||||
}
|
||||
.player-controls {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
padding: 6px 12px; margin-top: 4px;
|
||||
background: #fff; border: 1px solid #e0e0e0; border-radius: 8px;
|
||||
font-size: 12px; color: #555;
|
||||
}
|
||||
.player-controls button {
|
||||
width: 30px; height: 30px; border: none; border-radius: 50%;
|
||||
background: #0078d4; color: #fff; cursor: pointer;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-size: 12px; transition: background 0.15s;
|
||||
}
|
||||
.player-controls button:hover { background: #005fa3; }
|
||||
.player-controls .p-time { font-variant-numeric: tabular-nums; min-width: 36px; text-align: center; }
|
||||
.player-controls input[type="range"] {
|
||||
flex: 1; height: 4px; -webkit-appearance: none; appearance: none;
|
||||
background: #e0e0e0; border-radius: 2px; outline: none; cursor: pointer;
|
||||
}
|
||||
.player-controls input[type="range"]::-webkit-slider-thumb {
|
||||
-webkit-appearance: none; appearance: none;
|
||||
width: 14px; height: 14px; border-radius: 50%;
|
||||
background: #0078d4; cursor: pointer;
|
||||
}
|
||||
.cut-marker {
|
||||
position: absolute; top: 0; width: 24px; height: 100%;
|
||||
transform: translateX(-50%); z-index: 2; cursor: grab;
|
||||
@@ -431,6 +481,12 @@
|
||||
</div>
|
||||
<div id="timelineArea" style="display:none;">
|
||||
<div class="timeline-wrap" id="timelineBar"></div>
|
||||
<div class="player-controls" id="playerControls" style="display:none;">
|
||||
<button onclick="playerToggle()" title="播放/暂停"><i id="playerIcon" class="fas fa-play"></i></button>
|
||||
<span class="p-time" id="playerTime">0:00</span>
|
||||
<input type="range" id="playerSeekBar" min="0" max="1000" value="0" step="1">
|
||||
<span class="p-time" id="playerTotal">0:00</span>
|
||||
</div>
|
||||
<div id="redoWrap" style="display:none;text-align:center;margin-top:8px;">
|
||||
<button class="action-btn slice" style="width:auto;padding:8px 20px;font-size:13px;" onclick="redoManualSlice()">
|
||||
<i class="fas fa-redo"></i> 切割点已更改,点击重新切割
|
||||
@@ -466,6 +522,14 @@
|
||||
let manualSegChanged = false; // 用户是否手动调整过勾选
|
||||
let manualSegInit = false; // 新建预览时重置勾选
|
||||
|
||||
// 播放器状态
|
||||
var playerAudio = null;
|
||||
var playerPlaying = false;
|
||||
var _playerRAF = null;
|
||||
var _playerStartSec = 0;
|
||||
var _playerEndSec = 0;
|
||||
var _playerMode = 'full'; // 'full' 全曲 | 'seg' 片段
|
||||
|
||||
function showMessage(text, type, duration) {
|
||||
type = type || 'info'; duration = duration || 3000;
|
||||
var t = document.createElement('div');
|
||||
@@ -648,14 +712,6 @@
|
||||
document.getElementById('sliceList').innerHTML = html;
|
||||
}
|
||||
|
||||
function playPreviewSeg(index) {
|
||||
if (!taskId || !window._previewRanges || !window._previewRanges[index]) return;
|
||||
if (currentAudio) { currentAudio.pause(); currentAudio = null; }
|
||||
var r = window._previewRanges[index];
|
||||
var audio = new Audio('/audio-slicer/preview-range/' + taskId + '?start=' + r[0] + '&end=' + r[1]);
|
||||
audio.play();
|
||||
currentAudio = audio;
|
||||
}
|
||||
|
||||
function toggleSegment(idx) {
|
||||
var pos = selectedSegments.indexOf(idx);
|
||||
@@ -696,6 +752,7 @@
|
||||
if (el) el.textContent = '已选 ' + selectedSegments.length + '/' + total;
|
||||
var btnSlice = document.getElementById('btnSlice');
|
||||
if (btnSlice) btnSlice.disabled = selectedSegments.length === 0;
|
||||
_updateSegmentOverlay();
|
||||
}
|
||||
|
||||
async function doSlice() {
|
||||
@@ -790,13 +847,8 @@
|
||||
document.getElementById('sliceList').innerHTML = html;
|
||||
}
|
||||
|
||||
|
||||
var currentAudio = null;
|
||||
function playSlice(index) {
|
||||
if (currentAudio) { currentAudio.pause(); currentAudio = null; }
|
||||
var audio = new Audio('/audio-slicer/download/' + taskId + '/' + index);
|
||||
audio.play();
|
||||
currentAudio = audio;
|
||||
}
|
||||
|
||||
function downloadSlice(index) {
|
||||
var a = document.createElement('a');
|
||||
@@ -902,8 +954,6 @@
|
||||
// 新预览时重置勾选
|
||||
if (manualSegInit) {
|
||||
manualSegInit = false;
|
||||
manualSegSelected = [];
|
||||
manualSegChanged = false;
|
||||
}
|
||||
|
||||
var html = '';
|
||||
@@ -918,8 +968,8 @@
|
||||
}
|
||||
|
||||
var boundaries = [0].concat(cutPoints).concat([audioDuration]);
|
||||
// 确保勾选数组长度匹配
|
||||
if (manualSegSelected.length !== boundaries.length - 1) {
|
||||
// 仅初始化时全选;用户已手动调整勾选则保留
|
||||
if (manualSegSelected.length !== boundaries.length - 1 && !manualSegChanged) {
|
||||
manualSegSelected = [];
|
||||
for (var j = 0; j < boundaries.length - 1; j++) manualSegSelected.push(j);
|
||||
}
|
||||
@@ -965,13 +1015,6 @@
|
||||
markCutPointsChanged();
|
||||
}
|
||||
|
||||
var previewAudio = null;
|
||||
function playPreview(start, end) {
|
||||
if (!taskId) return;
|
||||
if (previewAudio) { previewAudio.pause(); previewAudio = null; }
|
||||
previewAudio = new Audio('/audio-slicer/preview-range/' + taskId + '?start=' + start + '&end=' + end);
|
||||
previewAudio.play();
|
||||
}
|
||||
|
||||
function toggleManualSeg(idx) {
|
||||
var pos = manualSegSelected.indexOf(idx);
|
||||
@@ -988,6 +1031,7 @@
|
||||
if (row) row.className = 'seg-row' + (pos < 0 ? ' selected' : ' dimmed');
|
||||
updateManualSegCount();
|
||||
updateManualBtn();
|
||||
_updateSegmentOverlay();
|
||||
}
|
||||
|
||||
function selectManualSegments() {
|
||||
@@ -995,6 +1039,7 @@
|
||||
for (var i = 0; i < cutPoints.length + 1; i++) manualSegSelected.push(i);
|
||||
manualSegChanged = true;
|
||||
renderCutList();
|
||||
_updateSegmentOverlay();
|
||||
}
|
||||
|
||||
|
||||
@@ -1030,7 +1075,9 @@
|
||||
|
||||
bar.className = readOnly ? 'readonly' : '';
|
||||
|
||||
var html = '<canvas id="waveformCanvas"></canvas>';
|
||||
var html = '<canvas id="waveformCanvas"></canvas>'
|
||||
+ '<div id="segmentOverlay"></div>'
|
||||
+ '<div id="playCursor"><div id="cursorHandle"></div></div>';
|
||||
// 时间刻度(在波形条上方)
|
||||
var step = Math.max(1, Math.ceil(audioDuration / 10));
|
||||
for (var t = 0; t <= audioDuration; t += step) {
|
||||
@@ -1050,20 +1097,62 @@
|
||||
|
||||
// 绘制波形
|
||||
if (waveformPeaks) drawWaveform();
|
||||
_updateSegmentOverlay();
|
||||
|
||||
if (readOnly) return; // 只读模式不绑定交互事件
|
||||
// 播放控制条
|
||||
var pc = document.getElementById('playerControls');
|
||||
if (pc) { pc.style.display = audioDuration > 0 ? '' : 'none'; }
|
||||
var pt = document.getElementById('playerTotal');
|
||||
if (pt) pt.textContent = formatTime(audioDuration);
|
||||
_showCursorAtCurrent();
|
||||
|
||||
// 时间轴空白区域点击 → 添加切割点
|
||||
// 绑定事件(只绑一次)
|
||||
if (!bar._bound) {
|
||||
bar._bound = true;
|
||||
|
||||
// 时间轴点击:只读模式 → seek,手动模式 → 添加切割点
|
||||
bar.addEventListener('click', function(e) {
|
||||
if (bar._suppressClick) { bar._suppressClick = false; return; }
|
||||
if (bar.classList.contains('readonly')) return;
|
||||
if (e.target.closest('.cut-marker')) return;
|
||||
if (e.target.closest('.cut-marker') || e.target.closest('#playCursor')) return;
|
||||
var rect = bar.getBoundingClientRect();
|
||||
var sec = (e.clientX - rect.left) / rect.width * audioDuration;
|
||||
if (bar.classList.contains('readonly')) {
|
||||
playerSeekTo(sec);
|
||||
return;
|
||||
}
|
||||
// 手动模式:不允许在灰色片段内添加切割点
|
||||
var ranges = _getSegmentRanges();
|
||||
var sel = _getSelectedSet();
|
||||
if (ranges.length && sel.length) {
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
if (sec >= ranges[i][0] && sec <= ranges[i][1]) {
|
||||
if (sel.indexOf(i) < 0) return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sec > 0.5 && sec < audioDuration - 0.5) addCutPoint(sec);
|
||||
});
|
||||
|
||||
// 播放游标拖拽
|
||||
_bindCursorDrag(bar);
|
||||
|
||||
// 进度条拖拽 seek
|
||||
var seekBar = document.getElementById('playerSeekBar');
|
||||
if (seekBar) {
|
||||
seekBar.addEventListener('input', function() {
|
||||
var sec = (parseFloat(this.value) / 1000) * audioDuration;
|
||||
var cursor = document.getElementById('playCursor');
|
||||
var timeEl = document.getElementById('playerTime');
|
||||
if (cursor) { cursor.style.display = 'block'; cursor.style.left = (sec / audioDuration * 100).toFixed(2) + '%'; }
|
||||
if (timeEl) timeEl.textContent = formatTime(sec);
|
||||
if (playerAudio) {
|
||||
var rel = sec - _playerStartSec;
|
||||
var dur = _playerEndSec - _playerStartSec;
|
||||
if (rel >= 0 && rel <= dur) playerAudio.currentTime = rel;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 为每个标记绑定拖动
|
||||
@@ -1099,6 +1188,245 @@
|
||||
}
|
||||
}
|
||||
|
||||
// ── 片段灰色覆盖(取消选中的片段变灰) ──
|
||||
function _getSegmentRanges() {
|
||||
// 手动模式:从 cutPoints 计算
|
||||
if (cutMode === 'manual') {
|
||||
if (cutPoints.length > 0) {
|
||||
var b = [0].concat(cutPoints.slice().sort(function(a,b){return a-b;})).concat([audioDuration]);
|
||||
var ranges = [];
|
||||
for (var i = 0; i < b.length - 1; i++) ranges.push([b[i], b[i+1]]);
|
||||
return ranges;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
// 自动模式:用预览区间
|
||||
if (window._previewRanges && window._previewRanges.length) {
|
||||
return window._previewRanges;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function _getSelectedSet() {
|
||||
if (manualSegSelected && manualSegSelected.length > 0 && cutPoints.length > 0) {
|
||||
return manualSegSelected;
|
||||
}
|
||||
return selectedSegments;
|
||||
}
|
||||
|
||||
function _updateSegmentOverlay() {
|
||||
var overlay = document.getElementById('segmentOverlay');
|
||||
if (!overlay || audioDuration <= 0) return;
|
||||
var ranges = _getSegmentRanges();
|
||||
if (!ranges.length) { overlay.innerHTML = ''; return; }
|
||||
var sel = _getSelectedSet();
|
||||
var html = '';
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
if (sel.indexOf(i) < 0) {
|
||||
var left = (ranges[i][0] / audioDuration * 100).toFixed(2);
|
||||
var width = ((ranges[i][1] - ranges[i][0]) / audioDuration * 100).toFixed(2);
|
||||
html += '<div class="seg-gray" style="left:' + left + '%;width:' + width + '%;"></div>';
|
||||
}
|
||||
}
|
||||
overlay.innerHTML = html;
|
||||
}
|
||||
|
||||
// ── 播放器系统 ──
|
||||
function _showCursorAtCurrent() {
|
||||
var cursor = document.getElementById('playCursor');
|
||||
if (!cursor) return;
|
||||
if (playerAudio) {
|
||||
var sec = _playerStartSec + playerAudio.currentTime;
|
||||
cursor.style.left = (sec / audioDuration * 100).toFixed(2) + '%';
|
||||
}
|
||||
}
|
||||
|
||||
function _playerTick() {
|
||||
var cursor = document.getElementById('playCursor');
|
||||
var timeEl = document.getElementById('playerTime');
|
||||
if (!playerAudio || !cursor) return;
|
||||
var sec = _playerStartSec + playerAudio.currentTime;
|
||||
cursor.style.left = (sec / audioDuration * 100).toFixed(2) + '%';
|
||||
if (timeEl) timeEl.textContent = formatTime(sec);
|
||||
var seekBar = document.getElementById('playerSeekBar');
|
||||
if (seekBar && audioDuration > 0) seekBar.value = (sec / audioDuration * 1000).toFixed(0);
|
||||
_playerRAF = requestAnimationFrame(_playerTick);
|
||||
}
|
||||
|
||||
function playerStartTick() {
|
||||
playerStopTick();
|
||||
var cursor = document.getElementById('playCursor');
|
||||
if (cursor) cursor.style.display = 'block';
|
||||
_playerRAF = requestAnimationFrame(_playerTick);
|
||||
}
|
||||
|
||||
function playerStopTick() {
|
||||
if (_playerRAF) { cancelAnimationFrame(_playerRAF); _playerRAF = null; }
|
||||
}
|
||||
|
||||
function playerSeekTo(sec) {
|
||||
sec = Math.max(0, Math.min(audioDuration, sec));
|
||||
var cursor = document.getElementById('playCursor');
|
||||
var timeEl = document.getElementById('playerTime');
|
||||
if (cursor) {
|
||||
cursor.style.display = 'block';
|
||||
cursor.style.left = (sec / audioDuration * 100).toFixed(2) + '%';
|
||||
}
|
||||
if (timeEl) timeEl.textContent = formatTime(sec);
|
||||
if (playerAudio && !playerAudio.paused) {
|
||||
var rel = sec - _playerStartSec;
|
||||
if (rel >= 0 && rel <= _playerEndSec - _playerStartSec) {
|
||||
playerAudio.currentTime = rel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function playerToggle() {
|
||||
if (playerPlaying) {
|
||||
playerPause();
|
||||
} else {
|
||||
playerPlay();
|
||||
}
|
||||
}
|
||||
|
||||
function playerPlay() {
|
||||
if (!taskId || audioDuration <= 0) return;
|
||||
if (playerAudio && !playerAudio.paused) return;
|
||||
// 恢复暂停的音频
|
||||
if (playerAudio && playerAudio.paused && playerAudio.currentTime > 0 && playerAudio.duration > 0) {
|
||||
playerAudio.play();
|
||||
playerPlaying = true;
|
||||
playerStartTick();
|
||||
_updatePlayerIcon();
|
||||
return;
|
||||
}
|
||||
// 新建全曲播放
|
||||
_playerStopAll();
|
||||
_playerMode = 'full';
|
||||
_playerStartSec = 0;
|
||||
_playerEndSec = audioDuration;
|
||||
playerAudio = new Audio('/audio-slicer/preview-range/' + taskId + '?start=0&end=' + audioDuration);
|
||||
playerAudio.addEventListener('ended', function() {
|
||||
playerPlaying = false;
|
||||
playerStopTick();
|
||||
_updatePlayerIcon();
|
||||
});
|
||||
playerAudio.play();
|
||||
playerPlaying = true;
|
||||
playerStartTick();
|
||||
_updatePlayerIcon();
|
||||
}
|
||||
|
||||
function playerPause() {
|
||||
if (playerAudio) playerAudio.pause();
|
||||
playerPlaying = false;
|
||||
playerStopTick();
|
||||
_updatePlayerIcon();
|
||||
}
|
||||
|
||||
function _playerStopAll() {
|
||||
playerStopTick();
|
||||
if (playerAudio) {
|
||||
playerAudio.pause();
|
||||
playerAudio.src = '';
|
||||
playerAudio = null;
|
||||
}
|
||||
playerPlaying = false;
|
||||
_updatePlayerIcon();
|
||||
if (currentAudio) { currentAudio.pause(); currentAudio = null; }
|
||||
}
|
||||
|
||||
function _updatePlayerIcon() {
|
||||
var icon = document.getElementById('playerIcon');
|
||||
if (icon) icon.className = playerPlaying ? 'fas fa-pause' : 'fas fa-play';
|
||||
}
|
||||
|
||||
// 播放片段(自动模式列表按钮)
|
||||
function playPreviewSeg(index) {
|
||||
if (!taskId || !window._previewRanges || !window._previewRanges[index]) return;
|
||||
_playerStopAll();
|
||||
var r = window._previewRanges[index];
|
||||
_playerMode = 'seg';
|
||||
_playerStartSec = r[0];
|
||||
_playerEndSec = r[1];
|
||||
playerAudio = new Audio('/audio-slicer/preview-range/' + taskId + '?start=' + r[0] + '&end=' + r[1]);
|
||||
playerAudio.addEventListener('ended', function() {
|
||||
playerPlaying = false;
|
||||
playerStopTick();
|
||||
_updatePlayerIcon();
|
||||
});
|
||||
playerAudio.play();
|
||||
playerPlaying = true;
|
||||
playerStartTick();
|
||||
_updatePlayerIcon();
|
||||
}
|
||||
|
||||
// 播放片段(手动模式列表按钮)
|
||||
function playPreview(start, end) {
|
||||
if (!taskId) return;
|
||||
_playerStopAll();
|
||||
_playerMode = 'seg';
|
||||
_playerStartSec = start;
|
||||
_playerEndSec = end;
|
||||
playerAudio = new Audio('/audio-slicer/preview-range/' + taskId + '?start=' + start + '&end=' + end);
|
||||
playerAudio.addEventListener('ended', function() {
|
||||
playerPlaying = false;
|
||||
playerStopTick();
|
||||
_updatePlayerIcon();
|
||||
});
|
||||
playerAudio.play();
|
||||
playerPlaying = true;
|
||||
playerStartTick();
|
||||
_updatePlayerIcon();
|
||||
}
|
||||
|
||||
// 播放已切片结果
|
||||
function playSlice(index) {
|
||||
_playerStopAll();
|
||||
var audio = new Audio('/audio-slicer/download/' + taskId + '/' + index);
|
||||
audio.play();
|
||||
currentAudio = audio;
|
||||
}
|
||||
|
||||
// 游标拖拽
|
||||
function _bindCursorDrag(bar) {
|
||||
var cursor = document.getElementById('playCursor');
|
||||
if (!cursor) return;
|
||||
var dragging = false;
|
||||
|
||||
cursor.addEventListener('pointerdown', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
dragging = true;
|
||||
cursor.setPointerCapture(e.pointerId);
|
||||
});
|
||||
|
||||
cursor.addEventListener('pointermove', function(e) {
|
||||
if (!dragging) return;
|
||||
var rect = bar.getBoundingClientRect();
|
||||
var sec = (e.clientX - rect.left) / rect.width * audioDuration;
|
||||
sec = Math.max(0, Math.min(audioDuration, sec));
|
||||
cursor.style.left = (sec / audioDuration * 100).toFixed(2) + '%';
|
||||
var timeEl = document.getElementById('playerTime');
|
||||
if (timeEl) timeEl.textContent = formatTime(sec);
|
||||
// 同步 audio currentTime
|
||||
if (playerAudio) {
|
||||
var rel = sec - _playerStartSec;
|
||||
var dur = _playerEndSec - _playerStartSec;
|
||||
if (rel >= 0 && rel <= dur) {
|
||||
playerAudio.currentTime = rel;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
cursor.addEventListener('pointerup', function(e) {
|
||||
if (!dragging) return;
|
||||
dragging = false;
|
||||
cursor.releasePointerCapture(e.pointerId);
|
||||
bar._suppressClick = true;
|
||||
});
|
||||
}
|
||||
|
||||
async function loadWaveform() {
|
||||
if (!taskId) return;
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user