tunzo-player 1.0.28 → 1.0.29

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.
@@ -1,5 +1,6 @@
1
1
  import { BehaviorSubject } from 'rxjs';
2
2
  export declare class Player {
3
+ private static audio;
3
4
  private static currentSong;
4
5
  private static currentIndex;
5
6
  private static isPlaying;
@@ -10,15 +11,13 @@ export declare class Player {
10
11
  static queue$: BehaviorSubject<any[]>;
11
12
  private static playlist;
12
13
  private static selectedQuality;
13
- private static isNative;
14
- private static webAudio;
15
14
  /** Initialize with playlist and quality */
16
- static initialize(playlist: any[], quality?: number): Promise<void>;
17
- /** Call this once on user gesture to unlock audio in WebView (Web only) */
15
+ static initialize(playlist: any[], quality?: number): void;
16
+ /** Setup audio element for better compatibility */
17
+ private static setupAudioElement;
18
+ /** Call this once on user gesture to unlock audio in WebView */
18
19
  static unlockAudio(): void;
19
- static play(song: any, index?: number): Promise<void>;
20
- private static playNative;
21
- private static playWeb;
20
+ static play(song: any, index?: number): void;
22
21
  static pause(): void;
23
22
  static resume(): void;
24
23
  static togglePlayPause(): void;
@@ -40,7 +39,7 @@ export declare class Player {
40
39
  static setQuality(index: number): void;
41
40
  static getQueue(): any[];
42
41
  static getPlaylist(): any[];
43
- private static setupNativeListeners;
44
- private static setupWebListeners;
45
- private static updateWebMediaSession;
42
+ private static setupMediaSession;
43
+ private static updateMediaSessionMetadata;
44
+ private static updatePositionState;
46
45
  }
@@ -1,153 +1,103 @@
1
1
  "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
2
  Object.defineProperty(exports, "__esModule", { value: true });
12
3
  exports.Player = void 0;
13
4
  const rxjs_1 = require("rxjs");
14
- const native_audio_1 = require("@capgo/native-audio");
15
5
  const keep_awake_1 = require("@capacitor-community/keep-awake");
16
- const core_1 = require("@capacitor/core");
17
6
  class Player {
18
7
  /** Initialize with playlist and quality */
19
- static initialize(playlist_1) {
20
- return __awaiter(this, arguments, void 0, function* (playlist, quality = 3) {
21
- this.playlist = playlist;
22
- this.selectedQuality = quality;
23
- // Configure native audio if on native platform
24
- if (this.isNative) {
25
- try {
26
- yield native_audio_1.NativeAudio.configure({
27
- showNotification: true,
28
- background: true,
29
- focus: true
30
- });
31
- }
32
- catch (e) {
33
- console.warn('NativeAudio configure failed:', e);
34
- }
35
- this.setupNativeListeners();
36
- }
37
- else {
38
- this.setupWebListeners();
39
- }
40
- });
41
- }
42
- /** Call this once on user gesture to unlock audio in WebView (Web only) */
43
- static unlockAudio() {
44
- if (!this.isNative) {
45
- this.webAudio.src = '';
46
- this.webAudio.load();
47
- this.webAudio.play().catch(() => { });
48
- }
8
+ static initialize(playlist, quality = 3) {
9
+ this.playlist = playlist;
10
+ this.selectedQuality = quality;
11
+ this.setupMediaSession();
12
+ this.setupAudioElement();
49
13
  }
50
- static play(song_1) {
51
- return __awaiter(this, arguments, void 0, function* (song, index = 0) {
52
- var _a;
53
- if (!song || !song.downloadUrl)
54
- return;
55
- this.currentSong = song;
56
- this.currentIndex = index;
57
- let url = ((_a = song.downloadUrl[this.selectedQuality]) === null || _a === void 0 ? void 0 : _a.url) || '';
58
- // 🚀 Auto-convert http → https
59
- if (url.startsWith('http://')) {
60
- url = url.replace('http://', 'https://');
61
- }
62
- try {
63
- if (this.isNative) {
64
- yield this.playNative(url, song);
65
- }
66
- else {
67
- this.playWeb(url, song);
68
- }
69
- this.isPlaying = true;
70
- keep_awake_1.KeepAwake.keepAwake(); // Keep CPU awake for streaming
14
+ /** Setup audio element for better compatibility */
15
+ static setupAudioElement() {
16
+ // Enable background playback
17
+ this.audio.preload = 'auto';
18
+ // @ts-ignore - Some browsers support this
19
+ this.audio.preservesPitch = false;
20
+ // Setup event listeners
21
+ this.audio.onloadedmetadata = () => {
22
+ this.duration = this.audio.duration;
23
+ this.updatePositionState();
24
+ };
25
+ this.audio.ontimeupdate = () => {
26
+ this.currentTime = this.audio.currentTime;
27
+ if (Math.floor(this.currentTime) % 5 === 0) {
28
+ this.updatePositionState();
71
29
  }
72
- catch (err) {
73
- this.isPlaying = false;
74
- console.warn('Audio play failed:', err);
30
+ };
31
+ this.audio.onended = () => {
32
+ this.autoNext();
33
+ };
34
+ this.audio.onplaying = () => {
35
+ this.isPlaying = true;
36
+ if ('mediaSession' in navigator) {
37
+ navigator.mediaSession.playbackState = 'playing';
75
38
  }
76
- });
77
- }
78
- static playNative(url, song) {
79
- return __awaiter(this, void 0, void 0, function* () {
80
- var _a;
81
- try {
82
- // Unload previous if any (though preload might handle it, safer to be clean)
83
- // NativeAudio.unload({ assetId: 'currentSong' }).catch(() => {});
84
- // Prepare artwork for lock screen
85
- let artworkPath = '';
86
- if (song.image) {
87
- if (Array.isArray(song.image)) {
88
- // Get highest quality image
89
- const img = song.image[song.image.length - 1];
90
- artworkPath = img.link || img.url || (typeof img === 'string' ? img : '');
91
- }
92
- else if (typeof song.image === 'string') {
93
- artworkPath = song.image;
94
- }
95
- }
96
- if (artworkPath.startsWith('http://')) {
97
- artworkPath = artworkPath.replace('http://', 'https://');
98
- }
99
- yield native_audio_1.NativeAudio.preload({
100
- assetId: 'currentSong',
101
- assetPath: url,
102
- isUrl: true,
103
- audioChannelNum: 1,
104
- // Metadata for Lock Screen
105
- notificationMetadata: {
106
- album: ((_a = song.album) === null || _a === void 0 ? void 0 : _a.name) || song.album || 'Unknown Album',
107
- artist: song.primaryArtists || song.artist || 'Unknown Artist',
108
- title: song.name || song.title || 'Unknown Title',
109
- artworkUrl: artworkPath
110
- }
111
- });
112
- yield native_audio_1.NativeAudio.play({ assetId: 'currentSong' });
39
+ };
40
+ this.audio.onpause = () => {
41
+ this.isPlaying = false;
42
+ if ('mediaSession' in navigator) {
43
+ navigator.mediaSession.playbackState = 'paused';
113
44
  }
114
- catch (e) {
115
- console.error("Native play error", e);
116
- throw e;
45
+ };
46
+ this.audio.onwaiting = () => {
47
+ if ('mediaSession' in navigator) {
48
+ navigator.mediaSession.playbackState = 'none';
117
49
  }
118
- });
50
+ };
51
+ this.audio.onerror = (e) => {
52
+ console.error('Audio error:', this.audio.error, e);
53
+ };
119
54
  }
120
- static playWeb(url, song) {
121
- this.webAudio.src = url;
122
- // @ts-ignore
123
- this.webAudio.title = song.name || song.title || 'Unknown Title';
124
- this.webAudio.preload = 'auto';
125
- this.webAudio.load();
126
- this.webAudio.play();
127
- // Basic MediaSession for Web
128
- if ('mediaSession' in navigator) {
129
- this.updateWebMediaSession(song);
55
+ /** Call this once on user gesture to unlock audio in WebView */
56
+ static unlockAudio() {
57
+ this.audio.src = '';
58
+ this.audio.load();
59
+ this.audio.play().catch(() => { });
60
+ }
61
+ static play(song, index = 0) {
62
+ var _a;
63
+ if (!song || !song.downloadUrl)
64
+ return;
65
+ this.currentSong = song;
66
+ this.currentIndex = index;
67
+ let url = ((_a = song.downloadUrl[this.selectedQuality]) === null || _a === void 0 ? void 0 : _a.url) || '';
68
+ // 🚀 Auto-convert http → https
69
+ if (url.startsWith('http://')) {
70
+ url = url.replace('http://', 'https://');
130
71
  }
72
+ this.audio.src = url;
73
+ this.audio.load();
74
+ this.audio.play().then(() => {
75
+ this.isPlaying = true;
76
+ this.updateMediaSessionMetadata(song);
77
+ keep_awake_1.KeepAwake.keepAwake(); // Keep screen/CPU awake
78
+ if ('mediaSession' in navigator) {
79
+ navigator.mediaSession.playbackState = 'playing';
80
+ }
81
+ }).catch((err) => {
82
+ this.isPlaying = false;
83
+ console.warn('Audio play failed:', err);
84
+ });
131
85
  }
132
86
  static pause() {
133
- if (this.isNative) {
134
- native_audio_1.NativeAudio.pause({ assetId: 'currentSong' });
135
- }
136
- else {
137
- this.webAudio.pause();
138
- }
87
+ this.audio.pause();
139
88
  this.isPlaying = false;
140
89
  keep_awake_1.KeepAwake.allowSleep();
90
+ if ('mediaSession' in navigator) {
91
+ navigator.mediaSession.playbackState = 'paused';
92
+ }
141
93
  }
142
94
  static resume() {
143
- if (this.isNative) {
144
- native_audio_1.NativeAudio.resume({ assetId: 'currentSong' });
145
- }
146
- else {
147
- this.webAudio.play();
148
- }
95
+ this.audio.play();
149
96
  this.isPlaying = true;
150
97
  keep_awake_1.KeepAwake.keepAwake();
98
+ if ('mediaSession' in navigator) {
99
+ navigator.mediaSession.playbackState = 'playing';
100
+ }
151
101
  }
152
102
  static togglePlayPause() {
153
103
  if (this.isPlaying) {
@@ -177,13 +127,8 @@ class Player {
177
127
  }
178
128
  }
179
129
  static seek(seconds) {
180
- if (this.isNative) {
181
- native_audio_1.NativeAudio.setCurrentTime({ assetId: 'currentSong', time: seconds });
182
- }
183
- else {
184
- this.webAudio.currentTime = seconds;
185
- }
186
- this.currentTime = seconds;
130
+ this.audio.currentTime = seconds;
131
+ this.updatePositionState();
187
132
  }
188
133
  static autoNext() {
189
134
  this.next();
@@ -245,62 +190,65 @@ class Player {
245
190
  return this.playlist;
246
191
  }
247
192
  // -------------------------------------------------------------------------
248
- // Listeners
193
+ // Native Media Session (Lock Screen Controls)
249
194
  // -------------------------------------------------------------------------
250
- static setupNativeListeners() {
251
- // Song Finished
252
- native_audio_1.NativeAudio.addListener('complete', (result) => {
253
- if (result.assetId === 'currentSong') {
254
- this.autoNext();
255
- }
256
- });
257
- // Time Update (Progress) - Note: Plugin might not emit this frequently
258
- // We might need to poll for current time if the plugin doesn't emit 'progress'
259
- // @capgo/native-audio usually emits 'progress' or we use getCurrentTime
260
- // Checking docs: usually we poll or listen to 'progress'
261
- // Assuming 'progress' event exists or we use setInterval
262
- setInterval(() => __awaiter(this, void 0, void 0, function* () {
263
- if (this.isPlaying && this.isNative) {
264
- try {
265
- const result = yield native_audio_1.NativeAudio.getCurrentTime({ assetId: 'currentSong' });
266
- this.currentTime = result.currentTime;
267
- const durResult = yield native_audio_1.NativeAudio.getDuration({ assetId: 'currentSong' });
268
- this.duration = durResult.duration;
195
+ static setupMediaSession() {
196
+ if ('mediaSession' in navigator) {
197
+ navigator.mediaSession.setActionHandler('play', () => this.resume());
198
+ navigator.mediaSession.setActionHandler('pause', () => this.pause());
199
+ navigator.mediaSession.setActionHandler('previoustrack', () => this.prev());
200
+ navigator.mediaSession.setActionHandler('nexttrack', () => this.next());
201
+ navigator.mediaSession.setActionHandler('seekto', (details) => {
202
+ if (details.seekTime !== undefined) {
203
+ this.seek(details.seekTime);
269
204
  }
270
- catch (e) { }
271
- }
272
- }), 1000);
273
- }
274
- static setupWebListeners() {
275
- this.webAudio.onended = () => this.autoNext();
276
- this.webAudio.ontimeupdate = () => {
277
- this.currentTime = this.webAudio.currentTime;
278
- this.duration = this.webAudio.duration || 0;
279
- };
280
- this.webAudio.onplaying = () => this.isPlaying = true;
281
- this.webAudio.onpause = () => this.isPlaying = false;
205
+ });
206
+ }
282
207
  }
283
- static updateWebMediaSession(song) {
208
+ static updateMediaSessionMetadata(song) {
284
209
  var _a;
285
- // ... (Keep existing Web MediaSession logic if needed, or simplify)
286
- // Since we are focusing on Native, we can keep the basic one or copy the previous logic
287
- // For brevity, I'll omit the full implementation here as Native is the priority
288
- // But to be safe, let's keep a minimal version
289
210
  if ('mediaSession' in navigator) {
211
+ const artwork = [];
212
+ if (song.image) {
213
+ if (Array.isArray(song.image)) {
214
+ song.image.forEach((img) => {
215
+ let src = img.link || img.url || (typeof img === 'string' ? img : '');
216
+ if (src) {
217
+ if (src.startsWith('http://')) {
218
+ src = src.replace('http://', 'https://');
219
+ }
220
+ artwork.push({ src, sizes: '500x500', type: 'image/jpeg' });
221
+ }
222
+ });
223
+ }
224
+ else if (typeof song.image === 'string') {
225
+ let src = song.image;
226
+ if (src.startsWith('http://')) {
227
+ src = src.replace('http://', 'https://');
228
+ }
229
+ artwork.push({ src: src, sizes: '500x500', type: 'image/jpeg' });
230
+ }
231
+ }
290
232
  navigator.mediaSession.metadata = new MediaMetadata({
291
233
  title: song.name || song.title || 'Unknown Title',
292
234
  artist: song.primaryArtists || song.artist || 'Unknown Artist',
293
- album: ((_a = song.album) === null || _a === void 0 ? void 0 : _a.name) || song.album || '',
294
- artwork: [{ src: 'https://via.placeholder.com/500', sizes: '500x500', type: 'image/png' }] // Placeholder
235
+ album: ((_a = song.album) === null || _a === void 0 ? void 0 : _a.name) || song.album || 'Unknown Album',
236
+ artwork: artwork.length > 0 ? artwork : undefined
237
+ });
238
+ }
239
+ }
240
+ static updatePositionState() {
241
+ if ('mediaSession' in navigator && this.duration > 0) {
242
+ navigator.mediaSession.setPositionState({
243
+ duration: this.duration,
244
+ playbackRate: this.audio.playbackRate,
245
+ position: this.audio.currentTime
295
246
  });
296
- navigator.mediaSession.setActionHandler('play', () => this.resume());
297
- navigator.mediaSession.setActionHandler('pause', () => this.pause());
298
- navigator.mediaSession.setActionHandler('previoustrack', () => this.prev());
299
- navigator.mediaSession.setActionHandler('nexttrack', () => this.next());
300
247
  }
301
248
  }
302
249
  }
303
250
  exports.Player = Player;
251
+ Player.audio = new Audio();
304
252
  Player.currentSong = null;
305
253
  Player.currentIndex = 0;
306
254
  Player.isPlaying = false;
@@ -311,5 +259,3 @@ Player.queue = [];
311
259
  Player.queue$ = new rxjs_1.BehaviorSubject([]);
312
260
  Player.playlist = [];
313
261
  Player.selectedQuality = 3;
314
- Player.isNative = core_1.Capacitor.isNativePlatform();
315
- Player.webAudio = new Audio(); // Fallback for web
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tunzo-player",
3
- "version": "1.0.28",
3
+ "version": "1.0.29",
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",
@@ -30,7 +30,6 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@capacitor-community/keep-awake": "^7.1.0",
33
- "@capgo/native-audio": "^7.9.9",
34
33
  "rxjs": "^7.8.2"
35
34
  }
36
35
  }
@@ -1,9 +1,8 @@
1
1
  import { BehaviorSubject } from 'rxjs';
2
- import { NativeAudio } from '@capgo/native-audio';
3
2
  import { KeepAwake } from '@capacitor-community/keep-awake';
4
- import { Capacitor } from '@capacitor/core';
5
3
 
6
4
  export class Player {
5
+ private static audio = new Audio();
7
6
  private static currentSong: any = null;
8
7
  private static currentIndex = 0;
9
8
  private static isPlaying = false;
@@ -14,41 +13,72 @@ export class Player {
14
13
  static queue$ = new BehaviorSubject<any[]>([]);
15
14
  private static playlist: any[] = [];
16
15
  private static selectedQuality = 3;
17
- private static isNative = Capacitor.isNativePlatform();
18
- private static webAudio = new Audio(); // Fallback for web
19
16
 
20
17
  /** Initialize with playlist and quality */
21
- static async initialize(playlist: any[], quality = 3) {
18
+ static initialize(playlist: any[], quality = 3) {
22
19
  this.playlist = playlist;
23
20
  this.selectedQuality = quality;
21
+ this.setupMediaSession();
22
+ this.setupAudioElement();
23
+ }
24
+
25
+ /** Setup audio element for better compatibility */
26
+ private static setupAudioElement() {
27
+ // Enable background playback
28
+ this.audio.preload = 'auto';
29
+ // @ts-ignore - Some browsers support this
30
+ this.audio.preservesPitch = false;
31
+
32
+ // Setup event listeners
33
+ this.audio.onloadedmetadata = () => {
34
+ this.duration = this.audio.duration;
35
+ this.updatePositionState();
36
+ };
24
37
 
25
- // Configure native audio if on native platform
26
- if (this.isNative) {
27
- try {
28
- await NativeAudio.configure({
29
- showNotification: true,
30
- background: true,
31
- focus: true
32
- });
33
- } catch (e) {
34
- console.warn('NativeAudio configure failed:', e);
38
+ this.audio.ontimeupdate = () => {
39
+ this.currentTime = this.audio.currentTime;
40
+ if (Math.floor(this.currentTime) % 5 === 0) {
41
+ this.updatePositionState();
35
42
  }
36
- this.setupNativeListeners();
37
- } else {
38
- this.setupWebListeners();
39
- }
43
+ };
44
+
45
+ this.audio.onended = () => {
46
+ this.autoNext();
47
+ };
48
+
49
+ this.audio.onplaying = () => {
50
+ this.isPlaying = true;
51
+ if ('mediaSession' in navigator) {
52
+ navigator.mediaSession.playbackState = 'playing';
53
+ }
54
+ };
55
+
56
+ this.audio.onpause = () => {
57
+ this.isPlaying = false;
58
+ if ('mediaSession' in navigator) {
59
+ navigator.mediaSession.playbackState = 'paused';
60
+ }
61
+ };
62
+
63
+ this.audio.onwaiting = () => {
64
+ if ('mediaSession' in navigator) {
65
+ navigator.mediaSession.playbackState = 'none';
66
+ }
67
+ };
68
+
69
+ this.audio.onerror = (e) => {
70
+ console.error('Audio error:', this.audio.error, e);
71
+ };
40
72
  }
41
73
 
42
- /** Call this once on user gesture to unlock audio in WebView (Web only) */
74
+ /** Call this once on user gesture to unlock audio in WebView */
43
75
  static unlockAudio() {
44
- if (!this.isNative) {
45
- this.webAudio.src = '';
46
- this.webAudio.load();
47
- this.webAudio.play().catch(() => { });
48
- }
76
+ this.audio.src = '';
77
+ this.audio.load();
78
+ this.audio.play().catch(() => { });
49
79
  }
50
80
 
51
- static async play(song: any, index: number = 0) {
81
+ static play(song: any, index: number = 0) {
52
82
  if (!song || !song.downloadUrl) return;
53
83
 
54
84
  this.currentSong = song;
@@ -61,94 +91,38 @@ export class Player {
61
91
  url = url.replace('http://', 'https://');
62
92
  }
63
93
 
64
- try {
65
- if (this.isNative) {
66
- await this.playNative(url, song);
67
- } else {
68
- this.playWeb(url, song);
69
- }
94
+ this.audio.src = url;
95
+ this.audio.load();
96
+
97
+ this.audio.play().then(() => {
70
98
  this.isPlaying = true;
71
- KeepAwake.keepAwake(); // Keep CPU awake for streaming
72
- } catch (err) {
99
+ this.updateMediaSessionMetadata(song);
100
+ KeepAwake.keepAwake(); // Keep screen/CPU awake
101
+ if ('mediaSession' in navigator) {
102
+ navigator.mediaSession.playbackState = 'playing';
103
+ }
104
+ }).catch((err) => {
73
105
  this.isPlaying = false;
74
106
  console.warn('Audio play failed:', err);
75
- }
76
- }
77
-
78
- private static async playNative(url: string, song: any) {
79
- try {
80
- // Unload previous if any (though preload might handle it, safer to be clean)
81
- // NativeAudio.unload({ assetId: 'currentSong' }).catch(() => {});
82
-
83
- // Prepare artwork for lock screen
84
- let artworkPath = '';
85
- if (song.image) {
86
- if (Array.isArray(song.image)) {
87
- // Get highest quality image
88
- const img = song.image[song.image.length - 1];
89
- artworkPath = img.link || img.url || (typeof img === 'string' ? img : '');
90
- } else if (typeof song.image === 'string') {
91
- artworkPath = song.image;
92
- }
93
- }
94
- if (artworkPath.startsWith('http://')) {
95
- artworkPath = artworkPath.replace('http://', 'https://');
96
- }
97
-
98
- await NativeAudio.preload({
99
- assetId: 'currentSong',
100
- assetPath: url,
101
- isUrl: true,
102
- audioChannelNum: 1,
103
- // Metadata for Lock Screen
104
- notificationMetadata: {
105
- album: song.album?.name || song.album || 'Unknown Album',
106
- artist: song.primaryArtists || song.artist || 'Unknown Artist',
107
- title: song.name || song.title || 'Unknown Title',
108
- artworkUrl: artworkPath
109
- }
110
- });
111
-
112
- await NativeAudio.play({ assetId: 'currentSong' });
113
-
114
- } catch (e) {
115
- console.error("Native play error", e);
116
- throw e;
117
- }
118
- }
119
-
120
- private static playWeb(url: string, song: any) {
121
- this.webAudio.src = url;
122
- // @ts-ignore
123
- this.webAudio.title = song.name || song.title || 'Unknown Title';
124
- this.webAudio.preload = 'auto';
125
- this.webAudio.load();
126
- this.webAudio.play();
127
-
128
- // Basic MediaSession for Web
129
- if ('mediaSession' in navigator) {
130
- this.updateWebMediaSession(song);
131
- }
107
+ });
132
108
  }
133
109
 
134
110
  static pause() {
135
- if (this.isNative) {
136
- NativeAudio.pause({ assetId: 'currentSong' });
137
- } else {
138
- this.webAudio.pause();
139
- }
111
+ this.audio.pause();
140
112
  this.isPlaying = false;
141
113
  KeepAwake.allowSleep();
114
+ if ('mediaSession' in navigator) {
115
+ navigator.mediaSession.playbackState = 'paused';
116
+ }
142
117
  }
143
118
 
144
119
  static resume() {
145
- if (this.isNative) {
146
- NativeAudio.resume({ assetId: 'currentSong' });
147
- } else {
148
- this.webAudio.play();
149
- }
120
+ this.audio.play();
150
121
  this.isPlaying = true;
151
122
  KeepAwake.keepAwake();
123
+ if ('mediaSession' in navigator) {
124
+ navigator.mediaSession.playbackState = 'playing';
125
+ }
152
126
  }
153
127
 
154
128
  static togglePlayPause() {
@@ -179,12 +153,8 @@ export class Player {
179
153
  }
180
154
 
181
155
  static seek(seconds: number) {
182
- if (this.isNative) {
183
- NativeAudio.setCurrentTime({ assetId: 'currentSong', time: seconds });
184
- } else {
185
- this.webAudio.currentTime = seconds;
186
- }
187
- this.currentTime = seconds;
156
+ this.audio.currentTime = seconds;
157
+ this.updatePositionState();
188
158
  }
189
159
 
190
160
  static autoNext() {
@@ -261,61 +231,62 @@ export class Player {
261
231
  }
262
232
 
263
233
  // -------------------------------------------------------------------------
264
- // Listeners
234
+ // Native Media Session (Lock Screen Controls)
265
235
  // -------------------------------------------------------------------------
266
236
 
267
- private static setupNativeListeners() {
268
- // Song Finished
269
- NativeAudio.addListener('complete', (result) => {
270
- if (result.assetId === 'currentSong') {
271
- this.autoNext();
272
- }
273
- });
274
-
275
- // Time Update (Progress) - Note: Plugin might not emit this frequently
276
- // We might need to poll for current time if the plugin doesn't emit 'progress'
277
- // @capgo/native-audio usually emits 'progress' or we use getCurrentTime
278
- // Checking docs: usually we poll or listen to 'progress'
279
- // Assuming 'progress' event exists or we use setInterval
280
- setInterval(async () => {
281
- if (this.isPlaying && this.isNative) {
282
- try {
283
- const result = await NativeAudio.getCurrentTime({ assetId: 'currentSong' });
284
- this.currentTime = result.currentTime;
285
-
286
- const durResult = await NativeAudio.getDuration({ assetId: 'currentSong' });
287
- this.duration = durResult.duration;
288
- } catch (e) { }
289
- }
290
- }, 1000);
291
- }
292
-
293
- private static setupWebListeners() {
294
- this.webAudio.onended = () => this.autoNext();
295
- this.webAudio.ontimeupdate = () => {
296
- this.currentTime = this.webAudio.currentTime;
297
- this.duration = this.webAudio.duration || 0;
298
- };
299
- this.webAudio.onplaying = () => this.isPlaying = true;
300
- this.webAudio.onpause = () => this.isPlaying = false;
237
+ private static setupMediaSession() {
238
+ if ('mediaSession' in navigator) {
239
+ navigator.mediaSession.setActionHandler('play', () => this.resume());
240
+ navigator.mediaSession.setActionHandler('pause', () => this.pause());
241
+ navigator.mediaSession.setActionHandler('previoustrack', () => this.prev());
242
+ navigator.mediaSession.setActionHandler('nexttrack', () => this.next());
243
+ navigator.mediaSession.setActionHandler('seekto', (details) => {
244
+ if (details.seekTime !== undefined) {
245
+ this.seek(details.seekTime);
246
+ }
247
+ });
248
+ }
301
249
  }
302
250
 
303
- private static updateWebMediaSession(song: any) {
304
- // ... (Keep existing Web MediaSession logic if needed, or simplify)
305
- // Since we are focusing on Native, we can keep the basic one or copy the previous logic
306
- // For brevity, I'll omit the full implementation here as Native is the priority
307
- // But to be safe, let's keep a minimal version
251
+ private static updateMediaSessionMetadata(song: any) {
308
252
  if ('mediaSession' in navigator) {
253
+ const artwork = [];
254
+ if (song.image) {
255
+ if (Array.isArray(song.image)) {
256
+ song.image.forEach((img: any) => {
257
+ let src = img.link || img.url || (typeof img === 'string' ? img : '');
258
+ if (src) {
259
+ if (src.startsWith('http://')) {
260
+ src = src.replace('http://', 'https://');
261
+ }
262
+ artwork.push({ src, sizes: '500x500', type: 'image/jpeg' });
263
+ }
264
+ });
265
+ } else if (typeof song.image === 'string') {
266
+ let src = song.image;
267
+ if (src.startsWith('http://')) {
268
+ src = src.replace('http://', 'https://');
269
+ }
270
+ artwork.push({ src: src, sizes: '500x500', type: 'image/jpeg' });
271
+ }
272
+ }
273
+
309
274
  navigator.mediaSession.metadata = new MediaMetadata({
310
275
  title: song.name || song.title || 'Unknown Title',
311
276
  artist: song.primaryArtists || song.artist || 'Unknown Artist',
312
- album: song.album?.name || song.album || '',
313
- artwork: [{ src: 'https://via.placeholder.com/500', sizes: '500x500', type: 'image/png' }] // Placeholder
277
+ album: song.album?.name || song.album || 'Unknown Album',
278
+ artwork: artwork.length > 0 ? artwork : undefined
279
+ });
280
+ }
281
+ }
282
+
283
+ private static updatePositionState() {
284
+ if ('mediaSession' in navigator && this.duration > 0) {
285
+ navigator.mediaSession.setPositionState({
286
+ duration: this.duration,
287
+ playbackRate: this.audio.playbackRate,
288
+ position: this.audio.currentTime
314
289
  });
315
- navigator.mediaSession.setActionHandler('play', () => this.resume());
316
- navigator.mediaSession.setActionHandler('pause', () => this.pause());
317
- navigator.mediaSession.setActionHandler('previoustrack', () => this.prev());
318
- navigator.mediaSession.setActionHandler('nexttrack', () => this.next());
319
290
  }
320
291
  }
321
292
  }