This commit is contained in:
Gauri Joshi 2025-08-25 21:48:03 +02:00
parent 7f91e5e6df
commit d6f59d2d94
4 changed files with 292 additions and 31 deletions

View File

@ -41,6 +41,83 @@ class DashboardAPI:
for video in fallback_videos:
video['like_probability'] = 0.5 # Default probability
return fallback_videos
def get_liked_videos(self):
"""Get videos that user liked, ordered by AI match confidence"""
import sqlite3
try:
conn = sqlite3.connect(self.db_path)
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
# Get liked videos with features
query = """
SELECT v.*, vf.*, p.liked
FROM videos v
JOIN video_features vf ON v.id = vf.video_id
JOIN preferences p ON v.id = p.video_id
WHERE p.liked = 1
ORDER BY v.view_count DESC
"""
cursor.execute(query)
results = cursor.fetchall()
conn.close()
liked_videos = []
for row in results:
video = {
'id': row['id'],
'title': row['title'],
'channel_name': row['channel_name'],
'view_count': row['view_count'],
'url': f"https://www.youtube.com/watch?v={row['id']}"
}
liked_videos.append(video)
# If model is trained, predict confidence for liked videos
if self.model_trained and self.model and liked_videos:
# Create pandas DataFrame for prediction
import pandas as pd
df_data = []
for row in results:
row_data = {
'id': row['id'],
'title': row['title'],
'channel_name': row['channel_name'],
'view_count': row['view_count'],
'title_length': row['title_length'],
'description_length': row['description_length'],
'view_like_ratio': row['view_like_ratio'],
'engagement_score': row['engagement_score'],
'title_sentiment': row['title_sentiment'],
'has_tutorial_keywords': row['has_tutorial_keywords'],
'has_beginner_keywords': row['has_beginner_keywords'],
'has_ai_keywords': row['has_ai_keywords'],
'has_challenge_keywords': row['has_challenge_keywords'],
'has_time_constraint': row['has_time_constraint']
}
df_data.append(row_data)
video_features_df = pd.DataFrame(df_data)
# Get predictions for confidence scores
predictions = predict_video_preferences_with_model(self.model, video_features_df)
# Sort by confidence and return
return sorted(predictions, key=lambda x: x.get('like_probability', 0), reverse=True)
# If no model, return with default confidence
for video in liked_videos:
video['like_probability'] = 0.8 # High default for liked videos
return liked_videos
except Exception as e:
print(f"Error getting liked videos: {e}")
return []
dashboard_api = DashboardAPI()
@ -124,6 +201,36 @@ def rate_video():
'error': str(e)
}), 500
@app.route('/api/liked')
def get_liked_videos():
try:
liked_videos = dashboard_api.get_liked_videos()
formatted_videos = []
for video in liked_videos:
formatted_videos.append({
'id': video['id'],
'title': video['title'],
'channel_name': video['channel_name'],
'view_count': video['view_count'],
'url': video['url'],
'thumbnail': f"https://img.youtube.com/vi/{video['id']}/hqdefault.jpg",
'confidence': round(video.get('like_probability', 0.8) * 100),
'views_formatted': format_view_count(video['view_count'])
})
return jsonify({
'success': True,
'videos': formatted_videos,
'total_liked': len(formatted_videos)
})
except Exception as e:
return jsonify({
'success': False,
'error': str(e)
}), 500
def format_view_count(count):
if count >= 1000000:
return f"{count/1000000:.1f}M views"

View File

@ -16,16 +16,15 @@ def search_more_videos():
if not api_key:
print("Error: YOUTUBE_API_KEY not found in environment variables")
return
db_path = "video_inspiration.db"
setup_database_tables(db_path)
print("🔍 Searching for more coding videos...")
# Use different/additional search queries to find new videos
additional_queries = [
"react tutorial 2024 millions views",
"python projects for beginners viral",
"python projects for beginners viral",
"full stack web development course",
"machine learning crash course",
"javascript frameworks comparison",
@ -35,24 +34,24 @@ def search_more_videos():
"database design tutorial",
"API development tutorial"
]
all_videos = []
for query in additional_queries:
print(f" Searching: {query}")
video_ids = search_youtube_videos_by_query(api_key, query, 10)
videos = get_video_details_from_youtube(api_key, video_ids)
all_videos.extend(videos)
unique_videos = remove_duplicate_videos(all_videos)
if unique_videos:
save_videos_to_database(unique_videos, db_path)
for video in unique_videos:
features = extract_all_features_from_video(video)
save_video_features_to_database(video['id'], features, db_path)
print(f"✅ Found and saved {len(unique_videos)} new videos!")
else:
print("❌ No new videos found.")

View File

@ -30,14 +30,26 @@ def search_youtube_videos_by_query(api_key: str, query: str, max_results: int) -
def get_coding_search_queries() -> List[str]:
return [
"coding tutorial millions views",
"programming challenge viral",
"I built app hours",
"learn programming beginner",
"coding project from scratch",
"AI coding tutorial",
"web development crash course",
"javascript tutorial millions",
"python tutorial viral",
"coding in 24 hours"
# AI focus (shorter terms)
"AI coding tools 2024",
"ChatGPT API project",
"machine learning build",
"AI developer workflow",
# Project-based (concise)
"react project build",
"python automation script",
"full stack app build",
"javascript project code",
# Implementation focus (brief)
"coding patterns advanced",
"developer setup optimization",
"API design examples",
"database optimization tips",
# Recent/specialized (short)
"modern dev practices",
"new coding tools 2024",
"framework comparison"
]

View File

@ -259,6 +259,39 @@
cursor: not-allowed;
}
.nav-buttons {
display: flex;
gap: 8px;
}
.nav-btn {
padding: 8px 16px;
border: 1px solid #333;
border-radius: 20px;
background-color: #222;
color: white;
cursor: pointer;
font-size: 14px;
transition: all 0.2s ease;
}
.nav-btn:hover {
background-color: #333;
}
.nav-btn.active {
background-color: #ff0000;
border-color: #ff0000;
}
.view-container {
display: none;
}
.view-container.active {
display: block;
}
.loading {
text-align: center;
padding: 60px;
@ -320,7 +353,14 @@
<button class="search-button">🔍</button>
</div>
<div style="width: 200px;"></div>
<div class="nav-buttons">
<button class="nav-btn active" id="ratingBtn" onclick="switchView('rating')">
📝 Rate Videos
</button>
<button class="nav-btn" id="likedBtn" onclick="switchView('liked')">
🎯 MyTube
</button>
</div>
</header>
<div class="status-bar" id="statusBar">
@ -331,13 +371,28 @@
</div>
<main class="main-content">
<h1 class="section-title" id="sectionTitle" style="display: none;">
Recommended for you
<span class="ai-badge">AI POWERED</span>
</h1>
<div class="video-grid" id="videoGrid">
<!-- Videos will be loaded here -->
<!-- Rating View -->
<div class="view-container active" id="ratingView">
<h1 class="section-title" id="sectionTitle" style="display: none;">
Recommended for you
<span class="ai-badge">AI POWERED</span>
</h1>
<div class="video-grid" id="videoGrid">
<!-- Videos will be loaded here -->
</div>
</div>
<!-- Liked Videos View -->
<div class="view-container" id="likedView">
<h1 class="section-title" id="likedSectionTitle" style="display: none;">
MyTube - Videos I Know You'll Love
<span class="ai-badge">AI CURATED</span>
</h1>
<div class="video-grid" id="likedVideoGrid">
<!-- Liked videos will be loaded here -->
</div>
</div>
</main>
@ -522,11 +577,99 @@
}, 3000);
}
async function loadLikedVideos() {
try {
const response = await fetch('/api/liked');
const data = await response.json();
if (!data.success) {
throw new Error(data.error || 'Failed to load liked videos');
}
displayLikedVideos(data.videos);
updateLikedStatusBar(data.total_liked);
} catch (error) {
console.error('Error loading liked videos:', error);
document.getElementById('likedVideoGrid').innerHTML =
`<div class="error">Failed to load liked videos: ${error.message}</div>`;
}
}
function displayLikedVideos(videos) {
const likedVideoGrid = document.getElementById('likedVideoGrid');
const likedSectionTitle = document.getElementById('likedSectionTitle');
if (videos.length === 0) {
likedVideoGrid.innerHTML = '<div class="loading">No videos curated yet. Rate some videos and I\'ll learn what you love!</div>';
likedSectionTitle.style.display = 'none';
return;
}
likedSectionTitle.style.display = 'flex';
likedVideoGrid.innerHTML = videos.map(video => `
<div class="video-card">
<div class="video-thumbnail" onclick="openVideo('${video.url}')">
<img src="${video.thumbnail}" alt="${video.title}" onerror="this.src='data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 width=%22320%22 height=%22180%22><rect width=%22100%%22 height=%22100%%22 fill=%22%23333%22/><text x=%2250%%22 y=%2250%%22 text-anchor=%22middle%22 dy=%22.3em%22 fill=%22%23999%22>No Image</text></svg>'">
<div class="confidence-badge confidence-high">
${video.confidence}% match
</div>
</div>
<div class="video-info">
<div class="video-title" onclick="openVideo('${video.url}')" style="cursor: pointer;">${video.title}</div>
<div class="video-meta">
<span>${video.channel_name}</span>
<span></span>
<span>${video.views_formatted}</span>
</div>
</div>
</div>
`).join('');
}
function updateLikedStatusBar(totalLiked) {
// Update status bar when viewing MyTube videos
const statusBar = document.getElementById('statusBar');
statusBar.innerHTML = `
<div class="status-trained">
🎯 MyTube AI Curation • ${totalLiked} videos I know you'll love • Ranked by confidence
</div>
`;
}
let currentView = 'rating';
function switchView(view) {
currentView = view;
// Update navigation buttons
document.getElementById('ratingBtn').classList.toggle('active', view === 'rating');
document.getElementById('likedBtn').classList.toggle('active', view === 'liked');
// Update view containers
document.getElementById('ratingView').classList.toggle('active', view === 'rating');
document.getElementById('likedView').classList.toggle('active', view === 'liked');
// Load appropriate data
if (view === 'rating') {
loadRecommendations();
} else if (view === 'liked') {
loadLikedVideos();
}
}
// Load recommendations when page loads
document.addEventListener('DOMContentLoaded', loadRecommendations);
// Refresh every 5 minutes
setInterval(loadRecommendations, 300000);
// Refresh current view every 5 minutes
setInterval(() => {
if (currentView === 'rating') {
loadRecommendations();
} else if (currentView === 'liked') {
loadLikedVideos();
}
}, 300000);
</script>
</body>
</html>