tunzo-player 1.0.8 → 1.0.9
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 +14 -16
- package/dist/core/player.js +81 -163
- package/package.json +29 -27
- package/src/core/player.ts +153 -203
- package/tsconfig.json +1 -2
package/dist/core/player.d.ts
CHANGED
|
@@ -9,31 +9,29 @@ export declare class Player {
|
|
|
9
9
|
private static queue;
|
|
10
10
|
private static playlist;
|
|
11
11
|
private static selectedQuality;
|
|
12
|
-
|
|
13
|
-
static initialize(playlist
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
static
|
|
17
|
-
static
|
|
18
|
-
static
|
|
19
|
-
static togglePlayPause():
|
|
20
|
-
static next():
|
|
21
|
-
static prev():
|
|
22
|
-
static
|
|
23
|
-
static
|
|
12
|
+
/** Initialize with playlist and quality */
|
|
13
|
+
static initialize(playlist: any[], quality?: number): void;
|
|
14
|
+
/** Call this once on user gesture to unlock audio in WebView */
|
|
15
|
+
static unlockAudio(): void;
|
|
16
|
+
static play(song: any, index?: number): void;
|
|
17
|
+
static pause(): void;
|
|
18
|
+
static resume(): void;
|
|
19
|
+
static togglePlayPause(): void;
|
|
20
|
+
static next(): void;
|
|
21
|
+
static prev(): void;
|
|
22
|
+
static seek(seconds: number): void;
|
|
23
|
+
static autoNext(): void;
|
|
24
|
+
static playRandom(): void;
|
|
24
25
|
static toggleShuffle(): void;
|
|
25
26
|
static addToQueue(song: any): void;
|
|
26
27
|
static removeFromQueue(index: number): void;
|
|
27
28
|
static reorderQueue(from: number, to: number): void;
|
|
28
|
-
static seekTo(seconds: number): Promise<void>;
|
|
29
29
|
static getCurrentTime(): number;
|
|
30
30
|
static getDuration(): number;
|
|
31
|
-
static formatTime(
|
|
31
|
+
static formatTime(time: number): string;
|
|
32
32
|
static isPlayingSong(): boolean;
|
|
33
33
|
static getCurrentSong(): any;
|
|
34
34
|
static setQuality(index: number): void;
|
|
35
35
|
static getQueue(): any[];
|
|
36
36
|
static getPlaylist(): any[];
|
|
37
|
-
static getShuffleStatus(): boolean;
|
|
38
|
-
static destroy(): Promise<void>;
|
|
39
37
|
}
|
package/dist/core/player.js
CHANGED
|
@@ -1,189 +1,120 @@
|
|
|
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
|
-
const core_1 = require("@capacitor/core");
|
|
14
|
-
const capacitor_music_controls_plugin_1 = require("capacitor-music-controls-plugin");
|
|
15
4
|
class Player {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
this.playlist = [...playlist];
|
|
5
|
+
/** Initialize with playlist and quality */
|
|
6
|
+
static initialize(playlist, quality = 3) {
|
|
7
|
+
this.playlist = playlist;
|
|
21
8
|
this.selectedQuality = quality;
|
|
22
|
-
this.isInitialized = true;
|
|
23
|
-
}
|
|
24
|
-
static play(song_1) {
|
|
25
|
-
return __awaiter(this, arguments, void 0, function* (song, index = 0) {
|
|
26
|
-
var _a, _b;
|
|
27
|
-
if (!this.isInitialized)
|
|
28
|
-
return;
|
|
29
|
-
if (!(song === null || song === void 0 ? void 0 : song.downloadUrl))
|
|
30
|
-
return;
|
|
31
|
-
yield this.stop();
|
|
32
|
-
this.currentSong = song;
|
|
33
|
-
this.currentIndex = index;
|
|
34
|
-
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);
|
|
35
|
-
if (!url)
|
|
36
|
-
return;
|
|
37
|
-
if (!this.audio)
|
|
38
|
-
this.audio = new Audio();
|
|
39
|
-
this.audio.src = url;
|
|
40
|
-
this.audio.play().catch(console.error);
|
|
41
|
-
this.audio.onloadedmetadata = () => this.duration = this.audio.duration;
|
|
42
|
-
this.audio.ontimeupdate = () => this.currentTime = this.audio.currentTime;
|
|
43
|
-
this.audio.onended = () => this.autoNext();
|
|
44
|
-
yield this.setupMusicControls(song, true);
|
|
45
|
-
this.isPlaying = true;
|
|
46
|
-
});
|
|
47
9
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
yield capacitor_music_controls_plugin_1.CapacitorMusicControls.create({
|
|
54
|
-
track: song.name || '',
|
|
55
|
-
artist: song.primaryArtists || '',
|
|
56
|
-
album: song.album || '',
|
|
57
|
-
cover: song.image || '',
|
|
58
|
-
isPlaying,
|
|
59
|
-
dismissable: true,
|
|
60
|
-
hasPrev: true,
|
|
61
|
-
hasNext: true,
|
|
62
|
-
});
|
|
63
|
-
capacitor_music_controls_plugin_1.CapacitorMusicControls.addListener('music-controls-next', () => this.next());
|
|
64
|
-
capacitor_music_controls_plugin_1.CapacitorMusicControls.addListener('music-controls-previous', () => this.prev());
|
|
65
|
-
capacitor_music_controls_plugin_1.CapacitorMusicControls.addListener('music-controls-pause', () => this.pause());
|
|
66
|
-
capacitor_music_controls_plugin_1.CapacitorMusicControls.addListener('music-controls-play', () => this.resume());
|
|
67
|
-
capacitor_music_controls_plugin_1.CapacitorMusicControls.addListener('music-controls-destroy', () => this.stop());
|
|
68
|
-
});
|
|
10
|
+
/** Call this once on user gesture to unlock audio in WebView */
|
|
11
|
+
static unlockAudio() {
|
|
12
|
+
this.audio.src = '';
|
|
13
|
+
this.audio.load();
|
|
14
|
+
this.audio.play().catch(() => { });
|
|
69
15
|
}
|
|
70
|
-
static
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
16
|
+
static play(song, index = 0) {
|
|
17
|
+
var _a;
|
|
18
|
+
if (!song || !song.downloadUrl)
|
|
19
|
+
return;
|
|
20
|
+
this.currentSong = song;
|
|
21
|
+
this.currentIndex = index;
|
|
22
|
+
this.audio.src = ((_a = song.downloadUrl[this.selectedQuality]) === null || _a === void 0 ? void 0 : _a.url) || '';
|
|
23
|
+
this.audio.load(); // Ensure audio is loaded before play
|
|
24
|
+
this.audio.play().then(() => {
|
|
25
|
+
this.isPlaying = true;
|
|
26
|
+
}).catch((err) => {
|
|
27
|
+
// Handle play errors (autoplay, WebView restrictions)
|
|
74
28
|
this.isPlaying = false;
|
|
75
|
-
|
|
29
|
+
console.warn('Audio play failed:', err);
|
|
76
30
|
});
|
|
31
|
+
// Set duration
|
|
32
|
+
this.audio.onloadedmetadata = () => {
|
|
33
|
+
this.duration = this.audio.duration;
|
|
34
|
+
};
|
|
35
|
+
// Set current time
|
|
36
|
+
this.audio.ontimeupdate = () => {
|
|
37
|
+
this.currentTime = this.audio.currentTime;
|
|
38
|
+
};
|
|
39
|
+
// Auto-play next song
|
|
40
|
+
this.audio.onended = () => {
|
|
41
|
+
this.autoNext();
|
|
42
|
+
};
|
|
77
43
|
}
|
|
78
|
-
static
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
yield this.audio.play();
|
|
82
|
-
this.isPlaying = true;
|
|
83
|
-
core_1.Capacitor.isNativePlatform() && capacitor_music_controls_plugin_1.CapacitorMusicControls.updateIsPlaying({ isPlaying: true });
|
|
84
|
-
});
|
|
44
|
+
static pause() {
|
|
45
|
+
this.audio.pause();
|
|
46
|
+
this.isPlaying = false;
|
|
85
47
|
}
|
|
86
|
-
static
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
this.audio.pause();
|
|
90
|
-
this.audio.src = '';
|
|
91
|
-
}
|
|
92
|
-
this.isPlaying = false;
|
|
93
|
-
yield capacitor_music_controls_plugin_1.CapacitorMusicControls.destroy();
|
|
94
|
-
});
|
|
48
|
+
static resume() {
|
|
49
|
+
this.audio.play();
|
|
50
|
+
this.isPlaying = true;
|
|
95
51
|
}
|
|
96
52
|
static togglePlayPause() {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
else if (this.playlist.length > 0) {
|
|
105
|
-
yield this.play(this.playlist[0], 0);
|
|
106
|
-
}
|
|
107
|
-
});
|
|
53
|
+
if (this.isPlaying) {
|
|
54
|
+
this.pause();
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
this.resume();
|
|
58
|
+
}
|
|
108
59
|
}
|
|
109
60
|
static next() {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
});
|
|
61
|
+
if (this.queue.length > 0) {
|
|
62
|
+
const nextQueued = this.queue.shift();
|
|
63
|
+
const index = this.playlist.findIndex(s => s.id === nextQueued.id);
|
|
64
|
+
this.play(nextQueued, index);
|
|
65
|
+
}
|
|
66
|
+
else if (this.isShuffle) {
|
|
67
|
+
this.playRandom();
|
|
68
|
+
}
|
|
69
|
+
else if (this.currentIndex < this.playlist.length - 1) {
|
|
70
|
+
this.play(this.playlist[this.currentIndex + 1], this.currentIndex + 1);
|
|
71
|
+
}
|
|
123
72
|
}
|
|
124
73
|
static prev() {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
132
|
-
});
|
|
74
|
+
if (this.currentIndex > 0) {
|
|
75
|
+
this.play(this.playlist[this.currentIndex - 1], this.currentIndex - 1);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
static seek(seconds) {
|
|
79
|
+
this.audio.currentTime = seconds;
|
|
133
80
|
}
|
|
134
81
|
static autoNext() {
|
|
135
|
-
|
|
136
|
-
yield this.next();
|
|
137
|
-
});
|
|
82
|
+
this.next();
|
|
138
83
|
}
|
|
139
84
|
static playRandom() {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
yield this.play(this.playlist[rand], rand);
|
|
148
|
-
});
|
|
85
|
+
if (this.playlist.length <= 1)
|
|
86
|
+
return;
|
|
87
|
+
let randomIndex;
|
|
88
|
+
do {
|
|
89
|
+
randomIndex = Math.floor(Math.random() * this.playlist.length);
|
|
90
|
+
} while (randomIndex === this.currentIndex);
|
|
91
|
+
this.play(this.playlist[randomIndex], randomIndex);
|
|
149
92
|
}
|
|
150
93
|
static toggleShuffle() {
|
|
151
94
|
this.isShuffle = !this.isShuffle;
|
|
152
95
|
}
|
|
153
96
|
static addToQueue(song) {
|
|
154
|
-
if (!this.queue.
|
|
97
|
+
if (!this.queue.some(q => q.id === song.id)) {
|
|
155
98
|
this.queue.push(song);
|
|
156
99
|
}
|
|
157
100
|
}
|
|
158
101
|
static removeFromQueue(index) {
|
|
159
|
-
|
|
160
|
-
this.queue.splice(index, 1);
|
|
161
|
-
}
|
|
102
|
+
this.queue.splice(index, 1);
|
|
162
103
|
}
|
|
163
104
|
static reorderQueue(from, to) {
|
|
164
|
-
|
|
165
|
-
return;
|
|
166
|
-
const [item] = this.queue.splice(from, 1);
|
|
105
|
+
const item = this.queue.splice(from, 1)[0];
|
|
167
106
|
this.queue.splice(to, 0, item);
|
|
168
107
|
}
|
|
169
|
-
static seekTo(seconds) {
|
|
170
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
171
|
-
if (this.audio) {
|
|
172
|
-
this.audio.currentTime = seconds;
|
|
173
|
-
this.currentTime = seconds;
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
108
|
static getCurrentTime() {
|
|
178
109
|
return this.currentTime;
|
|
179
110
|
}
|
|
180
111
|
static getDuration() {
|
|
181
112
|
return this.duration;
|
|
182
113
|
}
|
|
183
|
-
static formatTime(
|
|
184
|
-
const
|
|
185
|
-
const
|
|
186
|
-
return `${
|
|
114
|
+
static formatTime(time) {
|
|
115
|
+
const minutes = Math.floor(time / 60);
|
|
116
|
+
const seconds = Math.floor(time % 60);
|
|
117
|
+
return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`;
|
|
187
118
|
}
|
|
188
119
|
static isPlayingSong() {
|
|
189
120
|
return this.isPlaying;
|
|
@@ -195,33 +126,20 @@ class Player {
|
|
|
195
126
|
this.selectedQuality = index;
|
|
196
127
|
}
|
|
197
128
|
static getQueue() {
|
|
198
|
-
return
|
|
129
|
+
return this.queue;
|
|
199
130
|
}
|
|
200
131
|
static getPlaylist() {
|
|
201
|
-
return
|
|
202
|
-
}
|
|
203
|
-
static getShuffleStatus() {
|
|
204
|
-
return this.isShuffle;
|
|
205
|
-
}
|
|
206
|
-
static destroy() {
|
|
207
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
208
|
-
yield this.stop();
|
|
209
|
-
this.playlist = [];
|
|
210
|
-
this.queue = [];
|
|
211
|
-
this.audio = null;
|
|
212
|
-
this.isInitialized = false;
|
|
213
|
-
});
|
|
132
|
+
return this.playlist;
|
|
214
133
|
}
|
|
215
134
|
}
|
|
216
135
|
exports.Player = Player;
|
|
217
|
-
Player.audio =
|
|
136
|
+
Player.audio = new Audio();
|
|
218
137
|
Player.currentSong = null;
|
|
219
138
|
Player.currentIndex = 0;
|
|
220
139
|
Player.isPlaying = false;
|
|
221
140
|
Player.currentTime = 0;
|
|
222
141
|
Player.duration = 0;
|
|
223
|
-
Player.isShuffle =
|
|
142
|
+
Player.isShuffle = true;
|
|
224
143
|
Player.queue = [];
|
|
225
144
|
Player.playlist = [];
|
|
226
145
|
Player.selectedQuality = 3;
|
|
227
|
-
Player.isInitialized = false;
|
package/package.json
CHANGED
|
@@ -1,29 +1,31 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
|
|
2
|
+
"name": "tunzo-player",
|
|
3
|
+
"version": "1.0.9",
|
|
4
|
+
"description": "A music playback service for Angular and Ionic apps with native audio control support.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"publish:patch": "npm version patch && npm run build && npm publish",
|
|
10
|
+
"publish:minor": "npm version minor && npm run build && npm publish",
|
|
11
|
+
"publish:major": "npm version major && npm run build && npm publish",
|
|
12
|
+
"publish": "npm run build && npm publish"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"music",
|
|
16
|
+
"player",
|
|
17
|
+
"angular",
|
|
18
|
+
"ionic",
|
|
19
|
+
"audio",
|
|
20
|
+
"native",
|
|
21
|
+
"playback",
|
|
22
|
+
"service",
|
|
23
|
+
"tunzo",
|
|
24
|
+
"tunzo-player"
|
|
25
|
+
],
|
|
26
|
+
"author": "Kulasekaran",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
}
|
|
29
31
|
}
|
package/src/core/player.ts
CHANGED
|
@@ -1,214 +1,164 @@
|
|
|
1
|
-
import { Capacitor } from '@capacitor/core';
|
|
2
|
-
import { CapacitorMusicControls } from 'capacitor-music-controls-plugin';
|
|
3
|
-
|
|
4
1
|
export class Player {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
this.
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
this
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
static async play(song: any, index = 0) {
|
|
27
|
-
if (!this.isInitialized) return;
|
|
28
|
-
if (!song?.downloadUrl) return;
|
|
29
|
-
|
|
30
|
-
await this.stop();
|
|
31
|
-
|
|
32
|
-
this.currentSong = song;
|
|
33
|
-
this.currentIndex = index;
|
|
34
|
-
const url = song.downloadUrl[this.selectedQuality]?.url || song.downloadUrl[0]?.url;
|
|
35
|
-
|
|
36
|
-
if (!url) return;
|
|
37
|
-
|
|
38
|
-
if (!this.audio) this.audio = new Audio();
|
|
39
|
-
|
|
40
|
-
this.audio.src = url;
|
|
41
|
-
this.audio.play().catch(console.error);
|
|
42
|
-
|
|
43
|
-
this.audio.onloadedmetadata = () => this.duration = this.audio!.duration;
|
|
44
|
-
this.audio.ontimeupdate = () => this.currentTime = this.audio!.currentTime;
|
|
45
|
-
this.audio.onended = () => this.autoNext();
|
|
46
|
-
|
|
47
|
-
await this.setupMusicControls(song, true);
|
|
48
|
-
|
|
49
|
-
this.isPlaying = true;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
private static async setupMusicControls(song: any, isPlaying: boolean) {
|
|
53
|
-
if (!Capacitor.isNativePlatform()) return;
|
|
54
|
-
|
|
55
|
-
await CapacitorMusicControls.destroy(); // clear existing
|
|
56
|
-
await CapacitorMusicControls.create({
|
|
57
|
-
track: song.name || '',
|
|
58
|
-
artist: song.primaryArtists || '',
|
|
59
|
-
album: song.album || '',
|
|
60
|
-
cover: song.image || '',
|
|
61
|
-
isPlaying,
|
|
62
|
-
dismissable: true,
|
|
63
|
-
hasPrev: true,
|
|
64
|
-
hasNext: true,
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
CapacitorMusicControls.addListener('music-controls-next', () => this.next());
|
|
68
|
-
CapacitorMusicControls.addListener('music-controls-previous', () => this.prev());
|
|
69
|
-
CapacitorMusicControls.addListener('music-controls-pause', () => this.pause());
|
|
70
|
-
CapacitorMusicControls.addListener('music-controls-play', () => this.resume());
|
|
71
|
-
CapacitorMusicControls.addListener('music-controls-destroy', () => this.stop());
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
static async pause() {
|
|
75
|
-
if (this.audio) this.audio.pause();
|
|
76
|
-
this.isPlaying = false;
|
|
77
|
-
Capacitor.isNativePlatform() && CapacitorMusicControls.updateIsPlaying({ isPlaying: false });
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
static async resume() {
|
|
82
|
-
if (this.audio) await this.audio.play();
|
|
83
|
-
this.isPlaying = true;
|
|
84
|
-
Capacitor.isNativePlatform() && CapacitorMusicControls.updateIsPlaying({ isPlaying: true });
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
static async stop() {
|
|
89
|
-
if (this.audio) {
|
|
90
|
-
this.audio.pause();
|
|
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
|
+
/** Call this once on user gesture to unlock audio in WebView */
|
|
20
|
+
static unlockAudio() {
|
|
91
21
|
this.audio.src = '';
|
|
22
|
+
this.audio.load();
|
|
23
|
+
this.audio.play().catch(() => {});
|
|
92
24
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
25
|
+
|
|
26
|
+
static play(song: any, index: number = 0) {
|
|
27
|
+
if (!song || !song.downloadUrl) return;
|
|
28
|
+
|
|
29
|
+
this.currentSong = song;
|
|
30
|
+
this.currentIndex = index;
|
|
31
|
+
this.audio.src = song.downloadUrl[this.selectedQuality]?.url || '';
|
|
32
|
+
this.audio.load(); // Ensure audio is loaded before play
|
|
33
|
+
this.audio.play().then(() => {
|
|
34
|
+
this.isPlaying = true;
|
|
35
|
+
}).catch((err) => {
|
|
36
|
+
// Handle play errors (autoplay, WebView restrictions)
|
|
37
|
+
this.isPlaying = false;
|
|
38
|
+
console.warn('Audio play failed:', err);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Set duration
|
|
42
|
+
this.audio.onloadedmetadata = () => {
|
|
43
|
+
this.duration = this.audio.duration;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Set current time
|
|
47
|
+
this.audio.ontimeupdate = () => {
|
|
48
|
+
this.currentTime = this.audio.currentTime;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// Auto-play next song
|
|
52
|
+
this.audio.onended = () => {
|
|
53
|
+
this.autoNext();
|
|
54
|
+
};
|
|
104
55
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const next = this.queue.shift();
|
|
110
|
-
const index = this.playlist.findIndex(s => s.id === next.id);
|
|
111
|
-
await this.play(next, index);
|
|
112
|
-
} else if (this.isShuffle) {
|
|
113
|
-
await this.playRandom();
|
|
114
|
-
} else if (this.currentIndex < this.playlist.length - 1) {
|
|
115
|
-
await this.play(this.playlist[this.currentIndex + 1], this.currentIndex + 1);
|
|
56
|
+
|
|
57
|
+
static pause() {
|
|
58
|
+
this.audio.pause();
|
|
59
|
+
this.isPlaying = false;
|
|
116
60
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
await this.seekTo(0);
|
|
122
|
-
} else if (this.currentIndex > 0) {
|
|
123
|
-
await this.play(this.playlist[this.currentIndex - 1], this.currentIndex - 1);
|
|
61
|
+
|
|
62
|
+
static resume() {
|
|
63
|
+
this.audio.play();
|
|
64
|
+
this.isPlaying = true;
|
|
124
65
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if (this.playlist.length <= 1) return;
|
|
133
|
-
let rand = this.currentIndex;
|
|
134
|
-
while (rand === this.currentIndex) {
|
|
135
|
-
rand = Math.floor(Math.random() * this.playlist.length);
|
|
136
|
-
}
|
|
137
|
-
await this.play(this.playlist[rand], rand);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
static toggleShuffle() {
|
|
141
|
-
this.isShuffle = !this.isShuffle;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
static addToQueue(song: any) {
|
|
145
|
-
if (!this.queue.find(s => s.id === song.id)) {
|
|
146
|
-
this.queue.push(song);
|
|
66
|
+
|
|
67
|
+
static togglePlayPause() {
|
|
68
|
+
if (this.isPlaying) {
|
|
69
|
+
this.pause();
|
|
70
|
+
} else {
|
|
71
|
+
this.resume();
|
|
72
|
+
}
|
|
147
73
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
74
|
+
|
|
75
|
+
static next() {
|
|
76
|
+
if (this.queue.length > 0) {
|
|
77
|
+
const nextQueued = this.queue.shift();
|
|
78
|
+
const index = this.playlist.findIndex(s => s.id === nextQueued.id);
|
|
79
|
+
this.play(nextQueued, index);
|
|
80
|
+
} else if (this.isShuffle) {
|
|
81
|
+
this.playRandom();
|
|
82
|
+
} else if (this.currentIndex < this.playlist.length - 1) {
|
|
83
|
+
this.play(this.playlist[this.currentIndex + 1], this.currentIndex + 1);
|
|
84
|
+
}
|
|
153
85
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
static async seekTo(seconds: number) {
|
|
163
|
-
if (this.audio) {
|
|
86
|
+
|
|
87
|
+
static prev() {
|
|
88
|
+
if (this.currentIndex > 0) {
|
|
89
|
+
this.play(this.playlist[this.currentIndex - 1], this.currentIndex - 1);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
static seek(seconds: number) {
|
|
164
94
|
this.audio.currentTime = seconds;
|
|
165
|
-
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
static autoNext() {
|
|
98
|
+
this.next();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
static playRandom() {
|
|
102
|
+
if (this.playlist.length <= 1) return;
|
|
103
|
+
|
|
104
|
+
let randomIndex;
|
|
105
|
+
do {
|
|
106
|
+
randomIndex = Math.floor(Math.random() * this.playlist.length);
|
|
107
|
+
} while (randomIndex === this.currentIndex);
|
|
108
|
+
|
|
109
|
+
this.play(this.playlist[randomIndex], randomIndex);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
static toggleShuffle() {
|
|
113
|
+
this.isShuffle = !this.isShuffle;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
static addToQueue(song: any) {
|
|
117
|
+
if (!this.queue.some(q => q.id === song.id)) {
|
|
118
|
+
this.queue.push(song);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
static removeFromQueue(index: number) {
|
|
123
|
+
this.queue.splice(index, 1);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
static reorderQueue(from: number, to: number) {
|
|
127
|
+
const item = this.queue.splice(from, 1)[0];
|
|
128
|
+
this.queue.splice(to, 0, item);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
static getCurrentTime(): number {
|
|
132
|
+
return this.currentTime;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
static getDuration(): number {
|
|
136
|
+
return this.duration;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
static formatTime(time: number): string {
|
|
140
|
+
const minutes = Math.floor(time / 60);
|
|
141
|
+
const seconds = Math.floor(time % 60);
|
|
142
|
+
return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
static isPlayingSong(): boolean {
|
|
146
|
+
return this.isPlaying;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
static getCurrentSong(): any {
|
|
150
|
+
return this.currentSong;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
static setQuality(index: number) {
|
|
154
|
+
this.selectedQuality = index;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
static getQueue(): any[] {
|
|
158
|
+
return this.queue;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
static getPlaylist(): any[] {
|
|
162
|
+
return this.playlist;
|
|
166
163
|
}
|
|
167
164
|
}
|
|
168
|
-
|
|
169
|
-
static getCurrentTime(): number {
|
|
170
|
-
return this.currentTime;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
static getDuration(): number {
|
|
174
|
-
return this.duration;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
static formatTime(t: number): string {
|
|
178
|
-
const m = Math.floor(t / 60);
|
|
179
|
-
const s = Math.floor(t % 60);
|
|
180
|
-
return `${m}:${s < 10 ? '0' : ''}${s}`;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
static isPlayingSong(): boolean {
|
|
184
|
-
return this.isPlaying;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
static getCurrentSong(): any {
|
|
188
|
-
return this.currentSong;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
static setQuality(index: number) {
|
|
192
|
-
this.selectedQuality = index;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
static getQueue(): any[] {
|
|
196
|
-
return [...this.queue];
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
static getPlaylist(): any[] {
|
|
200
|
-
return [...this.playlist];
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
static getShuffleStatus(): boolean {
|
|
204
|
-
return this.isShuffle;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
static async destroy() {
|
|
208
|
-
await this.stop();
|
|
209
|
-
this.playlist = [];
|
|
210
|
-
this.queue = [];
|
|
211
|
-
this.audio = null;
|
|
212
|
-
this.isInitialized = false;
|
|
213
|
-
}
|
|
214
|
-
}
|
package/tsconfig.json
CHANGED
|
@@ -14,8 +14,7 @@
|
|
|
14
14
|
"esModuleInterop": true, // allow default imports from CommonJS
|
|
15
15
|
"forceConsistentCasingInFileNames": true,
|
|
16
16
|
"strict": true,
|
|
17
|
-
"skipLibCheck": true,
|
|
18
|
-
"types": ["@capacitor-community/native-audio"] // speed up builds, safe for libs
|
|
17
|
+
"skipLibCheck": true // speed up builds, safe for libs
|
|
19
18
|
},
|
|
20
19
|
"include": ["src"] // only compile the src folder
|
|
21
20
|
}
|