Full extension example

Here is a complete extension example combining registration, provider capabilities, and API usage:

{{#tabs }} {{#tab name="Rust" }}

#![allow(unused)]
#![no_main]

fn main() {
use moosync_edk::{
    ExtensionProviderScope, RequestedSearchResultRequest, ScrobbleRequest, SearchResult,
    api::{
        self, Accounts, ContextMenu, DatabaseEvents, Extension, PlayerEvents, PreferenceEvents,
        Provider, extension_api::get_current_song,
    },
    handler::register_extension,
    info,
};

// ANCHOR: first_extension
struct SampleExtension;

impl Extension for SampleExtension {}
impl Accounts for SampleExtension {}
impl DatabaseEvents for SampleExtension {}
impl PlayerEvents for SampleExtension {}
impl PreferenceEvents for SampleExtension {}
impl ContextMenu for SampleExtension {}

#[unsafe(no_mangle)]
pub extern "C" fn init() {
    info!("Initializing SampleExtension");
    register_extension(Box::new(SampleExtension)).unwrap();
    info!("Initialized SampleExtension");
}
// ANCHOR_END: first_extension

impl Provider for SampleExtension {
// ANCHOR: provider
    fn get_provider_scopes(&self) -> api::MoosyncResult<Vec<ExtensionProviderScope>> {
        Ok(vec![ExtensionProviderScope::Search])
    }

    fn search(&self, req: RequestedSearchResultRequest) -> api::MoosyncResult<SearchResult> {
        info!("Search requested for query: {}", req.query);

        let url = format!("https://api.spotify.com/v1/search?q={}&type=track", req.query);
        let resp = moosync_edk::http::get(&url, None)?;
        if resp.is_success() {
            info!("Search API response: {}", resp.text().unwrap_or_default());
        }

        Ok(SearchResult {
            songs: vec![],
            artists: vec![],
            playlists: vec![],
            albums: vec![],
            genres: vec![],
        })
    }
// ANCHOR_END: provider

// ANCHOR: api_usage
    fn scrobble(&self, _req: ScrobbleRequest) -> api::MoosyncResult<()> {
        let song = get_current_song()?;
        if let Some(song) = song {
            if let Some(inner) = song.song {
                if let Some(title) = inner.title {
                    info!("Currently playing song: {}", title);
                }
            }
        }
        Ok(())
    }
// ANCHOR_END: api_usage
}

// ANCHOR: http_usage
pub fn search_and_fetch_details(query: &str) -> api::MoosyncResult<()> {
    use moosync_edk::http::{self, HttpRequest};

    let url = format!("https://api.spotify.com/v1/search?q={}&type=track", query);
    let resp = http::get(&url, None)?;
    if resp.is_success() {
        info!("Search response: {}", resp.text().unwrap_or_default());
    }

    let post_req = HttpRequest::post("https://api.spotify.com/v1/playlists")
        .header("Authorization", "Bearer token123")
        .body(r#"{"name":"My Playlist"}"#.as_bytes().to_vec())
        .timeout_ms(5000);
    let post_resp = http::request(&post_req)?;
    info!("Create playlist status: {}", post_resp.status_code);

    let track_urls = vec![
        "https://api.spotify.com/v1/tracks/1",
        "https://api.spotify.com/v1/tracks/2",
    ];
    let responses = http::batch_get(&track_urls, None)?;
    for r in responses {
        info!("Track status: {}", r.status_code);
    }
    Ok(())
}
// ANCHOR_END: http_usage
}

{{#endtab }} {{#tab name="Golang" }}

package main

import (
	extensions "github.com/moosync/moosync/types/extensions"
	songs "github.com/moosync/moosync/types/songs"

	"github.com/Moosync/extensions-sdk/wasm-extension-go/pkg/api"
)

// ANCHOR: first_extension
type SampleExtension struct {
	api.DefaultExtension
}

//go:wasmexport entry
func entry() {
	extension := &SampleExtension{}
	api.RegisterExtension(extension)
}

func main() {}
// ANCHOR_END: first_extension

// ANCHOR: provider
func (s *SampleExtension) GetProviderScopes() ([]extensions.ExtensionProviderScope, error) {
	return []extensions.ExtensionProviderScope{
		extensions.ExtensionProviderScope_SEARCH,
	}, nil
}

func (s *SampleExtension) Search(req *extensions.RequestedSearchResultRequest) (*songs.SearchResult, error) {
	api.LogInfo("Search called with query: %s", req.GetQuery())

	url := "https://api.spotify.com/v1/search?q=" + req.GetQuery() + "&type=track"
	resp, err := api.HttpGet(url, nil)
	if err != nil {
		return nil, err
	}
	if resp.OK() {
		api.LogInfo("Search response: %s", resp.Text())
	}

	return &songs.SearchResult{
		Songs:     []*songs.Song{},
		Playlists: []*songs.Playlist{},
		Artists:   []*songs.Artist{},
		Albums:    []*songs.Album{},
		Genres:    []*songs.Genre{},
	}, nil
}
// ANCHOR_END: provider

// ANCHOR: api_usage
func (s *SampleExtension) Scrobble(req *extensions.ScrobbleRequest) error {
	song, err := api.GetCurrentSong()
	if err != nil {
		return err
	}
	if song != nil && song.GetSong() != nil {
		api.LogInfo("Currently playing song: %s", song.GetSong().GetTitle())
	}
	return nil
}
// ANCHOR_END: api_usage

// ANCHOR: http_usage
func searchAndFetchDetails(query string) error {
	url := "https://api.spotify.com/v1/search?q=" + query + "&type=track"
	resp, err := api.HttpGet(url, nil)
	if err != nil {
		return err
	}
	if resp.OK() {
		api.LogInfo("Search response: %s", resp.Text())
	}

	postReq := api.HttpRequest{
		URL:       "https://api.spotify.com/v1/playlists",
		Method:    "POST",
		Headers:   map[string]string{"Authorization": "Bearer token123"},
		Body:      []byte(`{"name":"My Playlist"}`),
		TimeoutMs: 5000,
	}
	postResp, err := api.SendHttpRequest(postReq)
	if err != nil {
		return err
	}
	api.LogInfo("Create playlist status: %d", postResp.StatusCode)

	trackURLs := []string{
		"https://api.spotify.com/v1/tracks/1",
		"https://api.spotify.com/v1/tracks/2",
	}
	resps, errs := api.BatchHttpGet(trackURLs, nil)
	if len(errs) > 0 {
		return errs[0]
	}
	for _, r := range resps {
		api.LogInfo("Track status: %d", r.StatusCode)
	}
	return nil
}
// ANCHOR_END: http_usage

{{#endtab }} {{#tab name="Python" }}

from moosync_edk import Extension, register_extension
from core.types.protos import extensions_pb2, songs_pb2

# ANCHOR: first_extension
class SampleExtension(Extension):
    def __init__(self):
        super().__init__()

def entry():
    print("Initializing SampleExtension")
    register_extension(SampleExtension())
# ANCHOR_END: first_extension

# ANCHOR: provider
class SearchProviderExtension(Extension):
    def get_provider_scopes(self, req):
        return extensions_pb2.GetProviderScopesResponse(
            scopes=[extensions_pb2.ExtensionProviderScope.SEARCH]
        )

    def search(self, req):
        print(f"Search query: {req.query}")

        url = f"https://api.spotify.com/v1/search?q={req.query}&type=track"
        resp = http_get(url)
        if resp.ok:
            print(f"Search response: {resp.text}")

        return songs_pb2.SearchResult(
            songs=[],
            artists=[],
            playlists=[],
            albums=[],
            genres=[]
        )
# ANCHOR_END: provider

# ANCHOR: api_usage
class ScrobbleExtension(Extension):
    def scrobble(self, req):
        song = self.api.get_current_song()
        if song and song.song:
            print(f"Currently playing: {song.song.title}")
# ANCHOR_END: api_usage

# ANCHOR: http_usage
from moosync_edk import http_get, http_request, http_batch_get

def search_and_fetch_details(query):
    url = f"https://api.spotify.com/v1/search?q={query}&type=track"
    resp = http_get(url)
    if resp.ok:
        print(f"Search response: {resp.text}")

    post_resp = http_request(
        url="https://api.spotify.com/v1/playlists",
        method="POST",
        headers={"Authorization": "Bearer token123"},
        body='{"name": "My Playlist"}',
        timeout_ms=5000,
    )
    print(f"Create playlist status: {post_resp.status_code}")

    track_urls = [
        "https://api.spotify.com/v1/tracks/1",
        "https://api.spotify.com/v1/tracks/2",
    ]
    resps = http_batch_get(track_urls)
    for r in resps:
        print(f"Track status: {r.status_code}")
# ANCHOR_END: http_usage

{{#endtab }} {{#tab name="Javascript" }}

import {
  GetProviderScopesResponse,
  RequestedSearchResultResponse,
  ScrobbleResponse,
  getApi,
} from "wasm-extension-js";

export { handle_extension_command } from "wasm-extension-js";

// ANCHOR: first_extension
export function entry(): number {
  const api = getApi();

  console.log("Initialized sample extension");
  return 0;
}
// ANCHOR_END: first_extension

// ANCHOR: provider
export function registerProviderHandlers() {
  const api = getApi();

  api.on("getProviderScopes", () => {
    return new GetProviderScopesResponse({
      scopes: [
        13 as any // ExtensionProviderScope.SEARCH
      ]
    });
  });

  api.on("requestedSearchResult", async (req) => {
    console.log("Search query:", req.query);

    const url = `https://api.spotify.com/v1/search?q=${encodeURIComponent(req.query)}&type=track`;
    const resp = await api.fetch(url);
    if (resp.ok) {
      const text = await resp.text();
      console.log("Search response:", text);
    }

    return new RequestedSearchResultResponse({
      songs: []
    });
  });
}
// ANCHOR_END: provider

// ANCHOR: api_usage
export function registerApiUsage() {
  const api = getApi();

  api.on("scrobble", async (req) => {
    const song = await api.getCurrentSong();
    if (song && song.song && song.song.title) {
      console.log("Currently playing song:", song.song.title);
    }
    return new ScrobbleResponse();
  });
}
// ANCHOR_END: api_usage

// ANCHOR: http_usage
export async function searchAndFetchDetails(query: string) {
  const api = getApi();

  const url = `https://api.spotify.com/v1/search?q=${encodeURIComponent(query)}&type=track`;
  const resp = await api.fetch(url);
  if (resp.ok) {
    const text = await resp.text();
    console.log("Search response:", text);
  }

  const postResp = await api.fetch({
    url: "https://api.spotify.com/v1/playlists",
    method: "POST",
    headers: { Authorization: "Bearer token123" },
    body: JSON.stringify({ name: "My Playlist" }),
    timeoutMs: 5000,
  });
  console.log("Create playlist status:", postResp.status);

  const responses = await api.batchFetch([
    "https://api.spotify.com/v1/tracks/1",
    "https://api.spotify.com/v1/tracks/2",
  ]);
  for (const r of responses) {
    console.log("Track status:", r.status);
  }
}
// ANCHOR_END: http_usage

{{#endtab }} {{#endtabs }}