mirror of
https://git.stupid.fish/teidesu/scripts.git
synced 2025-07-28 02:32:11 +10:00
chore: update public repo
This commit is contained in:
parent
874e1952e3
commit
ce9f435ef2
3 changed files with 70 additions and 26 deletions
|
@ -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()
|
||||||
|
|
18
scripts/infra/navidrome-stats.ts
Normal file
18
scripts/infra/navidrome-stats.ts
Normal 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))
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue