tunzo-player 1.0.0

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.
@@ -0,0 +1,9 @@
1
+ export declare class TunzoPlayerAPI {
2
+ /**
3
+ * Search for songs using the saavn.dev API
4
+ * @param query Search keyword (e.g., artist name, song name)
5
+ * @param limit Number of results to return (default: 250)
6
+ * @returns Array of song result objects
7
+ */
8
+ searchSongs(query: string, limit?: number): Promise<any[]>;
9
+ }
@@ -0,0 +1,38 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.TunzoPlayerAPI = void 0;
13
+ class TunzoPlayerAPI {
14
+ /**
15
+ * Search for songs using the saavn.dev API
16
+ * @param query Search keyword (e.g., artist name, song name)
17
+ * @param limit Number of results to return (default: 250)
18
+ * @returns Array of song result objects
19
+ */
20
+ searchSongs(query_1) {
21
+ return __awaiter(this, arguments, void 0, function* (query, limit = 250) {
22
+ var _a;
23
+ try {
24
+ const response = yield fetch(`https://saavn.dev/api/search/songs?query=${encodeURIComponent(query)}&limit=${limit}`);
25
+ if (!response.ok) {
26
+ throw new Error(`HTTP error! status: ${response.status}`);
27
+ }
28
+ const json = yield response.json();
29
+ return ((_a = json === null || json === void 0 ? void 0 : json.data) === null || _a === void 0 ? void 0 : _a.results) || [];
30
+ }
31
+ catch (error) {
32
+ console.error("TunzoPlayerAPI Error:", error);
33
+ return [];
34
+ }
35
+ });
36
+ }
37
+ }
38
+ exports.TunzoPlayerAPI = TunzoPlayerAPI;
@@ -0,0 +1,35 @@
1
+ export declare class Player {
2
+ private static audio;
3
+ private static currentSong;
4
+ private static currentIndex;
5
+ private static isPlaying;
6
+ private static currentTime;
7
+ private static duration;
8
+ private static isShuffle;
9
+ private static queue;
10
+ private static playlist;
11
+ private static selectedQuality;
12
+ /** 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;
23
+ static toggleShuffle(): void;
24
+ static addToQueue(song: any): void;
25
+ static removeFromQueue(index: number): void;
26
+ static reorderQueue(from: number, to: number): void;
27
+ static getCurrentTime(): number;
28
+ static getDuration(): number;
29
+ static formatTime(time: number): string;
30
+ static isPlayingSong(): boolean;
31
+ static getCurrentSong(): any;
32
+ static setQuality(index: number): void;
33
+ static getQueue(): any[];
34
+ static getPlaylist(): any[];
35
+ }
@@ -0,0 +1,133 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Player = void 0;
4
+ class Player {
5
+ /** Initialize with playlist and quality */
6
+ static initialize(playlist, quality = 3) {
7
+ this.playlist = playlist;
8
+ this.selectedQuality = quality;
9
+ }
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
+ };
31
+ }
32
+ static pause() {
33
+ this.audio.pause();
34
+ this.isPlaying = false;
35
+ }
36
+ static resume() {
37
+ this.audio.play();
38
+ this.isPlaying = true;
39
+ }
40
+ static togglePlayPause() {
41
+ if (this.isPlaying) {
42
+ this.pause();
43
+ }
44
+ else {
45
+ this.resume();
46
+ }
47
+ }
48
+ 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
+ }
60
+ }
61
+ 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;
68
+ }
69
+ static autoNext() {
70
+ this.next();
71
+ }
72
+ 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);
80
+ }
81
+ static toggleShuffle() {
82
+ this.isShuffle = !this.isShuffle;
83
+ }
84
+ static addToQueue(song) {
85
+ if (!this.queue.some(q => q.id === song.id)) {
86
+ this.queue.push(song);
87
+ }
88
+ }
89
+ static removeFromQueue(index) {
90
+ this.queue.splice(index, 1);
91
+ }
92
+ static reorderQueue(from, to) {
93
+ const item = this.queue.splice(from, 1)[0];
94
+ this.queue.splice(to, 0, item);
95
+ }
96
+ static getCurrentTime() {
97
+ return this.currentTime;
98
+ }
99
+ static getDuration() {
100
+ return this.duration;
101
+ }
102
+ static formatTime(time) {
103
+ const minutes = Math.floor(time / 60);
104
+ const seconds = Math.floor(time % 60);
105
+ return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`;
106
+ }
107
+ static isPlayingSong() {
108
+ return this.isPlaying;
109
+ }
110
+ static getCurrentSong() {
111
+ return this.currentSong;
112
+ }
113
+ static setQuality(index) {
114
+ this.selectedQuality = index;
115
+ }
116
+ static getQueue() {
117
+ return this.queue;
118
+ }
119
+ static getPlaylist() {
120
+ return this.playlist;
121
+ }
122
+ }
123
+ exports.Player = Player;
124
+ Player.audio = new Audio();
125
+ Player.currentSong = null;
126
+ Player.currentIndex = 0;
127
+ Player.isPlaying = false;
128
+ Player.currentTime = 0;
129
+ Player.duration = 0;
130
+ Player.isShuffle = true;
131
+ Player.queue = [];
132
+ Player.playlist = [];
133
+ Player.selectedQuality = 3;
@@ -0,0 +1,33 @@
1
+ export interface StreamQuality {
2
+ value: number;
3
+ label: string;
4
+ }
5
+ export declare class StreamSettings {
6
+ static readonly qualityOptions: StreamQuality[];
7
+ static readonly qualityValueKey = "qualityValue";
8
+ static readonly qualityLabelKey = "qualityLabel";
9
+ /**
10
+ * Loads stream quality from localStorage
11
+ */
12
+ static loadQuality(): StreamQuality;
13
+ /**
14
+ * Saves stream quality to localStorage
15
+ * @param value Index of quality option
16
+ * @param label Display label of selected quality
17
+ */
18
+ static saveQuality(value: number, label: string): void;
19
+ /**
20
+ * Updates quality using value index only
21
+ * @param value Stream quality index (0 to 4)
22
+ */
23
+ static updateQuality(value: number): StreamQuality;
24
+ /**
25
+ * Returns the label for a given value
26
+ * @param value Quality value index
27
+ */
28
+ static getLabel(value: number): string;
29
+ /**
30
+ * Returns the available quality options
31
+ */
32
+ static getOptions(): StreamQuality[];
33
+ }
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.StreamSettings = void 0;
4
+ class StreamSettings {
5
+ /**
6
+ * Loads stream quality from localStorage
7
+ */
8
+ static loadQuality() {
9
+ var _a;
10
+ const value = parseInt(localStorage.getItem(this.qualityValueKey) || '3');
11
+ const label = localStorage.getItem(this.qualityLabelKey) ||
12
+ ((_a = this.qualityOptions.find(q => q.value === value)) === null || _a === void 0 ? void 0 : _a.label) ||
13
+ 'High (160kbps)';
14
+ return { value, label };
15
+ }
16
+ /**
17
+ * Saves stream quality to localStorage
18
+ * @param value Index of quality option
19
+ * @param label Display label of selected quality
20
+ */
21
+ static saveQuality(value, label) {
22
+ localStorage.setItem(this.qualityValueKey, value.toString());
23
+ localStorage.setItem(this.qualityLabelKey, label);
24
+ }
25
+ /**
26
+ * Updates quality using value index only
27
+ * @param value Stream quality index (0 to 4)
28
+ */
29
+ static updateQuality(value) {
30
+ const quality = this.qualityOptions.find(q => q.value === value);
31
+ if (!quality)
32
+ throw new Error('Invalid quality value');
33
+ this.saveQuality(quality.value, quality.label);
34
+ return quality;
35
+ }
36
+ /**
37
+ * Returns the label for a given value
38
+ * @param value Quality value index
39
+ */
40
+ static getLabel(value) {
41
+ var _a;
42
+ return ((_a = this.qualityOptions.find(q => q.value === value)) === null || _a === void 0 ? void 0 : _a.label) || '';
43
+ }
44
+ /**
45
+ * Returns the available quality options
46
+ */
47
+ static getOptions() {
48
+ return this.qualityOptions;
49
+ }
50
+ }
51
+ exports.StreamSettings = StreamSettings;
52
+ // All available stream quality options
53
+ StreamSettings.qualityOptions = [
54
+ { value: 0, label: 'Very Low (12kbps)' },
55
+ { value: 1, label: 'Low (48kbps)' },
56
+ { value: 2, label: 'Medium (96kbps)' },
57
+ { value: 3, label: 'High (160kbps)' },
58
+ { value: 4, label: 'Ultra (320kbps)' },
59
+ ];
60
+ StreamSettings.qualityValueKey = 'qualityValue';
61
+ StreamSettings.qualityLabelKey = 'qualityLabel';
@@ -0,0 +1,25 @@
1
+ export declare class ThemeManager {
2
+ private static readonly key;
3
+ private static readonly darkClass;
4
+ /**
5
+ * Initializes the theme based on system preference or stored value
6
+ */
7
+ static initialize(): void;
8
+ /**
9
+ * Applies dark/light theme class
10
+ * @param shouldAdd Whether to add dark class
11
+ */
12
+ static apply(shouldAdd: boolean): void;
13
+ /**
14
+ * Toggles current theme between light and dark
15
+ */
16
+ static toggle(): void;
17
+ /**
18
+ * Returns current theme value
19
+ */
20
+ static getCurrent(): 'light' | 'dark';
21
+ /**
22
+ * Returns true if dark mode is active
23
+ */
24
+ static isDark(): boolean;
25
+ }
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ThemeManager = void 0;
4
+ class ThemeManager {
5
+ /**
6
+ * Initializes the theme based on system preference or stored value
7
+ */
8
+ static initialize() {
9
+ const stored = localStorage.getItem(this.key);
10
+ if (stored !== null) {
11
+ const shouldAdd = stored === 'dark';
12
+ this.apply(shouldAdd);
13
+ }
14
+ else {
15
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');
16
+ this.apply(prefersDark.matches);
17
+ // Save for future
18
+ localStorage.setItem(this.key, prefersDark.matches ? 'dark' : 'light');
19
+ }
20
+ // Optional: listen to system change
21
+ window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (event) => {
22
+ this.apply(event.matches);
23
+ localStorage.setItem(this.key, event.matches ? 'dark' : 'light');
24
+ });
25
+ }
26
+ /**
27
+ * Applies dark/light theme class
28
+ * @param shouldAdd Whether to add dark class
29
+ */
30
+ static apply(shouldAdd) {
31
+ document.documentElement.classList.toggle(this.darkClass, shouldAdd);
32
+ }
33
+ /**
34
+ * Toggles current theme between light and dark
35
+ */
36
+ static toggle() {
37
+ const isDark = document.documentElement.classList.contains(this.darkClass);
38
+ const newMode = isDark ? 'light' : 'dark';
39
+ this.apply(!isDark);
40
+ localStorage.setItem(this.key, newMode);
41
+ }
42
+ /**
43
+ * Returns current theme value
44
+ */
45
+ static getCurrent() {
46
+ return document.documentElement.classList.contains(this.darkClass) ? 'dark' : 'light';
47
+ }
48
+ /**
49
+ * Returns true if dark mode is active
50
+ */
51
+ static isDark() {
52
+ return this.getCurrent() === 'dark';
53
+ }
54
+ }
55
+ exports.ThemeManager = ThemeManager;
56
+ ThemeManager.key = 'themePalette'; // localStorage key
57
+ ThemeManager.darkClass = 'ion-palette-dark'; // CSS class to toggle
File without changes
@@ -0,0 +1 @@
1
+ "use strict";
@@ -0,0 +1,4 @@
1
+ export * from './core/player';
2
+ export * from './core/api-calles';
3
+ export * from './core/settings';
4
+ export * from './core/theme';
package/dist/index.js ADDED
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./core/player"), exports);
18
+ __exportStar(require("./core/api-calles"), exports);
19
+ __exportStar(require("./core/settings"), exports);
20
+ __exportStar(require("./core/theme"), exports);
package/package.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "tunzo-player",
3
+ "version": "1.0.0",
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
+ },
10
+ "keywords": ["music", "player", "angular", "ionic", "audio"],
11
+ "author": "Kulasekaran",
12
+ "license": "MIT",
13
+ "publishConfig": {
14
+ "access": "public"
15
+ }
16
+ }
@@ -0,0 +1,26 @@
1
+ export class TunzoPlayerAPI {
2
+ /**
3
+ * Search for songs using the saavn.dev API
4
+ * @param query Search keyword (e.g., artist name, song name)
5
+ * @param limit Number of results to return (default: 250)
6
+ * @returns Array of song result objects
7
+ */
8
+ async searchSongs(query: string, limit: number = 250): Promise<any[]> {
9
+ try {
10
+ const response = await fetch(
11
+ `https://saavn.dev/api/search/songs?query=${encodeURIComponent(query)}&limit=${limit}`
12
+ );
13
+
14
+ if (!response.ok) {
15
+ throw new Error(`HTTP error! status: ${response.status}`);
16
+ }
17
+
18
+ const json = await response.json();
19
+ return json?.data?.results || [];
20
+ } catch (error) {
21
+ console.error("TunzoPlayerAPI Error:", error);
22
+ return [];
23
+ }
24
+ }
25
+ }
26
+
@@ -0,0 +1,152 @@
1
+ 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;
47
+ }
48
+
49
+ static resume() {
50
+ this.audio.play();
51
+ this.isPlaying = true;
52
+ }
53
+
54
+ static togglePlayPause() {
55
+ if (this.isPlaying) {
56
+ this.pause();
57
+ } else {
58
+ this.resume();
59
+ }
60
+ }
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);
71
+ }
72
+ }
73
+
74
+ static prev() {
75
+ if (this.currentIndex > 0) {
76
+ this.play(this.playlist[this.currentIndex - 1], this.currentIndex - 1);
77
+ }
78
+ }
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);
106
+ }
107
+ }
108
+
109
+ static removeFromQueue(index: number) {
110
+ this.queue.splice(index, 1);
111
+ }
112
+
113
+ static reorderQueue(from: number, to: number) {
114
+ const item = this.queue.splice(from, 1)[0];
115
+ this.queue.splice(to, 0, item);
116
+ }
117
+
118
+ static getCurrentTime(): number {
119
+ return this.currentTime;
120
+ }
121
+
122
+ static getDuration(): number {
123
+ return this.duration;
124
+ }
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}`;
130
+ }
131
+
132
+ static isPlayingSong(): boolean {
133
+ return this.isPlaying;
134
+ }
135
+
136
+ static getCurrentSong(): any {
137
+ return this.currentSong;
138
+ }
139
+
140
+ static setQuality(index: number) {
141
+ this.selectedQuality = index;
142
+ }
143
+
144
+ static getQueue(): any[] {
145
+ return this.queue;
146
+ }
147
+
148
+ static getPlaylist(): any[] {
149
+ return this.playlist;
150
+ }
151
+ }
152
+
@@ -0,0 +1,69 @@
1
+ export interface StreamQuality {
2
+ value: number;
3
+ label: string;
4
+ }
5
+
6
+ export class StreamSettings {
7
+ // All available stream quality options
8
+ static readonly qualityOptions: StreamQuality[] = [
9
+ { value: 0, label: 'Very Low (12kbps)' },
10
+ { value: 1, label: 'Low (48kbps)' },
11
+ { value: 2, label: 'Medium (96kbps)' },
12
+ { value: 3, label: 'High (160kbps)' },
13
+ { value: 4, label: 'Ultra (320kbps)' },
14
+ ];
15
+
16
+ static readonly qualityValueKey = 'qualityValue';
17
+ static readonly qualityLabelKey = 'qualityLabel';
18
+
19
+ /**
20
+ * Loads stream quality from localStorage
21
+ */
22
+ static loadQuality(): StreamQuality {
23
+ const value = parseInt(localStorage.getItem(this.qualityValueKey) || '3');
24
+ const label =
25
+ localStorage.getItem(this.qualityLabelKey) ||
26
+ this.qualityOptions.find(q => q.value === value)?.label ||
27
+ 'High (160kbps)';
28
+
29
+ return { value, label };
30
+ }
31
+
32
+ /**
33
+ * Saves stream quality to localStorage
34
+ * @param value Index of quality option
35
+ * @param label Display label of selected quality
36
+ */
37
+ static saveQuality(value: number, label: string) {
38
+ localStorage.setItem(this.qualityValueKey, value.toString());
39
+ localStorage.setItem(this.qualityLabelKey, label);
40
+ }
41
+
42
+ /**
43
+ * Updates quality using value index only
44
+ * @param value Stream quality index (0 to 4)
45
+ */
46
+ static updateQuality(value: number): StreamQuality {
47
+ const quality = this.qualityOptions.find(q => q.value === value);
48
+ if (!quality) throw new Error('Invalid quality value');
49
+
50
+ this.saveQuality(quality.value, quality.label);
51
+ return quality;
52
+ }
53
+
54
+ /**
55
+ * Returns the label for a given value
56
+ * @param value Quality value index
57
+ */
58
+ static getLabel(value: number): string {
59
+ return this.qualityOptions.find(q => q.value === value)?.label || '';
60
+ }
61
+
62
+ /**
63
+ * Returns the available quality options
64
+ */
65
+ static getOptions(): StreamQuality[] {
66
+ return this.qualityOptions;
67
+ }
68
+ }
69
+
@@ -0,0 +1,61 @@
1
+ export class ThemeManager {
2
+ private static readonly key = 'themePalette'; // localStorage key
3
+ private static readonly darkClass = 'ion-palette-dark'; // CSS class to toggle
4
+
5
+ /**
6
+ * Initializes the theme based on system preference or stored value
7
+ */
8
+ static initialize() {
9
+ const stored = localStorage.getItem(this.key);
10
+
11
+ if (stored !== null) {
12
+ const shouldAdd = stored === 'dark';
13
+ this.apply(shouldAdd);
14
+ } else {
15
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');
16
+ this.apply(prefersDark.matches);
17
+
18
+ // Save for future
19
+ localStorage.setItem(this.key, prefersDark.matches ? 'dark' : 'light');
20
+ }
21
+
22
+ // Optional: listen to system change
23
+ window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (event) => {
24
+ this.apply(event.matches);
25
+ localStorage.setItem(this.key, event.matches ? 'dark' : 'light');
26
+ });
27
+ }
28
+
29
+ /**
30
+ * Applies dark/light theme class
31
+ * @param shouldAdd Whether to add dark class
32
+ */
33
+ static apply(shouldAdd: boolean) {
34
+ document.documentElement.classList.toggle(this.darkClass, shouldAdd);
35
+ }
36
+
37
+ /**
38
+ * Toggles current theme between light and dark
39
+ */
40
+ static toggle() {
41
+ const isDark = document.documentElement.classList.contains(this.darkClass);
42
+ const newMode = isDark ? 'light' : 'dark';
43
+ this.apply(!isDark);
44
+ localStorage.setItem(this.key, newMode);
45
+ }
46
+
47
+ /**
48
+ * Returns current theme value
49
+ */
50
+ static getCurrent(): 'light' | 'dark' {
51
+ return document.documentElement.classList.contains(this.darkClass) ? 'dark' : 'light';
52
+ }
53
+
54
+ /**
55
+ * Returns true if dark mode is active
56
+ */
57
+ static isDark(): boolean {
58
+ return this.getCurrent() === 'dark';
59
+ }
60
+ }
61
+
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from './core/player';
2
+ export * from './core/api-calles'
3
+ export * from './core/settings'
4
+ export * from './core/theme'
package/tsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ /* Output settings */
4
+ "outDir": "./dist", // output compiled files here
5
+ "rootDir": "./src", // source files are inside src/
6
+ "declaration": true, // generate .d.ts types
7
+ "declarationDir": "./dist", // put .d.ts files in dist too
8
+
9
+ /* Module & Target */
10
+ "module": "CommonJS", // compatible with Node/npm
11
+ "target": "ES2016", // or ES2020 if you prefer
12
+
13
+ /* Type Compatibility */
14
+ "esModuleInterop": true, // allow default imports from CommonJS
15
+ "forceConsistentCasingInFileNames": true,
16
+ "strict": true,
17
+ "skipLibCheck": true // speed up builds, safe for libs
18
+ },
19
+ "include": ["src"] // only compile the src folder
20
+ }