XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet
公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/gpt4free

Improve background page Improve share js functions Fix photoswipe in UI Support n parameter in PollinationsAI

a1871daf
hlohaus <983577+hlohaus@users.noreply.github.com>
提交于

代码差异

11 个文件 +127 -83
Modified g4f/Provider/PollinationsAI.py +25 -9
@@ -3,6 +3,7 @@ from __future__ import annotations
3 3 import json
4 4 import random
5 5 import requests
6 import asyncio
6 7 from urllib.parse import quote_plus
7 8 from typing import Optional
8 9 from aiohttp import ClientSession
@@ -148,6 +149,7 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
148 149 private: bool = False,
149 150 enhance: bool = False,
150 151 safe: bool = False,
152 n: int = 1,
151 153 # Text generation parameters
152 154 media: MediaListType = None,
153 155 temperature: float = None,
@@ -187,7 +189,8 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
187 189 nologo=nologo,
188 190 private=private,
189 191 enhance=enhance,
190 safe=safe
192 safe=safe,
193 n=n
191 194 ):
192 195 yield chunk
193 196 else:
@@ -223,12 +226,10 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
223 226 nologo: bool,
224 227 private: bool,
225 228 enhance: bool,
226 safe: bool
229 safe: bool,
230 n: int
227 231 ) -> AsyncResult:
228 if not cache and seed is None:
229 seed = random.randint(9999, 99999999)
230 232 params = use_aspect_ratio({
231 "seed": seed,
232 233 "width": width,
233 234 "height": height,
234 235 "model": model,
@@ -238,12 +239,27 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
238 239 "safe": str(safe).lower()
239 240 }, aspect_ratio)
240 241 query = "&".join(f"{k}={quote_plus(str(v))}" for k, v in params.items() if v is not None)
241 prompt = quote_plus(prompt)[:8112] # Limit URL length
242 prompt = quote_plus(prompt)[:2048-256-len(query)]
242 243 url = f"{cls.image_api_endpoint}prompt/{prompt}?{query}"
244 def get_image_url(i: int = 0, seed: Optional[int] = None):
245 if i == 0:
246 if not cache and seed is None:
247 seed = random.randint(0, 2**32)
248 else:
249 seed = random.randint(0, 2**32)
250 return f"{url}&seed={seed}" if seed else url
243 251 async with ClientSession(headers=DEFAULT_HEADERS, connector=get_connector(proxy=proxy)) as session:
244 async with session.get(url, allow_redirects=False) as response:
245 await raise_for_status(response)
246 yield ImageResponse(str(response.url), prompt)
252 async def get_image(i: int = 0, seed: Optional[int] = None):
253 async with session.get(get_image_url(i, seed), allow_redirects=False) as response:
254 try:
255 await raise_for_status(response)
256 except Exception as e:
257 debug.error(f"Error fetching image: {e}")
258 return str(response.url)
259 return str(response.url)
260 yield ImageResponse(await asyncio.gather(*[
261 get_image(i, seed) for i in range(int(n))
262 ]), prompt)
247 263
248 264 @classmethod
249 265 async def _generate_text(
Modified g4f/Provider/PollinationsImage.py +3 -1
@@ -45,6 +45,7 @@ class PollinationsImage(PollinationsAI):
45 45 private: bool = False,
46 46 enhance: bool = False,
47 47 safe: bool = False,
48 n: int = 4,
48 49 **kwargs
49 50 ) -> AsyncResult:
50 51 # Calling model updates before creating a generator
@@ -61,6 +62,7 @@ class PollinationsImage(PollinationsAI):
61 62 nologo=nologo,
62 63 private=private,
63 64 enhance=enhance,
64 safe=safe
65 safe=safe,
66 n=n
65 67 ):
66 68 yield chunk
Modified g4f/gui/client/background.html +39 -15
@@ -28,7 +28,6 @@
28 28
29 29 .gradient {
30 30 position: absolute;
31 z-index: -1;
32 31 left: 50vw;
33 32 border-radius: 50%;
34 33 background: radial-gradient(circle at center, var(--accent), var(--gradient));
@@ -73,12 +72,6 @@
73 72 overflow: hidden;
74 73 }
75 74
76 iframe {
77 background: transparent;
78 width: 100%;
79 border: none;
80 }
81
82 75 .hidden {
83 76 display: none;
84 77 }
@@ -86,7 +79,6 @@
86 79 #background, #image-feed, #video-feed {
87 80 height: 100%;
88 81 position: absolute;
89 z-index: -1;
90 82 object-fit: cover;
91 83 object-position: center;
92 84 width: 100%;
@@ -100,29 +92,41 @@
100 92 <video id="video-feed" class="hidden" alt="Video Feed" src="/search/video" autoplay></video>
101 93
102 94 <!-- Gradient Background Circle -->
103 <div class="gradient"></div></div>
95 <div class="gradient"></div>
104 96 <script>
105 97 (async () => {
106 98 const url = "https://image.pollinations.ai/feed";
107 99 const imageFeed = document.getElementById("image-feed");
108 100 const videoFeed = document.getElementById("video-feed");
109 101 const gradient = document.querySelector(".gradient");
110 const images = []
102 const images = [];
111 103 let es = null;
112 104 let skipVideo = 1;
113 let errorVideo = false;
105 let skipImage = 0;
106 let errorVideo = 0;
107 let errorImage = 0;
114 108 videoFeed.onloadeddata = () => {
115 109 videoFeed.classList.remove("hidden");
116 110 gradient.classList.add("hidden");
117 111 };
118 videoFeed.onerror = () => {
112 videoFeed.onerror = (e) => {
119 113 videoFeed.classList.add("hidden");
120 errorVideo = true;
114 errorVideo += 1;
115 if (errorVideo > 3) {
116 gradient.classList.remove("hidden");
117 return;
118 }
119 videoFeed.src = "/search/video?skip=" + skipVideo;
120 skipVideo++;
121 121 };
122 122 videoFeed.onended = () => {
123 123 videoFeed.src = "/search/video?skip=" + skipVideo;
124 124 skipVideo++;
125 125 };
126 videoFeed.onclick = () => {
127 videoFeed.src = "/search/video?skip=" + skipVideo;
128 skipVideo++;
129 };
126 130 function initES() {
127 131 if (es == null || es.readyState == EventSource.CLOSED) {
128 132 const eventSource = new EventSource(url);
@@ -152,9 +156,21 @@
152 156 }
153 157 }
154 158 }
155 initES();
159 let refreshOnHide = true;
160 document.addEventListener("visibilitychange", () => {
161 if (document.hidden) {
162 refreshOnHide = false;
163 } else {
164 refreshOnHide = true;
165 }
166 });
156 167 setInterval(() => {
157 if (!errorVideo) {
168 if (errorVideo < 3 || !refreshOnHide) {
169 return;
170 }
171 if (errorImage < 3) {
172 imageFeed.src = "/search/image+g4f?skip=" + skipImage;
173 skipImage++;
158 174 return;
159 175 }
160 176 if (images.length > 0) {
@@ -168,6 +184,14 @@
168 184 imageFeed.onerror = () => {
169 185 imageFeed.classList.add("hidden");
170 186 gradient.classList.remove("hidden");
187 errorImage++;
188 };
189 imageFeed.onload = () => {
190 imageFeed.classList.remove("hidden");
191 gradient.classList.add("hidden");
192 };
193 imageFeed.onclick = () => {
194 imageFeed.src = "/search/image?random=" + Math.random();
171 195 };
172 196 })();
173 197 </script>
Modified g4f/gui/client/index.html +1 -1
@@ -63,7 +63,7 @@
63 63 <script src="/static/js/highlight.min.js" async></script>
64 64 <script>
65 65 window.conversation_id = "{{conversation_id}}";
66 window.chat_id = "{{chat_id}}";
66 window.share_id = "{{share_id}}";
67 67 window.share_url = "{{share_url}}";
68 68 window.start_id = "{{conversation_id}}";
69 69 </script>
Modified g4f/gui/client/qrcode.html +5 -5
@@ -74,15 +74,15 @@
74 74 });
75 75
76 76 document.getElementById('generateQRCode').addEventListener('click', async () => {
77 const chat_id = generate_uuid();
77 const share_id = generate_uuid();
78 78
79 const url = `${share_url}/backend-api/v2/chat/${encodeURI(chat_id)}`;
79 const url = `${share_url}/backend-api/v2/chat/${encodeURI(share_id)}`;
80 80 const response = await fetch(url, {
81 81 method: 'POST',
82 82 headers: {'content-type': 'application/json'},
83 83 body: localStorage.getItem(`conversation:${conversation_id}`)
84 84 });
85 const share = `${share_url}/chat/${encodeURI(chat_id)}/${encodeURI(conversation_id)}`;
85 const share = `${share_url}/chat/${encodeURI(share_id)}/${encodeURI(conversation_id)}`;
86 86 const qrcodeStatus = document.getElementById('qrcode-status');
87 87 if (response.status !== 200) {
88 88 qrcodeStatus.innerText = 'Error generating QR code: ' + response.statusText;
@@ -115,8 +115,8 @@
115 115
116 116 const constraints = {
117 117 video: {
118 width: { ideal: 1280 },
119 height: { ideal: 1280 },
118 width: { ideal: 800 },
119 height: { ideal: 800 },
120 120 facingMode: facingMode
121 121 },
122 122 audio: false
Modified g4f/gui/client/static/js/chat.v1.js +28 -31
@@ -298,8 +298,8 @@ const register_message_buttons = async () => {
298 298 if (message_el) {
299 299 if ("index" in message_el.dataset) {
300 300 await remove_message(window.conversation_id, message_el.dataset.index);
301 chatBody.removeChild(message_el);
301 302 }
302 message_el.remove();
303 303 }
304 304 reloadConversation = true;
305 305 await safe_load_conversation(window.conversation_id, false);
@@ -842,7 +842,7 @@ async function add_message_chunk(message, message_id, provider, scroll, finish_m
842 842 for (const [key, value] of Object.entries(message.conversation)) {
843 843 conversation.data[key] = value;
844 844 }
845 await save_conversation(conversation_id, conversation);
845 await save_conversation(conversation_id, get_conversation_data(conversation));
846 846 } else if (message.type == "auth") {
847 847 error_storage[message_id] = message.message
848 848 content_map.inner.innerHTML += markdown_render(`**An error occured:** ${message.message}`);
@@ -1227,7 +1227,6 @@ function sanitize(input, replacement) {
1227 1227 }
1228 1228
1229 1229 async function set_conversation_title(conversation_id, title) {
1230 window.chat_id = null;
1231 1230 conversation = await get_conversation(conversation_id)
1232 1231 conversation.new_title = title;
1233 1232 const new_id = sanitize(title, " ");
@@ -1319,7 +1318,6 @@ const set_conversation = async (conversation_id) => {
1319 1318
1320 1319 const new_conversation = async () => {
1321 1320 history.pushState({}, null, `/chat/`);
1322 window.chat_id = null;
1323 1321 window.conversation_id = uuid();
1324 1322 document.title = window.title || document.title;
1325 1323 document.querySelector(".chat-header").innerText = "New Conversation - G4F";
@@ -1391,7 +1389,7 @@ const load_conversation = async (conversation, scroll=true) => {
1391 1389 document.title = title;
1392 1390 }
1393 1391 const chatHeader = document.querySelector(".chat-header");
1394 if (window.chat_id) {
1392 if (window.share_id && conversation.id == window.start_id) {
1395 1393 chatHeader.innerHTML = '<i class="fa-solid fa-qrcode"></i> ' + escapeHtml(title);
1396 1394 } else {
1397 1395 chatHeader.innerText = title;
@@ -1581,16 +1579,16 @@ async function get_conversation(conversation_id) {
1581 1579 return conversation;
1582 1580 }
1583 1581
1584 async function save_conversation(conversation_id, conversation) {
1582 function get_conversation_data(conversation) {
1585 1583 conversation.updated = Date.now();
1586 const data = JSON.stringify(conversation)
1584 return JSON.stringify(conversation);
1585 }
1586
1587 async function save_conversation(conversation_id, data) {
1587 1588 appStorage.setItem(
1588 1589 `conversation:${conversation_id}`,
1589 1590 data
1590 1591 );
1591 if (conversation_id != window.start_id) {
1592 window.chat_id = null;
1593 }
1594 1592 }
1595 1593
1596 1594 async function get_messages(conversation_id) {
@@ -1600,13 +1598,13 @@ async function get_messages(conversation_id) {
1600 1598
1601 1599 async function add_conversation(conversation_id) {
1602 1600 if (appStorage.getItem(`conversation:${conversation_id}`) == null) {
1603 await save_conversation(conversation_id, {
1601 await save_conversation(conversation_id, get_conversation_data({
1604 1602 id: conversation_id,
1605 1603 title: "",
1606 1604 added: Date.now(),
1607 1605 system: chatPrompt?.value,
1608 1606 items: [],
1609 });
1607 }));
1610 1608 }
1611 1609 try {
1612 1610 add_url_to_history(`/chat/${conversation_id}`);
@@ -1622,7 +1620,7 @@ async function save_system_message() {
1622 1620 const conversation = await get_conversation(window.conversation_id);
1623 1621 if (conversation) {
1624 1622 conversation.system = chatPrompt?.value;
1625 await save_conversation(window.conversation_id, conversation);
1623 await save_conversation(window.conversation_id, get_conversation_data(conversation));
1626 1624 }
1627 1625 }
1628 1626
@@ -1640,9 +1638,10 @@ const remove_message = async (conversation_id, index) => {
1640 1638 }
1641 1639 }
1642 1640 conversation.items = new_items;
1643 await save_conversation(conversation_id, conversation);
1644 if (window.chat_id && window.conversation_id == window.start_id) {
1645 const url = `${window.share_url}/backend-api/v2/chat/${window.chat_id}`;
1641 const data = get_conversation_data(conversation);
1642 await save_conversation(conversation_id, data);
1643 if (window.share_id && window.conversation_id == window.start_id) {
1644 const url = `${window.share_url}/backend-api/v2/chat/${window.share_id}`;
1646 1645 await fetch(url, {
1647 1646 method: 'POST',
1648 1647 headers: {'content-type': 'application/json'},
@@ -1716,13 +1715,14 @@ const add_message = async (
1716 1715 });
1717 1716 conversation.items = new_messages;
1718 1717 }
1719 await save_conversation(conversation_id, conversation);
1720 if (window.chat_id && conversation_id == window.start_id) {
1721 const url = `${window.share_url}/backend-api/v2/chat/${window.chat_id}`;
1718 data = get_conversation_data(conversation);
1719 await save_conversation(conversation_id, data);
1720 if (window.share_id && conversation_id == window.start_id) {
1721 const url = `${window.share_url}/backend-api/v2/chat/${window.share_id}`;
1722 1722 fetch(url, {
1723 1723 method: 'POST',
1724 1724 headers: {'content-type': 'application/json'},
1725 body: JSON.stringify(conversation),
1725 body: data
1726 1726 });
1727 1727 }
1728 1728 return conversation.items.length - 1;
@@ -1758,7 +1758,7 @@ const load_conversations = async () => {
1758 1758 // appStorage.removeItem(`conversation:${conversation.id}`);
1759 1759 // return;
1760 1760 // }
1761 const shareIcon = (conversation.id == window.start_id && window.chat_id) ? '<i class="fa-solid fa-qrcode"></i>': '';
1761 const shareIcon = (conversation.id == window.start_id && window.share_id) ? '<i class="fa-solid fa-qrcode"></i>': '';
1762 1762 html.push(`
1763 1763 <div class="convo" id="convo-${conversation.id}">
1764 1764 <div class="left" onclick="set_conversation('${conversation.id}')">
@@ -2071,14 +2071,14 @@ chatPrompt.addEventListener("input", function() {
2071 2071 });
2072 2072
2073 2073 window.addEventListener('load', async function() {
2074 if (!window.chat_id) {
2074 if (!window.share_id) {
2075 2075 return await load_conversation(JSON.parse(appStorage.getItem(`conversation:${window.conversation_id}`)));
2076 2076 }
2077 2077 if (!window.conversation_id) {
2078 window.conversation_id = window.chat_id;
2078 window.conversation_id = window.share_id;
2079 2079 }
2080 const response = await fetch(`${window.share_url}/backend-api/v2/chat/${window.chat_id ? window.chat_id : window.conversation_id}`, {
2081 headers: {'accept': 'application/json'},
2080 const response = await fetch(`${window.share_url}/backend-api/v2/chat/${window.share_id}`, {
2081 headers: {'accept': 'application/json', 'x-conversation-id': window.conversation_id},
2082 2082 });
2083 2083 if (!response.ok) {
2084 2084 return await load_conversation(JSON.parse(appStorage.getItem(`conversation:${window.conversation_id}`)));
@@ -2087,10 +2087,7 @@ window.addEventListener('load', async function() {
2087 2087 if (!window.conversation_id || conversation.id == window.conversation_id) {
2088 2088 window.conversation_id = conversation.id;
2089 2089 await load_conversation(conversation);
2090 appStorage.setItem(
2091 `conversation:${conversation.id}`,
2092 JSON.stringify(conversation)
2093 );
2090 await save_conversation(window.conversation_id, JSON.stringify(conversation));
2094 2091 await load_conversations();
2095 2092 let refreshOnHide = true;
2096 2093 document.addEventListener("visibilitychange", () => {
@@ -2101,7 +2098,7 @@ window.addEventListener('load', async function() {
2101 2098 }
2102 2099 });
2103 2100 var refreshIntervalId = setInterval(async () => {
2104 if (!window.chat_id) {
2101 if (!window.share_id) {
2105 2102 clearInterval(refreshIntervalId);
2106 2103 return;
2107 2104 }
@@ -2111,7 +2108,7 @@ window.addEventListener('load', async function() {
2111 2108 if (window.conversation_id != window.start_id) {
2112 2109 return;
2113 2110 }
2114 const response = await fetch(`${window.share_url}/backend-api/v2/chat/${window.chat_id}`, {
2111 const response = await fetch(`${window.share_url}/backend-api/v2/chat/${window.share_id}`, {
2115 2112 headers: {
2116 2113 'accept': 'application/json',
2117 2114 'if-none-match': conversation.updated,
Modified g4f/gui/client/static/js/photoswipe.js +1 -1
@@ -4,7 +4,7 @@ import PhotoSwipeAutoHideUI from "https://cdn.jsdelivr.net/gh/arnowelzel/photosw
4 4 import PhotoSwipeSlideshow from "https://cdn.jsdelivr.net/gh/dpet23/photoswipe-slideshow@v2.0.0/photoswipe-slideshow.esm.min.js";
5 5
6 6 const lightbox = new PhotoSwipeLightbox({
7 gallery: '#messages',
7 gallery: '#chatBody',
8 8 children: 'a:has(img)',
9 9 initialZoomLevel: 'fill',
10 10 secondaryZoomLevel: 1,
Modified g4f/gui/server/backend_api.py +15 -13
@@ -371,6 +371,8 @@ class Backend_Api(Api):
371 371 match_files = [file for file, count in match_files.items() if count >= request.args.get("min", len(search))]
372 372 if int(request.args.get("skip", 0)) >= len(match_files):
373 373 return jsonify({"error": {"message": "Not found"}}), 404
374 if (request.args.get("random", False)):
375 return redirect(f"/media/{random.choice(match_files)}"), 302
374 376 return redirect(f"/media/{match_files[int(request.args.get("skip", 0))]}"), 302
375 377
376 378 @app.route('/backend-api/v2/upload_cookies', methods=['POST'])
@@ -386,12 +388,12 @@ class Backend_Api(Api):
386 388 return "File saved", 200
387 389 return 'Not supported file', 400
388 390
389 @self.app.route('/backend-api/v2/chat/<chat_id>', methods=['GET'])
390 def get_chat(chat_id: str) -> str:
391 chat_id = secure_filename(chat_id)
392 if self.chat_cache.get(chat_id, 0) == request.headers.get("if-none-match", 0):
391 @self.app.route('/backend-api/v2/chat/<share_id>', methods=['GET'])
392 def get_chat(share_id: str) -> str:
393 share_id = secure_filename(share_id)
394 if self.chat_cache.get(share_id, 0) == request.headers.get("if-none-match", 0):
393 395 return jsonify({"error": {"message": "Not modified"}}), 304
394 bucket_dir = get_bucket_dir(chat_id)
396 bucket_dir = get_bucket_dir(share_id)
395 397 file = os.path.join(bucket_dir, "chat.json")
396 398 if not os.path.isfile(file):
397 399 return jsonify({"error": {"message": "Not found"}}), 404
@@ -399,23 +401,23 @@ class Backend_Api(Api):
399 401 chat_data = json.load(f)
400 402 if chat_data.get("updated", 0) == request.headers.get("if-none-match", 0):
401 403 return jsonify({"error": {"message": "Not modified"}}), 304
402 self.chat_cache[chat_id] = chat_data.get("updated", 0)
404 self.chat_cache[share_id] = chat_data.get("updated", 0)
403 405 return jsonify(chat_data), 200
404 406
405 @self.app.route('/backend-api/v2/chat/<chat_id>', methods=['POST'])
406 def upload_chat(chat_id: str) -> dict:
407 @self.app.route('/backend-api/v2/chat/<share_id>', methods=['POST'])
408 def upload_chat(share_id: str) -> dict:
407 409 chat_data = {**request.json}
408 410 updated = chat_data.get("updated", 0)
409 cache_value = self.chat_cache.get(chat_id, 0)
411 cache_value = self.chat_cache.get(share_id, 0)
410 412 if updated == cache_value:
411 413 return jsonify({"error": {"message": "invalid date"}}), 400
412 chat_id = secure_filename(chat_id)
413 bucket_dir = get_bucket_dir(chat_id)
414 share_id = secure_filename(share_id)
415 bucket_dir = get_bucket_dir(share_id)
414 416 os.makedirs(bucket_dir, exist_ok=True)
415 417 with open(os.path.join(bucket_dir, "chat.json"), 'w') as f:
416 418 json.dump(chat_data, f)
417 self.chat_cache[chat_id] = updated
418 return {"chat_id": chat_id}
419 self.chat_cache[share_id] = updated
420 return {"share_id": share_id}
419 421
420 422 def handle_synthesize(self, provider: str):
421 423 try:
Modified g4f/gui/server/website.py +6 -6
@@ -19,12 +19,12 @@ class Website:
19 19 'function': self._chat,
20 20 'methods': ['GET', 'POST']
21 21 },
22 '/chat/<chat_id>/': {
23 'function': self._chat_id,
22 '/chat/<share_id>/': {
23 'function': self._share_id,
24 24 'methods': ['GET', 'POST']
25 25 },
26 '/chat/<chat_id>/<conversation_id>': {
27 'function': self._chat_id,
26 '/chat/<share_id>/<conversation_id>': {
27 'function': self._share_id,
28 28 'methods': ['GET', 'POST']
29 29 },
30 30 '/chat/menu/': {
@@ -50,9 +50,9 @@ class Website:
50 50 return render_template('index.html', conversation_id=str(uuid.uuid4()))
51 51 return render_template('index.html', conversation_id=conversation_id)
52 52
53 def _chat_id(self, chat_id, conversation_id: str = ""):
53 def _share_id(self, share_id, conversation_id: str = ""):
54 54 share_url = os.environ.get("G4F_SHARE_URL", "")
55 return render_template('index.html', share_url=share_url, chat_id=chat_id, conversation_id=conversation_id)
55 return render_template('index.html', share_url=share_url, share_id=share_id, conversation_id=conversation_id)
56 56
57 57 def _index(self):
58 58 return render_template('index.html', conversation_id=str(uuid.uuid4()))
Modified g4f/image/copy_images.py +3 -1
@@ -25,8 +25,10 @@ def get_media_extension(media: str) -> str:
25 25 """Extract media file extension from URL or filename"""
26 26 match = re.search(r"\.(j?[a-z]{3})(?:\?|$)", media, re.IGNORECASE)
27 27 extension = match.group(1).lower() if match else ""
28 if not extension:
29 return ""
28 30 if extension not in EXTENSIONS_MAP:
29 raise ValueError(f"Unsupported media extension: {extension}")
31 raise ValueError(f"Unsupported media extension: {extension} in: {media}")
30 32 return f".{extension}"
31 33
32 34 def ensure_images_dir():
Modified g4f/requests/__init__.py +1 -0
@@ -154,6 +154,7 @@ async def get_nodriver(
154 154 if not os.path.exists(browser_executable_path):
155 155 browser_executable_path = None
156 156 lock_file = Path(get_cookies_dir()) / ".nodriver_is_open"
157 lock_file.parent.mkdir(exist_ok=True)
157 158 # Implement a short delay (milliseconds) to prevent race conditions.
158 159 await asyncio.sleep(0.1 * random.randint(0, 50))
159 160 if lock_file.exists():