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.
- package/dist/core/player.d.ts +3 -0
- package/dist/core/player.js +65 -0
- package/package.json +1 -1
- package/src/core/player.ts +68 -0
package/dist/core/player.d.ts
CHANGED
package/dist/core/player.js
CHANGED
|
@@ -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
package/src/core/player.ts
CHANGED
|
@@ -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
|
}
|