Experiment to rebuild Diffuse using web applets.
0
fork

Configure Feed

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

at cb8a125c0ec7af04dfca6d31ff6bb38b2cd890bc 149 lines 4.6 kB view raw
1import * as URI from "uri-js"; 2 3import type { Consult, ConsultGrouping, GroupConsult, Track } from "@applets/core/types.d.ts"; 4import { SCHEME } from "./constants"; 5import { 6 fetchHandles, 7 fetchHandlesList, 8 groupTracksByHandle, 9 recursiveList, 10 trackHandleId, 11} from "./common"; 12import { expose, transfer } from "@scripts/common"; 13 14//////////////////////////////////////////// 15// ACTIONS 16//////////////////////////////////////////// 17const actions = expose({ 18 consult, 19 contextualize, 20 groupConsult, 21 list, 22 resolve, 23}); 24 25export type Actions = typeof actions; 26 27// Actions 28 29export async function consult(fileUriOrScheme: string): Promise<Consult> { 30 if (!self.FileSystemDirectoryHandle) { 31 return { supported: false, reason: "File System Access API is not supported" }; 32 } 33 34 if (!fileUriOrScheme.includes(":")) { 35 if (fileUriOrScheme !== SCHEME) return { supported: false, reason: "Scheme does not match" }; 36 return { supported: true, consult: "undetermined" }; 37 } 38 39 const handles = await fetchHandles(); 40 const uri = URI.parse(fileUriOrScheme); 41 if (uri.scheme !== SCHEME) return { supported: false, reason: "Scheme does not match" }; 42 return { supported: true, consult: uri.host && !!handles[uri.host] ? true : false }; 43} 44 45export async function contextualize(cachedTracks: Track[]) {} 46 47async function groupConsult(tracks: Track[]): Promise<GroupConsult> { 48 const groups = groupTracksByHandle(tracks); 49 const handles = await fetchHandles(); 50 51 const promises = Object.entries(groups).map(async ([handleId, { tracks }]) => { 52 const handle = handles[handleId]; 53 const grouping: ConsultGrouping = handle 54 ? { available: true, tracks } 55 : { available: false, reason: "Handle not available" }; 56 57 return { 58 key: URI.serialize({ scheme: SCHEME, host: handleId }), 59 grouping, 60 }; 61 }); 62 63 const entries = (await Promise.all(promises)).map((entry) => [entry.key, entry.grouping]); 64 const obj = Object.fromEntries(entries); 65 66 return transfer(obj); 67} 68 69export async function list(cachedTracks: Track[] = []) { 70 const handles = await fetchHandlesList(); 71 72 // Recursive listing of all tracks of available handles 73 const processed: Track[][] = await Promise.all( 74 handles.map(({ id, handle }) => { 75 return recursiveList(handle, id, []); 76 }), 77 ); 78 79 // Group tracks by handle id & index by track uri 80 const cache = cachedTracks.reduce((acc: Record<string, Record<string, Track>>, track: Track) => { 81 const handleId = trackHandleId(track); 82 if (!handleId) return acc; 83 84 return { ...acc, [handleId]: { ...(acc[handleId] || {}), [track.uri]: track } }; 85 }, {}); 86 87 // Replace indexes in groups of which we have the handle. 88 // Keeping around tracks with handles we don't have access to, 89 // and removing tracks that are no longer available (for handles we do have access to). 90 const groups = processed.flat(1).reduce( 91 (acc, track) => { 92 const handleId = trackHandleId(track); 93 if (!handleId) throw new Error("New tracks are missing a handle id!"); 94 95 return { ...acc, [handleId]: { ...acc[handleId], [track.uri]: track } }; 96 }, 97 handles.reduce((acc: Record<string, Record<string, Track>>, handle) => { 98 return { ...acc, [handle.id]: {} }; 99 }, cache), 100 ); 101 102 // Transform in track list and sort by uri 103 const data = Object.values(groups) 104 .map((tracks) => Object.values(tracks)) 105 .flat(1) 106 .sort((a: any, b: any) => { 107 if (a.uri < b.uri) return -1; 108 if (a.uri > b.uri) return 1; 109 return 0; 110 }); 111 112 // Fin 113 return transfer(data); 114} 115 116export async function resolve(args: { uri: string }) { 117 const fileUri = args.uri; 118 119 const uri = URI.parse(fileUri); 120 if (uri.scheme !== SCHEME) return undefined; 121 if (!uri.host || !uri.path) return undefined; 122 123 const handles = await fetchHandles(); 124 const handle = handles[uri.host]; 125 if (!handle) return undefined; 126 127 const path = URI.unescapeComponent(uri.path); 128 const parts = (path.startsWith("/") ? path.slice(1) : path).split("/"); 129 const filename = parts[parts.length - 1]; 130 131 const dirHandle = await parts 132 .slice(0, -1) 133 .reduce( 134 async ( 135 acc: Promise<FileSystemDirectoryHandle>, 136 part: string, 137 ): Promise<FileSystemDirectoryHandle> => { 138 const h = await acc; 139 return await h.getDirectoryHandle(part); 140 }, 141 Promise.resolve(handle), 142 ); 143 144 const fileHandle = await dirHandle.getFileHandle(filename); 145 const file = await fileHandle.getFile(); 146 const url = URL.createObjectURL(file); 147 148 return { expiresAt: Infinity, url }; 149}