Add files via upload

This commit is contained in:
Savsis 2026-04-28 16:54:42 +05:00 • committed by GitHub
parent 81ce3b2271
commit 9fb037a648
13 changed files with 832 additions and 518 deletions

View file

@ -1,27 +1,25 @@
import * as SecureStore from 'expo-secure-store';
const BASE = 'https://music.nodeshift.space';
const CLOUD = 'https://cloud.nodeshift.space';
const BASE = 'https://music.nodeshift.space';
const CLOUD = 'https://cloud.nodeshift.space';
const CLIENT_ID = 'nsc_A5zi4cZ103aYJGcNTG1bERXV1YCAQW_U';
const REDIRECT = 'burmalda://auth/callback';
const REDIRECT = 'burmalda://auth/callback';
// ── Token storage ──────────────────────────────────────────────────────────────
// ── Token storage ─────────────────────────────────────────────────────────────
export async function saveTokens(access: string, refresh: string) {
await SecureStore.setItemAsync('access_token', access);
await SecureStore.setItemAsync('refresh_token', refresh);
}
export async function getAccessToken(): Promise<string | null> {
return SecureStore.getItemAsync('access_token');
}
export async function clearTokens() {
await SecureStore.deleteItemAsync('access_token');
await SecureStore.deleteItemAsync('refresh_token');
}
// ── Auth fetch helper ──────────────────────────────────────────────────────────
// ── Fetch helper ──────────────────────────────────────────────────────────────
async function apiFetch(path: string, opts: RequestInit = {}): Promise<Response> {
const token = await getAccessToken();
@ -33,15 +31,10 @@ async function apiFetch(path: string, opts: RequestInit = {}): Promise<Response>
return fetch(`${BASE}${path}`, { ...opts, headers });
}
// ── OAuth ──────────────────────────────────────────────────────────────────────
// ── OAuth ─────────────────────────────────────────────────────────────────────
export function buildOAuthUrl(state: string): string {
const p = new URLSearchParams({
client_id: CLIENT_ID,
redirect_uri: REDIRECT,
state,
scope: 'profile email',
});
const p = new URLSearchParams({ client_id: CLIENT_ID, redirect_uri: REDIRECT, state, scope: 'profile email' });
return `${CLOUD}/api/cloud/oauth/authorize?${p}`;
}
@ -58,7 +51,7 @@ export async function exchangeCode(code: string): Promise<{ access_token: string
return res.json();
}
// ── Types ──────────────────────────────────────────────────────────────────────
// ── Types ─────────────────────────────────────────────────────────────────────
export interface Track {
id: number;
@ -88,7 +81,12 @@ export interface User {
plan: string;
}
// ── API calls ──────────────────────────────────────────────────────────────────
export interface Lyrics {
synced: string | null;
plain: string | null;
}
// ── API calls ─────────────────────────────────────────────────────────────────
export async function getTracks(): Promise<Track[]> {
const r = await apiFetch('/api/music/tracks');
@ -114,6 +112,12 @@ export async function searchTracks(q: string): Promise<Track[]> {
return r.json();
}
export async function fetchLyrics(id: number): Promise<Lyrics | null> {
const r = await apiFetch(`/api/music/lyrics/${id}`);
if (!r.ok) return null;
return r.json();
}
export function streamUrl(filename: string): string {
return `${BASE}/api/music/stream/${filename}`;
}
@ -123,3 +127,21 @@ export function coverUrl(filename: string | null): string | null {
if (filename.startsWith('http')) return filename;
return `${BASE}/api/music/cover/${filename}`;
}
// ── LRC parser ────────────────────────────────────────────────────────────────
export interface LrcLine {
time: number;
text: string;
}
export function parseLrc(lrc: string): LrcLine[] {
const lines: LrcLine[] = [];
for (const raw of lrc.split('\n')) {
const m = raw.match(/^\[(\d+):(\d+)\.(\d+)\]\s*(.*)$/);
if (!m) continue;
const time = parseInt(m[1]) * 60 + parseFloat(`${m[2]}.${m[3]}`);
if (m[4].trim()) lines.push({ time, text: m[4].trim() });
}
return lines.sort((a, b) => a.time - b.time);
}