Spaces:
Sleeping
Sleeping
| # -*- coding: utf-8 -*- | |
| """youtube_video_recommendation.ipynb | |
| Automatically generated by Colab. | |
| Original file is located at | |
| https://colab.research.google.com/drive/1pLpvcg7hC7hp2PmGBc8qe86ktv3eT9gp | |
| """ | |
| import gradio as gr | |
| import requests | |
| import os | |
| API_KEY = os.getenv("API_KEY")# Replace with your own API key | |
| def search_youtube(query): | |
| if not query.strip(): | |
| return "<b>Please enter a search term.</b>" | |
| search_url = "https://www.googleapis.com/youtube/v3/search" | |
| search_params = { | |
| "part": "snippet", | |
| "q": query, | |
| "type": "video", | |
| "maxResults": 25, | |
| "key": API_KEY | |
| } | |
| search_response = requests.get(search_url, params=search_params).json() | |
| video_ids = [item["id"]["videoId"] for item in search_response.get("items", [])] | |
| if not video_ids: | |
| return "<b>β No results found.</b>" | |
| stats_url = "https://www.googleapis.com/youtube/v3/videos" | |
| stats_params = { | |
| "part": "snippet,statistics", | |
| "id": ",".join(video_ids), | |
| "key": API_KEY | |
| } | |
| stats_response = requests.get(stats_url, params=stats_params).json() | |
| if "items" not in stats_response: | |
| return "<b>β No results found or API limit reached.</b>" | |
| results = [] | |
| for item in stats_response["items"]: | |
| snippet = item["snippet"] | |
| title = snippet["title"] | |
| description = snippet.get("description", "") | |
| channel = snippet.get("channelTitle", "") | |
| tags = snippet.get("tags", []) | |
| video_id = item["id"] | |
| url = f"https://www.youtube.com/watch?v={video_id}" | |
| # Skip YouTube Shorts | |
| if "shorts" in title.lower() or "/shorts/" in url.lower(): | |
| continue | |
| combined_text = " ".join([title, description, channel] + tags).lower() | |
| stats = item.get("statistics", {}) | |
| thumbnail = snippet["thumbnails"]["medium"]["url"] | |
| views = int(stats.get("viewCount", 0)) | |
| likes = int(stats.get("likeCount", 0)) | |
| results.append({ | |
| "title": title, | |
| "thumbnail": thumbnail, | |
| "views": views, | |
| "likes": likes, | |
| "channel": channel, | |
| "url": url | |
| }) | |
| if not results: | |
| return "<b>β No suitable videos found.</b>" | |
| # Grid layout for results | |
| grid_cards = "" | |
| for vid in results: | |
| grid_cards += f""" | |
| <div style="background: #1e1e1e; color: white; border-radius: 12px; padding: 10px; box-shadow: 0 0 10px #333;"> | |
| <a href="{vid['url']}" target="_blank" style="text-decoration: none; color: inherit;"> | |
| <img src="{vid['thumbnail']}" style="width: 100%; height: auto; object-fit: cover; border-radius: 8px;" /> | |
| <div style="margin-top: 8px;"> | |
| <div style="color: #33ccff; font-size: 16px; font-weight: bold;">{vid['title']}</div> | |
| <div style="font-size: 14px; margin-top: 4px;">ποΈ {vid['channel']}</div> | |
| <div style="font-size: 14px;">ποΈ {vid['views']:,} views β€οΈ {vid['likes']:,} likes</div> | |
| </div> | |
| </a> | |
| </div> | |
| """ | |
| return f""" | |
| <div style='font-family: "Segoe UI", sans-serif; padding: 10px; max-height: 600px; overflow-y: auto;'> | |
| <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px;"> | |
| {grid_cards} | |
| </div> | |
| </div> | |
| """ | |
| # π¨ Gradio Interface (UI/Color/Font preserved) | |
| with gr.Blocks(theme=gr.themes.Base(), title="YouTube Course Finder") as demo: | |
| gr.HTML("<h1 style='text-align: center; background: linear-gradient(90deg, #ff0066, #6600ff); color: white; padding: 15px; border-radius: 10px;'>π₯ YouTube Course Finder</h1>") | |
| gr.Markdown("Search for top YouTube courses by topic. Only regular videos shown, Shorts are excluded.") | |
| with gr.Row(): | |
| query = gr.Textbox(label="Search Topic", placeholder="e.g. Python full course, HTML tutorial", lines=1) | |
| search_btn = gr.Button("π Search") | |
| output = gr.HTML() | |
| search_btn.click(fn=search_youtube, inputs=query, outputs=output) | |
| demo.launch(share=True) |