synxed-sdk 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,11 +7,12 @@ The official Synxed SDK for frontend developers to integrate high-quality music
7
7
 
8
8
  ## Features
9
9
 
10
- - **Simple Integration**: Play songs or playlists with just a few lines of code.
11
- - **Real-time Streaming**: Powered by Socket.IO and Protobuf for low-latency control.
12
- - **Framework Agnostic**: Works with React, Vue, Angular, or Vanilla JS.
13
- - **Type Safe**: Fully written in TypeScript with comprehensive definitions.
14
- - **Cross-Browser**: Robust audio playback using Howler.js.
10
+ - ** Simple Integration**: Play songs or playlists with just a few lines of code.
11
+ - **🎧 High-Fidelity Streaming**: Robust HLS support via `hls.js` for seamless playback across all browsers.
12
+ - **📊 Built-in Analytics**: Automatic session tracking and stream event reporting.
13
+ - **🛠 Framework Agnostic**: Works with React, Vue, Angular, or Vanilla JS.
14
+ - **🔒 Secure**: Designed for private, signed-URL streaming with backend proxy support.
15
+ - **💪 Type Safe**: Fully written in TypeScript with comprehensive definitions.
15
16
 
16
17
  ## Installation
17
18
 
@@ -19,28 +20,79 @@ The official Synxed SDK for frontend developers to integrate high-quality music
19
20
  npm install synxed-sdk
20
21
  ```
21
22
 
23
+ > **Note**: For HLS streaming support in browsers like Chrome, Firefox, and Edge, please ensure `hls.js` is installed in your project.
24
+
25
+ ```bash
26
+ npm install hls.js
27
+ ```
28
+
22
29
  ## Quick Start
23
30
 
31
+ ### Initialize the Player
32
+
24
33
  ```typescript
25
- import { SynxedPlayer } from 'synxed-sdk';
34
+ import { SynxedPlayer } from "synxed-sdk";
26
35
 
27
36
  const player = new SynxedPlayer({
28
- apiKey: 'YOUR_SYNXED_API_KEY',
29
- serverUrl: 'https://synxed-backend-production.up.railway.app/',
30
- autoConnect: true
37
+ apiKey: "YOUR_SYNXED_API_KEY",
38
+ serverUrl: "https://api.synxed.com", // or your local backend URL
39
+ autoConnect: true,
40
+ });
41
+ ```
42
+
43
+ ### Play a Playlist
44
+
45
+ Provide a playlist code generated from the Synxed platform.
46
+
47
+ ```typescript
48
+ // Start playback from a playlist
49
+ player.playPlaylist({
50
+ playlistCode: "sxpl_VZCCGQAQJV",
31
51
  });
52
+ ```
32
53
 
33
- // Play a song by Catalog ID
34
- player.playSong({ catalogTrackId: 'track-uuid-here' });
54
+ ### Play a Single Song
35
55
 
36
- // Listen for state changes
37
- player.on('stateChange', (state) => {
38
- console.log('Player status:', state.status);
56
+ ```typescript
57
+ // Play a song from the global catalog
58
+ player.playSong({
59
+ catalogTrackId: "2dcad8e0-3695-4971-9e35-f762f3f9d3a5",
39
60
  });
40
61
 
41
- // Listen for time updates
42
- player.on('timeUpdate', ({ currentTime, duration }) => {
43
- console.log(`Progress: ${currentTime} / ${duration}`);
62
+ // Or an internal track from your own library
63
+ player.playSong({
64
+ internalTrackId: "track-uuid-here",
65
+ });
66
+ ```
67
+
68
+ ### Control Playback
69
+
70
+ ```typescript
71
+ player.pause();
72
+ player.resume();
73
+ player.stop();
74
+ player.seek(30000); // Seek to 30 seconds
75
+ player.setVolume(0.8);
76
+ player.skip(); // Skip to next track in playlist
77
+ ```
78
+
79
+ ## Listening for Events
80
+
81
+ ```typescript
82
+ // Track state changes (idle, loading, playing, paused, error)
83
+ player.on("stateChange", (state) => {
84
+ console.log("Player status:", state.status);
85
+ });
86
+
87
+ // High-resolution time updates (60fps)
88
+ player.on("timeUpdate", ({ currentTime, duration }) => {
89
+ const progress = (currentTime / duration) * 100;
90
+ console.log(`Progress: ${progress.toFixed(2)}%`);
91
+ });
92
+
93
+ // Error handling
94
+ player.on("error", (err) => {
95
+ console.error("Playback Error:", err.message);
44
96
  });
45
97
  ```
46
98
 
@@ -48,33 +100,29 @@ player.on('timeUpdate', ({ currentTime, duration }) => {
48
100
 
49
101
  ### `new SynxedPlayer(config)`
50
102
 
51
- Initializes the player.
52
-
53
- - `apiKey`: Your developer API key.
54
- - `serverUrl`: The Synxed backend URL.
55
- - `autoConnect`: (Optional) Whether to connect immediately.
103
+ - `apiKey`: `string` (Required) Your developer API key.
104
+ - `serverUrl`: `string` (Required) Your Synxed backend URL.
105
+ - `autoConnect`: `boolean` (Default: `true`) Connect to the streaming socket immediately.
56
106
 
57
107
  ### Methods
58
108
 
59
- - `playSong(options)`: Plays a single track.
60
- - `playPlaylist(options)`: Starts playback from a playlist code.
61
- - `pause()`: Pauses the current track.
62
- - `resume()`: Resumes playback.
63
- - `stop()`: Stops playback and unloads the track.
64
- - `skip()`: Skips to the next track in the queue.
65
- - `seek(ms)`: Seeks to a specific position in milliseconds.
66
- - `setVolume(0-1)`: Adjusts the player volume.
67
- - `destroy()`: Cleans up the player and disconnects the socket.
109
+ - `playSong(options)`: `Promise<void>` Plays a single track by `catalogTrackId` or `internalTrackId`.
110
+ - `playPlaylist(options)`: `Promise<void>` Starts playback from a `playlistCode`.
111
+ - `pause()`: `void` Pauses the current stream.
112
+ - `resume()`: `void` Resumes a paused stream.
113
+ - `stop()`: `void` Stops playback and releases audio resources.
114
+ - `skip()`: `void` Skips to the next track in the playlist.
115
+ - `seek(ms)`: `void` Seeks to a specific position in milliseconds.
116
+ - `setVolume(0-1)`: `void` Sets the volume.
117
+ - `destroy()`: `void` Cleans up the player, listeners, and disconnects.
68
118
 
69
119
  ### Events
70
120
 
71
- - `stateChange`: Fired when the playback status changes (`idle`, `loading`, `playing`, `paused`, `error`).
72
- - `timeUpdate`: Fired during playback with current position and duration.
73
- - `trackChange`: Fired when the current track changes.
74
- - `connected`: Fired when the socket connects successfully.
75
- - `disconnected`: Fired when the socket disconnects.
76
- - `error`: Fired on any player or connection error.
121
+ - `stateChange`: `(state: PlayerState)` Fired when the engine state changes.
122
+ - `timeUpdate`: `({ currentTime, duration })` Fired during playback.
123
+ - `trackChange`: `(track: any)` Fired when a new track starts playing in a playlist.
124
+ - `error`: `(error: Error)` Fired on playback or connection failures.
77
125
 
78
126
  ## License
79
127
 
80
- MIT
128
+ MIT © [Synxed](https://synxed.com)
package/dist/index.d.mts CHANGED
@@ -64,6 +64,7 @@ declare class SynxedPlayer extends EventEmitter<SynxedEvents> {
64
64
  skip(): void;
65
65
  seek(ms: number): void;
66
66
  setVolume(volume: number): void;
67
+ private emitAnalytics;
67
68
  private handleServerMessage;
68
69
  private handleTrackEnded;
69
70
  private updateStatus;
package/dist/index.d.ts CHANGED
@@ -64,6 +64,7 @@ declare class SynxedPlayer extends EventEmitter<SynxedEvents> {
64
64
  skip(): void;
65
65
  seek(ms: number): void;
66
66
  setVolume(volume: number): void;
67
+ private emitAnalytics;
67
68
  private handleServerMessage;
68
69
  private handleTrackEnded;
69
70
  private updateStatus;
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- 'use strict';var socket_ioClient=require('socket.io-client'),k=require('protobufjs'),howler=require('howler');function _interopNamespace(e){if(e&&e.__esModule)return e;var n=Object.create(null);if(e){Object.keys(e).forEach(function(k){if(k!=='default'){var d=Object.getOwnPropertyDescriptor(e,k);Object.defineProperty(n,k,d.get?d:{enumerable:true,get:function(){return e[k]}});}})}n.default=e;return Object.freeze(n)}var k__namespace=/*#__PURE__*/_interopNamespace(k);var I=`
1
+ 'use strict';var socket_ioClient=require('socket.io-client'),k=require('protobufjs'),howler=require('howler');function _interopNamespace(e){if(e&&e.__esModule)return e;var n=Object.create(null);if(e){Object.keys(e).forEach(function(k){if(k!=='default'){var d=Object.getOwnPropertyDescriptor(e,k);Object.defineProperty(n,k,d.get?d:{enumerable:true,get:function(){return e[k]}});}})}n.default=e;return Object.freeze(n)}var k__namespace=/*#__PURE__*/_interopNamespace(k);var C=`
2
2
  syntax = "proto3";
3
3
 
4
4
  enum ContentKind {
@@ -18,30 +18,52 @@ enum ErrorCode {
18
18
  BAD_PROTOBUF = 6;
19
19
  }
20
20
 
21
+ enum SdkControlAction {
22
+ CONTROL_UNSPECIFIED = 0;
23
+ CONTROL_PLAY = 1;
24
+ CONTROL_PAUSE = 2;
25
+ CONTROL_STOP = 3;
26
+ CONTROL_SEEK = 4;
27
+ }
28
+
21
29
  message SdkClientInit {
22
- ContentKind content_kind = 1;
23
- string catalog_track_id = 2;
24
- string internal_track_id = 3;
25
- string playlist_code = 4;
26
- string listener_id = 5;
27
- string device_type = 6;
28
- string country_code = 7;
29
- string region = 8;
30
- string protocol_version = 9;
30
+ ContentKind contentKind = 1;
31
+ string catalogTrackId = 2;
32
+ string internalTrackId = 3;
33
+ string playlistCode = 4;
34
+ string categoryQuery = 5;
35
+ string listenerId = 6;
36
+ string deviceType = 7;
37
+ string countryCode = 8;
38
+ string region = 9;
39
+ string protocolVersion = 10;
40
+ }
41
+
42
+ message SdkClientControl {
43
+ SdkControlAction action = 1;
44
+ uint32 positionMs = 2;
45
+ }
46
+
47
+ message SdkClientAnalytics {
48
+ string eventType = 1;
49
+ uint32 positionMs = 2;
50
+ string extraJson = 3;
31
51
  }
32
52
 
33
53
  message SdkClientEnvelope {
34
54
  oneof payload {
35
55
  SdkClientInit init = 1;
56
+ SdkClientControl control = 2;
57
+ SdkClientAnalytics analytics = 3;
36
58
  }
37
59
  }
38
60
 
39
61
  message SdkServerInitAck {
40
- string session_id = 1;
41
- string playback_url = 2;
42
- bool is_hls = 3;
43
- uint32 heartbeat_interval_ms = 4;
44
- string content_summary = 5;
62
+ string sessionId = 1;
63
+ string playbackUrl = 2;
64
+ bool isHls = 3;
65
+ uint32 heartbeatIntervalMs = 4;
66
+ string contentSummary = 5;
45
67
  }
46
68
 
47
69
  message SdkServerError {
@@ -51,9 +73,9 @@ message SdkServerError {
51
73
 
52
74
  message SdkServerEnvelope {
53
75
  oneof payload {
54
- SdkServerInitAck init_ack = 1;
76
+ SdkServerInitAck initAck = 1;
55
77
  SdkServerError error = 2;
56
78
  }
57
79
  }
58
- `,f=k__namespace.parse(I).root,v=f.lookupType("SdkClientEnvelope"),y=f.lookupType("SdkServerEnvelope"),g=(e=>(e[e.UNSPECIFIED=0]="UNSPECIFIED",e[e.SONG=1]="SONG",e[e.PLAYLIST=2]="PLAYLIST",e[e.CATEGORY=3]="CATEGORY",e))(g||{}),S=(n=>(n[n.UNSPECIFIED=0]="UNSPECIFIED",n[n.UNAUTHORIZED=1]="UNAUTHORIZED",n[n.VALIDATION_ERROR=2]="VALIDATION_ERROR",n[n.NOT_FOUND=3]="NOT_FOUND",n[n.PROCESSING=4]="PROCESSING",n[n.SERVICE_UNAVAILABLE=5]="SERVICE_UNAVAILABLE",n[n.BAD_PROTOBUF=6]="BAD_PROTOBUF",n))(S||{});var c=class{static encodeClientEnvelope(i){let t=v.create(i);return v.encode(t).finish()}static decodeServerEnvelope(i){let t=y.decode(i);return y.toObject(t,{enums:String,longs:String,bytes:String,defaults:true,oneofs:true})}};var r=class{constructor(){this.listeners=new Map;}on(i,t){this.listeners.has(i)||this.listeners.set(i,new Set),this.listeners.get(i).add(t);}off(i,t){let s=this.listeners.get(i);s&&s.delete(t);}once(i,t){let s=((...e)=>{t(...e),this.off(i,s);});this.on(i,s);}emit(i,...t){let s=this.listeners.get(i);s&&s.forEach(e=>e(...t));}removeAllListeners(){this.listeners.clear();}};var l=class extends r{constructor(){super();this.socket=null;this.apiKey=null;this.serverUrl=null;}connect(t,s){this.socket?.connected||(this.apiKey=t,this.serverUrl=s,this.socket=socket_ioClient.io(`${s}/sdk`,{auth:{apiKey:t},transports:["websocket"]}),this.socket.on("connect",()=>{this.emit("connected");}),this.socket.on("disconnect",e=>{this.emit("disconnected",e);}),this.socket.on("connect_error",e=>{this.emit("error",e);}),this.socket.on("d",e=>{try{let d=e instanceof Uint8Array?e:new Uint8Array(e),T=c.decodeServerEnvelope(d);this.emit("message",T);}catch(d){this.emit("error",new Error(`Failed to decode message: ${d}`));}}));}disconnect(){this.socket&&(this.socket.disconnect(),this.socket=null);}sendInit(t){if(!this.socket?.connected)throw new Error("Socket not connected");let s={init:t},e=c.encodeClientEnvelope(s);this.socket.emit("d",e);}get isConnected(){return this.socket?.connected||false}};var u=class extends r{constructor(){super();this.howl=null;this.updateTimer=null;}load(t,s){this.stop(),this.emit("loading"),this.howl=new howler.Howl({src:[t],html5:true,format:s?["m3u8"]:void 0,onload:()=>{this.emit("loaded");},onloaderror:(e,d)=>{this.emit("error",d);},onplay:()=>{this.emit("playing"),this.startTimeUpdateLoop();},onpause:()=>{this.emit("paused"),this.stopTimeUpdateLoop();},onstop:()=>{this.emit("stopped"),this.stopTimeUpdateLoop();},onend:()=>{this.emit("ended"),this.stopTimeUpdateLoop();}});}play(){this.howl?.play();}pause(){this.howl?.pause();}stop(){this.howl?.stop(),this.howl?.unload(),this.howl=null;}seek(t){this.howl?.seek(t/1e3);}setVolume(t){this.howl?.volume(t);}get duration(){return (this.howl?.duration()||0)*1e3}get currentTime(){return (this.howl?.seek()||0)*1e3}startTimeUpdateLoop(){this.stopTimeUpdateLoop();let t=()=>{this.howl?.playing()&&(this.emit("timeupdate",{currentTime:this.currentTime,duration:this.duration}),this.updateTimer=requestAnimationFrame(t));};this.updateTimer=requestAnimationFrame(t);}stopTimeUpdateLoop(){this.updateTimer!==null&&(cancelAnimationFrame(this.updateTimer),this.updateTimer=null);}};var p=class extends r{constructor(){super();this.queue=[];this.currentIndex=-1;}setQueue(t){this.queue=t,this.currentIndex=t.length>0?0:-1,this.emit("queueUpdated",this.queue);}getCurrentTrack(){return this.currentIndex>=0&&this.currentIndex<this.queue.length?this.queue[this.currentIndex]:null}next(){if(this.currentIndex<this.queue.length-1){this.currentIndex++;let t=this.getCurrentTrack();return this.emit("trackChanged",t),t}return null}previous(){if(this.currentIndex>0){this.currentIndex--;let t=this.getCurrentTrack();return this.emit("trackChanged",t),t}return null}skipTo(t){if(t>=0&&t<this.queue.length){this.currentIndex=t;let s=this.getCurrentTrack();return this.emit("trackChanged",s),s}return null}get hasNext(){return this.currentIndex<this.queue.length-1}get hasPrevious(){return this.currentIndex>0}};var a=class extends Error{constructor(t,s){super(t);this.code=s;this.name="SynxedError";}},h=class extends a{constructor(i){super(i),this.name="SynxedConnectionError";}},m=class extends a{constructor(i){super(typeof i=="string"?i:"Playback failed"),this.name="SynxedPlaybackError";}},E=class extends a{constructor(i){super(i),this.name="SynxedProtocolError";}};var x=class extends r{constructor(t){super();this.status="idle";this.config=t,this.transport=new l,this.audio=new u,this.playlist=new p,this.setupListeners(),t.autoConnect&&this.connect();}setupListeners(){this.transport.on("connected",()=>this.emit("connected")),this.transport.on("disconnected",t=>this.emit("disconnected",t)),this.transport.on("error",t=>this.emit("error",new h(t.message))),this.transport.on("message",t=>this.handleServerMessage(t)),this.audio.on("playing",()=>this.updateStatus("playing")),this.audio.on("paused",()=>this.updateStatus("paused")),this.audio.on("stopped",()=>this.updateStatus("idle")),this.audio.on("loading",()=>this.updateStatus("loading")),this.audio.on("error",t=>this.emit("error",new m(t))),this.audio.on("timeupdate",t=>this.emit("timeUpdate",t)),this.audio.on("ended",()=>this.handleTrackEnded());}connect(){this.transport.connect(this.config.apiKey,this.config.serverUrl);}async playSong(t){this.transport.isConnected||this.connect(),this.transport.sendInit({content_kind:1,catalog_track_id:t.catalogTrackId,internal_track_id:t.internalTrackId,listener_id:t.listenerId});}async playPlaylist(t){this.transport.isConnected||this.connect(),this.transport.sendInit({content_kind:2,playlist_code:t.playlistCode,listener_id:t.listenerId});}pause(){this.audio.pause();}resume(){this.audio.play();}stop(){this.audio.stop();}skip(){let t=this.playlist.next();t&&this.playSong({catalogTrackId:t.id});}seek(t){this.audio.seek(t);}setVolume(t){this.audio.setVolume(t);}handleServerMessage(t){if(t.init_ack){let s=t.init_ack;if(this.audio.load(s.playback_url,s.is_hls),this.audio.play(),s.content_summary)try{let e=JSON.parse(s.content_summary);e.tracks&&this.playlist.setQueue(e.tracks);}catch{}}else t.error&&this.emit("error",new a(t.error.message,t.error.code));}handleTrackEnded(){this.playlist.hasNext?this.skip():this.updateStatus("idle");}updateStatus(t){this.status=t,this.emit("stateChange",{status:t,currentTime:this.audio.currentTime,duration:this.audio.duration,volume:1});}destroy(){this.audio.stop(),this.transport.disconnect(),this.removeAllListeners();}};exports.ContentKind=g;exports.ErrorCode=S;exports.SynxedConnectionError=h;exports.SynxedError=a;exports.SynxedPlaybackError=m;exports.SynxedPlayer=x;exports.SynxedProtocolError=E;//# sourceMappingURL=index.js.map
80
+ `,T=k__namespace.parse(C).root,f=T.lookupType("SdkClientEnvelope"),S=T.lookupType("SdkServerEnvelope"),E=(i=>(i[i.UNSPECIFIED=0]="UNSPECIFIED",i[i.SONG=1]="SONG",i[i.PLAYLIST=2]="PLAYLIST",i[i.CATEGORY=3]="CATEGORY",i))(E||{}),x=(o=>(o[o.UNSPECIFIED=0]="UNSPECIFIED",o[o.UNAUTHORIZED=1]="UNAUTHORIZED",o[o.VALIDATION_ERROR=2]="VALIDATION_ERROR",o[o.NOT_FOUND=3]="NOT_FOUND",o[o.PROCESSING=4]="PROCESSING",o[o.SERVICE_UNAVAILABLE=5]="SERVICE_UNAVAILABLE",o[o.BAD_PROTOBUF=6]="BAD_PROTOBUF",o))(x||{});var l=class{static encodeClientEnvelope(n){let t=f.create(n);return f.encode(t).finish()}static decodeServerEnvelope(n){let t=S.decode(n);return S.toObject(t,{enums:String,longs:String,bytes:String,defaults:true,oneofs:true})}};var a=class{constructor(){this.listeners=new Map;}on(n,t){this.listeners.has(n)||this.listeners.set(n,new Set),this.listeners.get(n).add(t);}off(n,t){let e=this.listeners.get(n);e&&e.delete(t);}once(n,t){let e=((...i)=>{t(...i),this.off(n,e);});this.on(n,e);}emit(n,...t){let e=this.listeners.get(n);e&&e.forEach(i=>i(...t));}removeAllListeners(){this.listeners.clear();}};var u=class extends a{constructor(){super();this.socket=null;this.apiKey=null;this.serverUrl=null;}connect(t,e){if(this.socket?.connected)return;this.apiKey=t,this.serverUrl=e;let i=e.endsWith("/")?e.slice(0,-1):e;this.socket=socket_ioClient.io(`${i}/sdk`,{auth:{apiKey:t},transports:["websocket"]}),this.socket.on("connect",()=>{this.emit("connected");}),this.socket.on("disconnect",s=>{this.emit("disconnected",s);}),this.socket.on("connect_error",s=>{this.emit("error",s);}),this.socket.on("d",s=>{try{let r=s instanceof Uint8Array?s:new Uint8Array(s),o=l.decodeServerEnvelope(r);this.emit("message",o);}catch(r){this.emit("error",new Error(`Failed to decode message: ${r}`));}});}async waitForConnection(){if(console.log("[Synxed] Waiting for connection..."),this.socket?.connected){console.log("[Synxed] Already connected");return}return new Promise((t,e)=>{let i=()=>{console.log("[Synxed] Socket connected event fired"),r(),t();},s=o=>{console.error("[Synxed] Socket connection error:",o),r(),e(o);},r=()=>{this.socket?.off("connect",i),this.socket?.off("connect_error",s);};this.socket?.once("connect",i),this.socket?.once("connect_error",s),this.socket||(console.error("[Synxed] No socket instance found"),e(new Error("Socket not initialized. Call connect() first.")));})}disconnect(){this.socket&&(this.socket.disconnect(),this.socket=null);}sendInit(t){if(!this.socket?.connected)throw new Error("Socket not connected");console.log("[Synxed] Sending init packet:",t);let e={init:t},i=l.encodeClientEnvelope(e);this.socket.emit("d",i);}sendControl(t){if(!this.socket?.connected)return;let e={control:t},i=l.encodeClientEnvelope(e);this.socket.emit("d",i);}sendAnalytics(t){if(!this.socket?.connected)return;let e={analytics:t},i=l.encodeClientEnvelope(e);this.socket.emit("d",i);}get isConnected(){return this.socket?.connected||false}};var h=class extends a{constructor(){super();this.howl=null;this.hls=null;this.audioEl=null;this.updateTimer=null;}async load(t,e){this.stop(),this.emit("loading"),console.log("[Synxed] AudioEngine loading URL:",t,"isHls:",e);let i=/^((?!chrome|android).)*safari/i.test(navigator.userAgent);if(e&&!i)try{let{default:s}=await import('hls.js');if(s.isSupported()){console.log("[Synxed] Using Hls.js for playback");let o=new URL(t).search;this.hls=new s,this.audioEl=new Audio,this.hls.loadSource(t),this.hls.attachMedia(this.audioEl),this.audioEl.onplay=()=>{this.emit("playing"),this.startTimeUpdateLoop();},this.audioEl.onpause=()=>{this.emit("paused"),this.stopTimeUpdateLoop();},this.audioEl.onended=()=>{this.emit("ended"),this.stopTimeUpdateLoop();},this.audioEl.onerror=v=>{console.error("[Synxed] HLS Audio error:",v),this.emit("error",v);},this.audioEl.onloadedmetadata=()=>{this.emit("loaded");},this.hls.on(s.Events.ERROR,(v,g)=>{g.fatal&&(console.error("[Synxed] Fatal HLS error:",g),this.emit("error",g));});return}}catch{console.warn("[Synxed] hls.js not found or error loading, falling back to native");}this.howl=new howler.Howl({src:[t],html5:true,format:e?["m3u8"]:void 0,onload:()=>{console.log("[Synxed] AudioEngine loaded successfully"),this.emit("loaded");},onloaderror:(s,r)=>{console.error("[Synxed] AudioEngine load error:",r,"ID:",s),this.emit("error",r);},onplay:()=>{this.emit("playing"),this.startTimeUpdateLoop();},onpause:()=>{this.emit("paused"),this.stopTimeUpdateLoop();},onstop:()=>{this.emit("stopped"),this.stopTimeUpdateLoop();},onend:()=>{this.emit("ended"),this.stopTimeUpdateLoop();}});}play(){this.audioEl?this.audioEl.play().catch(t=>console.error("[Synxed] Play failed:",t)):this.howl?.play();}pause(){this.audioEl?this.audioEl.pause():this.howl?.pause();}stop(){this.hls&&(this.hls.destroy(),this.hls=null),this.audioEl&&(this.audioEl.pause(),this.audioEl.src="",this.audioEl=null),this.howl&&(this.howl.stop(),this.howl.unload(),this.howl=null),this.stopTimeUpdateLoop();}seek(t){this.audioEl?this.audioEl.currentTime=t/1e3:this.howl?.seek(t/1e3);}setVolume(t){this.audioEl&&(this.audioEl.volume=t),this.howl&&this.howl.volume(t);}get duration(){return this.audioEl?this.audioEl.duration*1e3:(this.howl?.duration()||0)*1e3}get currentTime(){return this.audioEl?this.audioEl.currentTime*1e3:(this.howl?.seek()||0)*1e3}startTimeUpdateLoop(){this.stopTimeUpdateLoop();let t=()=>{(this.audioEl?!this.audioEl.paused:this.howl?.playing())&&(this.emit("timeupdate",{currentTime:this.currentTime,duration:this.duration}),this.updateTimer=requestAnimationFrame(t));};this.updateTimer=requestAnimationFrame(t);}stopTimeUpdateLoop(){this.updateTimer!==null&&(cancelAnimationFrame(this.updateTimer),this.updateTimer=null);}};var p=class extends a{constructor(){super();this.queue=[];this.currentIndex=-1;}setQueue(t){this.queue=t,this.currentIndex=t.length>0?0:-1,this.emit("queueUpdated",this.queue);}getCurrentTrack(){return this.currentIndex>=0&&this.currentIndex<this.queue.length?this.queue[this.currentIndex]:null}next(){if(this.currentIndex<this.queue.length-1){this.currentIndex++;let t=this.getCurrentTrack();return this.emit("trackChanged",t),t}return null}previous(){if(this.currentIndex>0){this.currentIndex--;let t=this.getCurrentTrack();return this.emit("trackChanged",t),t}return null}skipTo(t){if(t>=0&&t<this.queue.length){this.currentIndex=t;let e=this.getCurrentTrack();return this.emit("trackChanged",e),e}return null}get hasNext(){return this.currentIndex<this.queue.length-1}get hasPrevious(){return this.currentIndex>0}};var c=class extends Error{constructor(t,e){super(t);this.code=e;this.name="SynxedError";}},m=class extends c{constructor(n){super(n),this.name="SynxedConnectionError";}},y=class extends c{constructor(n){super(typeof n=="string"?n:"Playback failed"),this.name="SynxedPlaybackError";}},I=class extends c{constructor(n){super(n),this.name="SynxedProtocolError";}};var A=class extends a{constructor(t){super();this.status="idle";this.config=t,this.transport=new u,this.audio=new h,this.playlist=new p,this.setupListeners(),t.autoConnect&&this.connect();}setupListeners(){this.transport.on("connected",()=>this.emit("connected")),this.transport.on("disconnected",t=>this.emit("disconnected",t)),this.transport.on("error",t=>this.emit("error",new m(t.message))),this.transport.on("message",t=>this.handleServerMessage(t)),this.audio.on("playing",()=>this.updateStatus("playing")),this.audio.on("paused",()=>this.updateStatus("paused")),this.audio.on("stopped",()=>this.updateStatus("idle")),this.audio.on("loading",()=>this.updateStatus("loading")),this.audio.on("error",t=>this.emit("error",new y(t))),this.audio.on("timeupdate",t=>this.emit("timeUpdate",t)),this.audio.on("ended",()=>this.handleTrackEnded());}connect(){this.transport.connect(this.config.apiKey,this.config.serverUrl);}async playSong(t){this.transport.isConnected||this.connect(),await this.transport.waitForConnection(),this.transport.sendInit({contentKind:1,catalogTrackId:t.catalogTrackId,internalTrackId:t.internalTrackId,listenerId:t.listenerId});}async playPlaylist(t){this.transport.isConnected||this.connect(),await this.transport.waitForConnection(),this.transport.sendInit({contentKind:2,playlistCode:t.playlistCode,listenerId:t.listenerId});}pause(){this.audio.pause(),this.transport.sendControl({action:2,positionMs:Math.floor(this.audio.currentTime*1e3)});}resume(){this.audio.play(),this.transport.sendControl({action:1,positionMs:Math.floor(this.audio.currentTime*1e3)});}stop(){this.audio.stop(),this.transport.sendControl({action:3,positionMs:Math.floor(this.audio.currentTime*1e3)});}skip(){let t=this.playlist.next();t&&this.playSong({catalogTrackId:t.id});}seek(t){this.audio.seek(t),this.transport.sendControl({action:4,positionMs:t});}setVolume(t){this.audio.setVolume(t);}emitAnalytics(t,e,i){this.transport.sendAnalytics({eventType:t,positionMs:e??Math.floor(this.audio.currentTime*1e3),extraJson:i?JSON.stringify(i):void 0});}async handleServerMessage(t){if(console.log("[Synxed] Received server message:",t),t.initAck){let e=t.initAck;if(console.log("[Synxed] Loading playback URL:",e.playbackUrl,"isHls:",e.isHls),await this.audio.load(e.playbackUrl,e.isHls),this.audio.play(),this.emitAnalytics("stream_start"),e.contentSummary)try{let i=JSON.parse(e.contentSummary);i.tracks&&this.playlist.setQueue(i.tracks);}catch{}}else t.error&&this.emit("error",new c(t.error.message,t.error.code));}handleTrackEnded(){this.playlist.hasNext?this.skip():this.updateStatus("idle");}updateStatus(t){this.status=t,this.emit("stateChange",{status:t,currentTime:this.audio.currentTime,duration:this.audio.duration,volume:1});}destroy(){this.audio.stop(),this.transport.disconnect(),this.removeAllListeners();}};exports.ContentKind=E;exports.ErrorCode=x;exports.SynxedConnectionError=m;exports.SynxedError=c;exports.SynxedPlaybackError=y;exports.SynxedPlayer=A;exports.SynxedProtocolError=I;//# sourceMappingURL=index.js.map
59
81
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/proto/sdk-streaming.ts","../src/transport/ProtocolCodec.ts","../src/core/EventEmitter.ts","../src/transport/TransportManager.ts","../src/audio/AudioEngine.ts","../src/playlist/PlaylistManager.ts","../src/core/errors.ts","../src/core/SynxedPlayer.ts"],"names":["schema","root","k","SdkClientEnvelope","SdkServerEnvelope","ContentKind","ErrorCode","ProtocolCodec","payload","message","data","decoded","EventEmitter","event","handler","set","onceHandler","args","TransportManager","apiKey","serverUrl","io","reason","error","uint8Array","err","params","bytes","AudioEngine","url","isHls","Howl","id","ms","volume","update","PlaylistManager","tracks","track","index","SynxedError","code","SynxedConnectionError","SynxedPlaybackError","SynxedProtocolError","SynxedPlayer","config","time","options","nextTrack","ack","summary","status"],"mappings":"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","file":"index.js","sourcesContent":["import * as protobuf from 'protobufjs';\n\n/**\n * Protobuf schema definition for the SDK.\n * Matches the backend contract in src/modules/streaming/sdk-streaming.proto\n */\nconst schema = `\nsyntax = \"proto3\";\n\nenum ContentKind {\n CONTENT_KIND_UNSPECIFIED = 0;\n CONTENT_KIND_SONG = 1;\n CONTENT_KIND_PLAYLIST = 2;\n CONTENT_KIND_CATEGORY = 3;\n}\n\nenum ErrorCode {\n ERROR_CODE_UNSPECIFIED = 0;\n UNAUTHORIZED = 1;\n VALIDATION_ERROR = 2;\n NOT_FOUND = 3;\n PROCESSING = 4;\n SERVICE_UNAVAILABLE = 5;\n BAD_PROTOBUF = 6;\n}\n\nmessage SdkClientInit {\n ContentKind content_kind = 1;\n string catalog_track_id = 2;\n string internal_track_id = 3;\n string playlist_code = 4;\n string listener_id = 5;\n string device_type = 6;\n string country_code = 7;\n string region = 8;\n string protocol_version = 9;\n}\n\nmessage SdkClientEnvelope {\n oneof payload {\n SdkClientInit init = 1;\n }\n}\n\nmessage SdkServerInitAck {\n string session_id = 1;\n string playback_url = 2;\n bool is_hls = 3;\n uint32 heartbeat_interval_ms = 4;\n string content_summary = 5;\n}\n\nmessage SdkServerError {\n ErrorCode code = 1;\n string message = 2;\n}\n\nmessage SdkServerEnvelope {\n oneof payload {\n SdkServerInitAck init_ack = 1;\n SdkServerError error = 2;\n }\n}\n`;\n\nconst root = protobuf.parse(schema).root;\n\nexport const SdkClientEnvelope = root.lookupType('SdkClientEnvelope');\nexport const SdkServerEnvelope = root.lookupType('SdkServerEnvelope');\n\nexport enum ContentKind {\n UNSPECIFIED = 0,\n SONG = 1,\n PLAYLIST = 2,\n CATEGORY = 3,\n}\n\nexport enum ErrorCode {\n UNSPECIFIED = 0,\n UNAUTHORIZED = 1,\n VALIDATION_ERROR = 2,\n NOT_FOUND = 3,\n PROCESSING = 4,\n SERVICE_UNAVAILABLE = 5,\n BAD_PROTOBUF = 6,\n}\n","import { SdkClientEnvelope, SdkServerEnvelope } from '../proto/sdk-streaming';\n\nexport class ProtocolCodec {\n /**\n * Encodes a client envelope into a binary payload.\n */\n static encodeClientEnvelope(payload: any): Uint8Array {\n const message = SdkClientEnvelope.create(payload);\n return SdkClientEnvelope.encode(message).finish();\n }\n\n /**\n * Decodes a binary payload into a server envelope.\n */\n static decodeServerEnvelope(data: Uint8Array): any {\n const decoded = SdkServerEnvelope.decode(data);\n return SdkServerEnvelope.toObject(decoded, {\n enums: String,\n longs: String,\n bytes: String,\n defaults: true,\n oneofs: true,\n });\n }\n}\n","type Handler = (...args: any[]) => void;\n\nexport class EventEmitter<Events extends Record<string, any>> {\n private listeners: Map<keyof Events, Set<Handler>> = new Map();\n\n on<K extends keyof Events>(event: K, handler: Events[K]): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(handler);\n }\n\n off<K extends keyof Events>(event: K, handler: Events[K]): void {\n const set = this.listeners.get(event);\n if (set) {\n set.delete(handler);\n }\n }\n\n once<K extends keyof Events>(event: K, handler: Events[K]): void {\n const onceHandler = ((...args: any[]) => {\n handler(...args);\n this.off(event, onceHandler as Events[K]);\n }) as Events[K];\n this.on(event, onceHandler);\n }\n\n protected emit<K extends keyof Events>(event: K, ...args: Parameters<Events[K]>): void {\n const set = this.listeners.get(event);\n if (set) {\n set.forEach((handler) => handler(...args));\n }\n }\n\n removeAllListeners(): void {\n this.listeners.clear();\n }\n}\n","import { io, Socket } from 'socket.io-client';\nimport { ProtocolCodec } from './ProtocolCodec';\nimport { EventEmitter } from '../core/EventEmitter';\n\nexport interface TransportEvents {\n connected: () => void;\n disconnected: (reason: string) => void;\n error: (error: any) => void;\n message: (payload: any) => void;\n}\n\nexport class TransportManager extends EventEmitter<TransportEvents> {\n private socket: Socket | null = null;\n private apiKey: string | null = null;\n private serverUrl: string | null = null;\n\n constructor() {\n super();\n }\n\n /**\n * Connects to the Synxed SDK namespace.\n */\n connect(apiKey: string, serverUrl: string): void {\n if (this.socket?.connected) return;\n\n this.apiKey = apiKey;\n this.serverUrl = serverUrl;\n\n this.socket = io(`${serverUrl}/sdk`, {\n auth: { apiKey },\n transports: ['websocket'],\n });\n\n this.socket.on('connect', () => {\n this.emit('connected');\n });\n\n this.socket.on('disconnect', (reason) => {\n this.emit('disconnected', reason);\n });\n\n this.socket.on('connect_error', (error) => {\n this.emit('error', error);\n });\n\n this.socket.on('d', (data: ArrayBuffer | Uint8Array) => {\n try {\n const uint8Array = data instanceof Uint8Array ? data : new Uint8Array(data);\n const payload = ProtocolCodec.decodeServerEnvelope(uint8Array);\n this.emit('message', payload);\n } catch (err) {\n this.emit('error', new Error(`Failed to decode message: ${err}`));\n }\n });\n }\n\n /**\n * Disconnects from the server.\n */\n disconnect(): void {\n if (this.socket) {\n this.socket.disconnect();\n this.socket = null;\n }\n }\n\n /**\n * Sends an init message to the backend.\n */\n sendInit(params: any): void {\n if (!this.socket?.connected) {\n throw new Error('Socket not connected');\n }\n\n const payload = { init: params };\n const bytes = ProtocolCodec.encodeClientEnvelope(payload);\n this.socket.emit('d', bytes);\n }\n\n /**\n * Checks if the socket is connected.\n */\n get isConnected(): boolean {\n return this.socket?.connected || false;\n }\n}\n","import { Howl } from 'howler';\nimport { EventEmitter } from '../core/EventEmitter';\n\nexport interface AudioEvents {\n playing: () => void;\n paused: () => void;\n stopped: () => void;\n ended: () => void;\n loading: () => void;\n loaded: () => void;\n error: (error: any) => void;\n timeupdate: (data: { currentTime: number; duration: number }) => void;\n}\n\nexport class AudioEngine extends EventEmitter<AudioEvents> {\n private howl: Howl | null = null;\n private updateTimer: number | null = null;\n\n constructor() {\n super();\n }\n\n /**\n * Loads a track URL.\n */\n load(url: string, isHls: boolean): void {\n this.stop();\n this.emit('loading');\n\n // For HLS, we'd ideally use hls.js. \n // Howler doesn't natively support HLS streams well without custom logic.\n // For now, we'll use HTML5 audio mode which works for some HLS streams natively.\n this.howl = new Howl({\n src: [url],\n html5: true, // Required for streaming large files\n format: isHls ? ['m3u8'] : undefined,\n onload: () => {\n this.emit('loaded');\n },\n onloaderror: (id, err) => {\n this.emit('error', err);\n },\n onplay: () => {\n this.emit('playing');\n this.startTimeUpdateLoop();\n },\n onpause: () => {\n this.emit('paused');\n this.stopTimeUpdateLoop();\n },\n onstop: () => {\n this.emit('stopped');\n this.stopTimeUpdateLoop();\n },\n onend: () => {\n this.emit('ended');\n this.stopTimeUpdateLoop();\n },\n });\n }\n\n play(): void {\n this.howl?.play();\n }\n\n pause(): void {\n this.howl?.pause();\n }\n\n stop(): void {\n this.howl?.stop();\n this.howl?.unload();\n this.howl = null;\n }\n\n seek(ms: number): void {\n this.howl?.seek(ms / 1000);\n }\n\n setVolume(volume: number): void {\n this.howl?.volume(volume);\n }\n\n get duration(): number {\n return (this.howl?.duration() || 0) * 1000;\n }\n\n get currentTime(): number {\n return (this.howl?.seek() as number || 0) * 1000;\n }\n\n private startTimeUpdateLoop(): void {\n this.stopTimeUpdateLoop();\n const update = () => {\n if (this.howl?.playing()) {\n this.emit('timeupdate', {\n currentTime: this.currentTime,\n duration: this.duration,\n });\n this.updateTimer = requestAnimationFrame(update);\n }\n };\n this.updateTimer = requestAnimationFrame(update);\n }\n\n private stopTimeUpdateLoop(): void {\n if (this.updateTimer !== null) {\n cancelAnimationFrame(this.updateTimer);\n this.updateTimer = null;\n }\n }\n}\n","import { EventEmitter } from '../core/EventEmitter';\n\nexport interface PlaylistEvents {\n trackChanged: (track: any) => void;\n queueUpdated: (tracks: any[]) => void;\n}\n\nexport class PlaylistManager extends EventEmitter<PlaylistEvents> {\n private queue: any[] = [];\n private currentIndex: number = -1;\n\n constructor() {\n super();\n }\n\n setQueue(tracks: any[]): void {\n this.queue = tracks;\n this.currentIndex = tracks.length > 0 ? 0 : -1;\n this.emit('queueUpdated', this.queue);\n }\n\n getCurrentTrack(): any | null {\n if (this.currentIndex >= 0 && this.currentIndex < this.queue.length) {\n return this.queue[this.currentIndex];\n }\n return null;\n }\n\n next(): any | null {\n if (this.currentIndex < this.queue.length - 1) {\n this.currentIndex++;\n const track = this.getCurrentTrack();\n this.emit('trackChanged', track);\n return track;\n }\n return null;\n }\n\n previous(): any | null {\n if (this.currentIndex > 0) {\n this.currentIndex--;\n const track = this.getCurrentTrack();\n this.emit('trackChanged', track);\n return track;\n }\n return null;\n }\n\n skipTo(index: number): any | null {\n if (index >= 0 && index < this.queue.length) {\n this.currentIndex = index;\n const track = this.getCurrentTrack();\n this.emit('trackChanged', track);\n return track;\n }\n return null;\n }\n\n get hasNext(): boolean {\n return this.currentIndex < this.queue.length - 1;\n }\n\n get hasPrevious(): boolean {\n return this.currentIndex > 0;\n }\n}\n","export class SynxedError extends Error {\n constructor(message: string, public code?: string) {\n super(message);\n this.name = 'SynxedError';\n }\n}\n\nexport class SynxedConnectionError extends SynxedError {\n constructor(message: string) {\n super(message);\n this.name = 'SynxedConnectionError';\n }\n}\n\nexport class SynxedPlaybackError extends SynxedError {\n constructor(error: any) {\n super(typeof error === 'string' ? error : 'Playback failed');\n this.name = 'SynxedPlaybackError';\n }\n}\n\nexport class SynxedProtocolError extends SynxedError {\n constructor(message: string) {\n super(message);\n this.name = 'SynxedProtocolError';\n }\n}\n","import { TransportManager } from '../transport/TransportManager';\nimport { AudioEngine } from '../audio/AudioEngine';\nimport { PlaylistManager } from '../playlist/PlaylistManager';\nimport { EventEmitter } from './EventEmitter';\nimport { ContentKind } from '../proto/sdk-streaming';\nimport { SynxedConfig, PlayerState, PlaySongOptions, PlayPlaylistOptions, SynxedEvents } from '../types';\nimport { SynxedError, SynxedPlaybackError, SynxedConnectionError } from './errors';\n\nexport class SynxedPlayer extends EventEmitter<SynxedEvents> {\n private transport: TransportManager;\n private audio: AudioEngine;\n private playlist: PlaylistManager;\n private config: SynxedConfig;\n private status: PlayerState['status'] = 'idle';\n\n constructor(config: SynxedConfig) {\n super();\n this.config = config;\n this.transport = new TransportManager();\n this.audio = new AudioEngine();\n this.playlist = new PlaylistManager();\n\n this.setupListeners();\n\n if (config.autoConnect) {\n this.connect();\n }\n }\n\n private setupListeners(): void {\n // Transport Listeners\n this.transport.on('connected', () => this.emit('connected'));\n this.transport.on('disconnected', (reason) => this.emit('disconnected', reason));\n this.transport.on('error', (error) => this.emit('error', new SynxedConnectionError(error.message)));\n this.transport.on('message', (payload) => this.handleServerMessage(payload));\n\n // Audio Listeners\n this.audio.on('playing', () => this.updateStatus('playing'));\n this.audio.on('paused', () => this.updateStatus('paused'));\n this.audio.on('stopped', () => this.updateStatus('idle'));\n this.audio.on('loading', () => this.updateStatus('loading'));\n this.audio.on('error', (error) => this.emit('error', new SynxedPlaybackError(error)));\n this.audio.on('timeupdate', (time) => this.emit('timeUpdate', time));\n this.audio.on('ended', () => this.handleTrackEnded());\n }\n\n private connect(): void {\n this.transport.connect(this.config.apiKey, this.config.serverUrl);\n }\n\n async playSong(options: PlaySongOptions): Promise<void> {\n if (!this.transport.isConnected) this.connect();\n \n this.transport.sendInit({\n content_kind: ContentKind.SONG,\n catalog_track_id: options.catalogTrackId,\n internal_track_id: options.internalTrackId,\n listener_id: options.listenerId,\n });\n }\n\n async playPlaylist(options: PlayPlaylistOptions): Promise<void> {\n if (!this.transport.isConnected) this.connect();\n\n this.transport.sendInit({\n content_kind: ContentKind.PLAYLIST,\n playlist_code: options.playlistCode,\n listener_id: options.listenerId,\n });\n }\n\n pause(): void {\n this.audio.pause();\n }\n\n resume(): void {\n this.audio.play();\n }\n\n stop(): void {\n this.audio.stop();\n }\n\n skip(): void {\n const nextTrack = this.playlist.next();\n if (nextTrack) {\n // In current backend, we need to re-init for each track\n // This logic will be refined once backend handles queues\n this.playSong({ catalogTrackId: nextTrack.id }); \n }\n }\n\n seek(ms: number): void {\n this.audio.seek(ms);\n }\n\n setVolume(volume: number): void {\n this.audio.setVolume(volume);\n }\n\n private handleServerMessage(payload: any): void {\n if (payload.init_ack) {\n const ack = payload.init_ack;\n this.audio.load(ack.playback_url, ack.is_hls);\n this.audio.play();\n \n // Parse content_summary if it's a playlist\n if (ack.content_summary) {\n try {\n const summary = JSON.parse(ack.content_summary);\n if (summary.tracks) {\n this.playlist.setQueue(summary.tracks);\n }\n } catch (e) {\n // Ignore parse errors for now\n }\n }\n } else if (payload.error) {\n this.emit('error', new SynxedError(payload.error.message, payload.error.code));\n }\n }\n\n private handleTrackEnded(): void {\n if (this.playlist.hasNext) {\n this.skip();\n } else {\n this.updateStatus('idle');\n }\n }\n\n private updateStatus(status: PlayerState['status']): void {\n this.status = status;\n this.emit('stateChange', {\n status,\n currentTime: this.audio.currentTime,\n duration: this.audio.duration,\n volume: 1, // Store volume locally if needed\n });\n }\n\n destroy(): void {\n this.audio.stop();\n this.transport.disconnect();\n this.removeAllListeners();\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/proto/sdk-streaming.ts","../src/transport/ProtocolCodec.ts","../src/core/EventEmitter.ts","../src/transport/TransportManager.ts","../src/audio/AudioEngine.ts","../src/playlist/PlaylistManager.ts","../src/core/errors.ts","../src/core/SynxedPlayer.ts"],"names":["schema","root","k","SdkClientEnvelope","SdkServerEnvelope","ContentKind","ErrorCode","ProtocolCodec","payload","message","data","decoded","EventEmitter","event","handler","set","onceHandler","args","TransportManager","apiKey","serverUrl","normalizedUrl","io","reason","error","uint8Array","err","resolve","reject","onConnect","cleanup","onError","params","bytes","AudioEngine","url","isHls","isSafari","Hls","searchParams","e","Howl","id","ms","volume","update","PlaylistManager","tracks","track","index","SynxedError","code","SynxedConnectionError","SynxedPlaybackError","SynxedProtocolError","SynxedPlayer","config","time","options","nextTrack","eventType","positionMs","extra","ack","summary","status"],"mappings":"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","file":"index.js","sourcesContent":["import * as protobuf from 'protobufjs';\n\n/**\n * Protobuf schema definition for the SDK.\n * Matches the backend contract in src/modules/streaming/sdk-streaming.proto\n */\nconst schema = `\nsyntax = \"proto3\";\n\nenum ContentKind {\n CONTENT_KIND_UNSPECIFIED = 0;\n CONTENT_KIND_SONG = 1;\n CONTENT_KIND_PLAYLIST = 2;\n CONTENT_KIND_CATEGORY = 3;\n}\n\nenum ErrorCode {\n ERROR_CODE_UNSPECIFIED = 0;\n UNAUTHORIZED = 1;\n VALIDATION_ERROR = 2;\n NOT_FOUND = 3;\n PROCESSING = 4;\n SERVICE_UNAVAILABLE = 5;\n BAD_PROTOBUF = 6;\n}\n\nenum SdkControlAction {\n CONTROL_UNSPECIFIED = 0;\n CONTROL_PLAY = 1;\n CONTROL_PAUSE = 2;\n CONTROL_STOP = 3;\n CONTROL_SEEK = 4;\n}\n\nmessage SdkClientInit {\n ContentKind contentKind = 1;\n string catalogTrackId = 2;\n string internalTrackId = 3;\n string playlistCode = 4;\n string categoryQuery = 5;\n string listenerId = 6;\n string deviceType = 7;\n string countryCode = 8;\n string region = 9;\n string protocolVersion = 10;\n}\n\nmessage SdkClientControl {\n SdkControlAction action = 1;\n uint32 positionMs = 2;\n}\n\nmessage SdkClientAnalytics {\n string eventType = 1;\n uint32 positionMs = 2;\n string extraJson = 3;\n}\n\nmessage SdkClientEnvelope {\n oneof payload {\n SdkClientInit init = 1;\n SdkClientControl control = 2;\n SdkClientAnalytics analytics = 3;\n }\n}\n\nmessage SdkServerInitAck {\n string sessionId = 1;\n string playbackUrl = 2;\n bool isHls = 3;\n uint32 heartbeatIntervalMs = 4;\n string contentSummary = 5;\n}\n\nmessage SdkServerError {\n ErrorCode code = 1;\n string message = 2;\n}\n\nmessage SdkServerEnvelope {\n oneof payload {\n SdkServerInitAck initAck = 1;\n SdkServerError error = 2;\n }\n}\n`;\n\nconst root = protobuf.parse(schema).root;\n\nexport const SdkClientEnvelope = root.lookupType('SdkClientEnvelope');\nexport const SdkServerEnvelope = root.lookupType('SdkServerEnvelope');\n\nexport enum ContentKind {\n UNSPECIFIED = 0,\n SONG = 1,\n PLAYLIST = 2,\n CATEGORY = 3,\n}\n\nexport enum ErrorCode {\n UNSPECIFIED = 0,\n UNAUTHORIZED = 1,\n VALIDATION_ERROR = 2,\n NOT_FOUND = 3,\n PROCESSING = 4,\n SERVICE_UNAVAILABLE = 5,\n BAD_PROTOBUF = 6,\n}\n","import { SdkClientEnvelope, SdkServerEnvelope } from '../proto/sdk-streaming';\n\nexport class ProtocolCodec {\n /**\n * Encodes a client envelope into a binary payload.\n */\n static encodeClientEnvelope(payload: any): Uint8Array {\n const message = SdkClientEnvelope.create(payload);\n return SdkClientEnvelope.encode(message).finish();\n }\n\n /**\n * Decodes a binary payload into a server envelope.\n */\n static decodeServerEnvelope(data: Uint8Array): any {\n const decoded = SdkServerEnvelope.decode(data);\n return SdkServerEnvelope.toObject(decoded, {\n enums: String,\n longs: String,\n bytes: String,\n defaults: true,\n oneofs: true,\n });\n }\n}\n","type Handler = (...args: any[]) => void;\n\nexport class EventEmitter<Events extends Record<string, any>> {\n private listeners: Map<keyof Events, Set<Handler>> = new Map();\n\n on<K extends keyof Events>(event: K, handler: Events[K]): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(handler);\n }\n\n off<K extends keyof Events>(event: K, handler: Events[K]): void {\n const set = this.listeners.get(event);\n if (set) {\n set.delete(handler);\n }\n }\n\n once<K extends keyof Events>(event: K, handler: Events[K]): void {\n const onceHandler = ((...args: any[]) => {\n handler(...args);\n this.off(event, onceHandler as Events[K]);\n }) as Events[K];\n this.on(event, onceHandler);\n }\n\n protected emit<K extends keyof Events>(event: K, ...args: Parameters<Events[K]>): void {\n const set = this.listeners.get(event);\n if (set) {\n set.forEach((handler) => handler(...args));\n }\n }\n\n removeAllListeners(): void {\n this.listeners.clear();\n }\n}\n","import { io, Socket } from 'socket.io-client';\nimport { ProtocolCodec } from './ProtocolCodec';\nimport { EventEmitter } from '../core/EventEmitter';\n\nexport interface TransportEvents {\n connected: () => void;\n disconnected: (reason: string) => void;\n error: (error: any) => void;\n message: (payload: any) => void;\n}\n\nexport class TransportManager extends EventEmitter<TransportEvents> {\n private socket: Socket | null = null;\n private apiKey: string | null = null;\n private serverUrl: string | null = null;\n\n constructor() {\n super();\n }\n\n /**\n * Connects to the Synxed SDK namespace.\n */\n connect(apiKey: string, serverUrl: string): void {\n if (this.socket?.connected) return;\n\n this.apiKey = apiKey;\n this.serverUrl = serverUrl;\n\n const normalizedUrl = serverUrl.endsWith('/') ? serverUrl.slice(0, -1) : serverUrl;\n\n this.socket = io(`${normalizedUrl}/sdk`, {\n auth: { apiKey },\n transports: ['websocket'],\n });\n\n this.socket.on('connect', () => {\n this.emit('connected');\n });\n\n this.socket.on('disconnect', (reason) => {\n this.emit('disconnected', reason);\n });\n\n this.socket.on('connect_error', (error) => {\n this.emit('error', error);\n });\n\n this.socket.on('d', (data: ArrayBuffer | Uint8Array) => {\n try {\n const uint8Array = data instanceof Uint8Array ? data : new Uint8Array(data);\n const payload = ProtocolCodec.decodeServerEnvelope(uint8Array);\n this.emit('message', payload);\n } catch (err) {\n this.emit('error', new Error(`Failed to decode message: ${err}`));\n }\n });\n }\n\n /**\n * Returns a promise that resolves when the socket is connected.\n */\n async waitForConnection(): Promise<void> {\n console.log('[Synxed] Waiting for connection...');\n if (this.socket?.connected) {\n console.log('[Synxed] Already connected');\n return;\n }\n \n return new Promise((resolve, reject) => {\n const onConnect = () => {\n console.log('[Synxed] Socket connected event fired');\n cleanup();\n resolve();\n };\n \n const onError = (err: any) => {\n console.error('[Synxed] Socket connection error:', err);\n cleanup();\n reject(err);\n };\n \n const cleanup = () => {\n this.socket?.off('connect', onConnect);\n this.socket?.off('connect_error', onError);\n };\n\n this.socket?.once('connect', onConnect);\n this.socket?.once('connect_error', onError);\n \n if (!this.socket) {\n console.error('[Synxed] No socket instance found');\n reject(new Error('Socket not initialized. Call connect() first.'));\n }\n });\n }\n\n /**\n * Disconnects from the server.\n */\n disconnect(): void {\n if (this.socket) {\n this.socket.disconnect();\n this.socket = null;\n }\n }\n\n /**\n * Sends an init message to the backend.\n */\n sendInit(params: any): void {\n if (!this.socket?.connected) {\n throw new Error('Socket not connected');\n }\n\n console.log('[Synxed] Sending init packet:', params);\n const payload = { init: params };\n const bytes = ProtocolCodec.encodeClientEnvelope(payload);\n this.socket.emit('d', bytes);\n }\n\n /**\n * Sends a control message.\n */\n sendControl(params: any): void {\n if (!this.socket?.connected) return;\n const payload = { control: params };\n const bytes = ProtocolCodec.encodeClientEnvelope(payload);\n this.socket.emit('d', bytes);\n }\n\n /**\n * Sends an analytics message.\n */\n sendAnalytics(params: any): void {\n if (!this.socket?.connected) return;\n const payload = { analytics: params };\n const bytes = ProtocolCodec.encodeClientEnvelope(payload);\n this.socket.emit('d', bytes);\n }\n\n /**\n * Checks if the socket is connected.\n */\n get isConnected(): boolean {\n return this.socket?.connected || false;\n }\n}\n","import { Howl } from 'howler';\nimport { EventEmitter } from '../core/EventEmitter';\n\nexport interface AudioEvents {\n playing: () => void;\n paused: () => void;\n stopped: () => void;\n ended: () => void;\n loading: () => void;\n loaded: () => void;\n error: (error: any) => void;\n timeupdate: (data: { currentTime: number; duration: number }) => void;\n}\n\nexport class AudioEngine extends EventEmitter<AudioEvents> {\n private howl: Howl | null = null;\n private hls: any = null;\n private audioEl: HTMLAudioElement | null = null;\n private updateTimer: number | null = null;\n\n constructor() {\n super();\n }\n\n\n /**\n * Loads a track URL.\n */\n async load(url: string, isHls: boolean): Promise<void> {\n this.stop();\n this.emit('loading');\n\n console.log('[Synxed] AudioEngine loading URL:', url, 'isHls:', isHls);\n\n // Safari has native HLS support. Other browsers need hls.js.\n const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);\n\n if (isHls && !isSafari) {\n try {\n const { default: Hls } = await import('hls.js');\n if (Hls.isSupported()) {\n console.log('[Synxed] Using Hls.js for playback');\n \n // Extract query parameters from the manifest URL to forward to segments\n const urlObj = new URL(url);\n const searchParams = urlObj.search;\n\n this.hls = new Hls();\n this.audioEl = new Audio();\n this.hls.loadSource(url);\n this.hls.attachMedia(this.audioEl);\n \n this.audioEl.onplay = () => {\n this.emit('playing');\n this.startTimeUpdateLoop();\n };\n this.audioEl.onpause = () => {\n this.emit('paused');\n this.stopTimeUpdateLoop();\n };\n this.audioEl.onended = () => {\n this.emit('ended');\n this.stopTimeUpdateLoop();\n };\n this.audioEl.onerror = (e) => {\n console.error('[Synxed] HLS Audio error:', e);\n this.emit('error', e);\n };\n this.audioEl.onloadedmetadata = () => {\n this.emit('loaded');\n };\n\n this.hls.on(Hls.Events.ERROR, (event: any, data: any) => {\n if (data.fatal) {\n console.error('[Synxed] Fatal HLS error:', data);\n this.emit('error', data);\n }\n });\n\n return;\n }\n } catch (e) {\n console.warn('[Synxed] hls.js not found or error loading, falling back to native');\n }\n }\n\n // Fallback to Howler\n this.howl = new Howl({\n src: [url],\n html5: true, \n format: isHls ? ['m3u8'] : undefined,\n onload: () => {\n console.log('[Synxed] AudioEngine loaded successfully');\n this.emit('loaded');\n },\n onloaderror: (id, err) => {\n console.error('[Synxed] AudioEngine load error:', err, 'ID:', id);\n this.emit('error', err);\n },\n onplay: () => {\n this.emit('playing');\n this.startTimeUpdateLoop();\n },\n onpause: () => {\n this.emit('paused');\n this.stopTimeUpdateLoop();\n },\n onstop: () => {\n this.emit('stopped');\n this.stopTimeUpdateLoop();\n },\n onend: () => {\n this.emit('ended');\n this.stopTimeUpdateLoop();\n },\n });\n }\n\n play(): void {\n if (this.audioEl) {\n this.audioEl.play().catch(e => console.error('[Synxed] Play failed:', e));\n } else {\n this.howl?.play();\n }\n }\n\n pause(): void {\n if (this.audioEl) {\n this.audioEl.pause();\n } else {\n this.howl?.pause();\n }\n }\n\n stop(): void {\n if (this.hls) {\n this.hls.destroy();\n this.hls = null;\n }\n if (this.audioEl) {\n this.audioEl.pause();\n this.audioEl.src = '';\n this.audioEl = null;\n }\n if (this.howl) {\n this.howl.stop();\n this.howl.unload();\n this.howl = null;\n }\n this.stopTimeUpdateLoop();\n }\n\n seek(ms: number): void {\n if (this.audioEl) {\n this.audioEl.currentTime = ms / 1000;\n } else {\n this.howl?.seek(ms / 1000);\n }\n }\n\n setVolume(volume: number): void {\n if (this.audioEl) {\n this.audioEl.volume = volume;\n }\n if (this.howl) {\n this.howl.volume(volume);\n }\n }\n\n get duration(): number {\n if (this.audioEl) return this.audioEl.duration * 1000;\n return (this.howl?.duration() || 0) * 1000;\n }\n\n get currentTime(): number {\n if (this.audioEl) return this.audioEl.currentTime * 1000;\n return (this.howl?.seek() as number || 0) * 1000;\n }\n\n private startTimeUpdateLoop(): void {\n this.stopTimeUpdateLoop();\n const update = () => {\n const isPlaying = this.audioEl ? !this.audioEl.paused : this.howl?.playing();\n if (isPlaying) {\n this.emit('timeupdate', {\n currentTime: this.currentTime,\n duration: this.duration,\n });\n this.updateTimer = requestAnimationFrame(update);\n }\n };\n this.updateTimer = requestAnimationFrame(update);\n }\n\n private stopTimeUpdateLoop(): void {\n if (this.updateTimer !== null) {\n cancelAnimationFrame(this.updateTimer);\n this.updateTimer = null;\n }\n }\n}\n","import { EventEmitter } from '../core/EventEmitter';\n\nexport interface PlaylistEvents {\n trackChanged: (track: any) => void;\n queueUpdated: (tracks: any[]) => void;\n}\n\nexport class PlaylistManager extends EventEmitter<PlaylistEvents> {\n private queue: any[] = [];\n private currentIndex: number = -1;\n\n constructor() {\n super();\n }\n\n setQueue(tracks: any[]): void {\n this.queue = tracks;\n this.currentIndex = tracks.length > 0 ? 0 : -1;\n this.emit('queueUpdated', this.queue);\n }\n\n getCurrentTrack(): any | null {\n if (this.currentIndex >= 0 && this.currentIndex < this.queue.length) {\n return this.queue[this.currentIndex];\n }\n return null;\n }\n\n next(): any | null {\n if (this.currentIndex < this.queue.length - 1) {\n this.currentIndex++;\n const track = this.getCurrentTrack();\n this.emit('trackChanged', track);\n return track;\n }\n return null;\n }\n\n previous(): any | null {\n if (this.currentIndex > 0) {\n this.currentIndex--;\n const track = this.getCurrentTrack();\n this.emit('trackChanged', track);\n return track;\n }\n return null;\n }\n\n skipTo(index: number): any | null {\n if (index >= 0 && index < this.queue.length) {\n this.currentIndex = index;\n const track = this.getCurrentTrack();\n this.emit('trackChanged', track);\n return track;\n }\n return null;\n }\n\n get hasNext(): boolean {\n return this.currentIndex < this.queue.length - 1;\n }\n\n get hasPrevious(): boolean {\n return this.currentIndex > 0;\n }\n}\n","export class SynxedError extends Error {\n constructor(message: string, public code?: string) {\n super(message);\n this.name = 'SynxedError';\n }\n}\n\nexport class SynxedConnectionError extends SynxedError {\n constructor(message: string) {\n super(message);\n this.name = 'SynxedConnectionError';\n }\n}\n\nexport class SynxedPlaybackError extends SynxedError {\n constructor(error: any) {\n super(typeof error === 'string' ? error : 'Playback failed');\n this.name = 'SynxedPlaybackError';\n }\n}\n\nexport class SynxedProtocolError extends SynxedError {\n constructor(message: string) {\n super(message);\n this.name = 'SynxedProtocolError';\n }\n}\n","import { TransportManager } from '../transport/TransportManager';\nimport { AudioEngine } from '../audio/AudioEngine';\nimport { PlaylistManager } from '../playlist/PlaylistManager';\nimport { EventEmitter } from './EventEmitter';\nimport { ContentKind } from '../proto/sdk-streaming';\nimport { SynxedConfig, PlayerState, PlaySongOptions, PlayPlaylistOptions, SynxedEvents } from '../types';\nimport { SynxedError, SynxedPlaybackError, SynxedConnectionError } from './errors';\n\nexport class SynxedPlayer extends EventEmitter<SynxedEvents> {\n private transport: TransportManager;\n private audio: AudioEngine;\n private playlist: PlaylistManager;\n private config: SynxedConfig;\n private status: PlayerState['status'] = 'idle';\n\n constructor(config: SynxedConfig) {\n super();\n this.config = config;\n this.transport = new TransportManager();\n this.audio = new AudioEngine();\n this.playlist = new PlaylistManager();\n\n this.setupListeners();\n\n if (config.autoConnect) {\n this.connect();\n }\n }\n\n private setupListeners(): void {\n // Transport Listeners\n this.transport.on('connected', () => this.emit('connected'));\n this.transport.on('disconnected', (reason) => this.emit('disconnected', reason));\n this.transport.on('error', (error) => this.emit('error', new SynxedConnectionError(error.message)));\n this.transport.on('message', (payload) => this.handleServerMessage(payload));\n\n // Audio Listeners\n this.audio.on('playing', () => this.updateStatus('playing'));\n this.audio.on('paused', () => this.updateStatus('paused'));\n this.audio.on('stopped', () => this.updateStatus('idle'));\n this.audio.on('loading', () => this.updateStatus('loading'));\n this.audio.on('error', (error) => this.emit('error', new SynxedPlaybackError(error)));\n this.audio.on('timeupdate', (time) => this.emit('timeUpdate', time));\n this.audio.on('ended', () => this.handleTrackEnded());\n }\n\n private connect(): void {\n this.transport.connect(this.config.apiKey, this.config.serverUrl);\n }\n\n async playSong(options: PlaySongOptions): Promise<void> {\n if (!this.transport.isConnected) this.connect();\n await this.transport.waitForConnection();\n \n this.transport.sendInit({\n contentKind: ContentKind.SONG,\n catalogTrackId: options.catalogTrackId,\n internalTrackId: options.internalTrackId,\n listenerId: options.listenerId,\n });\n }\n\n async playPlaylist(options: PlayPlaylistOptions): Promise<void> {\n if (!this.transport.isConnected) this.connect();\n await this.transport.waitForConnection();\n\n this.transport.sendInit({\n contentKind: ContentKind.PLAYLIST,\n playlistCode: options.playlistCode,\n listenerId: options.listenerId,\n });\n }\n\n pause(): void {\n this.audio.pause();\n this.transport.sendControl({\n action: 2, // PAUSE\n positionMs: Math.floor(this.audio.currentTime * 1000),\n });\n }\n\n resume(): void {\n this.audio.play();\n this.transport.sendControl({\n action: 1, // PLAY\n positionMs: Math.floor(this.audio.currentTime * 1000),\n });\n }\n\n stop(): void {\n this.audio.stop();\n this.transport.sendControl({\n action: 3, // STOP\n positionMs: Math.floor(this.audio.currentTime * 1000),\n });\n }\n\n skip(): void {\n const nextTrack = this.playlist.next();\n if (nextTrack) {\n // In current backend, we need to re-init for each track\n // This logic will be refined once backend handles queues\n this.playSong({ catalogTrackId: nextTrack.id }); \n }\n }\n\n seek(ms: number): void {\n this.audio.seek(ms);\n this.transport.sendControl({\n action: 4, // SEEK\n positionMs: ms,\n });\n }\n\n setVolume(volume: number): void {\n this.audio.setVolume(volume);\n }\n\n private emitAnalytics(eventType: string, positionMs?: number, extra?: any): void {\n this.transport.sendAnalytics({\n eventType,\n positionMs: positionMs ?? Math.floor(this.audio.currentTime * 1000),\n extraJson: extra ? JSON.stringify(extra) : undefined,\n });\n }\n\n private async handleServerMessage(payload: any): Promise<void> {\n console.log('[Synxed] Received server message:', payload);\n if (payload.initAck) {\n const ack = payload.initAck;\n console.log('[Synxed] Loading playback URL:', ack.playbackUrl, 'isHls:', ack.isHls);\n await this.audio.load(ack.playbackUrl, ack.isHls);\n this.audio.play();\n this.emitAnalytics('stream_start');\n \n // Parse contentSummary if it's a playlist\n if (ack.contentSummary) {\n try {\n const summary = JSON.parse(ack.contentSummary);\n if (summary.tracks) {\n this.playlist.setQueue(summary.tracks);\n }\n } catch (e) {\n // Ignore parse errors for now\n }\n }\n } else if (payload.error) {\n this.emit('error', new SynxedError(payload.error.message, payload.error.code));\n }\n }\n\n private handleTrackEnded(): void {\n if (this.playlist.hasNext) {\n this.skip();\n } else {\n this.updateStatus('idle');\n }\n }\n\n private updateStatus(status: PlayerState['status']): void {\n this.status = status;\n this.emit('stateChange', {\n status,\n currentTime: this.audio.currentTime,\n duration: this.audio.duration,\n volume: 1, // Store volume locally if needed\n });\n }\n\n destroy(): void {\n this.audio.stop();\n this.transport.disconnect();\n this.removeAllListeners();\n }\n}\n"]}
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import {io}from'socket.io-client';import*as k from'protobufjs';import {Howl}from'howler';var I=`
1
+ import {io}from'socket.io-client';import*as k from'protobufjs';import {Howl}from'howler';var C=`
2
2
  syntax = "proto3";
3
3
 
4
4
  enum ContentKind {
@@ -18,30 +18,52 @@ enum ErrorCode {
18
18
  BAD_PROTOBUF = 6;
19
19
  }
20
20
 
21
+ enum SdkControlAction {
22
+ CONTROL_UNSPECIFIED = 0;
23
+ CONTROL_PLAY = 1;
24
+ CONTROL_PAUSE = 2;
25
+ CONTROL_STOP = 3;
26
+ CONTROL_SEEK = 4;
27
+ }
28
+
21
29
  message SdkClientInit {
22
- ContentKind content_kind = 1;
23
- string catalog_track_id = 2;
24
- string internal_track_id = 3;
25
- string playlist_code = 4;
26
- string listener_id = 5;
27
- string device_type = 6;
28
- string country_code = 7;
29
- string region = 8;
30
- string protocol_version = 9;
30
+ ContentKind contentKind = 1;
31
+ string catalogTrackId = 2;
32
+ string internalTrackId = 3;
33
+ string playlistCode = 4;
34
+ string categoryQuery = 5;
35
+ string listenerId = 6;
36
+ string deviceType = 7;
37
+ string countryCode = 8;
38
+ string region = 9;
39
+ string protocolVersion = 10;
40
+ }
41
+
42
+ message SdkClientControl {
43
+ SdkControlAction action = 1;
44
+ uint32 positionMs = 2;
45
+ }
46
+
47
+ message SdkClientAnalytics {
48
+ string eventType = 1;
49
+ uint32 positionMs = 2;
50
+ string extraJson = 3;
31
51
  }
32
52
 
33
53
  message SdkClientEnvelope {
34
54
  oneof payload {
35
55
  SdkClientInit init = 1;
56
+ SdkClientControl control = 2;
57
+ SdkClientAnalytics analytics = 3;
36
58
  }
37
59
  }
38
60
 
39
61
  message SdkServerInitAck {
40
- string session_id = 1;
41
- string playback_url = 2;
42
- bool is_hls = 3;
43
- uint32 heartbeat_interval_ms = 4;
44
- string content_summary = 5;
62
+ string sessionId = 1;
63
+ string playbackUrl = 2;
64
+ bool isHls = 3;
65
+ uint32 heartbeatIntervalMs = 4;
66
+ string contentSummary = 5;
45
67
  }
46
68
 
47
69
  message SdkServerError {
@@ -51,9 +73,9 @@ message SdkServerError {
51
73
 
52
74
  message SdkServerEnvelope {
53
75
  oneof payload {
54
- SdkServerInitAck init_ack = 1;
76
+ SdkServerInitAck initAck = 1;
55
77
  SdkServerError error = 2;
56
78
  }
57
79
  }
58
- `,f=k.parse(I).root,v=f.lookupType("SdkClientEnvelope"),y=f.lookupType("SdkServerEnvelope"),g=(e=>(e[e.UNSPECIFIED=0]="UNSPECIFIED",e[e.SONG=1]="SONG",e[e.PLAYLIST=2]="PLAYLIST",e[e.CATEGORY=3]="CATEGORY",e))(g||{}),S=(n=>(n[n.UNSPECIFIED=0]="UNSPECIFIED",n[n.UNAUTHORIZED=1]="UNAUTHORIZED",n[n.VALIDATION_ERROR=2]="VALIDATION_ERROR",n[n.NOT_FOUND=3]="NOT_FOUND",n[n.PROCESSING=4]="PROCESSING",n[n.SERVICE_UNAVAILABLE=5]="SERVICE_UNAVAILABLE",n[n.BAD_PROTOBUF=6]="BAD_PROTOBUF",n))(S||{});var c=class{static encodeClientEnvelope(i){let t=v.create(i);return v.encode(t).finish()}static decodeServerEnvelope(i){let t=y.decode(i);return y.toObject(t,{enums:String,longs:String,bytes:String,defaults:true,oneofs:true})}};var r=class{constructor(){this.listeners=new Map;}on(i,t){this.listeners.has(i)||this.listeners.set(i,new Set),this.listeners.get(i).add(t);}off(i,t){let s=this.listeners.get(i);s&&s.delete(t);}once(i,t){let s=((...e)=>{t(...e),this.off(i,s);});this.on(i,s);}emit(i,...t){let s=this.listeners.get(i);s&&s.forEach(e=>e(...t));}removeAllListeners(){this.listeners.clear();}};var l=class extends r{constructor(){super();this.socket=null;this.apiKey=null;this.serverUrl=null;}connect(t,s){this.socket?.connected||(this.apiKey=t,this.serverUrl=s,this.socket=io(`${s}/sdk`,{auth:{apiKey:t},transports:["websocket"]}),this.socket.on("connect",()=>{this.emit("connected");}),this.socket.on("disconnect",e=>{this.emit("disconnected",e);}),this.socket.on("connect_error",e=>{this.emit("error",e);}),this.socket.on("d",e=>{try{let d=e instanceof Uint8Array?e:new Uint8Array(e),T=c.decodeServerEnvelope(d);this.emit("message",T);}catch(d){this.emit("error",new Error(`Failed to decode message: ${d}`));}}));}disconnect(){this.socket&&(this.socket.disconnect(),this.socket=null);}sendInit(t){if(!this.socket?.connected)throw new Error("Socket not connected");let s={init:t},e=c.encodeClientEnvelope(s);this.socket.emit("d",e);}get isConnected(){return this.socket?.connected||false}};var u=class extends r{constructor(){super();this.howl=null;this.updateTimer=null;}load(t,s){this.stop(),this.emit("loading"),this.howl=new Howl({src:[t],html5:true,format:s?["m3u8"]:void 0,onload:()=>{this.emit("loaded");},onloaderror:(e,d)=>{this.emit("error",d);},onplay:()=>{this.emit("playing"),this.startTimeUpdateLoop();},onpause:()=>{this.emit("paused"),this.stopTimeUpdateLoop();},onstop:()=>{this.emit("stopped"),this.stopTimeUpdateLoop();},onend:()=>{this.emit("ended"),this.stopTimeUpdateLoop();}});}play(){this.howl?.play();}pause(){this.howl?.pause();}stop(){this.howl?.stop(),this.howl?.unload(),this.howl=null;}seek(t){this.howl?.seek(t/1e3);}setVolume(t){this.howl?.volume(t);}get duration(){return (this.howl?.duration()||0)*1e3}get currentTime(){return (this.howl?.seek()||0)*1e3}startTimeUpdateLoop(){this.stopTimeUpdateLoop();let t=()=>{this.howl?.playing()&&(this.emit("timeupdate",{currentTime:this.currentTime,duration:this.duration}),this.updateTimer=requestAnimationFrame(t));};this.updateTimer=requestAnimationFrame(t);}stopTimeUpdateLoop(){this.updateTimer!==null&&(cancelAnimationFrame(this.updateTimer),this.updateTimer=null);}};var p=class extends r{constructor(){super();this.queue=[];this.currentIndex=-1;}setQueue(t){this.queue=t,this.currentIndex=t.length>0?0:-1,this.emit("queueUpdated",this.queue);}getCurrentTrack(){return this.currentIndex>=0&&this.currentIndex<this.queue.length?this.queue[this.currentIndex]:null}next(){if(this.currentIndex<this.queue.length-1){this.currentIndex++;let t=this.getCurrentTrack();return this.emit("trackChanged",t),t}return null}previous(){if(this.currentIndex>0){this.currentIndex--;let t=this.getCurrentTrack();return this.emit("trackChanged",t),t}return null}skipTo(t){if(t>=0&&t<this.queue.length){this.currentIndex=t;let s=this.getCurrentTrack();return this.emit("trackChanged",s),s}return null}get hasNext(){return this.currentIndex<this.queue.length-1}get hasPrevious(){return this.currentIndex>0}};var a=class extends Error{constructor(t,s){super(t);this.code=s;this.name="SynxedError";}},h=class extends a{constructor(i){super(i),this.name="SynxedConnectionError";}},m=class extends a{constructor(i){super(typeof i=="string"?i:"Playback failed"),this.name="SynxedPlaybackError";}},E=class extends a{constructor(i){super(i),this.name="SynxedProtocolError";}};var x=class extends r{constructor(t){super();this.status="idle";this.config=t,this.transport=new l,this.audio=new u,this.playlist=new p,this.setupListeners(),t.autoConnect&&this.connect();}setupListeners(){this.transport.on("connected",()=>this.emit("connected")),this.transport.on("disconnected",t=>this.emit("disconnected",t)),this.transport.on("error",t=>this.emit("error",new h(t.message))),this.transport.on("message",t=>this.handleServerMessage(t)),this.audio.on("playing",()=>this.updateStatus("playing")),this.audio.on("paused",()=>this.updateStatus("paused")),this.audio.on("stopped",()=>this.updateStatus("idle")),this.audio.on("loading",()=>this.updateStatus("loading")),this.audio.on("error",t=>this.emit("error",new m(t))),this.audio.on("timeupdate",t=>this.emit("timeUpdate",t)),this.audio.on("ended",()=>this.handleTrackEnded());}connect(){this.transport.connect(this.config.apiKey,this.config.serverUrl);}async playSong(t){this.transport.isConnected||this.connect(),this.transport.sendInit({content_kind:1,catalog_track_id:t.catalogTrackId,internal_track_id:t.internalTrackId,listener_id:t.listenerId});}async playPlaylist(t){this.transport.isConnected||this.connect(),this.transport.sendInit({content_kind:2,playlist_code:t.playlistCode,listener_id:t.listenerId});}pause(){this.audio.pause();}resume(){this.audio.play();}stop(){this.audio.stop();}skip(){let t=this.playlist.next();t&&this.playSong({catalogTrackId:t.id});}seek(t){this.audio.seek(t);}setVolume(t){this.audio.setVolume(t);}handleServerMessage(t){if(t.init_ack){let s=t.init_ack;if(this.audio.load(s.playback_url,s.is_hls),this.audio.play(),s.content_summary)try{let e=JSON.parse(s.content_summary);e.tracks&&this.playlist.setQueue(e.tracks);}catch{}}else t.error&&this.emit("error",new a(t.error.message,t.error.code));}handleTrackEnded(){this.playlist.hasNext?this.skip():this.updateStatus("idle");}updateStatus(t){this.status=t,this.emit("stateChange",{status:t,currentTime:this.audio.currentTime,duration:this.audio.duration,volume:1});}destroy(){this.audio.stop(),this.transport.disconnect(),this.removeAllListeners();}};export{g as ContentKind,S as ErrorCode,h as SynxedConnectionError,a as SynxedError,m as SynxedPlaybackError,x as SynxedPlayer,E as SynxedProtocolError};//# sourceMappingURL=index.mjs.map
80
+ `,T=k.parse(C).root,f=T.lookupType("SdkClientEnvelope"),S=T.lookupType("SdkServerEnvelope"),E=(i=>(i[i.UNSPECIFIED=0]="UNSPECIFIED",i[i.SONG=1]="SONG",i[i.PLAYLIST=2]="PLAYLIST",i[i.CATEGORY=3]="CATEGORY",i))(E||{}),x=(o=>(o[o.UNSPECIFIED=0]="UNSPECIFIED",o[o.UNAUTHORIZED=1]="UNAUTHORIZED",o[o.VALIDATION_ERROR=2]="VALIDATION_ERROR",o[o.NOT_FOUND=3]="NOT_FOUND",o[o.PROCESSING=4]="PROCESSING",o[o.SERVICE_UNAVAILABLE=5]="SERVICE_UNAVAILABLE",o[o.BAD_PROTOBUF=6]="BAD_PROTOBUF",o))(x||{});var l=class{static encodeClientEnvelope(n){let t=f.create(n);return f.encode(t).finish()}static decodeServerEnvelope(n){let t=S.decode(n);return S.toObject(t,{enums:String,longs:String,bytes:String,defaults:true,oneofs:true})}};var a=class{constructor(){this.listeners=new Map;}on(n,t){this.listeners.has(n)||this.listeners.set(n,new Set),this.listeners.get(n).add(t);}off(n,t){let e=this.listeners.get(n);e&&e.delete(t);}once(n,t){let e=((...i)=>{t(...i),this.off(n,e);});this.on(n,e);}emit(n,...t){let e=this.listeners.get(n);e&&e.forEach(i=>i(...t));}removeAllListeners(){this.listeners.clear();}};var u=class extends a{constructor(){super();this.socket=null;this.apiKey=null;this.serverUrl=null;}connect(t,e){if(this.socket?.connected)return;this.apiKey=t,this.serverUrl=e;let i=e.endsWith("/")?e.slice(0,-1):e;this.socket=io(`${i}/sdk`,{auth:{apiKey:t},transports:["websocket"]}),this.socket.on("connect",()=>{this.emit("connected");}),this.socket.on("disconnect",s=>{this.emit("disconnected",s);}),this.socket.on("connect_error",s=>{this.emit("error",s);}),this.socket.on("d",s=>{try{let r=s instanceof Uint8Array?s:new Uint8Array(s),o=l.decodeServerEnvelope(r);this.emit("message",o);}catch(r){this.emit("error",new Error(`Failed to decode message: ${r}`));}});}async waitForConnection(){if(console.log("[Synxed] Waiting for connection..."),this.socket?.connected){console.log("[Synxed] Already connected");return}return new Promise((t,e)=>{let i=()=>{console.log("[Synxed] Socket connected event fired"),r(),t();},s=o=>{console.error("[Synxed] Socket connection error:",o),r(),e(o);},r=()=>{this.socket?.off("connect",i),this.socket?.off("connect_error",s);};this.socket?.once("connect",i),this.socket?.once("connect_error",s),this.socket||(console.error("[Synxed] No socket instance found"),e(new Error("Socket not initialized. Call connect() first.")));})}disconnect(){this.socket&&(this.socket.disconnect(),this.socket=null);}sendInit(t){if(!this.socket?.connected)throw new Error("Socket not connected");console.log("[Synxed] Sending init packet:",t);let e={init:t},i=l.encodeClientEnvelope(e);this.socket.emit("d",i);}sendControl(t){if(!this.socket?.connected)return;let e={control:t},i=l.encodeClientEnvelope(e);this.socket.emit("d",i);}sendAnalytics(t){if(!this.socket?.connected)return;let e={analytics:t},i=l.encodeClientEnvelope(e);this.socket.emit("d",i);}get isConnected(){return this.socket?.connected||false}};var h=class extends a{constructor(){super();this.howl=null;this.hls=null;this.audioEl=null;this.updateTimer=null;}async load(t,e){this.stop(),this.emit("loading"),console.log("[Synxed] AudioEngine loading URL:",t,"isHls:",e);let i=/^((?!chrome|android).)*safari/i.test(navigator.userAgent);if(e&&!i)try{let{default:s}=await import('hls.js');if(s.isSupported()){console.log("[Synxed] Using Hls.js for playback");let o=new URL(t).search;this.hls=new s,this.audioEl=new Audio,this.hls.loadSource(t),this.hls.attachMedia(this.audioEl),this.audioEl.onplay=()=>{this.emit("playing"),this.startTimeUpdateLoop();},this.audioEl.onpause=()=>{this.emit("paused"),this.stopTimeUpdateLoop();},this.audioEl.onended=()=>{this.emit("ended"),this.stopTimeUpdateLoop();},this.audioEl.onerror=v=>{console.error("[Synxed] HLS Audio error:",v),this.emit("error",v);},this.audioEl.onloadedmetadata=()=>{this.emit("loaded");},this.hls.on(s.Events.ERROR,(v,g)=>{g.fatal&&(console.error("[Synxed] Fatal HLS error:",g),this.emit("error",g));});return}}catch{console.warn("[Synxed] hls.js not found or error loading, falling back to native");}this.howl=new Howl({src:[t],html5:true,format:e?["m3u8"]:void 0,onload:()=>{console.log("[Synxed] AudioEngine loaded successfully"),this.emit("loaded");},onloaderror:(s,r)=>{console.error("[Synxed] AudioEngine load error:",r,"ID:",s),this.emit("error",r);},onplay:()=>{this.emit("playing"),this.startTimeUpdateLoop();},onpause:()=>{this.emit("paused"),this.stopTimeUpdateLoop();},onstop:()=>{this.emit("stopped"),this.stopTimeUpdateLoop();},onend:()=>{this.emit("ended"),this.stopTimeUpdateLoop();}});}play(){this.audioEl?this.audioEl.play().catch(t=>console.error("[Synxed] Play failed:",t)):this.howl?.play();}pause(){this.audioEl?this.audioEl.pause():this.howl?.pause();}stop(){this.hls&&(this.hls.destroy(),this.hls=null),this.audioEl&&(this.audioEl.pause(),this.audioEl.src="",this.audioEl=null),this.howl&&(this.howl.stop(),this.howl.unload(),this.howl=null),this.stopTimeUpdateLoop();}seek(t){this.audioEl?this.audioEl.currentTime=t/1e3:this.howl?.seek(t/1e3);}setVolume(t){this.audioEl&&(this.audioEl.volume=t),this.howl&&this.howl.volume(t);}get duration(){return this.audioEl?this.audioEl.duration*1e3:(this.howl?.duration()||0)*1e3}get currentTime(){return this.audioEl?this.audioEl.currentTime*1e3:(this.howl?.seek()||0)*1e3}startTimeUpdateLoop(){this.stopTimeUpdateLoop();let t=()=>{(this.audioEl?!this.audioEl.paused:this.howl?.playing())&&(this.emit("timeupdate",{currentTime:this.currentTime,duration:this.duration}),this.updateTimer=requestAnimationFrame(t));};this.updateTimer=requestAnimationFrame(t);}stopTimeUpdateLoop(){this.updateTimer!==null&&(cancelAnimationFrame(this.updateTimer),this.updateTimer=null);}};var p=class extends a{constructor(){super();this.queue=[];this.currentIndex=-1;}setQueue(t){this.queue=t,this.currentIndex=t.length>0?0:-1,this.emit("queueUpdated",this.queue);}getCurrentTrack(){return this.currentIndex>=0&&this.currentIndex<this.queue.length?this.queue[this.currentIndex]:null}next(){if(this.currentIndex<this.queue.length-1){this.currentIndex++;let t=this.getCurrentTrack();return this.emit("trackChanged",t),t}return null}previous(){if(this.currentIndex>0){this.currentIndex--;let t=this.getCurrentTrack();return this.emit("trackChanged",t),t}return null}skipTo(t){if(t>=0&&t<this.queue.length){this.currentIndex=t;let e=this.getCurrentTrack();return this.emit("trackChanged",e),e}return null}get hasNext(){return this.currentIndex<this.queue.length-1}get hasPrevious(){return this.currentIndex>0}};var c=class extends Error{constructor(t,e){super(t);this.code=e;this.name="SynxedError";}},m=class extends c{constructor(n){super(n),this.name="SynxedConnectionError";}},y=class extends c{constructor(n){super(typeof n=="string"?n:"Playback failed"),this.name="SynxedPlaybackError";}},I=class extends c{constructor(n){super(n),this.name="SynxedProtocolError";}};var A=class extends a{constructor(t){super();this.status="idle";this.config=t,this.transport=new u,this.audio=new h,this.playlist=new p,this.setupListeners(),t.autoConnect&&this.connect();}setupListeners(){this.transport.on("connected",()=>this.emit("connected")),this.transport.on("disconnected",t=>this.emit("disconnected",t)),this.transport.on("error",t=>this.emit("error",new m(t.message))),this.transport.on("message",t=>this.handleServerMessage(t)),this.audio.on("playing",()=>this.updateStatus("playing")),this.audio.on("paused",()=>this.updateStatus("paused")),this.audio.on("stopped",()=>this.updateStatus("idle")),this.audio.on("loading",()=>this.updateStatus("loading")),this.audio.on("error",t=>this.emit("error",new y(t))),this.audio.on("timeupdate",t=>this.emit("timeUpdate",t)),this.audio.on("ended",()=>this.handleTrackEnded());}connect(){this.transport.connect(this.config.apiKey,this.config.serverUrl);}async playSong(t){this.transport.isConnected||this.connect(),await this.transport.waitForConnection(),this.transport.sendInit({contentKind:1,catalogTrackId:t.catalogTrackId,internalTrackId:t.internalTrackId,listenerId:t.listenerId});}async playPlaylist(t){this.transport.isConnected||this.connect(),await this.transport.waitForConnection(),this.transport.sendInit({contentKind:2,playlistCode:t.playlistCode,listenerId:t.listenerId});}pause(){this.audio.pause(),this.transport.sendControl({action:2,positionMs:Math.floor(this.audio.currentTime*1e3)});}resume(){this.audio.play(),this.transport.sendControl({action:1,positionMs:Math.floor(this.audio.currentTime*1e3)});}stop(){this.audio.stop(),this.transport.sendControl({action:3,positionMs:Math.floor(this.audio.currentTime*1e3)});}skip(){let t=this.playlist.next();t&&this.playSong({catalogTrackId:t.id});}seek(t){this.audio.seek(t),this.transport.sendControl({action:4,positionMs:t});}setVolume(t){this.audio.setVolume(t);}emitAnalytics(t,e,i){this.transport.sendAnalytics({eventType:t,positionMs:e??Math.floor(this.audio.currentTime*1e3),extraJson:i?JSON.stringify(i):void 0});}async handleServerMessage(t){if(console.log("[Synxed] Received server message:",t),t.initAck){let e=t.initAck;if(console.log("[Synxed] Loading playback URL:",e.playbackUrl,"isHls:",e.isHls),await this.audio.load(e.playbackUrl,e.isHls),this.audio.play(),this.emitAnalytics("stream_start"),e.contentSummary)try{let i=JSON.parse(e.contentSummary);i.tracks&&this.playlist.setQueue(i.tracks);}catch{}}else t.error&&this.emit("error",new c(t.error.message,t.error.code));}handleTrackEnded(){this.playlist.hasNext?this.skip():this.updateStatus("idle");}updateStatus(t){this.status=t,this.emit("stateChange",{status:t,currentTime:this.audio.currentTime,duration:this.audio.duration,volume:1});}destroy(){this.audio.stop(),this.transport.disconnect(),this.removeAllListeners();}};export{E as ContentKind,x as ErrorCode,m as SynxedConnectionError,c as SynxedError,y as SynxedPlaybackError,A as SynxedPlayer,I as SynxedProtocolError};//# sourceMappingURL=index.mjs.map
59
81
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/proto/sdk-streaming.ts","../src/transport/ProtocolCodec.ts","../src/core/EventEmitter.ts","../src/transport/TransportManager.ts","../src/audio/AudioEngine.ts","../src/playlist/PlaylistManager.ts","../src/core/errors.ts","../src/core/SynxedPlayer.ts"],"names":["schema","root","SdkClientEnvelope","SdkServerEnvelope","ContentKind","ErrorCode","ProtocolCodec","payload","message","data","decoded","EventEmitter","event","handler","set","onceHandler","args","TransportManager","apiKey","serverUrl","io","reason","error","uint8Array","err","params","bytes","AudioEngine","url","isHls","Howl","id","ms","volume","update","PlaylistManager","tracks","track","index","SynxedError","code","SynxedConnectionError","SynxedPlaybackError","SynxedProtocolError","SynxedPlayer","config","time","options","nextTrack","ack","summary","status"],"mappings":"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","file":"index.mjs","sourcesContent":["import * as protobuf from 'protobufjs';\n\n/**\n * Protobuf schema definition for the SDK.\n * Matches the backend contract in src/modules/streaming/sdk-streaming.proto\n */\nconst schema = `\nsyntax = \"proto3\";\n\nenum ContentKind {\n CONTENT_KIND_UNSPECIFIED = 0;\n CONTENT_KIND_SONG = 1;\n CONTENT_KIND_PLAYLIST = 2;\n CONTENT_KIND_CATEGORY = 3;\n}\n\nenum ErrorCode {\n ERROR_CODE_UNSPECIFIED = 0;\n UNAUTHORIZED = 1;\n VALIDATION_ERROR = 2;\n NOT_FOUND = 3;\n PROCESSING = 4;\n SERVICE_UNAVAILABLE = 5;\n BAD_PROTOBUF = 6;\n}\n\nmessage SdkClientInit {\n ContentKind content_kind = 1;\n string catalog_track_id = 2;\n string internal_track_id = 3;\n string playlist_code = 4;\n string listener_id = 5;\n string device_type = 6;\n string country_code = 7;\n string region = 8;\n string protocol_version = 9;\n}\n\nmessage SdkClientEnvelope {\n oneof payload {\n SdkClientInit init = 1;\n }\n}\n\nmessage SdkServerInitAck {\n string session_id = 1;\n string playback_url = 2;\n bool is_hls = 3;\n uint32 heartbeat_interval_ms = 4;\n string content_summary = 5;\n}\n\nmessage SdkServerError {\n ErrorCode code = 1;\n string message = 2;\n}\n\nmessage SdkServerEnvelope {\n oneof payload {\n SdkServerInitAck init_ack = 1;\n SdkServerError error = 2;\n }\n}\n`;\n\nconst root = protobuf.parse(schema).root;\n\nexport const SdkClientEnvelope = root.lookupType('SdkClientEnvelope');\nexport const SdkServerEnvelope = root.lookupType('SdkServerEnvelope');\n\nexport enum ContentKind {\n UNSPECIFIED = 0,\n SONG = 1,\n PLAYLIST = 2,\n CATEGORY = 3,\n}\n\nexport enum ErrorCode {\n UNSPECIFIED = 0,\n UNAUTHORIZED = 1,\n VALIDATION_ERROR = 2,\n NOT_FOUND = 3,\n PROCESSING = 4,\n SERVICE_UNAVAILABLE = 5,\n BAD_PROTOBUF = 6,\n}\n","import { SdkClientEnvelope, SdkServerEnvelope } from '../proto/sdk-streaming';\n\nexport class ProtocolCodec {\n /**\n * Encodes a client envelope into a binary payload.\n */\n static encodeClientEnvelope(payload: any): Uint8Array {\n const message = SdkClientEnvelope.create(payload);\n return SdkClientEnvelope.encode(message).finish();\n }\n\n /**\n * Decodes a binary payload into a server envelope.\n */\n static decodeServerEnvelope(data: Uint8Array): any {\n const decoded = SdkServerEnvelope.decode(data);\n return SdkServerEnvelope.toObject(decoded, {\n enums: String,\n longs: String,\n bytes: String,\n defaults: true,\n oneofs: true,\n });\n }\n}\n","type Handler = (...args: any[]) => void;\n\nexport class EventEmitter<Events extends Record<string, any>> {\n private listeners: Map<keyof Events, Set<Handler>> = new Map();\n\n on<K extends keyof Events>(event: K, handler: Events[K]): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(handler);\n }\n\n off<K extends keyof Events>(event: K, handler: Events[K]): void {\n const set = this.listeners.get(event);\n if (set) {\n set.delete(handler);\n }\n }\n\n once<K extends keyof Events>(event: K, handler: Events[K]): void {\n const onceHandler = ((...args: any[]) => {\n handler(...args);\n this.off(event, onceHandler as Events[K]);\n }) as Events[K];\n this.on(event, onceHandler);\n }\n\n protected emit<K extends keyof Events>(event: K, ...args: Parameters<Events[K]>): void {\n const set = this.listeners.get(event);\n if (set) {\n set.forEach((handler) => handler(...args));\n }\n }\n\n removeAllListeners(): void {\n this.listeners.clear();\n }\n}\n","import { io, Socket } from 'socket.io-client';\nimport { ProtocolCodec } from './ProtocolCodec';\nimport { EventEmitter } from '../core/EventEmitter';\n\nexport interface TransportEvents {\n connected: () => void;\n disconnected: (reason: string) => void;\n error: (error: any) => void;\n message: (payload: any) => void;\n}\n\nexport class TransportManager extends EventEmitter<TransportEvents> {\n private socket: Socket | null = null;\n private apiKey: string | null = null;\n private serverUrl: string | null = null;\n\n constructor() {\n super();\n }\n\n /**\n * Connects to the Synxed SDK namespace.\n */\n connect(apiKey: string, serverUrl: string): void {\n if (this.socket?.connected) return;\n\n this.apiKey = apiKey;\n this.serverUrl = serverUrl;\n\n this.socket = io(`${serverUrl}/sdk`, {\n auth: { apiKey },\n transports: ['websocket'],\n });\n\n this.socket.on('connect', () => {\n this.emit('connected');\n });\n\n this.socket.on('disconnect', (reason) => {\n this.emit('disconnected', reason);\n });\n\n this.socket.on('connect_error', (error) => {\n this.emit('error', error);\n });\n\n this.socket.on('d', (data: ArrayBuffer | Uint8Array) => {\n try {\n const uint8Array = data instanceof Uint8Array ? data : new Uint8Array(data);\n const payload = ProtocolCodec.decodeServerEnvelope(uint8Array);\n this.emit('message', payload);\n } catch (err) {\n this.emit('error', new Error(`Failed to decode message: ${err}`));\n }\n });\n }\n\n /**\n * Disconnects from the server.\n */\n disconnect(): void {\n if (this.socket) {\n this.socket.disconnect();\n this.socket = null;\n }\n }\n\n /**\n * Sends an init message to the backend.\n */\n sendInit(params: any): void {\n if (!this.socket?.connected) {\n throw new Error('Socket not connected');\n }\n\n const payload = { init: params };\n const bytes = ProtocolCodec.encodeClientEnvelope(payload);\n this.socket.emit('d', bytes);\n }\n\n /**\n * Checks if the socket is connected.\n */\n get isConnected(): boolean {\n return this.socket?.connected || false;\n }\n}\n","import { Howl } from 'howler';\nimport { EventEmitter } from '../core/EventEmitter';\n\nexport interface AudioEvents {\n playing: () => void;\n paused: () => void;\n stopped: () => void;\n ended: () => void;\n loading: () => void;\n loaded: () => void;\n error: (error: any) => void;\n timeupdate: (data: { currentTime: number; duration: number }) => void;\n}\n\nexport class AudioEngine extends EventEmitter<AudioEvents> {\n private howl: Howl | null = null;\n private updateTimer: number | null = null;\n\n constructor() {\n super();\n }\n\n /**\n * Loads a track URL.\n */\n load(url: string, isHls: boolean): void {\n this.stop();\n this.emit('loading');\n\n // For HLS, we'd ideally use hls.js. \n // Howler doesn't natively support HLS streams well without custom logic.\n // For now, we'll use HTML5 audio mode which works for some HLS streams natively.\n this.howl = new Howl({\n src: [url],\n html5: true, // Required for streaming large files\n format: isHls ? ['m3u8'] : undefined,\n onload: () => {\n this.emit('loaded');\n },\n onloaderror: (id, err) => {\n this.emit('error', err);\n },\n onplay: () => {\n this.emit('playing');\n this.startTimeUpdateLoop();\n },\n onpause: () => {\n this.emit('paused');\n this.stopTimeUpdateLoop();\n },\n onstop: () => {\n this.emit('stopped');\n this.stopTimeUpdateLoop();\n },\n onend: () => {\n this.emit('ended');\n this.stopTimeUpdateLoop();\n },\n });\n }\n\n play(): void {\n this.howl?.play();\n }\n\n pause(): void {\n this.howl?.pause();\n }\n\n stop(): void {\n this.howl?.stop();\n this.howl?.unload();\n this.howl = null;\n }\n\n seek(ms: number): void {\n this.howl?.seek(ms / 1000);\n }\n\n setVolume(volume: number): void {\n this.howl?.volume(volume);\n }\n\n get duration(): number {\n return (this.howl?.duration() || 0) * 1000;\n }\n\n get currentTime(): number {\n return (this.howl?.seek() as number || 0) * 1000;\n }\n\n private startTimeUpdateLoop(): void {\n this.stopTimeUpdateLoop();\n const update = () => {\n if (this.howl?.playing()) {\n this.emit('timeupdate', {\n currentTime: this.currentTime,\n duration: this.duration,\n });\n this.updateTimer = requestAnimationFrame(update);\n }\n };\n this.updateTimer = requestAnimationFrame(update);\n }\n\n private stopTimeUpdateLoop(): void {\n if (this.updateTimer !== null) {\n cancelAnimationFrame(this.updateTimer);\n this.updateTimer = null;\n }\n }\n}\n","import { EventEmitter } from '../core/EventEmitter';\n\nexport interface PlaylistEvents {\n trackChanged: (track: any) => void;\n queueUpdated: (tracks: any[]) => void;\n}\n\nexport class PlaylistManager extends EventEmitter<PlaylistEvents> {\n private queue: any[] = [];\n private currentIndex: number = -1;\n\n constructor() {\n super();\n }\n\n setQueue(tracks: any[]): void {\n this.queue = tracks;\n this.currentIndex = tracks.length > 0 ? 0 : -1;\n this.emit('queueUpdated', this.queue);\n }\n\n getCurrentTrack(): any | null {\n if (this.currentIndex >= 0 && this.currentIndex < this.queue.length) {\n return this.queue[this.currentIndex];\n }\n return null;\n }\n\n next(): any | null {\n if (this.currentIndex < this.queue.length - 1) {\n this.currentIndex++;\n const track = this.getCurrentTrack();\n this.emit('trackChanged', track);\n return track;\n }\n return null;\n }\n\n previous(): any | null {\n if (this.currentIndex > 0) {\n this.currentIndex--;\n const track = this.getCurrentTrack();\n this.emit('trackChanged', track);\n return track;\n }\n return null;\n }\n\n skipTo(index: number): any | null {\n if (index >= 0 && index < this.queue.length) {\n this.currentIndex = index;\n const track = this.getCurrentTrack();\n this.emit('trackChanged', track);\n return track;\n }\n return null;\n }\n\n get hasNext(): boolean {\n return this.currentIndex < this.queue.length - 1;\n }\n\n get hasPrevious(): boolean {\n return this.currentIndex > 0;\n }\n}\n","export class SynxedError extends Error {\n constructor(message: string, public code?: string) {\n super(message);\n this.name = 'SynxedError';\n }\n}\n\nexport class SynxedConnectionError extends SynxedError {\n constructor(message: string) {\n super(message);\n this.name = 'SynxedConnectionError';\n }\n}\n\nexport class SynxedPlaybackError extends SynxedError {\n constructor(error: any) {\n super(typeof error === 'string' ? error : 'Playback failed');\n this.name = 'SynxedPlaybackError';\n }\n}\n\nexport class SynxedProtocolError extends SynxedError {\n constructor(message: string) {\n super(message);\n this.name = 'SynxedProtocolError';\n }\n}\n","import { TransportManager } from '../transport/TransportManager';\nimport { AudioEngine } from '../audio/AudioEngine';\nimport { PlaylistManager } from '../playlist/PlaylistManager';\nimport { EventEmitter } from './EventEmitter';\nimport { ContentKind } from '../proto/sdk-streaming';\nimport { SynxedConfig, PlayerState, PlaySongOptions, PlayPlaylistOptions, SynxedEvents } from '../types';\nimport { SynxedError, SynxedPlaybackError, SynxedConnectionError } from './errors';\n\nexport class SynxedPlayer extends EventEmitter<SynxedEvents> {\n private transport: TransportManager;\n private audio: AudioEngine;\n private playlist: PlaylistManager;\n private config: SynxedConfig;\n private status: PlayerState['status'] = 'idle';\n\n constructor(config: SynxedConfig) {\n super();\n this.config = config;\n this.transport = new TransportManager();\n this.audio = new AudioEngine();\n this.playlist = new PlaylistManager();\n\n this.setupListeners();\n\n if (config.autoConnect) {\n this.connect();\n }\n }\n\n private setupListeners(): void {\n // Transport Listeners\n this.transport.on('connected', () => this.emit('connected'));\n this.transport.on('disconnected', (reason) => this.emit('disconnected', reason));\n this.transport.on('error', (error) => this.emit('error', new SynxedConnectionError(error.message)));\n this.transport.on('message', (payload) => this.handleServerMessage(payload));\n\n // Audio Listeners\n this.audio.on('playing', () => this.updateStatus('playing'));\n this.audio.on('paused', () => this.updateStatus('paused'));\n this.audio.on('stopped', () => this.updateStatus('idle'));\n this.audio.on('loading', () => this.updateStatus('loading'));\n this.audio.on('error', (error) => this.emit('error', new SynxedPlaybackError(error)));\n this.audio.on('timeupdate', (time) => this.emit('timeUpdate', time));\n this.audio.on('ended', () => this.handleTrackEnded());\n }\n\n private connect(): void {\n this.transport.connect(this.config.apiKey, this.config.serverUrl);\n }\n\n async playSong(options: PlaySongOptions): Promise<void> {\n if (!this.transport.isConnected) this.connect();\n \n this.transport.sendInit({\n content_kind: ContentKind.SONG,\n catalog_track_id: options.catalogTrackId,\n internal_track_id: options.internalTrackId,\n listener_id: options.listenerId,\n });\n }\n\n async playPlaylist(options: PlayPlaylistOptions): Promise<void> {\n if (!this.transport.isConnected) this.connect();\n\n this.transport.sendInit({\n content_kind: ContentKind.PLAYLIST,\n playlist_code: options.playlistCode,\n listener_id: options.listenerId,\n });\n }\n\n pause(): void {\n this.audio.pause();\n }\n\n resume(): void {\n this.audio.play();\n }\n\n stop(): void {\n this.audio.stop();\n }\n\n skip(): void {\n const nextTrack = this.playlist.next();\n if (nextTrack) {\n // In current backend, we need to re-init for each track\n // This logic will be refined once backend handles queues\n this.playSong({ catalogTrackId: nextTrack.id }); \n }\n }\n\n seek(ms: number): void {\n this.audio.seek(ms);\n }\n\n setVolume(volume: number): void {\n this.audio.setVolume(volume);\n }\n\n private handleServerMessage(payload: any): void {\n if (payload.init_ack) {\n const ack = payload.init_ack;\n this.audio.load(ack.playback_url, ack.is_hls);\n this.audio.play();\n \n // Parse content_summary if it's a playlist\n if (ack.content_summary) {\n try {\n const summary = JSON.parse(ack.content_summary);\n if (summary.tracks) {\n this.playlist.setQueue(summary.tracks);\n }\n } catch (e) {\n // Ignore parse errors for now\n }\n }\n } else if (payload.error) {\n this.emit('error', new SynxedError(payload.error.message, payload.error.code));\n }\n }\n\n private handleTrackEnded(): void {\n if (this.playlist.hasNext) {\n this.skip();\n } else {\n this.updateStatus('idle');\n }\n }\n\n private updateStatus(status: PlayerState['status']): void {\n this.status = status;\n this.emit('stateChange', {\n status,\n currentTime: this.audio.currentTime,\n duration: this.audio.duration,\n volume: 1, // Store volume locally if needed\n });\n }\n\n destroy(): void {\n this.audio.stop();\n this.transport.disconnect();\n this.removeAllListeners();\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/proto/sdk-streaming.ts","../src/transport/ProtocolCodec.ts","../src/core/EventEmitter.ts","../src/transport/TransportManager.ts","../src/audio/AudioEngine.ts","../src/playlist/PlaylistManager.ts","../src/core/errors.ts","../src/core/SynxedPlayer.ts"],"names":["schema","root","SdkClientEnvelope","SdkServerEnvelope","ContentKind","ErrorCode","ProtocolCodec","payload","message","data","decoded","EventEmitter","event","handler","set","onceHandler","args","TransportManager","apiKey","serverUrl","normalizedUrl","io","reason","error","uint8Array","err","resolve","reject","onConnect","cleanup","onError","params","bytes","AudioEngine","url","isHls","isSafari","Hls","searchParams","e","Howl","id","ms","volume","update","PlaylistManager","tracks","track","index","SynxedError","code","SynxedConnectionError","SynxedPlaybackError","SynxedProtocolError","SynxedPlayer","config","time","options","nextTrack","eventType","positionMs","extra","ack","summary","status"],"mappings":"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","file":"index.mjs","sourcesContent":["import * as protobuf from 'protobufjs';\n\n/**\n * Protobuf schema definition for the SDK.\n * Matches the backend contract in src/modules/streaming/sdk-streaming.proto\n */\nconst schema = `\nsyntax = \"proto3\";\n\nenum ContentKind {\n CONTENT_KIND_UNSPECIFIED = 0;\n CONTENT_KIND_SONG = 1;\n CONTENT_KIND_PLAYLIST = 2;\n CONTENT_KIND_CATEGORY = 3;\n}\n\nenum ErrorCode {\n ERROR_CODE_UNSPECIFIED = 0;\n UNAUTHORIZED = 1;\n VALIDATION_ERROR = 2;\n NOT_FOUND = 3;\n PROCESSING = 4;\n SERVICE_UNAVAILABLE = 5;\n BAD_PROTOBUF = 6;\n}\n\nenum SdkControlAction {\n CONTROL_UNSPECIFIED = 0;\n CONTROL_PLAY = 1;\n CONTROL_PAUSE = 2;\n CONTROL_STOP = 3;\n CONTROL_SEEK = 4;\n}\n\nmessage SdkClientInit {\n ContentKind contentKind = 1;\n string catalogTrackId = 2;\n string internalTrackId = 3;\n string playlistCode = 4;\n string categoryQuery = 5;\n string listenerId = 6;\n string deviceType = 7;\n string countryCode = 8;\n string region = 9;\n string protocolVersion = 10;\n}\n\nmessage SdkClientControl {\n SdkControlAction action = 1;\n uint32 positionMs = 2;\n}\n\nmessage SdkClientAnalytics {\n string eventType = 1;\n uint32 positionMs = 2;\n string extraJson = 3;\n}\n\nmessage SdkClientEnvelope {\n oneof payload {\n SdkClientInit init = 1;\n SdkClientControl control = 2;\n SdkClientAnalytics analytics = 3;\n }\n}\n\nmessage SdkServerInitAck {\n string sessionId = 1;\n string playbackUrl = 2;\n bool isHls = 3;\n uint32 heartbeatIntervalMs = 4;\n string contentSummary = 5;\n}\n\nmessage SdkServerError {\n ErrorCode code = 1;\n string message = 2;\n}\n\nmessage SdkServerEnvelope {\n oneof payload {\n SdkServerInitAck initAck = 1;\n SdkServerError error = 2;\n }\n}\n`;\n\nconst root = protobuf.parse(schema).root;\n\nexport const SdkClientEnvelope = root.lookupType('SdkClientEnvelope');\nexport const SdkServerEnvelope = root.lookupType('SdkServerEnvelope');\n\nexport enum ContentKind {\n UNSPECIFIED = 0,\n SONG = 1,\n PLAYLIST = 2,\n CATEGORY = 3,\n}\n\nexport enum ErrorCode {\n UNSPECIFIED = 0,\n UNAUTHORIZED = 1,\n VALIDATION_ERROR = 2,\n NOT_FOUND = 3,\n PROCESSING = 4,\n SERVICE_UNAVAILABLE = 5,\n BAD_PROTOBUF = 6,\n}\n","import { SdkClientEnvelope, SdkServerEnvelope } from '../proto/sdk-streaming';\n\nexport class ProtocolCodec {\n /**\n * Encodes a client envelope into a binary payload.\n */\n static encodeClientEnvelope(payload: any): Uint8Array {\n const message = SdkClientEnvelope.create(payload);\n return SdkClientEnvelope.encode(message).finish();\n }\n\n /**\n * Decodes a binary payload into a server envelope.\n */\n static decodeServerEnvelope(data: Uint8Array): any {\n const decoded = SdkServerEnvelope.decode(data);\n return SdkServerEnvelope.toObject(decoded, {\n enums: String,\n longs: String,\n bytes: String,\n defaults: true,\n oneofs: true,\n });\n }\n}\n","type Handler = (...args: any[]) => void;\n\nexport class EventEmitter<Events extends Record<string, any>> {\n private listeners: Map<keyof Events, Set<Handler>> = new Map();\n\n on<K extends keyof Events>(event: K, handler: Events[K]): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(handler);\n }\n\n off<K extends keyof Events>(event: K, handler: Events[K]): void {\n const set = this.listeners.get(event);\n if (set) {\n set.delete(handler);\n }\n }\n\n once<K extends keyof Events>(event: K, handler: Events[K]): void {\n const onceHandler = ((...args: any[]) => {\n handler(...args);\n this.off(event, onceHandler as Events[K]);\n }) as Events[K];\n this.on(event, onceHandler);\n }\n\n protected emit<K extends keyof Events>(event: K, ...args: Parameters<Events[K]>): void {\n const set = this.listeners.get(event);\n if (set) {\n set.forEach((handler) => handler(...args));\n }\n }\n\n removeAllListeners(): void {\n this.listeners.clear();\n }\n}\n","import { io, Socket } from 'socket.io-client';\nimport { ProtocolCodec } from './ProtocolCodec';\nimport { EventEmitter } from '../core/EventEmitter';\n\nexport interface TransportEvents {\n connected: () => void;\n disconnected: (reason: string) => void;\n error: (error: any) => void;\n message: (payload: any) => void;\n}\n\nexport class TransportManager extends EventEmitter<TransportEvents> {\n private socket: Socket | null = null;\n private apiKey: string | null = null;\n private serverUrl: string | null = null;\n\n constructor() {\n super();\n }\n\n /**\n * Connects to the Synxed SDK namespace.\n */\n connect(apiKey: string, serverUrl: string): void {\n if (this.socket?.connected) return;\n\n this.apiKey = apiKey;\n this.serverUrl = serverUrl;\n\n const normalizedUrl = serverUrl.endsWith('/') ? serverUrl.slice(0, -1) : serverUrl;\n\n this.socket = io(`${normalizedUrl}/sdk`, {\n auth: { apiKey },\n transports: ['websocket'],\n });\n\n this.socket.on('connect', () => {\n this.emit('connected');\n });\n\n this.socket.on('disconnect', (reason) => {\n this.emit('disconnected', reason);\n });\n\n this.socket.on('connect_error', (error) => {\n this.emit('error', error);\n });\n\n this.socket.on('d', (data: ArrayBuffer | Uint8Array) => {\n try {\n const uint8Array = data instanceof Uint8Array ? data : new Uint8Array(data);\n const payload = ProtocolCodec.decodeServerEnvelope(uint8Array);\n this.emit('message', payload);\n } catch (err) {\n this.emit('error', new Error(`Failed to decode message: ${err}`));\n }\n });\n }\n\n /**\n * Returns a promise that resolves when the socket is connected.\n */\n async waitForConnection(): Promise<void> {\n console.log('[Synxed] Waiting for connection...');\n if (this.socket?.connected) {\n console.log('[Synxed] Already connected');\n return;\n }\n \n return new Promise((resolve, reject) => {\n const onConnect = () => {\n console.log('[Synxed] Socket connected event fired');\n cleanup();\n resolve();\n };\n \n const onError = (err: any) => {\n console.error('[Synxed] Socket connection error:', err);\n cleanup();\n reject(err);\n };\n \n const cleanup = () => {\n this.socket?.off('connect', onConnect);\n this.socket?.off('connect_error', onError);\n };\n\n this.socket?.once('connect', onConnect);\n this.socket?.once('connect_error', onError);\n \n if (!this.socket) {\n console.error('[Synxed] No socket instance found');\n reject(new Error('Socket not initialized. Call connect() first.'));\n }\n });\n }\n\n /**\n * Disconnects from the server.\n */\n disconnect(): void {\n if (this.socket) {\n this.socket.disconnect();\n this.socket = null;\n }\n }\n\n /**\n * Sends an init message to the backend.\n */\n sendInit(params: any): void {\n if (!this.socket?.connected) {\n throw new Error('Socket not connected');\n }\n\n console.log('[Synxed] Sending init packet:', params);\n const payload = { init: params };\n const bytes = ProtocolCodec.encodeClientEnvelope(payload);\n this.socket.emit('d', bytes);\n }\n\n /**\n * Sends a control message.\n */\n sendControl(params: any): void {\n if (!this.socket?.connected) return;\n const payload = { control: params };\n const bytes = ProtocolCodec.encodeClientEnvelope(payload);\n this.socket.emit('d', bytes);\n }\n\n /**\n * Sends an analytics message.\n */\n sendAnalytics(params: any): void {\n if (!this.socket?.connected) return;\n const payload = { analytics: params };\n const bytes = ProtocolCodec.encodeClientEnvelope(payload);\n this.socket.emit('d', bytes);\n }\n\n /**\n * Checks if the socket is connected.\n */\n get isConnected(): boolean {\n return this.socket?.connected || false;\n }\n}\n","import { Howl } from 'howler';\nimport { EventEmitter } from '../core/EventEmitter';\n\nexport interface AudioEvents {\n playing: () => void;\n paused: () => void;\n stopped: () => void;\n ended: () => void;\n loading: () => void;\n loaded: () => void;\n error: (error: any) => void;\n timeupdate: (data: { currentTime: number; duration: number }) => void;\n}\n\nexport class AudioEngine extends EventEmitter<AudioEvents> {\n private howl: Howl | null = null;\n private hls: any = null;\n private audioEl: HTMLAudioElement | null = null;\n private updateTimer: number | null = null;\n\n constructor() {\n super();\n }\n\n\n /**\n * Loads a track URL.\n */\n async load(url: string, isHls: boolean): Promise<void> {\n this.stop();\n this.emit('loading');\n\n console.log('[Synxed] AudioEngine loading URL:', url, 'isHls:', isHls);\n\n // Safari has native HLS support. Other browsers need hls.js.\n const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);\n\n if (isHls && !isSafari) {\n try {\n const { default: Hls } = await import('hls.js');\n if (Hls.isSupported()) {\n console.log('[Synxed] Using Hls.js for playback');\n \n // Extract query parameters from the manifest URL to forward to segments\n const urlObj = new URL(url);\n const searchParams = urlObj.search;\n\n this.hls = new Hls();\n this.audioEl = new Audio();\n this.hls.loadSource(url);\n this.hls.attachMedia(this.audioEl);\n \n this.audioEl.onplay = () => {\n this.emit('playing');\n this.startTimeUpdateLoop();\n };\n this.audioEl.onpause = () => {\n this.emit('paused');\n this.stopTimeUpdateLoop();\n };\n this.audioEl.onended = () => {\n this.emit('ended');\n this.stopTimeUpdateLoop();\n };\n this.audioEl.onerror = (e) => {\n console.error('[Synxed] HLS Audio error:', e);\n this.emit('error', e);\n };\n this.audioEl.onloadedmetadata = () => {\n this.emit('loaded');\n };\n\n this.hls.on(Hls.Events.ERROR, (event: any, data: any) => {\n if (data.fatal) {\n console.error('[Synxed] Fatal HLS error:', data);\n this.emit('error', data);\n }\n });\n\n return;\n }\n } catch (e) {\n console.warn('[Synxed] hls.js not found or error loading, falling back to native');\n }\n }\n\n // Fallback to Howler\n this.howl = new Howl({\n src: [url],\n html5: true, \n format: isHls ? ['m3u8'] : undefined,\n onload: () => {\n console.log('[Synxed] AudioEngine loaded successfully');\n this.emit('loaded');\n },\n onloaderror: (id, err) => {\n console.error('[Synxed] AudioEngine load error:', err, 'ID:', id);\n this.emit('error', err);\n },\n onplay: () => {\n this.emit('playing');\n this.startTimeUpdateLoop();\n },\n onpause: () => {\n this.emit('paused');\n this.stopTimeUpdateLoop();\n },\n onstop: () => {\n this.emit('stopped');\n this.stopTimeUpdateLoop();\n },\n onend: () => {\n this.emit('ended');\n this.stopTimeUpdateLoop();\n },\n });\n }\n\n play(): void {\n if (this.audioEl) {\n this.audioEl.play().catch(e => console.error('[Synxed] Play failed:', e));\n } else {\n this.howl?.play();\n }\n }\n\n pause(): void {\n if (this.audioEl) {\n this.audioEl.pause();\n } else {\n this.howl?.pause();\n }\n }\n\n stop(): void {\n if (this.hls) {\n this.hls.destroy();\n this.hls = null;\n }\n if (this.audioEl) {\n this.audioEl.pause();\n this.audioEl.src = '';\n this.audioEl = null;\n }\n if (this.howl) {\n this.howl.stop();\n this.howl.unload();\n this.howl = null;\n }\n this.stopTimeUpdateLoop();\n }\n\n seek(ms: number): void {\n if (this.audioEl) {\n this.audioEl.currentTime = ms / 1000;\n } else {\n this.howl?.seek(ms / 1000);\n }\n }\n\n setVolume(volume: number): void {\n if (this.audioEl) {\n this.audioEl.volume = volume;\n }\n if (this.howl) {\n this.howl.volume(volume);\n }\n }\n\n get duration(): number {\n if (this.audioEl) return this.audioEl.duration * 1000;\n return (this.howl?.duration() || 0) * 1000;\n }\n\n get currentTime(): number {\n if (this.audioEl) return this.audioEl.currentTime * 1000;\n return (this.howl?.seek() as number || 0) * 1000;\n }\n\n private startTimeUpdateLoop(): void {\n this.stopTimeUpdateLoop();\n const update = () => {\n const isPlaying = this.audioEl ? !this.audioEl.paused : this.howl?.playing();\n if (isPlaying) {\n this.emit('timeupdate', {\n currentTime: this.currentTime,\n duration: this.duration,\n });\n this.updateTimer = requestAnimationFrame(update);\n }\n };\n this.updateTimer = requestAnimationFrame(update);\n }\n\n private stopTimeUpdateLoop(): void {\n if (this.updateTimer !== null) {\n cancelAnimationFrame(this.updateTimer);\n this.updateTimer = null;\n }\n }\n}\n","import { EventEmitter } from '../core/EventEmitter';\n\nexport interface PlaylistEvents {\n trackChanged: (track: any) => void;\n queueUpdated: (tracks: any[]) => void;\n}\n\nexport class PlaylistManager extends EventEmitter<PlaylistEvents> {\n private queue: any[] = [];\n private currentIndex: number = -1;\n\n constructor() {\n super();\n }\n\n setQueue(tracks: any[]): void {\n this.queue = tracks;\n this.currentIndex = tracks.length > 0 ? 0 : -1;\n this.emit('queueUpdated', this.queue);\n }\n\n getCurrentTrack(): any | null {\n if (this.currentIndex >= 0 && this.currentIndex < this.queue.length) {\n return this.queue[this.currentIndex];\n }\n return null;\n }\n\n next(): any | null {\n if (this.currentIndex < this.queue.length - 1) {\n this.currentIndex++;\n const track = this.getCurrentTrack();\n this.emit('trackChanged', track);\n return track;\n }\n return null;\n }\n\n previous(): any | null {\n if (this.currentIndex > 0) {\n this.currentIndex--;\n const track = this.getCurrentTrack();\n this.emit('trackChanged', track);\n return track;\n }\n return null;\n }\n\n skipTo(index: number): any | null {\n if (index >= 0 && index < this.queue.length) {\n this.currentIndex = index;\n const track = this.getCurrentTrack();\n this.emit('trackChanged', track);\n return track;\n }\n return null;\n }\n\n get hasNext(): boolean {\n return this.currentIndex < this.queue.length - 1;\n }\n\n get hasPrevious(): boolean {\n return this.currentIndex > 0;\n }\n}\n","export class SynxedError extends Error {\n constructor(message: string, public code?: string) {\n super(message);\n this.name = 'SynxedError';\n }\n}\n\nexport class SynxedConnectionError extends SynxedError {\n constructor(message: string) {\n super(message);\n this.name = 'SynxedConnectionError';\n }\n}\n\nexport class SynxedPlaybackError extends SynxedError {\n constructor(error: any) {\n super(typeof error === 'string' ? error : 'Playback failed');\n this.name = 'SynxedPlaybackError';\n }\n}\n\nexport class SynxedProtocolError extends SynxedError {\n constructor(message: string) {\n super(message);\n this.name = 'SynxedProtocolError';\n }\n}\n","import { TransportManager } from '../transport/TransportManager';\nimport { AudioEngine } from '../audio/AudioEngine';\nimport { PlaylistManager } from '../playlist/PlaylistManager';\nimport { EventEmitter } from './EventEmitter';\nimport { ContentKind } from '../proto/sdk-streaming';\nimport { SynxedConfig, PlayerState, PlaySongOptions, PlayPlaylistOptions, SynxedEvents } from '../types';\nimport { SynxedError, SynxedPlaybackError, SynxedConnectionError } from './errors';\n\nexport class SynxedPlayer extends EventEmitter<SynxedEvents> {\n private transport: TransportManager;\n private audio: AudioEngine;\n private playlist: PlaylistManager;\n private config: SynxedConfig;\n private status: PlayerState['status'] = 'idle';\n\n constructor(config: SynxedConfig) {\n super();\n this.config = config;\n this.transport = new TransportManager();\n this.audio = new AudioEngine();\n this.playlist = new PlaylistManager();\n\n this.setupListeners();\n\n if (config.autoConnect) {\n this.connect();\n }\n }\n\n private setupListeners(): void {\n // Transport Listeners\n this.transport.on('connected', () => this.emit('connected'));\n this.transport.on('disconnected', (reason) => this.emit('disconnected', reason));\n this.transport.on('error', (error) => this.emit('error', new SynxedConnectionError(error.message)));\n this.transport.on('message', (payload) => this.handleServerMessage(payload));\n\n // Audio Listeners\n this.audio.on('playing', () => this.updateStatus('playing'));\n this.audio.on('paused', () => this.updateStatus('paused'));\n this.audio.on('stopped', () => this.updateStatus('idle'));\n this.audio.on('loading', () => this.updateStatus('loading'));\n this.audio.on('error', (error) => this.emit('error', new SynxedPlaybackError(error)));\n this.audio.on('timeupdate', (time) => this.emit('timeUpdate', time));\n this.audio.on('ended', () => this.handleTrackEnded());\n }\n\n private connect(): void {\n this.transport.connect(this.config.apiKey, this.config.serverUrl);\n }\n\n async playSong(options: PlaySongOptions): Promise<void> {\n if (!this.transport.isConnected) this.connect();\n await this.transport.waitForConnection();\n \n this.transport.sendInit({\n contentKind: ContentKind.SONG,\n catalogTrackId: options.catalogTrackId,\n internalTrackId: options.internalTrackId,\n listenerId: options.listenerId,\n });\n }\n\n async playPlaylist(options: PlayPlaylistOptions): Promise<void> {\n if (!this.transport.isConnected) this.connect();\n await this.transport.waitForConnection();\n\n this.transport.sendInit({\n contentKind: ContentKind.PLAYLIST,\n playlistCode: options.playlistCode,\n listenerId: options.listenerId,\n });\n }\n\n pause(): void {\n this.audio.pause();\n this.transport.sendControl({\n action: 2, // PAUSE\n positionMs: Math.floor(this.audio.currentTime * 1000),\n });\n }\n\n resume(): void {\n this.audio.play();\n this.transport.sendControl({\n action: 1, // PLAY\n positionMs: Math.floor(this.audio.currentTime * 1000),\n });\n }\n\n stop(): void {\n this.audio.stop();\n this.transport.sendControl({\n action: 3, // STOP\n positionMs: Math.floor(this.audio.currentTime * 1000),\n });\n }\n\n skip(): void {\n const nextTrack = this.playlist.next();\n if (nextTrack) {\n // In current backend, we need to re-init for each track\n // This logic will be refined once backend handles queues\n this.playSong({ catalogTrackId: nextTrack.id }); \n }\n }\n\n seek(ms: number): void {\n this.audio.seek(ms);\n this.transport.sendControl({\n action: 4, // SEEK\n positionMs: ms,\n });\n }\n\n setVolume(volume: number): void {\n this.audio.setVolume(volume);\n }\n\n private emitAnalytics(eventType: string, positionMs?: number, extra?: any): void {\n this.transport.sendAnalytics({\n eventType,\n positionMs: positionMs ?? Math.floor(this.audio.currentTime * 1000),\n extraJson: extra ? JSON.stringify(extra) : undefined,\n });\n }\n\n private async handleServerMessage(payload: any): Promise<void> {\n console.log('[Synxed] Received server message:', payload);\n if (payload.initAck) {\n const ack = payload.initAck;\n console.log('[Synxed] Loading playback URL:', ack.playbackUrl, 'isHls:', ack.isHls);\n await this.audio.load(ack.playbackUrl, ack.isHls);\n this.audio.play();\n this.emitAnalytics('stream_start');\n \n // Parse contentSummary if it's a playlist\n if (ack.contentSummary) {\n try {\n const summary = JSON.parse(ack.contentSummary);\n if (summary.tracks) {\n this.playlist.setQueue(summary.tracks);\n }\n } catch (e) {\n // Ignore parse errors for now\n }\n }\n } else if (payload.error) {\n this.emit('error', new SynxedError(payload.error.message, payload.error.code));\n }\n }\n\n private handleTrackEnded(): void {\n if (this.playlist.hasNext) {\n this.skip();\n } else {\n this.updateStatus('idle');\n }\n }\n\n private updateStatus(status: PlayerState['status']): void {\n this.status = status;\n this.emit('stateChange', {\n status,\n currentTime: this.audio.currentTime,\n duration: this.audio.duration,\n volume: 1, // Store volume locally if needed\n });\n }\n\n destroy(): void {\n this.audio.stop();\n this.transport.disconnect();\n this.removeAllListeners();\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synxed-sdk",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Synxed music SDK — integrate streaming music playback into any web app",
5
5
  "bin": {
6
6
  "synxed": "dist/cli.js"
@@ -27,15 +27,16 @@
27
27
  "prepublishOnly": "npm run build"
28
28
  },
29
29
  "dependencies": {
30
- "socket.io-client": "^4.7.5",
30
+ "howler": "^2.2.4",
31
31
  "protobufjs": "^7.3.0",
32
- "howler": "^2.2.4"
32
+ "socket.io-client": "^4.7.5"
33
33
  },
34
34
  "devDependencies": {
35
- "typescript": "^5.5.4",
35
+ "@types/howler": "^2.2.11",
36
+ "hls.js": "^1.6.16",
36
37
  "tsup": "^8.2.3",
37
- "vitest": "^2.0.5",
38
- "@types/howler": "^2.2.11"
38
+ "typescript": "^5.5.4",
39
+ "vitest": "^2.0.5"
39
40
  },
40
41
  "peerDependencies": {
41
42
  "hls.js": "^1.5.0"