mobenta commited on
Commit
d3a4b08
·
verified ·
1 Parent(s): 47b60b1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +115 -67
app.py CHANGED
@@ -1,84 +1,132 @@
1
- import requests
2
- import json
 
 
 
 
3
  import gradio as gr
 
 
 
4
  import logging
5
- from bs4 import BeautifulSoup
6
 
7
  # Configure logging for debugging purposes
8
  logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
9
 
10
- # Function to search YouTube without using the API
11
- def youtube_search(query, max_results=10):
12
- # Create the YouTube search URL
13
- search_url = f"https://www.youtube.com/results?search_query={query.replace(' ', '+')}"
14
- headers = {
15
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36"
 
 
16
  }
 
17
 
18
  logging.debug(f"Starting YouTube search for query: {query}")
19
 
20
  try:
21
- response = requests.get(search_url, headers=headers)
22
- response.raise_for_status() # Raise an error for bad status codes
23
-
24
- # Parse the HTML response using BeautifulSoup
25
- soup = BeautifulSoup(response.text, "html.parser")
26
-
27
- # Look for the initial JSON data block within the script tags
28
- scripts = soup.find_all("script")
29
- for script in scripts:
30
- if 'var ytInitialData = ' in script.text:
31
- json_text = script.text.split('var ytInitialData = ')[1].split("};")[0] + "}"
32
- data = json.loads(json_text)
33
-
34
- # Traverse through the JSON to find video entries
35
- video_items = []
36
- contents = data['contents']['twoColumnSearchResultsRenderer']['primaryContents']['sectionListRenderer']['contents']
37
- for content in contents:
38
- video_entries = content.get('itemSectionRenderer', {}).get('contents', [])
39
- for entry in video_entries:
40
- video_renderer = entry.get('videoRenderer')
41
- if video_renderer:
42
- video_id = video_renderer.get('videoId', 'N/A')
43
- video_title = video_renderer.get('title', {}).get('runs', [{}])[0].get('text', 'N/A')
44
- video_url = f"https://www.youtube.com/watch?v={video_id}"
45
- thumbnail_url = video_renderer.get('thumbnail', {}).get('thumbnails', [{}])[0].get('url', 'N/A')
46
-
47
- video_items.append((thumbnail_url, video_title, video_url))
48
-
49
- if len(video_items) >= max_results:
50
- break
51
-
52
- if video_items:
53
- return video_items, "" # Return the list of video items and no error message
54
- else:
55
- logging.warning("No video entries found.")
56
- return [], "No video entries found."
57
- logging.warning("JSON data block not found in the page.")
58
- return [], "Unable to find video data."
59
-
60
- except requests.exceptions.RequestException as e:
61
- error_message = f"Request error occurred: {e}"
62
- logging.error(error_message)
63
- return [], error_message
64
- except json.JSONDecodeError as e:
65
- error_message = f"JSON decoding error occurred: {e}"
66
- logging.error(error_message)
67
- return [], error_message
68
  except Exception as e:
69
- error_message = f"An unexpected error occurred: {e}"
70
  logging.error(error_message)
71
  return [], error_message
72
 
73
- # Function to create a gallery for Gradio
74
- def display_videos(query):
75
- gallery_items, error_message = youtube_search(query)
76
- if error_message:
77
- return gr.update(value=[], label="Error: Unable to fetch videos"), error_message
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
 
79
- # Display thumbnails and titles as gallery items
80
- formatted_gallery = [(f"<img src='{item[0]}' width='250'/>", f"<a href='{item[2]}' target='_blank'>{item[1]}</a>") for item in gallery_items]
81
- return formatted_gallery, ""
82
 
83
- # Gradio interface
84
- with gr
 
1
+ import subprocess
2
+ import sys
3
+
4
+ # Ensure compatible versions of httpx and httpcore are installed
5
+ subprocess.check_call([sys.executable, "-m", "pip", "install", "httpx==0.18.2", "httpcore==0.13.6"])
6
+
7
  import gradio as gr
8
+ import requests
9
+ import re
10
+ import yt_dlp
11
  import logging
 
12
 
13
  # Configure logging for debugging purposes
14
  logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
15
 
16
+ # Function to search YouTube videos using yt-dlp for better reliability
17
+ def youtube_search(query, max_results=50):
18
+ ydl_opts = {
19
+ 'quiet': False, # Set to False to get more detailed output from yt-dlp
20
+ 'extract_flat': 'in_playlist',
21
+ 'logger': logging.getLogger(), # Use the logging module to capture yt-dlp logs
22
+ 'simulate': True,
23
+ 'noplaylist': True, # To avoid playlist entries
24
  }
25
+ search_url = f"ytsearch{max_results}:{query}"
26
 
27
  logging.debug(f"Starting YouTube search for query: {query}")
28
 
29
  try:
30
+ with yt_dlp.YoutubeDL(ydl_opts) as ydl:
31
+ result = ydl.extract_info(search_url, download=False)
32
+ gallery_items = []
33
+
34
+ if 'entries' in result:
35
+ logging.debug(f"Number of entries found: {len(result['entries'])}")
36
+ for entry in result['entries']:
37
+ video_id = entry.get('id')
38
+ thumbnail_url = entry.get('thumbnail') if entry.get('thumbnail') else "https://via.placeholder.com/150"
39
+ video_title = entry.get('title', "Unknown Title")
40
+
41
+ if video_id:
42
+ gallery_items.append((thumbnail_url, video_id, video_title))
43
+ logging.debug(f"Added video: ID={video_id}, Thumbnail={thumbnail_url}, Title={video_title}")
44
+ else:
45
+ logging.debug(f"Missing video ID for entry: {entry}")
46
+ else:
47
+ logging.warning("No entries found in search result.")
48
+ return gallery_items, ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  except Exception as e:
50
+ error_message = f"Error during YouTube yt-dlp request: {e}"
51
  logging.error(error_message)
52
  return [], error_message
53
 
54
+ # Function to display the video using the video URL
55
+ def show_video(video_url):
56
+ # Regular expression to extract the YouTube video ID from the URL
57
+ video_id = None
58
+ patterns = [
59
+ r"youtube\.com/watch\?v=([^&?\/]+)",
60
+ r"youtube\.com/embed/([^&?\/]+)",
61
+ r"youtube\.com/v/([^&?\/]+)",
62
+ r"youtu\.be/([^&?\/]+)"
63
+ ]
64
+
65
+ for pattern in patterns:
66
+ match = re.search(pattern, video_url)
67
+ if match:
68
+ video_id = match.group(1)
69
+ logging.debug(f"Extracted video ID: {video_id}")
70
+ break
71
+
72
+ # If no video ID is found, return an error message
73
+ if not video_id:
74
+ logging.error("Invalid YouTube URL. Please enter a valid YouTube video link.")
75
+ return "Invalid YouTube URL. Please enter a valid YouTube video link."
76
+
77
+ # Create the embed URL
78
+ embed_url = f"https://www.youtube.com/embed/{video_id}"
79
+ logging.debug(f"Embed URL generated: {embed_url}")
80
+
81
+ # Return an iframe with the video
82
+ html_code = f'''
83
+ <iframe width="560" height="315" src="{embed_url}"
84
+ frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
85
+ allowfullscreen></iframe>
86
+ '''
87
+ return html_code
88
+
89
+ # Create the Gradio interface
90
+ with gr.Blocks() as demo:
91
+ gr.Markdown("## YouTube Video Search, Selection, and Playback")
92
+
93
+ with gr.Row():
94
+ with gr.Column(scale=3):
95
+ search_query_input = gr.Textbox(label="Search YouTube", placeholder="Enter your search query here")
96
+ search_button = gr.Button("Search")
97
+ search_output = gr.Gallery(label="Search Results", columns=5, height="1500px")
98
+ error_output = gr.Textbox(label="Error Message", interactive=False, visible=False)
99
+
100
+ with gr.Column(scale=2):
101
+ selected_video_link = gr.Textbox(label="Selected Video Link", interactive=False)
102
+ play_video_button = gr.Button("Play Video")
103
+ video_output = gr.HTML(label="Video Player")
104
+
105
+ # Define search button behavior
106
+ def update_search_results(query):
107
+ gallery_items, error_message = youtube_search(query)
108
+ if error_message:
109
+ return [], error_message, gr.update(visible=True)
110
+ # Display videos even if the title or thumbnail is missing by using placeholders
111
+ gallery_items_display = [(item[0], item[1]) for item in gallery_items] # Show thumbnail and video ID only
112
+ return gallery_items_display, "", gr.update(visible=False)
113
+
114
+ # Update the selected video link field when a video is clicked in the gallery
115
+ def on_video_select(evt: gr.SelectData):
116
+ # Extract the video ID from the event value, which is a dictionary containing details of the selected item
117
+ selected_video_id = evt.value["caption"]
118
+ video_url = f"https://www.youtube.com/watch?v={selected_video_id}"
119
+ logging.debug(f"Video selected: {video_url}")
120
+ return video_url
121
+
122
+ # Play the video when the Play Video button is clicked
123
+ def play_video(video_url):
124
+ logging.debug(f"Playing video with URL: {video_url}")
125
+ return show_video(video_url)
126
 
127
+ search_button.click(update_search_results, inputs=search_query_input, outputs=[search_output, error_output, error_output])
128
+ search_output.select(on_video_select, inputs=None, outputs=selected_video_link)
129
+ play_video_button.click(play_video, inputs=selected_video_link, outputs=video_output)
130
 
131
+ # Launch the Gradio interface
132
+ demo.launch()