Features: - VideoLearningAgent for automated video watching on Douyin/Kuaishou/TikTok - Web dashboard UI for video learning sessions - Real-time progress tracking with screenshot capture - App detection using get_current_app() for accurate recording - Session management with pause/resume/stop controls Technical improvements: - Simplified video detection logic using direct app detection - Full base64 hash for sensitive screenshot change detection - Immediate stop when target video count is reached - Fixed circular import issues with ModelConfig Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
201 lines
6.6 KiB
JavaScript
201 lines
6.6 KiB
JavaScript
/**
|
|
* Video Learning Module for AutoGLM Dashboard
|
|
*
|
|
* This module provides UI and functionality for the Video Learning Agent,
|
|
* allowing users to watch and learn from short video platforms.
|
|
*/
|
|
|
|
const VideoLearningModule = {
|
|
// Current session state
|
|
currentSessionId: null,
|
|
currentSessionStatus: null,
|
|
videos: [],
|
|
isPolling: false,
|
|
|
|
// Create a new learning session
|
|
async createSession(deviceId, options = {}) {
|
|
const {
|
|
platform = 'douyin',
|
|
targetCount = 10,
|
|
category = null,
|
|
watchDuration = 3.0,
|
|
} = options;
|
|
|
|
try {
|
|
const response = await axios.post('/api/video-learning/sessions', {
|
|
device_id: deviceId,
|
|
platform: platform,
|
|
target_count: targetCount,
|
|
category: category,
|
|
watch_duration: watchDuration,
|
|
});
|
|
|
|
this.currentSessionId = response.data.session_id;
|
|
this.startPolling();
|
|
|
|
return response.data;
|
|
} catch (error) {
|
|
console.error('Error creating session:', error);
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
// Start a session
|
|
async startSession(sessionId) {
|
|
try {
|
|
const response = await axios.post(`/api/video-learning/sessions/${sessionId}/start`);
|
|
return response.data;
|
|
} catch (error) {
|
|
console.error('Error starting session:', error);
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
// Control a session (pause/resume/stop)
|
|
async controlSession(sessionId, action) {
|
|
try {
|
|
const response = await axios.post(`/api/video-learning/sessions/${sessionId}/control`, {
|
|
action: action,
|
|
});
|
|
return response.data;
|
|
} catch (error) {
|
|
console.error('Error controlling session:', error);
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
// Get session status
|
|
async getSessionStatus(sessionId) {
|
|
try {
|
|
const response = await axios.get(`/api/video-learning/sessions/${sessionId}/status`);
|
|
this.currentSessionStatus = response.data;
|
|
return response.data;
|
|
} catch (error) {
|
|
console.error('Error getting session status:', error);
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
// Get session videos
|
|
async getSessionVideos(sessionId) {
|
|
try {
|
|
const response = await axios.get(`/api/video-learning/sessions/${sessionId}/videos`);
|
|
this.videos = response.data;
|
|
return response.data;
|
|
} catch (error) {
|
|
console.error('Error getting session videos:', error);
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
// List all active sessions
|
|
async listSessions() {
|
|
try {
|
|
const response = await axios.get('/api/video-learning/sessions');
|
|
return response.data;
|
|
} catch (error) {
|
|
console.error('Error listing sessions:', error);
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
// Delete a session
|
|
async deleteSession(sessionId) {
|
|
try {
|
|
const response = await axios.delete(`/api/video-learning/sessions/${sessionId}`);
|
|
if (this.currentSessionId === sessionId) {
|
|
this.currentSessionId = null;
|
|
this.currentSessionStatus = null;
|
|
this.stopPolling();
|
|
}
|
|
return response.data;
|
|
} catch (error) {
|
|
console.error('Error deleting session:', error);
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
// Start polling for session updates
|
|
startPolling(intervalMs = 1000) {
|
|
if (this.isPolling) return;
|
|
|
|
this.isPolling = true;
|
|
this.pollInterval = setInterval(async () => {
|
|
if (this.currentSessionId) {
|
|
try {
|
|
await this.getSessionStatus(this.currentSessionId);
|
|
await this.getSessionVideos(this.currentSessionId);
|
|
|
|
// Trigger custom event for UI updates
|
|
window.dispatchEvent(new CustomEvent('videoLearningUpdate', {
|
|
detail: {
|
|
sessionId: this.currentSessionId,
|
|
status: this.currentSessionStatus,
|
|
videos: this.videos,
|
|
}
|
|
}));
|
|
|
|
// Stop polling if session is complete, but do one final update
|
|
if (this.currentSessionStatus && !this.currentSessionStatus.is_active) {
|
|
console.log('[VideoLearning] Session completed, doing final update...');
|
|
// Do one final update to ensure we have the latest data
|
|
await this.getSessionStatus(this.currentSessionId);
|
|
await this.getSessionVideos(this.currentSessionId);
|
|
|
|
window.dispatchEvent(new CustomEvent('videoLearningUpdate', {
|
|
detail: {
|
|
sessionId: this.currentSessionId,
|
|
status: this.currentSessionStatus,
|
|
videos: this.videos,
|
|
}
|
|
}));
|
|
|
|
console.log('[VideoLearning] Final update complete, stopping poll');
|
|
this.stopPolling();
|
|
}
|
|
} catch (error) {
|
|
console.error('Error polling session status:', error);
|
|
// Don't stop polling on error, just log it
|
|
}
|
|
}
|
|
}, intervalMs);
|
|
console.log(`[VideoLearning] Started polling with ${intervalMs}ms interval`);
|
|
},
|
|
|
|
// Stop polling
|
|
stopPolling() {
|
|
if (this.pollInterval) {
|
|
clearInterval(this.pollInterval);
|
|
this.pollInterval = null;
|
|
console.log('[VideoLearning] Stopped polling');
|
|
}
|
|
this.isPolling = false;
|
|
},
|
|
|
|
// Format duration
|
|
formatDuration(seconds) {
|
|
if (seconds < 60) {
|
|
return `${seconds.toFixed(1)}s`;
|
|
}
|
|
const minutes = Math.floor(seconds / 60);
|
|
const remainingSeconds = seconds % 60;
|
|
return `${minutes}m ${remainingSeconds.toFixed(1)}s`;
|
|
},
|
|
|
|
// Format number with K/M suffix
|
|
formatNumber(num) {
|
|
if (num === null || num === undefined) return 'N/A';
|
|
if (num >= 1000000) {
|
|
return `${(num / 1000000).toFixed(1)}M`;
|
|
} else if (num >= 1000) {
|
|
return `${(num / 1000).toFixed(1)}K`;
|
|
}
|
|
return num.toString();
|
|
},
|
|
};
|
|
|
|
// Export for use in other modules
|
|
if (typeof module !== 'undefined' && module.exports) {
|
|
module.exports = VideoLearningModule;
|
|
}
|