feat: release version 1.0.5
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"source": "https://dreams.wf-calc.net",
|
||||
"source": "https://dreams.wf-calc.net; hololive Dreams CDN via https://github.com/HolodoriDB/holodori-asset-tools",
|
||||
"charts": [
|
||||
{
|
||||
"songId": "m0001",
|
||||
@@ -4712,6 +4712,58 @@
|
||||
],
|
||||
"path": "/data/charts/m0331.hard.json"
|
||||
},
|
||||
{
|
||||
"songId": "m0332",
|
||||
"title": "BANZAI☆MANKAI",
|
||||
"difficulty": "expert",
|
||||
"durationSeconds": 136,
|
||||
"scoreRatio": 280.40008469873237,
|
||||
"totalNotes": 938,
|
||||
"singerType": "solo",
|
||||
"singerCharacterIds": [
|
||||
"chr-00010"
|
||||
],
|
||||
"path": "/data/charts/m0332.expert.json"
|
||||
},
|
||||
{
|
||||
"songId": "m0332",
|
||||
"title": "BANZAI☆MANKAI",
|
||||
"difficulty": "hard",
|
||||
"durationSeconds": 136,
|
||||
"scoreRatio": 220.44697428340993,
|
||||
"totalNotes": 760,
|
||||
"singerType": "solo",
|
||||
"singerCharacterIds": [
|
||||
"chr-00010"
|
||||
],
|
||||
"path": "/data/charts/m0332.hard.json"
|
||||
},
|
||||
{
|
||||
"songId": "m0333",
|
||||
"title": "Takoyaki Gazing",
|
||||
"difficulty": "expert",
|
||||
"durationSeconds": 131,
|
||||
"scoreRatio": 299.0414358467602,
|
||||
"totalNotes": 888,
|
||||
"singerType": "solo",
|
||||
"singerCharacterIds": [
|
||||
"chr-00028"
|
||||
],
|
||||
"path": "/data/charts/m0333.expert.json"
|
||||
},
|
||||
{
|
||||
"songId": "m0333",
|
||||
"title": "Takoyaki Gazing",
|
||||
"difficulty": "hard",
|
||||
"durationSeconds": 131,
|
||||
"scoreRatio": 209.7523345809546,
|
||||
"totalNotes": 624,
|
||||
"singerType": "solo",
|
||||
"singerCharacterIds": [
|
||||
"chr-00028"
|
||||
],
|
||||
"path": "/data/charts/m0333.hard.json"
|
||||
},
|
||||
{
|
||||
"songId": "m0334",
|
||||
"title": "NIGHTBREAK",
|
||||
@@ -4738,6 +4790,58 @@
|
||||
],
|
||||
"path": "/data/charts/m0334.hard.json"
|
||||
},
|
||||
{
|
||||
"songId": "m0335",
|
||||
"title": "Oshi Mode ON",
|
||||
"difficulty": "expert",
|
||||
"durationSeconds": 129,
|
||||
"scoreRatio": 418.6877175179705,
|
||||
"totalNotes": 1173,
|
||||
"singerType": "solo",
|
||||
"singerCharacterIds": [
|
||||
"chr-03004"
|
||||
],
|
||||
"path": "/data/charts/m0335.expert.json"
|
||||
},
|
||||
{
|
||||
"songId": "m0335",
|
||||
"title": "Oshi Mode ON",
|
||||
"difficulty": "hard",
|
||||
"durationSeconds": 129,
|
||||
"scoreRatio": 250.28427636994522,
|
||||
"totalNotes": 753,
|
||||
"singerType": "solo",
|
||||
"singerCharacterIds": [
|
||||
"chr-03004"
|
||||
],
|
||||
"path": "/data/charts/m0335.hard.json"
|
||||
},
|
||||
{
|
||||
"songId": "m0336",
|
||||
"title": "Stardust Capsule",
|
||||
"difficulty": "expert",
|
||||
"durationSeconds": 117,
|
||||
"scoreRatio": 250.46994719811275,
|
||||
"totalNotes": 879,
|
||||
"singerType": "solo",
|
||||
"singerCharacterIds": [
|
||||
"chr-04003"
|
||||
],
|
||||
"path": "/data/charts/m0336.expert.json"
|
||||
},
|
||||
{
|
||||
"songId": "m0336",
|
||||
"title": "Stardust Capsule",
|
||||
"difficulty": "hard",
|
||||
"durationSeconds": 117,
|
||||
"scoreRatio": 152.06440826927707,
|
||||
"totalNotes": 611,
|
||||
"singerType": "solo",
|
||||
"singerCharacterIds": [
|
||||
"chr-04003"
|
||||
],
|
||||
"path": "/data/charts/m0336.hard.json"
|
||||
},
|
||||
{
|
||||
"songId": "m0337",
|
||||
"title": "美少女戦士まつりみっとZero",
|
||||
@@ -4816,6 +4920,32 @@
|
||||
],
|
||||
"path": "/data/charts/m0347.hard.json"
|
||||
},
|
||||
{
|
||||
"songId": "m0353",
|
||||
"title": "Night Loop",
|
||||
"difficulty": "expert",
|
||||
"durationSeconds": 134,
|
||||
"scoreRatio": 327.3933713079548,
|
||||
"totalNotes": 953,
|
||||
"singerType": "solo",
|
||||
"singerCharacterIds": [
|
||||
"chr-00014"
|
||||
],
|
||||
"path": "/data/charts/m0353.expert.json"
|
||||
},
|
||||
{
|
||||
"songId": "m0353",
|
||||
"title": "Night Loop",
|
||||
"difficulty": "hard",
|
||||
"durationSeconds": 134,
|
||||
"scoreRatio": 204.12650848747597,
|
||||
"totalNotes": 641,
|
||||
"singerType": "solo",
|
||||
"singerCharacterIds": [
|
||||
"chr-00014"
|
||||
],
|
||||
"path": "/data/charts/m0353.hard.json"
|
||||
},
|
||||
{
|
||||
"songId": "m0524",
|
||||
"title": "You & 合図",
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
export type ReleaseClockSource = 'server' | 'system';
|
||||
|
||||
export type ReleaseClock = {
|
||||
source: ReleaseClockSource;
|
||||
now: () => number;
|
||||
};
|
||||
|
||||
type TimeResponse = {
|
||||
now?: unknown;
|
||||
};
|
||||
|
||||
type ReleaseClockOptions = {
|
||||
fetcher?: typeof fetch;
|
||||
wallNow?: () => number;
|
||||
monotonicNow?: () => number;
|
||||
online?: boolean;
|
||||
timeoutMs?: number;
|
||||
};
|
||||
|
||||
function defaultMonotonicNow(): number {
|
||||
return typeof performance === 'undefined' ? Date.now() : performance.now();
|
||||
}
|
||||
|
||||
function serverClock(serverNow: number, monotonicNow: () => number, baseMonotonic = monotonicNow()): ReleaseClock {
|
||||
return {
|
||||
source: 'server',
|
||||
now: () => serverNow + Math.max(0, monotonicNow() - baseMonotonic)
|
||||
};
|
||||
}
|
||||
|
||||
function systemClock(wallNow: () => number): ReleaseClock {
|
||||
return { source: 'system', now: wallNow };
|
||||
}
|
||||
|
||||
export function createInitialReleaseClock(serverNow: unknown, options: Pick<ReleaseClockOptions, 'wallNow' | 'monotonicNow'> = {}): ReleaseClock {
|
||||
const wallNow = options.wallNow ?? Date.now;
|
||||
const monotonicNow = options.monotonicNow ?? defaultMonotonicNow;
|
||||
return typeof serverNow === 'number' && Number.isFinite(serverNow)
|
||||
? serverClock(serverNow, monotonicNow)
|
||||
: systemClock(wallNow);
|
||||
}
|
||||
|
||||
export async function synchronizeReleaseClock(options: ReleaseClockOptions = {}): Promise<ReleaseClock> {
|
||||
const wallNow = options.wallNow ?? Date.now;
|
||||
const monotonicNow = options.monotonicNow ?? defaultMonotonicNow;
|
||||
const online = options.online ?? (typeof location === 'undefined' || location.protocol !== 'file:');
|
||||
if (!online) return systemClock(wallNow);
|
||||
|
||||
const fetcher = options.fetcher ?? globalThis.fetch?.bind(globalThis);
|
||||
if (!fetcher) return systemClock(wallNow);
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), options.timeoutMs ?? 3_000);
|
||||
const requestStarted = monotonicNow();
|
||||
try {
|
||||
const response = await fetcher('/api/time', {
|
||||
cache: 'no-store',
|
||||
headers: { accept: 'application/json' },
|
||||
signal: controller.signal
|
||||
});
|
||||
if (!response.ok) return systemClock(wallNow);
|
||||
const payload = await response.json() as TimeResponse;
|
||||
if (typeof payload.now !== 'number' || !Number.isFinite(payload.now)) return systemClock(wallNow);
|
||||
|
||||
const responseReceived = monotonicNow();
|
||||
const estimatedNow = payload.now + Math.max(0, responseReceived - requestStarted) / 2;
|
||||
return serverClock(estimatedNow, monotonicNow, responseReceived);
|
||||
} catch {
|
||||
return systemClock(wallNow);
|
||||
} finally {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
export const SONG_RELEASE_TIMES: Readonly<Record<string, number>> = {
|
||||
m0332: 1_787_137_200_000,
|
||||
m0353: 1_787_151_600_000,
|
||||
m0335: 1_787_310_000_000,
|
||||
m0333: 1_787_482_800_000,
|
||||
m0336: 1_787_655_600_000
|
||||
};
|
||||
|
||||
export function isSongReleased(songId: string, now = Date.now()): boolean {
|
||||
const releaseTime = SONG_RELEASE_TIMES[songId];
|
||||
return releaseTime === undefined || now >= releaseTime;
|
||||
}
|
||||
|
||||
export function releasedSongCharts<T extends { songId: string }>(charts: readonly T[], now = Date.now()): T[] {
|
||||
return charts.filter((chart) => isSongReleased(chart.songId, now));
|
||||
}
|
||||
Reference in New Issue
Block a user