tunzo-player 1.0.24 → 1.0.25
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 +32 -0
- package/dist/core/player.js +29 -3
- package/package.json +1 -1
- package/src/core/player.ts +31 -3
package/README.md
CHANGED
|
@@ -76,4 +76,36 @@ yarn add tunzo-player
|
|
|
76
76
|
| 3 | High (160kbps) |
|
|
77
77
|
| 4 | Ultra (320kbps) |
|
|
78
78
|
|
|
79
|
+
| 4 | Ultra (320kbps) |
|
|
80
|
+
|
|
81
|
+
## 📱 Native Configuration (Ionic/Capacitor)
|
|
82
|
+
|
|
83
|
+
To ensure background audio works correctly on Android and iOS (preventing the app from pausing when the screen locks), you must configure your native projects.
|
|
84
|
+
|
|
85
|
+
### **Android (`android/app/src/main/AndroidManifest.xml`)**
|
|
86
|
+
|
|
87
|
+
Add the following permissions inside the `<manifest>` tag:
|
|
88
|
+
|
|
89
|
+
```xml
|
|
90
|
+
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
|
91
|
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Note:** Modern Android versions might require a foreground service notification to keep the audio alive indefinitely. The `MediaSession` API implemented in this package helps, but for guaranteed persistence, consider using a native audio plugin if issues persist.
|
|
95
|
+
|
|
96
|
+
### **iOS (`ios/App/App/Info.plist`)**
|
|
97
|
+
|
|
98
|
+
Add `audio` to the `UIBackgroundModes` key to allow background playback:
|
|
99
|
+
|
|
100
|
+
```xml
|
|
101
|
+
<key>UIBackgroundModes</key>
|
|
102
|
+
<array>
|
|
103
|
+
<string>audio</string>
|
|
104
|
+
</array>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## 🤝 Contributing
|
|
108
|
+
|
|
109
|
+
Contributions are welcome! Please open an issue or submit a pull request.
|
|
110
|
+
|
|
79
111
|
# tunzo-player
|
package/dist/core/player.js
CHANGED
|
@@ -28,6 +28,8 @@ class Player {
|
|
|
28
28
|
url = url.replace('http://', 'https://');
|
|
29
29
|
}
|
|
30
30
|
this.audio.src = url;
|
|
31
|
+
// @ts-ignore
|
|
32
|
+
this.audio.title = song.name || song.title || 'Unknown Title'; // Help some browsers identify the track
|
|
31
33
|
this.audio.preload = 'auto'; // Improve loading
|
|
32
34
|
this.audio.load(); // Ensure audio is loaded before play
|
|
33
35
|
this.audio.play().then(() => {
|
|
@@ -48,6 +50,22 @@ class Player {
|
|
|
48
50
|
// Set current time
|
|
49
51
|
this.audio.ontimeupdate = () => {
|
|
50
52
|
this.currentTime = this.audio.currentTime;
|
|
53
|
+
// Update position state less frequently to avoid spamming, but enough to keep sync
|
|
54
|
+
if (Math.floor(this.currentTime) % 5 === 0) {
|
|
55
|
+
this.updatePositionState();
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
// Handle buffering/stalled states
|
|
59
|
+
this.audio.onwaiting = () => {
|
|
60
|
+
if ('mediaSession' in navigator) {
|
|
61
|
+
navigator.mediaSession.playbackState = 'none'; // Or 'paused' to indicate buffering
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
this.audio.onplaying = () => {
|
|
65
|
+
this.isPlaying = true;
|
|
66
|
+
if ('mediaSession' in navigator) {
|
|
67
|
+
navigator.mediaSession.playbackState = 'playing';
|
|
68
|
+
}
|
|
51
69
|
};
|
|
52
70
|
// Auto-play next song
|
|
53
71
|
this.audio.onended = () => {
|
|
@@ -186,20 +204,28 @@ class Player {
|
|
|
186
204
|
if (Array.isArray(song.image)) {
|
|
187
205
|
// Assuming image array contains objects with url/link and quality
|
|
188
206
|
song.image.forEach((img) => {
|
|
189
|
-
|
|
207
|
+
let src = img.link || img.url || (typeof img === 'string' ? img : '');
|
|
190
208
|
if (src) {
|
|
209
|
+
// 🚀 Auto-convert http → https for images too
|
|
210
|
+
if (src.startsWith('http://')) {
|
|
211
|
+
src = src.replace('http://', 'https://');
|
|
212
|
+
}
|
|
191
213
|
artwork.push({ src, sizes: '500x500', type: 'image/jpeg' });
|
|
192
214
|
}
|
|
193
215
|
});
|
|
194
216
|
}
|
|
195
217
|
else if (typeof song.image === 'string') {
|
|
196
|
-
|
|
218
|
+
let src = song.image;
|
|
219
|
+
if (src.startsWith('http://')) {
|
|
220
|
+
src = src.replace('http://', 'https://');
|
|
221
|
+
}
|
|
222
|
+
artwork.push({ src: src, sizes: '500x500', type: 'image/jpeg' });
|
|
197
223
|
}
|
|
198
224
|
}
|
|
199
225
|
navigator.mediaSession.metadata = new MediaMetadata({
|
|
200
226
|
title: song.name || song.title || 'Unknown Title',
|
|
201
227
|
artist: song.primaryArtists || song.artist || 'Unknown Artist',
|
|
202
|
-
album: ((_a = song.album) === null || _a === void 0 ? void 0 : _a.name) || song.album || '',
|
|
228
|
+
album: ((_a = song.album) === null || _a === void 0 ? void 0 : _a.name) || song.album || 'Unknown Album',
|
|
203
229
|
artwork: artwork.length > 0 ? artwork : undefined
|
|
204
230
|
});
|
|
205
231
|
}
|
package/package.json
CHANGED
package/src/core/player.ts
CHANGED
|
@@ -42,6 +42,8 @@ export class Player {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
this.audio.src = url;
|
|
45
|
+
// @ts-ignore
|
|
46
|
+
this.audio.title = song.name || song.title || 'Unknown Title'; // Help some browsers identify the track
|
|
45
47
|
this.audio.preload = 'auto'; // Improve loading
|
|
46
48
|
this.audio.load(); // Ensure audio is loaded before play
|
|
47
49
|
this.audio.play().then(() => {
|
|
@@ -64,6 +66,24 @@ export class Player {
|
|
|
64
66
|
// Set current time
|
|
65
67
|
this.audio.ontimeupdate = () => {
|
|
66
68
|
this.currentTime = this.audio.currentTime;
|
|
69
|
+
// Update position state less frequently to avoid spamming, but enough to keep sync
|
|
70
|
+
if (Math.floor(this.currentTime) % 5 === 0) {
|
|
71
|
+
this.updatePositionState();
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// Handle buffering/stalled states
|
|
76
|
+
this.audio.onwaiting = () => {
|
|
77
|
+
if ('mediaSession' in navigator) {
|
|
78
|
+
navigator.mediaSession.playbackState = 'none'; // Or 'paused' to indicate buffering
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
this.audio.onplaying = () => {
|
|
83
|
+
this.isPlaying = true;
|
|
84
|
+
if ('mediaSession' in navigator) {
|
|
85
|
+
navigator.mediaSession.playbackState = 'playing';
|
|
86
|
+
}
|
|
67
87
|
};
|
|
68
88
|
|
|
69
89
|
// Auto-play next song
|
|
@@ -226,20 +246,28 @@ export class Player {
|
|
|
226
246
|
if (Array.isArray(song.image)) {
|
|
227
247
|
// Assuming image array contains objects with url/link and quality
|
|
228
248
|
song.image.forEach((img: any) => {
|
|
229
|
-
|
|
249
|
+
let src = img.link || img.url || (typeof img === 'string' ? img : '');
|
|
230
250
|
if (src) {
|
|
251
|
+
// 🚀 Auto-convert http → https for images too
|
|
252
|
+
if (src.startsWith('http://')) {
|
|
253
|
+
src = src.replace('http://', 'https://');
|
|
254
|
+
}
|
|
231
255
|
artwork.push({ src, sizes: '500x500', type: 'image/jpeg' });
|
|
232
256
|
}
|
|
233
257
|
});
|
|
234
258
|
} else if (typeof song.image === 'string') {
|
|
235
|
-
|
|
259
|
+
let src = song.image;
|
|
260
|
+
if (src.startsWith('http://')) {
|
|
261
|
+
src = src.replace('http://', 'https://');
|
|
262
|
+
}
|
|
263
|
+
artwork.push({ src: src, sizes: '500x500', type: 'image/jpeg' });
|
|
236
264
|
}
|
|
237
265
|
}
|
|
238
266
|
|
|
239
267
|
navigator.mediaSession.metadata = new MediaMetadata({
|
|
240
268
|
title: song.name || song.title || 'Unknown Title',
|
|
241
269
|
artist: song.primaryArtists || song.artist || 'Unknown Artist',
|
|
242
|
-
album: song.album?.name || song.album || '',
|
|
270
|
+
album: song.album?.name || song.album || 'Unknown Album',
|
|
243
271
|
artwork: artwork.length > 0 ? artwork : undefined
|
|
244
272
|
});
|
|
245
273
|
}
|