From 3acc595ac463ce517482c10a586bec22fcbc7746 Mon Sep 17 00:00:00 2001 From: Christian Bager Bach Houmann Date: Mon, 27 Feb 2023 14:25:25 +0100 Subject: [PATCH] fix: fix downloads on mobile devices content-type header is case-insensitive on mobile fix #69 --- src/downloadEpisode.ts | 138 ++++++++++++++++++++++------------------- src/main.ts | 19 +++--- 2 files changed, 84 insertions(+), 73 deletions(-) diff --git a/src/downloadEpisode.ts b/src/downloadEpisode.ts index 4b3bc29..340150d 100644 --- a/src/downloadEpisode.ts +++ b/src/downloadEpisode.ts @@ -2,14 +2,13 @@ import { Notice, requestUrl } from "obsidian"; import { downloadedEpisodes } from "./store"; import { DownloadPathTemplateEngine } from "./TemplateEngine"; import { Episode } from "./types/Episode"; -import Progressbar from "./ui/common/Progressbar.svelte"; import getUrlExtension from "./utility/getUrlExtension"; async function downloadFile( url: string, options?: Partial<{ - onProgress: (progress: number, total: number) => void, - onFinished: () => void + onFinished: () => void; + onError: (error: Error) => void; }> ) { try { @@ -24,78 +23,81 @@ async function downloadFile( options?.onFinished?.(); return { - blob: new Blob([response.arrayBuffer], { type: response.headers['content-type'] ?? "" }), + blob: new Blob([response.arrayBuffer], { + type: response.headers["content-type"] ?? response.headers["Content-Type"] ?? "", + }), contentLength, receivedLength: contentLength, responseUrl: url, }; } catch (error) { - throw new Error(`Failed to download ${url}: ${error.message}`); + const err = new Error(`Failed to download ${url}:\n\n${error.message}`); + options?.onError?.(err); + + throw err; } } -export default async function downloadEpisodeWithProgressNotice(episode: Episode, downloadPathTemplate: string): Promise { +export default async function downloadEpisodeWithNotice( + episode: Episode, + downloadPathTemplate: string +): Promise { const { doc, update } = createNoticeDoc(`Download "${episode.title}"`); const SOME_LARGE_INT_SO_THE_BOX_DOESNT_AUTO_CLOSE = 999999999; const notice = new Notice(doc, SOME_LARGE_INT_SO_THE_BOX_DOESNT_AUTO_CLOSE); - update(bodyEl => bodyEl.createEl('p', { text: 'Starting download...' })); - - let progressBar: Progressbar; - let percentEl: HTMLSpanElement; - update(bodyEl => { - percentEl = bodyEl.createSpan({ text: '0%' }); - progressBar = new Progressbar({ - target: bodyEl, - props: { - max: 100, - value: 0, - style: { - width: '100%', - height: '2rem', - } - } - }); + update((bodyEl) => bodyEl.createEl("p", { text: "Starting download..." })); + + update((bodyEl) => { + bodyEl.createEl("p", { text: "Downloading..." }); }); const { blob, responseUrl } = await downloadFile(episode.streamUrl, { - onProgress: (progress, total) => { - update(_ => { - percentEl.textContent = `${Math.floor(progress / total * 100)}%`; - progressBar.$set({ value: progress / total * 100 }); - }, false); - }, onFinished: () => { - progressBar.$destroy(); - update(bodyEl => bodyEl.createEl('p', { text: 'Download complete!' })); + update((bodyEl) => + bodyEl.createEl("p", { text: "Download complete!" }) + ); + }, + onError: (error) => { + update((bodyEl) => + bodyEl.createEl("p", { text: `Download failed: ${error.message}` }) + ); } }); - console.log(blob); + if (!blob.type.contains("audio")) { + update((bodyEl) => { + bodyEl.createEl("p", { + text: `Downloaded file is not an audio file. It is of type "${blob.type}". Blob: ${blob.size} bytes.`, + }); + }); - if (!blob.type.contains('audio')) { - throw new Error('Not an audio file'); + throw new Error("Not an audio file"); } try { - update(bodyEl => bodyEl.createEl('p', { text: `Creating file...` })); - + update((bodyEl) => bodyEl.createEl("p", { text: `Creating file...` })); + await createEpisodeFile({ episode, downloadPathTemplate, blob, responseUrl, - }) + }); - update(bodyEl => bodyEl.createEl('p', { text: `Successfully downloaded "${episode.title}" from ${episode.podcastName}.` })); + update((bodyEl) => + bodyEl.createEl("p", { + text: `Successfully downloaded "${episode.title}" from ${episode.podcastName}.`, + }) + ); } catch (error) { - update(bodyEl => { - bodyEl.createEl('p', { - text: `Failed to create file for downloaded episode "${episode.title}" from ${episode.podcastName}.` + update((bodyEl) => { + bodyEl.createEl("p", { + text: `Failed to create file for downloaded episode "${episode.title}" from ${episode.podcastName}.`, }); - const errorMsgEl = bodyEl.createEl('p', { text: error.message }); - errorMsgEl.style.fontStyle = 'italic'; + const errorMsgEl = bodyEl.createEl("p", { text: error.message }); + errorMsgEl.style.fontStyle = "italic"; }); } @@ -106,18 +108,18 @@ function createNoticeDoc(title: string) { const doc = new DocumentFragment(); const container = doc.createDiv(); container.style.width = "100%"; - container.style.display = 'flex'; + container.style.display = "flex"; - const titleEl = container.createEl('span', { text: title }); - titleEl.style.textAlign = 'center'; - titleEl.style.fontWeight = 'bold'; - titleEl.style.marginBottom = '0.5em'; + const titleEl = container.createEl("span", { text: title }); + titleEl.style.textAlign = "center"; + titleEl.style.fontWeight = "bold"; + titleEl.style.marginBottom = "0.5em"; const bodyEl = doc.createDiv(); - bodyEl.style.display = 'flex'; - bodyEl.style.flexDirection = 'column'; - bodyEl.style.alignItems = 'center'; - bodyEl.style.justifyContent = 'center'; + bodyEl.style.display = "flex"; + bodyEl.style.flexDirection = "column"; + bodyEl.style.alignItems = "center"; + bodyEl.style.justifyContent = "center"; return { doc, @@ -125,20 +127,25 @@ function createNoticeDoc(title: string) { if (empty) bodyEl.empty(); updateFn(bodyEl); }, - } + }; } -async function createEpisodeFile({episode, downloadPathTemplate, blob, responseUrl }: { - episode: Episode, - downloadPathTemplate: string, - blob: Blob, - responseUrl: string +async function createEpisodeFile({ + episode, + downloadPathTemplate, + blob, + responseUrl, +}: { + episode: Episode; + downloadPathTemplate: string; + blob: Blob; + responseUrl: string; }) { const basename = DownloadPathTemplateEngine(downloadPathTemplate, episode); const filePath = `${basename}.${getUrlExtension(responseUrl)}`; const buffer = await blob.arrayBuffer(); - + try { await app.vault.createBinary(filePath, buffer); } catch (error) { @@ -148,12 +155,15 @@ async function createEpisodeFile({episode, downloadPathTemplate, blob, responseU downloadedEpisodes.addEpisode(episode, filePath, blob.size); } -export async function downloadEpisode(episode: Episode, downloadPathTemplate: string) { +export async function downloadEpisode( + episode: Episode, + downloadPathTemplate: string +) { try { const { blob, responseUrl } = await downloadFile(episode.streamUrl); - if (!blob.type.contains('audio')) { - throw new Error('Not an audio file.'); + if (!blob.type.contains("audio")) { + throw new Error("Not an audio file."); } await createEpisodeFile({ @@ -163,6 +173,8 @@ export async function downloadEpisode(episode: Episode, downloadPathTemplate: st responseUrl, }); } catch (error) { - throw new Error(`Failed to download ${episode.title}: ${error.message}`); + throw new Error( + `Failed to download ${episode.title}: ${error.message}` + ); } } diff --git a/src/main.ts b/src/main.ts index 8ebc055..8af88f7 100644 --- a/src/main.ts +++ b/src/main.ts @@ -30,7 +30,7 @@ import { Episode } from "./types/Episode"; import CurrentEpisodeController from "./store_controllers/CurrentEpisodeController"; import { TimestampTemplateEngine } from "./TemplateEngine"; import createPodcastNote from "./createPodcastNote"; -import downloadEpisodeWithProgressNotice from "./downloadEpisode"; +import downloadEpisodeWithNotice from "./downloadEpisode"; import DownloadedEpisode from "./types/DownloadedEpisode"; import DownloadedEpisodesController from "./store_controllers/DownloadedEpisodesController"; import { LocalFilesController } from "./store_controllers/LocalFilesController"; @@ -177,10 +177,7 @@ export default class PodNotes extends Plugin implements IPodNotes { } const episode = this.api.podcast; - downloadEpisodeWithProgressNotice( - episode, - this.settings.download.path - ); + downloadEpisodeWithNotice(episode, this.settings.download.path); }, }); @@ -245,16 +242,16 @@ export default class PodNotes extends Plugin implements IPodNotes { }); this.addCommand({ - id: 'podnotes-toggle-playback', - name: 'Toggle playback', + id: "podnotes-toggle-playback", + name: "Toggle playback", checkCallback: (checking) => { if (checking) { return !!this.api.podcast; } this.api.togglePlayback(); - } - }) + }, + }); this.addSettingTab(new PodNotesSettingsTab(this.app, this)); @@ -266,7 +263,9 @@ export default class PodNotes extends Plugin implements IPodNotes { this.app.workspace.onLayoutReady(this.onLayoutReady.bind(this)); - this.registerObsidianProtocolHandler("podnotes", (action) => podNotesURIHandler(action, this.api)); + this.registerObsidianProtocolHandler("podnotes", (action) => + podNotesURIHandler(action, this.api) + ); this.registerEvent(getContextMenuHandler()); }