synxed-sdk 0.1.1 → 0.1.2

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
@@ -73,7 +73,9 @@ player.resume();
73
73
  player.stop();
74
74
  player.seek(30000); // Seek to 30 seconds
75
75
  player.setVolume(0.8);
76
- player.skip(); // Skip to next track in playlist
76
+ player.skip(); // Skip to next track in playlist
77
+ player.previous(); // Go to previous track in playlist
78
+ player.skipTo(2); // Jump directly to track at index 2
77
79
  ```
78
80
 
79
81
  ## Listening for Events
@@ -112,16 +114,36 @@ player.on("error", (err) => {
112
114
  - `resume()`: `void` Resumes a paused stream.
113
115
  - `stop()`: `void` Stops playback and releases audio resources.
114
116
  - `skip()`: `void` Skips to the next track in the playlist.
117
+ - `previous()`: `void` Goes to the previous track in the playlist.
118
+ - `skipTo(index)`: `void` Jumps directly to a specific track index in the playlist.
115
119
  - `seek(ms)`: `void` Seeks to a specific position in milliseconds.
116
120
  - `setVolume(0-1)`: `void` Sets the volume.
117
121
  - `destroy()`: `void` Cleans up the player, listeners, and disconnects.
118
122
 
119
123
  ### Events
120
124
 
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.
125
+ - `stateChange`: `(state: PlayerState)` Fired when the engine state changes. Includes `currentTrack`, `currentTime`, `duration`, `volume`, and `status`.
126
+ - `timeUpdate`: `({ currentTime, duration })` Fired during playback at ~60fps.
127
+ - `trackChange`: `(track: TrackInfo)` Fired when a new track starts playing in a playlist.
128
+ - `queueUpdated`: `(tracks: TrackInfo[])` Fired when the playlist queue changes (initial load, skip, reset).
124
129
  - `error`: `(error: Error)` Fired on playback or connection failures.
130
+ - `connected`: `()` Fired when the socket connection is established.
131
+ - `disconnected`: `(reason: string)` Fired when the socket disconnects.
132
+
133
+ ### TrackInfo
134
+
135
+ Each track in the playlist has the following shape:
136
+
137
+ ```typescript
138
+ interface TrackInfo {
139
+ id: string;
140
+ kind: "catalog" | "internal"; // distinguishes catalog vs internal tracks
141
+ title?: string;
142
+ artist?: string;
143
+ duration?: number;
144
+ albumArt?: string;
145
+ }
146
+ ```
125
147
 
126
148
  ## License
127
149
 
package/dist/index.d.mts CHANGED
@@ -23,6 +23,7 @@ interface PlayPlaylistOptions {
23
23
  }
24
24
  interface TrackInfo {
25
25
  id: string;
26
+ kind: "catalog" | "internal";
26
27
  title?: string;
27
28
  artist?: string;
28
29
  duration?: number;
@@ -30,7 +31,7 @@ interface TrackInfo {
30
31
  }
31
32
  interface PlayerState {
32
33
  status: 'idle' | 'loading' | 'playing' | 'paused' | 'error';
33
- currentTrack?: TrackInfo;
34
+ currentTrack: TrackInfo | null;
34
35
  currentTime: number;
35
36
  duration: number;
36
37
  volume: number;
@@ -42,6 +43,7 @@ interface SynxedEvents {
42
43
  duration: number;
43
44
  }) => void;
44
45
  trackChange: (track: TrackInfo) => void;
46
+ queueUpdated: (tracks: TrackInfo[]) => void;
45
47
  error: (error: Error) => void;
46
48
  connected: () => void;
47
49
  disconnected: (reason: string) => void;
@@ -53,7 +55,9 @@ declare class SynxedPlayer extends EventEmitter<SynxedEvents> {
53
55
  private playlist;
54
56
  private config;
55
57
  private status;
58
+ private volume;
56
59
  constructor(config: SynxedConfig);
60
+ get currentTrack(): TrackInfo | null;
57
61
  private setupListeners;
58
62
  private connect;
59
63
  playSong(options: PlaySongOptions): Promise<void>;
@@ -62,6 +66,8 @@ declare class SynxedPlayer extends EventEmitter<SynxedEvents> {
62
66
  resume(): void;
63
67
  stop(): void;
64
68
  skip(): void;
69
+ previous(): void;
70
+ skipTo(index: number): void;
65
71
  seek(ms: number): void;
66
72
  setVolume(volume: number): void;
67
73
  private emitAnalytics;
package/dist/index.d.ts CHANGED
@@ -23,6 +23,7 @@ interface PlayPlaylistOptions {
23
23
  }
24
24
  interface TrackInfo {
25
25
  id: string;
26
+ kind: "catalog" | "internal";
26
27
  title?: string;
27
28
  artist?: string;
28
29
  duration?: number;
@@ -30,7 +31,7 @@ interface TrackInfo {
30
31
  }
31
32
  interface PlayerState {
32
33
  status: 'idle' | 'loading' | 'playing' | 'paused' | 'error';
33
- currentTrack?: TrackInfo;
34
+ currentTrack: TrackInfo | null;
34
35
  currentTime: number;
35
36
  duration: number;
36
37
  volume: number;
@@ -42,6 +43,7 @@ interface SynxedEvents {
42
43
  duration: number;
43
44
  }) => void;
44
45
  trackChange: (track: TrackInfo) => void;
46
+ queueUpdated: (tracks: TrackInfo[]) => void;
45
47
  error: (error: Error) => void;
46
48
  connected: () => void;
47
49
  disconnected: (reason: string) => void;
@@ -53,7 +55,9 @@ declare class SynxedPlayer extends EventEmitter<SynxedEvents> {
53
55
  private playlist;
54
56
  private config;
55
57
  private status;
58
+ private volume;
56
59
  constructor(config: SynxedConfig);
60
+ get currentTrack(): TrackInfo | null;
57
61
  private setupListeners;
58
62
  private connect;
59
63
  playSong(options: PlaySongOptions): Promise<void>;
@@ -62,6 +66,8 @@ declare class SynxedPlayer extends EventEmitter<SynxedEvents> {
62
66
  resume(): void;
63
67
  stop(): void;
64
68
  skip(): void;
69
+ previous(): void;
70
+ skipTo(index: number): void;
65
71
  seek(ms: number): void;
66
72
  setVolume(volume: number): void;
67
73
  private emitAnalytics;
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 C=`
1
+ 'use strict';var socket_ioClient=require('socket.io-client'),E=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 E__namespace=/*#__PURE__*/_interopNamespace(E);var C=`
2
2
  syntax = "proto3";
3
3
 
4
4
  enum ContentKind {
@@ -24,6 +24,8 @@ enum SdkControlAction {
24
24
  CONTROL_PAUSE = 2;
25
25
  CONTROL_STOP = 3;
26
26
  CONTROL_SEEK = 4;
27
+ CONTROL_SKIP_NEXT = 5;
28
+ CONTROL_SKIP_PREVIOUS = 6;
27
29
  }
28
30
 
29
31
  message SdkClientInit {
@@ -77,5 +79,5 @@ message SdkServerEnvelope {
77
79
  SdkServerError error = 2;
78
80
  }
79
81
  }
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
82
+ `,S=E__namespace.parse(C).root,f=S.lookupType("SdkClientEnvelope"),k=S.lookupType("SdkServerEnvelope"),T=(i=>(i[i.UNSPECIFIED=0]="UNSPECIFIED",i[i.SONG=1]="SONG",i[i.PLAYLIST=2]="PLAYLIST",i[i.CATEGORY=3]="CATEGORY",i))(T||{}),I=(s=>(s[s.UNSPECIFIED=0]="UNSPECIFIED",s[s.UNAUTHORIZED=1]="UNAUTHORIZED",s[s.VALIDATION_ERROR=2]="VALIDATION_ERROR",s[s.NOT_FOUND=3]="NOT_FOUND",s[s.PROCESSING=4]="PROCESSING",s[s.SERVICE_UNAVAILABLE=5]="SERVICE_UNAVAILABLE",s[s.BAD_PROTOBUF=6]="BAD_PROTOBUF",s))(I||{});var c=class{static encodeClientEnvelope(n){let t=f.create(n);return f.encode(t).finish()}static decodeServerEnvelope(n){let t=k.decode(n);return k.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 h=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,r=i.includes("localhost")||i.includes("127.0.0.1");this.socket=socket_ioClient.io(`${i}/sdk`,{auth:{apiKey:t},transports:r?["polling","websocket"]:["websocket","polling"],extraHeaders:{"ngrok-skip-browser-warning":"true"}}),this.socket.on("connect",()=>{this.emit("connected");}),this.socket.on("disconnect",o=>{this.emit("disconnected",o);}),this.socket.on("connect_error",o=>{this.emit("error",o);}),this.socket.on("d",o=>{try{let s=o instanceof Uint8Array?o:new Uint8Array(o),d=c.decodeServerEnvelope(s);this.emit("message",d);}catch(s){this.emit("error",new Error(`Failed to decode message: ${s}`));}});}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"),o(),t();},r=s=>{console.error("[Synxed] Socket connection error:",s),o(),e(s);},o=()=>{this.socket?.off("connect",i),this.socket?.off("connect_error",r);};this.socket?.once("connect",i),this.socket?.once("connect_error",r),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=c.encodeClientEnvelope(e);this.socket.emit("d",i);}sendControl(t){if(!this.socket?.connected)return;let e={control:t},i=c.encodeClientEnvelope(e);this.socket.emit("d",i);}sendAnalytics(t){if(!this.socket?.connected)return;let e={analytics:t},i=c.encodeClientEnvelope(e);this.socket.emit("d",i);}get isConnected(){return this.socket?.connected||false}};var p=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:r}=await import('hls.js');if(r.isSupported()){console.log("[Synxed] Using Hls.js for playback");let s=new URL(t).search;this.hls=new r({xhrSetup:d=>{d.setRequestHeader("ngrok-skip-browser-warning","true");}}),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=d=>{console.error("[Synxed] HLS Audio error:",d),this.emit("error",d);},this.audioEl.onloadedmetadata=()=>{this.emit("loaded");},this.hls.on(r.Events.ERROR,(d,v)=>{v.fatal&&(console.error("[Synxed] Fatal HLS error:",v),this.emit("error",v));});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:(r,o)=>{console.error("[Synxed] AudioEngine load error:",o,"ID:",r),this.emit("error",o);},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 m=class extends a{constructor(){super();this.queue=[];this.currentIndex=-1;}setQueue(t,e){this.queue=t,this.currentIndex=typeof e=="number"&&e>=0&&e<t.length?e:t.length>0?0:-1,this.emit("queueUpdated",this.queue),this.getCurrentTrack()&&this.emit("trackChanged",this.getCurrentTrack());}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}reset(){this.queue=[],this.currentIndex=-1,this.emit("queueUpdated",[]);}get hasNext(){return this.currentIndex<this.queue.length-1}get hasPrevious(){return this.currentIndex>0}get queueSnapshot(){return [...this.queue]}get currentIndexSnapshot(){return this.currentIndex}};var u=class extends Error{constructor(t,e){super(t);this.code=e;this.name="SynxedError";}},y=class extends u{constructor(n){super(n),this.name="SynxedConnectionError";}},g=class extends u{constructor(n){super(typeof n=="string"?n:"Playback failed"),this.name="SynxedPlaybackError";}},O=class extends u{constructor(n){super(n),this.name="SynxedProtocolError";}};var x=class extends a{constructor(t){super();this.status="idle";this.volume=.8;this.config=t,this.transport=new h,this.audio=new p,this.playlist=new m,this.setupListeners(),t.autoConnect&&this.connect();}get currentTrack(){return this.playlist.getCurrentTrack()}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 y(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 g(t))),this.audio.on("timeupdate",t=>this.emit("timeUpdate",t)),this.audio.on("ended",()=>this.handleTrackEnded()),this.playlist.on("trackChanged",t=>this.emit("trackChange",t)),this.playlist.on("queueUpdated",t=>this.emit("queueUpdated",t));}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(){if(this.playlist.queueSnapshot.length>1){this.transport.sendControl({action:5,positionMs:Math.floor(this.audio.currentTime*1e3)}),this.updateStatus("loading");return}let t=this.playlist.next();t&&this.playSong(t.kind==="internal"?{internalTrackId:t.id}:{catalogTrackId:t.id});}previous(){if(this.playlist.queueSnapshot.length>1){this.transport.sendControl({action:6,positionMs:Math.floor(this.audio.currentTime*1e3)}),this.updateStatus("loading");return}let t=this.playlist.previous();t&&this.playSong(t.kind==="internal"?{internalTrackId:t.id}:{catalogTrackId:t.id});}skipTo(t){let e=this.playlist.skipTo(t);e&&this.playSong(e.kind==="internal"?{internalTrackId:e.id}:{catalogTrackId:e.id});}seek(t){this.audio.seek(t),this.transport.sendControl({action:4,positionMs:t});}setVolume(t){this.volume=Math.max(0,Math.min(1,t)),this.audio.setVolume(this.volume);}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);if(i.tracks&&Array.isArray(i.tracks)){let r=typeof i.currentIndex=="number"?i.currentIndex:0;this.playlist.setQueue(i.tracks,r);}}catch{}}else t.error&&this.emit("error",new u(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,currentTrack:this.playlist.getCurrentTrack(),currentTime:this.audio.currentTime,duration:this.audio.duration,volume:this.volume});}destroy(){this.audio.stop(),this.transport.disconnect(),this.playlist.reset(),this.removeAllListeners();}};exports.ContentKind=T;exports.ErrorCode=I;exports.SynxedConnectionError=y;exports.SynxedError=u;exports.SynxedPlaybackError=g;exports.SynxedPlayer=x;exports.SynxedProtocolError=O;//# sourceMappingURL=index.js.map
81
83
  //# 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","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"]}
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","E","SdkClientEnvelope","SdkServerEnvelope","ContentKind","ErrorCode","ProtocolCodec","payload","message","data","decoded","EventEmitter","event","handler","set","onceHandler","args","TransportManager","apiKey","serverUrl","normalizedUrl","isLocal","io","reason","error","uint8Array","err","resolve","reject","onConnect","cleanup","onError","params","bytes","AudioEngine","url","isHls","isSafari","Hls","searchParams","xhr","e","Howl","id","ms","volume","update","PlaylistManager","tracks","currentIndex","track","index","SynxedError","code","SynxedConnectionError","SynxedPlaybackError","SynxedProtocolError","SynxedPlayer","config","time","options","nextTrack","prevTrack","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 CONTROL_SKIP_NEXT = 5;\n CONTROL_SKIP_PREVIOUS = 6;\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\nexport enum SdkControlAction {\n CONTROL_UNSPECIFIED = 0,\n CONTROL_PLAY = 1,\n CONTROL_PAUSE = 2,\n CONTROL_STOP = 3,\n CONTROL_SEEK = 4,\n CONTROL_SKIP_NEXT = 5,\n CONTROL_SKIP_PREVIOUS = 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 const isLocal = normalizedUrl.includes('localhost') || normalizedUrl.includes('127.0.0.1');\n\n this.socket = io(`${normalizedUrl}/sdk`, {\n auth: { apiKey },\n // Use websocket first for production to bypass CORS pre-flight blocks\n transports: isLocal ? ['polling', 'websocket'] : ['websocket', 'polling'],\n extraHeaders: {\n \"ngrok-skip-browser-warning\": \"true\"\n }\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 xhrSetup: (xhr) => {\n xhr.setRequestHeader(\"ngrok-skip-browser-warning\", \"true\");\n }\n });\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';\nimport { TrackInfo } from '../types';\n\nexport interface PlaylistEvents {\n trackChanged: (track: TrackInfo) => void;\n queueUpdated: (tracks: TrackInfo[]) => void;\n}\n\nexport class PlaylistManager extends EventEmitter<PlaylistEvents> {\n private queue: TrackInfo[] = [];\n private currentIndex: number = -1;\n\n constructor() {\n super();\n }\n\n setQueue(tracks: TrackInfo[], currentIndex?: number): void {\n this.queue = tracks;\n this.currentIndex =\n typeof currentIndex === 'number' &&\n currentIndex >= 0 &&\n currentIndex < tracks.length\n ? currentIndex\n : tracks.length > 0\n ? 0\n : -1;\n this.emit('queueUpdated', this.queue);\n if (this.getCurrentTrack()) {\n this.emit('trackChanged', this.getCurrentTrack()!);\n }\n }\n\n getCurrentTrack(): TrackInfo | 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(): TrackInfo | 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(): TrackInfo | 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): TrackInfo | 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 reset(): void {\n this.queue = [];\n this.currentIndex = -1;\n this.emit('queueUpdated', []);\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 get queueSnapshot(): TrackInfo[] {\n return [...this.queue];\n }\n\n get currentIndexSnapshot(): number {\n return this.currentIndex;\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, SdkControlAction } from '../proto/sdk-streaming';\nimport { SynxedConfig, PlayerState, PlaySongOptions, PlayPlaylistOptions, SynxedEvents, TrackInfo } 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 private volume = 0.8;\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 get currentTrack(): TrackInfo | null {\n return this.playlist.getCurrentTrack();\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 // Playlist Listeners\n this.playlist.on('trackChanged', (track) => this.emit('trackChange', track));\n this.playlist.on('queueUpdated', (tracks) => this.emit('queueUpdated', tracks));\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: SdkControlAction.CONTROL_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: SdkControlAction.CONTROL_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: SdkControlAction.CONTROL_STOP,\n positionMs: Math.floor(this.audio.currentTime * 1000),\n });\n }\n\n skip(): void {\n // For multi-track playlists, delegate to server-side skip\n if (this.playlist.queueSnapshot.length > 1) {\n this.transport.sendControl({\n action: SdkControlAction.CONTROL_SKIP_NEXT,\n positionMs: Math.floor(this.audio.currentTime * 1000),\n });\n this.updateStatus('loading');\n return;\n }\n\n // Fallback: client-side skip for single-track sessions\n const nextTrack = this.playlist.next();\n if (nextTrack) {\n this.playSong(\n nextTrack.kind === 'internal'\n ? { internalTrackId: nextTrack.id }\n : { catalogTrackId: nextTrack.id },\n );\n }\n }\n\n previous(): void {\n if (this.playlist.queueSnapshot.length > 1) {\n this.transport.sendControl({\n action: SdkControlAction.CONTROL_SKIP_PREVIOUS,\n positionMs: Math.floor(this.audio.currentTime * 1000),\n });\n this.updateStatus('loading');\n return;\n }\n\n const prevTrack = this.playlist.previous();\n if (prevTrack) {\n this.playSong(\n prevTrack.kind === 'internal'\n ? { internalTrackId: prevTrack.id }\n : { catalogTrackId: prevTrack.id },\n );\n }\n }\n\n skipTo(index: number): void {\n const track = this.playlist.skipTo(index);\n if (track) {\n this.playSong(\n track.kind === 'internal'\n ? { internalTrackId: track.id }\n : { catalogTrackId: track.id },\n );\n }\n }\n\n seek(ms: number): void {\n this.audio.seek(ms);\n this.transport.sendControl({\n action: SdkControlAction.CONTROL_SEEK,\n positionMs: ms,\n });\n }\n\n setVolume(volume: number): void {\n this.volume = Math.max(0, Math.min(1, volume));\n this.audio.setVolume(this.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 to populate playlist queue\n if (ack.contentSummary) {\n try {\n const summary = JSON.parse(ack.contentSummary);\n if (summary.tracks && Array.isArray(summary.tracks)) {\n const currentIndex = typeof summary.currentIndex === 'number' ? summary.currentIndex : 0;\n this.playlist.setQueue(summary.tracks as TrackInfo[], currentIndex);\n }\n } catch (e) {\n // Content summary parse failure is non-fatal\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 currentTrack: this.playlist.getCurrentTrack(),\n currentTime: this.audio.currentTime,\n duration: this.audio.duration,\n volume: this.volume,\n });\n }\n\n destroy(): void {\n this.audio.stop();\n this.transport.disconnect();\n this.playlist.reset();\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 C=`
1
+ import {io}from'socket.io-client';import*as E from'protobufjs';import {Howl}from'howler';var C=`
2
2
  syntax = "proto3";
3
3
 
4
4
  enum ContentKind {
@@ -24,6 +24,8 @@ enum SdkControlAction {
24
24
  CONTROL_PAUSE = 2;
25
25
  CONTROL_STOP = 3;
26
26
  CONTROL_SEEK = 4;
27
+ CONTROL_SKIP_NEXT = 5;
28
+ CONTROL_SKIP_PREVIOUS = 6;
27
29
  }
28
30
 
29
31
  message SdkClientInit {
@@ -77,5 +79,5 @@ message SdkServerEnvelope {
77
79
  SdkServerError error = 2;
78
80
  }
79
81
  }
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
82
+ `,S=E.parse(C).root,f=S.lookupType("SdkClientEnvelope"),k=S.lookupType("SdkServerEnvelope"),T=(i=>(i[i.UNSPECIFIED=0]="UNSPECIFIED",i[i.SONG=1]="SONG",i[i.PLAYLIST=2]="PLAYLIST",i[i.CATEGORY=3]="CATEGORY",i))(T||{}),I=(s=>(s[s.UNSPECIFIED=0]="UNSPECIFIED",s[s.UNAUTHORIZED=1]="UNAUTHORIZED",s[s.VALIDATION_ERROR=2]="VALIDATION_ERROR",s[s.NOT_FOUND=3]="NOT_FOUND",s[s.PROCESSING=4]="PROCESSING",s[s.SERVICE_UNAVAILABLE=5]="SERVICE_UNAVAILABLE",s[s.BAD_PROTOBUF=6]="BAD_PROTOBUF",s))(I||{});var c=class{static encodeClientEnvelope(n){let t=f.create(n);return f.encode(t).finish()}static decodeServerEnvelope(n){let t=k.decode(n);return k.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 h=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,r=i.includes("localhost")||i.includes("127.0.0.1");this.socket=io(`${i}/sdk`,{auth:{apiKey:t},transports:r?["polling","websocket"]:["websocket","polling"],extraHeaders:{"ngrok-skip-browser-warning":"true"}}),this.socket.on("connect",()=>{this.emit("connected");}),this.socket.on("disconnect",o=>{this.emit("disconnected",o);}),this.socket.on("connect_error",o=>{this.emit("error",o);}),this.socket.on("d",o=>{try{let s=o instanceof Uint8Array?o:new Uint8Array(o),d=c.decodeServerEnvelope(s);this.emit("message",d);}catch(s){this.emit("error",new Error(`Failed to decode message: ${s}`));}});}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"),o(),t();},r=s=>{console.error("[Synxed] Socket connection error:",s),o(),e(s);},o=()=>{this.socket?.off("connect",i),this.socket?.off("connect_error",r);};this.socket?.once("connect",i),this.socket?.once("connect_error",r),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=c.encodeClientEnvelope(e);this.socket.emit("d",i);}sendControl(t){if(!this.socket?.connected)return;let e={control:t},i=c.encodeClientEnvelope(e);this.socket.emit("d",i);}sendAnalytics(t){if(!this.socket?.connected)return;let e={analytics:t},i=c.encodeClientEnvelope(e);this.socket.emit("d",i);}get isConnected(){return this.socket?.connected||false}};var p=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:r}=await import('hls.js');if(r.isSupported()){console.log("[Synxed] Using Hls.js for playback");let s=new URL(t).search;this.hls=new r({xhrSetup:d=>{d.setRequestHeader("ngrok-skip-browser-warning","true");}}),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=d=>{console.error("[Synxed] HLS Audio error:",d),this.emit("error",d);},this.audioEl.onloadedmetadata=()=>{this.emit("loaded");},this.hls.on(r.Events.ERROR,(d,v)=>{v.fatal&&(console.error("[Synxed] Fatal HLS error:",v),this.emit("error",v));});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:(r,o)=>{console.error("[Synxed] AudioEngine load error:",o,"ID:",r),this.emit("error",o);},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 m=class extends a{constructor(){super();this.queue=[];this.currentIndex=-1;}setQueue(t,e){this.queue=t,this.currentIndex=typeof e=="number"&&e>=0&&e<t.length?e:t.length>0?0:-1,this.emit("queueUpdated",this.queue),this.getCurrentTrack()&&this.emit("trackChanged",this.getCurrentTrack());}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}reset(){this.queue=[],this.currentIndex=-1,this.emit("queueUpdated",[]);}get hasNext(){return this.currentIndex<this.queue.length-1}get hasPrevious(){return this.currentIndex>0}get queueSnapshot(){return [...this.queue]}get currentIndexSnapshot(){return this.currentIndex}};var u=class extends Error{constructor(t,e){super(t);this.code=e;this.name="SynxedError";}},y=class extends u{constructor(n){super(n),this.name="SynxedConnectionError";}},g=class extends u{constructor(n){super(typeof n=="string"?n:"Playback failed"),this.name="SynxedPlaybackError";}},O=class extends u{constructor(n){super(n),this.name="SynxedProtocolError";}};var x=class extends a{constructor(t){super();this.status="idle";this.volume=.8;this.config=t,this.transport=new h,this.audio=new p,this.playlist=new m,this.setupListeners(),t.autoConnect&&this.connect();}get currentTrack(){return this.playlist.getCurrentTrack()}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 y(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 g(t))),this.audio.on("timeupdate",t=>this.emit("timeUpdate",t)),this.audio.on("ended",()=>this.handleTrackEnded()),this.playlist.on("trackChanged",t=>this.emit("trackChange",t)),this.playlist.on("queueUpdated",t=>this.emit("queueUpdated",t));}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(){if(this.playlist.queueSnapshot.length>1){this.transport.sendControl({action:5,positionMs:Math.floor(this.audio.currentTime*1e3)}),this.updateStatus("loading");return}let t=this.playlist.next();t&&this.playSong(t.kind==="internal"?{internalTrackId:t.id}:{catalogTrackId:t.id});}previous(){if(this.playlist.queueSnapshot.length>1){this.transport.sendControl({action:6,positionMs:Math.floor(this.audio.currentTime*1e3)}),this.updateStatus("loading");return}let t=this.playlist.previous();t&&this.playSong(t.kind==="internal"?{internalTrackId:t.id}:{catalogTrackId:t.id});}skipTo(t){let e=this.playlist.skipTo(t);e&&this.playSong(e.kind==="internal"?{internalTrackId:e.id}:{catalogTrackId:e.id});}seek(t){this.audio.seek(t),this.transport.sendControl({action:4,positionMs:t});}setVolume(t){this.volume=Math.max(0,Math.min(1,t)),this.audio.setVolume(this.volume);}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);if(i.tracks&&Array.isArray(i.tracks)){let r=typeof i.currentIndex=="number"?i.currentIndex:0;this.playlist.setQueue(i.tracks,r);}}catch{}}else t.error&&this.emit("error",new u(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,currentTrack:this.playlist.getCurrentTrack(),currentTime:this.audio.currentTime,duration:this.audio.duration,volume:this.volume});}destroy(){this.audio.stop(),this.transport.disconnect(),this.playlist.reset(),this.removeAllListeners();}};export{T as ContentKind,I as ErrorCode,y as SynxedConnectionError,u as SynxedError,g as SynxedPlaybackError,x as SynxedPlayer,O as SynxedProtocolError};//# sourceMappingURL=index.mjs.map
81
83
  //# 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","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"]}
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","isLocal","io","reason","error","uint8Array","err","resolve","reject","onConnect","cleanup","onError","params","bytes","AudioEngine","url","isHls","isSafari","Hls","searchParams","xhr","e","Howl","id","ms","volume","update","PlaylistManager","tracks","currentIndex","track","index","SynxedError","code","SynxedConnectionError","SynxedPlaybackError","SynxedProtocolError","SynxedPlayer","config","time","options","nextTrack","prevTrack","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 CONTROL_SKIP_NEXT = 5;\n CONTROL_SKIP_PREVIOUS = 6;\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\nexport enum SdkControlAction {\n CONTROL_UNSPECIFIED = 0,\n CONTROL_PLAY = 1,\n CONTROL_PAUSE = 2,\n CONTROL_STOP = 3,\n CONTROL_SEEK = 4,\n CONTROL_SKIP_NEXT = 5,\n CONTROL_SKIP_PREVIOUS = 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 const isLocal = normalizedUrl.includes('localhost') || normalizedUrl.includes('127.0.0.1');\n\n this.socket = io(`${normalizedUrl}/sdk`, {\n auth: { apiKey },\n // Use websocket first for production to bypass CORS pre-flight blocks\n transports: isLocal ? ['polling', 'websocket'] : ['websocket', 'polling'],\n extraHeaders: {\n \"ngrok-skip-browser-warning\": \"true\"\n }\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 xhrSetup: (xhr) => {\n xhr.setRequestHeader(\"ngrok-skip-browser-warning\", \"true\");\n }\n });\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';\nimport { TrackInfo } from '../types';\n\nexport interface PlaylistEvents {\n trackChanged: (track: TrackInfo) => void;\n queueUpdated: (tracks: TrackInfo[]) => void;\n}\n\nexport class PlaylistManager extends EventEmitter<PlaylistEvents> {\n private queue: TrackInfo[] = [];\n private currentIndex: number = -1;\n\n constructor() {\n super();\n }\n\n setQueue(tracks: TrackInfo[], currentIndex?: number): void {\n this.queue = tracks;\n this.currentIndex =\n typeof currentIndex === 'number' &&\n currentIndex >= 0 &&\n currentIndex < tracks.length\n ? currentIndex\n : tracks.length > 0\n ? 0\n : -1;\n this.emit('queueUpdated', this.queue);\n if (this.getCurrentTrack()) {\n this.emit('trackChanged', this.getCurrentTrack()!);\n }\n }\n\n getCurrentTrack(): TrackInfo | 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(): TrackInfo | 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(): TrackInfo | 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): TrackInfo | 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 reset(): void {\n this.queue = [];\n this.currentIndex = -1;\n this.emit('queueUpdated', []);\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 get queueSnapshot(): TrackInfo[] {\n return [...this.queue];\n }\n\n get currentIndexSnapshot(): number {\n return this.currentIndex;\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, SdkControlAction } from '../proto/sdk-streaming';\nimport { SynxedConfig, PlayerState, PlaySongOptions, PlayPlaylistOptions, SynxedEvents, TrackInfo } 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 private volume = 0.8;\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 get currentTrack(): TrackInfo | null {\n return this.playlist.getCurrentTrack();\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 // Playlist Listeners\n this.playlist.on('trackChanged', (track) => this.emit('trackChange', track));\n this.playlist.on('queueUpdated', (tracks) => this.emit('queueUpdated', tracks));\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: SdkControlAction.CONTROL_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: SdkControlAction.CONTROL_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: SdkControlAction.CONTROL_STOP,\n positionMs: Math.floor(this.audio.currentTime * 1000),\n });\n }\n\n skip(): void {\n // For multi-track playlists, delegate to server-side skip\n if (this.playlist.queueSnapshot.length > 1) {\n this.transport.sendControl({\n action: SdkControlAction.CONTROL_SKIP_NEXT,\n positionMs: Math.floor(this.audio.currentTime * 1000),\n });\n this.updateStatus('loading');\n return;\n }\n\n // Fallback: client-side skip for single-track sessions\n const nextTrack = this.playlist.next();\n if (nextTrack) {\n this.playSong(\n nextTrack.kind === 'internal'\n ? { internalTrackId: nextTrack.id }\n : { catalogTrackId: nextTrack.id },\n );\n }\n }\n\n previous(): void {\n if (this.playlist.queueSnapshot.length > 1) {\n this.transport.sendControl({\n action: SdkControlAction.CONTROL_SKIP_PREVIOUS,\n positionMs: Math.floor(this.audio.currentTime * 1000),\n });\n this.updateStatus('loading');\n return;\n }\n\n const prevTrack = this.playlist.previous();\n if (prevTrack) {\n this.playSong(\n prevTrack.kind === 'internal'\n ? { internalTrackId: prevTrack.id }\n : { catalogTrackId: prevTrack.id },\n );\n }\n }\n\n skipTo(index: number): void {\n const track = this.playlist.skipTo(index);\n if (track) {\n this.playSong(\n track.kind === 'internal'\n ? { internalTrackId: track.id }\n : { catalogTrackId: track.id },\n );\n }\n }\n\n seek(ms: number): void {\n this.audio.seek(ms);\n this.transport.sendControl({\n action: SdkControlAction.CONTROL_SEEK,\n positionMs: ms,\n });\n }\n\n setVolume(volume: number): void {\n this.volume = Math.max(0, Math.min(1, volume));\n this.audio.setVolume(this.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 to populate playlist queue\n if (ack.contentSummary) {\n try {\n const summary = JSON.parse(ack.contentSummary);\n if (summary.tracks && Array.isArray(summary.tracks)) {\n const currentIndex = typeof summary.currentIndex === 'number' ? summary.currentIndex : 0;\n this.playlist.setQueue(summary.tracks as TrackInfo[], currentIndex);\n }\n } catch (e) {\n // Content summary parse failure is non-fatal\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 currentTrack: this.playlist.getCurrentTrack(),\n currentTime: this.audio.currentTime,\n duration: this.audio.duration,\n volume: this.volume,\n });\n }\n\n destroy(): void {\n this.audio.stop();\n this.transport.disconnect();\n this.playlist.reset();\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.1",
3
+ "version": "0.1.2",
4
4
  "description": "Synxed music SDK — integrate streaming music playback into any web app",
5
5
  "bin": {
6
6
  "synxed": "dist/cli.js"