Experiment to rebuild Diffuse using web applets.
0

Configure Feed

Select the types of activity you want to include in your feed.

1import { parseFromTokenizer, parseWebStream } from "music-metadata"; 2import { contentType } from "@std/media-types"; 3import * as URI from "uri-js"; 4import * as HTTP_TOKENIZER from "@tokenizer/http"; 5import * as RANGE_TOKENIZER from "@tokenizer/range"; 6 7import type { TrackStats, TrackTags } from "@applets/core/types"; 8import type { Extraction, Urls } from "./types.d.ts"; 9import { expose } from "@scripts/common"; 10 11//////////////////////////////////////////// 12// ACTIONS 13//////////////////////////////////////////// 14const actions = expose({ 15 supply, 16}); 17 18export type Actions = typeof actions; 19 20// Actions 21 22async function supply(args: { 23 includeArtwork?: boolean; 24 mimeType?: string; 25 stream?: ReadableStream; 26 urls?: Urls; 27}): Promise<Extraction> { 28 // Construct records 29 // TODO: Use other metadata lib as fallback: https://github.com/buzz/mediainfo.js 30 const response = await musicMetadataTags(args).catch((err): Extraction => { 31 console.warn("Metadata processor error:", err); 32 console.log(args); 33 34 return {}; 35 }); 36 37 // Fin 38 return response; 39} 40 41//////////////////////////////////////////// 42// 🛠️ 43//////////////////////////////////////////// 44async function musicMetadataTags({ 45 includeArtwork, 46 mimeType, 47 stream, 48 urls, 49}: { 50 includeArtwork?: boolean; 51 mimeType?: string; 52 stream?: ReadableStream; 53 urls?: Urls; 54}): Promise<Extraction> { 55 const uri = urls ? URI.parse(urls.get) : undefined; 56 const pathParts = uri?.path?.split("/"); 57 const filename = pathParts?.[pathParts.length - 1]; 58 59 let meta; 60 61 if (urls?.get.startsWith("blob:")) { 62 const mimeFallback = filename?.includes(".") 63 ? contentType(filename.split(".").reverse()[0]) 64 : undefined; 65 66 const resp = await fetch(urls.get); 67 const stream = resp.body; 68 69 if (!stream) return {}; 70 meta = await parseWebStream( 71 stream, 72 { mimeType: mimeType || mimeFallback }, 73 { skipCovers: !includeArtwork }, 74 ); 75 } else if (urls) { 76 const httpClient = new HTTP_TOKENIZER.HttpClient(urls.head, { resolveUrl: false }); 77 httpClient.resolvedUrl = urls.get; 78 79 const tokenizer = await RANGE_TOKENIZER.tokenizer(httpClient); 80 81 meta = await parseFromTokenizer(tokenizer, { skipCovers: !includeArtwork }); 82 } else if (stream) { 83 meta = await parseWebStream(stream, { mimeType }, { skipCovers: !includeArtwork }); 84 } else { 85 throw new Error("Missing args, need either some urls or a stream."); 86 } 87 88 const stats: TrackStats = { 89 duration: meta.format.duration, 90 }; 91 92 const tags: TrackTags = { 93 album: meta.common.album, 94 artist: meta.common.artist, 95 disc: { no: meta.common.disk.no || 1, of: meta.common.disk.of ?? undefined }, 96 genre: Array.isArray(meta.common.genre) ? meta.common.genre[0] : meta.common.genre, 97 title: meta.common.title || filename || urls?.head || "Unknown", 98 track: { no: meta.common.track.no || 1, of: meta.common.track.of ?? undefined }, 99 year: meta.common.year, 100 }; 101 102 return { 103 artwork: includeArtwork ? meta.common.picture : undefined, 104 stats, 105 tags, 106 }; 107}