file-show: read id3 tags without external lib
Massimo Melina committed
Feb 8, 2024 at 11:54 UTC
232828cab522d00b7a096c67511e01ede0ca6db6
3 files changed
+21
-24
frontend/src/id3.ts
new
+19
@@ -0,0 +1,19 @@
1
+export async function getId3Tags(url: string) {
2
+ const buf = await fetch(url, { headers: { Range: 'bytes=0-2047' } }).then(x => x.arrayBuffer())
3
+ const dv = new DataView(buf)
4
+ if (dv.getUint32(0) !== 0x49443303) return // ID3 identifier
5
+ const tags: Record<string, string> = {}
6
+ let index = 10
7
+ while (index < buf.byteLength) {
8
+ const frameId = String.fromCharCode(dv.getUint8(index++), dv.getUint8(index++), dv.getUint8(index++), dv.getUint8(index++))
9
+ if (frameId === '\0\0\0\0') break
10
+ const frameSize = dv.getUint32(index)
11
+ index += 6 // skip size + flags
12
+ const enc = !dv.getUint8(index++) ? 'ISO-8859-1' // 1 is for unicode
13
+ : { 239: 'utf-8', 255: 'utf-16le', 254: 'utf-16be' }[dv.getUint8(index)] // decode bom
14
+ tags[frameId] = new TextDecoder(enc).decode(buf.slice(index, index += frameSize - 1))
15
+ }
16
+ for (const [k, v] of Object.entries({ TALB: 'album', TIT2: 'title', TPE1: 'artist', TYER: 'year', TRCK: 'track' })) // easier access to main fields
17
+ tags[v] = tags[k]
18
+ return tags
19
+}
frontend/src/show.ts
+2
-14
@@ -1,7 +1,6 @@
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, getOrSet,
4
- loadJs} from './misc'
3
+import { basename, dirname, domOn, hfsEvent, hIcon, isMac, newDialog, pathEncode, restartAnimation } from './misc'
4
import { useEventListener, useWindowSize } from 'usehooks-ts'
5
import { EntryDetails, useMidnight } from './BrowseFiles'
6
import { Btn, FlexV, iconBtn, Spinner } from './components'
@@ -9,6 +8,7 @@ import { openFileMenu } from './fileMenu'
8
import { t, useI18N } from './i18n'
9
import { alertDialog } from './dialog'
10
import _ from 'lodash'
11
+import { getId3Tags } from './id3'
12
13
enum ZoomMode {
14
fullWidth,
@@ -291,15 +291,3 @@ function showHelp() {
291
)
292
})
293
}
294
-
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,16 +104,6 @@ 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
-
107
type DurationUnit = 'day' | 'hour' | 'minute' | 'second'
108
export function createDurationFormatter({ locale=undefined, unitDisplay='narrow', largest='day', smallest='second', maxTokens, skipZeroes }:
109
{ skipZeroes?: boolean, largest?: DurationUnit, smallest?: DurationUnit, locale?: string, unitDisplay?: 'long' | 'short' | 'narrow', maxTokens?: 1 | 2 | 3 }={}) {