file-show: read id3 tags
Massimo Melina committed
Feb 7, 2024 at 22:22 UTC
b8fc648926cc77d91f71879a8ebd6a84e17f4c3d
3 files changed
+26
-11
frontend/src/show.ts
+15
-10
@@ -1,6 +1,7 @@
1
import { DirEntry, DirList, ext2type, state, useSnapState } from './state'
2
import { createElement as h, Fragment, useEffect, useRef, useState } from 'react'
3
-import { basename, dirname, domOn, hfsEvent, hIcon, isMac, newDialog, pathEncode, restartAnimation } from './misc'
3
+import { basename, dirname, domOn, hfsEvent, hIcon, isMac, newDialog, pathEncode, restartAnimation, getOrSet,
4
+ loadJs} from './misc'
5
import { useEventListener, useWindowSize } from 'usehooks-ts'
6
import { EntryDetails, useMidnight } from './BrowseFiles'
7
import { Btn, FlexV, iconBtn, Spinner } from './components'
@@ -139,16 +140,18 @@ export function fileShow(entry: DirEntry, { startPlaying=false } = {}) {
140
setLoading(false)
141
},
142
onError: curFailed,
142
- onPlay() {
143
+ async onPlay() {
144
const folder = dirname(cur.n)
145
const covers = state.list.filter(x => folder === dirname(x.n) // same folder
146
&& x.name.match(/(?:folder|cover|albumart.*)\.jpe?g$/i))
147
setCover(_.maxBy(covers, 's')?.n || '')
147
- navigator.mediaSession.metadata = new MediaMetadata({
148
+ const meta = navigator.mediaSession.metadata = new MediaMetadata({
149
title: cur.name,
150
album: decodeURIComponent(basename(dirname(cur.uri))),
151
artwork: covers.map(x => ({ src: x.n }))
152
})
153
+ if (cur.ext === 'mp3')
154
+ Object.assign(meta, await getId3Tags(location + cur.n).catch(() => {}))
155
}
156
})
157
),
@@ -289,12 +292,14 @@ function showHelp() {
292
})
293
}
294
292
-function loadJs(url: string) {
293
- return new Promise((resolve, reject) => {
294
- const el = document.createElement('script')
295
- el.setAttribute('src', url)
296
- document.head.appendChild(el)
297
- el.addEventListener('load', resolve)
298
- el.addEventListener('error', reject)
295
+function getId3Tags(url: string) {
296
+ return new Promise(async (resolve, reject) => {
297
+ const tagLib = (window as any).jsmediatags
298
+ || await loadJs('https://cdnjs.cloudflare.com/ajax/libs/jsmediatags/3.9.5/jsmediatags.min.js')
299
+ .then(() => (window as any).jsmediatags)
300
+ tagLib.read(url, {
301
+ onSuccess: (res: any) => resolve(res.tags),
302
+ onError: reject
303
+ })
304
})
305
}
\ No newline at end of file
shared/index.ts
+10
@@ -104,6 +104,16 @@ export function disableConsoleDebug() {
104
console.debug = (...args) => (window as any).DEV && was(...args)
105
}
106
107
+export function loadJs(url: string) {
108
+ return new Promise((resolve, reject) => {
109
+ const el = document.createElement('script')
110
+ el.setAttribute('src', url)
111
+ document.head.appendChild(el)
112
+ el.addEventListener('load', resolve)
113
+ el.addEventListener('error', reject)
114
+ })
115
+}
116
+
117
type DurationUnit = 'day' | 'hour' | 'minute' | 'second'
118
export function createDurationFormatter({ locale=undefined, unitDisplay='narrow', largest='day', smallest='second', maxTokens, skipZeroes }:
119
{ skipZeroes?: boolean, largest?: DurationUnit, smallest?: DurationUnit, locale?: string, unitDisplay?: 'long' | 'short' | 'narrow', maxTokens?: 1 | 2 | 3 }={}) {
src/serveFile.ts
+1
-1
@@ -125,7 +125,7 @@ export function applyRange(ctx: Koa.Context, totalSize=ctx.response.length) {
125
return ctx.throw(HTTP_BAD_REQUEST, 'bad range')
126
const max = totalSize - 1
127
const start = bytes[0] ? Number(bytes[0]) : Math.max(0, totalSize-Number(bytes[1])) // a negative start is relative to the end
128
- const end = bytes[0] ? Number(bytes[1] || max) : max
128
+ const end = (bytes[0] && bytes[1]) ? Math.min(max, Number(bytes[1])) : max
129
// we don't support last-bytes without knowing max
130
if (isNaN(end) && isNaN(max) || end > max || start > max) {
131
ctx.status = HTTP_RANGE_NOT_SATISFIABLE