461 lines
18 KiB
JavaScript
461 lines
18 KiB
JavaScript
const API_ENDPOINT = 'https://dev.obscura.work/v1';
|
|
|
|
// 确保 selectedLang 在 module.js 中可用
|
|
if (typeof selectedLang === 'undefined') {
|
|
selectedLang = 'zh'; // 如果未定义,设置默认值
|
|
}
|
|
|
|
async function showFeature() {
|
|
try {
|
|
const userInfo = await fetchUserInfo();
|
|
if (!userInfo) {
|
|
alert(lang[selectedLang].pleaseLogin);
|
|
window.location.href = 'login.html';
|
|
return;
|
|
}
|
|
|
|
// 检查邮箱验证状态
|
|
const emailVerified = userInfo.email_verified === 'True' || userInfo.email_verified === true;
|
|
console.log('Email Verified:', emailVerified); // 打印邮箱验证状态
|
|
if (!emailVerified ) {
|
|
alert(lang[selectedLang].pleaseVerifyEmail);
|
|
showUserCenterSection('profile');
|
|
return;
|
|
}
|
|
|
|
// 如果用户已登录且邮箱已验证,继续显示功能
|
|
const chatContent = document.getElementById('chatContent');
|
|
const dashboardContent = document.getElementById('dashboardContent');
|
|
const usersContent = document.getElementById('usersContent');
|
|
const pageTitle = document.getElementById('pageTitle');
|
|
const featureContent = document.getElementById('featureContent');
|
|
const userCenterContent = document.getElementById('userCenterContent');
|
|
const apiKeyInput = document.getElementById('apiKeyInput');
|
|
const showApiDocs = document.getElementById('showApiDocs');
|
|
const apiDocsContent = document.getElementById('apiDocsContent');
|
|
|
|
if (apiDocsContent) apiDocsContent.style.display = 'none';
|
|
if (featureContent) featureContent.style.display = 'flex';
|
|
if (chatContent) chatContent.style.display = 'none';
|
|
if (dashboardContent) dashboardContent.style.display = 'none';
|
|
if (usersContent) usersContent.style.display = 'none';
|
|
if (userCenterContent) userCenterContent.style.display = 'none';
|
|
if (showApiDocs) showApiDocs.style.display = 'none';
|
|
if (pageTitle) pageTitle.innerText = lang[selectedLang].featureDemo;
|
|
|
|
} catch (error) {
|
|
console.error('Error in showFeature:', error);
|
|
alert(lang[selectedLang].errorGettingUserInfo);
|
|
}
|
|
}
|
|
|
|
function displayTokenUsage(data) {
|
|
console.log(lang[selectedLang].tokenUsage, data);
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// 首先声明所有需要的变量
|
|
const aiInput = document.getElementById('aiInput');
|
|
const fileInput = document.getElementById('fileInput');
|
|
const fileInputButton = document.getElementById('fileInputButton');
|
|
const takePictureButton = document.getElementById('takePictureButton');
|
|
const recordVideoButton = document.getElementById('recordVideoButton');
|
|
const clearButton = document.getElementById('clearButton');
|
|
const stopRecordingButton = document.getElementById('stopRecordingButton');
|
|
const previewArea = document.getElementById('previewArea');
|
|
const resultArea = document.getElementById('resultArea');
|
|
const loadingIndicator = document.getElementById('loadingIndicator');
|
|
const poseButton = document.getElementById('poseButton');
|
|
const fallButton = document.getElementById('fallButton');
|
|
const cpmButton = document.getElementById('cpmButton');
|
|
const objectButton = document.getElementById('objectButton');
|
|
const faceButton = document.getElementById('faceButton');
|
|
const compareButton = document.getElementById('compareButton');
|
|
const mediapipeButton = document.getElementById('mediapipeButton');
|
|
const qwenvlButton = document.getElementById('qwenvlButton');
|
|
const cpmanalyzeButton = document.getElementById('cpmanalyzeButton');
|
|
const qwenvlanalyzeButton = document.getElementById('qwenvlanalyzeButton');
|
|
let mediaRecorder;
|
|
let recordedChunks = [];
|
|
// 定义任务类型
|
|
const TASK_TYPES = {
|
|
POSE: 'pose',
|
|
CPM: 'cpm',
|
|
QWENVL: 'qwenvl',
|
|
YOLO: 'yolo',
|
|
FALL: 'fall',
|
|
FACE: 'face',
|
|
MEDIAPIPE: 'mediapipe',
|
|
COMPARE: 'compare',
|
|
CPM_ANALYZE: 'cpm_analyze',
|
|
QWENVL_ANALYZE: 'qwenvl_analyze'
|
|
};
|
|
|
|
// 然后添加事件监听器
|
|
if (fileInput) {
|
|
fileInput.addEventListener('change', function(e) {
|
|
const file = e.target.files[0];
|
|
if (file) {
|
|
const reader = new FileReader();
|
|
reader.onload = function(e) {
|
|
previewArea.innerHTML = '';
|
|
if (file.type.startsWith('image/')) {
|
|
const img = document.createElement('img');
|
|
img.src = e.target.result;
|
|
previewArea.appendChild(img);
|
|
} else if (file.type.startsWith('video/')) {
|
|
const video = document.createElement('video');
|
|
video.src = e.target.result;
|
|
video.controls = true;
|
|
previewArea.appendChild(video);
|
|
}
|
|
}
|
|
reader.readAsDataURL(file);
|
|
}
|
|
});
|
|
}
|
|
|
|
if (fileInputButton) {
|
|
fileInputButton.addEventListener('click', function() {
|
|
fileInput.click();
|
|
});
|
|
}
|
|
|
|
|
|
function uploadFile(taskType) {
|
|
const file = fileInput.files[0];
|
|
console.log("Selected file:", file);
|
|
const apiKey = apiKeyInput.value.trim();
|
|
console.log("API Key:", apiKey);
|
|
|
|
if (!file) {
|
|
alert(lang[selectedLang].pleaseSelectFile);
|
|
return;
|
|
}
|
|
|
|
if (!apiKey) {
|
|
alert(lang[selectedLang].pleaseEnterApiKey);
|
|
return;
|
|
}
|
|
|
|
loadingIndicator.style.display = 'block';
|
|
disableAllButtons();
|
|
|
|
const formData = new FormData();
|
|
formData.append('file', file);
|
|
console.log(`Sending request to: ${API_ENDPOINT}/${taskType}`);
|
|
fetch(`${API_ENDPOINT}/${taskType}`, {
|
|
method: 'POST',
|
|
body: formData,
|
|
headers: {
|
|
'Authorization': `Bearer ${apiKey}`
|
|
}
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
console.log('Upload response:', data);
|
|
if (data.task_id) {
|
|
pollResult(data.task_id, apiKey, taskType);
|
|
displayTokenUsage(data);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error:', error);
|
|
alert(lang[selectedLang].errorUploadingFile);
|
|
loadingIndicator.style.display = 'none';
|
|
enableAllButtons();
|
|
});
|
|
}
|
|
|
|
// 新的轮询函数
|
|
function pollResult(taskId, apiKey, taskType) {
|
|
const pollInterval = setInterval(() => {
|
|
fetch(`${API_ENDPOINT}/result/${taskId}`, {
|
|
headers: {
|
|
'Authorization': `Bearer ${apiKey}`
|
|
}
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
console.log('Poll response:', data);
|
|
if (data.status === 'completed') {
|
|
clearInterval(pollInterval);
|
|
addResultCard(getAnalysisTitle(taskType), data);
|
|
if (taskType !== 'cpm' && taskType !== 'qwenvl' && taskType !== 'qwenvl_analyze' && taskType !== 'cpm_analyze') {
|
|
fetchAnnotatedFile(API_ENDPOINT, taskId, apiKey);
|
|
}
|
|
loadingIndicator.style.display = 'none';
|
|
enableAllButtons();
|
|
} else if (data.status === 'failed') {
|
|
clearInterval(pollInterval);
|
|
alert(lang[selectedLang].processingFailed);
|
|
loadingIndicator.style.display = 'none';
|
|
enableAllButtons();
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error:', error);
|
|
clearInterval(pollInterval);
|
|
alert(lang[selectedLang].errorGettingResults);
|
|
loadingIndicator.style.display = 'none';
|
|
enableAllButtons();
|
|
});
|
|
}, 2000); // 每2秒轮询一次
|
|
}
|
|
|
|
|
|
|
|
// 辅助函数
|
|
function getAnalysisTitle(analysisType) {
|
|
switch(analysisType) {
|
|
case 'pose': return '姿态分析';
|
|
case 'fall': return '跌倒检测';
|
|
case 'cpm': return 'MiniCPM内容分析';
|
|
case 'object': return '目标检测';
|
|
case 'face': return '人脸检测';
|
|
case 'compare': return '面部特征提取';
|
|
case 'mediapipe': return 'mediapipe面部识别';
|
|
case 'qwenvl': return 'Qwen内容分析';
|
|
case 'qwenvl_analyze': return 'qwenvl_OCR分析';
|
|
case 'cpm_analyze': return 'cpm_OCR分析';
|
|
default: return '分析结果';
|
|
}
|
|
}
|
|
|
|
|
|
function fetchAnnotatedFile(apiEndpoint, taskId, apiKey) {
|
|
fetch(`${apiEndpoint}/annotated/${taskId}`, {
|
|
headers: {
|
|
'Authorization': `Bearer ${apiKey}`
|
|
}
|
|
})
|
|
.then(response => {
|
|
if (response.ok) {
|
|
return response.blob();
|
|
}
|
|
if (response.status === 404) {
|
|
throw new Error(lang[selectedLang].resultImageNotFound);
|
|
}
|
|
throw new Error('Network response was not ok.');
|
|
})
|
|
.then(blob => {
|
|
const url = URL.createObjectURL(blob);
|
|
updatePreviewWithAnnotated(url, `result_${taskId}.png`);
|
|
})
|
|
.catch(error => {
|
|
console.error('Error:', error);
|
|
if (error.message === lang[selectedLang].resultImageNotFound) {
|
|
alert(lang[selectedLang].resultImageNotFound);
|
|
} else {
|
|
alert(lang[selectedLang].errorGettingResultImage);
|
|
}
|
|
});
|
|
}
|
|
|
|
function addResultCard(title, data) {
|
|
const card = document.createElement('div');
|
|
card.className = 'result-card';
|
|
|
|
card.innerHTML = `
|
|
<h3>${title}</h3>
|
|
<pre>${JSON.stringify(data, null, 2)}</pre>
|
|
`;
|
|
|
|
resultArea.innerHTML = `<h3>${lang[selectedLang].detectionResult}</h3>`; // 保留标题
|
|
resultArea.appendChild(card);
|
|
}
|
|
|
|
function updatePreviewWithAnnotated(url, filename) {
|
|
previewArea.innerHTML = '';
|
|
|
|
const isImage = filename.match(/\.(jpg|jpeg|png|gif)$/i);
|
|
|
|
if (isImage) {
|
|
const img = document.createElement('img');
|
|
img.src = url;
|
|
img.alt = "Annotated Image";
|
|
img.style.maxWidth = "100%";
|
|
previewArea.appendChild(img);
|
|
} else {
|
|
const video = document.createElement('video');
|
|
video.src = url;
|
|
video.controls = true;
|
|
video.style.maxWidth = "100%";
|
|
previewArea.appendChild(video);
|
|
}
|
|
}
|
|
|
|
function clearAll() {
|
|
aiInput.value = '';
|
|
fileInput.value = '';
|
|
previewArea.innerHTML = '<p>请输入图片URL或上传图片</p>';
|
|
resultArea.innerHTML = '<h3>检测结果</h3>';
|
|
loadingIndicator.style.display = 'none';
|
|
enableAllButtons();
|
|
}
|
|
|
|
function disableAllButtons() {
|
|
poseButton.disabled = true;
|
|
fallButton.disabled = true;
|
|
cpmButton.disabled = true;
|
|
objectButton.disabled = true;
|
|
faceButton.disabled = true;
|
|
qwenvlButton.disabled = true;
|
|
mediapipeButton.disabled = true;
|
|
takePictureButton.disabled = true;
|
|
recordVideoButton.disabled = true;
|
|
compareButton.disabled = true;
|
|
qwenvlanalyzeButton.disabled =true;
|
|
cpmanalyzeButton.disabled = true
|
|
}
|
|
|
|
function enableAllButtons() {
|
|
poseButton.disabled = false;
|
|
fallButton.disabled = false;
|
|
cpmButton.disabled = false;
|
|
objectButton.disabled = false;
|
|
faceButton.disabled = false;
|
|
qwenvlButton.disabled = false;
|
|
mediapipeButton.disabled = false;
|
|
takePictureButton.disabled = false;
|
|
recordVideoButton.disabled = false;
|
|
compareButton.disabled = false;
|
|
qwenvlanalyzeButton.disabled =false;
|
|
cpmanalyzeButton.disabled = false
|
|
}
|
|
|
|
function takePicture() {
|
|
navigator.mediaDevices.getUserMedia({ video: true })
|
|
.then(function(stream) {
|
|
const video = document.createElement('video');
|
|
video.srcObject = stream;
|
|
video.play();
|
|
|
|
video.onloadedmetadata = function(e) {
|
|
const canvas = document.createElement('canvas');
|
|
canvas.width = video.videoWidth;
|
|
canvas.height = video.videoHeight;
|
|
canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
stream.getTracks().forEach(track => track.stop());
|
|
|
|
previewArea.innerHTML = '';
|
|
const img = document.createElement('img');
|
|
img.src = canvas.toDataURL('image/jpeg');
|
|
previewArea.appendChild(img);
|
|
|
|
canvas.toBlob(function(blob) {
|
|
const file = new File([blob], "camera_photo.jpg", { type: "image/jpeg" });
|
|
const dataTransfer = new DataTransfer();
|
|
dataTransfer.items.add(file);
|
|
fileInput.files = dataTransfer.files;
|
|
}, 'image/jpeg');
|
|
};
|
|
})
|
|
.catch(function(err) {
|
|
console.error(lang[selectedLang].cameraAccessFailed, err);
|
|
alert(lang[selectedLang].cameraPermissionDenied);
|
|
});
|
|
}
|
|
|
|
function startRecording() {
|
|
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
|
|
.then(function(stream) {
|
|
previewArea.innerHTML = '<video id="liveVideo" autoplay muted></video>';
|
|
const liveVideo = document.getElementById('liveVideo');
|
|
liveVideo.srcObject = stream;
|
|
|
|
mediaRecorder = new MediaRecorder(stream);
|
|
mediaRecorder.ondataavailable = function(e) {
|
|
if (e.data.size > 0) {
|
|
recordedChunks.push(e.data);
|
|
}
|
|
};
|
|
|
|
mediaRecorder.onstop = function() {
|
|
const blob = new Blob(recordedChunks, { type: 'video/webm' });
|
|
const videoURL = URL.createObjectURL(blob);
|
|
previewArea.innerHTML = '<video id="recordedVideo" controls></video>';
|
|
const recordedVideo = document.getElementById('recordedVideo');
|
|
recordedVideo.src = videoURL;
|
|
|
|
const file = new File([blob], "camera_video.webm", { type: "video/webm" });
|
|
const dataTransfer = new DataTransfer();
|
|
dataTransfer.items.add(file);
|
|
fileInput.files = dataTransfer.files;
|
|
|
|
stream.getTracks().forEach(track => track.stop());
|
|
};
|
|
|
|
mediaRecorder.start();
|
|
recordVideoButton.style.display = 'none';
|
|
stopRecordingButton.style.display = 'block';
|
|
|
|
setTimeout(() => {
|
|
if (mediaRecorder.state === 'recording') {
|
|
stopRecording();
|
|
}
|
|
}, 15000);
|
|
})
|
|
.catch(function(err) {
|
|
console.error(lang[selectedLang].cameraAccessFailed, err);
|
|
alert(lang[selectedLang].cameraPermissionDenied);
|
|
});
|
|
}
|
|
|
|
function stopRecording() {
|
|
if (mediaRecorder && mediaRecorder.state === 'recording') {
|
|
mediaRecorder.stop();
|
|
recordVideoButton.style.display = 'block';
|
|
stopRecordingButton.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
// 最后添加其他按钮的事件监听器
|
|
if (recordVideoButton) recordVideoButton.addEventListener('click', startRecording);
|
|
if (stopRecordingButton) stopRecordingButton.addEventListener('click', stopRecording);
|
|
if (clearButton) clearButton.addEventListener('click', clearAll);
|
|
if (takePictureButton) takePictureButton.addEventListener('click', takePicture);
|
|
if (poseButton) poseButton.addEventListener('click', () => uploadFile(TASK_TYPES.POSE));
|
|
if (fallButton) fallButton.addEventListener('click', () => uploadFile(TASK_TYPES.FALL));
|
|
if (objectButton) objectButton.addEventListener('click', () => uploadFile(TASK_TYPES.YOLO));
|
|
if (faceButton) faceButton.addEventListener('click', () => uploadFile(TASK_TYPES.FACE));
|
|
if (compareButton) compareButton.addEventListener('click', () => uploadFile(TASK_TYPES.COMPARE));
|
|
if (mediapipeButton) mediapipeButton.addEventListener('click', () => uploadFile(TASK_TYPES.MEDIAPIPE));
|
|
if (cpmButton) cpmButton.addEventListener('click', () => uploadFile(TASK_TYPES.CPM));
|
|
if (qwenvlButton) qwenvlButton.addEventListener('click', () => uploadFile(TASK_TYPES.QWENVL));
|
|
if (qwenvlanalyzeButton) qwenvlanalyzeButton.addEventListener('click', () => uploadFile(TASK_TYPES.QWENVL_ANALYZE));
|
|
if (cpmanalyzeButton) cpmanalyzeButton.addEventListener('click', () => uploadFile(TASK_TYPES.CPM_ANALYZE));
|
|
});
|
|
|
|
|
|
|
|
// 获取用户信息的函数
|
|
async function fetchUserInfo() {
|
|
try {
|
|
const token = localStorage.getItem('access_token');
|
|
if (!token) {
|
|
console.error('No access token found');
|
|
return null;
|
|
}
|
|
|
|
const response = await fetch(`${BASE_URL}/me`, {
|
|
headers: {
|
|
'Authorization': `Bearer ${token}`
|
|
}
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error('Failed to fetch user info');
|
|
}
|
|
|
|
const userInfo = await response.json();
|
|
return userInfo;
|
|
|
|
} catch (error) {
|
|
console.error('Error fetching user info:', error);
|
|
// 可以在这里处理错误,比如清除 token 并重定向到登录页面
|
|
localStorage.removeItem('access_token');
|
|
window.location.href = 'login.html'; // 重定向到登录页面
|
|
return null;
|
|
}
|
|
} |