pygpt-net 2.6.64__py3-none-any.whl → 2.6.66__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pygpt_net/CHANGELOG.txt +21 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/app.py +5 -1
- pygpt_net/controller/chat/chat.py +0 -0
- pygpt_net/controller/chat/handler/openai_stream.py +137 -7
- pygpt_net/controller/chat/render.py +0 -0
- pygpt_net/controller/config/field/checkbox_list.py +34 -1
- pygpt_net/controller/files/files.py +71 -2
- pygpt_net/controller/media/media.py +20 -1
- pygpt_net/controller/presets/editor.py +137 -22
- pygpt_net/controller/presets/presets.py +4 -1
- pygpt_net/controller/ui/mode.py +14 -10
- pygpt_net/controller/ui/ui.py +18 -1
- pygpt_net/core/agents/custom/__init__.py +18 -2
- pygpt_net/core/agents/custom/runner.py +2 -2
- pygpt_net/core/attachments/clipboard.py +146 -0
- pygpt_net/core/image/image.py +34 -1
- pygpt_net/core/render/web/renderer.py +33 -11
- pygpt_net/core/tabs/tabs.py +0 -0
- pygpt_net/core/types/image.py +61 -3
- pygpt_net/data/config/config.json +4 -3
- pygpt_net/data/config/models.json +629 -41
- pygpt_net/data/css/style.dark.css +12 -0
- pygpt_net/data/css/style.light.css +12 -0
- pygpt_net/data/icons/pin2.svg +1 -0
- pygpt_net/data/icons/pin3.svg +3 -0
- pygpt_net/data/icons/point.svg +1 -0
- pygpt_net/data/icons/target.svg +1 -0
- pygpt_net/data/js/app/ui.js +19 -2
- pygpt_net/data/js/app/user.js +22 -54
- pygpt_net/data/js/app.min.js +7 -9
- pygpt_net/data/locale/locale.de.ini +4 -0
- pygpt_net/data/locale/locale.en.ini +8 -0
- pygpt_net/data/locale/locale.es.ini +4 -0
- pygpt_net/data/locale/locale.fr.ini +4 -0
- pygpt_net/data/locale/locale.it.ini +4 -0
- pygpt_net/data/locale/locale.pl.ini +4 -0
- pygpt_net/data/locale/locale.uk.ini +4 -0
- pygpt_net/data/locale/locale.zh.ini +4 -0
- pygpt_net/icons.qrc +4 -0
- pygpt_net/icons_rc.py +274 -137
- pygpt_net/item/model.py +15 -19
- pygpt_net/js_rc.py +2038 -2075
- pygpt_net/provider/agents/openai/agent.py +0 -0
- pygpt_net/provider/api/google/__init__.py +20 -9
- pygpt_net/provider/api/google/image.py +161 -28
- pygpt_net/provider/api/google/video.py +73 -36
- pygpt_net/provider/api/openai/__init__.py +21 -11
- pygpt_net/provider/api/openai/agents/client.py +0 -0
- pygpt_net/provider/api/openai/video.py +562 -0
- pygpt_net/provider/core/config/patch.py +15 -0
- pygpt_net/provider/core/model/patch.py +29 -3
- pygpt_net/provider/vector_stores/qdrant.py +117 -0
- pygpt_net/ui/__init__.py +6 -1
- pygpt_net/ui/dialog/preset.py +9 -4
- pygpt_net/ui/layout/chat/attachments.py +18 -1
- pygpt_net/ui/layout/status.py +3 -3
- pygpt_net/ui/layout/toolbox/raw.py +7 -1
- pygpt_net/ui/widget/element/status.py +55 -0
- pygpt_net/ui/widget/filesystem/explorer.py +116 -2
- pygpt_net/ui/widget/lists/context.py +26 -16
- pygpt_net/ui/widget/option/checkbox_list.py +14 -2
- pygpt_net/ui/widget/textarea/input.py +71 -17
- {pygpt_net-2.6.64.dist-info → pygpt_net-2.6.66.dist-info}/METADATA +76 -25
- {pygpt_net-2.6.64.dist-info → pygpt_net-2.6.66.dist-info}/RECORD +63 -55
- {pygpt_net-2.6.64.dist-info → pygpt_net-2.6.66.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.64.dist-info → pygpt_net-2.6.66.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.64.dist-info → pygpt_net-2.6.66.dist-info}/entry_points.txt +0 -0
pygpt_net/CHANGELOG.txt
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
2.6.66 (2025-12-25)
|
|
2
|
+
|
|
3
|
+
- Added Sora 2 support - #155.
|
|
4
|
+
- Added Nano Banana support.
|
|
5
|
+
- Added Qdrant Vector Store - merged PR #147 by @Anush008.
|
|
6
|
+
- Added models: gpt-5.2, gpt-image-1.5, gemini-3, nano-banana-pro, sora-2, claude-sonnet-4.5, claude-opus-4.5, veo-3.1.
|
|
7
|
+
- Added Select/unselect All option in checkbox lists.
|
|
8
|
+
- OpenAI SDK upgraded to 2.14.0, Anthropic SDK upgraded to 0.75.0, xAI SDK upgraded to 1.5.0, Google GenAI upgraded to 1.56.0, LlamaIndex upgraded to 0.14.10.
|
|
9
|
+
- Fix: charset-normalizer 3.2.0 circular import - #152.
|
|
10
|
+
- Fix: Google client closed state.
|
|
11
|
+
|
|
12
|
+
2.6.65 (2025-09-28)
|
|
13
|
+
|
|
14
|
+
- Added drag and drop functionality for files and directories from the filesystem in attachments and file explorer.
|
|
15
|
+
- Added automatic thumbnail generation when uploading avatars.
|
|
16
|
+
- Added a last status timer.
|
|
17
|
+
- Added a fade effect to collapsed user messages.
|
|
18
|
+
- Added a scroll area to the agent options in the presets editor.
|
|
19
|
+
- Added a hover effect to lists.
|
|
20
|
+
- Improved UI/UX.
|
|
21
|
+
|
|
1
22
|
2.6.64 (2025-09-27)
|
|
2
23
|
|
|
3
24
|
- Added translations to agent headers.
|
pygpt_net/__init__.py
CHANGED
|
@@ -6,15 +6,15 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date: 2025.
|
|
9
|
+
# Updated Date: 2025.12.15 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
__author__ = "Marcin Szczygliński"
|
|
13
13
|
__copyright__ = "Copyright 2025, Marcin Szczygliński"
|
|
14
14
|
__credits__ = ["Marcin Szczygliński"]
|
|
15
15
|
__license__ = "MIT"
|
|
16
|
-
__version__ = "2.6.
|
|
17
|
-
__build__ = "2025-
|
|
16
|
+
__version__ = "2.6.66"
|
|
17
|
+
__build__ = "2025-12-25"
|
|
18
18
|
__maintainer__ = "Marcin Szczygliński"
|
|
19
19
|
__github__ = "https://github.com/szczyglis-dev/py-gpt"
|
|
20
20
|
__report__ = "https://github.com/szczyglis-dev/py-gpt/issues"
|
pygpt_net/app.py
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date: 2025.09.
|
|
9
|
+
# Updated Date: 2025.09.28 09:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import os
|
|
@@ -20,6 +20,8 @@ from pygpt_net.utils import set_env
|
|
|
20
20
|
|
|
21
21
|
# app env
|
|
22
22
|
set_env("PYGPT_APP_ENV", "prod", allow_overwrite=True) # dev | prod
|
|
23
|
+
# IF dev, JS will be loaded from `data/js/app/*` [js_rc.py], not from `data/js/app.min.js`
|
|
24
|
+
# recompile js_rc.py with: bin/resources.sh, minify to app.min.js with: bin/minify.sh
|
|
23
25
|
|
|
24
26
|
# debug
|
|
25
27
|
# set_env("QTWEBENGINE_REMOTE_DEBUGGING", 9222)
|
|
@@ -144,6 +146,7 @@ from pygpt_net.provider.llms.open_router import OpenRouterLLM
|
|
|
144
146
|
from pygpt_net.provider.vector_stores.chroma import ChromaProvider
|
|
145
147
|
from pygpt_net.provider.vector_stores.elasticsearch import ElasticsearchProvider
|
|
146
148
|
from pygpt_net.provider.vector_stores.pinecode import PinecodeProvider
|
|
149
|
+
from pygpt_net.provider.vector_stores.qdrant import QdrantProvider
|
|
147
150
|
from pygpt_net.provider.vector_stores.redis import RedisProvider
|
|
148
151
|
from pygpt_net.provider.vector_stores.simple import SimpleProvider
|
|
149
152
|
|
|
@@ -473,6 +476,7 @@ def run(**kwargs):
|
|
|
473
476
|
launcher.add_vector_store(ChromaProvider())
|
|
474
477
|
launcher.add_vector_store(ElasticsearchProvider())
|
|
475
478
|
launcher.add_vector_store(PinecodeProvider())
|
|
479
|
+
launcher.add_vector_store(QdrantProvider())
|
|
476
480
|
launcher.add_vector_store(RedisProvider())
|
|
477
481
|
launcher.add_vector_store(SimpleProvider())
|
|
478
482
|
|
|
File without changes
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date: 2025.
|
|
9
|
+
# Updated Date: 2025.12.26 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import base64
|
|
@@ -17,6 +17,115 @@ from typing import Optional, Any
|
|
|
17
17
|
from .utils import capture_openai_usage
|
|
18
18
|
|
|
19
19
|
|
|
20
|
+
# v2: Support both dict and Pydantic objects returned by OpenAI Python SDK v2
|
|
21
|
+
def _to_dict_safe(obj: Any) -> Optional[dict]:
|
|
22
|
+
"""
|
|
23
|
+
Convert OpenAI SDK typed models (Pydantic) or plain objects to dict safely.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
dict or None
|
|
27
|
+
"""
|
|
28
|
+
if obj is None:
|
|
29
|
+
return None
|
|
30
|
+
if isinstance(obj, dict):
|
|
31
|
+
return obj
|
|
32
|
+
# Pydantic v2
|
|
33
|
+
try:
|
|
34
|
+
if hasattr(obj, "model_dump"):
|
|
35
|
+
return obj.model_dump()
|
|
36
|
+
except Exception:
|
|
37
|
+
pass
|
|
38
|
+
# Pydantic v1 fallback
|
|
39
|
+
try:
|
|
40
|
+
if hasattr(obj, "dict"):
|
|
41
|
+
return obj.dict()
|
|
42
|
+
except Exception:
|
|
43
|
+
pass
|
|
44
|
+
# Generic best-effort
|
|
45
|
+
try:
|
|
46
|
+
return dict(obj)
|
|
47
|
+
except Exception:
|
|
48
|
+
pass
|
|
49
|
+
try:
|
|
50
|
+
return getattr(obj, "__dict__", None)
|
|
51
|
+
except Exception:
|
|
52
|
+
pass
|
|
53
|
+
return None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# v2: Extract nested attribute or dict key chain (e.g. "url_citation.url")
|
|
57
|
+
def _deep_get(obj: Any, path: str, default: Any = None) -> Any:
|
|
58
|
+
"""
|
|
59
|
+
Best-effort nested getter that works for dicts and objects.
|
|
60
|
+
"""
|
|
61
|
+
cur = obj
|
|
62
|
+
for part in path.split("."):
|
|
63
|
+
if cur is None:
|
|
64
|
+
return default
|
|
65
|
+
if isinstance(cur, dict):
|
|
66
|
+
cur = cur.get(part, None)
|
|
67
|
+
else:
|
|
68
|
+
cur = getattr(cur, part, None)
|
|
69
|
+
return cur if cur is not None else default
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# v2: Normalize annotation shape across SDK versions
|
|
73
|
+
def _annotation_type(ann: Any) -> Optional[str]:
|
|
74
|
+
"""
|
|
75
|
+
Return the annotation 'type' in a robust way.
|
|
76
|
+
"""
|
|
77
|
+
t = getattr(ann, "type", None)
|
|
78
|
+
if t:
|
|
79
|
+
return t
|
|
80
|
+
if isinstance(ann, dict):
|
|
81
|
+
return ann.get("type")
|
|
82
|
+
# Try dictified view
|
|
83
|
+
ann_d = _to_dict_safe(ann)
|
|
84
|
+
if isinstance(ann_d, dict):
|
|
85
|
+
return ann_d.get("type")
|
|
86
|
+
return None
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# v2: Extract URL from url_citation annotation across shapes
|
|
90
|
+
def _extract_url_from_annotation(ann: Any) -> Optional[str]:
|
|
91
|
+
"""
|
|
92
|
+
Supports shapes:
|
|
93
|
+
- {"type":"url_citation","url":"..."}
|
|
94
|
+
- {"type":"url_citation","url_citation":{"url":"..."}}
|
|
95
|
+
- Typed models with attributes: ann.url OR ann.url_citation.url
|
|
96
|
+
"""
|
|
97
|
+
# direct attribute
|
|
98
|
+
url = getattr(ann, "url", None)
|
|
99
|
+
if isinstance(url, str) and url:
|
|
100
|
+
return url
|
|
101
|
+
|
|
102
|
+
# dict direct
|
|
103
|
+
if isinstance(ann, dict):
|
|
104
|
+
url = ann.get("url")
|
|
105
|
+
if isinstance(url, str) and url:
|
|
106
|
+
return url
|
|
107
|
+
nested = ann.get("url_citation")
|
|
108
|
+
if isinstance(nested, dict):
|
|
109
|
+
url = nested.get("url")
|
|
110
|
+
if isinstance(url, str) and url:
|
|
111
|
+
return url
|
|
112
|
+
|
|
113
|
+
# typed nested or generic deep getters
|
|
114
|
+
for candidate in ("url_citation.url", "url_citation.href", "href", "source_url"):
|
|
115
|
+
url = _deep_get(ann, candidate)
|
|
116
|
+
if isinstance(url, str) and url:
|
|
117
|
+
return url
|
|
118
|
+
|
|
119
|
+
# try after dictify
|
|
120
|
+
ann_d = _to_dict_safe(ann)
|
|
121
|
+
if isinstance(ann_d, dict):
|
|
122
|
+
url = ann_d.get("url") or _deep_get(ann_d, "url_citation.url")
|
|
123
|
+
if isinstance(url, str) and url:
|
|
124
|
+
return url
|
|
125
|
+
|
|
126
|
+
return None
|
|
127
|
+
|
|
128
|
+
|
|
20
129
|
def process_api_chat(ctx, state, chunk) -> Optional[str]:
|
|
21
130
|
"""
|
|
22
131
|
OpenAI-compatible Chat Completions stream delta (robust to dict/object tool_calls).
|
|
@@ -196,17 +305,38 @@ def process_api_chat_responses(ctx, core, state, chunk, etype: Optional[str]) ->
|
|
|
196
305
|
|
|
197
306
|
elif etype == "response.output_text.annotation.added":
|
|
198
307
|
ann = chunk.annotation
|
|
199
|
-
|
|
308
|
+
|
|
309
|
+
# v2: SDK v2 can return a typed model; support both dict and typed
|
|
310
|
+
a_type = _annotation_type(ann)
|
|
311
|
+
|
|
312
|
+
if a_type == "url_citation":
|
|
200
313
|
if state.citations is None:
|
|
201
314
|
state.citations = []
|
|
202
|
-
|
|
203
|
-
|
|
315
|
+
|
|
316
|
+
# Extract URL across shapes and SDK versions
|
|
317
|
+
url_citation = _extract_url_from_annotation(ann)
|
|
318
|
+
|
|
319
|
+
if url_citation and url_citation not in state.citations:
|
|
204
320
|
state.citations.append(url_citation)
|
|
321
|
+
|
|
322
|
+
# keep ctx.urls always reflecting the current list
|
|
205
323
|
ctx.urls = state.citations
|
|
206
|
-
|
|
324
|
+
|
|
325
|
+
elif a_type == "container_file_citation":
|
|
326
|
+
# container-created file (Code Interpreter)
|
|
327
|
+
ann_d = _to_dict_safe(ann) or {}
|
|
328
|
+
state.files.append({
|
|
329
|
+
"container_id": ann_d.get("container_id", _deep_get(ann, "container_id")),
|
|
330
|
+
"file_id": ann_d.get("file_id", _deep_get(ann, "file_id")),
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
elif a_type == "file_citation":
|
|
334
|
+
# v2: Some SDKs emit plain 'file_citation' (non-container). Keep parity with container handling.
|
|
335
|
+
ann_d = _to_dict_safe(ann) or {}
|
|
336
|
+
# optional: store as generic file citation (without container)
|
|
207
337
|
state.files.append({
|
|
208
|
-
"container_id": ann
|
|
209
|
-
"file_id": ann
|
|
338
|
+
"container_id": ann_d.get("container_id", _deep_get(ann, "container_id")), # may be None
|
|
339
|
+
"file_id": ann_d.get("file_id", _deep_get(ann, "file_id")),
|
|
210
340
|
})
|
|
211
341
|
|
|
212
342
|
elif etype == "response.reasoning_summary_text.delta":
|
|
File without changes
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date: 2025.
|
|
9
|
+
# Updated Date: 2025.12.25 20:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from typing import Any, Dict, List
|
|
@@ -86,6 +86,39 @@ class CheckboxList:
|
|
|
86
86
|
except Exception as e:
|
|
87
87
|
self.window.core.debug.log(e)
|
|
88
88
|
|
|
89
|
+
|
|
90
|
+
def on_select_all(
|
|
91
|
+
self,
|
|
92
|
+
parent_id: str,
|
|
93
|
+
key: str,
|
|
94
|
+
option: dict
|
|
95
|
+
):
|
|
96
|
+
"""
|
|
97
|
+
Event: select all checkboxes
|
|
98
|
+
|
|
99
|
+
:param parent_id: Options parent ID
|
|
100
|
+
:param key: Option key
|
|
101
|
+
:param option: Option data
|
|
102
|
+
"""
|
|
103
|
+
ui = self.window.ui
|
|
104
|
+
cfg_parent = ui.config.get(parent_id)
|
|
105
|
+
if not cfg_parent:
|
|
106
|
+
return
|
|
107
|
+
entry = cfg_parent.get(key)
|
|
108
|
+
if entry is None or not hasattr(entry, "boxes"):
|
|
109
|
+
return
|
|
110
|
+
boxes = entry.boxes
|
|
111
|
+
|
|
112
|
+
mode = "unselect_all"
|
|
113
|
+
|
|
114
|
+
for name, cb in boxes.items():
|
|
115
|
+
if cb is not None and not cb.isChecked():
|
|
116
|
+
mode = "select_all"
|
|
117
|
+
|
|
118
|
+
for name, cb in boxes.items():
|
|
119
|
+
if cb is not None:
|
|
120
|
+
cb.setChecked(mode == "select_all")
|
|
121
|
+
|
|
89
122
|
def get_value(
|
|
90
123
|
self,
|
|
91
124
|
parent_id: str,
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date: 2025.
|
|
9
|
+
# Updated Date: 2025.09.28 08:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import datetime
|
|
@@ -270,6 +270,75 @@ class Files:
|
|
|
270
270
|
self.window.update_status(f"[OK] Uploaded: {num} files.")
|
|
271
271
|
self.update_explorer()
|
|
272
272
|
|
|
273
|
+
def upload_paths(
|
|
274
|
+
self,
|
|
275
|
+
paths: list,
|
|
276
|
+
target_directory: Optional[str] = None
|
|
277
|
+
):
|
|
278
|
+
"""
|
|
279
|
+
Upload provided local paths (files or directories) into target directory.
|
|
280
|
+
- Directories are copied recursively.
|
|
281
|
+
- Name collisions are resolved using timestamp prefix, consistent with upload_local().
|
|
282
|
+
- Skips copying directory into itself or its subdirectory.
|
|
283
|
+
|
|
284
|
+
:param paths: list of absolute local paths
|
|
285
|
+
:param target_directory: destination directory (defaults to user 'data' dir)
|
|
286
|
+
"""
|
|
287
|
+
if not paths:
|
|
288
|
+
return
|
|
289
|
+
if target_directory is None:
|
|
290
|
+
target_directory = self.window.core.config.get_user_dir('data')
|
|
291
|
+
|
|
292
|
+
try:
|
|
293
|
+
if not os.path.exists(target_directory):
|
|
294
|
+
os.makedirs(target_directory, exist_ok=True)
|
|
295
|
+
except Exception as e:
|
|
296
|
+
self.window.core.debug.log(e)
|
|
297
|
+
return
|
|
298
|
+
|
|
299
|
+
copied = 0
|
|
300
|
+
|
|
301
|
+
def unique_dest(dest_path: str) -> str:
|
|
302
|
+
if not os.path.exists(dest_path):
|
|
303
|
+
return dest_path
|
|
304
|
+
base_dir = os.path.dirname(dest_path)
|
|
305
|
+
name = os.path.basename(dest_path)
|
|
306
|
+
new_name = self.make_ts_prefix() + "_" + name
|
|
307
|
+
return os.path.join(base_dir, new_name)
|
|
308
|
+
|
|
309
|
+
for src in paths:
|
|
310
|
+
try:
|
|
311
|
+
if not src or not os.path.exists(src):
|
|
312
|
+
continue
|
|
313
|
+
|
|
314
|
+
# Prevent copying a directory into itself or its subdirectory
|
|
315
|
+
try:
|
|
316
|
+
if os.path.isdir(src):
|
|
317
|
+
common = os.path.commonpath([os.path.abspath(src), os.path.abspath(target_directory)])
|
|
318
|
+
if common == os.path.abspath(src):
|
|
319
|
+
# target is inside src; skip
|
|
320
|
+
self.window.core.debug.log(f"Skipped copying directory into itself: {src} -> {target_directory}")
|
|
321
|
+
continue
|
|
322
|
+
except Exception:
|
|
323
|
+
pass
|
|
324
|
+
|
|
325
|
+
dest_base = os.path.join(target_directory, os.path.basename(src))
|
|
326
|
+
dest_path = unique_dest(dest_base)
|
|
327
|
+
|
|
328
|
+
if os.path.isdir(src):
|
|
329
|
+
shutil.copytree(src, dest_path)
|
|
330
|
+
copied += 1
|
|
331
|
+
else:
|
|
332
|
+
copy2(src, dest_path)
|
|
333
|
+
copied += 1
|
|
334
|
+
except Exception as e:
|
|
335
|
+
self.window.core.debug.log(e)
|
|
336
|
+
print(f"Error uploading path {src}: {e}")
|
|
337
|
+
|
|
338
|
+
if copied > 0:
|
|
339
|
+
self.window.update_status(f"[OK] Uploaded: {copied} files.")
|
|
340
|
+
self.update_explorer()
|
|
341
|
+
|
|
273
342
|
def rename(self, path: str):
|
|
274
343
|
"""
|
|
275
344
|
Rename file or directory
|
|
@@ -480,4 +549,4 @@ class Files:
|
|
|
480
549
|
|
|
481
550
|
def reload(self):
|
|
482
551
|
"""Reload files"""
|
|
483
|
-
self.update_explorer(reload=True)
|
|
552
|
+
self.update_explorer(reload=True)
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date: 2025.
|
|
9
|
+
# Updated Date: 2025.12.25 20:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from typing import Any
|
|
@@ -30,6 +30,15 @@ class Media:
|
|
|
30
30
|
else:
|
|
31
31
|
self.window.ui.config['global']['img_raw'].setChecked(False)
|
|
32
32
|
|
|
33
|
+
# mode (image|video|music)
|
|
34
|
+
mode = self.window.core.config.get('img_mode', 'image')
|
|
35
|
+
self.window.controller.config.apply_value(
|
|
36
|
+
parent_id="global",
|
|
37
|
+
key="img_mode",
|
|
38
|
+
option=self.window.core.image.get_mode_option(),
|
|
39
|
+
value=mode,
|
|
40
|
+
)
|
|
41
|
+
|
|
33
42
|
# image: resolution
|
|
34
43
|
resolution = self.window.core.config.get('img_resolution', '1024x1024')
|
|
35
44
|
self.window.controller.config.apply_value(
|
|
@@ -51,6 +60,7 @@ class Media:
|
|
|
51
60
|
# -- add hooks --
|
|
52
61
|
if not self.initialized:
|
|
53
62
|
self.window.ui.add_hook("update.global.img_resolution", self.hook_update)
|
|
63
|
+
self.window.ui.add_hook("update.global.img_mode", self.hook_update)
|
|
54
64
|
self.window.ui.add_hook("update.global.video.aspect_ratio", self.hook_update)
|
|
55
65
|
|
|
56
66
|
def reload(self):
|
|
@@ -69,6 +79,11 @@ class Media:
|
|
|
69
79
|
if not value:
|
|
70
80
|
return
|
|
71
81
|
self.window.core.config.set('img_resolution', value)
|
|
82
|
+
elif key == "img_mode":
|
|
83
|
+
if not value:
|
|
84
|
+
return
|
|
85
|
+
self.window.core.config.set('img_mode', value)
|
|
86
|
+
self.window.controller.ui.mode.update() # switch image|video options
|
|
72
87
|
elif key == "video.aspect_ratio":
|
|
73
88
|
if not value:
|
|
74
89
|
return
|
|
@@ -92,6 +107,10 @@ class Media:
|
|
|
92
107
|
else:
|
|
93
108
|
self.enable_raw()
|
|
94
109
|
|
|
110
|
+
def get_mode(self) -> str:
|
|
111
|
+
"""Get media generation mode (image/video/music)"""
|
|
112
|
+
return self.window.core.config.get("img_mode", "image")
|
|
113
|
+
|
|
95
114
|
def is_image_model(self) -> bool:
|
|
96
115
|
"""
|
|
97
116
|
Check if the model is an image generation model
|