mirror of
https://github.com/xtekky/gpt4free.git
synced 2025-10-17 22:00:46 +08:00
Improve background page
Improve share js functions Fix photoswipe in UI Support n parameter in PollinationsAI
This commit is contained in:
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|||||||
import json
|
import json
|
||||||
import random
|
import random
|
||||||
import requests
|
import requests
|
||||||
|
import asyncio
|
||||||
from urllib.parse import quote_plus
|
from urllib.parse import quote_plus
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from aiohttp import ClientSession
|
from aiohttp import ClientSession
|
||||||
@@ -148,6 +149,7 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
|
|||||||
private: bool = False,
|
private: bool = False,
|
||||||
enhance: bool = False,
|
enhance: bool = False,
|
||||||
safe: bool = False,
|
safe: bool = False,
|
||||||
|
n: int = 1,
|
||||||
# Text generation parameters
|
# Text generation parameters
|
||||||
media: MediaListType = None,
|
media: MediaListType = None,
|
||||||
temperature: float = None,
|
temperature: float = None,
|
||||||
@@ -187,7 +189,8 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
|
|||||||
nologo=nologo,
|
nologo=nologo,
|
||||||
private=private,
|
private=private,
|
||||||
enhance=enhance,
|
enhance=enhance,
|
||||||
safe=safe
|
safe=safe,
|
||||||
|
n=n
|
||||||
):
|
):
|
||||||
yield chunk
|
yield chunk
|
||||||
else:
|
else:
|
||||||
@@ -223,12 +226,10 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
|
|||||||
nologo: bool,
|
nologo: bool,
|
||||||
private: bool,
|
private: bool,
|
||||||
enhance: bool,
|
enhance: bool,
|
||||||
safe: bool
|
safe: bool,
|
||||||
|
n: int
|
||||||
) -> AsyncResult:
|
) -> AsyncResult:
|
||||||
if not cache and seed is None:
|
|
||||||
seed = random.randint(9999, 99999999)
|
|
||||||
params = use_aspect_ratio({
|
params = use_aspect_ratio({
|
||||||
"seed": seed,
|
|
||||||
"width": width,
|
"width": width,
|
||||||
"height": height,
|
"height": height,
|
||||||
"model": model,
|
"model": model,
|
||||||
@@ -238,12 +239,27 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
|
|||||||
"safe": str(safe).lower()
|
"safe": str(safe).lower()
|
||||||
}, aspect_ratio)
|
}, aspect_ratio)
|
||||||
query = "&".join(f"{k}={quote_plus(str(v))}" for k, v in params.items() if v is not None)
|
query = "&".join(f"{k}={quote_plus(str(v))}" for k, v in params.items() if v is not None)
|
||||||
prompt = quote_plus(prompt)[:8112] # Limit URL length
|
prompt = quote_plus(prompt)[:2048-256-len(query)]
|
||||||
url = f"{cls.image_api_endpoint}prompt/{prompt}?{query}"
|
url = f"{cls.image_api_endpoint}prompt/{prompt}?{query}"
|
||||||
|
def get_image_url(i: int = 0, seed: Optional[int] = None):
|
||||||
|
if i == 0:
|
||||||
|
if not cache and seed is None:
|
||||||
|
seed = random.randint(0, 2**32)
|
||||||
|
else:
|
||||||
|
seed = random.randint(0, 2**32)
|
||||||
|
return f"{url}&seed={seed}" if seed else url
|
||||||
async with ClientSession(headers=DEFAULT_HEADERS, connector=get_connector(proxy=proxy)) as session:
|
async with ClientSession(headers=DEFAULT_HEADERS, connector=get_connector(proxy=proxy)) as session:
|
||||||
async with session.get(url, allow_redirects=False) as response:
|
async def get_image(i: int = 0, seed: Optional[int] = None):
|
||||||
await raise_for_status(response)
|
async with session.get(get_image_url(i, seed), allow_redirects=False) as response:
|
||||||
yield ImageResponse(str(response.url), prompt)
|
try:
|
||||||
|
await raise_for_status(response)
|
||||||
|
except Exception as e:
|
||||||
|
debug.error(f"Error fetching image: {e}")
|
||||||
|
return str(response.url)
|
||||||
|
return str(response.url)
|
||||||
|
yield ImageResponse(await asyncio.gather(*[
|
||||||
|
get_image(i, seed) for i in range(int(n))
|
||||||
|
]), prompt)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def _generate_text(
|
async def _generate_text(
|
||||||
|
@@ -45,6 +45,7 @@ class PollinationsImage(PollinationsAI):
|
|||||||
private: bool = False,
|
private: bool = False,
|
||||||
enhance: bool = False,
|
enhance: bool = False,
|
||||||
safe: bool = False,
|
safe: bool = False,
|
||||||
|
n: int = 4,
|
||||||
**kwargs
|
**kwargs
|
||||||
) -> AsyncResult:
|
) -> AsyncResult:
|
||||||
# Calling model updates before creating a generator
|
# Calling model updates before creating a generator
|
||||||
@@ -61,6 +62,7 @@ class PollinationsImage(PollinationsAI):
|
|||||||
nologo=nologo,
|
nologo=nologo,
|
||||||
private=private,
|
private=private,
|
||||||
enhance=enhance,
|
enhance=enhance,
|
||||||
safe=safe
|
safe=safe,
|
||||||
|
n=n
|
||||||
):
|
):
|
||||||
yield chunk
|
yield chunk
|
||||||
|
@@ -28,7 +28,6 @@
|
|||||||
|
|
||||||
.gradient {
|
.gradient {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: -1;
|
|
||||||
left: 50vw;
|
left: 50vw;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: radial-gradient(circle at center, var(--accent), var(--gradient));
|
background: radial-gradient(circle at center, var(--accent), var(--gradient));
|
||||||
@@ -73,12 +72,6 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
iframe {
|
|
||||||
background: transparent;
|
|
||||||
width: 100%;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hidden {
|
.hidden {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@@ -86,7 +79,6 @@
|
|||||||
#background, #image-feed, #video-feed {
|
#background, #image-feed, #video-feed {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: -1;
|
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
object-position: center;
|
object-position: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -100,29 +92,41 @@
|
|||||||
<video id="video-feed" class="hidden" alt="Video Feed" src="/search/video" autoplay></video>
|
<video id="video-feed" class="hidden" alt="Video Feed" src="/search/video" autoplay></video>
|
||||||
|
|
||||||
<!-- Gradient Background Circle -->
|
<!-- Gradient Background Circle -->
|
||||||
<div class="gradient"></div></div>
|
<div class="gradient"></div>
|
||||||
<script>
|
<script>
|
||||||
(async () => {
|
(async () => {
|
||||||
const url = "https://image.pollinations.ai/feed";
|
const url = "https://image.pollinations.ai/feed";
|
||||||
const imageFeed = document.getElementById("image-feed");
|
const imageFeed = document.getElementById("image-feed");
|
||||||
const videoFeed = document.getElementById("video-feed");
|
const videoFeed = document.getElementById("video-feed");
|
||||||
const gradient = document.querySelector(".gradient");
|
const gradient = document.querySelector(".gradient");
|
||||||
const images = []
|
const images = [];
|
||||||
let es = null;
|
let es = null;
|
||||||
let skipVideo = 1;
|
let skipVideo = 1;
|
||||||
let errorVideo = false;
|
let skipImage = 0;
|
||||||
|
let errorVideo = 0;
|
||||||
|
let errorImage = 0;
|
||||||
videoFeed.onloadeddata = () => {
|
videoFeed.onloadeddata = () => {
|
||||||
videoFeed.classList.remove("hidden");
|
videoFeed.classList.remove("hidden");
|
||||||
gradient.classList.add("hidden");
|
gradient.classList.add("hidden");
|
||||||
};
|
};
|
||||||
videoFeed.onerror = () => {
|
videoFeed.onerror = (e) => {
|
||||||
videoFeed.classList.add("hidden");
|
videoFeed.classList.add("hidden");
|
||||||
errorVideo = true;
|
errorVideo += 1;
|
||||||
|
if (errorVideo > 3) {
|
||||||
|
gradient.classList.remove("hidden");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
videoFeed.src = "/search/video?skip=" + skipVideo;
|
||||||
|
skipVideo++;
|
||||||
};
|
};
|
||||||
videoFeed.onended = () => {
|
videoFeed.onended = () => {
|
||||||
videoFeed.src = "/search/video?skip=" + skipVideo;
|
videoFeed.src = "/search/video?skip=" + skipVideo;
|
||||||
skipVideo++;
|
skipVideo++;
|
||||||
};
|
};
|
||||||
|
videoFeed.onclick = () => {
|
||||||
|
videoFeed.src = "/search/video?skip=" + skipVideo;
|
||||||
|
skipVideo++;
|
||||||
|
};
|
||||||
function initES() {
|
function initES() {
|
||||||
if (es == null || es.readyState == EventSource.CLOSED) {
|
if (es == null || es.readyState == EventSource.CLOSED) {
|
||||||
const eventSource = new EventSource(url);
|
const eventSource = new EventSource(url);
|
||||||
@@ -152,9 +156,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
initES();
|
let refreshOnHide = true;
|
||||||
|
document.addEventListener("visibilitychange", () => {
|
||||||
|
if (document.hidden) {
|
||||||
|
refreshOnHide = false;
|
||||||
|
} else {
|
||||||
|
refreshOnHide = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
if (!errorVideo) {
|
if (errorVideo < 3 || !refreshOnHide) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (errorImage < 3) {
|
||||||
|
imageFeed.src = "/search/image+g4f?skip=" + skipImage;
|
||||||
|
skipImage++;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (images.length > 0) {
|
if (images.length > 0) {
|
||||||
@@ -168,6 +184,14 @@
|
|||||||
imageFeed.onerror = () => {
|
imageFeed.onerror = () => {
|
||||||
imageFeed.classList.add("hidden");
|
imageFeed.classList.add("hidden");
|
||||||
gradient.classList.remove("hidden");
|
gradient.classList.remove("hidden");
|
||||||
|
errorImage++;
|
||||||
|
};
|
||||||
|
imageFeed.onload = () => {
|
||||||
|
imageFeed.classList.remove("hidden");
|
||||||
|
gradient.classList.add("hidden");
|
||||||
|
};
|
||||||
|
imageFeed.onclick = () => {
|
||||||
|
imageFeed.src = "/search/image?random=" + Math.random();
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
@@ -63,7 +63,7 @@
|
|||||||
<script src="/static/js/highlight.min.js" async></script>
|
<script src="/static/js/highlight.min.js" async></script>
|
||||||
<script>
|
<script>
|
||||||
window.conversation_id = "{{conversation_id}}";
|
window.conversation_id = "{{conversation_id}}";
|
||||||
window.chat_id = "{{chat_id}}";
|
window.share_id = "{{share_id}}";
|
||||||
window.share_url = "{{share_url}}";
|
window.share_url = "{{share_url}}";
|
||||||
window.start_id = "{{conversation_id}}";
|
window.start_id = "{{conversation_id}}";
|
||||||
</script>
|
</script>
|
||||||
|
@@ -74,15 +74,15 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById('generateQRCode').addEventListener('click', async () => {
|
document.getElementById('generateQRCode').addEventListener('click', async () => {
|
||||||
const chat_id = generate_uuid();
|
const share_id = generate_uuid();
|
||||||
|
|
||||||
const url = `${share_url}/backend-api/v2/chat/${encodeURI(chat_id)}`;
|
const url = `${share_url}/backend-api/v2/chat/${encodeURI(share_id)}`;
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {'content-type': 'application/json'},
|
headers: {'content-type': 'application/json'},
|
||||||
body: localStorage.getItem(`conversation:${conversation_id}`)
|
body: localStorage.getItem(`conversation:${conversation_id}`)
|
||||||
});
|
});
|
||||||
const share = `${share_url}/chat/${encodeURI(chat_id)}/${encodeURI(conversation_id)}`;
|
const share = `${share_url}/chat/${encodeURI(share_id)}/${encodeURI(conversation_id)}`;
|
||||||
const qrcodeStatus = document.getElementById('qrcode-status');
|
const qrcodeStatus = document.getElementById('qrcode-status');
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
qrcodeStatus.innerText = 'Error generating QR code: ' + response.statusText;
|
qrcodeStatus.innerText = 'Error generating QR code: ' + response.statusText;
|
||||||
@@ -115,8 +115,8 @@
|
|||||||
|
|
||||||
const constraints = {
|
const constraints = {
|
||||||
video: {
|
video: {
|
||||||
width: { ideal: 1280 },
|
width: { ideal: 800 },
|
||||||
height: { ideal: 1280 },
|
height: { ideal: 800 },
|
||||||
facingMode: facingMode
|
facingMode: facingMode
|
||||||
},
|
},
|
||||||
audio: false
|
audio: false
|
||||||
|
@@ -298,8 +298,8 @@ const register_message_buttons = async () => {
|
|||||||
if (message_el) {
|
if (message_el) {
|
||||||
if ("index" in message_el.dataset) {
|
if ("index" in message_el.dataset) {
|
||||||
await remove_message(window.conversation_id, message_el.dataset.index);
|
await remove_message(window.conversation_id, message_el.dataset.index);
|
||||||
|
chatBody.removeChild(message_el);
|
||||||
}
|
}
|
||||||
message_el.remove();
|
|
||||||
}
|
}
|
||||||
reloadConversation = true;
|
reloadConversation = true;
|
||||||
await safe_load_conversation(window.conversation_id, false);
|
await safe_load_conversation(window.conversation_id, false);
|
||||||
@@ -842,7 +842,7 @@ async function add_message_chunk(message, message_id, provider, scroll, finish_m
|
|||||||
for (const [key, value] of Object.entries(message.conversation)) {
|
for (const [key, value] of Object.entries(message.conversation)) {
|
||||||
conversation.data[key] = value;
|
conversation.data[key] = value;
|
||||||
}
|
}
|
||||||
await save_conversation(conversation_id, conversation);
|
await save_conversation(conversation_id, get_conversation_data(conversation));
|
||||||
} else if (message.type == "auth") {
|
} else if (message.type == "auth") {
|
||||||
error_storage[message_id] = message.message
|
error_storage[message_id] = message.message
|
||||||
content_map.inner.innerHTML += markdown_render(`**An error occured:** ${message.message}`);
|
content_map.inner.innerHTML += markdown_render(`**An error occured:** ${message.message}`);
|
||||||
@@ -1227,7 +1227,6 @@ function sanitize(input, replacement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function set_conversation_title(conversation_id, title) {
|
async function set_conversation_title(conversation_id, title) {
|
||||||
window.chat_id = null;
|
|
||||||
conversation = await get_conversation(conversation_id)
|
conversation = await get_conversation(conversation_id)
|
||||||
conversation.new_title = title;
|
conversation.new_title = title;
|
||||||
const new_id = sanitize(title, " ");
|
const new_id = sanitize(title, " ");
|
||||||
@@ -1319,7 +1318,6 @@ const set_conversation = async (conversation_id) => {
|
|||||||
|
|
||||||
const new_conversation = async () => {
|
const new_conversation = async () => {
|
||||||
history.pushState({}, null, `/chat/`);
|
history.pushState({}, null, `/chat/`);
|
||||||
window.chat_id = null;
|
|
||||||
window.conversation_id = uuid();
|
window.conversation_id = uuid();
|
||||||
document.title = window.title || document.title;
|
document.title = window.title || document.title;
|
||||||
document.querySelector(".chat-header").innerText = "New Conversation - G4F";
|
document.querySelector(".chat-header").innerText = "New Conversation - G4F";
|
||||||
@@ -1391,7 +1389,7 @@ const load_conversation = async (conversation, scroll=true) => {
|
|||||||
document.title = title;
|
document.title = title;
|
||||||
}
|
}
|
||||||
const chatHeader = document.querySelector(".chat-header");
|
const chatHeader = document.querySelector(".chat-header");
|
||||||
if (window.chat_id) {
|
if (window.share_id && conversation.id == window.start_id) {
|
||||||
chatHeader.innerHTML = '<i class="fa-solid fa-qrcode"></i> ' + escapeHtml(title);
|
chatHeader.innerHTML = '<i class="fa-solid fa-qrcode"></i> ' + escapeHtml(title);
|
||||||
} else {
|
} else {
|
||||||
chatHeader.innerText = title;
|
chatHeader.innerText = title;
|
||||||
@@ -1581,16 +1579,16 @@ async function get_conversation(conversation_id) {
|
|||||||
return conversation;
|
return conversation;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function save_conversation(conversation_id, conversation) {
|
function get_conversation_data(conversation) {
|
||||||
conversation.updated = Date.now();
|
conversation.updated = Date.now();
|
||||||
const data = JSON.stringify(conversation)
|
return JSON.stringify(conversation);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function save_conversation(conversation_id, data) {
|
||||||
appStorage.setItem(
|
appStorage.setItem(
|
||||||
`conversation:${conversation_id}`,
|
`conversation:${conversation_id}`,
|
||||||
data
|
data
|
||||||
);
|
);
|
||||||
if (conversation_id != window.start_id) {
|
|
||||||
window.chat_id = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function get_messages(conversation_id) {
|
async function get_messages(conversation_id) {
|
||||||
@@ -1600,13 +1598,13 @@ async function get_messages(conversation_id) {
|
|||||||
|
|
||||||
async function add_conversation(conversation_id) {
|
async function add_conversation(conversation_id) {
|
||||||
if (appStorage.getItem(`conversation:${conversation_id}`) == null) {
|
if (appStorage.getItem(`conversation:${conversation_id}`) == null) {
|
||||||
await save_conversation(conversation_id, {
|
await save_conversation(conversation_id, get_conversation_data({
|
||||||
id: conversation_id,
|
id: conversation_id,
|
||||||
title: "",
|
title: "",
|
||||||
added: Date.now(),
|
added: Date.now(),
|
||||||
system: chatPrompt?.value,
|
system: chatPrompt?.value,
|
||||||
items: [],
|
items: [],
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
add_url_to_history(`/chat/${conversation_id}`);
|
add_url_to_history(`/chat/${conversation_id}`);
|
||||||
@@ -1622,7 +1620,7 @@ async function save_system_message() {
|
|||||||
const conversation = await get_conversation(window.conversation_id);
|
const conversation = await get_conversation(window.conversation_id);
|
||||||
if (conversation) {
|
if (conversation) {
|
||||||
conversation.system = chatPrompt?.value;
|
conversation.system = chatPrompt?.value;
|
||||||
await save_conversation(window.conversation_id, conversation);
|
await save_conversation(window.conversation_id, get_conversation_data(conversation));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1640,9 +1638,10 @@ const remove_message = async (conversation_id, index) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
conversation.items = new_items;
|
conversation.items = new_items;
|
||||||
await save_conversation(conversation_id, conversation);
|
const data = get_conversation_data(conversation);
|
||||||
if (window.chat_id && window.conversation_id == window.start_id) {
|
await save_conversation(conversation_id, data);
|
||||||
const url = `${window.share_url}/backend-api/v2/chat/${window.chat_id}`;
|
if (window.share_id && window.conversation_id == window.start_id) {
|
||||||
|
const url = `${window.share_url}/backend-api/v2/chat/${window.share_id}`;
|
||||||
await fetch(url, {
|
await fetch(url, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {'content-type': 'application/json'},
|
headers: {'content-type': 'application/json'},
|
||||||
@@ -1716,13 +1715,14 @@ const add_message = async (
|
|||||||
});
|
});
|
||||||
conversation.items = new_messages;
|
conversation.items = new_messages;
|
||||||
}
|
}
|
||||||
await save_conversation(conversation_id, conversation);
|
data = get_conversation_data(conversation);
|
||||||
if (window.chat_id && conversation_id == window.start_id) {
|
await save_conversation(conversation_id, data);
|
||||||
const url = `${window.share_url}/backend-api/v2/chat/${window.chat_id}`;
|
if (window.share_id && conversation_id == window.start_id) {
|
||||||
|
const url = `${window.share_url}/backend-api/v2/chat/${window.share_id}`;
|
||||||
fetch(url, {
|
fetch(url, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {'content-type': 'application/json'},
|
headers: {'content-type': 'application/json'},
|
||||||
body: JSON.stringify(conversation),
|
body: data
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return conversation.items.length - 1;
|
return conversation.items.length - 1;
|
||||||
@@ -1758,7 +1758,7 @@ const load_conversations = async () => {
|
|||||||
// appStorage.removeItem(`conversation:${conversation.id}`);
|
// appStorage.removeItem(`conversation:${conversation.id}`);
|
||||||
// return;
|
// return;
|
||||||
// }
|
// }
|
||||||
const shareIcon = (conversation.id == window.start_id && window.chat_id) ? '<i class="fa-solid fa-qrcode"></i>': '';
|
const shareIcon = (conversation.id == window.start_id && window.share_id) ? '<i class="fa-solid fa-qrcode"></i>': '';
|
||||||
html.push(`
|
html.push(`
|
||||||
<div class="convo" id="convo-${conversation.id}">
|
<div class="convo" id="convo-${conversation.id}">
|
||||||
<div class="left" onclick="set_conversation('${conversation.id}')">
|
<div class="left" onclick="set_conversation('${conversation.id}')">
|
||||||
@@ -2071,14 +2071,14 @@ chatPrompt.addEventListener("input", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener('load', async function() {
|
window.addEventListener('load', async function() {
|
||||||
if (!window.chat_id) {
|
if (!window.share_id) {
|
||||||
return await load_conversation(JSON.parse(appStorage.getItem(`conversation:${window.conversation_id}`)));
|
return await load_conversation(JSON.parse(appStorage.getItem(`conversation:${window.conversation_id}`)));
|
||||||
}
|
}
|
||||||
if (!window.conversation_id) {
|
if (!window.conversation_id) {
|
||||||
window.conversation_id = window.chat_id;
|
window.conversation_id = window.share_id;
|
||||||
}
|
}
|
||||||
const response = await fetch(`${window.share_url}/backend-api/v2/chat/${window.chat_id ? window.chat_id : window.conversation_id}`, {
|
const response = await fetch(`${window.share_url}/backend-api/v2/chat/${window.share_id}`, {
|
||||||
headers: {'accept': 'application/json'},
|
headers: {'accept': 'application/json', 'x-conversation-id': window.conversation_id},
|
||||||
});
|
});
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
return await load_conversation(JSON.parse(appStorage.getItem(`conversation:${window.conversation_id}`)));
|
return await load_conversation(JSON.parse(appStorage.getItem(`conversation:${window.conversation_id}`)));
|
||||||
@@ -2087,10 +2087,7 @@ window.addEventListener('load', async function() {
|
|||||||
if (!window.conversation_id || conversation.id == window.conversation_id) {
|
if (!window.conversation_id || conversation.id == window.conversation_id) {
|
||||||
window.conversation_id = conversation.id;
|
window.conversation_id = conversation.id;
|
||||||
await load_conversation(conversation);
|
await load_conversation(conversation);
|
||||||
appStorage.setItem(
|
await save_conversation(window.conversation_id, JSON.stringify(conversation));
|
||||||
`conversation:${conversation.id}`,
|
|
||||||
JSON.stringify(conversation)
|
|
||||||
);
|
|
||||||
await load_conversations();
|
await load_conversations();
|
||||||
let refreshOnHide = true;
|
let refreshOnHide = true;
|
||||||
document.addEventListener("visibilitychange", () => {
|
document.addEventListener("visibilitychange", () => {
|
||||||
@@ -2101,7 +2098,7 @@ window.addEventListener('load', async function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
var refreshIntervalId = setInterval(async () => {
|
var refreshIntervalId = setInterval(async () => {
|
||||||
if (!window.chat_id) {
|
if (!window.share_id) {
|
||||||
clearInterval(refreshIntervalId);
|
clearInterval(refreshIntervalId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -2111,7 +2108,7 @@ window.addEventListener('load', async function() {
|
|||||||
if (window.conversation_id != window.start_id) {
|
if (window.conversation_id != window.start_id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const response = await fetch(`${window.share_url}/backend-api/v2/chat/${window.chat_id}`, {
|
const response = await fetch(`${window.share_url}/backend-api/v2/chat/${window.share_id}`, {
|
||||||
headers: {
|
headers: {
|
||||||
'accept': 'application/json',
|
'accept': 'application/json',
|
||||||
'if-none-match': conversation.updated,
|
'if-none-match': conversation.updated,
|
||||||
|
@@ -4,7 +4,7 @@ import PhotoSwipeAutoHideUI from "https://cdn.jsdelivr.net/gh/arnowelzel/photosw
|
|||||||
import PhotoSwipeSlideshow from "https://cdn.jsdelivr.net/gh/dpet23/photoswipe-slideshow@v2.0.0/photoswipe-slideshow.esm.min.js";
|
import PhotoSwipeSlideshow from "https://cdn.jsdelivr.net/gh/dpet23/photoswipe-slideshow@v2.0.0/photoswipe-slideshow.esm.min.js";
|
||||||
|
|
||||||
const lightbox = new PhotoSwipeLightbox({
|
const lightbox = new PhotoSwipeLightbox({
|
||||||
gallery: '#messages',
|
gallery: '#chatBody',
|
||||||
children: 'a:has(img)',
|
children: 'a:has(img)',
|
||||||
initialZoomLevel: 'fill',
|
initialZoomLevel: 'fill',
|
||||||
secondaryZoomLevel: 1,
|
secondaryZoomLevel: 1,
|
||||||
|
@@ -371,6 +371,8 @@ class Backend_Api(Api):
|
|||||||
match_files = [file for file, count in match_files.items() if count >= request.args.get("min", len(search))]
|
match_files = [file for file, count in match_files.items() if count >= request.args.get("min", len(search))]
|
||||||
if int(request.args.get("skip", 0)) >= len(match_files):
|
if int(request.args.get("skip", 0)) >= len(match_files):
|
||||||
return jsonify({"error": {"message": "Not found"}}), 404
|
return jsonify({"error": {"message": "Not found"}}), 404
|
||||||
|
if (request.args.get("random", False)):
|
||||||
|
return redirect(f"/media/{random.choice(match_files)}"), 302
|
||||||
return redirect(f"/media/{match_files[int(request.args.get("skip", 0))]}"), 302
|
return redirect(f"/media/{match_files[int(request.args.get("skip", 0))]}"), 302
|
||||||
|
|
||||||
@app.route('/backend-api/v2/upload_cookies', methods=['POST'])
|
@app.route('/backend-api/v2/upload_cookies', methods=['POST'])
|
||||||
@@ -386,12 +388,12 @@ class Backend_Api(Api):
|
|||||||
return "File saved", 200
|
return "File saved", 200
|
||||||
return 'Not supported file', 400
|
return 'Not supported file', 400
|
||||||
|
|
||||||
@self.app.route('/backend-api/v2/chat/<chat_id>', methods=['GET'])
|
@self.app.route('/backend-api/v2/chat/<share_id>', methods=['GET'])
|
||||||
def get_chat(chat_id: str) -> str:
|
def get_chat(share_id: str) -> str:
|
||||||
chat_id = secure_filename(chat_id)
|
share_id = secure_filename(share_id)
|
||||||
if self.chat_cache.get(chat_id, 0) == request.headers.get("if-none-match", 0):
|
if self.chat_cache.get(share_id, 0) == request.headers.get("if-none-match", 0):
|
||||||
return jsonify({"error": {"message": "Not modified"}}), 304
|
return jsonify({"error": {"message": "Not modified"}}), 304
|
||||||
bucket_dir = get_bucket_dir(chat_id)
|
bucket_dir = get_bucket_dir(share_id)
|
||||||
file = os.path.join(bucket_dir, "chat.json")
|
file = os.path.join(bucket_dir, "chat.json")
|
||||||
if not os.path.isfile(file):
|
if not os.path.isfile(file):
|
||||||
return jsonify({"error": {"message": "Not found"}}), 404
|
return jsonify({"error": {"message": "Not found"}}), 404
|
||||||
@@ -399,23 +401,23 @@ class Backend_Api(Api):
|
|||||||
chat_data = json.load(f)
|
chat_data = json.load(f)
|
||||||
if chat_data.get("updated", 0) == request.headers.get("if-none-match", 0):
|
if chat_data.get("updated", 0) == request.headers.get("if-none-match", 0):
|
||||||
return jsonify({"error": {"message": "Not modified"}}), 304
|
return jsonify({"error": {"message": "Not modified"}}), 304
|
||||||
self.chat_cache[chat_id] = chat_data.get("updated", 0)
|
self.chat_cache[share_id] = chat_data.get("updated", 0)
|
||||||
return jsonify(chat_data), 200
|
return jsonify(chat_data), 200
|
||||||
|
|
||||||
@self.app.route('/backend-api/v2/chat/<chat_id>', methods=['POST'])
|
@self.app.route('/backend-api/v2/chat/<share_id>', methods=['POST'])
|
||||||
def upload_chat(chat_id: str) -> dict:
|
def upload_chat(share_id: str) -> dict:
|
||||||
chat_data = {**request.json}
|
chat_data = {**request.json}
|
||||||
updated = chat_data.get("updated", 0)
|
updated = chat_data.get("updated", 0)
|
||||||
cache_value = self.chat_cache.get(chat_id, 0)
|
cache_value = self.chat_cache.get(share_id, 0)
|
||||||
if updated == cache_value:
|
if updated == cache_value:
|
||||||
return jsonify({"error": {"message": "invalid date"}}), 400
|
return jsonify({"error": {"message": "invalid date"}}), 400
|
||||||
chat_id = secure_filename(chat_id)
|
share_id = secure_filename(share_id)
|
||||||
bucket_dir = get_bucket_dir(chat_id)
|
bucket_dir = get_bucket_dir(share_id)
|
||||||
os.makedirs(bucket_dir, exist_ok=True)
|
os.makedirs(bucket_dir, exist_ok=True)
|
||||||
with open(os.path.join(bucket_dir, "chat.json"), 'w') as f:
|
with open(os.path.join(bucket_dir, "chat.json"), 'w') as f:
|
||||||
json.dump(chat_data, f)
|
json.dump(chat_data, f)
|
||||||
self.chat_cache[chat_id] = updated
|
self.chat_cache[share_id] = updated
|
||||||
return {"chat_id": chat_id}
|
return {"share_id": share_id}
|
||||||
|
|
||||||
def handle_synthesize(self, provider: str):
|
def handle_synthesize(self, provider: str):
|
||||||
try:
|
try:
|
||||||
|
@@ -19,12 +19,12 @@ class Website:
|
|||||||
'function': self._chat,
|
'function': self._chat,
|
||||||
'methods': ['GET', 'POST']
|
'methods': ['GET', 'POST']
|
||||||
},
|
},
|
||||||
'/chat/<chat_id>/': {
|
'/chat/<share_id>/': {
|
||||||
'function': self._chat_id,
|
'function': self._share_id,
|
||||||
'methods': ['GET', 'POST']
|
'methods': ['GET', 'POST']
|
||||||
},
|
},
|
||||||
'/chat/<chat_id>/<conversation_id>': {
|
'/chat/<share_id>/<conversation_id>': {
|
||||||
'function': self._chat_id,
|
'function': self._share_id,
|
||||||
'methods': ['GET', 'POST']
|
'methods': ['GET', 'POST']
|
||||||
},
|
},
|
||||||
'/chat/menu/': {
|
'/chat/menu/': {
|
||||||
@@ -50,9 +50,9 @@ class Website:
|
|||||||
return render_template('index.html', conversation_id=str(uuid.uuid4()))
|
return render_template('index.html', conversation_id=str(uuid.uuid4()))
|
||||||
return render_template('index.html', conversation_id=conversation_id)
|
return render_template('index.html', conversation_id=conversation_id)
|
||||||
|
|
||||||
def _chat_id(self, chat_id, conversation_id: str = ""):
|
def _share_id(self, share_id, conversation_id: str = ""):
|
||||||
share_url = os.environ.get("G4F_SHARE_URL", "")
|
share_url = os.environ.get("G4F_SHARE_URL", "")
|
||||||
return render_template('index.html', share_url=share_url, chat_id=chat_id, conversation_id=conversation_id)
|
return render_template('index.html', share_url=share_url, share_id=share_id, conversation_id=conversation_id)
|
||||||
|
|
||||||
def _index(self):
|
def _index(self):
|
||||||
return render_template('index.html', conversation_id=str(uuid.uuid4()))
|
return render_template('index.html', conversation_id=str(uuid.uuid4()))
|
||||||
|
@@ -25,8 +25,10 @@ def get_media_extension(media: str) -> str:
|
|||||||
"""Extract media file extension from URL or filename"""
|
"""Extract media file extension from URL or filename"""
|
||||||
match = re.search(r"\.(j?[a-z]{3})(?:\?|$)", media, re.IGNORECASE)
|
match = re.search(r"\.(j?[a-z]{3})(?:\?|$)", media, re.IGNORECASE)
|
||||||
extension = match.group(1).lower() if match else ""
|
extension = match.group(1).lower() if match else ""
|
||||||
|
if not extension:
|
||||||
|
return ""
|
||||||
if extension not in EXTENSIONS_MAP:
|
if extension not in EXTENSIONS_MAP:
|
||||||
raise ValueError(f"Unsupported media extension: {extension}")
|
raise ValueError(f"Unsupported media extension: {extension} in: {media}")
|
||||||
return f".{extension}"
|
return f".{extension}"
|
||||||
|
|
||||||
def ensure_images_dir():
|
def ensure_images_dir():
|
||||||
|
@@ -154,6 +154,7 @@ async def get_nodriver(
|
|||||||
if not os.path.exists(browser_executable_path):
|
if not os.path.exists(browser_executable_path):
|
||||||
browser_executable_path = None
|
browser_executable_path = None
|
||||||
lock_file = Path(get_cookies_dir()) / ".nodriver_is_open"
|
lock_file = Path(get_cookies_dir()) / ".nodriver_is_open"
|
||||||
|
lock_file.parent.mkdir(exist_ok=True)
|
||||||
# Implement a short delay (milliseconds) to prevent race conditions.
|
# Implement a short delay (milliseconds) to prevent race conditions.
|
||||||
await asyncio.sleep(0.1 * random.randint(0, 50))
|
await asyncio.sleep(0.1 * random.randint(0, 50))
|
||||||
if lock_file.exists():
|
if lock_file.exists():
|
||||||
|
Reference in New Issue
Block a user