mirror of
https://github.com/monochrome-music/monochrome.git
synced 2026-10-19 01:30:46 -05:00
style: auto-fix linting issues
This commit is contained in:
1 parent
3726d5d731
commit
3eb5271c3e
8 files changed
+79
-138
No files matched your search
@@ -99,6 +99,7 @@ The application is configured via environment variables. Copy `.env.example` to
|
||||
### Authentication (Better Auth)
|
||||
|
||||
Monochrome uses Better Auth for user authentication (defaulting to `https://auth.monochrome.st`). You can configure a custom auth instance using:
|
||||
|
||||
- `AUTH_URL`: Your Better Auth endpoint (e.g., `https://auth.monochrome.st`).
|
||||
|
||||
### Database (PocketBase)
|
||||
|
||||
+9
-9
@@ -27,15 +27,15 @@ PLEASE do not use any rehost of monochrome and complain to us about features not
|
||||
|
||||
### Official & Community APIs
|
||||
|
||||
| Provider | URL | Notes |
|
||||
| ----------------- | ----------------------------------- | ----------------------------------------------------------------------- |
|
||||
| **Monochrome** | `https://api.monochrome.tf` | Official API |
|
||||
| **Lucida (QQDL)** | `https://wolf.qqdl.site` | Community hosted |
|
||||
| | `https://maus.qqdl.site` | Community hosted |
|
||||
| | `https://vogel.qqdl.site` | Community hosted |
|
||||
| | `https://katze.qqdl.site` | Community hosted |
|
||||
| | `https://hund.qqdl.site` | Community hosted |
|
||||
| **Kinoplus** | `https://tidal.kinoplus.online` | Community hosted - [Limited/No-Sub](https://rentry.co/limitedtidalaccs) |
|
||||
| Provider | URL | Notes |
|
||||
| ----------------- | ------------------------------- | ----------------------------------------------------------------------- |
|
||||
| **Monochrome** | `https://api.monochrome.tf` | Official API |
|
||||
| **Lucida (QQDL)** | `https://wolf.qqdl.site` | Community hosted |
|
||||
| | `https://maus.qqdl.site` | Community hosted |
|
||||
| | `https://vogel.qqdl.site` | Community hosted |
|
||||
| | `https://katze.qqdl.site` | Community hosted |
|
||||
| | `https://hund.qqdl.site` | Community hosted |
|
||||
| **Kinoplus** | `https://tidal.kinoplus.online` | Community hosted - [Limited/No-Sub](https://rentry.co/limitedtidalaccs) |
|
||||
|
||||
---
|
||||
|
||||
|
||||
+20
-39
@@ -1279,7 +1279,12 @@
|
||||
</p>
|
||||
<div style="margin-bottom: 1rem">
|
||||
<label style="display: block; margin-bottom: 0.5rem; font-size: 0.9rem">PocketBase URL</label>
|
||||
<input type="url" id="custom-pb-url" class="template-input" placeholder="https://data.monochrome.st" />
|
||||
<input
|
||||
type="url"
|
||||
id="custom-pb-url"
|
||||
class="template-input"
|
||||
placeholder="https://data.monochrome.st"
|
||||
/>
|
||||
</div>
|
||||
<div style="margin-bottom: 1rem">
|
||||
<label style="display: block; margin-bottom: 0.5rem; font-size: 0.9rem">Auth Endpoint</label>
|
||||
@@ -4220,24 +4225,12 @@
|
||||
</div>
|
||||
<select id="streaming-quality-setting">
|
||||
<option value="auto">Auto (Adaptive)</option>
|
||||
<option value="DOLBY_ATMOS_EAC3_HIGH">
|
||||
Dolby Atmos — E-AC-3 High
|
||||
</option>
|
||||
<option value="DOLBY_ATMOS_EAC3_LOW">
|
||||
Dolby Atmos — E-AC-3 Low
|
||||
</option>
|
||||
<option value="DOLBY_ATMOS_AC4_HIGH">
|
||||
Dolby Atmos — AC-4 High
|
||||
</option>
|
||||
<option value="DOLBY_ATMOS_AC4_LOW">
|
||||
Dolby Atmos — AC-4 Low
|
||||
</option>
|
||||
<option value="HI_RES_LOSSLESS">
|
||||
Hi-Res Lossless (24-bit)
|
||||
</option>
|
||||
<option value="LOSSLESS">
|
||||
Lossless (16-bit)
|
||||
</option>
|
||||
<option value="DOLBY_ATMOS_EAC3_HIGH">Dolby Atmos — E-AC-3 High</option>
|
||||
<option value="DOLBY_ATMOS_EAC3_LOW">Dolby Atmos — E-AC-3 Low</option>
|
||||
<option value="DOLBY_ATMOS_AC4_HIGH">Dolby Atmos — AC-4 High</option>
|
||||
<option value="DOLBY_ATMOS_AC4_LOW">Dolby Atmos — AC-4 Low</option>
|
||||
<option value="HI_RES_LOSSLESS">Hi-Res Lossless (24-bit)</option>
|
||||
<option value="LOSSLESS">Lossless (16-bit)</option>
|
||||
<option value="HIGH">High</option>
|
||||
<option value="LOW">Low</option>
|
||||
</select>
|
||||
@@ -5528,24 +5521,12 @@
|
||||
>
|
||||
</div>
|
||||
<select id="download-quality-setting">
|
||||
<option value="DOLBY_ATMOS_EAC3_HIGH">
|
||||
Dolby Atmos — E-AC-3 High
|
||||
</option>
|
||||
<option value="DOLBY_ATMOS_EAC3_LOW">
|
||||
Dolby Atmos — E-AC-3 Low
|
||||
</option>
|
||||
<option value="DOLBY_ATMOS_AC4_HIGH">
|
||||
Dolby Atmos — AC-4 High
|
||||
</option>
|
||||
<option value="DOLBY_ATMOS_AC4_LOW">
|
||||
Dolby Atmos — AC-4 Low
|
||||
</option>
|
||||
<option value="HI_RES_LOSSLESS">
|
||||
Hi-Res Lossless (24-bit)
|
||||
</option>
|
||||
<option value="LOSSLESS">
|
||||
Lossless (16-bit)
|
||||
</option>
|
||||
<option value="DOLBY_ATMOS_EAC3_HIGH">Dolby Atmos — E-AC-3 High</option>
|
||||
<option value="DOLBY_ATMOS_EAC3_LOW">Dolby Atmos — E-AC-3 Low</option>
|
||||
<option value="DOLBY_ATMOS_AC4_HIGH">Dolby Atmos — AC-4 High</option>
|
||||
<option value="DOLBY_ATMOS_AC4_LOW">Dolby Atmos — AC-4 Low</option>
|
||||
<option value="HI_RES_LOSSLESS">Hi-Res Lossless (24-bit)</option>
|
||||
<option value="LOSSLESS">Lossless (16-bit)</option>
|
||||
<option value="HIGH">High</option>
|
||||
<option value="LOW">Low</option>
|
||||
</select>
|
||||
@@ -6176,8 +6157,8 @@
|
||||
<p class="version">Version 2.5.0</p>
|
||||
<p class="version" id="about-commit-info"></p>
|
||||
<p class="disclaimer">
|
||||
This is an independent client and is not affiliated with or endorsed by any
|
||||
music streaming service.
|
||||
This is an independent client and is not affiliated with or endorsed by any music
|
||||
streaming service.
|
||||
</p>
|
||||
<p class="disclaimer">
|
||||
This application does not host, store, or distribute any media files. <br />
|
||||
|
||||
+9
-17
@@ -9,11 +9,7 @@ import {
|
||||
normalizeAppleArtist,
|
||||
normalizeAppleSearchResults,
|
||||
} from './apple-music-api.js';
|
||||
import {
|
||||
TracksStreamerAPI,
|
||||
tracksStreamerAPI,
|
||||
normalizeTracksSearchResults,
|
||||
} from './tracks-api.js';
|
||||
import { TracksStreamerAPI, tracksStreamerAPI, normalizeTracksSearchResults } from './tracks-api.js';
|
||||
import { getCommunityPlaylist } from './community-playlists.js';
|
||||
|
||||
/**
|
||||
@@ -169,7 +165,8 @@ export class MusicAPI {
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.name === 'AbortError') throw error;
|
||||
if (import.meta.env.DEV) console.warn('[searchTracks] Tracks Streamer unavailable, trying fallbacks', error);
|
||||
if (import.meta.env.DEV)
|
||||
console.warn('[searchTracks] Tracks Streamer unavailable, trying fallbacks', error);
|
||||
}
|
||||
return this.searchSection('tracks', 'songs', query, options, () => this.getAPI().searchTracks(query, options));
|
||||
}
|
||||
@@ -185,7 +182,8 @@ export class MusicAPI {
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.name === 'AbortError') throw error;
|
||||
if (import.meta.env.DEV) console.warn('[searchArtists] Tracks Streamer unavailable, trying fallbacks', error);
|
||||
if (import.meta.env.DEV)
|
||||
console.warn('[searchArtists] Tracks Streamer unavailable, trying fallbacks', error);
|
||||
}
|
||||
return this.searchSection('artists', 'artists', query, options, () =>
|
||||
this.getAPI().searchArtists(query, options)
|
||||
@@ -203,7 +201,8 @@ export class MusicAPI {
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.name === 'AbortError') throw error;
|
||||
if (import.meta.env.DEV) console.warn('[searchAlbums] Tracks Streamer unavailable, trying fallbacks', error);
|
||||
if (import.meta.env.DEV)
|
||||
console.warn('[searchAlbums] Tracks Streamer unavailable, trying fallbacks', error);
|
||||
}
|
||||
return this.searchSection('albums', 'albums', query, options, () => this.getAPI().searchAlbums(query, options));
|
||||
}
|
||||
@@ -488,10 +487,7 @@ export class MusicAPI {
|
||||
|
||||
// Stream methods
|
||||
async getStreamUrl(id, quality, options = {}) {
|
||||
let track =
|
||||
options?.track ||
|
||||
this.getCachedTracksTrack(id) ||
|
||||
this.getCachedAppleTrack(id);
|
||||
let track = options?.track || this.getCachedTracksTrack(id) || this.getCachedAppleTrack(id);
|
||||
|
||||
if (!track && (this.isTracksId(id) || this.isAppleId(id) || /^\d{17,20}$/.test(String(id)))) {
|
||||
track = await this.getTrackMetadata(id).catch(() => null);
|
||||
@@ -660,11 +656,7 @@ export class MusicAPI {
|
||||
if (type === 'artist' && this.tracksArtistIds.has(id)) return true;
|
||||
if (type === 'track' && this.tracksTrackCache.has(id)) return true;
|
||||
if (!type) {
|
||||
return (
|
||||
this.tracksTrackCache.has(id) ||
|
||||
this.tracksAlbumIds.has(id) ||
|
||||
this.tracksArtistIds.has(id)
|
||||
);
|
||||
return this.tracksTrackCache.has(id) || this.tracksAlbumIds.has(id) || this.tracksArtistIds.has(id);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -1509,7 +1509,6 @@ export class Player {
|
||||
let resolvedStreamInfo = await streamInfoPromise;
|
||||
if (this.playbackSequence !== currentSequence) return;
|
||||
|
||||
|
||||
streamUrl = resolvedStreamInfo.url;
|
||||
this.currentStreamInfo = resolvedStreamInfo;
|
||||
this.currentStreamProvider = resolvedStreamInfo.provider || null;
|
||||
|
||||
+18
-60
@@ -33,16 +33,9 @@ export function normalizeTracksTrack(item) {
|
||||
const trackId = String(item.trackId || item.id || '');
|
||||
const id = trackId;
|
||||
|
||||
const artistId =
|
||||
item.artistIds?.[0] ||
|
||||
item.artists?.[0]?.artistId ||
|
||||
item.artists?.[0]?.id ||
|
||||
'';
|
||||
const artistId = item.artistIds?.[0] || item.artists?.[0]?.artistId || item.artists?.[0]?.id || '';
|
||||
const artistName =
|
||||
item.artistNames?.[0] ||
|
||||
item.artists?.[0]?.name ||
|
||||
item.artists?.[0]?.displayName ||
|
||||
'Unknown Artist';
|
||||
item.artistNames?.[0] || item.artists?.[0]?.name || item.artists?.[0]?.displayName || 'Unknown Artist';
|
||||
|
||||
const primaryArtist = {
|
||||
id: artistId ? String(artistId) : '',
|
||||
@@ -86,11 +79,7 @@ export function normalizeTracksTrack(item) {
|
||||
|
||||
const releaseId = item.releaseId ? String(item.releaseId) : '';
|
||||
const artwork = item.artwork || item.cover || '';
|
||||
const albumTitle =
|
||||
item.albumTitle ||
|
||||
item.releaseTitle ||
|
||||
(item.release && item.release.title) ||
|
||||
'';
|
||||
const albumTitle = item.albumTitle || item.releaseTitle || (item.release && item.release.title) || '';
|
||||
|
||||
const album = {
|
||||
id: releaseId,
|
||||
@@ -148,16 +137,9 @@ export function normalizeTracksRelease(item) {
|
||||
const releaseId = String(item.releaseId || item.id || '');
|
||||
const id = releaseId;
|
||||
|
||||
const artistId =
|
||||
item.artistIds?.[0] ||
|
||||
item.artists?.[0]?.artistId ||
|
||||
item.artists?.[0]?.id ||
|
||||
'';
|
||||
const artistId = item.artistIds?.[0] || item.artists?.[0]?.artistId || item.artists?.[0]?.id || '';
|
||||
const artistName =
|
||||
item.artistNames?.[0] ||
|
||||
item.artists?.[0]?.name ||
|
||||
item.artists?.[0]?.displayName ||
|
||||
'Unknown Artist';
|
||||
item.artistNames?.[0] || item.artists?.[0]?.name || item.artists?.[0]?.displayName || 'Unknown Artist';
|
||||
|
||||
const primaryArtist = {
|
||||
id: artistId ? String(artistId) : '',
|
||||
@@ -186,9 +168,7 @@ export function normalizeTracksRelease(item) {
|
||||
: [primaryArtist];
|
||||
|
||||
const artwork = item.artwork || item.cover || '';
|
||||
const trackCount =
|
||||
item.trackCount ||
|
||||
(Array.isArray(item.tracks) ? item.tracks.length : 0);
|
||||
const trackCount = item.trackCount || (Array.isArray(item.tracks) ? item.tracks.length : 0);
|
||||
|
||||
return {
|
||||
id,
|
||||
@@ -251,10 +231,7 @@ export function normalizeTracksPlaylist(item) {
|
||||
if (!item) return null;
|
||||
const playlistId = String(item.playlistId || item.id || '');
|
||||
const id = playlistId;
|
||||
const image =
|
||||
typeof item.thumbnail === 'string' && item.thumbnail.startsWith('http')
|
||||
? item.thumbnail
|
||||
: '';
|
||||
const image = typeof item.thumbnail === 'string' && item.thumbnail.startsWith('http') ? item.thumbnail : '';
|
||||
|
||||
return {
|
||||
id,
|
||||
@@ -336,9 +313,7 @@ export function extractTracksSuggestions(results, query) {
|
||||
seen.add(normalizedQuery);
|
||||
}
|
||||
|
||||
const rawTracks = Array.isArray(results)
|
||||
? results
|
||||
: results?.tracks?.items || results?.tracks || [];
|
||||
const rawTracks = Array.isArray(results) ? results : results?.tracks?.items || results?.tracks || [];
|
||||
|
||||
for (const raw of rawTracks) {
|
||||
if (!raw) continue;
|
||||
@@ -387,24 +362,18 @@ export function scoreTrackCandidate(candidate, target) {
|
||||
if (targetTitleClean && candidateTitleClean) {
|
||||
if (targetTitleClean === candidateTitleClean) {
|
||||
score += 100;
|
||||
} else if (
|
||||
targetTitleClean.includes(candidateTitleClean) ||
|
||||
candidateTitleClean.includes(targetTitleClean)
|
||||
) {
|
||||
} else if (targetTitleClean.includes(candidateTitleClean) || candidateTitleClean.includes(targetTitleClean)) {
|
||||
score += 65;
|
||||
}
|
||||
}
|
||||
|
||||
const targetArtistName = cleanString(
|
||||
target.artist?.name || target.artists?.[0]?.name || target.artist || ''
|
||||
);
|
||||
const candidateArtistNames = (
|
||||
candidate.artistNames || [candidate.artist?.name || '']
|
||||
).map(cleanString);
|
||||
const targetArtistName = cleanString(target.artist?.name || target.artists?.[0]?.name || target.artist || '');
|
||||
const candidateArtistNames = (candidate.artistNames || [candidate.artist?.name || '']).map(cleanString);
|
||||
|
||||
if (targetArtistName) {
|
||||
const hasArtistMatch = candidateArtistNames.some(
|
||||
(cName) => cName === targetArtistName || cName.includes(targetArtistName) || targetArtistName.includes(cName)
|
||||
(cName) =>
|
||||
cName === targetArtistName || cName.includes(targetArtistName) || targetArtistName.includes(cName)
|
||||
);
|
||||
if (hasArtistMatch) {
|
||||
score += 50;
|
||||
@@ -412,14 +381,9 @@ export function scoreTrackCandidate(candidate, target) {
|
||||
}
|
||||
|
||||
// Check duration similarity (within 6 seconds)
|
||||
const targetDur =
|
||||
target.duration > 1000
|
||||
? Math.round(target.duration / 1000)
|
||||
: Math.round(target.duration || 0);
|
||||
const targetDur = target.duration > 1000 ? Math.round(target.duration / 1000) : Math.round(target.duration || 0);
|
||||
const candDur =
|
||||
candidate.duration > 1000
|
||||
? Math.round(candidate.duration / 1000)
|
||||
: Math.round(candidate.duration || 0);
|
||||
candidate.duration > 1000 ? Math.round(candidate.duration / 1000) : Math.round(candidate.duration || 0);
|
||||
|
||||
if (targetDur > 0 && candDur > 0) {
|
||||
const diff = Math.abs(targetDur - candDur);
|
||||
@@ -460,9 +424,7 @@ export class TracksStreamerAPI {
|
||||
});
|
||||
|
||||
if (
|
||||
(response.status === 502 ||
|
||||
response.status === 503 ||
|
||||
response.status === 504) &&
|
||||
(response.status === 502 || response.status === 503 || response.status === 504) &&
|
||||
attempt < retries
|
||||
) {
|
||||
await new Promise((r) => setTimeout(r, 200 * (attempt + 1)));
|
||||
@@ -854,12 +816,8 @@ export class TracksStreamerAPI {
|
||||
* @returns {Promise<Object|null>} Stream info or null if unresolved
|
||||
*/
|
||||
async resolveTrackStream(idOrTrack, quality = 'LOSSLESS', options = {}) {
|
||||
const inputTrack =
|
||||
options.track || (typeof idOrTrack === 'object' ? idOrTrack : null);
|
||||
const id =
|
||||
typeof idOrTrack === 'string'
|
||||
? idOrTrack
|
||||
: String(inputTrack?.id || '');
|
||||
const inputTrack = options.track || (typeof idOrTrack === 'object' ? idOrTrack : null);
|
||||
const id = typeof idOrTrack === 'string' ? idOrTrack : String(inputTrack?.id || '');
|
||||
|
||||
// 1. Direct match: Already a tracks.monochrome.st track
|
||||
if (inputTrack?.tracksTrackId || inputTrack?.recordingId) {
|
||||
|
||||
@@ -3691,10 +3691,8 @@ export class UIRenderer {
|
||||
`Community Year-End ${year}${communityAgg.totalLists ? ` · ${communityAgg.totalLists} lists` : ''}`,
|
||||
communityAgg.items
|
||||
);
|
||||
if (topRated?.items?.length)
|
||||
this.renderAOTYRankedRows(contentDiv, `Top Rated ${year}`, topRated.items);
|
||||
if (!contentDiv.children.length)
|
||||
contentDiv.innerHTML = createPlaceholder('No chart data found.');
|
||||
if (topRated?.items?.length) this.renderAOTYRankedRows(contentDiv, `Top Rated ${year}`, topRated.items);
|
||||
if (!contentDiv.children.length) contentDiv.innerHTML = createPlaceholder('No chart data found.');
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
contentDiv.innerHTML = createPlaceholder('Failed to load charts.');
|
||||
@@ -3872,7 +3870,15 @@ export class UIRenderer {
|
||||
this.renderAOTYSection(contentDiv, `Albums matching “${q.trim()}”`, data.albums);
|
||||
return;
|
||||
}
|
||||
const rows = data.artists || data.labels || data.lists || data.items || data.news || data.tags || data.users || [];
|
||||
const rows =
|
||||
data.artists ||
|
||||
data.labels ||
|
||||
data.lists ||
|
||||
data.items ||
|
||||
data.news ||
|
||||
data.tags ||
|
||||
data.users ||
|
||||
[];
|
||||
if (!rows.length) {
|
||||
contentDiv.innerHTML = createPlaceholder('No results found.');
|
||||
return;
|
||||
@@ -3920,7 +3926,10 @@ export class UIRenderer {
|
||||
};
|
||||
container.querySelector('#aoty-search-form').addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
void runSearch(container.querySelector('#aoty-search-q').value, container.querySelector('#aoty-search-scope').value);
|
||||
void runSearch(
|
||||
container.querySelector('#aoty-search-q').value,
|
||||
container.querySelector('#aoty-search-scope').value
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4031,11 +4040,7 @@ export class UIRenderer {
|
||||
</div>`;
|
||||
|
||||
row.querySelector('.row-title').textContent = itemTitle || item.title || '';
|
||||
const metaParts = [
|
||||
itemArtist,
|
||||
item.date,
|
||||
...(item.genres?.slice(0, 2) || []),
|
||||
].filter(Boolean);
|
||||
const metaParts = [itemArtist, item.date, ...(item.genres?.slice(0, 2) || [])].filter(Boolean);
|
||||
row.querySelector('.row-meta').textContent = metaParts.join(' · ');
|
||||
row.appendChild(scoreEl);
|
||||
if (embeddedScore == null) scoreTargets.push({ item, scoreEl });
|
||||
|
||||
+6
-1
@@ -1562,7 +1562,12 @@
|
||||
</p>
|
||||
<div style="margin-bottom: 1rem">
|
||||
<label style="display: block; margin-bottom: 0.5rem; font-size: 0.9rem">PocketBase URL</label>
|
||||
<input type="url" id="custom-pb-url" class="template-input" placeholder="https://data.monochrome.st" />
|
||||
<input
|
||||
type="url"
|
||||
id="custom-pb-url"
|
||||
class="template-input"
|
||||
placeholder="https://data.monochrome.st"
|
||||
/>
|
||||
</div>
|
||||
<div style="margin-bottom: 1rem">
|
||||
<label style="display: block; margin-bottom: 0.5rem; font-size: 0.9rem">Auth Endpoint</label>
|
||||
|
||||
Reference in new issue
Block a user