tunzo-player 1.0.6 → 1.0.7

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
@@ -76,3 +76,4 @@ yarn add tunzo-player
76
76
  | 3 | High (160kbps) |
77
77
  | 4 | Ultra (320kbps) |
78
78
 
79
+ # tunzo-player
@@ -1,5 +1,5 @@
1
1
  export declare class Player {
2
- private static audio;
2
+ private static audioInstance;
3
3
  private static currentSong;
4
4
  private static currentIndex;
5
5
  private static isPlaying;
@@ -9,21 +9,24 @@ export declare class Player {
9
9
  private static queue;
10
10
  private static playlist;
11
11
  private static selectedQuality;
12
+ private static readonly audioId;
13
+ private static isInitialized;
12
14
  /** Initialize with playlist and quality */
13
- static initialize(playlist: any[], quality?: number): void;
14
- static play(song: any, index?: number): void;
15
- static pause(): void;
16
- static resume(): void;
17
- static togglePlayPause(): void;
18
- static next(): void;
19
- static prev(): void;
20
- static seek(seconds: number): void;
21
- static autoNext(): void;
22
- static playRandom(): void;
15
+ static initialize(playlist?: any[], quality?: number): void;
16
+ static play(song: any, index?: number): Promise<void>;
17
+ private static cleanupCurrentPlayback;
18
+ static pause(): Promise<void>;
19
+ static resume(): Promise<void>;
20
+ static togglePlayPause(): Promise<void>;
21
+ static next(): Promise<void>;
22
+ static prev(): Promise<void>;
23
+ static autoNext(): Promise<void>;
24
+ static playRandom(): Promise<void>;
23
25
  static toggleShuffle(): void;
24
26
  static addToQueue(song: any): void;
25
27
  static removeFromQueue(index: number): void;
26
28
  static reorderQueue(from: number, to: number): void;
29
+ static seekTo(time: number): Promise<void>;
27
30
  static getCurrentTime(): number;
28
31
  static getDuration(): number;
29
32
  static formatTime(time: number): string;
@@ -32,4 +35,6 @@ export declare class Player {
32
35
  static setQuality(index: number): void;
33
36
  static getQueue(): any[];
34
37
  static getPlaylist(): any[];
38
+ static getShuffleStatus(): boolean;
39
+ static destroy(): Promise<void>;
35
40
  }
@@ -1,82 +1,218 @@
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
+ };
2
11
  Object.defineProperty(exports, "__esModule", { value: true });
3
12
  exports.Player = void 0;
13
+ const core_1 = require("@capacitor/core");
14
+ const native_audio_1 = require("@capacitor-community/native-audio");
4
15
  class Player {
5
16
  /** Initialize with playlist and quality */
6
- static initialize(playlist, quality = 3) {
7
- this.playlist = playlist;
17
+ static initialize(playlist = [], quality = 3) {
18
+ if (!core_1.Capacitor.isNativePlatform() && !this.audioInstance) {
19
+ this.audioInstance = new Audio();
20
+ }
21
+ this.playlist = [...playlist];
8
22
  this.selectedQuality = quality;
23
+ this.isInitialized = true;
24
+ }
25
+ static play(song_1) {
26
+ return __awaiter(this, arguments, void 0, function* (song, index = 0) {
27
+ var _a, _b;
28
+ if (!this.isInitialized) {
29
+ console.error('Player not initialized. Call Player.initialize() first.');
30
+ return;
31
+ }
32
+ if (!song || !song.downloadUrl) {
33
+ console.error('Invalid song or missing downloadUrl');
34
+ return;
35
+ }
36
+ // Stop current playback
37
+ yield this.cleanupCurrentPlayback();
38
+ this.currentSong = song;
39
+ this.currentIndex = index;
40
+ const url = ((_a = song.downloadUrl[this.selectedQuality]) === null || _a === void 0 ? void 0 : _a.url) || ((_b = song.downloadUrl[0]) === null || _b === void 0 ? void 0 : _b.url);
41
+ if (!url) {
42
+ console.error('No valid URL found for playback');
43
+ return;
44
+ }
45
+ try {
46
+ if (core_1.Capacitor.isNativePlatform()) {
47
+ // For native platform, use the correct NativeAudio methods
48
+ yield native_audio_1.NativeAudio.configure({
49
+ fade: false,
50
+ focus: true
51
+ });
52
+ yield native_audio_1.NativeAudio.preload({
53
+ assetId: this.audioId,
54
+ assetPath: url,
55
+ audioChannelNum: 1,
56
+ isUrl: true
57
+ });
58
+ yield native_audio_1.NativeAudio.play({
59
+ assetId: this.audioId
60
+ });
61
+ // For native, we need to manually track duration
62
+ this.duration = song.duration || 0;
63
+ }
64
+ else {
65
+ if (!this.audioInstance) {
66
+ this.audioInstance = new Audio();
67
+ }
68
+ this.audioInstance.src = url;
69
+ yield this.audioInstance.play();
70
+ this.audioInstance.onloadedmetadata = () => {
71
+ var _a;
72
+ this.duration = ((_a = this.audioInstance) === null || _a === void 0 ? void 0 : _a.duration) || 0;
73
+ };
74
+ this.audioInstance.ontimeupdate = () => {
75
+ var _a;
76
+ this.currentTime = ((_a = this.audioInstance) === null || _a === void 0 ? void 0 : _a.currentTime) || 0;
77
+ };
78
+ this.audioInstance.onended = () => {
79
+ this.autoNext();
80
+ };
81
+ this.audioInstance.onerror = () => {
82
+ console.error('Error during playback');
83
+ this.isPlaying = false;
84
+ };
85
+ }
86
+ this.isPlaying = true;
87
+ }
88
+ catch (err) {
89
+ console.error('Playback error:', err);
90
+ this.isPlaying = false;
91
+ }
92
+ });
9
93
  }
10
- static play(song, index = 0) {
11
- var _a;
12
- if (!song || !song.downloadUrl)
13
- return;
14
- this.currentSong = song;
15
- this.currentIndex = index;
16
- this.audio.src = ((_a = song.downloadUrl[this.selectedQuality]) === null || _a === void 0 ? void 0 : _a.url) || '';
17
- this.audio.play();
18
- this.isPlaying = true;
19
- // Set duration
20
- this.audio.onloadedmetadata = () => {
21
- this.duration = this.audio.duration;
22
- };
23
- // Set current time
24
- this.audio.ontimeupdate = () => {
25
- this.currentTime = this.audio.currentTime;
26
- };
27
- // Auto-play next song
28
- this.audio.onended = () => {
29
- this.autoNext();
30
- };
94
+ static cleanupCurrentPlayback() {
95
+ return __awaiter(this, void 0, void 0, function* () {
96
+ if (core_1.Capacitor.isNativePlatform()) {
97
+ try {
98
+ yield native_audio_1.NativeAudio.stop({ assetId: this.audioId });
99
+ yield native_audio_1.NativeAudio.unload({ assetId: this.audioId });
100
+ }
101
+ catch (err) {
102
+ console.debug('No audio to unload');
103
+ }
104
+ }
105
+ else if (this.audioInstance) {
106
+ this.audioInstance.pause();
107
+ this.audioInstance.src = '';
108
+ this.audioInstance.onloadedmetadata = null;
109
+ this.audioInstance.ontimeupdate = null;
110
+ this.audioInstance.onended = null;
111
+ this.audioInstance.onerror = null;
112
+ }
113
+ });
31
114
  }
32
115
  static pause() {
33
- this.audio.pause();
34
- this.isPlaying = false;
116
+ return __awaiter(this, void 0, void 0, function* () {
117
+ if (!this.isInitialized)
118
+ return;
119
+ try {
120
+ if (core_1.Capacitor.isNativePlatform()) {
121
+ yield native_audio_1.NativeAudio.pause({ assetId: this.audioId });
122
+ }
123
+ else if (this.audioInstance) {
124
+ this.audioInstance.pause();
125
+ }
126
+ this.isPlaying = false;
127
+ }
128
+ catch (err) {
129
+ console.error('Pause error:', err);
130
+ }
131
+ });
35
132
  }
36
133
  static resume() {
37
- this.audio.play();
38
- this.isPlaying = true;
134
+ return __awaiter(this, void 0, void 0, function* () {
135
+ if (!this.isInitialized)
136
+ return;
137
+ try {
138
+ if (core_1.Capacitor.isNativePlatform()) {
139
+ yield native_audio_1.NativeAudio.play({ assetId: this.audioId });
140
+ }
141
+ else if (this.audioInstance) {
142
+ yield this.audioInstance.play();
143
+ }
144
+ this.isPlaying = true;
145
+ }
146
+ catch (err) {
147
+ console.error('Resume error:', err);
148
+ }
149
+ });
39
150
  }
40
151
  static togglePlayPause() {
41
- if (this.isPlaying) {
42
- this.pause();
43
- }
44
- else {
45
- this.resume();
46
- }
152
+ return __awaiter(this, void 0, void 0, function* () {
153
+ if (!this.isInitialized)
154
+ return;
155
+ if (this.isPlaying) {
156
+ yield this.pause();
157
+ }
158
+ else if (this.currentSong) {
159
+ yield this.resume();
160
+ }
161
+ else if (this.playlist.length > 0) {
162
+ yield this.play(this.playlist[0], 0);
163
+ }
164
+ });
47
165
  }
48
166
  static next() {
49
- if (this.queue.length > 0) {
50
- const nextQueued = this.queue.shift();
51
- const index = this.playlist.findIndex(s => s.id === nextQueued.id);
52
- this.play(nextQueued, index);
53
- }
54
- else if (this.isShuffle) {
55
- this.playRandom();
56
- }
57
- else if (this.currentIndex < this.playlist.length - 1) {
58
- this.play(this.playlist[this.currentIndex + 1], this.currentIndex + 1);
59
- }
167
+ return __awaiter(this, void 0, void 0, function* () {
168
+ if (!this.isInitialized)
169
+ return;
170
+ if (this.queue.length > 0) {
171
+ const nextQueued = this.queue.shift();
172
+ const index = this.playlist.findIndex(s => s.id === nextQueued.id);
173
+ yield this.play(nextQueued, index);
174
+ }
175
+ else if (this.isShuffle) {
176
+ yield this.playRandom();
177
+ }
178
+ else if (this.currentIndex < this.playlist.length - 1) {
179
+ yield this.play(this.playlist[this.currentIndex + 1], this.currentIndex + 1);
180
+ }
181
+ else {
182
+ // End of playlist
183
+ this.isPlaying = false;
184
+ this.currentSong = null;
185
+ }
186
+ });
60
187
  }
61
188
  static prev() {
62
- if (this.currentIndex > 0) {
63
- this.play(this.playlist[this.currentIndex - 1], this.currentIndex - 1);
64
- }
65
- }
66
- static seek(seconds) {
67
- this.audio.currentTime = seconds;
189
+ return __awaiter(this, void 0, void 0, function* () {
190
+ if (!this.isInitialized)
191
+ return;
192
+ if (this.currentTime > 3) {
193
+ // If more than 3 seconds into song, restart current song
194
+ yield this.seekTo(0);
195
+ }
196
+ else if (this.currentIndex > 0) {
197
+ yield this.play(this.playlist[this.currentIndex - 1], this.currentIndex - 1);
198
+ }
199
+ });
68
200
  }
69
201
  static autoNext() {
70
- this.next();
202
+ return __awaiter(this, void 0, void 0, function* () {
203
+ yield this.next();
204
+ });
71
205
  }
72
206
  static playRandom() {
73
- if (this.playlist.length <= 1)
74
- return;
75
- let randomIndex;
76
- do {
77
- randomIndex = Math.floor(Math.random() * this.playlist.length);
78
- } while (randomIndex === this.currentIndex);
79
- this.play(this.playlist[randomIndex], randomIndex);
207
+ return __awaiter(this, void 0, void 0, function* () {
208
+ if (!this.isInitialized || this.playlist.length <= 1)
209
+ return;
210
+ let randomIndex;
211
+ do {
212
+ randomIndex = Math.floor(Math.random() * this.playlist.length);
213
+ } while (randomIndex === this.currentIndex && this.playlist.length > 1);
214
+ yield this.play(this.playlist[randomIndex], randomIndex);
215
+ });
80
216
  }
81
217
  static toggleShuffle() {
82
218
  this.isShuffle = !this.isShuffle;
@@ -87,11 +223,34 @@ class Player {
87
223
  }
88
224
  }
89
225
  static removeFromQueue(index) {
90
- this.queue.splice(index, 1);
226
+ if (index >= 0 && index < this.queue.length) {
227
+ this.queue.splice(index, 1);
228
+ }
91
229
  }
92
230
  static reorderQueue(from, to) {
93
- const item = this.queue.splice(from, 1)[0];
94
- this.queue.splice(to, 0, item);
231
+ if (from >= 0 && from < this.queue.length && to >= 0 && to < this.queue.length) {
232
+ const item = this.queue.splice(from, 1)[0];
233
+ this.queue.splice(to, 0, item);
234
+ }
235
+ }
236
+ static seekTo(time) {
237
+ return __awaiter(this, void 0, void 0, function* () {
238
+ if (!this.isInitialized)
239
+ return;
240
+ try {
241
+ if (core_1.Capacitor.isNativePlatform()) {
242
+ // NativeAudio doesn't support seeking directly, might need a plugin extension
243
+ console.warn('Seeking not fully supported on native platform');
244
+ }
245
+ else if (this.audioInstance) {
246
+ this.audioInstance.currentTime = time;
247
+ }
248
+ this.currentTime = time;
249
+ }
250
+ catch (err) {
251
+ console.error('Seek error:', err);
252
+ }
253
+ });
95
254
  }
96
255
  static getCurrentTime() {
97
256
  return this.currentTime;
@@ -111,23 +270,36 @@ class Player {
111
270
  return this.currentSong;
112
271
  }
113
272
  static setQuality(index) {
114
- this.selectedQuality = index;
273
+ if (index >= 0) {
274
+ this.selectedQuality = index;
275
+ }
115
276
  }
116
277
  static getQueue() {
117
- return this.queue;
278
+ return [...this.queue];
118
279
  }
119
280
  static getPlaylist() {
120
- return this.playlist;
281
+ return [...this.playlist];
282
+ }
283
+ static getShuffleStatus() {
284
+ return this.isShuffle;
285
+ }
286
+ static destroy() {
287
+ return __awaiter(this, void 0, void 0, function* () {
288
+ yield this.cleanupCurrentPlayback();
289
+ this.isInitialized = false;
290
+ });
121
291
  }
122
292
  }
123
293
  exports.Player = Player;
124
- Player.audio = new Audio();
294
+ Player.audioInstance = null;
125
295
  Player.currentSong = null;
126
296
  Player.currentIndex = 0;
127
297
  Player.isPlaying = false;
128
298
  Player.currentTime = 0;
129
299
  Player.duration = 0;
130
- Player.isShuffle = true;
300
+ Player.isShuffle = false;
131
301
  Player.queue = [];
132
302
  Player.playlist = [];
133
303
  Player.selectedQuality = 3;
304
+ Player.audioId = 'current-song';
305
+ Player.isInitialized = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tunzo-player",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
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",
@@ -18,5 +18,11 @@
18
18
  "license": "MIT",
19
19
  "publishConfig": {
20
20
  "access": "public"
21
+ },
22
+ "devDependencies": {
23
+ "typescript": "^5.8.3"
24
+ },
25
+ "dependencies": {
26
+ "@capacitor-community/native-audio": "^7.0.0"
21
27
  }
22
28
  }
@@ -1,152 +1,294 @@
1
+
2
+ import { Capacitor } from '@capacitor/core';
3
+ import { NativeAudio } from '@capacitor-community/native-audio';
4
+
1
5
  export class Player {
2
- private static audio = new Audio();
3
- private static currentSong: any = null;
4
- private static currentIndex = 0;
5
- private static isPlaying = false;
6
- private static currentTime = 0;
7
- private static duration = 0;
8
- private static isShuffle = true;
9
- private static queue: any[] = [];
10
- private static playlist: any[] = [];
11
- private static selectedQuality = 3;
12
-
13
- /** Initialize with playlist and quality */
14
- static initialize(playlist: any[], quality = 3) {
15
- this.playlist = playlist;
16
- this.selectedQuality = quality;
17
- }
18
-
19
- static play(song: any, index: number = 0) {
20
- if (!song || !song.downloadUrl) return;
21
-
22
- this.currentSong = song;
23
- this.currentIndex = index;
24
- this.audio.src = song.downloadUrl[this.selectedQuality]?.url || '';
25
- this.audio.play();
26
- this.isPlaying = true;
27
-
28
- // Set duration
29
- this.audio.onloadedmetadata = () => {
30
- this.duration = this.audio.duration;
31
- };
32
-
33
- // Set current time
34
- this.audio.ontimeupdate = () => {
35
- this.currentTime = this.audio.currentTime;
36
- };
37
-
38
- // Auto-play next song
39
- this.audio.onended = () => {
40
- this.autoNext();
41
- };
42
- }
43
-
44
- static pause() {
45
- this.audio.pause();
46
- this.isPlaying = false;
6
+ private static audioInstance: HTMLAudioElement | null = null;
7
+ private static currentSong: any = null;
8
+ private static currentIndex = 0;
9
+ private static isPlaying = false;
10
+ private static currentTime = 0;
11
+ private static duration = 0;
12
+ private static isShuffle = false;
13
+ private static queue: any[] = [];
14
+ private static playlist: any[] = [];
15
+ private static selectedQuality = 3;
16
+ private static readonly audioId: string = 'current-song';
17
+ private static isInitialized = false;
18
+
19
+ /** Initialize with playlist and quality */
20
+ static initialize(playlist: any[] = [], quality = 3) {
21
+ if (!Capacitor.isNativePlatform() && !this.audioInstance) {
22
+ this.audioInstance = new Audio();
47
23
  }
48
-
49
- static resume() {
50
- this.audio.play();
51
- this.isPlaying = true;
24
+
25
+ this.playlist = [...playlist];
26
+ this.selectedQuality = quality;
27
+ this.isInitialized = true;
28
+ }
29
+
30
+ static async play(song: any, index: number = 0) {
31
+ if (!this.isInitialized) {
32
+ console.error('Player not initialized. Call Player.initialize() first.');
33
+ return;
34
+ }
35
+
36
+ if (!song || !song.downloadUrl) {
37
+ console.error('Invalid song or missing downloadUrl');
38
+ return;
39
+ }
40
+
41
+ // Stop current playback
42
+ await this.cleanupCurrentPlayback();
43
+
44
+ this.currentSong = song;
45
+ this.currentIndex = index;
46
+ const url = song.downloadUrl[this.selectedQuality]?.url || song.downloadUrl[0]?.url;
47
+
48
+ if (!url) {
49
+ console.error('No valid URL found for playback');
50
+ return;
52
51
  }
53
-
54
- static togglePlayPause() {
55
- if (this.isPlaying) {
56
- this.pause();
52
+
53
+ try {
54
+ if (Capacitor.isNativePlatform()) {
55
+ // For native platform, use the correct NativeAudio methods
56
+ await NativeAudio.configure({
57
+ fade: false,
58
+ focus: true
59
+ });
60
+
61
+ await NativeAudio.preload({
62
+ assetId: this.audioId,
63
+ assetPath: url,
64
+ audioChannelNum: 1,
65
+ isUrl: true
66
+ });
67
+
68
+ await NativeAudio.play({
69
+ assetId: this.audioId
70
+ });
71
+
72
+ // For native, we need to manually track duration
73
+ this.duration = song.duration || 0;
57
74
  } else {
58
- this.resume();
75
+ if (!this.audioInstance) {
76
+ this.audioInstance = new Audio();
77
+ }
78
+
79
+ this.audioInstance.src = url;
80
+ await this.audioInstance.play();
81
+
82
+ this.audioInstance.onloadedmetadata = () => {
83
+ this.duration = this.audioInstance?.duration || 0;
84
+ };
85
+
86
+ this.audioInstance.ontimeupdate = () => {
87
+ this.currentTime = this.audioInstance?.currentTime || 0;
88
+ };
89
+
90
+ this.audioInstance.onended = () => {
91
+ this.autoNext();
92
+ };
93
+
94
+ this.audioInstance.onerror = () => {
95
+ console.error('Error during playback');
96
+ this.isPlaying = false;
97
+ };
59
98
  }
99
+
100
+ this.isPlaying = true;
101
+ } catch (err) {
102
+ console.error('Playback error:', err);
103
+ this.isPlaying = false;
60
104
  }
61
-
62
- static next() {
63
- if (this.queue.length > 0) {
64
- const nextQueued = this.queue.shift();
65
- const index = this.playlist.findIndex(s => s.id === nextQueued.id);
66
- this.play(nextQueued, index);
67
- } else if (this.isShuffle) {
68
- this.playRandom();
69
- } else if (this.currentIndex < this.playlist.length - 1) {
70
- this.play(this.playlist[this.currentIndex + 1], this.currentIndex + 1);
105
+ }
106
+
107
+ private static async cleanupCurrentPlayback() {
108
+ if (Capacitor.isNativePlatform()) {
109
+ try {
110
+ await NativeAudio.stop({ assetId: this.audioId });
111
+ await NativeAudio.unload({ assetId: this.audioId });
112
+ } catch (err) {
113
+ console.debug('No audio to unload');
71
114
  }
115
+ } else if (this.audioInstance) {
116
+ this.audioInstance.pause();
117
+ this.audioInstance.src = '';
118
+ this.audioInstance.onloadedmetadata = null;
119
+ this.audioInstance.ontimeupdate = null;
120
+ this.audioInstance.onended = null;
121
+ this.audioInstance.onerror = null;
72
122
  }
73
-
74
- static prev() {
75
- if (this.currentIndex > 0) {
76
- this.play(this.playlist[this.currentIndex - 1], this.currentIndex - 1);
123
+ }
124
+
125
+ static async pause() {
126
+ if (!this.isInitialized) return;
127
+
128
+ try {
129
+ if (Capacitor.isNativePlatform()) {
130
+ await NativeAudio.pause({ assetId: this.audioId });
131
+ } else if (this.audioInstance) {
132
+ this.audioInstance.pause();
77
133
  }
134
+ this.isPlaying = false;
135
+ } catch (err) {
136
+ console.error('Pause error:', err);
78
137
  }
79
-
80
- static seek(seconds: number) {
81
- this.audio.currentTime = seconds;
82
- }
83
-
84
- static autoNext() {
85
- this.next();
86
- }
87
-
88
- static playRandom() {
89
- if (this.playlist.length <= 1) return;
90
-
91
- let randomIndex;
92
- do {
93
- randomIndex = Math.floor(Math.random() * this.playlist.length);
94
- } while (randomIndex === this.currentIndex);
95
-
96
- this.play(this.playlist[randomIndex], randomIndex);
97
- }
98
-
99
- static toggleShuffle() {
100
- this.isShuffle = !this.isShuffle;
101
- }
102
-
103
- static addToQueue(song: any) {
104
- if (!this.queue.some(q => q.id === song.id)) {
105
- this.queue.push(song);
138
+ }
139
+
140
+ static async resume() {
141
+ if (!this.isInitialized) return;
142
+
143
+ try {
144
+ if (Capacitor.isNativePlatform()) {
145
+ await NativeAudio.play({ assetId: this.audioId });
146
+ } else if (this.audioInstance) {
147
+ await this.audioInstance.play();
106
148
  }
149
+ this.isPlaying = true;
150
+ } catch (err) {
151
+ console.error('Resume error:', err);
107
152
  }
108
-
109
- static removeFromQueue(index: number) {
110
- this.queue.splice(index, 1);
153
+ }
154
+
155
+ static async togglePlayPause() {
156
+ if (!this.isInitialized) return;
157
+
158
+ if (this.isPlaying) {
159
+ await this.pause();
160
+ } else if (this.currentSong) {
161
+ await this.resume();
162
+ } else if (this.playlist.length > 0) {
163
+ await this.play(this.playlist[0], 0);
111
164
  }
112
-
113
- static reorderQueue(from: number, to: number) {
114
- const item = this.queue.splice(from, 1)[0];
115
- this.queue.splice(to, 0, item);
165
+ }
166
+
167
+ static async next() {
168
+ if (!this.isInitialized) return;
169
+
170
+ if (this.queue.length > 0) {
171
+ const nextQueued = this.queue.shift();
172
+ const index = this.playlist.findIndex(s => s.id === nextQueued.id);
173
+ await this.play(nextQueued, index);
174
+ } else if (this.isShuffle) {
175
+ await this.playRandom();
176
+ } else if (this.currentIndex < this.playlist.length - 1) {
177
+ await this.play(this.playlist[this.currentIndex + 1], this.currentIndex + 1);
178
+ } else {
179
+ // End of playlist
180
+ this.isPlaying = false;
181
+ this.currentSong = null;
116
182
  }
117
-
118
- static getCurrentTime(): number {
119
- return this.currentTime;
183
+ }
184
+
185
+ static async prev() {
186
+ if (!this.isInitialized) return;
187
+
188
+ if (this.currentTime > 3) {
189
+ // If more than 3 seconds into song, restart current song
190
+ await this.seekTo(0);
191
+ } else if (this.currentIndex > 0) {
192
+ await this.play(this.playlist[this.currentIndex - 1], this.currentIndex - 1);
120
193
  }
121
-
122
- static getDuration(): number {
123
- return this.duration;
194
+ }
195
+
196
+ static async autoNext() {
197
+ await this.next();
198
+ }
199
+
200
+ static async playRandom() {
201
+ if (!this.isInitialized || this.playlist.length <= 1) return;
202
+
203
+ let randomIndex;
204
+ do {
205
+ randomIndex = Math.floor(Math.random() * this.playlist.length);
206
+ } while (randomIndex === this.currentIndex && this.playlist.length > 1);
207
+
208
+ await this.play(this.playlist[randomIndex], randomIndex);
209
+ }
210
+
211
+ static toggleShuffle() {
212
+ this.isShuffle = !this.isShuffle;
213
+ }
214
+
215
+ static addToQueue(song: any) {
216
+ if (!this.queue.some(q => q.id === song.id)) {
217
+ this.queue.push(song);
124
218
  }
125
-
126
- static formatTime(time: number): string {
127
- const minutes = Math.floor(time / 60);
128
- const seconds = Math.floor(time % 60);
129
- return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`;
219
+ }
220
+
221
+ static removeFromQueue(index: number) {
222
+ if (index >= 0 && index < this.queue.length) {
223
+ this.queue.splice(index, 1);
130
224
  }
131
-
132
- static isPlayingSong(): boolean {
133
- return this.isPlaying;
225
+ }
226
+
227
+ static reorderQueue(from: number, to: number) {
228
+ if (from >= 0 && from < this.queue.length && to >= 0 && to < this.queue.length) {
229
+ const item = this.queue.splice(from, 1)[0];
230
+ this.queue.splice(to, 0, item);
134
231
  }
135
-
136
- static getCurrentSong(): any {
137
- return this.currentSong;
232
+ }
233
+
234
+ static async seekTo(time: number) {
235
+ if (!this.isInitialized) return;
236
+
237
+ try {
238
+ if (Capacitor.isNativePlatform()) {
239
+ // NativeAudio doesn't support seeking directly, might need a plugin extension
240
+ console.warn('Seeking not fully supported on native platform');
241
+ } else if (this.audioInstance) {
242
+ this.audioInstance.currentTime = time;
243
+ }
244
+ this.currentTime = time;
245
+ } catch (err) {
246
+ console.error('Seek error:', err);
138
247
  }
139
-
140
- static setQuality(index: number) {
248
+ }
249
+
250
+ static getCurrentTime(): number {
251
+ return this.currentTime;
252
+ }
253
+
254
+ static getDuration(): number {
255
+ return this.duration;
256
+ }
257
+
258
+ static formatTime(time: number): string {
259
+ const minutes = Math.floor(time / 60);
260
+ const seconds = Math.floor(time % 60);
261
+ return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`;
262
+ }
263
+
264
+ static isPlayingSong(): boolean {
265
+ return this.isPlaying;
266
+ }
267
+
268
+ static getCurrentSong(): any {
269
+ return this.currentSong;
270
+ }
271
+
272
+ static setQuality(index: number) {
273
+ if (index >= 0) {
141
274
  this.selectedQuality = index;
142
275
  }
143
-
144
- static getQueue(): any[] {
145
- return this.queue;
146
- }
147
-
148
- static getPlaylist(): any[] {
149
- return this.playlist;
150
- }
151
276
  }
152
-
277
+
278
+ static getQueue(): any[] {
279
+ return [...this.queue];
280
+ }
281
+
282
+ static getPlaylist(): any[] {
283
+ return [...this.playlist];
284
+ }
285
+
286
+ static getShuffleStatus(): boolean {
287
+ return this.isShuffle;
288
+ }
289
+
290
+ static async destroy() {
291
+ await this.cleanupCurrentPlayback();
292
+ this.isInitialized = false;
293
+ }
294
+ }
package/tsconfig.json CHANGED
@@ -14,7 +14,8 @@
14
14
  "esModuleInterop": true, // allow default imports from CommonJS
15
15
  "forceConsistentCasingInFileNames": true,
16
16
  "strict": true,
17
- "skipLibCheck": true // speed up builds, safe for libs
17
+ "skipLibCheck": true,
18
+ "types": ["@capacitor-community/native-audio"] // speed up builds, safe for libs
18
19
  },
19
20
  "include": ["src"] // only compile the src folder
20
21
  }