@samitouri / QOSami-HFS / commits / 232828ca

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 }={}) {