Update video provider

This commit is contained in:
hlohaus
2025-06-19 10:04:43 +02:00
parent aa92c2225e
commit 04b59558b4
5 changed files with 45 additions and 42 deletions

View File

@@ -22,28 +22,38 @@ from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
from ..helper import format_media_prompt
from ... import debug
PUBLIC_URL = "https://home.g4f.dev"
SEARCH_URL = f"{PUBLIC_URL}/search/video+"
class RequestConfig:
urls: dict[str, list[str]] = {}
headers: dict = {}
@classmethod
def get_response(cls, prompt: str) -> VideoResponse | None:
async def get_response(cls, prompt: str) -> VideoResponse | None:
if prompt in cls.urls and cls.urls[prompt]:
cls.urls[prompt] = list(set(cls.urls[prompt]))
debug.log(f"Video URL: {len(cls.urls[prompt])}")
return VideoResponse(cls.urls[prompt], prompt, {
"headers": {"authorization": cls.headers.get("authorization")} if cls.headers.get("authorization") else {},
"preview": [url.replace("md.mp4", "thumb.webp") for url in cls.urls[prompt]]
})
async with ClientSession() as session:
found_urls = []
for skip in range(0, 9):
async with session.get(SEARCH_URL + quote_plus(prompt) + f"?skip={skip}", timeout=ClientTimeout(total=10)) as response:
if response.ok:
found_urls.append(str(response.url))
else:
break
if found_urls:
return VideoResponse(found_urls, prompt)
class Video(AsyncGeneratorProvider, ProviderModelMixin):
urls = [
"https://sora.chatgpt.com/explore",
#"https://aistudio.google.com/generate-video"
]
pub_url = "https://home.g4f.dev"
api_url = f"{pub_url}/backend-api/v2/create?provider=Video&cache=true&prompt="
search_url = f"{pub_url}/search/video+"
api_url = f"{PUBLIC_URL}/backend-api/v2/create?provider=Video&cache=true&prompt="
drive_url = "https://www.googleapis.com/drive/v3/"
active_by_default = True
@@ -67,27 +77,12 @@ class Video(AsyncGeneratorProvider, ProviderModelMixin):
**kwargs
) -> AsyncResult:
yield ProviderInfo(**cls.get_dict(), model="sora")
started = time.time()
prompt = format_media_prompt(messages, prompt)
if not prompt:
raise ValueError("Prompt cannot be empty.")
async with ClientSession() as session:
yield Reasoning(label="Lookup")
found_urls = []
for skip in range(0, 9):
async with session.get(cls.search_url + quote_plus(prompt) + f"?skip={skip}", timeout=ClientTimeout(total=10)) as response:
if response.ok:
yield Reasoning(label=f"Found {skip+1}", status="")
found_urls.append(str(response.url))
else:
break
if found_urls:
yield Reasoning(label=f"Finished", status="")
yield VideoResponse(found_urls, prompt)
return
response = RequestConfig.get_response(prompt)
response = await RequestConfig.get_response(prompt)
if response:
yield Reasoning(label="Found cached Video", status="")
yield Reasoning(label=f"Found {len(response.urls)} Video(s)", status="")
yield response
return
try:
@@ -104,16 +99,17 @@ class Video(AsyncGeneratorProvider, ProviderModelMixin):
yield Reasoning(label="Finished", status="")
if response.headers.get("content-type", "text/plain").startswith("text/plain"):
data = (await response.text()).split("\n")
yield VideoResponse([f"{cls.pub_url}{url}" if url.startswith("/") else url for url in data], prompt)
yield VideoResponse([f"{PUBLIC_URL}{url}" if url.startswith("/") else url for url in data], prompt)
return
yield VideoResponse(str(response.url), prompt)
return
raise MissingRequirementsError("Video provider requires a browser to be installed.")
try:
yield ContinueResponse("Timeout waiting for Video URL")
cls.page = await browser.get(random.choice(cls.urls))
except Exception as e:
debug.error(f"Error opening page:", e)
response = RequestConfig.get_response(prompt)
response = await RequestConfig.get_response(prompt)
if response:
yield Reasoning(label="Found", status="")
yield response
@@ -138,11 +134,12 @@ class Video(AsyncGeneratorProvider, ProviderModelMixin):
debug.error(f"Error clicking button:", e)
try:
if aspect_ratio:
button = await page.find("2:3")
button = await page.find(":")
if button:
await button.click()
else:
debug.error("No '2:3' button found.")
debug.error("No 'x:x' button found.")
await asyncio.sleep(1)
button = await page.find(aspect_ratio)
if button:
await button.click()
@@ -200,13 +197,11 @@ class Video(AsyncGeneratorProvider, ProviderModelMixin):
await page.send(nodriver.cdp.network.enable())
page.add_handler(nodriver.cdp.network.RequestWillBeSent, on_request)
for idx in range(600):
yield Reasoning(label=f"Waiting for Video... {idx+1}/600")
if time.time() - started > 30:
yield ContinueResponse("Timeout waiting for Video URL")
yield Reasoning(label="Waiting for Video...", status=f"{idx+1}/600")
await asyncio.sleep(1)
if RequestConfig.urls[prompt]:
await asyncio.sleep(2)
response = RequestConfig.get_response(prompt)
response = await RequestConfig.get_response(prompt)
if response:
yield Reasoning(label="Finished", status="")
yield response

View File

@@ -94,6 +94,12 @@ async def lifespan(app: FastAPI):
for browser in util.get_registered_instances():
if browser.connection:
browser.stop()
lock_file = os.path.join(get_cookies_dir(), ".nodriver_is_open")
if os.path.exists(lock_file):
try:
os.remove(lock_file)
except Exception as e:
debug.error(f"Failed to remove lock file {lock_file}:" ,e)
def create_app():
app = FastAPI(lifespan=lifespan)

View File

@@ -549,8 +549,10 @@ class Images:
async def async_create_variation(
self,
*,
image: ImageType,
image_name: str = None,
prompt: str = "Create a variation of this image",
model: Optional[str] = None,
provider: Optional[ProviderType] = None,
response_format: Optional[str] = None,
@@ -561,7 +563,6 @@ class Images:
provider_name = provider_handler.__name__ if hasattr(provider_handler, "__name__") else type(provider_handler).__name__
if proxy is None:
proxy = self.client.proxy
prompt = "create a variation of this image"
resolve_media(kwargs, image, image_name)
error = None
response = None
@@ -618,7 +619,7 @@ class Images:
images = await asyncio.gather(*[get_b64_from_url(image) for image in response.get_list()])
else:
# Save locally for None (default) case
if download_media or response.get("cookies"):
if download_media or response.get("cookies") or response.get("headers"):
images = await copy_media(response.get_list(), response.get("cookies"), response.get("headers"), proxy, response.alt)
images = [Image.model_construct(url=image, revised_prompt=response.alt) for image in images]

View File

@@ -435,14 +435,16 @@ class Backend_Api(Api):
return jsonify({"error": {"message": "Not found"}}), 404
if search not in self.match_files:
self.match_files[search] = {}
found_mime_type = False
for root, _, files in os.walk(media_dir):
for file in files:
mime_type = is_allowed_extension(file)
if mime_type is not None:
mime_type = secure_filename(mime_type)
if safe_search[0] in mime_type:
found_mime_type = True
self.match_files[search][file] = self.match_files[search].get(file, 0) + 1
for tag in safe_search:
for tag in safe_search[1:] if found_mime_type else safe_search:
if tag in file.lower():
self.match_files[search][file] = self.match_files[search].get(file, 0) + 1
break

View File

@@ -188,9 +188,8 @@ async def copy_media(
target_path = f"{target_path}{media_extension}"
except ValueError:
pass
# Build URL with safe encoding
url_filename = quote(os.path.basename(target_path))
return f"/media/{url_filename}" + ('?' + (add_url if isinstance(add_url, str) else '' + 'url=' + quote(image)) if add_url and not image.startswith('data:') else '')
# Build URL relative to media directory
return f"/media/{os.path.basename(target_path)}" + ('?' + (add_url if isinstance(add_url, str) else '' + 'url=' + quote(image)) if add_url and not image.startswith('data:') else '')
except (ClientError, IOError, OSError, ValueError) as e:
debug.error(f"Image copying failed:", e)