Skip to content

Commit

Permalink
Move some common file functions to allow them to be used without incl…
Browse files Browse the repository at this point in the history
…ude our browser code
  • Loading branch information
curiousdannii committed Oct 13, 2024
1 parent f173344 commit 29c8c84
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 48 deletions.
54 changes: 54 additions & 0 deletions src/common/file.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
Common file processing functions
================================
Copyright (c) 2024 Dannii Willis
MIT licenced
https://github.com/curiousdannii/asyncglk
*/

export type ProgressCallback = (bytes: number) => void

/** Parse Base 64 into a Uint8Array */
export async function parse_base64(data: string, data_type = 'octet-binary'): Promise<Uint8Array> {
// Parse base64 using a trick from https://stackoverflow.com/a/54123275/2854284
const response = await fetch(`data:application/${data_type};base64,${data}`)
if (!response.ok) {
throw new Error(`Could not parse base64: ${response.status}`)
}
return new Uint8Array(await response.arrayBuffer())
}

/** Read a response, with optional progress notifications */
export async function read_response(response: Response, progress_callback?: ProgressCallback): Promise<Uint8Array> {
if (!response.ok) {
throw new Error(`Could not fetch ${response.url}, got ${response.status}`)
}

if (!progress_callback) {
return new Uint8Array(await response.arrayBuffer())
}

// Read the response, calling the callback with each chunk
const chunks: Array<[number, Uint8Array]> = []
let length = 0
const reader = response.body!.getReader()
for (;;) {
const {done, value} = await reader.read()
if (done) {
break
}
chunks.push([length, value])
progress_callback(value.length)
length += value.length
}

// Join the chunks together
const result = new Uint8Array(length)
for (const [offset, chunk] of chunks) {
result.set(chunk, offset)
}
return result
}
3 changes: 2 additions & 1 deletion src/dialog/browser/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ https://github.com/curiousdannii/asyncglk
import {saveAs as filesave_saveAs} from 'file-saver'
import path from 'path-browserify-esm'

import type {ProgressCallback} from '../../common/file.js'
import type {DialogDirectories, DialogOptions} from '../common/interface.js'
import {show_alert} from './common.js'
import {DownloadProvider, read_uploaded_file} from './download.js'
import type {BrowseableProvider, BrowserDialog, DirEntry, DownloadOptions, FilesMetadata, ProgressCallback, Provider} from './interface.js'
import type {BrowseableProvider, BrowserDialog, DirEntry, DownloadOptions, FilesMetadata, Provider} from './interface.js'
import {WebStorageProvider} from './storage.js'
import FileDialog from './ui/FileDialog.svelte'

Expand Down
46 changes: 3 additions & 43 deletions src/dialog/browser/download.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ https://github.com/curiousdannii/asyncglk

// The download provider stores its own files just in a map (maybe to be cached in the future), but if files are written next to them, then they need to be done so in another provider

import type {ProgressCallback} from '../../common/file.js'
import {NullProvider} from './common.js'
import type {DownloadOptions, ProgressCallback, Provider} from './interface.js'
import type {DownloadOptions, Provider} from './interface.js'
import {parse_base64, read_response} from '../../common/file.js'
import {utf8decoder} from '../../common/misc.js'

export class DownloadProvider implements Provider {
Expand Down Expand Up @@ -140,48 +142,6 @@ export async function fetch_storyfile(options: DownloadOptions, url: string, pro
return data
}

/** Parse Base 64 into a Uint8Array */
export async function parse_base64(data: string, data_type = 'octet-binary'): Promise<Uint8Array> {
// Parse base64 using a trick from https://stackoverflow.com/a/54123275/2854284
const response = await fetch(`data:application/${data_type};base64,${data}`)
if (!response.ok) {
throw new Error(`Could not parse base64: ${response.status}`)
}
return new Uint8Array(await response.arrayBuffer())
}

/** Read a response, with optional progress notifications */
export async function read_response(response: Response, progress_callback?: ProgressCallback): Promise<Uint8Array> {
if (!response.ok) {
throw new Error(`Could not fetch ${response.url}, got ${response.status}`)
}

if (!progress_callback) {
return new Uint8Array(await response.arrayBuffer())
}

// Read the response, calling the callback with each chunk
const chunks: Array<[number, Uint8Array]> = []
let length = 0
const reader = response.body!.getReader()
for (;;) {
const {done, value} = await reader.read()
if (done) {
break
}
chunks.push([length, value])
progress_callback(value.length)
length += value.length
}

// Join the chunks together
const result = new Uint8Array(length)
for (const [offset, chunk] of chunks) {
result.set(chunk, offset)
}
return result
}

/** Read an uploaded file and return it as a Uint8Array */
export function read_uploaded_file(file: File): Promise<Uint8Array> {
return new Promise((resolve, reject) => {
Expand Down
3 changes: 1 addition & 2 deletions src/dialog/browser/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@ https://github.com/curiousdannii/asyncglk
*/

import type {ProgressCallback} from '../../common/file.js'
import type {AsyncDialog} from '../common/interface.js'

export type ProgressCallback = (bytes: number) => void

export interface BrowserDialog extends AsyncDialog {
download(url: string, progress_callback?: ProgressCallback): Promise<string>
upload(file: File): Promise<string>
Expand Down
1 change: 0 additions & 1 deletion src/index-browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,5 @@ https://github.com/curiousdannii/asyncglk
export * from './index-common.js'

export {ProviderBasedBrowserDialog} from './dialog/browser/browser.js'
export {parse_base64, read_response} from './dialog/browser/download.js'

export {default as WebGlkOte} from './glkote/web/web.js'
3 changes: 2 additions & 1 deletion src/index-common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ export type {BlorbChunk, BlorbDataChunk, ImageInfo, ImageSize, InfoMap, InfoMapR
export {IFF} from './blorb/iff.js'

export * as constants from './common/constants.js'
export {parse_base64, type ProgressCallback, read_response} from './common/file.js'
export {FileView} from './common/misc.js'
export * as protocol from './common/protocol.js'

export {filetype_to_extension, filters_for_usage, path_native_to_posix, path_posix_to_native} from './dialog/common/common.js'
export type {AsyncDialog, AutosaveData, ClassicFileStream, ClassicStreamingDialog, ClassicSyncDialog, Dialog, DialogDirectories, DialogOptions} from './dialog/common/interface.js'

export type {BrowserDialog, DownloadOptions, ProgressCallback} from './dialog/browser/interface.js'
export type {BrowserDialog, DownloadOptions} from './dialog/browser/interface.js'

export type {GiDispa, GlkApi, GlkApiAsync, GlkApiOptions, GlkClassName, GlkFref, GlkObject, GlkSchannel, GlkStream, GlkVM, GlkWindow} from './glkapi/interface.js'
export {AsyncGlk, RefBox, RefStruct} from './glkapi/glkapi.js'
Expand Down

0 comments on commit 29c8c84

Please sign in to comment.