Remove client files from main package

This commit is contained in:
hlohaus
2025-04-20 20:44:10 +02:00
parent 2e928c3b94
commit 236d2aa114
32 changed files with 60 additions and 6431 deletions

View File

@@ -231,6 +231,7 @@ class Gemini(AsyncGeneratorProvider, ProviderModelMixin):
await raise_for_status(response)
image_prompt = response_part = None
last_content = ""
youtube_ids = []
async for line in response.content:
try:
try:
@@ -246,6 +247,11 @@ class Gemini(AsyncGeneratorProvider, ProviderModelMixin):
continue
if return_conversation:
yield Conversation(response_part[1][0], response_part[1][1], response_part[4][0][0], model)
def find_youtube_ids(content: str):
pattern = re.compile(r"http://www.youtube.com/watch\?v=([\w-]+)")
for match in pattern.finditer(content):
if match.group(1) not in youtube_ids:
yield match.group(1)
def read_recusive(data):
for item in data:
if isinstance(item, list):
@@ -267,6 +273,7 @@ class Gemini(AsyncGeneratorProvider, ProviderModelMixin):
reasoning = re.sub(r"\nyoutube_tool\n", "\n\n", reasoning)
reasoning = re.sub(r"\nYouTube\n", "\nYouTube ", reasoning)
reasoning = reasoning.replace('\nhttps://www.gstatic.com/images/branding/productlogos/youtube/v9/192px.svg', '<i class="fa-brands fa-youtube"></i>')
youtube_ids = list(find_youtube_ids(reasoning))
content = response_part[4][0][1][0]
if reasoning:
yield Reasoning(reasoning, status="🤔")
@@ -297,11 +304,7 @@ class Gemini(AsyncGeneratorProvider, ProviderModelMixin):
yield ImageResponse(images, image_prompt, {"cookies": cls._cookies})
except (TypeError, IndexError, KeyError):
pass
youtube_ids = []
pattern = re.compile(r"http://www.youtube.com/watch\?v=([\w-]+)")
for match in pattern.finditer(content):
if match.group(1) not in youtube_ids:
youtube_ids.append(match.group(1))
youtube_ids = youtube_ids if youtube_ids else find_youtube_ids(content)
if youtube_ids:
yield YouTube(youtube_ids)

View File

@@ -1,208 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>G4F DEMO</title>
<link rel="apple-touch-icon" sizes="180x180" href="/static/img/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/static/img/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/static/img/favicon-16x16.png">
<link rel="manifest" href="/static/img/site.webmanifest">
<style>
:root {
--colour-1: #000000;
--colour-2: #ccc;
--colour-3: #e4d4ff;
--colour-4: #f0f0f0;
--colour-5: #181818;
--colour-6: #242424;
--accent: #8b3dff;
--gradient: #1a1a1a;
--background: #16101b;
--size: 70vw;
--top: 50%;
--blur: 40px;
--opacity: 0.6;
}
.gradient {
position: absolute;
left: 50vw;
border-radius: 50%;
background: radial-gradient(circle at center, var(--accent), var(--gradient));
width: var(--size);
height: var(--size);
top: var(--top);
transform: translate(-50%, -50%);
filter: blur(var(--blur)) opacity(var(--opacity));
animation: zoom_gradient 6s infinite alternate;
display: none;
max-height: 100%;
transition: max-height 0.25s ease-in;
}
.gradient.hidden {
max-height: 0;
transition: max-height 0.15s ease-out;
}
@media only screen and (min-width: 40em) {
body .gradient{
display: block;
}
}
/* Animation for the gradient circle */
@keyframes zoom_gradient {
0% {
transform: translate(-50%, -50%) scale(1);
}
100% {
transform: translate(-50%, -50%) scale(1.5);
}
}
/* Body and text color */
body {
background: var(--background);
height: 100vh;
margin: 0;
padding: 0;
overflow: hidden;
}
.hidden {
display: none;
}
#background, #image-feed, #video-feed {
height: 100%;
position: absolute;
object-fit: cover;
object-position: center;
width: 100%;
background: black;
}
</style>
<link rel="stylesheet" href="/static/css/all.min.css">
</head>
<body>
<img id="image-feed" class="hidden" alt="Image Feed">
<video id="video-feed" class="hidden" alt="Video Feed" src="/search/video+g4f" autoplay muted></video>
<!-- Gradient Background Circle -->
<div class="gradient"></div>
<script>
(async () => {
const url = "https://image.pollinations.ai/feed";
const imageFeed = document.getElementById("image-feed");
const videoFeed = document.getElementById("video-feed");
const gradient = document.querySelector(".gradient");
const images = [];
let es = null;
let skipVideo = 1;
let skipImage = 0;
let errorVideo = 0;
let errorImage = 0;
let skipRefresh = 0;
videoFeed.onloadeddata = () => {
videoFeed.classList.remove("hidden");
gradient.classList.add("hidden");
};
videoFeed.onerror = (e) => {
videoFeed.classList.add("hidden");
errorVideo += 1;
if (errorVideo > 3) {
gradient.classList.remove("hidden");
return;
}
videoFeed.src = "/search/video+g4f?skip=" + skipVideo;
skipVideo++;
};
videoFeed.onended = () => {
videoFeed.src = "/search/video+g4f?skip=" + skipVideo;
skipVideo++;
};
videoFeed.onclick = () => {
videoFeed.src = "/search/video+g4f?skip=" + skipVideo;
skipVideo++;
};
function initES() {
if (es == null || es.readyState == EventSource.CLOSED) {
const eventSource = new EventSource(url);
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.nsfw || !data.nologo || data.width < 512 || !data.imageURL || data.isChild || data.status != "end_generating") {
return;
}
const lower = data.prompt.toLowerCase();
const tags = ["nsfw", "timeline", "feet", "blood", "soap", "orally", "heel", "latex", "bathroom", "boobs", "charts", "gel", "logo", "infographic", "warts", " bra ", "prostitute", "curvy", "breasts", "written", "bodies", "naked", "classroom", "malone", "dirty", "shoes", "shower", "banner", "fat", "nipples", "couple", "sexual", "sandal", "supplier", "overlord", "succubus", "platinum", "cracy", "crazy", "hemale", "oprah", "lamic", "ropes", "cables", "wires", "dirty", "messy", "cluttered", "chaotic", "disorganized", "disorderly", "untidy", "unorganized", "unorderly", "unsystematic", "disarranged", "disarrayed", "disheveled", "disordered", "jumbled", "muddled", "scattered", "shambolic", "sloppy", "unkept", "unruly", "bottomless", "18 year"];
for (i in tags) {
if (lower.indexOf(tags[i]) != -1) {
console.log("Skipping image with tag: " + tags[i]);
console.debug("Skipping image:", data.imageURL);
return;
}
}
const landscape = window.innerWidth > window.innerHeight;
if (landscape && data.width > data.height) {
images.push(data.imageURL);
} else if (!landscape && data.width < data.height) {
images.push(data.imageURL);
}
};
eventSource.onerror = (event) => {
eventSource.close();
}
}
}
let refreshOnHide = true;
document.addEventListener("visibilitychange", () => {
if (document.hidden) {
refreshOnHide = false;
} else {
refreshOnHide = true;
}
});
setInterval(() => {
if (errorVideo < 3 || !refreshOnHide) {
return;
}
if (skipRefresh > 0) {
skipRefresh -= 1;
return;
}
if (errorImage < 3) {
imageFeed.src = "/search/image+g4f?skip=" + skipImage;
skipImage++;
return;
}
if (images.length > 0) {
imageFeed.classList.remove("hidden");
imageFeed.src = images.shift();
gradient.classList.add("hidden");
} else {
initES();
}
}, 7000);
imageFeed.onerror = () => {
imageFeed.classList.add("hidden");
gradient.classList.remove("hidden");
errorImage++;
};
imageFeed.onload = () => {
imageFeed.classList.remove("hidden");
gradient.classList.add("hidden");
errorImage = 0;
};
imageFeed.onclick = () => {
imageFeed.src = "/search/image?random=" + Math.random();
if (skipRefresh < 4) {
skipRefresh += 1;
}
};
})();
</script>
</body>
</html>

View File

@@ -1,331 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>G4F DEMO</title>
<link rel="apple-touch-icon" sizes="180x180" href="/static/img/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/static/img/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/static/img/favicon-16x16.png">
<link rel="manifest" href="/static/img/site.webmanifest">
<style>
:root {
--colour-1: #000000;
--colour-2: #ccc;
--colour-3: #e4d4ff;
--colour-4: #f0f0f0;
--colour-5: #181818;
--colour-6: #242424;
--accent: #8b3dff;
--gradient: #1a1a1a;
--background: #16101b;
--size: 70vw;
--top: 50%;
--blur: 40px;
--opacity: 0.6;
}
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap");
/* Body and text color */
body {
background: var(--background);
color: var(--colour-3);
font-family: "Inter", sans-serif;
height: 100vh;
margin: 0;
padding: 0;
overflow: hidden;
font-weight: bold;
}
/* Container for the main content */
.container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100%;
text-align: center;
z-index: 1;
transition: transform 0.25s ease-in;
}
.container.slide {
transform: translateX(-100%);
transition: transform 0.15s ease-out;
}
.slide-button {
position: absolute;
top: 20px;
left: 20px;
background: var(--colour-4);
border: none;
border-radius: 4px;
cursor: pointer;
padding: 6px 8px;
}
header {
font-size: 3rem;
text-transform: uppercase;
margin: 20px;
color: var(--colour-4);
}
iframe {
background: transparent;
width: 100%;
border: none;
}
#background {
height: 100%;
position: absolute;
object-fit: cover;
object-position: center;
width: 100%;
background: black;
}
.container * {
z-index: 2;
}
.description, form p a {
font-size: 1.2rem;
margin-bottom: 30px;
color: var(--colour-2);
}
.input-field {
width: 80%;
max-width: 400px;
padding: 12px;
margin: 10px 0;
border: 2px solid var(--colour-6);
background-color: var(--colour-5);
color: var(--colour-3);
border-radius: 8px;
font-size: 1.1rem;
}
.input-field:focus {
outline: none;
border-color: var(--accent);
}
.button {
background-color: var(--accent);
color: var(--colour-3);
border: none;
padding: 15px 30px;
font-size: 1.1rem;
border-radius: 8px;
cursor: pointer;
transition: background-color 0.3s ease;
margin-top: 15px;
width: 100%;
max-width: 400px;
font-weight: bold;
text-decoration: none;
white-space: nowrap;
}
.button:hover {
background-color: #7a2ccd;
}
.button-container {
display: flex;
flex-direction: row;
align-items: center;
gap: 10px;
}
.footer {
margin-top: 30px;
font-size: 0.9rem;
color: var(--colour-2);
}
.hidden {
display: none;
}
</style>
<link rel="stylesheet" href="/static/css/all.min.css">
<script>
(async () => {
const isIframe = window.self !== window.top;
const backendUrl = "{{backend_url}}";
let url = new URL(window.location.href)
if (isIframe && backendUrl) {
window.location.replace(`${backendUrl}${url.search}`);
return;
}
})();
</script>
<script src="https://unpkg.com/es-module-shims@1.7.0/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"@huggingface/hub": "https://cdn.jsdelivr.net/npm/@huggingface/hub@0.21.0/+esm"
}
}
</script>
</head>
<body>
<iframe id="background" src="/background"></iframe>
<button class="slide-button">
<i class="fa-solid fa-arrow-left"></i>
</button>
<!-- Main Content -->
<div class="container">
<header>
G4F DEMO
</header>
<div class="description">
Welcome to the G4F Web UI! <br>
Your AI assistant is ready to assist you.
</div>
<!-- Input and Button -->
<form action="/chat/">
<input type="text" name="token" class="input-field" placeholder="Enter an Access Token..." autocomplete="off">
<div class="button-container">
<a id="new_window" href="" class="button hidden" target="_blank">New Window</a>
<button class="button">Submit</button>
</div>
<p>
<a href="https://huggingface.co/settings/tokens" target="_blank">Get Access Token</a>
</p>
<img src="https://huggingface.co/datasets/huggingface/badges/resolve/main/sign-in-with-huggingface-xl-dark.svg" alt="Sign in with Hugging Face" style="cursor: pointer; display: none;" id="signin">
<button id="signout" style="display: none">Sign out</button>
</form>
<script type="module">
import * as hub from "@huggingface/hub";
import { oauthLoginUrl, oauthHandleRedirectIfPresent } from "@huggingface/hub";
const isIframe = window.self !== window.top;
const button = document.querySelector('form a.button');
if (isIframe) {
button.classList.remove('hidden');
}
const form = document.querySelector("form");
const input = document.querySelector('form input[name="token"]');
async function check_access_token() {
const accessToken = input.value || localStorage.getItem("HuggingFace-api_key");
if (!accessToken) {
return;
}
let user;
try {
user = await hub.whoAmI({accessToken: accessToken});
} catch(e) {
console.log(e);
input.setCustomValidity("Invalid Access Token.");
localStorage.removeItem("HuggingFace-api_key");
if (localStorage.getItem("oauth")) {
localStorage.removeItem("oauth");
window.location.replace("/");
}
return;
}
localStorage.setItem("HuggingFace-api_key", accessToken);
localStorage.setItem("user", user.name);
localStorage.setItem("report_error", "true")
location.href = "/chat/";
}
input.addEventListener("input", () => check_access_token());
input.addEventListener("click", () => check_access_token());
form.addEventListener("submit", async (event) => {
event.preventDefault();
check_access_token();
});
let oauthResult = localStorage.getItem("oauth");
if (oauthResult) {
let user;
try {
oauthResult = JSON.parse(oauthResult);
user = await hub.whoAmI({accessToken: oauthResult.accessToken});
} catch (e) {
console.error(e);
oauthResult = null;
localStorage.removeItem("oauth");
localStorage.removeItem("HuggingFace-api_key");
}
}
oauthResult ||= await oauthHandleRedirectIfPresent();
if (oauthResult) {
localStorage.setItem("oauth", JSON.stringify(oauthResult));
localStorage.setItem("HuggingFace-api_key", oauthResult.accessToken);
localStorage.setItem("user", oauthResult.userInfo.fullname);
document.getElementById("signout").style.removeProperty("display");
document.getElementById("signout").onclick = async function() {
localStorage.removeItem("oauth");
localStorage.removeItem("HuggingFace-api_key");
window.location.replace("/");
}
} else {
localStorage.removeItem("oauth");
document.getElementById("signin").style.removeProperty("display");
document.getElementById("signin").onclick = async function() {
window.location.href = (await oauthLoginUrl({
clientId: '{{client_id}}',
scopes: ['inference-api']
}));
}
}
</script>
<!-- Footer -->
<div class="footer">
<p>&copy; 2025 G4F. All Rights Reserved.</p>
<p>Powered by the G4F framework</p>
</div>
</div>
<script>
(async () => {
const today = new Date().toJSON().slice(0, 10);
const max = 6;
const cache_id = Math.floor(Math.random() * max);
if (cache_id > 3) {
let prompt;
if (cache_id % 2 == 0) {
prompt = `Today is ${new Date().toJSON().slice(0, 10)}.
Create a single-page HTML screensaver reflecting the current season (based on the date).
Avoid using any text.`;
} else {
prompt = `Create a single-page HTML screensaver. Avoid using any text.`;
}
const response = await fetch(`/backend-api/v2/create?prompt=${prompt}&filter_markdown=html&cache=${cache_id}`);
const text = await response.text()
if (text) {
background.src = `data:text/html;charset=utf-8,${encodeURIComponent(text)}`;
}
}
const container = document.querySelector('.container');
const button = document.querySelector('.slide-button');
const slideIcon = button.querySelector('i');
button.onclick = () => {
if (container.classList.contains('slide')) {
container.classList.remove('slide');
slideIcon.classList.remove('fa-arrow-right');
slideIcon.classList.add('fa-arrow-left');
} else {
container.classList.add('slide');
slideIcon.classList.remove('fa-arrow-left');
slideIcon.classList.add('fa-arrow-right');
}
}
})();
</script>
</body>
</html>

View File

@@ -1,244 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>G4F GUI</title>
<link rel="apple-touch-icon" sizes="180x180" href="/static/img/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/static/img/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/static/img/favicon-16x16.png">
<link rel="manifest" href="/static/img/site.webmanifest">
<style>
:root {
--colour-1: #000000;
--colour-2: #ccc;
--colour-3: #e4d4ff;
--colour-4: #f0f0f0;
--colour-5: #181818;
--colour-6: #242424;
--accent: #8b3dff;
--gradient: #1a1a1a;
--background: #16101b;
--size: 70vw;
--top: 50%;
--blur: 40px;
--opacity: 0.6;
}
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap");
/* Body and text color */
body {
background: var(--background);
color: var(--colour-3);
font-family: "Inter", sans-serif;
height: 100vh;
margin: 0;
padding: 0;
overflow: hidden;
font-weight: bold;
}
/* Container for the main content */
.container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100%;
text-align: center;
}
header {
font-size: 3rem;
text-transform: uppercase;
margin: 20px;
color: var(--colour-4);
}
iframe {
background: transparent;
width: 100%;
border: none;
}
#background {
height: 100%;
position: absolute;
top: 0;
}
.container * {
z-index: 2;
}
.stream-widget {
max-height: 0;
transition: max-height 0.15s ease-out;
color: var(--colour-5);
overflow: scroll;
text-align: left;
}
.stream-widget.show {
max-height: 1000px;
height: 1000px;
transition: max-height 0.25s ease-in;
background: rgba(255,255,255,0.7);
border-top: 2px solid rgba(255,255,255,0.5);
padding: 20px;
}
.stream-widget img {
max-width: 320px;
display: block;
}
#stream-container {
width: 100%;
}
.description {
font-size: 1.2rem;
margin-bottom: 30px;
color: var(--colour-2);
}
.input-field {
width: 80%;
max-width: 400px;
padding: 12px;
margin: 10px 0;
border: 2px solid var(--colour-6);
background-color: var(--colour-5);
color: var(--colour-3);
border-radius: 8px;
font-size: 1.1rem;
}
.input-field:focus {
outline: none;
border-color: var(--accent);
}
.button {
background-color: var(--accent);
color: var(--colour-3);
border: none;
padding: 15px 30px;
font-size: 1.1rem;
border-radius: 8px;
cursor: pointer;
transition: background-color 0.3s ease;
margin-top: 15px;
width: 100%;
max-width: 400px;
font-weight: bold;
}
.button:hover {
background-color: #7a2ccd;
}
.footer {
margin-top: 30px;
font-size: 0.9rem;
color: var(--colour-2);
}
</style>
<script src="https://cdn.jsdelivr.net/npm/markdown-it@13.0.1/dist/markdown-it.min.js"></script>
</head>
<body>
<iframe id="background" src="/background"></iframe>
<!-- Main Content -->
<div class="container">
<header>
G4F GUI
</header>
<div class="description">
Welcome to the G4F GUI! <br>
Your AI assistant is ready to assist you.
</div>
<!-- Input and Button -->
<form action="/chat/">
<input type="text" name="prompt" class="input-field" placeholder="Enter your query...">
<button class="button">Open Chat</button>
</form>
<!-- Footer -->
<div class="footer">
<p>&copy; 2025 G4F. All Rights Reserved.</p>
<p>Powered by the G4F framework</p>
</div>
<iframe class="stream-widget" frameborder="0"></iframe>
</div>
<script>
const iframe = document.querySelector('.stream-widget');
const rand_idx = Math.floor(Math.random() * 12)
if (rand_idx < 3) {
search = "xtekky/gpt4free releases";
} else if (rand_idx < 6) {
search = "developer news";
} else {
search = (navigator.language == "de" ? "news in deutsch" : navigator.language == "en" ? "world news" : `news in ${navigator.language}`);
}
const summary_prompt = "Present the news from the search results in a clear and organized markdown format. Include a headline, a brief summary, key points, and one or more relevant images with proper attribution. Ensure the content is concise, well-structured, and visually appealing.";
const url = `/backend-api/v2/create?prompt=${summary_prompt}&stream=1&web_search=${search}`;
iframe.src = url;
const message = "Loading...";
setTimeout(()=>{
iframe.classList.add('show');
const iframeDocument = iframe.contentDocument || iframe.contentWindow?.document;
if (iframeDocument) {
const iframeBody = iframeDocument.querySelector("body");
if (iframeBody) {
iframeBody.innerHTML = message + iframeBody.innerHTML;
}
} else {
iframe.parentElement.removeChild(iframe);
}
}, 1000);
function filterMarkdown(text, allowedTypes = null, defaultValue = null) {
const match = text.match(/```(.+)\n(?<code>[\s\S]+?)(\n```|$)/);
if (match) {
const [, type, code] = match;
if (!allowedTypes || allowedTypes.includes(type)) {
return code;
}
}
return defaultValue;
}
let scroll_to_bottom_callback = () => {
const i = document.querySelector(".stream-widget");
if (!i.contentWindow || !i.contentDocument) {
return;
}
clientHeight = i.contentDocument.body?.scrollHeight;
i.contentWindow.scrollTo(0, clientHeight);
if (clientHeight - i.contentWindow.scrollY < 2 * clientHeight) {
setTimeout(scroll_to_bottom_callback, 1000);
}
};
setTimeout(scroll_to_bottom_callback, 1000);
iframe.onload = () => {
const iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
const iframeContent = iframeDocument.querySelector("pre");
let iframeText = iframeContent.innerHTML;
const markdown = window.markdownit();
const iframeContainer = document.querySelector(".container");
iframe.remove()
if (iframeText.indexOf('"error"') < 0) {
iframeContainer.innerHTML += `<div class="stream-widget show">${markdown.render(filterMarkdown(iframeText, "markdown", iframeText))}</div>`;
}
scroll_to_bottom_callback = () => null;
}
</script>
</body>
</html>

View File

@@ -1,370 +0,0 @@
<!DOCTYPE html>
<html lang="en" data-framework="javascript">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0 maximum-scale=1.0">
<meta name="description" content="A conversational AI system that listens, learns, and challenges">
<meta property="og:title" content="ChatGPT">
<meta property="og:image" content="https://openai.com/content/images/2022/11/ChatGPT.jpg">
<meta property="og:description" content="A conversational AI system that listens, learns, and challenges">
<meta property="og:url" content="https://g4f.ai">
<link rel="stylesheet" href="/static/css/style.css">
<link rel="stylesheet" href="/static/css/all.min.css">
<link rel="apple-touch-icon" sizes="180x180" href="/static/img/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/static/img/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/static/img/favicon-16x16.png">
<link rel="manifest" href="/static/img/site.webmanifest">
<script src="/static/js/highlightjs-copy.min.js"></script>
<script src="/static/js/chat.v1.js" defer></script>
<script src="https://cdn.jsdelivr.net/npm/markdown-it@13.0.1/dist/markdown-it.min.js"></script>
<link rel="stylesheet" href="/static/css/dracula.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe/dist/photoswipe.css">
<script>
MathJax = {
chtml: {
scale: 1,
displayAlign: 'left'
},
tex: {
inlineMath: [['$', '$'], ['\\(', '\\)']],
displayMath: [['$$', '$$'], ['\\[', '\\]']]
},
};
</script>
<script id="MathJax-script" src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" async></script>
<script>
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
</script>
<template>
<script type="module" src="https://cdn.jsdelivr.net/npm/mistral-tokenizer-js" async>
import mistralTokenizer from "mistral-tokenizer-js"
</script>
<script type="module" src="https://cdn.jsdelivr.net/gh/belladoreai/llama-tokenizer-js@master/llama-tokenizer.js" async>
import llamaTokenizer from "llama-tokenizer-js"
</script>
<script src="https://cdn.jsdelivr.net/npm/gpt-tokenizer/dist/cl100k_base.js" async></script>
<script src="https://cdn.jsdelivr.net/npm/gpt-tokenizer/dist/o200k_base.js" async></script>
</template>
<script async>
if (localStorage.getItem("countTokens") != "false") {
const template = document.head.querySelector('template');
document.head.appendChild(template.content);
}
</script>
<script type="module" src="/static/js/photoswipe.js" async></script>
<script>
const user_image = '<img src="/static/img/user.png" alt="your avatar">';
const gpt_image = '<img src="/static/img/gpt.png" alt="your avatar">';
</script>
<script src="/static/js/highlight.min.js" async></script>
<script>
window.conversation_id = "{{conversation_id}}";
window.share_id = "{{share_id}}";
window.share_url = "{{share_url}}";
window.start_id = "{{conversation_id}}";
</script>
<title>G4F Chat</title>
</head>
<body>
<script async>
localStorage.getItem("darkMode") == "false" ? document.body.classList.add("white") : null;
</script>
<div class="gradient"></div>
<div class="sidebar shown">
<div class="sidebar-header">
<div class="sidebar-logo">G4F Chat</div>
<div class="mobile-sidebar-toggle">
<i class="fa-solid fa-bars"></i>
</div>
</div>
<div class="top">
<button class="new_convo" onclick="new_conversation()">
<i class="fa-regular fa-plus"></i>
<span>New Conversation</span>
</button>
<button class="new_convo" onclick="new_conversation(true)">
<i class="fa-solid fa-user-secret"></i>
<span>Private Conversation</span>
</button>
</div>
<div class="bottom_buttons">
<button onclick="open_settings();">
<i class="fa-solid fa-toolbox"></i>
<span>Open Settings</span>
</button>
<div class="info">
<i class="fa-brands fa-discord"></i>
<span class="convo-title">discord ~ <a href="https://discord.gg/qXA4Wf4Fsm" target="_blank">discord.gg/qXA4Wf4Fsm</a>
</span>
</div>
<div class="info">
<i class="fa-brands fa-github"></i>
<span class="convo-title">github ~ <a href="https://github.com/xtekky/gpt4free" target="_blank">@xtekky/gpt4free</a>
</span>
</div>
<div class="info">
<i class="fa-solid fa-star"></i>
<span id="version_text" class="convo-title"></span>
</div>
</div>
</div>
<div class="settings hidden">
<div class="paper">
<div class="settings-top-bar">
<button class="settings-back-button" onclick="open_settings();">
<i class="fa-solid fa-arrow-left"></i>
</button>
<span>Settings</span>
</div>
<div class="field">
<span class="label">Enable Dark Mode</span>
<input type="checkbox" id="darkMode" checked />
<label for="darkMode" class="toogle" title=""></label>
</div>
<div class="field">
<span class="label">Web Access with DuckDuckGo</span>
<input type="checkbox" id="switch" />
<label for="switch" class="toogle" title="Add the pages of the first 5 search results to the query."></label>
</div>
<div class="field">
<span class="label">Disable Conversation History</span>
<input type="checkbox" id="history" />
<label for="history" class="toogle" title="To improve the reaction time or if you have trouble with large conversations."></label>
</div>
<div class="field">
<span class="label">Hide System-prompt</span>
<input type="checkbox" id="hide-systemPrompt" />
<label for="hide-systemPrompt" class="toogle" title="For more space on phones"></label>
</div>
<div class="field">
<span class="label">Download generated media</span>
<input type="checkbox" id="download_media" checked/>
<label for="download_media" class="toogle" title="Download and save generated images, audios and videos"></label>
</div>
<div class="field">
<span class="label">Refine files with spaCy</span>
<input type="checkbox" id="refine"/>
<label for="refine" class="toogle" title=""></label>
</div>
<div class="field">
<span class="label">Track usage</span>
<input type="checkbox" id="track_usage"/>
<label for="track_usage" class="toogle" title=""></label>
</div>
<div class="field">
<span class="label">Report errors</span>
<input type="checkbox" id="report_error" checked/>
<label for="report_error" class="toogle" title=""></label>
</div>
<div class="field">
<span class="label">Count words and tokens</span>
<input type="checkbox" id="countTokens" checked/>
<label for="countTokens" class="toogle" title=""></label>
</div>
<div class="field">
<span class="label">Automatic Orientation (16:9 or 9:16)</span>
<input type="checkbox" id="automaticOrientation" checked/>
<label for="automaticOrientation" class="toogle" title=""></label>
</div>
<div class="field box">
<label for="systemPrompt" class="label">System prompt</label>
<textarea id="systemPrompt" placeholder="You are a helpful assistant." data-example="If you need to generate images, you can use the following format: ![keywords](/generate/filename.jpg). This will enable the use of an image generation tool."></textarea>
</div>
<div class="field box">
<label for="userInput-height" class="label" title="">Input max. height</label>
<input type="number" id="userInput-height" value="200"/>
</div>
<div class="field box">
<label for="recognition-language" class="label" title="">Speech recognition language</label>
<input type="text" id="recognition-language" value="" placeholder="navigator.language"/>
</div>
<div class="field mem0 hidden">
<span class="label">Enable Memory with Mem0</span>
<input type="checkbox" id="mem0"/>
<label for="mem0" class="toogle" title=""></label>
<button onclick="import_memory()">
<i class="fa-solid fa-arrow-up-from-bracket"></i>
<span>Import Messages to Mem0</span>
</button>
</div>
<div class="field box hidden">
<label for="mem0-api_key" class="label" title="">Mem0 API:</label>
<input type="text" id="mem0-api_key" name="mem0[api_key]" placeholder="api_key"/>
</div>
<div class="field box hidden">
<label for="Custom-api_base" class="label" title="">Custom Provider (Base Url):</label>
<input type="text" id="Custom-api_base" name="Custom[api_base]" placeholder="http://localhost:8080/v1"/>
</div>
<div class="field box hidden">
<label for="Custom-api_key" class="label" title="">Custom Provider:</label>
<input type="text" id="Custom-api_key" name="Custom[api_key]" placeholder="api_key"/>
</div>
<div class="field box hidden">
<label for="BingCreateImages-api_key" class="label" title="">Microsoft Designer in Bing:</label>
<input type="text" id="BingCreateImages-api_key" name="BingCreateImages[api_key]" placeholder="&quot;_U&quot; cookie"/>
</div>
</div>
<div class="bottom_buttons">
<button onclick="delete_conversations()">
<i class="fa-solid fa-trash"></i>
<span>Clear Conversations</span>
</button>
<button onclick="save_storage()">
<i class="fa-solid fa-download"></i>
<a href="" onclick="return false;">Export Conversations</a>
</button>
<button onclick="save_storage(true)">
<i class="fa-solid fa-pencil"></i>
<a href="" onclick="return false;">Export Settings</a>
</button>
<button id="showLog">
<i class="fa-solid fa-terminal"></i>
<a href="" onclick="return false;">Show log</a>
</button>
</div>
</div>
<div class="provider_forms hidden">
<div class="bottom_buttons">
<button id="close_provider_forms">
<i class="fa-regular fa-circle-xmark"></i>
<a href="" onclick="return false;">Close</a>
</button>
</div>
</div>
<div class="chat-container">
<div class="chat-top-panel">
<div class="mobile-sidebar-toggle">
<i class="fa-solid fa-bars"></i>
</div>
<div class="convo-title">New Conversation</div>
<button class="new_convo_icon" onclick="new_conversation()">
<i class="fa-regular fa-plus"></i>
</button>
</div>
<textarea id="chatPrompt" class="box" placeholder="System prompt"></textarea>
<button class="slide-header">
<i class="fa-solid fa-angles-up"></i>
</button>
<div class="chat-body" id="chatBody"></div>
<div class="chat-footer">
<div class="chat-toolbar">
<div id="input-count" class="">
<button class="hide-input">
<i class="fa-solid fa-angles-down"></i>
</button>
<input type="checkbox" id="agree" name="agree" value="yes" checked>
<label for="agree" class="text" onclick="this.innerText='';">Scroll to bottom</label>
</div>
<div class="stop_generating stop_generating-hidden">
<button id="cancelButton">
<span>Stop Generating</span>
<i class="fa-solid fa-stop"></i>
</button>
</div>
<div class="regenerate">
<button id="regenerateButton">
<span>Regenerate</span>
<i class="fa-solid fa-rotate"></i>
</button>
</div>
</div>
<div class="media-player">
<i class="fa-regular fa-x"></i>
</div>
<div class="media-select hidden">
<label class="image-select" for="image" title="">
<input type="file" id="image" name="image" accept="image/*" required/>
<i class="fa-regular fa-image"></i>
</label>
<label class="capture-camera" for="camera">
<input type="file" id="camera" name="camera" accept="image/*" capture="camera" required/>
<i class="fa-solid fa-camera"></i>
</label>
<button class="close">
<i class="fa-solid fa-xmark"></i>
</button>
</div>
<div class="user-input">
<div class="input-area">
<textarea id="userInput" class="box" placeholder="Type a message..." cols="30" rows="10"
style="white-space: pre-wrap;resize: none;"></textarea>
<label class="file-label image-label">
<i class="fa-regular fa-image"></i>
</label>
<label class="file-label" for="file">
<input type="file" id="file" name="file" accept="*/*" required multiple/>
<i class="fa-solid fa-paperclip"></i>
</label>
<label class="micro-label" for="micro">
<i class="fa-solid fa-microphone-slash"></i>
</label>
<div class="send-buttons">
<button id="addButton">
<i class="fa-solid fa-square-plus"></i>
Add
</button>
<button id="sendButton">
<i class="fa-regular fa-paper-plane"></i>
<a href="" id="download" class="hidden"></a>
Send
</button>
</div>
</div>
</div>
<div class="chat-buttons">
<div class="field">
<button id="search">
<i class="fa-solid fa-search"></i>
</button>
</div>
<div class="field">
<select name="model" id="model">
<option value="" selected="selected">Model: Default</option>
<option value="gpt-4">gpt-4</option>
<option value="gpt-4o">gpt-4o</option>
<option value="gpt-4o-mini">gpt-4o-mini</option>
<option value="llama-3.1-70b">llama-3.1-70b</option>
<option value="mixtral-8x7b">mixtral-8x7b</option>
<option value="claude-3.5-sonnet">claude-3.5-sonnet</option>
<option value="flux">flux (Image Generation)</option>
<option value="dall-e-3">dall-e-3 (Image Generation)</option>
<option disabled="disabled">----</option>
</select>
<select name="model2" id="model2" class="hidden model"></select>
<input type="text" id="model3" value="" class="hidden model" placeholder="Model:"/>
</div>
<div class="field">
<select name="provider" id="provider">
<option value="">Provider: Auto</option>
<option value="OpenaiChat">OpenAI ChatGPT</option>
<option value="Copilot">Microsoft Copilot</option>
<option value="Gemini">Google Gemini</option>
<option value="DDG">DuckDuckGo AI Chat</option>
<option value="Blackbox">Blackbox AI</option>
<option value="Custom Model">Custom Model</option>
<option disabled="disabled">----</option>
</select>
</div>
<div class="field">
<button id="pin">
<i class="fa-solid fa-thumbtack"></i>
</button>
</div>
<div id="pin_container" class="field"></div>
</div>
</div>
</div>
<div class="log hidden">
<div class="log-top-bar">
<button class="log-back-button" onclick="open_settings();">
<i class="fa-solid fa-arrow-left"></i>
</button>
<span>Logs</span>
</div>
</div>
</body>
</html>

View File

@@ -1,154 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>QR Scanner and QR Code Generator</title>
<script src="https://cdn.jsdelivr.net/npm/qrcodejs/qrcode.min.js"></script>
<style>
body { font-family: Arial, sans-serif; text-align: center; margin: 20px; }
video { width: 400px; height: 400px; border: 1px solid black; display: block; margin: auto; object-fit: cover; max-width: 100%;}
#qrcode { margin-top: 20px; }
#qrcode img, #qrcode canvas { margin: 0 auto; width: 400px; height: 400px; max-width: 100%;}
button { margin: 5px; padding: 10px; }
</style>
</head>
<body>
<h1>QR Scanner & QR Code</h1>
<h2>QR Code Scanner</h2>
<video id="video"></video>
<button id="startCamera">Start Camera</button>
<button id="stopCamera">Stop Camera</button>
<button id="switchCamera">Switch Camera</button>
<button id="toggleFlash">Toggle Flash</button>
<p id="cam-status"></p>
<h2>QR Code</h2>
<div id="qrcode"></div>
<p><a id="qrcode-status" target="_parent"></a></p>
<button id="generateQRCode">Generate QR Code</button>
<script type="module">
const share_url = "{{share_url}}" || window.location.origin;
const conversation_id = "{{conversation_id}}";
if (!conversation_id) {
document.getElementById('generateQRCode')
.setAttribute('disabled', 'disabled');
}
import QrScanner from 'https://cdn.jsdelivr.net/npm/qr-scanner/qr-scanner.min.js';
const videoElem = document.getElementById('video');
const camStatus = document.getElementById('cam-status');
let qrScanner;
document.getElementById('stopCamera').addEventListener('click', () => {
if (currentStream) {
currentStream.getTracks().forEach(track => track.stop());
}
if (qrScanner) {
qrScanner.stop();
}
});
document.getElementById('toggleFlash').addEventListener('click', async () => {
if (qrScanner) {
const hasFlash = await qrScanner.hasFlash();
if (hasFlash) {
qrScanner.toggleFlash();
} else {
alert('Flash not supported on this camera.');
}
}
});
let share_id = null;
document.getElementById('generateQRCode').addEventListener('click', async () => {
if (share_id) {
const delete_url = `${share_url}/backend-api/v2/files/${encodeURI(share_id)}`;
await fetch(delete_url, {
method: 'DELETE'
});
}
share_id = crypto.randomUUID();
const url = `${share_url}/backend-api/v2/chat/${encodeURI(share_id)}`;
const response = await fetch(url, {
method: 'POST',
headers: {'content-type': 'application/json'},
body: localStorage.getItem(`conversation:${conversation_id}`)
});
const share = `${share_url}/chat/${encodeURI(share_id)}/${encodeURI(conversation_id)}`;
const qrcodeStatus = document.getElementById('qrcode-status');
if (response.status !== 200) {
qrcodeStatus.innerText = 'Error generating QR code: ' + response.statusText;
return;
}
qrcodeStatus.innerText = share;
qrcodeStatus.href = share;
document.getElementById("qrcode").innerHTML = '';
const qrcode = new QRCode(
document.getElementById("qrcode"),
share,
{
width: 400,
height: 400,
colorDark: "#000000",
colorLight: "#ffffff",
correctLevel: QRCode.CorrectLevel.H
});
});
const switchButton = document.getElementById('switchCamera');
let currentStream = null;
let facingMode = 'environment';
async function startCamera() {
try {
if (currentStream) {
currentStream.getTracks().forEach(track => track.stop());
}
const constraints = {
video: {
width: { ideal: 800 },
height: { ideal: 800 },
facingMode: facingMode
},
audio: false
};
const stream = await navigator.mediaDevices.getUserMedia(constraints);
currentStream = stream;
video.srcObject = stream;
video.play();
qrScanner = new QrScanner(videoElem, result => {
camStatus.innerText = 'Camera Success: ' + result.data;
console.log('decoded QR code:', result);
if (result.data.startsWith(share_url)) {
window.parent.location = result.data;
}
}, {
highlightScanRegion: true,
highlightCodeOutline: true,
});
await qrScanner.start();
} catch (error) {
console.error('Error accessing the camera:', error);
alert(`Could not access the camera: ${error.message}`);
}
}
switchButton.addEventListener('click', () => {
facingMode = facingMode === 'user' ? 'environment' : 'user';
startCamera();
});
document.getElementById('startCamera').addEventListener('click', () => {
startCamera();
});
</script>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +0,0 @@
/*!
Theme: Dracula
Author: Mike Barkmin (http://github.com/mikebarkmin) based on Dracula Theme (http://github.com/dracula)
License: ~ MIT (or more permissive) [via base16-schemes-source]
Maintainer: @highlightjs/core-team
Version: 2021.09.0
*/pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#e9e9f4;background:#282936}.hljs ::selection,.hljs::selection{background-color:#4d4f68;color:#e9e9f4}.hljs-comment{color:#626483}.hljs-tag{color:#62d6e8}.hljs-operator,.hljs-punctuation,.hljs-subst{color:#e9e9f4}.hljs-operator{opacity:.7}.hljs-bullet,.hljs-deletion,.hljs-name,.hljs-selector-tag,.hljs-template-variable,.hljs-variable{color:#ea51b2}.hljs-attr,.hljs-link,.hljs-literal,.hljs-number,.hljs-symbol,.hljs-variable.constant_{color:#b45bcf}.hljs-class .hljs-title,.hljs-title,.hljs-title.class_{color:#00f769}.hljs-strong{font-weight:700;color:#00f769}.hljs-addition,.hljs-code,.hljs-string,.hljs-title.class_.inherited__{color:#ebff87}.hljs-built_in,.hljs-doctag,.hljs-keyword.hljs-atrule,.hljs-quote,.hljs-regexp{color:#a1efe4}.hljs-attribute,.hljs-function .hljs-title,.hljs-section,.hljs-title.function_,.ruby .hljs-property{color:#62d6e8}.diff .hljs-meta,.hljs-keyword,.hljs-template-tag,.hljs-type{color:#b45bcf}.hljs-emphasis{color:#b45bcf;font-style:italic}.hljs-meta,.hljs-meta .hljs-keyword,.hljs-meta .hljs-string{color:#00f769}.hljs-meta .hljs-keyword,.hljs-meta-keyword{font-weight:700}

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 499 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -1,28 +0,0 @@
{
"name": "",
"short_name": "G4F",
"icons": [
{
"src": "/static/img/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/static/img/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone",
"scope": "/",
"share_target": {
"action": "/chat/share",
"method": "GET",
"enctype": "application/x-www-form-urlencoded",
"params": {
"text": "prompt"
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -1,53 +0,0 @@
class CopyButtonPlugin {
constructor(options = {}) {
self.hook = options.hook;
self.callback = options.callback
}
"after:highlightElement"({
el,
text
}) {
if (el.parentElement.tagName != "PRE") {
return;
}
let button = Object.assign(document.createElement("button"), {
innerHTML: "Copy",
className: "hljs-copy-button"
});
button.dataset.copied = false;
el.parentElement.classList.add("hljs-copy-wrapper");
el.parentElement.appendChild(button);
el.parentElement.style.setProperty("--hljs-theme-background", window.getComputedStyle(el).backgroundColor);
button.onclick = async () => {
let newText = text;
if (hook && typeof hook === "function") {
newText = hook(text, el) || text
}
try {
if (!navigator.clipboard) {
throw new Error("navigator.clipboard: Clipboard API unavailable.");
}
await navigator.clipboard.writeText(newText);
} catch (e) {
console.error(e);
console.error("Clipboard API writeText() failed! Fallback to document.exec(\"copy\")...");
fallback_clipboard(newText);
}
button.innerHTML = "Copied!";
button.dataset.copied = true;
let alert = Object.assign(document.createElement("div"), {
role: "status",
className: "hljs-copy-alert",
innerHTML: "Copied to clipboard"
});
el.parentElement.appendChild(alert);
setTimeout(() => {
button.innerHTML = "Copy";
button.dataset.copied = false;
el.parentElement.removeChild(alert);
alert = null
}, 2e3)
}
if (typeof callback === "function") return callback(newText, el);
}
}

View File

@@ -1,64 +0,0 @@
import PhotoSwipeLightbox from "https://cdn.jsdelivr.net/npm/photoswipe@5.3.8/dist/photoswipe-lightbox.esm.min.js";
import PhotoSwipeVideoPlugin from "https://cdn.jsdelivr.net/gh/dimsemenov/photoswipe-video-plugin@5e32d6589df53df2887900bcd55267d72aee57a6/dist/photoswipe-video-plugin.esm.min.js";
import PhotoSwipeAutoHideUI from "https://cdn.jsdelivr.net/gh/arnowelzel/photoswipe-auto-hide-ui@1.0.1/photoswipe-auto-hide-ui.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({
gallery: '#chatBody',
children: 'a:has(img)',
initialZoomLevel: 'fill',
secondaryZoomLevel: 1,
maxZoomLevel: 2,
allowPanToNext: true,
doubleTapAction: 'close',
pswpModule: () => import('https://cdn.jsdelivr.net/npm/photoswipe'),
});
lightbox.addFilter('itemData', (itemData, index) => {
const img = itemData.element.querySelector('img');
itemData.width = img.naturalWidth || 1024;
itemData.height = img.naturalHeight || 1024;
itemData.src = img.src;
return itemData;
});
lightbox.on('uiRegister', function() {
lightbox.pswp.ui.registerElement({
name: 'custom-caption',
order: 9,
isButton: false,
appendTo: 'root',
html: 'Caption text',
onInit: (el, pswp) => {
lightbox.pswp.on('change', () => {
const currSlideElement = lightbox.pswp.currSlide.data.element;
if (currSlideElement) {
const img = currSlideElement.querySelector('img');
const download = document.createElement("a");
download.setAttribute("href", img.getAttribute('src'));
let extension = img.getAttribute('src').includes(".webp") ? ".webp" : ".jpg";
download.setAttribute("download", `${img.getAttribute('alt')} ${lightbox.pswp.currSlide.index}${extension}`);
download.style.float = "right";
download.innerHTML = '<i class="fa-solid fa-download"></i>';
let span = document.createElement("span");
span.innerText = img.getAttribute('alt');
el.innerHTML = '';
el.appendChild(download);
el.appendChild(span);
}
});
}
});
});
// Add a slideshow to the PhotoSwipe gallery.
const _slideshowPlugin = new PhotoSwipeSlideshow(lightbox, {
defaultDelayMs: 7000,
restartOnSlideChange: true,
progressBarPosition: "top",
autoHideProgressBar: false
});
// Plugin to display video.
const _videoPlugin = new PhotoSwipeVideoPlugin(lightbox, {});
// Hide the PhotoSwipe UI after some time of inactivity.
const _autoHideUI = new PhotoSwipeAutoHideUI(lightbox, {});
lightbox.init();

View File

@@ -1,11 +1,5 @@
import sys, os
from flask import Flask
def create_app() -> Flask:
if getattr(sys, 'frozen', False):
template_folder = os.path.join(sys._MEIPASS, "client")
else:
template_folder = "../client"
app = Flask(__name__, template_folder=template_folder, static_folder=f"{template_folder}/static")
app.config["TEMPLATES_AUTO_RELOAD"] = True # Enable auto reload in debug mode
app = Flask(__name__)
return app

View File

@@ -60,23 +60,6 @@ class Backend_Api(Api):
self.app: Flask = app
self.chat_cache = {}
if app.demo:
@app.route('/', methods=['GET'])
def home():
client_id = os.environ.get("OAUTH_CLIENT_ID", "")
backend_url = os.environ.get("G4F_BACKEND_URL", "")
return render_template('demo.html', backend_url=backend_url, client_id=client_id)
else:
@app.route('/', methods=['GET'])
def home():
return render_template('home.html')
@app.route('/qrcode', methods=['GET'])
@app.route('/qrcode/<conversation_id>', methods=['GET'])
def qrcode(conversation_id: str = ""):
share_url = os.environ.get("G4F_SHARE_URL", "")
return render_template('qrcode.html', conversation_id=conversation_id, share_url=share_url)
@app.route('/backend-api/v2/models', methods=['GET'])
def jsonify_models(**kwargs):
response = get_demo_models() if app.demo else self.get_models(**kwargs)
@@ -260,6 +243,7 @@ class Backend_Api(Api):
cache_id = sha256(cache_id.encode() + json.dumps(parameters, sort_keys=True).encode()).hexdigest()
cache_dir = Path(get_cookies_dir()) / ".scrape_cache" / "create"
cache_file = cache_dir / f"{quote_plus(request.args.get('prompt').strip()[:20])}.{cache_id}.txt"
response = None
if cache_file.exists():
with cache_file.open("r") as f:
response = f.read()
@@ -369,7 +353,7 @@ class Backend_Api(Api):
self.match_files[search][file] = self.match_files[search].get(file, 0) + 1
break
match_files = [file for file, count in self.match_files[search].items() if count >= request.args.get("min", len(safe_search))]
if int(request.args.get("skip", 0)) >= len(match_files):
if int(request.args.get("skip") or 0) >= len(match_files):
return jsonify({"error": {"message": "Not found"}}), 404
if (request.args.get("random", False)):
seed = request.args.get("random")

View File

@@ -1,65 +1,73 @@
from __future__ import annotations
import os
import uuid
from flask import render_template, redirect
import requests
from datetime import datetime
from flask import send_from_directory, redirect
from ...image.copy_images import secure_filename, get_media_dir, ensure_media_dir
from ...errors import VersionNotFoundError
from ... import version
GPT4FREE_URL = "https://gpt4free.github.io"
def redirect_home():
return redirect('/chat')
def render(filename = "chat"):
try:
latest_version = version.utils.latest_version
except VersionNotFoundError:
latest_version = version.utils.current_version
today = datetime.today().strftime('%Y-%m-%d')
cache_file = os.path.join(get_media_dir(), f"{today}.{secure_filename(filename)}.{version.utils.current_version}-{latest_version}.html")
if not os.path.exists(cache_file):
ensure_media_dir()
html = requests.get(f"{GPT4FREE_URL}/{filename}.html").text
html = html.replace("../dist/", f"{GPT4FREE_URL}/dist/")
html = html.replace('"dist/', f"\"{GPT4FREE_URL}/dist/")
with open(cache_file, 'w', encoding='utf-8') as f:
f.write(html)
return send_from_directory(os.path.abspath(get_media_dir()), os.path.basename(cache_file))
class Website:
def __init__(self, app) -> None:
self.app = app
self.routes = {
'/chat/': {
'/': {
'function': self._index,
'methods': ['GET', 'POST']
},
'/chat/': {
'function': self._chat,
'methods': ['GET', 'POST']
},
'/qrcode.html': {
'function': self._qrcode,
'methods': ['GET', 'POST']
},
'/background.html': {
'function': self._background,
'methods': ['GET', 'POST']
},
'/chat/<conversation_id>': {
'function': self._chat,
'methods': ['GET', 'POST']
},
'/chat/<share_id>/': {
'function': self._share_id,
'methods': ['GET', 'POST']
},
'/chat/<share_id>/<conversation_id>': {
'function': self._share_id,
'methods': ['GET', 'POST']
},
'/chat/menu/': {
'/media/': {
'function': redirect_home,
'methods': ['GET', 'POST']
},
'/chat/settings/': {
'function': self._settings,
'methods': ['GET', 'POST']
},
'/images/': {
'function': redirect_home,
'methods': ['GET', 'POST']
},
'/background': {
'function': self._background,
'methods': ['GET']
},
}
def _chat(self, conversation_id):
if conversation_id == "share":
return render_template('index.html', conversation_id=str(uuid.uuid4()))
return render_template('index.html', conversation_id=conversation_id)
def _index(self, filename = "index"):
return render(filename)
def _share_id(self, share_id, conversation_id: str = ""):
share_url = os.environ.get("G4F_SHARE_URL", "")
conversation_id = conversation_id if conversation_id else str(uuid.uuid4())
return render_template('index.html', share_url=share_url, share_id=share_id, conversation_id=conversation_id)
def _qrcode(self, filename = "qrcode"):
return render(filename)
def _index(self):
return render_template('index.html', conversation_id=str(uuid.uuid4()))
def _background(self, filename = "background"):
return render(filename)
def _settings(self):
return render_template('index.html', conversation_id=str(uuid.uuid4()))
def _background(self):
return render_template('background.html')
def _chat(self, filename = "chat"):
filename = "chat/index" if filename == 'chat' else secure_filename(filename)
return render(filename)