diff --git a/plugins/cmd/hostgen/README.md b/plugins/cmd/hostgen/README.md index 0a59c5325..1bbf748f5 100644 --- a/plugins/cmd/hostgen/README.md +++ b/plugins/cmd/hostgen/README.md @@ -248,16 +248,36 @@ def cache_get_string(key: str) -> CacheGetStringResult: ... ``` -#### Example Python Plugin Usage +#### Python Plugin Usage + +> **Important:** Due to a limitation in extism-py, you cannot directly import the generated Python wrappers. +> The `@extism.import_fn` decorators are only detected when defined in the plugin's main `__init__.py` file. +> Generated Python files serve as **reference/template code** - copy the needed functions into your plugin. + +Example of copying the generated wrapper into your plugin's `__init__.py`: ```python -from nd_host_subsonicapi import subsonicapi_call, HostFunctionError +import extism +import json -try: - response = subsonicapi_call("getAlbumList2?type=random&size=10") - data = json.loads(response) -except HostFunctionError as e: - print(f"API error: {e}") +# Copy host function declarations from generated files into your __init__.py +@extism.import_fn("extism:host/user", "subsonicapi_call") +def _host_subsonicapi_call(input_ptr: extism.JsonI64) -> extism.JsonI64: + pass + +def subsonicapi_call(endpoint: str) -> str: + """Call the SubsonicAPI with the given endpoint.""" + result = _host_subsonicapi_call(endpoint) + return result + +# Now use it in your plugin +@extism.plugin_fn +def my_plugin_function(): + try: + response = subsonicapi_call("getAlbumList2?type=random&size=10") + data = json.loads(response) + except Exception as e: + extism.log(extism.LogLevel.Error, f"API error: {e}") ``` ## Troubleshooting diff --git a/plugins/cmd/hostgen/internal/templates/client_py.py.tmpl b/plugins/cmd/hostgen/internal/templates/client_py.py.tmpl index 8ae577e0e..4ad6ae01e 100644 --- a/plugins/cmd/hostgen/internal/templates/client_py.py.tmpl +++ b/plugins/cmd/hostgen/internal/templates/client_py.py.tmpl @@ -3,8 +3,9 @@ # This file contains client wrappers for the {{.Service.Name}} host service. # It is intended for use in Navidrome plugins built with extism-py. # -# Usage: -# from nd_host_{{.Service.Name | lower}} import {{range $i, $m := .Service.Methods}}{{if $i}}, {{end}}{{pythonFunc $m}}{{end}} +# IMPORTANT: Due to a limitation in extism-py, you cannot import this file directly. +# The @extism.import_fn decorators are only detected when defined in the plugin's +# main __init__.py file. Copy the needed functions from this file into your plugin. from dataclasses import dataclass from typing import Any diff --git a/plugins/cmd/hostgen/testdata/codec_client_expected.py b/plugins/cmd/hostgen/testdata/codec_client_expected.py index 6e17a684a..5521f1bbf 100644 --- a/plugins/cmd/hostgen/testdata/codec_client_expected.py +++ b/plugins/cmd/hostgen/testdata/codec_client_expected.py @@ -3,8 +3,9 @@ # This file contains client wrappers for the Codec host service. # It is intended for use in Navidrome plugins built with extism-py. # -# Usage: -# from nd_host_codec import codec_encode +# IMPORTANT: Due to a limitation in extism-py, you cannot import this file directly. +# The @extism.import_fn decorators are only detected when defined in the plugin's +# main __init__.py file. Copy the needed functions from this file into your plugin. from dataclasses import dataclass from typing import Any diff --git a/plugins/cmd/hostgen/testdata/counter_client_expected.py b/plugins/cmd/hostgen/testdata/counter_client_expected.py index 4bce404a3..6bdbc61dc 100644 --- a/plugins/cmd/hostgen/testdata/counter_client_expected.py +++ b/plugins/cmd/hostgen/testdata/counter_client_expected.py @@ -3,8 +3,9 @@ # This file contains client wrappers for the Counter host service. # It is intended for use in Navidrome plugins built with extism-py. # -# Usage: -# from nd_host_counter import counter_count +# IMPORTANT: Due to a limitation in extism-py, you cannot import this file directly. +# The @extism.import_fn decorators are only detected when defined in the plugin's +# main __init__.py file. Copy the needed functions from this file into your plugin. from dataclasses import dataclass from typing import Any diff --git a/plugins/cmd/hostgen/testdata/echo_client_expected.py b/plugins/cmd/hostgen/testdata/echo_client_expected.py index 6de0d2c21..d3144c347 100644 --- a/plugins/cmd/hostgen/testdata/echo_client_expected.py +++ b/plugins/cmd/hostgen/testdata/echo_client_expected.py @@ -3,8 +3,9 @@ # This file contains client wrappers for the Echo host service. # It is intended for use in Navidrome plugins built with extism-py. # -# Usage: -# from nd_host_echo import echo_echo +# IMPORTANT: Due to a limitation in extism-py, you cannot import this file directly. +# The @extism.import_fn decorators are only detected when defined in the plugin's +# main __init__.py file. Copy the needed functions from this file into your plugin. from dataclasses import dataclass from typing import Any diff --git a/plugins/cmd/hostgen/testdata/list_client_expected.py b/plugins/cmd/hostgen/testdata/list_client_expected.py index 0be0e2a76..3a19ac476 100644 --- a/plugins/cmd/hostgen/testdata/list_client_expected.py +++ b/plugins/cmd/hostgen/testdata/list_client_expected.py @@ -3,8 +3,9 @@ # This file contains client wrappers for the List host service. # It is intended for use in Navidrome plugins built with extism-py. # -# Usage: -# from nd_host_list import list_items +# IMPORTANT: Due to a limitation in extism-py, you cannot import this file directly. +# The @extism.import_fn decorators are only detected when defined in the plugin's +# main __init__.py file. Copy the needed functions from this file into your plugin. from dataclasses import dataclass from typing import Any diff --git a/plugins/cmd/hostgen/testdata/math_client_expected.py b/plugins/cmd/hostgen/testdata/math_client_expected.py index dfaa895a2..e714fbc02 100644 --- a/plugins/cmd/hostgen/testdata/math_client_expected.py +++ b/plugins/cmd/hostgen/testdata/math_client_expected.py @@ -3,8 +3,9 @@ # This file contains client wrappers for the Math host service. # It is intended for use in Navidrome plugins built with extism-py. # -# Usage: -# from nd_host_math import math_add +# IMPORTANT: Due to a limitation in extism-py, you cannot import this file directly. +# The @extism.import_fn decorators are only detected when defined in the plugin's +# main __init__.py file. Copy the needed functions from this file into your plugin. from dataclasses import dataclass from typing import Any diff --git a/plugins/cmd/hostgen/testdata/meta_client_expected.py b/plugins/cmd/hostgen/testdata/meta_client_expected.py index f900d0811..e132fb334 100644 --- a/plugins/cmd/hostgen/testdata/meta_client_expected.py +++ b/plugins/cmd/hostgen/testdata/meta_client_expected.py @@ -3,8 +3,9 @@ # This file contains client wrappers for the Meta host service. # It is intended for use in Navidrome plugins built with extism-py. # -# Usage: -# from nd_host_meta import meta_get, meta_set +# IMPORTANT: Due to a limitation in extism-py, you cannot import this file directly. +# The @extism.import_fn decorators are only detected when defined in the plugin's +# main __init__.py file. Copy the needed functions from this file into your plugin. from dataclasses import dataclass from typing import Any diff --git a/plugins/cmd/hostgen/testdata/ping_client_expected.py b/plugins/cmd/hostgen/testdata/ping_client_expected.py index 503892778..492cc436c 100644 --- a/plugins/cmd/hostgen/testdata/ping_client_expected.py +++ b/plugins/cmd/hostgen/testdata/ping_client_expected.py @@ -3,8 +3,9 @@ # This file contains client wrappers for the Ping host service. # It is intended for use in Navidrome plugins built with extism-py. # -# Usage: -# from nd_host_ping import ping_ping +# IMPORTANT: Due to a limitation in extism-py, you cannot import this file directly. +# The @extism.import_fn decorators are only detected when defined in the plugin's +# main __init__.py file. Copy the needed functions from this file into your plugin. from dataclasses import dataclass from typing import Any diff --git a/plugins/cmd/hostgen/testdata/search_client_expected.py b/plugins/cmd/hostgen/testdata/search_client_expected.py index 9befe7ab4..87cb3f9cd 100644 --- a/plugins/cmd/hostgen/testdata/search_client_expected.py +++ b/plugins/cmd/hostgen/testdata/search_client_expected.py @@ -3,8 +3,9 @@ # This file contains client wrappers for the Search host service. # It is intended for use in Navidrome plugins built with extism-py. # -# Usage: -# from nd_host_search import search_find +# IMPORTANT: Due to a limitation in extism-py, you cannot import this file directly. +# The @extism.import_fn decorators are only detected when defined in the plugin's +# main __init__.py file. Copy the needed functions from this file into your plugin. from dataclasses import dataclass from typing import Any diff --git a/plugins/cmd/hostgen/testdata/store_client_expected.py b/plugins/cmd/hostgen/testdata/store_client_expected.py index fd8ca5e40..5f1e6a2b0 100644 --- a/plugins/cmd/hostgen/testdata/store_client_expected.py +++ b/plugins/cmd/hostgen/testdata/store_client_expected.py @@ -3,8 +3,9 @@ # This file contains client wrappers for the Store host service. # It is intended for use in Navidrome plugins built with extism-py. # -# Usage: -# from nd_host_store import store_save +# IMPORTANT: Due to a limitation in extism-py, you cannot import this file directly. +# The @extism.import_fn decorators are only detected when defined in the plugin's +# main __init__.py file. Copy the needed functions from this file into your plugin. from dataclasses import dataclass from typing import Any diff --git a/plugins/cmd/hostgen/testdata/users_client_expected.py b/plugins/cmd/hostgen/testdata/users_client_expected.py index 67927d950..c89abb134 100644 --- a/plugins/cmd/hostgen/testdata/users_client_expected.py +++ b/plugins/cmd/hostgen/testdata/users_client_expected.py @@ -3,8 +3,9 @@ # This file contains client wrappers for the Users host service. # It is intended for use in Navidrome plugins built with extism-py. # -# Usage: -# from nd_host_users import users_get +# IMPORTANT: Due to a limitation in extism-py, you cannot import this file directly. +# The @extism.import_fn decorators are only detected when defined in the plugin's +# main __init__.py file. Copy the needed functions from this file into your plugin. from dataclasses import dataclass from typing import Any diff --git a/plugins/examples/Makefile b/plugins/examples/Makefile index d50c4b94a..8dc5cd3ea 100644 --- a/plugins/examples/Makefile +++ b/plugins/examples/Makefile @@ -43,8 +43,10 @@ else endif # Python plugin builds (generic rule for any folder with plugin/__init__.py) -$(PYTHON_PLUGINS:%=%.wasm): %.wasm: %/plugin/__init__.py +# Use secondary expansion to get all .py files in the plugin directory as dependencies +.SECONDEXPANSION: +$(PYTHON_PLUGINS:%=%.wasm): %.wasm: $$(wildcard %/plugin/*.py) ifndef EXTISM_PY $(error extism-py is not installed. Install from https://github.com/extism/python-pdk) endif - cd $* && extism-py plugin/__init__.py -o ../$@ + cd $* && PYTHONPATH=plugin extism-py plugin/__init__.py -o ../$@ diff --git a/plugins/examples/nowplaying-py/Makefile b/plugins/examples/nowplaying-py/Makefile index 1c56a2a60..2bf6ea971 100644 --- a/plugins/examples/nowplaying-py/Makefile +++ b/plugins/examples/nowplaying-py/Makefile @@ -5,8 +5,8 @@ WASM_FILE = nowplaying-py.wasm build: $(WASM_FILE) -$(WASM_FILE): plugin/__init__.py plugin/nd_host_scheduler.py plugin/nd_host_subsonicapi.py - PYTHONPATH=plugin extism-py plugin/__init__.py -o $(WASM_FILE) +$(WASM_FILE): plugin/__init__.py + extism-py plugin/__init__.py -o $(WASM_FILE) clean: rm -f $(WASM_FILE) diff --git a/plugins/examples/nowplaying-py/README.md b/plugins/examples/nowplaying-py/README.md index 4db1b8f8b..725258498 100644 --- a/plugins/examples/nowplaying-py/README.md +++ b/plugins/examples/nowplaying-py/README.md @@ -4,10 +4,10 @@ A Python example plugin that demonstrates the **Scheduler** and **SubsonicAPI** ## Features -- Uses `scheduler_schedule_recurring` host function to set up a recurring task +- Uses `scheduler_schedulerecurring` host function to set up a recurring task - Uses `subsonicapi_call` host function to query the `getNowPlaying` API - Configurable cron expression and user via plugin config -- Uses generated Python host function wrappers from `plugins/host/python/` +- Demonstrates Python host function imports using `@extism.import_fn` ## Prerequisites @@ -29,7 +29,7 @@ make nowplaying-py.wasm Or directly: ```bash -PYTHONPATH=plugin extism-py plugin/__init__.py -o nowplaying-py.wasm +extism-py plugin/__init__.py -o nowplaying-py.wasm ``` ## Installation @@ -86,27 +86,31 @@ Or when no one is playing: 2. **Callback (`nd_scheduler_callback`)**: When the scheduled task fires, calls the SubsonicAPI `getNowPlaying` endpoint and logs the results. -## Using Generated Host Function Wrappers +## Host Function Usage (Python) -This plugin uses the generated Python host function wrappers from `plugins/host/python/`. These wrappers are generated by `hostgen` and provide type-safe, Pythonic interfaces to Navidrome host functions: +This plugin demonstrates how to call Navidrome host functions from Python: ```python -# Import generated host function wrappers -from nd_host_scheduler import scheduler_schedule_recurring, HostFunctionError -from nd_host_subsonicapi import subsonicapi_call +import extism +import json -# Use them directly - no manual JSON marshalling needed! -schedule_id = scheduler_schedule_recurring( - cron_expression="*/1 * * * *", - payload="check", - schedule_i_d="nowplaying-check" -) +# Import the host function +@extism.import_fn("extism:host/user", "subsonicapi_call") +def _subsonicapi_call(offset: int) -> int: + """Raw host function - returns memory offset.""" + ... -response_json = subsonicapi_call(uri="getNowPlaying") -``` - -The wrappers handle: -- JSON marshalling/unmarshalling -- Memory allocation and management -- Error handling (raises `HostFunctionError` on failure) -- Type hints for better IDE support \ No newline at end of file +# Wrapper for JSON marshalling +def subsonicapi_call(uri: str) -> dict: + request = {"uri": uri} + request_bytes = json.dumps(request).encode('utf-8') + request_mem = extism.memory.alloc(request_bytes) + response_offset = _subsonicapi_call(request_mem.offset) + response_mem = extism.memory.find(response_offset) + response = json.loads(extism.memory.string(response_mem)) + + if response.get("error"): + raise Exception(response["error"]) + + return json.loads(response.get("responseJSON", "{}")) +``` \ No newline at end of file diff --git a/plugins/examples/nowplaying-py/plugin/__init__.py b/plugins/examples/nowplaying-py/plugin/__init__.py index bfd20d964..702bc0da0 100644 --- a/plugins/examples/nowplaying-py/plugin/__init__.py +++ b/plugins/examples/nowplaying-py/plugin/__init__.py @@ -17,28 +17,86 @@ import extism import json -# Import generated host function wrappers -from nd_host_scheduler import scheduler_schedule_recurring, HostFunctionError -from nd_host_subsonicapi import subsonicapi_call as _subsonicapi_call_raw - # Schedule ID for our recurring task SCHEDULE_ID = "nowplaying-check" +# ============================================================================= +# Host Function Imports +# ============================================================================= +# These are custom host functions provided by Navidrome. +# We import them using the extism:host/user namespace. + + +@extism.import_fn("extism:host/user", "scheduler_schedulerecurring") +def _scheduler_schedulerecurring(offset: int) -> int: + """Raw host function - do not call directly.""" + ... + + +@extism.import_fn("extism:host/user", "subsonicapi_call") +def _subsonicapi_call(offset: int) -> int: + """Raw host function - do not call directly.""" + ... + + +# ============================================================================= +# Host Function Wrappers +# ============================================================================= +# These wrappers handle JSON marshalling/unmarshalling and memory management. +# They were copied from plugins/host/python due to extism-py limitations. + + +def scheduler_schedule_recurring(cron_expression: str, payload: str, schedule_id: str) -> str: + """Schedule a recurring task using a cron expression. + + Args: + cron_expression: Cron format (e.g., "*/1 * * * *" for every minute) + payload: Data to pass to the callback + schedule_id: Unique identifier for the schedule + + Returns: + The schedule ID (same as input or auto-generated) + """ + request = { + "cronExpression": cron_expression, + "payload": payload, + "scheduleID": schedule_id + } + request_bytes = json.dumps(request).encode('utf-8') + request_mem = extism.memory.alloc(request_bytes) + response_offset = _scheduler_schedulerecurring(request_mem.offset) + response_mem = extism.memory.find(response_offset) + response = json.loads(extism.memory.string(response_mem)) + + if response.get("error"): + raise Exception(response["error"]) + + return response.get("newScheduleID", schedule_id) + + def subsonicapi_call(uri: str) -> dict: - """Call a Subsonic API endpoint and parse the response. - - This is a convenience wrapper around the generated subsonicapi_call - that parses the JSON response string into a dict. - + """Call a Subsonic API endpoint. + Args: uri: API path (e.g., "getNowPlaying") - + Returns: Parsed JSON response from the API """ - response_json = _subsonicapi_call_raw(uri) - return json.loads(response_json) if response_json else {} + request = {"uri": uri} + request_bytes = json.dumps(request).encode('utf-8') + request_mem = extism.memory.alloc(request_bytes) + response_offset = _subsonicapi_call(request_mem.offset) + response_mem = extism.memory.find(response_offset) + response = json.loads(extism.memory.string(response_mem)) + + if response.get("error"): + raise Exception(response["error"]) + + # Parse the nested JSON response + response_json = response.get("responseJSON", "{}") + return json.loads(response_json) # ============================================================================= diff --git a/plugins/examples/nowplaying-py/plugin/nd_host_scheduler.py b/plugins/examples/nowplaying-py/plugin/nd_host_scheduler.py deleted file mode 100644 index f7b8e320b..000000000 --- a/plugins/examples/nowplaying-py/plugin/nd_host_scheduler.py +++ /dev/null @@ -1,142 +0,0 @@ -# Code generated by hostgen. DO NOT EDIT. -# -# This file contains client wrappers for the Scheduler host service. -# It is intended for use in Navidrome plugins built with extism-py. -# -# Usage: -# from nd_host_scheduler import scheduler_schedule_one_time, scheduler_schedule_recurring, scheduler_cancel_schedule - -from dataclasses import dataclass -from typing import Any - -import extism -import json - - -class HostFunctionError(Exception): - """Raised when a host function returns an error.""" - pass - - -@extism.import_fn("extism:host/user", "scheduler_scheduleonetime") -def _scheduler_scheduleonetime(offset: int) -> int: - """Raw host function - do not call directly.""" - ... - - -@extism.import_fn("extism:host/user", "scheduler_schedulerecurring") -def _scheduler_schedulerecurring(offset: int) -> int: - """Raw host function - do not call directly.""" - ... - - -@extism.import_fn("extism:host/user", "scheduler_cancelschedule") -def _scheduler_cancelschedule(offset: int) -> int: - """Raw host function - do not call directly.""" - ... - - -def scheduler_schedule_one_time(delay_seconds: int, payload: str, schedule_i_d: str) -> str: - """ScheduleOneTime schedules a one-time event to be triggered after the specified delay. -Plugins that use this function must also implement the SchedulerCallback capability - -Parameters: - - delaySeconds: Number of seconds to wait before triggering the event - - payload: Data to be passed to the scheduled event handler - - scheduleID: Optional unique identifier for the scheduled job. If empty, one will be generated - -Returns the schedule ID that can be used to cancel the job, or an error if scheduling fails. - - Args: - delay_seconds: int parameter. - payload: str parameter. - schedule_i_d: str parameter. - - Returns: - str: The result value. - - Raises: - HostFunctionError: If the host function returns an error. - """ - request = { - "delaySeconds": delay_seconds, - "payload": payload, - "scheduleID": schedule_i_d, - } - request_bytes = json.dumps(request).encode("utf-8") - request_mem = extism.memory.alloc(request_bytes) - response_offset = _scheduler_scheduleonetime(request_mem.offset) - response_mem = extism.memory.find(response_offset) - response = json.loads(extism.memory.string(response_mem)) - - if response.get("error"): - raise HostFunctionError(response["error"]) - - return response.get("newScheduleID", "") - - -def scheduler_schedule_recurring(cron_expression: str, payload: str, schedule_i_d: str) -> str: - """ScheduleRecurring schedules a recurring event using a cron expression. -Plugins that use this function must also implement the SchedulerCallback capability - -Parameters: - - cronExpression: Standard cron format expression (e.g., "0 0 * * *" for daily at midnight) - - payload: Data to be passed to each scheduled event handler invocation - - scheduleID: Optional unique identifier for the scheduled job. If empty, one will be generated - -Returns the schedule ID that can be used to cancel the job, or an error if scheduling fails. - - Args: - cron_expression: str parameter. - payload: str parameter. - schedule_i_d: str parameter. - - Returns: - str: The result value. - - Raises: - HostFunctionError: If the host function returns an error. - """ - request = { - "cronExpression": cron_expression, - "payload": payload, - "scheduleID": schedule_i_d, - } - request_bytes = json.dumps(request).encode("utf-8") - request_mem = extism.memory.alloc(request_bytes) - response_offset = _scheduler_schedulerecurring(request_mem.offset) - response_mem = extism.memory.find(response_offset) - response = json.loads(extism.memory.string(response_mem)) - - if response.get("error"): - raise HostFunctionError(response["error"]) - - return response.get("newScheduleID", "") - - -def scheduler_cancel_schedule(schedule_i_d: str) -> None: - """CancelSchedule cancels a scheduled job identified by its schedule ID. - -This works for both one-time and recurring schedules. Once cancelled, the job will not trigger -any future events. - -Returns an error if the schedule ID is not found or if cancellation fails. - - Args: - schedule_i_d: str parameter. - - Raises: - HostFunctionError: If the host function returns an error. - """ - request = { - "scheduleID": schedule_i_d, - } - request_bytes = json.dumps(request).encode("utf-8") - request_mem = extism.memory.alloc(request_bytes) - response_offset = _scheduler_cancelschedule(request_mem.offset) - response_mem = extism.memory.find(response_offset) - response = json.loads(extism.memory.string(response_mem)) - - if response.get("error"): - raise HostFunctionError(response["error"]) - diff --git a/plugins/examples/nowplaying-py/plugin/nd_host_subsonicapi.py b/plugins/examples/nowplaying-py/plugin/nd_host_subsonicapi.py deleted file mode 100644 index baabad0ec..000000000 --- a/plugins/examples/nowplaying-py/plugin/nd_host_subsonicapi.py +++ /dev/null @@ -1,54 +0,0 @@ -# Code generated by hostgen. DO NOT EDIT. -# -# This file contains client wrappers for the SubsonicAPI host service. -# It is intended for use in Navidrome plugins built with extism-py. -# -# Usage: -# from nd_host_subsonicapi import subsonicapi_call - -from dataclasses import dataclass -from typing import Any - -import extism -import json - - -class HostFunctionError(Exception): - """Raised when a host function returns an error.""" - pass - - -@extism.import_fn("extism:host/user", "subsonicapi_call") -def _subsonicapi_call(offset: int) -> int: - """Raw host function - do not call directly.""" - ... - - -def subsonicapi_call(uri: str) -> str: - """Call executes a Subsonic API request and returns the JSON response. - -The uri parameter should be the Subsonic API path without the server prefix, -e.g., "getAlbumList2?type=random&size=10". The response is returned as raw JSON. - - Args: - uri: str parameter. - - Returns: - str: The result value. - - Raises: - HostFunctionError: If the host function returns an error. - """ - request = { - "uri": uri, - } - request_bytes = json.dumps(request).encode("utf-8") - request_mem = extism.memory.alloc(request_bytes) - response_offset = _subsonicapi_call(request_mem.offset) - response_mem = extism.memory.find(response_offset) - response = json.loads(extism.memory.string(response_mem)) - - if response.get("error"): - raise HostFunctionError(response["error"]) - - return response.get("responseJSON", "") diff --git a/plugins/host/doc.go b/plugins/host/doc.go index 514a6e9a7..39fadd1ec 100644 --- a/plugins/host/doc.go +++ b/plugins/host/doc.go @@ -37,5 +37,5 @@ // Generated files follow the pattern _gen.go and include a header comment // indicating they should not be edited manually. // -//go:generate go run ../cmd/hostgen -input=. -output=. +//go:generate go run ../cmd/hostgen -input=. -output=. -python -go package host diff --git a/plugins/host/python/nd_host_artwork.py b/plugins/host/python/nd_host_artwork.py index a41762cee..3919dbbe9 100644 --- a/plugins/host/python/nd_host_artwork.py +++ b/plugins/host/python/nd_host_artwork.py @@ -3,8 +3,9 @@ # This file contains client wrappers for the Artwork host service. # It is intended for use in Navidrome plugins built with extism-py. # -# Usage: -# from nd_host_artwork import artwork_get_artist_url, artwork_get_album_url, artwork_get_track_url, artwork_get_playlist_url +# IMPORTANT: Due to a limitation in extism-py, you cannot import this file directly. +# The @extism.import_fn decorators are only detected when defined in the plugin's +# main __init__.py file. Copy the needed functions from this file into your plugin. from dataclasses import dataclass from typing import Any diff --git a/plugins/host/python/nd_host_cache.py b/plugins/host/python/nd_host_cache.py index 8a91cfea9..f06b2ad0c 100644 --- a/plugins/host/python/nd_host_cache.py +++ b/plugins/host/python/nd_host_cache.py @@ -3,8 +3,9 @@ # This file contains client wrappers for the Cache host service. # It is intended for use in Navidrome plugins built with extism-py. # -# Usage: -# from nd_host_cache import cache_set_string, cache_get_string, cache_set_int, cache_get_int, cache_set_float, cache_get_float, cache_set_bytes, cache_get_bytes, cache_has, cache_remove +# IMPORTANT: Due to a limitation in extism-py, you cannot import this file directly. +# The @extism.import_fn decorators are only detected when defined in the plugin's +# main __init__.py file. Copy the needed functions from this file into your plugin. from dataclasses import dataclass from typing import Any diff --git a/plugins/host/python/nd_host_scheduler.py b/plugins/host/python/nd_host_scheduler.py index f7b8e320b..d9a6049e9 100644 --- a/plugins/host/python/nd_host_scheduler.py +++ b/plugins/host/python/nd_host_scheduler.py @@ -3,8 +3,9 @@ # This file contains client wrappers for the Scheduler host service. # It is intended for use in Navidrome plugins built with extism-py. # -# Usage: -# from nd_host_scheduler import scheduler_schedule_one_time, scheduler_schedule_recurring, scheduler_cancel_schedule +# IMPORTANT: Due to a limitation in extism-py, you cannot import this file directly. +# The @extism.import_fn decorators are only detected when defined in the plugin's +# main __init__.py file. Copy the needed functions from this file into your plugin. from dataclasses import dataclass from typing import Any diff --git a/plugins/host/python/nd_host_subsonicapi.py b/plugins/host/python/nd_host_subsonicapi.py index baabad0ec..4d5e9112d 100644 --- a/plugins/host/python/nd_host_subsonicapi.py +++ b/plugins/host/python/nd_host_subsonicapi.py @@ -3,8 +3,9 @@ # This file contains client wrappers for the SubsonicAPI host service. # It is intended for use in Navidrome plugins built with extism-py. # -# Usage: -# from nd_host_subsonicapi import subsonicapi_call +# IMPORTANT: Due to a limitation in extism-py, you cannot import this file directly. +# The @extism.import_fn decorators are only detected when defined in the plugin's +# main __init__.py file. Copy the needed functions from this file into your plugin. from dataclasses import dataclass from typing import Any diff --git a/plugins/host/python/nd_host_websocket.py b/plugins/host/python/nd_host_websocket.py index 37fff3d1f..c025db690 100644 --- a/plugins/host/python/nd_host_websocket.py +++ b/plugins/host/python/nd_host_websocket.py @@ -3,8 +3,9 @@ # This file contains client wrappers for the WebSocket host service. # It is intended for use in Navidrome plugins built with extism-py. # -# Usage: -# from nd_host_websocket import websocket_connect, websocket_send_text, websocket_send_binary, websocket_close_connection +# IMPORTANT: Due to a limitation in extism-py, you cannot import this file directly. +# The @extism.import_fn decorators are only detected when defined in the plugin's +# main __init__.py file. Copy the needed functions from this file into your plugin. from dataclasses import dataclass from typing import Any