mirror of
https://github.com/xtekky/gpt4free.git
synced 2025-12-06 02:30:41 -08:00
Update video provider
This commit is contained in:
parent
aa92c2225e
commit
04b59558b4
5 changed files with 45 additions and 42 deletions
|
|
@ -22,28 +22,38 @@ from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
|
||||||
from ..helper import format_media_prompt
|
from ..helper import format_media_prompt
|
||||||
from ... import debug
|
from ... import debug
|
||||||
|
|
||||||
|
PUBLIC_URL = "https://home.g4f.dev"
|
||||||
|
SEARCH_URL = f"{PUBLIC_URL}/search/video+"
|
||||||
|
|
||||||
class RequestConfig:
|
class RequestConfig:
|
||||||
urls: dict[str, list[str]] = {}
|
urls: dict[str, list[str]] = {}
|
||||||
headers: dict = {}
|
headers: dict = {}
|
||||||
|
|
||||||
@classmethod
|
@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]:
|
if prompt in cls.urls and cls.urls[prompt]:
|
||||||
cls.urls[prompt] = list(set(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, {
|
return VideoResponse(cls.urls[prompt], prompt, {
|
||||||
"headers": {"authorization": cls.headers.get("authorization")} if cls.headers.get("authorization") else {},
|
"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]]
|
"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):
|
class Video(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
urls = [
|
urls = [
|
||||||
"https://sora.chatgpt.com/explore",
|
"https://sora.chatgpt.com/explore",
|
||||||
#"https://aistudio.google.com/generate-video"
|
#"https://aistudio.google.com/generate-video"
|
||||||
]
|
]
|
||||||
pub_url = "https://home.g4f.dev"
|
api_url = f"{PUBLIC_URL}/backend-api/v2/create?provider=Video&cache=true&prompt="
|
||||||
api_url = f"{pub_url}/backend-api/v2/create?provider=Video&cache=true&prompt="
|
|
||||||
search_url = f"{pub_url}/search/video+"
|
|
||||||
drive_url = "https://www.googleapis.com/drive/v3/"
|
drive_url = "https://www.googleapis.com/drive/v3/"
|
||||||
|
|
||||||
active_by_default = True
|
active_by_default = True
|
||||||
|
|
@ -67,27 +77,12 @@ class Video(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
**kwargs
|
**kwargs
|
||||||
) -> AsyncResult:
|
) -> AsyncResult:
|
||||||
yield ProviderInfo(**cls.get_dict(), model="sora")
|
yield ProviderInfo(**cls.get_dict(), model="sora")
|
||||||
started = time.time()
|
|
||||||
prompt = format_media_prompt(messages, prompt)
|
prompt = format_media_prompt(messages, prompt)
|
||||||
if not prompt:
|
if not prompt:
|
||||||
raise ValueError("Prompt cannot be empty.")
|
raise ValueError("Prompt cannot be empty.")
|
||||||
async with ClientSession() as session:
|
response = await RequestConfig.get_response(prompt)
|
||||||
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)
|
|
||||||
if response:
|
if response:
|
||||||
yield Reasoning(label="Found cached Video", status="")
|
yield Reasoning(label=f"Found {len(response.urls)} Video(s)", status="")
|
||||||
yield response
|
yield response
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
|
|
@ -104,16 +99,17 @@ class Video(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
yield Reasoning(label="Finished", status="")
|
yield Reasoning(label="Finished", status="")
|
||||||
if response.headers.get("content-type", "text/plain").startswith("text/plain"):
|
if response.headers.get("content-type", "text/plain").startswith("text/plain"):
|
||||||
data = (await response.text()).split("\n")
|
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
|
return
|
||||||
yield VideoResponse(str(response.url), prompt)
|
yield VideoResponse(str(response.url), prompt)
|
||||||
return
|
return
|
||||||
raise MissingRequirementsError("Video provider requires a browser to be installed.")
|
raise MissingRequirementsError("Video provider requires a browser to be installed.")
|
||||||
try:
|
try:
|
||||||
|
yield ContinueResponse("Timeout waiting for Video URL")
|
||||||
cls.page = await browser.get(random.choice(cls.urls))
|
cls.page = await browser.get(random.choice(cls.urls))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug.error(f"Error opening page:", e)
|
debug.error(f"Error opening page:", e)
|
||||||
response = RequestConfig.get_response(prompt)
|
response = await RequestConfig.get_response(prompt)
|
||||||
if response:
|
if response:
|
||||||
yield Reasoning(label="Found", status="")
|
yield Reasoning(label="Found", status="")
|
||||||
yield response
|
yield response
|
||||||
|
|
@ -138,17 +134,18 @@ class Video(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
debug.error(f"Error clicking button:", e)
|
debug.error(f"Error clicking button:", e)
|
||||||
try:
|
try:
|
||||||
if aspect_ratio:
|
if aspect_ratio:
|
||||||
button = await page.find("2:3")
|
button = await page.find(":")
|
||||||
if button:
|
if button:
|
||||||
await button.click()
|
await button.click()
|
||||||
else:
|
else:
|
||||||
debug.error("No '2:3' button found.")
|
debug.error("No 'x:x' button found.")
|
||||||
button = await page.find(aspect_ratio)
|
await asyncio.sleep(1)
|
||||||
if button:
|
button = await page.find(aspect_ratio)
|
||||||
await button.click()
|
if button:
|
||||||
yield Reasoning(label=f"Clicked '{aspect_ratio}' button")
|
await button.click()
|
||||||
else:
|
yield Reasoning(label=f"Clicked '{aspect_ratio}' button")
|
||||||
debug.error(f"No '{aspect_ratio}' button found.")
|
else:
|
||||||
|
debug.error(f"No '{aspect_ratio}' button found.")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug.error(f"Error clicking button:", e)
|
debug.error(f"Error clicking button:", e)
|
||||||
debug.log(f"Using prompt: {prompt}")
|
debug.log(f"Using prompt: {prompt}")
|
||||||
|
|
@ -200,13 +197,11 @@ class Video(AsyncGeneratorProvider, ProviderModelMixin):
|
||||||
await page.send(nodriver.cdp.network.enable())
|
await page.send(nodriver.cdp.network.enable())
|
||||||
page.add_handler(nodriver.cdp.network.RequestWillBeSent, on_request)
|
page.add_handler(nodriver.cdp.network.RequestWillBeSent, on_request)
|
||||||
for idx in range(600):
|
for idx in range(600):
|
||||||
yield Reasoning(label=f"Waiting for Video... {idx+1}/600")
|
yield Reasoning(label="Waiting for Video...", status=f"{idx+1}/600")
|
||||||
if time.time() - started > 30:
|
|
||||||
yield ContinueResponse("Timeout waiting for Video URL")
|
|
||||||
await asyncio.sleep(1)
|
await asyncio.sleep(1)
|
||||||
if RequestConfig.urls[prompt]:
|
if RequestConfig.urls[prompt]:
|
||||||
await asyncio.sleep(2)
|
await asyncio.sleep(2)
|
||||||
response = RequestConfig.get_response(prompt)
|
response = await RequestConfig.get_response(prompt)
|
||||||
if response:
|
if response:
|
||||||
yield Reasoning(label="Finished", status="")
|
yield Reasoning(label="Finished", status="")
|
||||||
yield response
|
yield response
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,12 @@ async def lifespan(app: FastAPI):
|
||||||
for browser in util.get_registered_instances():
|
for browser in util.get_registered_instances():
|
||||||
if browser.connection:
|
if browser.connection:
|
||||||
browser.stop()
|
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():
|
def create_app():
|
||||||
app = FastAPI(lifespan=lifespan)
|
app = FastAPI(lifespan=lifespan)
|
||||||
|
|
|
||||||
|
|
@ -549,8 +549,10 @@ class Images:
|
||||||
|
|
||||||
async def async_create_variation(
|
async def async_create_variation(
|
||||||
self,
|
self,
|
||||||
|
*,
|
||||||
image: ImageType,
|
image: ImageType,
|
||||||
image_name: str = None,
|
image_name: str = None,
|
||||||
|
prompt: str = "Create a variation of this image",
|
||||||
model: Optional[str] = None,
|
model: Optional[str] = None,
|
||||||
provider: Optional[ProviderType] = None,
|
provider: Optional[ProviderType] = None,
|
||||||
response_format: Optional[str] = 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__
|
provider_name = provider_handler.__name__ if hasattr(provider_handler, "__name__") else type(provider_handler).__name__
|
||||||
if proxy is None:
|
if proxy is None:
|
||||||
proxy = self.client.proxy
|
proxy = self.client.proxy
|
||||||
prompt = "create a variation of this image"
|
|
||||||
resolve_media(kwargs, image, image_name)
|
resolve_media(kwargs, image, image_name)
|
||||||
error = None
|
error = None
|
||||||
response = None
|
response = None
|
||||||
|
|
@ -618,7 +619,7 @@ class Images:
|
||||||
images = await asyncio.gather(*[get_b64_from_url(image) for image in response.get_list()])
|
images = await asyncio.gather(*[get_b64_from_url(image) for image in response.get_list()])
|
||||||
else:
|
else:
|
||||||
# Save locally for None (default) case
|
# 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 = 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]
|
images = [Image.model_construct(url=image, revised_prompt=response.alt) for image in images]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -435,14 +435,16 @@ class Backend_Api(Api):
|
||||||
return jsonify({"error": {"message": "Not found"}}), 404
|
return jsonify({"error": {"message": "Not found"}}), 404
|
||||||
if search not in self.match_files:
|
if search not in self.match_files:
|
||||||
self.match_files[search] = {}
|
self.match_files[search] = {}
|
||||||
|
found_mime_type = False
|
||||||
for root, _, files in os.walk(media_dir):
|
for root, _, files in os.walk(media_dir):
|
||||||
for file in files:
|
for file in files:
|
||||||
mime_type = is_allowed_extension(file)
|
mime_type = is_allowed_extension(file)
|
||||||
if mime_type is not None:
|
if mime_type is not None:
|
||||||
mime_type = secure_filename(mime_type)
|
mime_type = secure_filename(mime_type)
|
||||||
if safe_search[0] in 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
|
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():
|
if tag in file.lower():
|
||||||
self.match_files[search][file] = self.match_files[search].get(file, 0) + 1
|
self.match_files[search][file] = self.match_files[search].get(file, 0) + 1
|
||||||
break
|
break
|
||||||
|
|
|
||||||
|
|
@ -188,9 +188,8 @@ async def copy_media(
|
||||||
target_path = f"{target_path}{media_extension}"
|
target_path = f"{target_path}{media_extension}"
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
# Build URL with safe encoding
|
# Build URL relative to media directory
|
||||||
url_filename = quote(os.path.basename(target_path))
|
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 '')
|
||||||
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 '')
|
|
||||||
|
|
||||||
except (ClientError, IOError, OSError, ValueError) as e:
|
except (ClientError, IOError, OSError, ValueError) as e:
|
||||||
debug.error(f"Image copying failed:", e)
|
debug.error(f"Image copying failed:", e)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue