mirror of
https://github.com/Heretek-AI/Asset-Manager.git
synced 2026-11-07 10:45:57 -10:00
dock thumbnails: preview pane, Bearer auth fix, headless panel test
This commit is contained in:
1 parent
89b3b4eed5
commit
745bdc9e73
3 files changed
+150
-15
No files matched your search
@@ -3,11 +3,20 @@ extends VBoxContainer
|
||||
## Dock panel: search the remote library, download into res://.
|
||||
|
||||
const CONFIG_PATH := "user://asset_library.cfg"
|
||||
const Client := preload("res://addons/asset_library/asset_client.gd")
|
||||
|
||||
var _items: Array = []
|
||||
var _http: HTTPRequest
|
||||
var _dl: HTTPRequest
|
||||
var _dl_name := ""
|
||||
var _thumb: HTTPRequest
|
||||
var _dl_name: String = ""
|
||||
var _url_field: LineEdit
|
||||
var _token_field: LineEdit
|
||||
var _query_field: LineEdit
|
||||
var _go_button: Button
|
||||
var _results: ItemList
|
||||
var _preview: TextureRect
|
||||
var _download_button: Button
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
@@ -17,38 +26,52 @@ func _ready() -> void:
|
||||
_dl = HTTPRequest.new()
|
||||
add_child(_dl)
|
||||
_dl.request_completed.connect(_on_blob_done)
|
||||
%Go.pressed.connect(_on_search)
|
||||
%Download.pressed.connect(_on_download)
|
||||
_thumb = HTTPRequest.new()
|
||||
add_child(_thumb)
|
||||
_thumb.request_completed.connect(_on_thumb_done)
|
||||
# Plain relative paths, not %-unique names: % resolution depends on
|
||||
# scene-owner bookkeeping that is absent when the panel is instanced
|
||||
# outside the editor (headless probes, runtime tools).
|
||||
_url_field = get_node("URL") as LineEdit
|
||||
_token_field = get_node("Token") as LineEdit
|
||||
_query_field = get_node("SearchRow/Query") as LineEdit
|
||||
_go_button = get_node("SearchRow/Go") as Button
|
||||
_results = get_node("Results") as ItemList
|
||||
_preview = get_node("Preview") as TextureRect
|
||||
_download_button = get_node("Download") as Button
|
||||
_go_button.pressed.connect(_on_search)
|
||||
_results.item_selected.connect(_on_preview_index)
|
||||
_download_button.pressed.connect(_on_download)
|
||||
_load_config()
|
||||
|
||||
|
||||
func _load_config() -> void:
|
||||
var cfg := ConfigFile.new()
|
||||
if cfg.load(CONFIG_PATH) == OK:
|
||||
%URL.text = str(cfg.get_value("server", "url", ""))
|
||||
%Token.text = str(cfg.get_value("server", "token", ""))
|
||||
_url_field.text = str(cfg.get_value("server", "url", ""))
|
||||
_token_field.text = str(cfg.get_value("server", "token", ""))
|
||||
|
||||
|
||||
func _save_config() -> void:
|
||||
var cfg := ConfigFile.new()
|
||||
cfg.set_value("server", "url", %URL.text)
|
||||
cfg.set_value("server", "token", %Token.text)
|
||||
cfg.set_value("server", "url", _url_field.text)
|
||||
cfg.set_value("server", "token", _token_field.text)
|
||||
cfg.save(CONFIG_PATH)
|
||||
|
||||
|
||||
func _api(path: String) -> String:
|
||||
return %URL.text.strip_edges().trim_suffix("/") + path
|
||||
return _url_field.text.strip_edges().trim_suffix("/") + path
|
||||
|
||||
|
||||
func _auth() -> PackedStringArray:
|
||||
return PackedStringArray(
|
||||
["Authorization: Bearer " + %Token.text])
|
||||
["Authorization: Bearer " + _token_field.text])
|
||||
|
||||
|
||||
func _on_search() -> void:
|
||||
_save_config()
|
||||
var url := AssetLibraryClient.search_url(
|
||||
_api("/api/v1/assets"), {"q": %Query.text})
|
||||
var url := Client.search_url(
|
||||
_api("/api/v1/assets"), {"q": _query_field.text})
|
||||
if _http.request(url, _auth()) != OK:
|
||||
push_error("asset-library: search request failed to start")
|
||||
|
||||
@@ -58,14 +81,14 @@ func _on_list_done(result: int, _code: int, _headers: PackedStringArray,
|
||||
if result != HTTPRequest.RESULT_SUCCESS:
|
||||
push_error("asset-library: search failed")
|
||||
return
|
||||
_items = AssetLibraryClient.parse_assets(body.get_string_from_utf8())
|
||||
%Results.clear()
|
||||
_items = Client.parse_assets(body.get_string_from_utf8())
|
||||
_results.clear()
|
||||
for item: Variant in _items:
|
||||
%Results.add_item(str((item as Dictionary).get("name", "?")))
|
||||
_results.add_item(str((item as Dictionary).get("name", "?")))
|
||||
|
||||
|
||||
func _on_download() -> void:
|
||||
var selected: PackedInt32Array = %Results.get_selected_items()
|
||||
var selected: PackedInt32Array = _results.get_selected_items()
|
||||
if selected.is_empty():
|
||||
return
|
||||
var item: Dictionary = _items[selected[0]]
|
||||
@@ -75,6 +98,28 @@ func _on_download() -> void:
|
||||
push_error("asset-library: download request failed to start")
|
||||
|
||||
|
||||
func _on_preview_index(index: int) -> void:
|
||||
if index < 0 or index >= _items.size():
|
||||
return
|
||||
var item: Dictionary = _items[index]
|
||||
var url := _api("/api/v1/assets/%d/thumbnail" % int(item.get("id", 0)))
|
||||
if _thumb.request(url, _auth()) != OK:
|
||||
push_error("asset-library: thumbnail request failed to start")
|
||||
|
||||
|
||||
func _on_thumb_done(result: int, code: int, _headers: PackedStringArray,
|
||||
body: PackedByteArray) -> void:
|
||||
if result != HTTPRequest.RESULT_SUCCESS or code != 200:
|
||||
_preview.texture = null
|
||||
return
|
||||
var image := Image.new()
|
||||
if image.load_png_from_buffer(body) != OK:
|
||||
push_warning("asset-library: thumbnail is not a PNG")
|
||||
_preview.texture = null
|
||||
return
|
||||
_preview.texture = ImageTexture.create_from_image(image)
|
||||
|
||||
|
||||
func _on_blob_done(result: int, code: int, _headers: PackedStringArray,
|
||||
body: PackedByteArray) -> void:
|
||||
if result != HTTPRequest.RESULT_SUCCESS or code != 200:
|
||||
|
||||
@@ -24,5 +24,10 @@ text = "Search"
|
||||
[node name="Results" type="ItemList" parent="." unique_name_in_owner=true]
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Preview" type="TextureRect" parent="." unique_name_in_owner=true]
|
||||
custom_minimum_size = Vector2(0, 128)
|
||||
expand_mode = 1
|
||||
stretch_mode = 5
|
||||
|
||||
[node name="Download" type="Button" parent="." unique_name_in_owner=true]
|
||||
text = "Download selected"
|
||||
@@ -211,3 +211,88 @@ def test_full_loop_server_to_imported(tmp_path, godot_bin):
|
||||
got = list((tmp_path / "plugproj").rglob("*.ctex"))
|
||||
assert got, "no imported output produced"
|
||||
assert os.path.isfile(os.path.join(root, "hero.png.import"))
|
||||
|
||||
|
||||
THUMB_PROBE_GD = """\
|
||||
extends SceneTree
|
||||
|
||||
var _panel
|
||||
var _phase := 0
|
||||
var _frames := 0
|
||||
var _base := ""
|
||||
var _token := ""
|
||||
var _want_id := 0
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
\tvar args := OS.get_cmdline_user_args()
|
||||
\t_base = str(args[0])
|
||||
\t_token = str(args[1])
|
||||
\t_want_id = int(args[2])
|
||||
\t_panel = load("res://addons/asset_library/asset_dock.tscn").instantiate()
|
||||
\troot.add_child(_panel)
|
||||
|
||||
|
||||
func _process(_delta: float) -> bool:
|
||||
\t_frames += 1
|
||||
\tif _frames > 900:
|
||||
\t\tprint("E2E_THUMB:timeout")
|
||||
\t\treturn true
|
||||
\tif _phase == 0:
|
||||
\t\tif _panel.get("_http") == null:
|
||||
\t\t\treturn false
|
||||
\t\t_panel.get_node("URL").text = _base
|
||||
\t\t_panel.get_node("Token").text = _token
|
||||
\t\t_panel.get_node("SearchRow/Query").text = ""
|
||||
\t\t_panel._on_search()
|
||||
\t\t_phase = 1
|
||||
\t\treturn false
|
||||
\tif _phase == 1 and _panel._items.size() > 0:
|
||||
\t\tvar first: Dictionary = _panel._items[0]
|
||||
\t\tif int(first.get("id", -1)) == _want_id:
|
||||
\t\t\t_panel._on_preview_index(0)
|
||||
\t\t\t_phase = 2
|
||||
\t\telse:
|
||||
\t\t\tprint("E2E_THUMB:wrong-id")
|
||||
\t\t\treturn true
|
||||
\telif _phase == 2:
|
||||
\t\tvar tex: Texture2D = _panel.get_node("Preview").texture
|
||||
\t\tif tex != null:
|
||||
\t\t\tprint("E2E_THUMB:ok %dx%d" % [tex.get_width(),
|
||||
\t\t\t\ttex.get_height()])
|
||||
\t\t\treturn true
|
||||
\treturn false
|
||||
"""
|
||||
|
||||
|
||||
def test_dock_shows_server_thumbnail(tmp_path, godot_bin):
|
||||
import base64
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from test_library_server import TOKEN, start_server
|
||||
_, base = start_server(tmp_path / "srv")
|
||||
png = base64.b64decode(
|
||||
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==")
|
||||
source = tmp_path / "hero.png"
|
||||
source.write_bytes(png)
|
||||
root = make_plugin_project(tmp_path)
|
||||
probe = os.path.join(root, "thumb_probe.gd")
|
||||
with open(probe, "w", encoding="utf-8") as handle:
|
||||
handle.write(THUMB_PROBE_GD)
|
||||
# publish through the real CLI so importer metadata travels too
|
||||
done = subprocess.run(
|
||||
[sys.executable, "-m", "assetmgr.cli", "library-push",
|
||||
str(source), "--server", base, "--token", TOKEN,
|
||||
"--kind", "texture", "--importer", "texture", "--json"],
|
||||
capture_output=True, text=True, timeout=120)
|
||||
assert done.returncode == 0, done.stderr[-1000:]
|
||||
asset_id = json.loads(done.stdout)["id"]
|
||||
run = subprocess.run(
|
||||
[godot_bin, "--headless", "--path", root,
|
||||
"--script", probe, "--",
|
||||
base, TOKEN, str(asset_id)],
|
||||
capture_output=True, text=True, timeout=120)
|
||||
assert run.returncode == 0, run.stderr[-2000:] + run.stdout[-1000:]
|
||||
assert "E2E_THUMB:ok" in run.stdout, run.stdout
|
||||
Reference in new issue
Block a user