### Extension Examples Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/partners.html Examples demonstrating how to create and use extensions within the Spotiflac Mobile project. ```APIDOC ## Extension Examples This section provides examples of creating and utilizing extensions. ### Example 1: Simple Metadata Provider Demonstrates a basic metadata provider extension. ### Example 2: Download Provider with Auth Illustrates a download provider extension that includes authentication. ``` -------------------------------- ### Basic Manifest File Example Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/downloads.html A minimal example of a Spotiflac extension's manifest file. Ensure all required fields are present for proper extension loading. ```json { "id": "com.example.spotiflac.extension", "version": "1.0.0", "name": "My Spotiflac Extension", "main": "index.js", "logo": "./logo.png", "features": [ "player", "search" ] } ``` -------------------------------- ### Download and Install Extension from Store Source: https://context7.com/zarzet/spotiflac-mobile/llms.txt Download an extension from the store by its ID and specify a destination directory for installation. The function returns the path where the extension was downloaded. ```dart // Download and install extension from store final downloadPath = await PlatformBridge.downloadStoreExtension( 'extension-id', '/path/to/extensions', // Destination directory ); print('Downloaded to: $downloadPath'); ``` -------------------------------- ### Example URL Patterns for Platforms Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/docs.html Provide example patterns for common music platforms like YouTube Music, SoundCloud, and Bandcamp. These patterns are used to match URLs. ```json "patterns": ["music.youtube.com", "youtube.com/watch", "youtu.be"] ``` ```json "patterns": ["soundcloud.com"] ``` ```json "patterns": ["bandcamp.com"] ``` -------------------------------- ### Initialize Development Environment Source: https://github.com/zarzet/spotiflac-mobile/blob/main/CONTRIBUTING.md Commands to configure the Flutter version, install dependencies, and generate necessary code. ```bash fvm use ``` ```bash flutter pub get ``` ```bash dart run build_runner build --delete-conflicting-outputs ``` -------------------------------- ### Manifest File with Settings Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/downloads.html Example of a manifest file defining user-configurable settings. Settings allow users to customize extension behavior. ```json { "id": "com.example.spotiflac.extension", "version": "1.0.0", "name": "My Spotiflac Extension", "main": "index.js", "logo": "./logo.png", "features": [ "player", "search" ], "settings": { "theme": { "type": "dropdown", "label": "Theme", "options": [ "light", "dark" ], "default": "light" } } } ``` -------------------------------- ### Access Quality Settings in JavaScript Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/docs.html Example function demonstrating how to retrieve and validate quality-specific settings during a download operation. ```javascript function download(trackId, quality, outputPath, progressCallback) { // Get quality-specific settings const qualitySettings = settings.qualitySettings?.[quality] || {}; let endpoint; let apiKey; if (quality === 'PREMIUM_FLAC') { endpoint = qualitySettings.premium_endpoint || 'https://api.example.com/premium/stream'; apiKey = qualitySettings.premium_api_key; if (!apiKey) { return { success: false, error: 'Premium API key required', error_type: 'auth_error' }; } } else { endpoint = qualitySettings.free_endpoint || 'https://api.example.com/free/stream'; apiKey = settings.api_key; // Use global API key for free tier } // ... download logic using endpoint and apiKey } ``` -------------------------------- ### Complete enrichTrack Example with Service IDs Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/docs.html A comprehensive example of the `enrichTrack` function, demonstrating how to build a source URL, query the Odesli API, and extract ISRC and direct download IDs for various services. Adjust the `sourceUrl` to match your specific service. ```javascript function enrichTrack(track) { if (!track || !track.id) return track; // Build URL for Odesli lookup (adjust for your service) const sourceUrl = "https://your-service.com/track/" + track.id; const odesliUrl = "https://api.song.link/v1-alpha.1/links?url=" + encodeURIComponent(sourceUrl); try { const res = fetch(odesliUrl, { method: "GET" }); if (!res || !res.ok) return track; const data = res.json(); const enrichment = { external_links: {} }; // Extract ISRC (used for search fallback) if (data.entitiesByUniqueId) { for (const key of Object.keys(data.entitiesByUniqueId)) { const entity = data.entitiesByUniqueId[key]; if (entity && entity.isrc) { enrichment.isrc = entity.isrc; break; } } } // Extract service-specific IDs for DIRECT download (no search!) ``` -------------------------------- ### YouTube Music / Innertube API Example Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/downloads.html A practical example of interacting with the YouTube Music Innertube API. This snippet shows how to fetch search results. ```javascript async function searchYouTubeMusic(query) { const response = await Spicetify.Platform.Network.request({ url: "https://music.youtube.com/youtubei/v1/search", method: "POST", headers: { "Content-Type": "application/json", "X-Goog-AuthUser": "0", "X-Goog-Device-ID": "YOUR_DEVICE_ID", "X-Goog-Page-Id": "YOUR_PAGE_ID" }, body: JSON.stringify({ "context": { "client": { "clientName": "WEB_REMIX", "clientVersion": "1.2345.6789" } }, "query": query }) }); return response; } ``` -------------------------------- ### Manifest File with Quality Options Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/downloads.html Example of a manifest file including quality options for audio streaming. This allows users to select their preferred audio quality. ```json { "id": "com.example.spotiflac.extension", "version": "1.0.0", "name": "My Spotiflac Extension", "main": "index.js", "logo": "./logo.png", "features": [ "player", "search" ], "quality": { "96": "96 kbps", "160": "160 kbps", "320": "320 kbps" } } ``` -------------------------------- ### Lyrics Provider Extension Manifest Configuration Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/docs.html Example manifest.json configuration for a lyrics provider extension. ```APIDOC ## Manifest Configuration ### Description This is an example of a `manifest.json` file for a lyrics provider extension. It specifies the extension's name, version, description, author, type, and necessary permissions. ### Type - `lyrics_provider`: Declares this extension as a lyrics provider. ### Permissions - `network`: Specifies the network domains the extension can access. In this case, `api.my-lyrics.com` is allowed. - `storage`: Indicates if the extension requires storage access (set to `true` in this example). ### Request Example ```json { "name": "my-lyrics-source", "displayName": "My Lyrics Source", "version": "1.0.0", "description": "Fetch lyrics from My Lyrics API", "author": "Developer", "type": ["lyrics_provider"], "permissions": { "network": ["api.my-lyrics.com"], "storage": true } } ``` ``` -------------------------------- ### Network Permissions in Manifest Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/docs.html Example of how to configure network permissions in the `manifest.json` file. Ensure all necessary domains, including subdomains and wildcard patterns, are listed to avoid 'Permission denied' errors. ```json "permissions": { "network": [ "music.youtube.com", "www.youtube.com", "youtube.com", "youtubei.googleapis.com", "*.googlevideo.com", "*.youtube.com", "*.ytimg.com" ] } ``` -------------------------------- ### Build and Package Extension Scripts Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/docs.html Includes npm scripts for building the extension using esbuild and packaging it into a .spotiflac-ext file. Ensure you have esbuild installed globally or as a dev dependency. ```json { "scripts": { "build": "esbuild src/index.js --bundle --outfile=dist/index.js --format=iife", "package": "npm run build && zip -j my-extension.spotiflac-ext manifest.json dist/index.js icon.png" } } ``` -------------------------------- ### Registering Extension with Metadata Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/downloads.html Example of registering an extension with detailed metadata. This function is crucial for the extension to be recognized by Spotiflac. ```javascript function registerExtension() { Spicetify.Platform.Extensions.register({ id: "com.example.spotiflac.extension", version: "1.0.0", name: "My Spotiflac Extension", main: "index.js", logo: "./logo.png", features: ["player", "search"], // ... other metadata }); } registerExtension(); ``` -------------------------------- ### Manifest File with Album & Playlist Functions (v3.0.1+) Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/downloads.html Example of a manifest file enabling album and playlist functions for versions 3.0.1 and above. This requires specific feature flags. ```json { "id": "com.example.spotiflac.extension", "version": "1.0.0", "name": "My Spotiflac Extension", "main": "index.js", "logo": "./logo.png", "features": [ "player", "search", "album_functions", "playlist_functions" ] } ``` -------------------------------- ### Manifest File with Home Feed Support Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/downloads.html Example of a manifest file enabling home feed support. This allows the extension to contribute content to the user'.s home feed. ```json { "id": "com.example.spotiflac.extension", "version": "1.0.0", "name": "My Spotiflac Extension", "main": "index.js", "logo": "./logo.png", "features": [ "player", "search", "home_feed" ] } ``` -------------------------------- ### Manifest File with Custom Search Behavior Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/downloads.html Example of a manifest file configuring custom search behavior. This allows extensions to provide their own search logic. ```json { "id": "com.example.spotiflac.extension", "version": "1.0.0", "name": "My Spotiflac Extension", "main": "index.js", "logo": "./logo.png", "features": [ "player", "search" ], "search_behavior": { "default_search_provider": "google", "custom_providers": [ { "name": "Spotiflac Search", "url": "https://api.spotiflac.com/search?q={query}" } ] } } ``` -------------------------------- ### Initialize and Manage Extension System Source: https://context7.com/zarzet/spotiflac-mobile/llms.txt Initializes the extension system, specifying directories for extensions and their data. It also handles loading extensions from a directory or a specific file path, and retrieves a list of all installed extensions. ```dart import 'package:spotiflac_android/services/platform_bridge.dart'; // Initialize the extension system await PlatformBridge.initExtensionSystem( '/path/to/extensions', // Directory for installed extensions '/path/to/data', // Directory for extension data ); // Load extensions from directory final loadResult = await PlatformBridge.loadExtensionsFromDir('/path/to/extensions'); print('Loaded: ${loadResult['loaded']}'); print('Errors: ${loadResult['errors']}'); // Install a new extension from file final installResult = await PlatformBridge.loadExtensionFromPath( '/path/to/extension.spotiflac-ext', ); print('Extension ID: ${installResult['id']}'); print('Name: ${installResult['name']}'); print('Version: ${installResult['version']}'); // Get all installed extensions final extensions = await PlatformBridge.getInstalledExtensions(); for (final ext in extensions) { print('${ext['id']}: ${ext['manifest']['displayName']} v${ext['manifest']['version']}'); print(' Types: ${ext['manifest']['type']}'); print(' Enabled: ${ext['enabled']}'); } ``` -------------------------------- ### Manifest File with Permissions Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/downloads.html Example of a manifest file specifying necessary permissions for the extension. Permissions control access to certain functionalities or data. ```json { "id": "com.example.spotiflac.extension", "version": "1.0.0", "name": "My Spotiflac Extension", "main": "index.js", "logo": "./logo.png", "features": [ "player", "search" ], "permissions": [ "storage", "network" ] } ``` -------------------------------- ### Using atob() / btoa() for Base64 Encoding/Decoding Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/downloads.html Example of using `atob()` and `btoa()` for Base64 encoding and decoding strings. These are useful for data manipulation. ```javascript const originalString = "Hello, Spotiflac!"; // Encode to Base64 const encodedString = btoa(originalString); console.log("Encoded:", encodedString); // Output: "SGVsbG8sIFNwb3RpZmxhYyE=" // Decode from Base64 const decodedString = atob(encodedString); console.log("Decoded:", decodedString); // Output: "Hello, Spotiflac!" ``` -------------------------------- ### HTTP API - Basic Requests Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/docs.html Demonstrates how to perform basic HTTP requests (GET, POST, PUT, DELETE, PATCH) using the `http` client. ```APIDOC // GET request const response = http.get(url, headers); // POST request - body can be string or object (auto-stringified to JSON) const response = http.post(url, body, headers); // PUT request - same signature as POST const response = http.put(url, body, headers); // DELETE request - no body const response = http.delete(url, headers); // PATCH request - same signature as POST const response = http.patch(url, body, headers); ``` -------------------------------- ### Manifest File with Custom Track Matching Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/downloads.html Example of a manifest file configuring custom track matching. This allows extensions to define their own logic for matching tracks. ```json { "id": "com.example.spotiflac.extension", "version": "1.0.0", "name": "My Spotiflac Extension", "main": "index.js", "logo": "./logo.png", "features": [ "player", "search", "custom_track_matching" ] } ``` -------------------------------- ### File API Path Resolution Examples Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/docs.html Demonstrates how to use relative and absolute paths with the File API. Relative paths are recommended and are resolved relative to the extension's data directory, providing a sandboxed environment. Absolute paths are permitted for specific integrations like the download queue. ```javascript // Relative paths (recommended) file.write("cache/data.json", data); // → {ext_dir}/cache/data.json file.read("config.txt"); // → {ext_dir}/config.txt file.exists("downloads/track.flac"); // → {ext_dir}/downloads/track.flac // Absolute paths (allowed for download queue integration) file.write("/storage/emulated/0/Music/track.flac", data); // Allowed file.read("/sdcard/Download/file.txt"); // Allowed ``` -------------------------------- ### Go Backend API - File and Time Utilities Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/docs.html Provides utilities for file operations like sanitizing filenames and getting audio quality, as well as functions for building filenames and retrieving accurate local time and timezone information. ```APIDOC ## Go Backend API Utilities ### Description Provides utilities for file operations and time-related functions. ### Functions #### `sanitizeFilename(filename string) string` Sanitizes a given filename to ensure it is safe for use. #### `getAudioQuality(filePath string) object` Retrieves audio quality information from a file. **Returns:** - `{ bitDepth: number, sampleRate: number, totalSamples: number }` on success. - `{ error: string }` on failure. #### `buildFilename(template string, metadata object) string` Builds a filename based on a template and provided metadata. **Parameters:** - `template` (string) - The filename template. - `metadata` (object) - An object containing metadata like title, artist, album, track_number, etc. #### `getLocalTime() object` Gets the device's local time with accurate timezone detection. **Returns:** - `{ year: number, month: number, // 1-12 day: number, // 1-31 hour: number, // 0-23 (local time) minute: number, // 0-59 second: number, // 0-59 weekday: number, // 0=Sunday, 1=Monday, ..., 6=Saturday offsetMinutes: number, // Timezone offset (JS convention: negative for east of UTC) timezone: string, // Go timezone string timestamp: number // Unix timestamp }` ### Usage Examples #### Time-Based Greeting ```javascript function getTimeBasedGreeting() { var localTime = gobackend.getLocalTime(); var hour = localTime.hour; if (hour >= 5 && hour < 12) { return "Good morning"; } else if (hour >= 12 && hour < 17) { return "Good afternoon"; } else if (hour >= 17 && hour < 21) { return "Good evening"; } else { return "Good night"; } } ``` #### Timezone in API Calls ```javascript function fetchHomeFeed() { let timeZone = "UTC"; try { const localTime = gobackend.getLocalTime(); if (localTime.timezone && localTime.timezone !== "Local") { timeZone = localTime.timezone; } else { const offsetMinutes = localTime.offsetMinutes; const tzMap = { '-420': 'Asia/Jakarta', // UTC+7 '-480': 'Asia/Singapore', // UTC+8 '-540': 'Asia/Tokyo', // UTC+9 '0': 'Europe/London', // UTC+0 '300': 'America/New_York', // UTC-5 '480': 'America/Los_Angeles' // UTC-8 }; timeZone = tzMap[String(offsetMinutes)] || "UTC"; } } catch (e) { // Fallback to UTC } const response = http.get("https://api.example.com/home?timezone=" + encodeURIComponent(timeZone)); // ... } ``` ``` -------------------------------- ### Start OAuth Authorization Flow Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/docs.html Constructs the authorization URL with client ID, redirect URI, response type, and scope, then requests the application to open this URL in a browser to initiate the OAuth flow. ```javascript // Call this to start OAuth flow function startAuth() { const clientId = settings.client_id; const redirectUri = "spotiflac://oauth/callback"; const authUrl = `https://api.example.com/oauth/authorize?` + `client_id=${clientId}&` + `redirect_uri=${encodeURIComponent(redirectUri)}&` + `response_type=code&` + `scope=read,download`; // Request app to open auth URL auth.openAuthUrl(authUrl, redirectUri); return { success: true, message: "Please complete authentication in browser" }; } ``` -------------------------------- ### Conventional Commits Examples Source: https://github.com/zarzet/spotiflac-mobile/blob/main/CONTRIBUTING.md Examples of valid commit messages following the Conventional Commits format for different types of changes. ```text feat(download): add batch download support fix(ui): resolve overflow on small screens docs: update contributing guidelines chore(deps): update flutter_riverpod to 3.1.0 ``` -------------------------------- ### Clone and Configure Repository Source: https://github.com/zarzet/spotiflac-mobile/blob/main/CONTRIBUTING.md Initial steps to clone the fork and set up the upstream remote for synchronization. ```bash git clone https://github.com/YOUR_USERNAME/SpotiFLAC-Mobile.git cd SpotiFLAC-Mobile ``` ```bash git remote add upstream https://github.com/zarzet/SpotiFLAC-Mobile.git ``` -------------------------------- ### HMAC-SHA256 Example (API Signing) Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/downloads.html Provides an example of generating an HMAC-SHA256 hash, commonly used for signing API requests to ensure authenticity and integrity. ```javascript async function generateHmacSha256(key, message) { const crypto = Spicetify.Platform.Crypto; const encoder = new TextEncoder(); const keyData = encoder.encode(key); const messageData = encoder.encode(message); const cryptoKey = await crypto.subtle.importKey( "raw", keyData, { name: "HMAC", hash: "SHA-256" }, false, ["sign", "verify"] ); const signature = await crypto.subtle.sign("HMAC", cryptoKey, messageData); // Convert signature to hex string for display const hashArray = Array.from(new Uint8Array(signature)); const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); return hashHex; } // Example usage: const apiKey = "YOUR_API_KEY"; const requestPayload = "timestamp=1678886400&userId=123"; // Example request data generateHmacSha256(apiKey, requestPayload).then(signature => { console.log("API Signature (HMAC-SHA256):", signature); }); ``` -------------------------------- ### Run and Build Application Source: https://github.com/zarzet/spotiflac-mobile/blob/main/CONTRIBUTING.md Commands to execute the application or generate APK builds. ```bash flutter run ``` ```bash # Debug build flutter build apk --debug # Release build flutter build apk --release ``` -------------------------------- ### Package Extension using Command Line Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/docs.html Provides commands for packaging your extension into a .spotiflac-ext file. Choose the appropriate command based on your operating system. ```bash # Windows (PowerShell) Compress-Archive -Path manifest.json, index.js -DestinationPath my-extension.zip Rename-Item my-extension.zip my-extension.spotiflac-ext ``` ```bash # Linux/Mac zip my-extension.zip manifest.json index.js mv my-extension.zip my-extension.spotiflac-ext ``` -------------------------------- ### HTTP GET Request with Timeout Handling Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/docs.html This snippet shows a basic HTTP GET request. Requests exceeding the 30-second timeout will result in an error, which should be logged. ```javascript // Requests that take longer than 30 seconds will fail const response = http.get("https://slow-api.example.com/data"); if (response.error) { // Could be timeout: "context deadline exceeded" or similar log.error("Request failed:", response.error); } ``` -------------------------------- ### Get Extension Home Feed and Browse Categories Source: https://context7.com/zarzet/spotiflac-mobile/llms.txt Retrieve the home feed content for a specific extension or get a list of available browse categories it supports. ```dart // Get extension home feed final homeFeed = await PlatformBridge.getExtensionHomeFeed('extension-id'); // Get browse categories final categories = await PlatformBridge.getExtensionBrowseCategories('extension-id'); ``` -------------------------------- ### auth.startOAuthWithPKCE Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/docs.html Initiates the OAuth flow by generating a PKCE pair and opening the authorization URL. ```APIDOC ## POST auth.startOAuthWithPKCE ### Description Generates a PKCE verifier and challenge, stores the verifier, and opens the Spotify authorization URL. ### Request Body - **authUrl** (string) - Required - The OAuth authorization endpoint. - **clientId** (string) - Required - Your OAuth client ID. - **redirectUri** (string) - Required - The callback URL. - **scope** (string) - Optional - OAuth scopes. - **extraParams** (object) - Optional - Additional URL parameters. ### Response - **success** (boolean) - Indicates if the URL was opened successfully. - **authUrl** (string) - The generated authorization URL. ``` -------------------------------- ### Initialize Documentation Search Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/index.html Initializes the search overlay, handles keyboard shortcuts for Mac, and defines the search index structure. ```javascript (function() { var overlay = document.getElementById('searchOverlay'); var input = document.getElementById('searchInput'); var body = document.getElementById('searchBody'); var hintEl = document.getElementById('searchShortcutHint'); var activeIdx = -1; var results = []; if (navigator.platform && navigator.platform.indexOf('Mac') > -1) { if (hintEl) hintEl.textContent = '\u2318 K'; } var searchIndex = [{"id":"table-of-contents","title":"Table of Contents","level":2,"section":"Table of Contents"},{"id":"introduction","title":"Introduction","level":2,"section":"Introduction"},{"id":"requirements","title":"Requirements","level":3,"section":"Introduction"},{"id":"extension-structure","title":"Extension Structure","level":2,"section":"Extension Structure"},{"id":"manifest-file","title":"Manifest File","level":2,"section":"Manifest File"},{"id":"complete-manifest-example","title":"Complete Manifest Example","level":3,"section":"Manifest File"},{"id":"manifest-fields","title":"Manifest Fields","level":3,"section":"Manifest File"},{"id":"quality-options","title":"Quality Options","level":3,"section":"Manifest File"},{"id":"quality-specific-settings","title":"Quality-Specific Settings","level":3,"section":"Manifest File"},{"id":"permissions","title":"Permissions","level":3,"section":"Manifest File"},{"id":"extension-types","title":"Extension Types","level":3,"section":"Manifest File"},{"id":"settings","title":"Settings","level":3,"section":"Manifest File"},{"id":"button-setting-type","title":"Button Setting Type","level":3,"section":"Manifest File"},{"id":"custom-search-behavior","title":"Custom Search Behavior","level":3,"section":"Manifest File"},{"id":"thumbnail-ratio-presets","title":"Thumbnail Ratio Presets","level":4,"section":"Manifest File"},{"id":"custom-url-handler","title":"Custom URL Handler","level":3,"section":"Manifest File"},{"id":"album--playlist-functions-v301","title":"Album & Playlist Functions (v3.0.1+)","level":3,"section":"Manifest File"},{"id":"artist-support","title":"Artist Support","level":3,"section":"Manifest File"},{"id":"home-feed-support","title":"Home Feed Support","level":3,"section":"Manifest File"},{"id":"track-enrichment","title":"Track Enrichment","level":3,"section":"Manifest File"},{"id":"custom-track-matching","title":"Custom Track Matching","level":3,"section":"Manifest File"},{"id":"post-processing-hooks","title":"Post-Processing Hooks","level":3,"section":"Ma ``` -------------------------------- ### GET /getDownloadProgress Source: https://context7.com/zarzet/spotiflac-mobile/llms.txt Retrieves the current progress status for active downloads. ```APIDOC ## GET /getDownloadProgress ### Description Retrieves the current progress percentage for a specific download item. ### Method GET ### Response #### Success Response (200) - **progress** (integer) - Percentage of completion ``` -------------------------------- ### GET /artists/{artistId} Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/docs.html Retrieves detailed information for a specific artist, including their albums. ```APIDOC ## GET https://api.mymusic.com/artists/{artistId} ### Description Fetches details for a specific artist by ID, including their albums. ### Method GET ### Endpoint https://api.mymusic.com/artists/{artistId} ### Parameters #### Path Parameters - **artistId** (string) - Required - The unique identifier of the artist. ### Response #### Success Response (200) - **Object** - An artist object containing id, name, images, and an albums array. ``` -------------------------------- ### Manifest File with Quality-Specific Settings Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/downloads.html Demonstrates how to define quality-specific settings within the manifest file. This enables granular control over audio quality configurations. ```json { "id": "com.example.spotiflac.extension", "version": "1.0.0", "name": "My Spotiflac Extension", "main": "index.js", "logo": "./logo.png", "features": [ "player", "search" ], "quality": { "96": { "bitrate": 96, "name": "Standard" }, "160": { "bitrate": 160, "name": "High" }, "320": { "bitrate": 320, "name": "Lossless" } } } ``` -------------------------------- ### GET /albums/{albumId} Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/docs.html Retrieves detailed information for a specific album, including its tracklist. ```APIDOC ## GET https://api.mymusic.com/albums/{albumId} ### Description Fetches details for a specific album by ID, including a list of tracks. ### Method GET ### Endpoint https://api.mymusic.com/albums/{albumId} ### Parameters #### Path Parameters - **albumId** (string) - Required - The unique identifier of the album. ### Response #### Success Response (200) - **Object** - An album object containing id, name, artists, release_date, total_tracks, images, and a tracks array. ``` -------------------------------- ### GET /tracks/{trackId} Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/docs.html Retrieves detailed information for a specific track by its unique identifier. ```APIDOC ## GET https://api.mymusic.com/tracks/{trackId} ### Description Fetches details for a specific track by ID. ### Method GET ### Endpoint https://api.mymusic.com/tracks/{trackId} ### Parameters #### Path Parameters - **trackId** (string) - Required - The unique identifier of the track. ### Response #### Success Response (200) - **Object** - A track object containing id, name, artists, album_name, album_artist, isrc, duration_ms, track_number, disc_number, release_date, and images. ``` -------------------------------- ### Using fetch() API Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/downloads.html Demonstrates the usage of the `fetch()` API for making network requests. This is a standard browser API available in Spotiflac extensions. ```javascript async function fetchData() { try { const response = await fetch("https://api.example.com/data"); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); console.log(data); } catch (error) { console.error("Error fetching data:", error); } } ``` -------------------------------- ### Implement TOTP Authentication Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/docs.html Example implementation of Time-based One-Time Password generation using HMAC-SHA1. ```javascript function generateTOTP(secret, counter) { // Decode base32 secret to bytes const key = base32Decode(secret); // Convert counter to 8 bytes (big-endian) const counterBytes = []; let c = counter; for (let i = 7; i >= 0; i--) { counterBytes[i] = c & 0xff; c = Math.floor(c / 256); } // HMAC-SHA1 - key and message can be arrays of bytes const hmac = utils.hmacSHA1(key, counterBytes); // Dynamic truncation const offset = hmac[hmac.length - 1] & 0x0f; const code = ((hmac[offset] & 0x7f) << 24) | ((hmac[offset + 1] & 0xff) << 16) | ((hmac[offset + 2] & 0xff) << 8) | (hmac[offset + 3] & 0xff); return (code % 1000000).toString().padStart(6, "0"); } // Usage const counter = Math.floor(Date.now() / 1000 / 30); const totpCode = generateTOTP(base32Secret, counter); ``` -------------------------------- ### Implement Metadata Provider Logic Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/docs.html Provides the core logic for initializing the extension, cleaning up resources, and searching for tracks using an external API. The `searchTracks` function must adhere to the expected return format. ```javascript let settings = {}; function initialize(config) { settings = config || {}; log.info("Free Music API initialized"); return true; } function cleanup() { log.info("Cleanup"); } function searchTracks(query, limit) { const url = "https://api.freemusic.com/search?q=" + encodeURIComponent(query) + "&limit=" + limit; const response = http.get(url, {}); if (!response.ok) return []; const data = JSON.parse(response.body); return data.results.map((t) => ({ id: t.id, name: t.title, artists: t.artist, album_name: t.album, isrc: t.isrc, duration_ms: t.duration_ms, images: t.artwork, })); } // REQUIRED: Register the extension registerExtension({ initialize: initialize, cleanup: cleanup, searchTracks: searchTracks }); ``` -------------------------------- ### Initialize Sections Data Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/docs.html Internal configuration for documentation navigation structure. ```javascript const sectionsData = [{"id": "table-of-contents", "title": "Table of Contents", "children": [], "items": [{"id": "table-of-contents", "title": "Table of Contents", "level": 2}]}, {"id": "introduction", "title": "Introduction", "children": [{"id": "requirements", "title": "Requirements", "level": 3}], "items": [{"id": "introduction", "title": "Introduction", "level": 2}, {"id": "requirements", "title": "Requirements", "level": 3}]}, {"id": "extension-structure", "title": "Extension Structure", "children": [], "items": [{"id": "extension-structure", "title": "Extension Structure", "level": 2}]}, {"id": "manifest-file", "title": "Manifest File", "children": [{"id": "complete-manifest-example", "title": "Complete Manifest Example", "level": 3}, {"id": "manifest-fields", "title": "Manifest Fields", "level": 3}, {"id": "quality-options", "title": "Quality Options", "level": 3}, {"id": "quality-specific-settings", "title": "Quality-Specific Settings", "level": 3}, {"id": "permissions", "title": "Permissions", "level": 3}, {"id": "extension-types", "title": "Extension Types", "level": 3}, {"id": "settings", "title": "Settings", "level": 3}, {"id": "button-setting-type", "title": "Button Setting Type", "level": 3}, {"id": "custom-search-behavior", "title": "Custom Search Behavior", "level": 3}, {"id": "custom-url-handler", "title": "Custom URL Handler", "level": 3}, {"id": "album--playlist-functions-v301", "title": "Album & Playlist Functions (v3.0.1+)", "level": 3}, {"id": "artist-support", "title": "Artist Support", "level": 3}, {"id": "home-feed-support", "title": "Home Feed Support", "level": 3}, {"id": "track-enrichment", "title": "Track Enrichment", "level": 3}, {"id": "custom-track-matching", "title": "Custom Track Matching", "level": 3}, {"id": "post-processing-hooks", "title": "Post-Processing Hooks", "level": 3}, {"id": "lyrics-provider", "title": "Lyrics Provider", "level": 3}], "items": [{"id": "manifest-file", "title": "Manifest File", "level": 2}, {"id": "complete-manifest-example", "title": "Complete Manifest Example", "level": 3}, {"id": "manifest-fields", "title": "Manifest Fields", "level": 3}, {"id": "quality-options", "title": "Quality Options", "level": 3}, {"id": "quality-specific-settings", "title": "Quality-Specific Settings", "level": 3}, {"id": "permissions", "title": "Permissions", "level": 3}, {"id": "extension-types", "title": "Extension Types", "level": 3}, {"id": "settings", "title": "Settings", "level": 3}, {"id": "button-setting-type", "title": "Button Setting Type", "level": 3}, {"id": "custom-search-behavior", "title": "Custom Search Behavior", "level": 3}, {"id": "thumbnail-ratio-presets", "title": "Thumbnail Ratio Presets", "level": 4}, {"id": "custom-url-handler", "title": "Custom URL Handler", "level": 3}, {"id": "album--playlist-functions-v301", "title": "Album & Pl ``` -------------------------------- ### Extension Store Management Source: https://context7.com/zarzet/spotiflac-mobile/llms.txt Methods for interacting with the extension store, including browsing, searching, and installing extensions. ```APIDOC ## PlatformBridge.getStoreExtensions ### Description Retrieves a list of available extensions from the store. ### Parameters #### Request Body - **forceRefresh** (bool) - Optional - Whether to ignore cache and fetch fresh data. ### Response - **List** - A list of extensions including name, version, description, category, and download count. ``` ```APIDOC ## PlatformBridge.downloadStoreExtension ### Description Downloads and installs an extension from the store to a local directory. ### Parameters #### Request Body - **extension-id** (String) - Required - The ID of the extension to download. - **destination** (String) - Required - The local file path for installation. ``` ```APIDOC ## PlatformBridge.setStoreRegistryUrl ### Description Configures a custom registry URL for the extension store. ### Parameters #### Request Body - **url** (String) - Required - The registry URL. ``` -------------------------------- ### GET /search Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/docs.html Searches for tracks based on a query string and returns a list of transformed track objects. ```APIDOC ## GET https://api.mymusic.com/search ### Description Searches for tracks by query and returns a list of tracks formatted for SpotiFLAC. ### Method GET ### Endpoint https://api.mymusic.com/search ### Parameters #### Query Parameters - **q** (string) - Required - The search query string. - **type** (string) - Required - Set to 'track'. - **limit** (number) - Optional - Max number of results to return. ### Response #### Success Response (200) - **Array** (Object) - A list of track objects containing id, name, artists, album_name, album_artist, isrc, duration_ms, track_number, disc_number, release_date, and images. ``` -------------------------------- ### Using URL / URLSearchParams Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/downloads.html Example of utilizing the `URL` and `URLSearchParams` APIs for parsing and manipulating URLs and query strings. ```javascript const urlString = "https://example.com/path?name=Spotiflac&version=1.0"; // Parse URL const url = new URL(urlString); console.log("Hostname:", url.hostname); // Output: "example.com" console.log("Pathname:", url.pathname); // Output: "/path" // Manipulate query parameters const params = url.searchParams; params.append("newParam", "newValue"); console.log("Updated URL Search:", url.search); // Output: "?name=Spotiflac&version=1.0&newParam=newValue" // Get a specific parameter console.log("Name parameter:", params.get("name")); // Output: "Spotiflac" ``` -------------------------------- ### Implementing getAlbum, getPlaylist, and Registration Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/docs.html Functions to fetch track details for albums and playlists, followed by extension registration. ```javascript /** * Fetch album tracks by ID * @param {string} albumId - Album ID from search result * @returns {Object} Album with tracks array */ function getAlbum(albumId) { const data = fetchAlbumData(albumId); return { id: albumId, name: data.title, artists: data.artist, cover_url: data.thumbnail, release_date: data.year, total_tracks: data.tracks.length, album_type: data.type, // "album", "ep", "single" tracks: data.tracks.map(t => ({ id: t.id, name: t.title, artists: t.artist, album_name: data.title, duration_ms: t.duration * 1000, cover_url: t.thumbnail || data.thumbnail, track_number: t.trackNumber, provider_id: "your-extension-id" })), provider_id: "your-extension-id" }; } /** * Fetch playlist tracks by ID * @param {string} playlistId - Playlist ID from search result * @returns {Object} Playlist with tracks array */ function getPlaylist(playlistId) { const data = fetchPlaylistData(playlistId); return { id: playlistId, name: data.title, owner: data.owner, cover_url: data.thumbnail, total_tracks: data.tracks.length, tracks: data.tracks.map(t => ({ id: t.id, name: t.title, artists: t.artist, album_name: t.album || data.title, duration_ms: t.duration * 1000, cover_url: t.thumbnail, provider_id: "your-extension-id" })), provider_id: "your-extension-id" }; } // Register functions registerExtension({ initialize: initialize, customSearch: customSearch, getAlbum: getAlbum, // Required for album support getPlaylist: getPlaylist, // Required for playlist support // ... other functions }); ``` -------------------------------- ### GET /getArtist Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/docs.html Fetches detailed artist information including a list of associated albums by the artist's unique identifier. ```APIDOC ## GET /getArtist ### Description Fetches artist info and albums by ID. ### Parameters #### Path Parameters - **artistId** (string) - Required - Artist ID from search result ### Response #### Success Response (200) - **id** (string) - Artist ID - **name** (string) - Artist name - **image_url** (string) - Artist image URL - **albums** (array) - Array of album objects - **provider_id** (string) - Your extension ID ### Album Object Schema - **id** (string) - Album ID - **name** (string) - Album title - **artists** (string) - Artist name(s) - **cover_url** (string) - Album artwork URL - **release_date** (string) - Release date (YYYY or YYYY-MM-DD) - **total_tracks** (number) - Number of tracks - **album_type** (string) - "album", "ep", "single", "compilation" - **provider_id** (string) - Your extension ID ``` -------------------------------- ### GET /checkAvailability Source: https://context7.com/zarzet/spotiflac-mobile/llms.txt Checks the availability of a specific track across supported streaming services using Spotify ID and ISRC. ```APIDOC ## GET /checkAvailability ### Description Checks if a track is available for download across supported streaming services (Tidal, Qobuz, Deezer, Amazon). ### Method GET ### Parameters #### Query Parameters - **spotifyId** (string) - Required - The Spotify track ID. - **isrc** (string) - Required - The ISRC code of the track. ### Response #### Success Response (200) - **tidal** (boolean) - Availability on Tidal - **qobuz** (boolean) - Availability on Qobuz - **deezer** (boolean) - Availability on Deezer - **amazon** (boolean) - Availability on Amazon ``` -------------------------------- ### PKCE OAuth Flow (Recommended) Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/downloads.html Illustrates the recommended Proof Key for Code Exchange (PKCE) flow for OAuth 2.0 authorization. This enhances security for public clients like mobile apps. ```javascript async function initiatePkceFlow() { const clientId = "YOUR_CLIENT_ID"; const redirectUri = "YOUR_REDIRECT_URI"; const scope = "your_scopes"; // Generate code verifier and challenge const codeVerifier = Spicetify.Platform.Auth.generateCodeVerifier(); const codeChallenge = await Spicetify.Platform.Auth.generateCodeChallenge(codeVerifier); // Construct authorization URL const authUrl = `https://accounts.spotify.com/authorize?client_id=${clientId}&response_type=code&redirect_uri=${redirectUri}&scope=${scope}&code_challenge_method=S256&code_challenge=${codeChallenge}`; // Redirect user or open in a browser window.open(authUrl, "_blank"); // In a real app, you would listen for the redirect and exchange the code for a token // using Spicetify.Platform.Auth.requestToken(code, codeVerifier); } // Example usage (e.g., on a button click): // document.getElementById("authButton").addEventListener("click", initiatePkceFlow); ``` -------------------------------- ### Dart Localization Example Source: https://github.com/zarzet/spotiflac-mobile/blob/main/CONTRIBUTING.md Use the AppLocalizations class for user-facing strings to ensure proper localization. Avoid hardcoding strings directly. ```dart // Good Text(AppLocalizations.of(context)!.downloadComplete) // Bad Text('Download Complete') ``` -------------------------------- ### Implement VM Synchronization for Extensions Source: https://github.com/zarzet/spotiflac-mobile/blob/main/CHANGELOG.md Use a sync.Mutex to prevent race conditions when accessing the Goja VM across multiple goroutines. ```go type LoadedExtension struct { VMMu sync.Mutex // ... other fields } ``` -------------------------------- ### HMAC-SHA1 for TOTP Example Source: https://github.com/zarzet/spotiflac-mobile/blob/main/site/downloads.html Illustrates how to compute an HMAC-SHA1 hash, often used in Time-based One-Time Passwords (TOTP). Requires the `Crypto` API. ```javascript async function generateHmacSha1(key, message) { const crypto = Spicetify.Platform.Crypto; const encoder = new TextEncoder(); const keyData = encoder.encode(key); const messageData = encoder.encode(message); const cryptoKey = await crypto.subtle.importKey( "raw", keyData, { name: "HMAC", hash: "SHA-1" }, false, ["sign", "verify"] ); const signature = await crypto.subtle.sign("HMAC", cryptoKey, messageData); // Convert signature to hex string for display const hashArray = Array.from(new Uint8Array(signature)); const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); return hashHex; } // Example usage: const secretKey = "YOUR_SECRET_KEY"; const timeBasedMessage = "1234567890"; // Example time step generateHmacSha1(secretKey, timeBasedMessage).then(hash => { console.log("HMAC-SHA1 Hash:", hash); }); ```