chore: update public repo

This commit is contained in:
desu-bot 2025-04-09 04:20:08 +00:00
parent 874e1952e3
commit ce9f435ef2
No known key found for this signature in database
3 changed files with 70 additions and 26 deletions

View file

@ -5,7 +5,7 @@ import { join } from 'node:path'
import kuromoji from 'kuromoji' import kuromoji from 'kuromoji'
import { isKana, toRomaji } from 'wanakana' import { isKana, toRomaji } from 'wanakana'
import { fetchSongs, navidromeFfetch as ffetch } from '../../utils/navidrome.ts' import { fetchSongs, fetchSongsIter } from '../../utils/navidrome.ts'
const WHITELIST_KEYS = new Set([ const WHITELIST_KEYS = new Set([
// actual different tracks with the same title // actual different tracks with the same title
@ -53,8 +53,6 @@ function clean(s: string) {
return str return str
} }
const CHUNK_SIZE = 1000
function getSongKey(song: NavidromeSong) { function getSongKey(song: NavidromeSong) {
return JSON.stringify([ return JSON.stringify([
clean(song.artist), clean(song.artist),
@ -64,23 +62,20 @@ function getSongKey(song: NavidromeSong) {
const seen = new Map<string, NavidromeSong[]>() const seen = new Map<string, NavidromeSong[]>()
for (let offset = 0; ; offset += CHUNK_SIZE) { for await (const song of fetchSongsIter({
const songs = await fetchSongs(offset, CHUNK_SIZE) onChunkProcessed: (page, items) => {
if (songs.length === 0) break console.log('⌛ fetched chunk %d (%d items)', page, items)
},
for (const song of songs) { })) {
const key = getSongKey(song) const key = getSongKey(song)
if (WHITELIST_KEYS.has(key)) continue if (WHITELIST_KEYS.has(key)) continue
let arr = seen.get(key) let arr = seen.get(key)
if (!arr) { if (!arr) {
arr = [] arr = []
seen.set(key, arr) seen.set(key, arr)
}
arr.push(song)
} }
console.log('⌛ fetched chunk %d (%d items)', Math.floor(offset / CHUNK_SIZE), songs.length) arr.push(song)
} }
const keysSorted = Array.from(seen.keys()).sort() const keysSorted = Array.from(seen.keys()).sort()

View file

@ -0,0 +1,18 @@
import { fetchSongs, fetchSongsIter } from '../../utils/navidrome.ts'
let count = 0
let totalSize = 0
let totalDuration = 0
console.log('⌛ fetching songs...')
for await (const song of fetchSongsIter()) {
count += 1
totalSize += song.size
totalDuration += song.duration
}
console.log('---')
console.log('total songs: %d', count)
console.log('total size: %d GiB', (totalSize / 1024 / 1024 / 1024).toFixed(3))
console.log('total duration: %d min (%d h)', (totalDuration / 60).toFixed(3), (totalDuration / 60 / 60).toFixed(3))

View file

@ -2,12 +2,26 @@ import { z } from 'zod'
import { ffetch as ffetchBase } from './fetch.ts' import { ffetch as ffetchBase } from './fetch.ts'
import { getEnv } from './misc.ts' import { getEnv } from './misc.ts'
export const navidromeFfetch = ffetchBase.extend({ let _cachedFfetch: typeof ffetchBase | undefined
baseUrl: getEnv('NAVIDROME_ENDPOINT'), export async function getNavidromeFfetch() {
headers: { if (_cachedFfetch) return _cachedFfetch
'x-nd-authorization': `Bearer ${getEnv('NAVIDROME_TOKEN')}`, const baseUrl = getEnv('NAVIDROME_ENDPOINT')
}, const authRes = await ffetchBase.post('/auth/login', {
}) baseUrl,
json: {
username: getEnv('NAVIDROME_USERNAME'),
password: getEnv('NAVIDROME_PASSWORD'),
},
}).parsedJson(z.object({ token: z.string() }))
_cachedFfetch = ffetchBase.extend({
baseUrl,
headers: {
'x-nd-authorization': `Bearer ${authRes.token}`,
},
})
return _cachedFfetch
}
export const NavidromeSong = z.object({ export const NavidromeSong = z.object({
id: z.string(), id: z.string(),
@ -17,11 +31,13 @@ export const NavidromeSong = z.object({
artist: z.string(), artist: z.string(),
path: z.string(), path: z.string(),
duration: z.number(), duration: z.number(),
size: z.number(),
}) })
export type NavidromeSong = z.infer<typeof NavidromeSong> export type NavidromeSong = z.infer<typeof NavidromeSong>
export function fetchSongs(offset: number, pageSize: number) { export async function fetchSongs(offset: number, pageSize: number) {
return navidromeFfetch('/api/song', { const api = await getNavidromeFfetch()
return api('/api/song', {
query: { query: {
_start: offset, _start: offset,
_end: offset + pageSize, _end: offset + pageSize,
@ -30,3 +46,18 @@ export function fetchSongs(offset: number, pageSize: number) {
}, },
}).parsedJson(z.array(NavidromeSong)) }).parsedJson(z.array(NavidromeSong))
} }
export async function* fetchSongsIter(params?: {
chunkSize?: number
onChunkProcessed?: (page: number, items: number) => void
}) {
const { chunkSize = 1000, onChunkProcessed } = params ?? {}
for (let offset = 0; ; offset += chunkSize) {
const songs = await fetchSongs(offset, chunkSize)
if (songs.length === 0) return
yield * songs
onChunkProcessed?.(Math.floor(offset / chunkSize), songs.length)
}
}