### Install nowplaying package Source: https://github.com/joeyeamigh/nowplaying/blob/main/README.md Use a package manager to add the dependency to your project. ```bash npm install node-nowplaying # or yarn add node-nowplaying # or bun add node-nowplaying ``` -------------------------------- ### Development setup commands Source: https://github.com/joeyeamigh/nowplaying/blob/main/README.md Commands to clone, build, and run the project locally. ```bash git clone https://github.com/JoeyEamigh/nowplaying.git cd nowplaying bun install bun run build # run the example cd bindings && bun run example ``` -------------------------------- ### subscribe() Source: https://context7.com/joeyeamigh/nowplaying/llms.txt Starts listening for media events from the system. This must be called before any playback control methods. ```APIDOC ## subscribe() Starts listening for media events from the system. This initializes the platform-specific media session handler and begins emitting events through the callback whenever the media state changes. Must be called before any playback control methods. ### Method ``` async ``` ### Endpoint ``` player.subscribe() ``` ### Request Example ```typescript import { NowPlaying } from 'node-nowplaying'; const player = new NowPlaying((event) => { // This callback fires whenever media state changes console.log(`Now playing: ${event.trackName} by ${event.artist?.join(', ')}`); console.log(`Status: ${event.isPlaying ? 'Playing' : 'Paused'}`); if (event.thumbnail) { // Thumbnail is either a file path or base64 data URL console.log('Thumbnail available:', event.thumbnail.substring(0, 50) + '...'); } }); // Start receiving media events await player.subscribe(); // Keep the process alive to continue receiving events await new Promise((resolve) => setTimeout(resolve, 60000)); // Clean up when done await player.unsubscribe(); ``` ``` -------------------------------- ### Complete Media Monitoring Application Source: https://context7.com/joeyeamigh/nowplaying/llms.txt This example demonstrates a full application for media monitoring, including logging, playback control, and cleanup. It requires importing 'node-nowplaying' and includes helper functions for delays and log handling. ```typescript import { NowPlaying, type NowPlayingMessage, type LogMessage } from 'node-nowplaying'; // Helper function for delays const sleep = (seconds: number) => new Promise(resolve => setTimeout(resolve, seconds * 1000)); // Log handler for debugging const logCallback = (log: LogMessage) => { const levels: Record = { 'TRACE': 'πŸ”', 'DEBUG': 'πŸ›', 'INFO': 'ℹ️', 'WARN': '⚠️', 'ERROR': '❌' }; console.log(`${levels[log.level] || 'β€’'} [${log.timestamp}] ${log.target}: ${log.message}`); }; // Media state handler const mediaCallback = (event: NowPlayingMessage) => { console.log('\n--- Media Update ---'); console.log(`🎡 ${event.trackName}`); console.log(`πŸ‘€ ${event.artist?.join(', ') || 'Unknown Artist'}`); console.log(`πŸ’Ώ ${event.album || 'Unknown Album'}`); console.log(`${event.isPlaying ? '▢️ Playing' : '⏸️ Paused'}`); console.log(`πŸ”Š Volume: ${event.volume}%`); if (event.trackProgress && event.trackDuration) { const progress = Math.round((event.trackProgress / event.trackDuration) * 100); console.log(`⏱️ Progress: ${progress}%`); } console.log('-------------------\n'); }; async function main() { try { // Create player with logging enabled const player = new NowPlaying(mediaCallback, { logLevelDirective: 'n_nowplaying=info,nowplaying=info', logCallback }); console.log('Starting media monitoring...'); await player.subscribe(); // Wait and observe current media await sleep(5); // Pause playback console.log('Pausing...'); await player.pause(); await sleep(3); // Resume playback console.log('Resuming...'); await player.play(); await sleep(3); // Skip to next track console.log('Skipping to next track...'); await player.nextTrack(); await sleep(5); // Go back to previous track console.log('Going back...'); await player.previousTrack(); await sleep(3); // Clean up console.log('Stopping...'); await player.unsubscribe(); console.log('Done!'); } catch (error) { console.error('Error:', error); process.exit(1); } } main(); ``` -------------------------------- ### Control Playback with play() Source: https://context7.com/joeyeamigh/nowplaying/llms.txt Starts media playback, optionally targeting a specific device ID on Windows or Linux. ```typescript import { NowPlaying } from 'node-nowplaying'; const player = new NowPlaying((event) => { console.log(`${event.trackName} - ${event.isPlaying ? 'Playing' : 'Paused'}`); }); await player.subscribe(); // Start playback on all devices await player.play(); // Or target a specific device (Windows/Linux only) await player.play('spotify'); ``` -------------------------------- ### Subscribe to Media Events Source: https://context7.com/joeyeamigh/nowplaying/llms.txt Initializes the media session handler and starts emitting events. Must be called before control methods. ```typescript import { NowPlaying } from 'node-nowplaying'; const player = new NowPlaying((event) => { // This callback fires whenever media state changes console.log(`Now playing: ${event.trackName} by ${event.artist?.join(', ')}`); console.log(`Status: ${event.isPlaying ? 'Playing' : 'Paused'}`); if (event.thumbnail) { // Thumbnail is either a file path or base64 data URL console.log('Thumbnail available:', event.thumbnail.substring(0, 50) + '...'); } }); // Start receiving media events await player.subscribe(); // Keep the process alive to continue receiving events await new Promise((resolve) => setTimeout(resolve, 60000)); // Clean up when done await player.unsubscribe(); ``` -------------------------------- ### Seek to Specific Position with seekTo() Source: https://context7.com/joeyeamigh/nowplaying/llms.txt Seeks to a specific position in the current track. Position is specified in milliseconds from the start of the track. ```typescript import { NowPlaying } from 'node-nowplaying'; const player = new NowPlaying((event) => { console.log(`Progress: ${event.trackProgress}ms / ${event.trackDuration}ms`); if (event.canFastForward) { console.log('Seeking is supported'); } }); await player.subscribe(); // Seek to 30 seconds into the track await player.seekTo(30000); console.log('Seeked to 30 seconds'); // Seek to 1 minute 30 seconds await player.seekTo(90000); console.log('Seeked to 1:30'); ``` -------------------------------- ### Initialize NowPlaying Instance Source: https://context7.com/joeyeamigh/nowplaying/llms.txt Demonstrates basic instantiation with event callbacks and advanced configuration using logging options. ```typescript import { NowPlaying, type NowPlayingMessage, type LogMessage, type NowPlayingOptions } from 'node-nowplaying'; // Basic instantiation with event callback const player = new NowPlaying((event: NowPlayingMessage) => { console.log('Track:', event.trackName); console.log('Artist:', event.artist?.join(', ')); console.log('Album:', event.album); console.log('Playing:', event.isPlaying); console.log('Progress:', event.trackProgress, '/', event.trackDuration, 'ms'); console.log('Volume:', event.volume, '%'); }); // Advanced instantiation with logging options const options: NowPlayingOptions = { logLevelDirective: 'n_nowplaying=debug,nowplaying=debug', logCallback: (log: LogMessage) => { console.log(`[${log.timestamp}] [${log.level}] ${log.target}: ${log.message}`); } }; const playerWithLogging = new NowPlaying((event) => { console.log('[NowPlaying]:', event); }, options); ``` -------------------------------- ### NowPlaying Constructor Source: https://context7.com/joeyeamigh/nowplaying/llms.txt Instantiate the NowPlaying module. It accepts a callback for media state updates and an optional configuration object for advanced logging. ```APIDOC ## NowPlaying Constructor Creates a new NowPlaying instance with a callback function that receives media state updates. An optional configuration object allows enabling debug logging with custom log handlers and level directives. ### Request Example ```typescript import { NowPlaying, type NowPlayingMessage, type LogMessage, type NowPlayingOptions } from 'node-nowplaying'; // Basic instantiation with event callback const player = new NowPlaying((event: NowPlayingMessage) => { console.log('Track:', event.trackName); console.log('Artist:', event.artist?.join(', ')); console.log('Album:', event.album); console.log('Playing:', event.isPlaying); console.log('Progress:', event.trackProgress, '/', event.trackDuration, 'ms'); console.log('Volume:', event.volume, '%'); }); // Advanced instantiation with logging options const options: NowPlayingOptions = { logLevelDirective: 'n_nowplaying=debug,nowplaying=debug', logCallback: (log: LogMessage) => { console.log(`[${log.timestamp}] [${log.level}] ${log.target}: ${log.message}`); } }; const playerWithLogging = new NowPlaying((event) => { console.log('[NowPlaying]:', event); }, options); ``` ``` -------------------------------- ### Basic media control usage Source: https://github.com/joeyeamigh/nowplaying/blob/main/README.md Initialize the NowPlaying instance, subscribe to events, and execute playback commands. ```typescript import { NowPlaying } from 'node-nowplaying'; // create a new instance with a callback for media updates const player = new NowPlaying(event => { console.log('[NowPlaying]:', event); }); // initialize and subscribe to media events await player.subscribe(); // control playback await player.play(); await player.pause(); await player.nextTrack(); await player.previousTrack(); // clean up when done await player.unsubscribe(); ``` -------------------------------- ### NowPlaying constructor signature Source: https://github.com/joeyeamigh/nowplaying/blob/main/README.md Define the callback and optional configuration for the class instance. ```typescript new NowPlaying( callback: (event: NowPlayingMessage) => void, options?: NowPlayingOptions ) ``` -------------------------------- ### play() Source: https://context7.com/joeyeamigh/nowplaying/llms.txt Begins playback of the current media. Optionally accepts a device ID to target a specific player. ```APIDOC ## play() Begins playback of the current media. Optionally accepts a device ID to target a specific player when multiple media sessions are active (device targeting is ignored on macOS). ### Method ``` async ``` ### Endpoint ``` player.play([deviceId]) ``` ### Parameters #### Query Parameters - **deviceId** (string) - Optional - The ID of the device/player to target. Ignored on macOS. ### Request Example ```typescript import { NowPlaying } from 'node-nowplaying'; const player = new NowPlaying((event) => { console.log(`${event.trackName} - ${event.isPlaying ? 'Playing' : 'Paused'}`); }); await player.subscribe(); // Start playback on all devices await player.play(); // Or target a specific device (Windows/Linux only) await player.play('spotify'); ``` ``` -------------------------------- ### sendCommand() Source: https://context7.com/joeyeamigh/nowplaying/llms.txt Sends a raw command to the media player. ```APIDOC ## sendCommand() ### Description Sends a raw command to the media player using the PlayerCommand interface. This provides lower-level access to all playback controls with explicit device targeting and command data. ### Parameters - **command** (PlayerCommand) - Required - The command object containing target device and data ``` -------------------------------- ### Configure logging for NowPlaying Source: https://github.com/joeyeamigh/nowplaying/blob/main/README.md Pass a custom log handler and directive to the constructor options. ```typescript import { NowPlaying, type LogMessage } from 'node-nowplaying'; // custom log handler const logCallback = (log: LogMessage) => { console.log( `[${log.timestamp}] [${log.level}] ${log.target}: ${log.message}`, ); }; // create with logging options const player = new NowPlaying(event => console.log('[NowPlaying]:', event), { logCallback, logLevelDirective: 'nowplaying=debug,n_nowplaying=debug', }); await player.subscribe(); ``` -------------------------------- ### NowPlaying Class Methods Source: https://github.com/joeyeamigh/nowplaying/blob/main/README.md Methods available on the NowPlaying instance to control media playback and manage event subscriptions. ```APIDOC ## NowPlaying Methods ### Description Methods to interact with media players, including playback control, seeking, and volume adjustment. ### Methods - **subscribe()** - Starts listening for media events. - **unsubscribe()** - Stops listening for media events. - **play(to?: string)** - Begin playback, optionally on a specific device. - **pause(to?: string)** - Pause playback, optionally on a specific device. - **playPause(to?: string)** - Toggle play/pause, optionally on a specific device. - **nextTrack(to?: string)** - Skip to the next track, optionally on a specific device. - **previousTrack(to?: string)** - Go to the previous track, optionally on a specific device. - **seekTo(positionMs: number, to?: string)** - Seek to a position in milliseconds. - **setVolume(volume: number, to?: string)** - Set volume level 0-100. - **setShuffle(shuffle: boolean, to?: string)** - Set shuffle mode. - **sendCommand(command: PlayerCommand)** - Send a custom command to the media player. ``` -------------------------------- ### NowPlayingOptions interface Source: https://github.com/joeyeamigh/nowplaying/blob/main/README.md Configuration options for the NowPlaying instance. ```typescript interface NowPlayingOptions { logCallback?: (event: LogMessage) => void; logLevelDirective?: string; } ``` -------------------------------- ### Send Raw Commands with sendCommand() Source: https://context7.com/joeyeamigh/nowplaying/llms.txt Sends a raw command to the media player using the PlayerCommand interface. This provides lower-level access to all playback controls with explicit device targeting and command data. ```typescript import { NowPlaying, type PlayerCommand } from 'node-nowplaying'; const player = new NowPlaying((event) => { console.log(`Device: ${event.device}`); console.log(`Device ID: ${event.deviceId}`); console.log(`Track: ${event.trackName}`); }); await player.subscribe(); // Send play command const playCommand: PlayerCommand = { to: undefined, // Target all devices (or specify device ID) data: { type: 'Play' } }; await player.sendCommand(playCommand); // Send pause command to specific device const pauseCommand: PlayerCommand = { to: 'spotify', data: { type: 'Pause' } }; await player.sendCommand(pauseCommand); // Send seek command const seekCommand: PlayerCommand = { to: undefined, data: { type: 'SeekTo', positionMs: 60000 } }; await player.sendCommand(seekCommand); // Send volume command const volumeCommand: PlayerCommand = { to: undefined, data: { type: 'SetVolume', volume: 75 } }; await player.sendCommand(volumeCommand); // Send shuffle command const shuffleCommand: PlayerCommand = { to: undefined, data: { type: 'SetShuffle', shuffle: true } }; await player.sendCommand(shuffleCommand); ``` -------------------------------- ### Go to Previous Track with previousTrack() Source: https://context7.com/joeyeamigh/nowplaying/llms.txt Returns to the previous track in the current playlist or queue, or restarts the current track depending on the media player's behavior. ```typescript import { NowPlaying } from 'node-nowplaying'; const player = new NowPlaying((event) => { console.log(`Track: ${event.trackName}`); console.log(`Progress: ${event.trackProgress}ms / ${event.trackDuration}ms`); }); await player.subscribe(); // Wait 10 seconds then go back await new Promise((resolve) => setTimeout(resolve, 10000)); await player.previousTrack(); console.log('Went to previous track'); ``` -------------------------------- ### NowPlaying Data Structures Source: https://github.com/joeyeamigh/nowplaying/blob/main/README.md Definitions for the message objects and configuration options used by the NowPlaying module. ```APIDOC ## Data Structures ### NowPlayingMessage - **trackName** (string) - Name of the current track - **isPlaying** (boolean) - Current playback status - **album** (string) - Optional album name - **artist** (Array) - List of artists - **trackDuration** (number) - Total duration in ms - **trackProgress** (number) - Current progress in ms - **volume** (number) - Current volume level ### NowPlayingOptions - **logCallback** (function) - Optional handler for log messages - **logLevelDirective** (string) - Configuration for logging verbosity ``` -------------------------------- ### setVolume() Source: https://context7.com/joeyeamigh/nowplaying/llms.txt Sets the playback volume level. ```APIDOC ## setVolume() ### Description Sets the playback volume level. Volume is specified as a percentage from 0 (muted) to 100 (maximum). ### Parameters - **volume** (number) - Required - Volume percentage (0-100) ``` -------------------------------- ### Set Playback Volume with setVolume() Source: https://context7.com/joeyeamigh/nowplaying/llms.txt Sets the playback volume level. Volume is specified as a percentage from 0 (muted) to 100 (maximum). ```typescript import { NowPlaying } from 'node-nowplaying'; const player = new NowPlaying((event) => { console.log(`Current volume: ${event.volume}%`); if (event.canChangeVolume) { console.log('Volume control is available'); } }); await player.subscribe(); // Set volume to 50% await player.setVolume(50); console.log('Set volume to 50%'); // Mute await player.setVolume(0); console.log('Muted'); // Set to maximum await player.setVolume(100); console.log('Set to maximum volume'); ``` -------------------------------- ### previousTrack() Source: https://context7.com/joeyeamigh/nowplaying/llms.txt Returns to the previous track or restarts the current one. ```APIDOC ## previousTrack() ### Description Returns to the previous track in the current playlist or queue, or restarts the current track depending on the media player's behavior. ``` -------------------------------- ### nextTrack() Source: https://context7.com/joeyeamigh/nowplaying/llms.txt Skips to the next track in the current playlist or queue. ```APIDOC ## nextTrack() ### Description Skips to the next track in the current playlist or queue. Optionally accepts a device ID to target a specific player. ``` -------------------------------- ### playPause() Source: https://context7.com/joeyeamigh/nowplaying/llms.txt Toggles between play and pause states. ```APIDOC ## playPause() ### Description Toggles between play and pause states. This is equivalent to pressing the play/pause button on a keyboard or remote control. ``` -------------------------------- ### Control Playback with pause() Source: https://context7.com/joeyeamigh/nowplaying/llms.txt Pauses current media playback, with optional device targeting. ```typescript import { NowPlaying } from 'node-nowplaying'; const player = new NowPlaying((event) => { console.log(`Playback state: ${event.isPlaying ? 'Playing' : 'Paused'}`); }); await player.subscribe(); // Wait for some playback await new Promise((resolve) => setTimeout(resolve, 5000)); // Pause playback await player.pause(); console.log('Paused playback'); ``` -------------------------------- ### NowPlayingMessage interface Source: https://github.com/joeyeamigh/nowplaying/blob/main/README.md Structure of the media state object returned by the library. ```typescript interface NowPlayingMessage { album?: string; artist?: Array; playlist?: string; playlistId?: string; trackName: string; shuffleState?: boolean; repeatState?: string; // "off", "all", "track" isPlaying: boolean; canFastForward: boolean; canSkip: boolean; canLike: boolean; canChangeVolume: boolean; canSetOutput: boolean; trackDuration?: number; trackProgress?: number; playbackRate?: number; volume: number; device?: string; id?: string; deviceId?: string; url?: string; thumbnail?: string; } ``` -------------------------------- ### setShuffle() Source: https://context7.com/joeyeamigh/nowplaying/llms.txt Enables or disables shuffle mode. ```APIDOC ## setShuffle() ### Description Enables or disables shuffle mode for the current playback session. ### Parameters - **shuffle** (boolean) - Required - True to enable, false to disable ``` -------------------------------- ### Toggle Playback with playPause() Source: https://context7.com/joeyeamigh/nowplaying/llms.txt Use playPause() to toggle between playing and paused states. This is equivalent to pressing the play/pause button on a keyboard or remote control. ```typescript import { NowPlaying } from 'node-nowplaying'; const player = new NowPlaying((event) => { console.log(`Now ${event.isPlaying ? 'playing' : 'paused'}: ${event.trackName}`); }); await player.subscribe(); // Toggle playback every 5 seconds setInterval(async () => { await player.playPause(); console.log('Toggled playback'); }, 5000); ``` -------------------------------- ### pause() Source: https://context7.com/joeyeamigh/nowplaying/llms.txt Pauses the currently playing media. Optionally accepts a device ID to target a specific player. ```APIDOC ## pause() Pauses the currently playing media. Optionally accepts a device ID to target a specific player when multiple media sessions are active. ### Method ``` async ``` ### Endpoint ``` player.pause([deviceId]) ``` ### Parameters #### Query Parameters - **deviceId** (string) - Optional - The ID of the device/player to target. Ignored on macOS. ### Request Example ```typescript import { NowPlaying } from 'node-nowplaying'; const player = new NowPlaying((event) => { console.log(`Playback state: ${event.isPlaying ? 'Playing' : 'Paused'}`); }); await player.subscribe(); // Wait for some playback await new Promise((resolve) => setTimeout(resolve, 5000)); // Pause playback await player.pause(); console.log('Paused playback'); ``` ``` -------------------------------- ### seekTo() Source: https://context7.com/joeyeamigh/nowplaying/llms.txt Seeks to a specific position in the current track. ```APIDOC ## seekTo() ### Description Seeks to a specific position in the current track. Position is specified in milliseconds from the start of the track. ### Parameters - **positionMs** (number) - Required - Position in milliseconds ``` -------------------------------- ### NowPlayingMessage Interface Usage Source: https://context7.com/joeyeamigh/nowplaying/llms.txt Use this snippet to access detailed information about the currently playing media. The NowPlayingMessage object is passed to your callback function when the media state changes. Ensure the 'node-nowplaying' library is imported. ```typescript import { NowPlaying, type NowPlayingMessage } from 'node-nowplaying'; const player = new NowPlaying((event: NowPlayingMessage) => { // Track information console.log('Track:', event.trackName); // Required: track name console.log('Album:', event.album); // Optional: album name console.log('Artist:', event.artist); // Optional: array of artist names console.log('URL:', event.url); // Optional: track URL console.log('ID:', event.id); // Optional: track identifier (not available on macOS) // Playback state console.log('Playing:', event.isPlaying); // Required: true if playing console.log('Progress:', event.trackProgress); // Optional: position in milliseconds console.log('Duration:', event.trackDuration); // Optional: total length in milliseconds console.log('Volume:', event.volume); // Required: 0-100 percentage console.log('Playback Rate:', event.playbackRate);// Optional: 1.0 = normal speed // Playlist information console.log('Playlist:', event.playlist); // Optional: playlist name console.log('Playlist ID:', event.playlistId); // Optional: playlist identifier console.log('Shuffle:', event.shuffleState); // Optional: shuffle enabled console.log('Repeat:', event.repeatState); // Optional: "off", "all", or "track" // Device information console.log('Device:', event.device); // Optional: device name console.log('Device ID:', event.deviceId); // Optional: device identifier // Capability flags console.log('Can Skip:', event.canSkip); // Required: can skip tracks console.log('Can Fast Forward:', event.canFastForward); // Required: can seek console.log('Can Like:', event.canLike); // Required: can like/favorite console.log('Can Change Volume:', event.canChangeVolume); // Required console.log('Can Set Output:', event.canSetOutput); // Required: can change output device // Thumbnail (path or base64 data URL) if (event.thumbnail) { if (event.thumbnail.startsWith('data:image')) { console.log('Thumbnail: Base64 encoded image'); } else { console.log('Thumbnail path:', event.thumbnail); } } }); await player.subscribe(); ``` -------------------------------- ### Skip to Next Track with nextTrack() Source: https://context7.com/joeyeamigh/nowplaying/llms.txt Skips to the next track in the current playlist or queue. Optionally accepts a device ID to target a specific player. ```typescript import { NowPlaying } from 'node-nowplaying'; const player = new NowPlaying((event) => { console.log(`Track changed to: ${event.trackName}`); if (event.canSkip) { console.log('Skip to next is available'); } }); await player.subscribe(); // Skip to next track await player.nextTrack(); console.log('Skipped to next track'); ``` -------------------------------- ### unsubscribe() Source: https://context7.com/joeyeamigh/nowplaying/llms.txt Stops listening for media events and cleans up resources. This should be called when you no longer need to monitor media playback. ```APIDOC ## unsubscribe() Stops listening for media events and cleans up resources. This should be called when you no longer need to monitor media playback to properly release system resources and stop the background event loop. ### Method ``` async ``` ### Endpoint ``` player.unsubscribe() ``` ### Request Example ```typescript import { NowPlaying } from 'node-nowplaying'; const player = new NowPlaying((event) => { console.log('Media update:', event.trackName); }); await player.subscribe(); // Monitor for 30 seconds await new Promise((resolve) => setTimeout(resolve, 30000)); // Stop monitoring and clean up await player.unsubscribe(); console.log('Stopped listening for media events'); ``` ``` -------------------------------- ### Enable/Disable Shuffle with setShuffle() Source: https://context7.com/joeyeamigh/nowplaying/llms.txt Enables or disables shuffle mode for the current playback session. ```typescript import { NowPlaying } from 'node-nowplaying'; const player = new NowPlaying((event) => { console.log(`Shuffle: ${event.shuffleState ? 'On' : 'Off'}`); console.log(`Repeat: ${event.repeatState}`); // "off", "all", or "track" }); await player.subscribe(); // Enable shuffle await player.setShuffle(true); console.log('Enabled shuffle mode'); // Disable shuffle await player.setShuffle(false); console.log('Disabled shuffle mode'); ``` -------------------------------- ### Unsubscribe from Media Events Source: https://context7.com/joeyeamigh/nowplaying/llms.txt Stops monitoring and releases system resources. Call this when media monitoring is no longer required. ```typescript import { NowPlaying } from 'node-nowplaying'; const player = new NowPlaying((event) => { console.log('Media update:', event.trackName); }); await player.subscribe(); // Monitor for 30 seconds await new Promise((resolve) => setTimeout(resolve, 30000)); // Stop monitoring and clean up await player.unsubscribe(); console.log('Stopped listening for media events'); ``` -------------------------------- ### LogMessage interface Source: https://github.com/joeyeamigh/nowplaying/blob/main/README.md Structure of log messages emitted by the library. ```typescript interface LogMessage { level: string; // "TRACE", "DEBUG", "INFO", "WARN", "ERROR" target: string; // Component that generated the log message: string; // Log content timestamp: string; // ISO 8601 format timestamp } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.