tunzo-player 1.0.8 → 1.0.11
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 +17 -16
- package/dist/core/player.js +89 -163
- package/package.json +32 -27
- package/src/core/player.ts +159 -199
- package/tsconfig.json +1 -2
package/dist/core/player.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { BehaviorSubject } from 'rxjs';
|
|
1
2
|
export declare class Player {
|
|
2
3
|
private static audio;
|
|
3
4
|
private static currentSong;
|
|
@@ -7,33 +8,33 @@ export declare class Player {
|
|
|
7
8
|
private static duration;
|
|
8
9
|
private static isShuffle;
|
|
9
10
|
private static queue;
|
|
11
|
+
static queue$: BehaviorSubject<any[]>;
|
|
10
12
|
private static playlist;
|
|
11
13
|
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
|
|
14
|
+
/** Initialize with playlist and quality */
|
|
15
|
+
static initialize(playlist: any[], quality?: number): void;
|
|
16
|
+
/** Call this once on user gesture to unlock audio in WebView */
|
|
17
|
+
static unlockAudio(): void;
|
|
18
|
+
static play(song: any, index?: number): void;
|
|
19
|
+
static pause(): void;
|
|
20
|
+
static resume(): void;
|
|
21
|
+
static togglePlayPause(): void;
|
|
22
|
+
static next(): void;
|
|
23
|
+
static prev(): void;
|
|
24
|
+
static seek(seconds: number): void;
|
|
25
|
+
static autoNext(): void;
|
|
26
|
+
static playRandom(): void;
|
|
24
27
|
static toggleShuffle(): void;
|
|
28
|
+
static getShuffleStatus(): boolean;
|
|
25
29
|
static addToQueue(song: any): void;
|
|
26
30
|
static removeFromQueue(index: number): void;
|
|
27
31
|
static reorderQueue(from: number, to: number): void;
|
|
28
|
-
static seekTo(seconds: number): Promise<void>;
|
|
29
32
|
static getCurrentTime(): number;
|
|
30
33
|
static getDuration(): number;
|
|
31
|
-
static formatTime(
|
|
34
|
+
static formatTime(time: number): string;
|
|
32
35
|
static isPlayingSong(): boolean;
|
|
33
36
|
static getCurrentSong(): any;
|
|
34
37
|
static setQuality(index: number): void;
|
|
35
38
|
static getQueue(): any[];
|
|
36
39
|
static getPlaylist(): any[];
|
|
37
|
-
static getShuffleStatus(): boolean;
|
|
38
|
-
static destroy(): Promise<void>;
|
|
39
40
|
}
|
package/dist/core/player.js
CHANGED
|
@@ -1,178 +1,116 @@
|
|
|
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
|
|
14
|
-
const capacitor_music_controls_plugin_1 = require("capacitor-music-controls-plugin");
|
|
4
|
+
const rxjs_1 = require("rxjs");
|
|
15
5
|
class Player {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
this.playlist = [...playlist];
|
|
6
|
+
/** Initialize with playlist and quality */
|
|
7
|
+
static initialize(playlist, quality = 3) {
|
|
8
|
+
this.playlist = playlist;
|
|
21
9
|
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
10
|
}
|
|
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
|
-
});
|
|
11
|
+
/** Call this once on user gesture to unlock audio in WebView */
|
|
12
|
+
static unlockAudio() {
|
|
13
|
+
this.audio.src = '';
|
|
14
|
+
this.audio.load();
|
|
15
|
+
this.audio.play().catch(() => { });
|
|
69
16
|
}
|
|
70
|
-
static
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
17
|
+
static play(song, index = 0) {
|
|
18
|
+
var _a;
|
|
19
|
+
if (!song || !song.downloadUrl)
|
|
20
|
+
return;
|
|
21
|
+
this.currentSong = song;
|
|
22
|
+
this.currentIndex = index;
|
|
23
|
+
this.audio.src = ((_a = song.downloadUrl[this.selectedQuality]) === null || _a === void 0 ? void 0 : _a.url) || '';
|
|
24
|
+
this.audio.load(); // Ensure audio is loaded before play
|
|
25
|
+
this.audio.play().then(() => {
|
|
26
|
+
this.isPlaying = true;
|
|
27
|
+
}).catch((err) => {
|
|
28
|
+
// Handle play errors (autoplay, WebView restrictions)
|
|
74
29
|
this.isPlaying = false;
|
|
75
|
-
|
|
30
|
+
console.warn('Audio play failed:', err);
|
|
76
31
|
});
|
|
32
|
+
// Set duration
|
|
33
|
+
this.audio.onloadedmetadata = () => {
|
|
34
|
+
this.duration = this.audio.duration;
|
|
35
|
+
};
|
|
36
|
+
// Set current time
|
|
37
|
+
this.audio.ontimeupdate = () => {
|
|
38
|
+
this.currentTime = this.audio.currentTime;
|
|
39
|
+
};
|
|
40
|
+
// Auto-play next song
|
|
41
|
+
this.audio.onended = () => {
|
|
42
|
+
this.autoNext();
|
|
43
|
+
};
|
|
77
44
|
}
|
|
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
|
-
});
|
|
45
|
+
static pause() {
|
|
46
|
+
this.audio.pause();
|
|
47
|
+
this.isPlaying = false;
|
|
85
48
|
}
|
|
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
|
-
});
|
|
49
|
+
static resume() {
|
|
50
|
+
this.audio.play();
|
|
51
|
+
this.isPlaying = true;
|
|
95
52
|
}
|
|
96
53
|
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
|
-
});
|
|
54
|
+
if (this.isPlaying) {
|
|
55
|
+
this.pause();
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
this.resume();
|
|
59
|
+
}
|
|
108
60
|
}
|
|
109
61
|
static next() {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
});
|
|
62
|
+
if (this.queue.length > 0) {
|
|
63
|
+
const nextQueued = this.queue.shift();
|
|
64
|
+
const index = this.playlist.findIndex(s => s.id === nextQueued.id);
|
|
65
|
+
this.play(nextQueued, index);
|
|
66
|
+
}
|
|
67
|
+
else if (this.isShuffle) {
|
|
68
|
+
this.playRandom();
|
|
69
|
+
}
|
|
70
|
+
else if (this.currentIndex < this.playlist.length - 1) {
|
|
71
|
+
this.play(this.playlist[this.currentIndex + 1], this.currentIndex + 1);
|
|
72
|
+
}
|
|
123
73
|
}
|
|
124
74
|
static prev() {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
132
|
-
});
|
|
75
|
+
if (this.currentIndex > 0) {
|
|
76
|
+
this.play(this.playlist[this.currentIndex - 1], this.currentIndex - 1);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
static seek(seconds) {
|
|
80
|
+
this.audio.currentTime = seconds;
|
|
133
81
|
}
|
|
134
82
|
static autoNext() {
|
|
135
|
-
|
|
136
|
-
yield this.next();
|
|
137
|
-
});
|
|
83
|
+
this.next();
|
|
138
84
|
}
|
|
139
85
|
static playRandom() {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
yield this.play(this.playlist[rand], rand);
|
|
148
|
-
});
|
|
86
|
+
if (this.playlist.length <= 1)
|
|
87
|
+
return;
|
|
88
|
+
let randomIndex;
|
|
89
|
+
do {
|
|
90
|
+
randomIndex = Math.floor(Math.random() * this.playlist.length);
|
|
91
|
+
} while (randomIndex === this.currentIndex);
|
|
92
|
+
this.play(this.playlist[randomIndex], randomIndex);
|
|
149
93
|
}
|
|
150
94
|
static toggleShuffle() {
|
|
151
95
|
this.isShuffle = !this.isShuffle;
|
|
152
96
|
}
|
|
97
|
+
static getShuffleStatus() {
|
|
98
|
+
return this.isShuffle;
|
|
99
|
+
}
|
|
153
100
|
static addToQueue(song) {
|
|
154
|
-
if (!this.queue.
|
|
101
|
+
if (!this.queue.some(q => q.id === song.id)) {
|
|
155
102
|
this.queue.push(song);
|
|
103
|
+
this.queue$.next([...this.queue]);
|
|
156
104
|
}
|
|
157
105
|
}
|
|
158
106
|
static removeFromQueue(index) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
107
|
+
this.queue.splice(index, 1);
|
|
108
|
+
this.queue$.next([...this.queue]);
|
|
162
109
|
}
|
|
163
110
|
static reorderQueue(from, to) {
|
|
164
|
-
|
|
165
|
-
return;
|
|
166
|
-
const [item] = this.queue.splice(from, 1);
|
|
111
|
+
const item = this.queue.splice(from, 1)[0];
|
|
167
112
|
this.queue.splice(to, 0, item);
|
|
168
|
-
|
|
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
|
-
});
|
|
113
|
+
this.queue$.next([...this.queue]);
|
|
176
114
|
}
|
|
177
115
|
static getCurrentTime() {
|
|
178
116
|
return this.currentTime;
|
|
@@ -180,10 +118,10 @@ class Player {
|
|
|
180
118
|
static getDuration() {
|
|
181
119
|
return this.duration;
|
|
182
120
|
}
|
|
183
|
-
static formatTime(
|
|
184
|
-
const
|
|
185
|
-
const
|
|
186
|
-
return `${
|
|
121
|
+
static formatTime(time) {
|
|
122
|
+
const minutes = Math.floor(time / 60);
|
|
123
|
+
const seconds = Math.floor(time % 60);
|
|
124
|
+
return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`;
|
|
187
125
|
}
|
|
188
126
|
static isPlayingSong() {
|
|
189
127
|
return this.isPlaying;
|
|
@@ -195,33 +133,21 @@ class Player {
|
|
|
195
133
|
this.selectedQuality = index;
|
|
196
134
|
}
|
|
197
135
|
static getQueue() {
|
|
198
|
-
return
|
|
136
|
+
return this.queue;
|
|
199
137
|
}
|
|
200
138
|
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
|
-
});
|
|
139
|
+
return this.playlist;
|
|
214
140
|
}
|
|
215
141
|
}
|
|
216
142
|
exports.Player = Player;
|
|
217
|
-
Player.audio =
|
|
143
|
+
Player.audio = new Audio();
|
|
218
144
|
Player.currentSong = null;
|
|
219
145
|
Player.currentIndex = 0;
|
|
220
146
|
Player.isPlaying = false;
|
|
221
147
|
Player.currentTime = 0;
|
|
222
148
|
Player.duration = 0;
|
|
223
|
-
Player.isShuffle =
|
|
149
|
+
Player.isShuffle = true;
|
|
224
150
|
Player.queue = [];
|
|
151
|
+
Player.queue$ = new rxjs_1.BehaviorSubject([]);
|
|
225
152
|
Player.playlist = [];
|
|
226
153
|
Player.selectedQuality = 3;
|
|
227
|
-
Player.isInitialized = false;
|
package/package.json
CHANGED
|
@@ -1,29 +1,34 @@
|
|
|
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.11",
|
|
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
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"rxjs": "^7.8.2"
|
|
33
|
+
}
|
|
29
34
|
}
|
package/src/core/player.ts
CHANGED
|
@@ -1,214 +1,174 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { CapacitorMusicControls } from 'capacitor-music-controls-plugin';
|
|
1
|
+
import { BehaviorSubject } from 'rxjs';
|
|
3
2
|
|
|
4
3
|
export class Player {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
this.
|
|
4
|
+
private static audio = new Audio();
|
|
5
|
+
private static currentSong: any = null;
|
|
6
|
+
private static currentIndex = 0;
|
|
7
|
+
private static isPlaying = false;
|
|
8
|
+
private static currentTime = 0;
|
|
9
|
+
private static duration = 0;
|
|
10
|
+
private static isShuffle = true;
|
|
11
|
+
private static queue: any[] = [];
|
|
12
|
+
static queue$ = new BehaviorSubject<any[]>([]);
|
|
13
|
+
private static playlist: any[] = [];
|
|
14
|
+
private static selectedQuality = 3;
|
|
15
|
+
|
|
16
|
+
/** Initialize with playlist and quality */
|
|
17
|
+
static initialize(playlist: any[], quality = 3) {
|
|
18
|
+
this.playlist = playlist;
|
|
19
|
+
this.selectedQuality = quality;
|
|
20
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();
|
|
21
|
+
|
|
22
|
+
/** Call this once on user gesture to unlock audio in WebView */
|
|
23
|
+
static unlockAudio() {
|
|
91
24
|
this.audio.src = '';
|
|
25
|
+
this.audio.load();
|
|
26
|
+
this.audio.play().catch(() => {});
|
|
92
27
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
28
|
+
|
|
29
|
+
static play(song: any, index: number = 0) {
|
|
30
|
+
if (!song || !song.downloadUrl) return;
|
|
31
|
+
|
|
32
|
+
this.currentSong = song;
|
|
33
|
+
this.currentIndex = index;
|
|
34
|
+
this.audio.src = song.downloadUrl[this.selectedQuality]?.url || '';
|
|
35
|
+
this.audio.load(); // Ensure audio is loaded before play
|
|
36
|
+
this.audio.play().then(() => {
|
|
37
|
+
this.isPlaying = true;
|
|
38
|
+
}).catch((err) => {
|
|
39
|
+
// Handle play errors (autoplay, WebView restrictions)
|
|
40
|
+
this.isPlaying = false;
|
|
41
|
+
console.warn('Audio play failed:', err);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Set duration
|
|
45
|
+
this.audio.onloadedmetadata = () => {
|
|
46
|
+
this.duration = this.audio.duration;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Set current time
|
|
50
|
+
this.audio.ontimeupdate = () => {
|
|
51
|
+
this.currentTime = this.audio.currentTime;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// Auto-play next song
|
|
55
|
+
this.audio.onended = () => {
|
|
56
|
+
this.autoNext();
|
|
57
|
+
};
|
|
104
58
|
}
|
|
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);
|
|
59
|
+
|
|
60
|
+
static pause() {
|
|
61
|
+
this.audio.pause();
|
|
62
|
+
this.isPlaying = false;
|
|
116
63
|
}
|
|
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);
|
|
64
|
+
|
|
65
|
+
static resume() {
|
|
66
|
+
this.audio.play();
|
|
67
|
+
this.isPlaying = true;
|
|
124
68
|
}
|
|
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);
|
|
69
|
+
|
|
70
|
+
static togglePlayPause() {
|
|
71
|
+
if (this.isPlaying) {
|
|
72
|
+
this.pause();
|
|
73
|
+
} else {
|
|
74
|
+
this.resume();
|
|
75
|
+
}
|
|
136
76
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
77
|
+
|
|
78
|
+
static next() {
|
|
79
|
+
if (this.queue.length > 0) {
|
|
80
|
+
const nextQueued = this.queue.shift();
|
|
81
|
+
const index = this.playlist.findIndex(s => s.id === nextQueued.id);
|
|
82
|
+
this.play(nextQueued, index);
|
|
83
|
+
} else if (this.isShuffle) {
|
|
84
|
+
this.playRandom();
|
|
85
|
+
} else if (this.currentIndex < this.playlist.length - 1) {
|
|
86
|
+
this.play(this.playlist[this.currentIndex + 1], this.currentIndex + 1);
|
|
87
|
+
}
|
|
147
88
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
89
|
+
|
|
90
|
+
static prev() {
|
|
91
|
+
if (this.currentIndex > 0) {
|
|
92
|
+
this.play(this.playlist[this.currentIndex - 1], this.currentIndex - 1);
|
|
93
|
+
}
|
|
153
94
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
static reorderQueue(from: number, to: number) {
|
|
157
|
-
if (from === to) return;
|
|
158
|
-
const [item] = this.queue.splice(from, 1);
|
|
159
|
-
this.queue.splice(to, 0, item);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
static async seekTo(seconds: number) {
|
|
163
|
-
if (this.audio) {
|
|
95
|
+
|
|
96
|
+
static seek(seconds: number) {
|
|
164
97
|
this.audio.currentTime = seconds;
|
|
165
|
-
this.currentTime = seconds;
|
|
166
98
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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
|
-
}
|
|
99
|
+
|
|
100
|
+
static autoNext() {
|
|
101
|
+
this.next();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
static playRandom() {
|
|
105
|
+
if (this.playlist.length <= 1) return;
|
|
106
|
+
|
|
107
|
+
let randomIndex;
|
|
108
|
+
do {
|
|
109
|
+
randomIndex = Math.floor(Math.random() * this.playlist.length);
|
|
110
|
+
} while (randomIndex === this.currentIndex);
|
|
111
|
+
|
|
112
|
+
this.play(this.playlist[randomIndex], randomIndex);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
static toggleShuffle() {
|
|
116
|
+
this.isShuffle = !this.isShuffle;
|
|
117
|
+
}
|
|
206
118
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
119
|
+
static getShuffleStatus(): boolean {
|
|
120
|
+
return this.isShuffle;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
static addToQueue(song: any) {
|
|
124
|
+
if (!this.queue.some(q => q.id === song.id)) {
|
|
125
|
+
this.queue.push(song);
|
|
126
|
+
this.queue$.next([...this.queue]);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
static removeFromQueue(index: number) {
|
|
131
|
+
this.queue.splice(index, 1);
|
|
132
|
+
this.queue$.next([...this.queue]);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
static reorderQueue(from: number, to: number) {
|
|
136
|
+
const item = this.queue.splice(from, 1)[0];
|
|
137
|
+
this.queue.splice(to, 0, item);
|
|
138
|
+
this.queue$.next([...this.queue]);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
static getCurrentTime(): number {
|
|
142
|
+
return this.currentTime;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
static getDuration(): number {
|
|
146
|
+
return this.duration;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
static formatTime(time: number): string {
|
|
150
|
+
const minutes = Math.floor(time / 60);
|
|
151
|
+
const seconds = Math.floor(time % 60);
|
|
152
|
+
return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
static isPlayingSong(): boolean {
|
|
156
|
+
return this.isPlaying;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
static getCurrentSong(): any {
|
|
160
|
+
return this.currentSong;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
static setQuality(index: number) {
|
|
164
|
+
this.selectedQuality = index;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
static getQueue(): any[] {
|
|
168
|
+
return this.queue;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
static getPlaylist(): any[] {
|
|
172
|
+
return this.playlist;
|
|
173
|
+
}
|
|
213
174
|
}
|
|
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
|
}
|