返回提交历史
Modified
g4f/api/__init__.py
+12
-17
Modified
g4f/gui/client/index.html
+1
-3
Modified
g4f/gui/client/static/css/style.css
+5
-1
Modified
g4f/gui/client/static/img/site.webmanifest
+2
-2
Modified
g4f/gui/server/website.py
+2
-0
Modified
g4f/image/copy_images.py
+26
-28
XFEstudio/gpt4free
Improve copy_images api Update share target support Improve search enabled hightlight
31a4812d
代码差异
6 个文件
+48
-51
@@ -40,7 +40,7 @@ from g4f.client import AsyncClient, ChatCompletion, ImagesResponse, convert_to_p
40
40
from g4f.providers.response import BaseConversation, JsonConversation
41
41
from g4f.client.helper import filter_none
42
42
from g4f.image import is_data_uri_an_image
43
from g4f.image.copy_images import images_dir, copy_images
43
from g4f.image.copy_images import images_dir, copy_images, get_source_url
44
44
from g4f.errors import ProviderNotFoundError, ModelNotFoundError, MissingAuthError, NoValidHarFileError
45
45
from g4f.cookies import read_cookie_files, get_cookies_dir
46
46
from g4f.Provider import ProviderType, ProviderUtils, __providers__
@@ -298,11 +298,9 @@ class Api:
298
298
})
299
299
async def chat_completions(
300
300
config: ChatCompletionsConfig,
301
credentials: Annotated[HTTPAuthorizationCredentials, Depends(Api.security)] = None,
302
provider: str = None
301
credentials: Annotated[HTTPAuthorizationCredentials, Depends(Api.security)] = None
303
302
):
304
303
try:
305
config.provider = provider if config.provider is None else config.provider
306
304
if config.provider is None:
307
305
config.provider = AppConfig.provider
308
306
if credentials is not None:
@@ -582,19 +580,16 @@ class Api:
582
580
except KeyError:
583
581
pass
584
582
if not os.path.isfile(target):
585
source_url = str(request.query_params).split("url=", 1)
586
if len(source_url) > 1:
587
source_url = source_url[1]
588
source_url = source_url.replace("%2F", "/").replace("%3A", ":").replace("%3F", "?").replace("%3D", "=")
589
if source_url.startswith("https://"):
590
try:
591
await copy_images(
592
[source_url],
593
target=target)
594
debug.log(f"Image copied from {source_url}")
595
except Exception as e:
596
debug.log(f"{type(e).__name__}: Download failed: {source_url}\n{e}")
597
return RedirectResponse(url=source_url)
583
source_url = get_source_url(str(request.query_params))
584
if source_url is not None:
585
try:
586
await copy_images(
587
[source_url],
588
target=target)
589
debug.log(f"Image copied from {source_url}")
590
except Exception as e:
591
debug.log(f"{type(e).__name__}: Download failed: {source_url}\n{e}")
592
return RedirectResponse(url=source_url)
598
593
if not os.path.isfile(target):
599
594
return ErrorResponse.from_message("File not found", HTTP_404_NOT_FOUND)
600
595
async def stream():
@@ -288,9 +288,7 @@
288
288
<div class="buttons">
289
289
<div class="field">
290
290
<button id="search">
291
<a href="" onclick="return false;" title="Enable Web Access">
292
<i class="fa-solid fa-search"></i>
293
</a>
291
<i class="fa-solid fa-search"></i>
294
292
</button>
295
293
</div>
296
294
<div class="field">
@@ -684,10 +684,14 @@ input-count .text {
684
684
.file-label:has(> input:valid),
685
685
.file-label.selected,
686
686
.micro-label.recognition,
687
#search.active a i {
687
#search.active i {
688
688
color: var(--accent);
689
689
}
690
690
691
#search.active {
692
border-color: var(--accent);
693
}
694
691
695
label[for="image"] {
692
696
top: 32px;
693
697
}
@@ -21,9 +21,9 @@
21
21
"method": "GET",
22
22
"enctype": "application/x-www-form-urlencoded",
23
23
"params": {
24
"title": "title",
24
"title": "name",
25
25
"text": "prompt",
26
"url": "url"
26
"url": "link"
27
27
}
28
28
}
29
29
}
@@ -31,6 +31,8 @@ class Website:
31
31
}
32
32
33
33
def _chat(self, conversation_id):
34
if conversation_id == "share":
35
return render_template('index.html', chat_id=str(uuid.uuid4()))
34
36
if '-' not in conversation_id:
35
37
return redirect_home()
36
38
return render_template('index.html', chat_id=conversation_id)
@@ -58,36 +58,34 @@ async def copy_images(
58
58
async def copy_image(image: str, target: str = None, headers: dict = headers, ssl: bool = ssl) -> str:
59
59
if target is None or len(images) > 1:
60
60
hash = hashlib.sha256(image.encode()).hexdigest()
61
target = f"{quote_plus('+'.join(alt.split()[:10])[:100], '')}_{hash}" if alt else str(uuid.uuid4())
61
target = f"{quote_plus('+'.join(alt.split()[:10]), '')[:100]}_{hash[:16]}" if alt else str(uuid.uuid4())
62
62
target = f"{int(time.time())}_{target}{get_image_extension(image)}"
63
63
target = os.path.join(images_dir, target)
64
try:
65
if image.startswith("data:"):
66
with open(target, "wb") as f:
67
f.write(extract_data_uri(image))
68
else:
69
try:
70
if BackendApi.working and image.startswith(BackendApi.url) and headers is None:
71
headers = BackendApi.headers
72
ssl = BackendApi.ssl
73
async with session.get(image, ssl=ssl, headers=headers) as response:
74
response.raise_for_status()
75
with open(target, "wb") as f:
76
async for chunk in response.content.iter_chunked(4096):
77
f.write(chunk)
78
except ClientError as e:
79
debug.log(f"copy_images failed: {e.__class__.__name__}: {e}")
80
return get_source_url(image, image)
81
if "." not in target:
82
with open(target, "rb") as f:
83
extension = is_accepted_format(f.read(12)).split("/")[-1]
84
extension = "jpg" if extension == "jpeg" else extension
85
new_target = f"{target}.{extension}"
86
os.rename(target, new_target)
87
target = new_target
88
finally:
89
if "." not in target and os.path.exists(target):
90
os.unlink(target)
64
if image.startswith("data:"):
65
with open(target, "wb") as f:
66
f.write(extract_data_uri(image))
67
else:
68
try:
69
if BackendApi.working and image.startswith(BackendApi.url) and headers is None:
70
headers = BackendApi.headers
71
ssl = BackendApi.ssl
72
async with session.get(image, ssl=ssl, headers=headers) as response:
73
response.raise_for_status()
74
with open(target, "wb") as f:
75
async for chunk in response.content.iter_chunked(4096):
76
f.write(chunk)
77
except ClientError as e:
78
debug.log(f"copy_images failed: {e.__class__.__name__}: {e}")
79
if os.path.exists(target):
80
os.unlink(target)
81
return get_source_url(image, image)
82
if "." not in target:
83
with open(target, "rb") as f:
84
extension = is_accepted_format(f.read(12)).split("/")[-1]
85
extension = "jpg" if extension == "jpeg" else extension
86
new_target = f"{target}.{extension}"
87
os.rename(target, new_target)
88
target = new_target
91
89
return f"/images/{os.path.basename(target)}{'?url=' + image if add_url and not image.startswith('data:') else ''}"
92
90
93
91
return await asyncio.gather(*[copy_image(image, target) for image in images])