d3evil4 commited on
Commit
030cf68
·
verified ·
1 Parent(s): aee3f8a

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +228 -0
app.py ADDED
@@ -0,0 +1,228 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, HTTPException, BackgroundTasks
2
+ from fastapi.responses import JSONResponse
3
+ import asyncio
4
+ import random
5
+ import logging
6
+ from typing import List, Dict, Optional, Any
7
+ from playwright.async_api import async_playwright
8
+ import uvicorn
9
+ from pydantic import BaseModel
10
+
11
+ # Set up logging
12
+ logging.basicConfig(level=logging.INFO,
13
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
14
+ logger = logging.getLogger(__name__)
15
+
16
+ app = FastAPI(title="Kling Video Fetcher",
17
+ description="API to fetch random videos from Kling AI gallery")
18
+
19
+ # Model for the response
20
+ class VideoResponse(BaseModel):
21
+ url: str
22
+ thumbnail: Optional[str] = None
23
+ title: Optional[str] = None
24
+ status: str = "success"
25
+
26
+ # Cache to store recent video URLs
27
+ video_cache: List[Dict[str, Any]] = []
28
+ last_fetch_time = 0
29
+
30
+ async def fetch_random_video_from_kling() -> Dict[str, Any]:
31
+ """
32
+ Fetches a random video from Kling AI gallery and returns its URL.
33
+ Returns a dictionary with video information.
34
+ """
35
+ try:
36
+ async with async_playwright() as p:
37
+ # Configure browser with a realistic user agent and longer timeout
38
+ browser_context_options = {
39
+ 'viewport': {'width': 1280, 'height': 800},
40
+ 'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36',
41
+ }
42
+
43
+ # Launch the browser in headless mode with anti-detection args
44
+ browser = await p.chromium.launch(
45
+ headless=True,
46
+ args=[
47
+ '--no-sandbox',
48
+ '--disable-setuid-sandbox',
49
+ '--disable-dev-shm-usage',
50
+ '--disable-accelerated-2d-canvas',
51
+ '--no-first-run',
52
+ '--no-zygote',
53
+ '--disable-gpu',
54
+ '--disable-web-security',
55
+ ]
56
+ )
57
+ context = await browser.new_context(**browser_context_options)
58
+ page = await context.new_page()
59
+
60
+ try:
61
+ logger.info("Navigating to Kling AI gallery...")
62
+ # Use a longer timeout and wait until networkidle
63
+ await page.goto(
64
+ "https://app.klingai.com/global/community/material",
65
+ timeout=90000,
66
+ wait_until="networkidle"
67
+ )
68
+ logger.info("Successfully loaded gallery page")
69
+ except Exception as e:
70
+ logger.error(f"Error navigating to gallery: {e}")
71
+ raise HTTPException(status_code=504, detail=f"Unable to access Kling AI gallery: {str(e)}")
72
+
73
+ # Wait for dynamic content to load
74
+ logger.info("Waiting for dynamic content to load...")
75
+ await page.wait_for_timeout(5000)
76
+
77
+ # Scroll to load content
78
+ logger.info("Scrolling to load gallery content...")
79
+ scroll_attempts = 6
80
+ for i in range(scroll_attempts):
81
+ logger.info(f"Scroll {i+1}/{scroll_attempts}")
82
+ await page.evaluate("window.scrollBy(0, 800)")
83
+ await page.wait_for_timeout(1500)
84
+
85
+ # Find potential video elements
86
+ logger.info("Searching for video elements...")
87
+ all_potential_elements = []
88
+
89
+ # Direct video elements
90
+ videos = await page.query_selector_all("video")
91
+ if videos:
92
+ logger.info(f"Found {len(videos)} direct video elements")
93
+ all_potential_elements.extend(videos)
94
+
95
+ # Look for video containers
96
+ video_containers = await page.query_selector_all(
97
+ '[class*="video"], [class*="player"], [class*="media"], ' +
98
+ '[class*="thumbnail"], [class*="card"], [class*="item"]'
99
+ )
100
+ if video_containers:
101
+ logger.info(f"Found {len(video_containers)} potential video containers")
102
+ all_potential_elements.extend(video_containers)
103
+
104
+ # Filter to keep only visible elements
105
+ visible_elements = []
106
+ for element in all_potential_elements:
107
+ try:
108
+ is_visible = await element.is_visible()
109
+ if is_visible:
110
+ visible_elements.append(element)
111
+ except Exception:
112
+ continue
113
+
114
+ if visible_elements:
115
+ logger.info(f"Found {len(visible_elements)} visible elements")
116
+ all_potential_elements = visible_elements
117
+
118
+ if not all_potential_elements:
119
+ logger.error("No potential video elements found")
120
+ raise HTTPException(status_code=404, detail="No video elements found on the page")
121
+
122
+ # Try multiple elements until we find a video
123
+ # Shuffling the elements to increase randomness
124
+ random.shuffle(all_potential_elements)
125
+
126
+ # We'll try up to 5 random elements
127
+ max_attempts = min(5, len(all_potential_elements))
128
+
129
+ for attempt in range(max_attempts):
130
+ selected_element = all_potential_elements[attempt]
131
+
132
+ try:
133
+ # Scroll the element into view
134
+ await selected_element.scroll_into_view_if_needed()
135
+ await page.wait_for_timeout(1000)
136
+
137
+ # Click on the selected element
138
+ logger.info(f"Clicking on element (attempt {attempt+1}/{max_attempts})...")
139
+ await selected_element.click()
140
+ await page.wait_for_timeout(3000)
141
+
142
+ # Look for video elements that appeared after clicking
143
+ post_click_videos = await page.query_selector_all("video")
144
+
145
+ if post_click_videos:
146
+ logger.info(f"Found {len(post_click_videos)} video elements after clicking!")
147
+
148
+ # Find a video with a source
149
+ for video in post_click_videos:
150
+ try:
151
+ video_src = await video.evaluate("el => el.src || ''")
152
+ poster_src = await video.evaluate("el => el.poster || ''")
153
+
154
+ if video_src:
155
+ # Try to get title from page
156
+ title = await page.title()
157
+ # Check if we're on a different page now
158
+ current_url = page.url
159
+
160
+ result = {
161
+ "url": video_src,
162
+ "thumbnail": poster_src if poster_src else None,
163
+ "title": title if title != "Kling AI" else None,
164
+ "page_url": current_url
165
+ }
166
+
167
+ await browser.close()
168
+ return result
169
+ except Exception as e:
170
+ logger.error(f"Error extracting video details: {e}")
171
+ continue
172
+
173
+ except Exception as e:
174
+ logger.error(f"Error with element attempt {attempt+1}: {e}")
175
+ continue
176
+
177
+ # If we get here, we couldn't find a video after multiple attempts
178
+ await browser.close()
179
+ raise HTTPException(status_code=404, detail="Could not find any video with a valid source")
180
+
181
+ except Exception as e:
182
+ logger.error(f"Unexpected error: {e}")
183
+ raise HTTPException(status_code=500, detail=f"An error occurred: {str(e)}")
184
+
185
+ @app.get("/api/random-video", response_model=VideoResponse)
186
+ async def get_random_video():
187
+ """
188
+ Endpoint to get a random video from Kling AI gallery.
189
+ Returns the video URL and metadata.
190
+ """
191
+ try:
192
+ # Fetch a random video
193
+ video_info = await fetch_random_video_from_kling()
194
+
195
+ # Return the video information
196
+ return {
197
+ "url": video_info["url"],
198
+ "thumbnail": video_info.get("thumbnail"),
199
+ "title": video_info.get("title"),
200
+ "status": "success"
201
+ }
202
+ except HTTPException as e:
203
+ # Re-raise HTTP exceptions
204
+ raise e
205
+ except Exception as e:
206
+ # Log and convert other exceptions to HTTPException
207
+ logger.error(f"Error in endpoint: {e}")
208
+ raise HTTPException(status_code=500, detail=f"An error occurred: {str(e)}")
209
+
210
+ @app.get("/")
211
+ async def root():
212
+ """Root endpoint with API information"""
213
+ return {
214
+ "name": "Kling Video Fetcher API",
215
+ "version": "1.0",
216
+ "endpoints": {
217
+ "/api/random-video": "Get a random video URL from Kling AI gallery"
218
+ }
219
+ }
220
+
221
+ @app.get("/health")
222
+ async def health_check():
223
+ """Health check endpoint"""
224
+ return {"status": "healthy"}
225
+
226
+ # Run the server
227
+ if __name__ == "__main__":
228
+ uvicorn.run("app:app", host="0.0.0.0", port=8000, reload=True)