fix mobile UI + MAYBE direct tidal.com querying for pages functions???
This commit is contained in:
parent
fdb13d4a1c
commit
0255bffdd1
7 changed files with 320 additions and 300 deletions
9
.claude/settings.local.json
Normal file
9
.claude/settings.local.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(grep:*)",
|
||||
"Bash(find /c/Users/Admin/Documents/GitHub/monochrome -type f \\\\\\(-name *.html -o -name index.html \\\\\\))",
|
||||
"Bash(xargs wc:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,50 @@
|
|||
// functions/album/[id].js
|
||||
|
||||
class TidalAPI {
|
||||
static CLIENT_ID = 'txNoH4kkV41MfH25';
|
||||
static CLIENT_SECRET = 'dQjy0MinCEvxi1O4UmxvxWnDjt4cgHBPw8ll6nYBk98=';
|
||||
|
||||
async getToken() {
|
||||
const params = new URLSearchParams({
|
||||
client_id: TidalAPI.CLIENT_ID,
|
||||
client_secret: TidalAPI.CLIENT_SECRET,
|
||||
grant_type: 'client_credentials',
|
||||
});
|
||||
const res = await fetch('https://auth.tidal.com/v1/oauth2/token', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
Authorization: 'Basic ' + btoa(`${TidalAPI.CLIENT_ID}:${TidalAPI.CLIENT_SECRET}`),
|
||||
},
|
||||
body: params,
|
||||
});
|
||||
if (!res.ok) throw new Error(`Token request failed: ${res.status}`);
|
||||
const data = await res.json();
|
||||
return data.access_token;
|
||||
}
|
||||
|
||||
async fetchJson(url, params = {}) {
|
||||
const token = await this.getToken();
|
||||
const u = new URL(url);
|
||||
Object.entries(params).forEach(([k, v]) => u.searchParams.set(k, String(v)));
|
||||
const res = await fetch(u.toString(), {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
if (!res.ok) throw new Error(`Tidal API error: ${res.status}`);
|
||||
return res.json();
|
||||
}
|
||||
|
||||
async getAlbumMetadata(id) {
|
||||
return await this.fetchJson(`https://api.tidal.com/v1/albums/${id}`, { countryCode: 'US' });
|
||||
}
|
||||
|
||||
getCoverUrl(id, size = '1280') {
|
||||
if (!id) return '';
|
||||
const formattedId = String(id).replace(/-/g, '/');
|
||||
return `https://resources.tidal.com/images/${formattedId}/${size}x${size}.jpg`;
|
||||
}
|
||||
}
|
||||
|
||||
class ServerAPI {
|
||||
constructor() {
|
||||
this.INSTANCES_URLS = [
|
||||
|
|
@ -96,13 +141,26 @@ export async function onRequest(context) {
|
|||
const albumId = params.id;
|
||||
|
||||
if (isBot && albumId) {
|
||||
let api;
|
||||
let album;
|
||||
let tracks = [];
|
||||
try {
|
||||
const api = new ServerAPI();
|
||||
const data = await api.getAlbumMetadata(albumId);
|
||||
const album = data.data || data.album || data;
|
||||
const tracks = album.items || data.tracks || [];
|
||||
api = new TidalAPI();
|
||||
album = await api.getAlbumMetadata(albumId);
|
||||
} catch (directError) {
|
||||
console.warn(`Direct Tidal API failed for album ${albumId}, falling back to proxies:`, directError);
|
||||
try {
|
||||
api = new ServerAPI();
|
||||
const data = await api.getAlbumMetadata(albumId);
|
||||
album = data.data || data.album || data;
|
||||
tracks = album.items || data.tracks || [];
|
||||
} catch (fallbackError) {
|
||||
console.error(`All methods failed for album ${albumId}:`, fallbackError);
|
||||
}
|
||||
}
|
||||
|
||||
if (album && (album.title || album.name)) {
|
||||
if (album && (album.title || album.name)) {
|
||||
try {
|
||||
const title = album.title || album.name;
|
||||
const artist = album.artist?.name || 'Unknown Artist';
|
||||
const year = album.releaseDate ? new Date(album.releaseDate).getFullYear() : '';
|
||||
|
|
@ -122,7 +180,7 @@ export async function onRequest(context) {
|
|||
<title>${title}</title>
|
||||
<meta name="description" content="${description}">
|
||||
<meta name="theme-color" content="#000000">
|
||||
|
||||
|
||||
<meta property="og:site_name" content="Monochrome">
|
||||
<meta property="og:title" content="${title}">
|
||||
<meta property="og:description" content="${description}">
|
||||
|
|
@ -131,7 +189,7 @@ export async function onRequest(context) {
|
|||
<meta property="og:url" content="${pageUrl}">
|
||||
<meta property="music:musician" content="${artist}">
|
||||
<meta property="music:release_date" content="${album.releaseDate}">
|
||||
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="${title}">
|
||||
<meta name="twitter:description" content="${description}">
|
||||
|
|
@ -146,9 +204,9 @@ export async function onRequest(context) {
|
|||
`;
|
||||
|
||||
return new Response(metaHtml, { headers: { 'content-type': 'text/html;charset=UTF-8' } });
|
||||
} catch (error) {
|
||||
console.error(`Error generating meta tags for album ${albumId}:`, error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error for album ${albumId}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,50 @@
|
|||
// functions/artist/[id].js
|
||||
|
||||
class TidalAPI {
|
||||
static CLIENT_ID = 'txNoH4kkV41MfH25';
|
||||
static CLIENT_SECRET = 'dQjy0MinCEvxi1O4UmxvxWnDjt4cgHBPw8ll6nYBk98=';
|
||||
|
||||
async getToken() {
|
||||
const params = new URLSearchParams({
|
||||
client_id: TidalAPI.CLIENT_ID,
|
||||
client_secret: TidalAPI.CLIENT_SECRET,
|
||||
grant_type: 'client_credentials',
|
||||
});
|
||||
const res = await fetch('https://auth.tidal.com/v1/oauth2/token', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
Authorization: 'Basic ' + btoa(`${TidalAPI.CLIENT_ID}:${TidalAPI.CLIENT_SECRET}`),
|
||||
},
|
||||
body: params,
|
||||
});
|
||||
if (!res.ok) throw new Error(`Token request failed: ${res.status}`);
|
||||
const data = await res.json();
|
||||
return data.access_token;
|
||||
}
|
||||
|
||||
async fetchJson(url, params = {}) {
|
||||
const token = await this.getToken();
|
||||
const u = new URL(url);
|
||||
Object.entries(params).forEach(([k, v]) => u.searchParams.set(k, String(v)));
|
||||
const res = await fetch(u.toString(), {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
if (!res.ok) throw new Error(`Tidal API error: ${res.status}`);
|
||||
return res.json();
|
||||
}
|
||||
|
||||
async getArtistMetadata(id) {
|
||||
return await this.fetchJson(`https://api.tidal.com/v1/artists/${id}`, { countryCode: 'US' });
|
||||
}
|
||||
|
||||
getArtistPictureUrl(id, size = '750') {
|
||||
if (!id) return '';
|
||||
const formattedId = id.replace(/-/g, '/');
|
||||
return `https://resources.tidal.com/images/${formattedId}/${size}x${size}.jpg`;
|
||||
}
|
||||
}
|
||||
|
||||
class ServerAPI {
|
||||
constructor() {
|
||||
this.INSTANCES_URLS = [
|
||||
|
|
@ -96,12 +141,24 @@ export async function onRequest(context) {
|
|||
const artistId = params.id;
|
||||
|
||||
if (isBot && artistId) {
|
||||
let api;
|
||||
let artist;
|
||||
try {
|
||||
const api = new ServerAPI();
|
||||
const data = await api.getArtistMetadata(artistId);
|
||||
const artist = data.artist || data.data || data;
|
||||
api = new TidalAPI();
|
||||
artist = await api.getArtistMetadata(artistId);
|
||||
} catch (directError) {
|
||||
console.warn(`Direct Tidal API failed for artist ${artistId}, falling back to proxies:`, directError);
|
||||
try {
|
||||
api = new ServerAPI();
|
||||
const data = await api.getArtistMetadata(artistId);
|
||||
artist = data.artist || data.data || data;
|
||||
} catch (fallbackError) {
|
||||
console.error(`All methods failed for artist ${artistId}:`, fallbackError);
|
||||
}
|
||||
}
|
||||
|
||||
if (artist && (artist.name || artist.title)) {
|
||||
if (artist && (artist.name || artist.title)) {
|
||||
try {
|
||||
const name = artist.name || artist.title;
|
||||
const description = `Listen to ${name} on Monochrome`;
|
||||
const imageUrl = artist.picture
|
||||
|
|
@ -117,14 +174,14 @@ export async function onRequest(context) {
|
|||
<title>${name}</title>
|
||||
<meta name="description" content="${description}">
|
||||
<meta name="theme-color" content="#000000">
|
||||
|
||||
|
||||
<meta property="og:site_name" content="Monochrome">
|
||||
<meta property="og:title" content="${name}">
|
||||
<meta property="og:description" content="${description}">
|
||||
<meta property="og:image" content="${imageUrl}">
|
||||
<meta property="og:type" content="profile">
|
||||
<meta property="og:url" content="${pageUrl}">
|
||||
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="${name}">
|
||||
<meta name="twitter:description" content="${description}">
|
||||
|
|
@ -138,9 +195,9 @@ export async function onRequest(context) {
|
|||
`;
|
||||
|
||||
return new Response(metaHtml, { headers: { 'content-type': 'text/html;charset=UTF-8' } });
|
||||
} catch (error) {
|
||||
console.error(`Error generating meta tags for artist ${artistId}:`, error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error for artist ${artistId}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,50 @@
|
|||
// functions/playlist/[id].js
|
||||
|
||||
class TidalAPI {
|
||||
static CLIENT_ID = 'txNoH4kkV41MfH25';
|
||||
static CLIENT_SECRET = 'dQjy0MinCEvxi1O4UmxvxWnDjt4cgHBPw8ll6nYBk98=';
|
||||
|
||||
async getToken() {
|
||||
const params = new URLSearchParams({
|
||||
client_id: TidalAPI.CLIENT_ID,
|
||||
client_secret: TidalAPI.CLIENT_SECRET,
|
||||
grant_type: 'client_credentials',
|
||||
});
|
||||
const res = await fetch('https://auth.tidal.com/v1/oauth2/token', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
Authorization: 'Basic ' + btoa(`${TidalAPI.CLIENT_ID}:${TidalAPI.CLIENT_SECRET}`),
|
||||
},
|
||||
body: params,
|
||||
});
|
||||
if (!res.ok) throw new Error(`Token request failed: ${res.status}`);
|
||||
const data = await res.json();
|
||||
return data.access_token;
|
||||
}
|
||||
|
||||
async fetchJson(url, params = {}) {
|
||||
const token = await this.getToken();
|
||||
const u = new URL(url);
|
||||
Object.entries(params).forEach(([k, v]) => u.searchParams.set(k, String(v)));
|
||||
const res = await fetch(u.toString(), {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
if (!res.ok) throw new Error(`Tidal API error: ${res.status}`);
|
||||
return res.json();
|
||||
}
|
||||
|
||||
async getPlaylistMetadata(id) {
|
||||
return await this.fetchJson(`https://api.tidal.com/v1/playlists/${id}`, { countryCode: 'US' });
|
||||
}
|
||||
|
||||
getCoverUrl(id, size = '1080') {
|
||||
if (!id) return '';
|
||||
const formattedId = String(id).replace(/-/g, '/');
|
||||
return `https://resources.tidal.com/images/${formattedId}/${size}x${size}.jpg`;
|
||||
}
|
||||
}
|
||||
|
||||
class ServerAPI {
|
||||
constructor() {
|
||||
this.INSTANCES_URLS = [
|
||||
|
|
@ -75,7 +120,6 @@ class ServerAPI {
|
|||
const response = await this.fetchWithRetry(`/playlist/${id}`);
|
||||
return await response.json();
|
||||
} catch {
|
||||
// Fallback to query param style
|
||||
const response = await this.fetchWithRetry(`/playlist?id=${id}`);
|
||||
return await response.json();
|
||||
}
|
||||
|
|
@ -97,12 +141,24 @@ export async function onRequest(context) {
|
|||
const playlistId = params.id;
|
||||
|
||||
if (isBot && playlistId) {
|
||||
let api;
|
||||
let playlist;
|
||||
try {
|
||||
const api = new ServerAPI();
|
||||
const data = await api.getPlaylistMetadata(playlistId);
|
||||
const playlist = data.playlist || data.data || data;
|
||||
api = new TidalAPI();
|
||||
playlist = await api.getPlaylistMetadata(playlistId);
|
||||
} catch (directError) {
|
||||
console.warn(`Direct Tidal API failed for playlist ${playlistId}, falling back to proxies:`, directError);
|
||||
try {
|
||||
api = new ServerAPI();
|
||||
const data = await api.getPlaylistMetadata(playlistId);
|
||||
playlist = data.playlist || data.data || data;
|
||||
} catch (fallbackError) {
|
||||
console.error(`All methods failed for playlist ${playlistId}:`, fallbackError);
|
||||
}
|
||||
}
|
||||
|
||||
if (playlist && (playlist.title || playlist.name)) {
|
||||
if (playlist && (playlist.title || playlist.name)) {
|
||||
try {
|
||||
const title = playlist.title || playlist.name;
|
||||
const trackCount = playlist.numberOfTracks;
|
||||
const description = `Playlist • ${trackCount} Tracks\nListen on Monochrome`;
|
||||
|
|
@ -120,7 +176,7 @@ export async function onRequest(context) {
|
|||
<title>${title}</title>
|
||||
<meta name="description" content="${description}">
|
||||
<meta name="theme-color" content="#000000">
|
||||
|
||||
|
||||
<meta property="og:site_name" content="Monochrome">
|
||||
<meta property="og:title" content="${title}">
|
||||
<meta property="og:description" content="${description}">
|
||||
|
|
@ -128,7 +184,7 @@ export async function onRequest(context) {
|
|||
<meta property="og:type" content="music.playlist">
|
||||
<meta property="og:url" content="${pageUrl}">
|
||||
<meta property="music:song_count" content="${trackCount}">
|
||||
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="${title}">
|
||||
<meta name="twitter:description" content="${description}">
|
||||
|
|
@ -143,9 +199,9 @@ export async function onRequest(context) {
|
|||
`;
|
||||
|
||||
return new Response(metaHtml, { headers: { 'content-type': 'text/html;charset=UTF-8' } });
|
||||
} catch (error) {
|
||||
console.error(`Error generating meta tags for playlist ${playlistId}:`, error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error for playlist ${playlistId}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,61 @@ function getTrackArtists(track = {}, { fallback = 'Unknown Artist' } = {}) {
|
|||
return fallback;
|
||||
}
|
||||
|
||||
class TidalAPI {
|
||||
static CLIENT_ID = 'txNoH4kkV41MfH25';
|
||||
static CLIENT_SECRET = 'dQjy0MinCEvxi1O4UmxvxWnDjt4cgHBPw8ll6nYBk98=';
|
||||
|
||||
async getToken() {
|
||||
const params = new URLSearchParams({
|
||||
client_id: TidalAPI.CLIENT_ID,
|
||||
client_secret: TidalAPI.CLIENT_SECRET,
|
||||
grant_type: 'client_credentials',
|
||||
});
|
||||
const res = await fetch('https://auth.tidal.com/v1/oauth2/token', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
Authorization: 'Basic ' + btoa(`${TidalAPI.CLIENT_ID}:${TidalAPI.CLIENT_SECRET}`),
|
||||
},
|
||||
body: params,
|
||||
});
|
||||
if (!res.ok) throw new Error(`Token request failed: ${res.status}`);
|
||||
const data = await res.json();
|
||||
return data.access_token;
|
||||
}
|
||||
|
||||
async fetchJson(url, params = {}) {
|
||||
const token = await this.getToken();
|
||||
const u = new URL(url);
|
||||
Object.entries(params).forEach(([k, v]) => u.searchParams.set(k, String(v)));
|
||||
const res = await fetch(u.toString(), {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
if (!res.ok) throw new Error(`Tidal API error: ${res.status}`);
|
||||
return res.json();
|
||||
}
|
||||
|
||||
async getTrackMetadata(id) {
|
||||
return await this.fetchJson(`https://api.tidal.com/v1/tracks/${id}/`, { countryCode: 'US' });
|
||||
}
|
||||
|
||||
async getStreamUrl(id) {
|
||||
const data = await this.fetchJson(`https://api.tidal.com/v1/tracks/${id}/playbackinfo`, {
|
||||
audioquality: 'LOW',
|
||||
playbackmode: 'STREAM',
|
||||
assetpresentation: 'FULL',
|
||||
countryCode: 'US',
|
||||
});
|
||||
return data.url || data.streamUrl;
|
||||
}
|
||||
|
||||
getCoverUrl(id, size = '1280') {
|
||||
if (!id) return '';
|
||||
const formattedId = String(id).replace(/-/g, '/');
|
||||
return `https://resources.tidal.com/images/${formattedId}/${size}x${size}.jpg`;
|
||||
}
|
||||
}
|
||||
|
||||
class ServerAPI {
|
||||
constructor() {
|
||||
this.INSTANCES_URLS = [
|
||||
|
|
@ -116,11 +171,24 @@ export async function onRequest(context) {
|
|||
const trackId = params.id;
|
||||
|
||||
if (isBot && trackId) {
|
||||
// Try direct Tidal API first, fall back to proxy instances
|
||||
let api;
|
||||
let track;
|
||||
try {
|
||||
const api = new ServerAPI();
|
||||
const track = await api.getTrackMetadata(trackId);
|
||||
api = new TidalAPI();
|
||||
track = await api.getTrackMetadata(trackId);
|
||||
} catch (directError) {
|
||||
console.warn(`Direct Tidal API failed for track ${trackId}, falling back to proxies:`, directError);
|
||||
try {
|
||||
api = new ServerAPI();
|
||||
track = await api.getTrackMetadata(trackId);
|
||||
} catch (fallbackError) {
|
||||
console.error(`All methods failed for track ${trackId}:`, fallbackError);
|
||||
}
|
||||
}
|
||||
|
||||
if (track) {
|
||||
if (track) {
|
||||
try {
|
||||
const title = getTrackTitle(track);
|
||||
const artist = getTrackArtists(track);
|
||||
const description = `${artist} - ${track.album.title}`;
|
||||
|
|
@ -136,7 +204,7 @@ export async function onRequest(context) {
|
|||
console.error('Failed to fetch stream fallback:', e);
|
||||
}
|
||||
}
|
||||
// this prob wont work im js winging it
|
||||
|
||||
const audioMeta = audioUrl
|
||||
? `
|
||||
<meta property="og:audio" content="${audioUrl}">
|
||||
|
|
@ -153,7 +221,7 @@ export async function onRequest(context) {
|
|||
<meta charset="UTF-8">
|
||||
<title>${title} by ${artist}</title>
|
||||
<meta name="description" content="${description}">
|
||||
|
||||
|
||||
<meta property="og:title" content="${title}">
|
||||
<meta property="og:description" content="${description}">
|
||||
<meta property="og:image" content="${imageUrl}">
|
||||
|
|
@ -162,9 +230,9 @@ export async function onRequest(context) {
|
|||
<meta property="music:duration" content="${track.duration}">
|
||||
<meta property="music:album" content="${track.album.title}">
|
||||
<meta property="music:musician" content="${artist}">
|
||||
|
||||
|
||||
${audioMeta}
|
||||
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="${title}">
|
||||
<meta name="twitter:description" content="${description}">
|
||||
|
|
@ -182,9 +250,9 @@ export async function onRequest(context) {
|
|||
return new Response(metaHtml, {
|
||||
headers: { 'content-type': 'text/html;charset=UTF-8' },
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`Error generating meta tags for track ${trackId}:`, error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error generating meta tags for track ${trackId}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
286
package-lock.json
generated
286
package-lock.json
generated
|
|
@ -27,8 +27,8 @@
|
|||
"butterchurn-presets": "^2.4.7",
|
||||
"client-zip": "^2.5.0",
|
||||
"cookie-session": "^2.1.1",
|
||||
"dashjs": "https://github.com/Dash-Industry-Forum/dash.js/archive/refs/tags/v5.1.1.tar.gz",
|
||||
"eventemitter3": "^5.0.4",
|
||||
"events": "^3.3.0",
|
||||
"fuse.js": "^7.1.0",
|
||||
"hls.js": "^1.6.15",
|
||||
"jose": "^6.2.0",
|
||||
|
|
@ -36,6 +36,7 @@
|
|||
"mime": "^4.1.0",
|
||||
"npm": "^11.11.1",
|
||||
"pocketbase": "^0.26.8",
|
||||
"shaka-player": "^5.0.7",
|
||||
"simple-icons": "^16.12.0",
|
||||
"svgo": "^4.0.1",
|
||||
"url-toolkit": "^2.2.5",
|
||||
|
|
@ -4412,110 +4413,6 @@
|
|||
"string.prototype.matchall": "^4.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@svta/cml-608": {
|
||||
"version": "1.0.1",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/@svta/cml-cmcd": {
|
||||
"version": "1.0.1",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@svta/cml-cta": "1.0.1",
|
||||
"@svta/cml-structured-field-values": "1.0.1",
|
||||
"@svta/cml-utils": "1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@svta/cml-cmsd": {
|
||||
"version": "1.0.1",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@svta/cml-cta": "1.0.1",
|
||||
"@svta/cml-structured-field-values": "1.0.1",
|
||||
"@svta/cml-utils": "1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@svta/cml-cta": {
|
||||
"version": "1.0.1",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@svta/cml-structured-field-values": "1.0.1",
|
||||
"@svta/cml-utils": "1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@svta/cml-dash": {
|
||||
"version": "1.0.1",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@svta/cml-utils": "1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@svta/cml-id3": {
|
||||
"version": "1.0.1",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@svta/cml-utils": "1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@svta/cml-request": {
|
||||
"version": "1.0.1",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@svta/cml-utils": "1.0.1",
|
||||
"@svta/cml-xml": "1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@svta/cml-structured-field-values": {
|
||||
"version": "1.0.1",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@svta/cml-utils": "1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@svta/cml-utils": {
|
||||
"version": "1.0.1",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/@svta/cml-xml": {
|
||||
"version": "1.0.1",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@svta/cml-utils": "1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@svta/common-media-library": {
|
||||
"version": "0.18.1",
|
||||
"license": "Apache-2.0",
|
||||
|
|
@ -5264,39 +5161,6 @@
|
|||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/bcp-47": {
|
||||
"version": "2.1.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-alphabetical": "^2.0.0",
|
||||
"is-alphanumerical": "^2.0.0",
|
||||
"is-decimal": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/bcp-47-match": {
|
||||
"version": "2.0.3",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/bcp-47-normalize": {
|
||||
"version": "2.3.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bcp-47": "^2.0.0",
|
||||
"bcp-47-match": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/big-integer": {
|
||||
"version": "1.6.52",
|
||||
"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz",
|
||||
|
|
@ -5667,10 +5531,6 @@
|
|||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/codem-isoboxer": {
|
||||
"version": "0.3.10",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/color": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
|
||||
|
|
@ -6646,30 +6506,6 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/dashjs": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://github.com/Dash-Industry-Forum/dash.js/archive/refs/tags/v5.1.1.tar.gz",
|
||||
"integrity": "sha512-lhD1tvEe4PO6t086flm6WfO2Jt1EOIolDQ17F3vLomMthaL1RH96h8peIQTvrDvfSJTRXeisL+CwPj4oud5e9g==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@svta/cml-608": "1.0.1",
|
||||
"@svta/cml-cmcd": "1.0.1",
|
||||
"@svta/cml-cmsd": "1.0.1",
|
||||
"@svta/cml-dash": "1.0.1",
|
||||
"@svta/cml-id3": "1.0.1",
|
||||
"@svta/cml-request": "1.0.1",
|
||||
"@svta/cml-xml": "1.0.1",
|
||||
"bcp-47-match": "^2.0.3",
|
||||
"bcp-47-normalize": "^2.3.0",
|
||||
"codem-isoboxer": "0.3.10",
|
||||
"fast-deep-equal": "3.1.3",
|
||||
"html-entities": "^2.5.2",
|
||||
"imsc": "^1.1.5",
|
||||
"localforage": "^1.10.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"ua-parser-js": "^1.0.37"
|
||||
}
|
||||
},
|
||||
"node_modules/data-view-buffer": {
|
||||
"version": "1.0.2",
|
||||
"dev": true,
|
||||
|
|
@ -7569,6 +7405,15 @@
|
|||
"version": "5.0.4",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/events": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
|
||||
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.8.x"
|
||||
}
|
||||
},
|
||||
"node_modules/events-universal": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz",
|
||||
|
|
@ -7599,6 +7444,7 @@
|
|||
},
|
||||
"node_modules/fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-fifo": {
|
||||
|
|
@ -8992,20 +8838,6 @@
|
|||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/html-entities": {
|
||||
"version": "2.6.0",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/mdevils"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://patreon.com/mdevils"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/html-tags": {
|
||||
"version": "3.3.1",
|
||||
"dev": true,
|
||||
|
|
@ -9084,10 +8916,6 @@
|
|||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/immediate": {
|
||||
"version": "3.0.6",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/import-fresh": {
|
||||
"version": "3.3.1",
|
||||
"dev": true,
|
||||
|
|
@ -9111,17 +8939,6 @@
|
|||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/imsc": {
|
||||
"version": "1.1.5",
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"sax": "1.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/imsc/node_modules/sax": {
|
||||
"version": "1.2.1",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/imurmurhash": {
|
||||
"version": "0.1.4",
|
||||
"dev": true,
|
||||
|
|
@ -9180,26 +8997,6 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-alphabetical": {
|
||||
"version": "2.0.1",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/is-alphanumerical": {
|
||||
"version": "2.0.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-alphabetical": "^2.0.0",
|
||||
"is-decimal": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/is-array-buffer": {
|
||||
"version": "3.0.5",
|
||||
"dev": true,
|
||||
|
|
@ -9324,14 +9121,6 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-decimal": {
|
||||
"version": "2.0.1",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/is-docker": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
|
||||
|
|
@ -9960,13 +9749,6 @@
|
|||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lie": {
|
||||
"version": "3.1.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"immediate": "~3.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/lines-and-columns": {
|
||||
"version": "1.2.4",
|
||||
"dev": true,
|
||||
|
|
@ -10037,13 +9819,6 @@
|
|||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/localforage": {
|
||||
"version": "1.10.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"lie": "3.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/locate-path": {
|
||||
"version": "6.0.0",
|
||||
"dev": true,
|
||||
|
|
@ -12635,10 +12410,6 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/path-browserify": {
|
||||
"version": "1.0.1",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/path-exists": {
|
||||
"version": "4.0.0",
|
||||
"dev": true,
|
||||
|
|
@ -14027,6 +13798,15 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/shaka-player": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/shaka-player/-/shaka-player-5.0.8.tgz",
|
||||
"integrity": "sha512-f886rKRvQ0IKhWGk+rINS++YTjTJyc4DT5YypTsHW6wiNV9fiHi2n35+lg5R+hj9RfhqkmJHMjJb3gprUTNa8w==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/sharp": {
|
||||
"version": "0.34.5",
|
||||
"dev": true,
|
||||
|
|
@ -15342,30 +15122,6 @@
|
|||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/ua-parser-js": {
|
||||
"version": "1.0.41",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/ua-parser-js"
|
||||
},
|
||||
{
|
||||
"type": "paypal",
|
||||
"url": "https://paypal.me/faisalman"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/faisalman"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"ua-parser-js": "script/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/uglify-js": {
|
||||
"version": "3.19.3",
|
||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz",
|
||||
|
|
|
|||
16
styles.css
16
styles.css
|
|
@ -1328,6 +1328,8 @@ ul {
|
|||
.search-bar {
|
||||
width: 80%;
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.search-bar input {
|
||||
|
|
@ -1634,6 +1636,13 @@ input[type='search']::-webkit-search-cancel-button {
|
|||
gap: var(--spacing-xs);
|
||||
margin-bottom: var(--spacing-lg);
|
||||
border-bottom: 1px solid var(--border);
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.search-tabs::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.search-tab {
|
||||
|
|
@ -1645,6 +1654,8 @@ input[type='search']::-webkit-search-cancel-button {
|
|||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
border-bottom: 2px solid transparent;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
|
||||
/* Keep layout stable */
|
||||
border-radius: var(--radius-sm) var(--radius-sm) 0 0;
|
||||
|
|
@ -6300,6 +6311,8 @@ img[src=''] {
|
|||
padding: var(--spacing-md);
|
||||
grid-area: main;
|
||||
overflow-y: visible;
|
||||
overflow-x: hidden;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.main-header {
|
||||
|
|
@ -6335,6 +6348,7 @@ img[src=''] {
|
|||
|
||||
.search-bar {
|
||||
max-width: none;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.content-section {
|
||||
|
|
@ -6960,6 +6974,8 @@ img[src=''] {
|
|||
.main-content {
|
||||
padding: var(--spacing-sm);
|
||||
grid-area: main;
|
||||
overflow-x: hidden;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.progress-bar:not(.waveform-loaded),
|
||||
|
|
|
|||
Loading…
Reference in a new issue