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