docs: add detailed documentation for plugin endpoints and methods

This commit is contained in:
Kingkor Roy Tirtho 2025-08-14 09:25:13 +06:00
parent b3f6824a94
commit 8bdbe7dfba
8 changed files with 538 additions and 1 deletions

View File

@ -0,0 +1,48 @@
---
layout: "layouts/DocLayout.astro"
title: The AlbumEndpoint
description: ""
order: 6
---
The AlbumEndpoint is used to fetch album information and do album-related actions. In the `src/segments/album.ht` file you can find all the
required method definitions.
```hetu_script
class AlbumEndpoint {
construct (this.client)
fun getAlbum(id: string) {
// TODO: Implement method
}
fun tracks(id: string, {offset: int, limit: int}) {
// TODO: Implement method
}
fun releases({offset: int, limit: int}) {
// TODO: Implement method
}
fun save(albumIds: List) { // List<String>
// TODO: Implement method
}
fun unsave(albumIds: List) { // List<String>
// TODO: Implement method
}
}
```
| Method | Description | Returns |
| ------------ | ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `getAlbum()` | Fetches album information by ID. | [`SpotubeFullAlbumObject`][SpotubeFullAlbumObject] |
| `tracks()` | Fetches tracks of the specified album. Accepts an ID and optional pagination parameters. | [`SpotubePaginationResponseObject`][SpotubePaginationResponseObject] of [`SpotubeFullTrackObject`][SpotubeFullTrackObject] |
| `releases()` | Fetches new album releases user followed artists or globally | [`SpotubePaginationResponseObject`][SpotubePaginationResponseObject] of [`SpotubeFullAlbumObject`][SpotubeFullAlbumObject] |
| `save()` | Saves the specified albums. Accepts a list of album IDs. | `void` |
| `unsave()` | Removes the specified albums from saved albums. Accepts a list of album IDs. | `void` |
{/* Urls */}
[SpotubePaginationResponseObject]: /models/spotube-pagination-response-object
[SpotubeFullAlbumObject]: /models/spotube-full-album-object
[SpotubeFullTrackObject]: /models/spotube-full-track-object

View File

@ -0,0 +1,56 @@
---
layout: "layouts/DocLayout.astro"
title: The ArtistEndpoint
description: ""
order: 7
---
The ArtistEndpoint is used to fetch artist information and do artist-related actions. In the `src/segments/artist.ht` file you can find all the
required method definitions.
```hetu_script
class ArtistEndpoint {
var client: HttpClient
construct (this.client)
fun getArtist(id: string) {
// TODO: Implement method
}
fun related(id: string, {offset: int, limit: int}) {
// TODO: Implement method
}
fun topTracks(id: string, {limit: int, offset: int}) {
// TODO: Implement method
}
fun albums(id: string, {offset: int, limit: int}) {
// TODO: Implement method
}
fun save(artistIds: List) {
// TODO: Implement method
}
fun unsave(artistIds: List) {
// TODO: Implement method
}
}
```
| Method | Description | Returns |
| ------------- | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `getArtist()` | Fetches artist information by ID. | [`SpotubeFullArtistObject`][SpotubeFullArtistObject] |
| `related()` | Fetches related artists based on the specified artist ID. Accepts optional pagination. | [`SpotubePaginationResponseObject`][SpotubePaginationResponseObject] of [`SpotubeFullArtistObject`][SpotubeFullArtistObject] |
| `topTracks()` | Fetches top tracks of the specified artist. Accepts optional pagination. | [`SpotubePaginationResponseObject`][SpotubePaginationResponseObject] of [`SpotubeFullTrackObject`][SpotubeFullTrackObject] |
| `albums()` | Fetches albums of the specified artist. Accepts optional pagination. | [`SpotubePaginationResponseObject`][SpotubePaginationResponseObject] of [`SpotubeFullAlbumObject`][SpotubeFullAlbumObject] |
| `save()` | Saves the specified artists. Accepts a list of artist IDs. | `void` |
| `unsave()` | Removes the specified artists from saved artists. Accepts a list of artist IDs. | `void` |
{/* Urls */}
[SpotubePaginationResponseObject]: /models/spotube-pagination-response-object
[SpotubeFullAlbumObject]: /models/spotube-full-album-object
[SpotubeFullArtistObject]: /models/spotube-full-artist-object
[SpotubeFullTrackObject]: /models/spotube-full-track-object

View File

@ -0,0 +1,72 @@
---
layout: "layouts/DocLayout.astro"
title: The AuthEndpoint
description: ""
order: 3
---
> If your plugin doesn't need authentication support, you can skip this section.
In the `src/segments/auth.ht` file you can find all the required method definition. These are the necessary
methods Spotube calls in it's lifecycle.
```hetu_script
class AuthEndpoint {
var client: HttpClient
final controller: StreamController
get authStateStream -> Stream => controller.stream
construct (this.client){
controller = StreamController.broadcast()
}
fun isAuthenticated() -> bool {
// TODO: Implement method
return false
}
fun authenticate() -> Future {
// TODO: Implement method
}
fun logout() -> Future {
// TODO: Implement method
}
}
```
For this specific endpoint, you may need `WebView` or `Forms` to get user inputs. The [`hetu_spotube_plugin`][hetu_spotube_plugin] provides
such APIs.
> Learn more about it in the [Spotube Plugin API][spotube_plugin_api] section
### The `.authStateStream` property
The `AuthEndpoint.authStateStream` property is also necessary to notify Spotube about the authentication status. [`hetu_std`][hetu_std] is a built-in
module and it exports `StreamController` which basically 1:1 copy of the Dart's [StreamController][dart_stream_controller].
If the status of authentication changes you need to add a new event using the `controller.add`
Following events are respected by Spotube:
| Name | Description |
| ----------- | ------------------------------------------------------------ |
| `login` | When user successfully completes login |
| `logout` | When user logs out of the service |
| `recovered` | When user's cached/saved credentials are recovered from disk |
| `refreshed` | When user's session is refreshed |
Example of adding a new authentication event:
```hetu_script
controller.add({ type: "login" }.toJson())
```
By the way, the event type is a `Map<String, dynamic>` in the Dart side, so make sure to always convert hetu_script's [structs into Maps][hetu_struct_into_map]
{/* Urls */}
[hetu_script_import_export_docs]: https://hetu-script.github.io/docs/en-US/grammar/import/
[hetu_spotube_plugin]: https://github.com/KRTirtho/hetu_spotube_plugin
[spotube_plugin_api]: /
[hetu_std]: https://github.com/hetu-community/hetu_std
[dart_stream_controller]: https://api.flutter.dev/flutter/dart-async/StreamController-class.html
[hetu_struct_into_map]: https://hetu-script.github.io/docs/en-US/api_reference/hetu/#struct

View File

@ -13,12 +13,71 @@ This guide will help you initialize a plugin project and write your first plugin
[spotube-plugin-template][spotube-plugin-template] is a template repository for Spotube plugins. It's a starting point
with everything you need to get started with plugin development. You should use it to create your own plugin.
Simply clone or click "Use this template" button on the repository page to create a new repository.
Simply clone or click "Use this template" button on the GitHub repository page to create a new repository.
```bash
$ git clone https://github.com/KRTirtho/spotube-plugin-template.git
$ cd spotube-plugin-template
```
## Understanding plugins.json
After cloning the repository, you will find a file named `plugins.json` in the root directory.
This file is crucial for Spotube to recognize your plugin. It looks like this:
```json
{
"type": "metadata",
"version": "1.0.0",
"name": "Alphanumeric plugin name with hyphens or underscore",
"author": "Your Name",
"description": "A brief description of the plugin's functionality.",
"entryPoint": "plugin class name",
"apis": ["webview", "localstorage", "timezone"],
"abilities": ["authentication", "scrobbling"],
"repository": "https://github.com/KRTirtho/spotube-plugin-template",
"pluginApiVersion": "1.0.0"
}
```
| Property | Description |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type` | The type of the plugin, which is always `metadata` for Spotube plugins. |
| `version` | The version of the plugin, following [semantic versioning][semantic-version] (e.g., `1.0.0`). |
| `name` | The name of the plugin |
| `author` | The name of the plugin author. |
| `description` | A brief description of the plugin's functionality. |
| `entryPoint` | The name of the class that serves as the entry point for the plugin. |
| `apis` | An array of APIs that the plugin uses. This is used to determine which APIs are available to the plugin. Following APIs are available "webview", "localstorage", "timezone" |
| `abilities` | An array of abilities that the plugin has. This is used to determine which abilities the plugin has. Following abilities can be listed: "authentication", "scrobbling" |
| `repository` | The URL of the plugin's repository. This is used to display the plugin's repository in the plugin manager. |
| `pluginApiVersion` | The version of the plugin API that the plugin uses. This is used to determine if the plugin is compatible with the current version of Spotube. |
Change the values in the `plugins.json` file to match your plugin's information.
## Running the `example` app
There's an `example` folder that contains a simple Flutter app that utilizes all the methods
Spotube would call on your plugin. You can run this app to test your plugin's functionality.
But first you need too compile the plugin to bytecode. You can simply do this using:
```shell
$ make
```
Make sure you've `make` command installed on your system and also must have the [hetu_script_dev_tools][hetu_script_dev_tools] package globally installed.
After compiling the plugin, you can run the example app like any other Flutter app.
```shell
$ cd example
$ flutter run
```
> Most of the buttons, will not work as they not yet implemented. You've to implement the methods in your plugin source code.
> We will cover how to implement the methods in the next section.
{/* Links */}
[spotube-plugin-template]: https://github.com/KRTirtho/spotube-plugin-template
[semantic-version]: https://semver.org/
[hetu_script_dev_tools]: https://pub.dev/packages/hetu_script_dev_tools

View File

@ -0,0 +1,95 @@
---
layout: "layouts/DocLayout.astro"
title: Implementing plugin methods
description: Tutorial on how to implement methods in your Spotube plugin.
order: 2
---
In the previous section, you learned how to create a plugin project and run the example app.
In this section, you will learn how to implement methods in your Spotube plugin.
## The `entryPoint` class
The `entryPoint` (from the plugin.json) class is the main class of your plugin. You can find it in `src/plugin.ht`. It's the class
that Spotube will instantiate when it loads your plugin. You can pretty much keep this class same as the template, unless you
there's something specific you want to change.
```hetu_script
// The name of the class should match the `entryPoint` in plugin.json
class TemplateMetadataProviderPlugin {
// These are required properties that Spotube will use to call the methods.
// ==== Start of required properties ====
var auth: AuthEndpoint
var album: AlbumEndpoint
var artist: ArtistEndpoint
var browse: BrowseEndpoint
var playlist: PlaylistEndpoint
var search: SearchEndpoint
var track: TrackEndpoint
var user: UserEndpoint
var core: CorePlugin
// ==== End of required properties ====
var api: HttpClient
construct (){
api = HttpClient(
HttpBaseOptions(
baseUrl: "https://example.com"
)
)
auth = AuthEndpoint(api)
album = AlbumEndpoint(api)
artist = ArtistEndpoint(api)
browse = BrowseEndpoint(api)
playlist = PlaylistEndpoint(api)
search = SearchEndpoint(api)
track = TrackEndpoint(api)
user = UserEndpoint(api)
core = CorePlugin(api)
auth.authStateStream.listen((event) {
// get authentication events
})
}
}
```
If you read how the import/export works for [hetu_script][hetu_script_import_export_docs], you should realize it's pretty similar to ECMA Script modules or ES6+ Modules
from the JavaScript world.
```hetu_script
import { AuthEndpoint } from './segments/auth.ht'
import { AlbumEndpoint } from "./segments/album.ht"
import { ArtistEndpoint } from "./segments/artist.ht"
import { BrowseEndpoint } from "./segments/browse.ht"
import { PlaylistEndpoint } from './segments/playlist.ht'
import { SearchEndpoint } from './segments/search.ht'
import { TrackEndpoint } from './segments/track.ht'
import { UserEndpoint } from './segments/user.ht'
import { CorePlugin } from './segments/core.ht'
```
## Implementing subclasses
Now that we've seen `entryPoint` class, we can look into the properties of that classes which are the actual
classes that contains methods that Spotube calls. All of them are in `src/segments` folder
> **IMPORTANT!:** hetu\*script claims it supports async/await. But unfortunately it still doesn't work yet.
> So for now, we have to bear with .then()
>
> Also, if you've read the hetu_script docs, you should know hetu_script doesn't support <ins>Error Handling</ins>.
> This is a design decision of the language and the errors should only be handled in the Dart code.
> So there's no try/catch/finally or .catch() method
In the next section, we will cover how to implement the methods in these classes.
{/* Urls */}
[hetu_script_import_export_docs]: https://hetu-script.github.io/docs/en-US/grammar/import/
[hetu_spotube_plugin]: https://github.com/KRTirtho/hetu_spotube_plugin
[spotube_plugin_api]: /
[hetu_std]: https://github.com/hetu-community/hetu_std
[dart_stream_controller]: https://api.flutter.dev/flutter/dart-async/StreamController-class.html
[hetu_struct_into_map]: https://hetu-script.github.io/docs/en-US/api_reference/hetu/#struct

View File

@ -0,0 +1,81 @@
---
layout: "layouts/DocLayout.astro"
title: The PlaylistEndpoint
description: ""
order: 8
---
The PlaylistEndpoint is used to fetch playlist information and do track-related actions. In the `src/segments/playlist.ht` file you can find all the
required method definitions.
```hetu_script
class PlaylistEndpoint {
var client: HttpClient
construct (this.client)
fun getPlaylist(id: string) {
// TODO: Implement method
}
fun tracks(id: string, { offset: int, limit: int }) {
// TODO: Implement method
}
fun create(userId: string, {
name: string,
description: string,
public: bool,
collaborative: bool
}) {
// TODO: Implement method
}
fun update(playlistId: string, {
name: string,
description: string,
public: bool,
collaborative: bool
}) {
// TODO: Implement method
}
fun deletePlaylist(playlistId: string) {
// TODO: Implement method
}
fun addTracks(playlistId: string, { trackIds: List, position: int }) {
// TODO: Implement method
}
fun removeTracks(playlistId: string, { trackIds: List }) {
// TODO: Implement method
}
fun save(playlistId: string) {
// TODO: Implement method
}
fun unsave(playlistId: string) {
// TODO: Implement method
}
}
```
| Method | Description | Returns |
| ---------------- | ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `getPlaylist` | Fetches a playlist by its ID. | [`SpotubeFullPlaylistObject`][SpotubeFullPlaylistObject] |
| `tracks` | Fetches tracks in a playlist. | [`SpotubePaginationResponseObject`][SpotubePaginationResponseObject] of [`SpotubeFullTrackObject`][SpotubeFullTrackObject] |
| `create` | Creates a new playlist and returns | [`SpotubeFullPlaylistObject`][SpotubeFullPlaylistObject] |
| `update` | Updates an existing playlist. | `void` |
| `deletePlaylist` | Deletes a playlist. | `void` |
| `addTracks` | Adds tracks to a playlist. | `void` |
| `removeTracks` | Removes tracks from a playlist. | `void` |
| `save` | Saves a playlist to the user's library. | `void` |
| `unsave` | Removes a playlist from the user's library. | `void` |
{/* Urls */}
[SpotubePaginationResponseObject]: /models/spotube-pagination-response-object
[SpotubeFullPlaylistObject]: /models/spotube-full-playlist-object
[SpotubeFullTrackObject]: /models/spotube-full-track-object

View File

@ -0,0 +1,43 @@
---
layout: "layouts/DocLayout.astro"
title: The TrackEndpoint
description: ""
order: 5
---
The TrackEndpoint is used to fetch track information and do track-related actions. In the `src/segments/track.ht` file you can find all the
required method definitions.
```hetu_script
class TrackEndpoint {
var client: HttpClient
construct (this.client)
fun getTrack(id: string) {
// TODO: Implement method
}
fun save(trackIds: List) { // List<String>
// TODO: Implement method
}
fun unsave(trackIds: List) { // List<String>
// TODO: Implement method
}
fun radio(id: string) {
// TODO: Implement method
}
}
```
| Method | Description | Returns |
| ------------ | ------------------------------------------------------------------------------------ | -------------------------------------------------------- |
| `getTrack()` | Fetches track information by ID. | [SpotubeFullTrackObject][SpotubeFullTrackObject] |
| `save()` | Saves the specified tracks. Accepts a list of track IDs. | void |
| `unsave()` | Removes the specified tracks from saved tracks. Accepts a list of track IDs. | void |
| `radio()` | Fetches related tracks based on specified tracks. Try to return a List of 50 tracks. | [List\<SpotubeFullTrackObject\>][SpotubeFullTrackObject] |
{/* Urls */}
[SpotubeFullTrackObject]: /models/spotube-full-track-object

View File

@ -0,0 +1,83 @@
---
layout: "layouts/DocLayout.astro"
title: The UserEndpoint
description: ""
order: 4
---
The UserEndpoint is used to fetch user information and manage user-related actions.
In the `src/segments/user.ht` file you can find all the required method definitions. These are the necessary
methods Spotube calls in its lifecycle.
> Most of these methods should be just a mapping to an API call with minimum latency. Avoid calling plugin APIs like WebView or Forms
> in these methods. User interactions should be avoided here generally.
```hetu_script
class UserEndpoint {
var client: HttpClient
construct (this.client)
fun me() {
// TODO: Implement method
}
fun savedTracks({ offset: int, limit: int }) {
// TODO: Implement method
}
fun savedPlaylists({ offset: int, limit: int }) {
// TODO: Implement method
}
fun savedAlbums({ offset: int, limit: int }) {
// TODO: Implement method
}
fun savedArtists({ offset: int, limit: int }) {
// TODO: Implement method
}
fun isSavedPlaylist(playlistId: string) { // Future<bool>
// TODO: Implement method
}
fun isSavedTracks(trackIds: List) { // Future<List<bool>>
// TODO: Implement method
}
fun isSavedAlbums(albumIds: List) { // Future<List<bool>>
// TODO: Implement method
}
fun isSavedArtists(artistIds: List) { // Future<List<bool>>
// TODO: Implement method
}
}
```
These methods are pretty self-explanatory. You need to implement them to fetch user information from your service.
| Method | Description | Returns |
| ------------------- | ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------- |
| `me()` | Fetches the current user's information. | [`SpotubeUserObject`][SpotubeUserObject] |
| `savedTracks()` | Fetches the user's saved tracks with pagination support. | [`SpotubePaginationResponseObject`][SpotubePaginationResponseObject] of [`SpotubeFullTrackObject`][SpotubeFullTrackObject] |
| `savedPlaylists()` | Fetches the user's saved playlists with pagination support. | [`SpotubePaginationResponseObject`][SpotubePaginationResponseObject] of [`SpotubeFullPlaylistObject`][SpotubeFullPlaylistObject] |
| `savedAlbums()` | Fetches the user's saved albums with pagination support. | [`SpotubePaginationResponseObject`][SpotubePaginationResponseObject] of [`SpotubeFullAlbumObject`][SpotubeFullAlbumObject] |
| `savedArtists()` | Fetches the user's saved artists with pagination support. | [`SpotubePaginationResponseObject`][SpotubePaginationResponseObject] of [`SpotubeFullArtistObject`][SpotubeFullArtistObject] |
| `isSavedPlaylist()` | Checks if a playlist is saved by the user. Returns a `Future<bool>`. | `bool` |
| `isSavedTracks()` | Checks if tracks are saved by the user. Returns a `Future<List<bool>>`. | `List<bool>` (each boolean corresponds to a track ID) |
| `isSavedAlbums()` | Checks if albums are saved by the user. Returns a `Future<List<bool>>`. | `List<bool>` (each boolean corresponds to an album ID) |
| `isSavedArtists()` | Checks if artists are saved by the user. Returns a `Future<List<bool>>`. | `List<bool>` (each boolean corresponds to an artist ID) |
> Note: The `isSavedTracks`, `isSavedAlbums`, and `isSavedArtists` methods accept a list of IDs and return a list of booleans
> indicating whether each item is saved by the user. The order of the booleans in the list corresponds to the order of the IDs
> in the input list.
{/* Links */}
[SpotubeUserObject]: /models/spotube-user-object
[SpotubePaginationResponseObject]: /models/spotube-pagination-response-object
[SpotubeFullTrackObject]: /models/spotube-full-track-object
[SpotubeFullPlaylistObject]: /models/spotube-full-playlist-object
[SpotubeFullAlbumObject]: /models/spotube-full-album-object
[SpotubeFullArtistObject]: /models/spotube-full-artist-object