tunzo-player 1.0.23 → 1.0.24

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.
@@ -37,4 +37,7 @@ export declare class Player {
37
37
  static setQuality(index: number): void;
38
38
  static getQueue(): any[];
39
39
  static getPlaylist(): any[];
40
+ private static setupMediaSession;
41
+ private static updateMediaSessionMetadata;
42
+ private static updatePositionState;
40
43
  }
@@ -7,6 +7,7 @@ class Player {
7
7
  static initialize(playlist, quality = 3) {
8
8
  this.playlist = playlist;
9
9
  this.selectedQuality = quality;
10
+ this.setupMediaSession();
10
11
  }
11
12
  /** Call this once on user gesture to unlock audio in WebView */
12
13
  static unlockAudio() {
@@ -27,9 +28,14 @@ class Player {
27
28
  url = url.replace('http://', 'https://');
28
29
  }
29
30
  this.audio.src = url;
31
+ this.audio.preload = 'auto'; // Improve loading
30
32
  this.audio.load(); // Ensure audio is loaded before play
31
33
  this.audio.play().then(() => {
32
34
  this.isPlaying = true;
35
+ this.updateMediaSessionMetadata(song);
36
+ if ('mediaSession' in navigator) {
37
+ navigator.mediaSession.playbackState = 'playing';
38
+ }
33
39
  }).catch((err) => {
34
40
  this.isPlaying = false;
35
41
  console.warn('Audio play failed:', err);
@@ -37,6 +43,7 @@ class Player {
37
43
  // Set duration
38
44
  this.audio.onloadedmetadata = () => {
39
45
  this.duration = this.audio.duration;
46
+ this.updatePositionState();
40
47
  };
41
48
  // Set current time
42
49
  this.audio.ontimeupdate = () => {
@@ -54,10 +61,16 @@ class Player {
54
61
  static pause() {
55
62
  this.audio.pause();
56
63
  this.isPlaying = false;
64
+ if ('mediaSession' in navigator) {
65
+ navigator.mediaSession.playbackState = 'paused';
66
+ }
57
67
  }
58
68
  static resume() {
59
69
  this.audio.play();
60
70
  this.isPlaying = true;
71
+ if ('mediaSession' in navigator) {
72
+ navigator.mediaSession.playbackState = 'playing';
73
+ }
61
74
  }
62
75
  static togglePlayPause() {
63
76
  if (this.isPlaying) {
@@ -88,6 +101,7 @@ class Player {
88
101
  }
89
102
  static seek(seconds) {
90
103
  this.audio.currentTime = seconds;
104
+ this.updatePositionState();
91
105
  }
92
106
  static autoNext() {
93
107
  this.next();
@@ -148,6 +162,57 @@ class Player {
148
162
  static getPlaylist() {
149
163
  return this.playlist;
150
164
  }
165
+ // -------------------------------------------------------------------------
166
+ // Native Media Session (Lock Screen Controls)
167
+ // -------------------------------------------------------------------------
168
+ static setupMediaSession() {
169
+ if ('mediaSession' in navigator) {
170
+ navigator.mediaSession.setActionHandler('play', () => this.resume());
171
+ navigator.mediaSession.setActionHandler('pause', () => this.pause());
172
+ navigator.mediaSession.setActionHandler('previoustrack', () => this.prev());
173
+ navigator.mediaSession.setActionHandler('nexttrack', () => this.next());
174
+ navigator.mediaSession.setActionHandler('seekto', (details) => {
175
+ if (details.seekTime !== undefined) {
176
+ this.seek(details.seekTime);
177
+ }
178
+ });
179
+ }
180
+ }
181
+ static updateMediaSessionMetadata(song) {
182
+ var _a;
183
+ if ('mediaSession' in navigator) {
184
+ const artwork = [];
185
+ if (song.image) {
186
+ if (Array.isArray(song.image)) {
187
+ // Assuming image array contains objects with url/link and quality
188
+ song.image.forEach((img) => {
189
+ const src = img.link || img.url || (typeof img === 'string' ? img : '');
190
+ if (src) {
191
+ artwork.push({ src, sizes: '500x500', type: 'image/jpeg' });
192
+ }
193
+ });
194
+ }
195
+ else if (typeof song.image === 'string') {
196
+ artwork.push({ src: song.image, sizes: '500x500', type: 'image/jpeg' });
197
+ }
198
+ }
199
+ navigator.mediaSession.metadata = new MediaMetadata({
200
+ title: song.name || song.title || 'Unknown Title',
201
+ artist: song.primaryArtists || song.artist || 'Unknown Artist',
202
+ album: ((_a = song.album) === null || _a === void 0 ? void 0 : _a.name) || song.album || '',
203
+ artwork: artwork.length > 0 ? artwork : undefined
204
+ });
205
+ }
206
+ }
207
+ static updatePositionState() {
208
+ if ('mediaSession' in navigator && this.duration > 0) {
209
+ navigator.mediaSession.setPositionState({
210
+ duration: this.duration,
211
+ playbackRate: this.audio.playbackRate,
212
+ position: this.audio.currentTime
213
+ });
214
+ }
215
+ }
151
216
  }
152
217
  exports.Player = Player;
153
218
  Player.audio = new Audio();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tunzo-player",
3
- "version": "1.0.23",
3
+ "version": "1.0.24",
4
4
  "description": "A music playback service for Angular and Ionic apps with native audio control support.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -17,6 +17,7 @@ export class Player {
17
17
  static initialize(playlist: any[], quality = 3) {
18
18
  this.playlist = playlist;
19
19
  this.selectedQuality = quality;
20
+ this.setupMediaSession();
20
21
  }
21
22
 
22
23
  /** Call this once on user gesture to unlock audio in WebView */
@@ -41,9 +42,14 @@ export class Player {
41
42
  }
42
43
 
43
44
  this.audio.src = url;
45
+ this.audio.preload = 'auto'; // Improve loading
44
46
  this.audio.load(); // Ensure audio is loaded before play
45
47
  this.audio.play().then(() => {
46
48
  this.isPlaying = true;
49
+ this.updateMediaSessionMetadata(song);
50
+ if ('mediaSession' in navigator) {
51
+ navigator.mediaSession.playbackState = 'playing';
52
+ }
47
53
  }).catch((err) => {
48
54
  this.isPlaying = false;
49
55
  console.warn('Audio play failed:', err);
@@ -52,6 +58,7 @@ export class Player {
52
58
  // Set duration
53
59
  this.audio.onloadedmetadata = () => {
54
60
  this.duration = this.audio.duration;
61
+ this.updatePositionState();
55
62
  };
56
63
 
57
64
  // Set current time
@@ -74,11 +81,17 @@ export class Player {
74
81
  static pause() {
75
82
  this.audio.pause();
76
83
  this.isPlaying = false;
84
+ if ('mediaSession' in navigator) {
85
+ navigator.mediaSession.playbackState = 'paused';
86
+ }
77
87
  }
78
88
 
79
89
  static resume() {
80
90
  this.audio.play();
81
91
  this.isPlaying = true;
92
+ if ('mediaSession' in navigator) {
93
+ navigator.mediaSession.playbackState = 'playing';
94
+ }
82
95
  }
83
96
 
84
97
  static togglePlayPause() {
@@ -110,6 +123,7 @@ export class Player {
110
123
 
111
124
  static seek(seconds: number) {
112
125
  this.audio.currentTime = seconds;
126
+ this.updatePositionState();
113
127
  }
114
128
 
115
129
  static autoNext() {
@@ -186,4 +200,58 @@ export class Player {
186
200
  static getPlaylist(): any[] {
187
201
  return this.playlist;
188
202
  }
203
+
204
+ // -------------------------------------------------------------------------
205
+ // Native Media Session (Lock Screen Controls)
206
+ // -------------------------------------------------------------------------
207
+
208
+ private static setupMediaSession() {
209
+ if ('mediaSession' in navigator) {
210
+ navigator.mediaSession.setActionHandler('play', () => this.resume());
211
+ navigator.mediaSession.setActionHandler('pause', () => this.pause());
212
+ navigator.mediaSession.setActionHandler('previoustrack', () => this.prev());
213
+ navigator.mediaSession.setActionHandler('nexttrack', () => this.next());
214
+ navigator.mediaSession.setActionHandler('seekto', (details) => {
215
+ if (details.seekTime !== undefined) {
216
+ this.seek(details.seekTime);
217
+ }
218
+ });
219
+ }
220
+ }
221
+
222
+ private static updateMediaSessionMetadata(song: any) {
223
+ if ('mediaSession' in navigator) {
224
+ const artwork = [];
225
+ if (song.image) {
226
+ if (Array.isArray(song.image)) {
227
+ // Assuming image array contains objects with url/link and quality
228
+ song.image.forEach((img: any) => {
229
+ const src = img.link || img.url || (typeof img === 'string' ? img : '');
230
+ if (src) {
231
+ artwork.push({ src, sizes: '500x500', type: 'image/jpeg' });
232
+ }
233
+ });
234
+ } else if (typeof song.image === 'string') {
235
+ artwork.push({ src: song.image, sizes: '500x500', type: 'image/jpeg' });
236
+ }
237
+ }
238
+
239
+ navigator.mediaSession.metadata = new MediaMetadata({
240
+ title: song.name || song.title || 'Unknown Title',
241
+ artist: song.primaryArtists || song.artist || 'Unknown Artist',
242
+ album: song.album?.name || song.album || '',
243
+ artwork: artwork.length > 0 ? artwork : undefined
244
+ });
245
+ }
246
+ }
247
+
248
+ private static updatePositionState() {
249
+ if ('mediaSession' in navigator && this.duration > 0) {
250
+ navigator.mediaSession.setPositionState({
251
+ duration: this.duration,
252
+ playbackRate: this.audio.playbackRate,
253
+ position: this.audio.currentTime
254
+ });
255
+ }
256
+ }
189
257
  }