@@ -5,6 +5,7 @@ import type { Episode } from "./types/Episode";
55import type { LocalEpisode } from "./types/LocalEpisode" ;
66import { isLocalFile } from "./utility/isLocalFile" ;
77import getUrlExtension from "./utility/getUrlExtension" ;
8+ import getExtensionFromContentType from "./utility/getExtensionFromContentType" ;
89
910function getErrorMessage ( error : unknown ) : string {
1011 return error instanceof Error ? error . message : String ( error ) ;
@@ -76,18 +77,11 @@ export default async function downloadEpisodeWithNotice(
7677 } ,
7778 } ) ;
7879
79- const fileExtension = await detectAudioFileExtension ( blob ) ;
80- if ( ! fileExtension ) {
81- update ( ( bodyEl ) => {
82- bodyEl . createEl ( "p" , {
83- text : `Could not determine file extension for downloaded file. Blob: ${ blob . size } bytes.` ,
84- } ) ;
85- } ) ;
80+ const inferredExtension = await inferFileExtensionFromDownload ( episode , blob ) ;
81+ const normalizedType = ( blob . type ?? "" ) . toLowerCase ( ) ;
82+ const typeAppearsAudio = normalizedType === "" || normalizedType . includes ( "audio" ) ;
8683
87- throw new Error ( "Could not determine file extension" ) ;
88- }
89-
90- if ( ! blob . type . contains ( "audio" ) && ! fileExtension ) {
84+ if ( ! typeAppearsAudio && ! inferredExtension ) {
9185 update ( ( bodyEl ) => {
9286 bodyEl . createEl ( "p" , {
9387 text : `Downloaded file is not an audio file. It is of type "${ blob . type } ". Blob: ${ blob . size } bytes.` ,
@@ -97,6 +91,8 @@ export default async function downloadEpisodeWithNotice(
9791 throw new Error ( "Not an audio file" ) ;
9892 }
9993
94+ const fileExtension = inferredExtension ?? "mp3" ;
95+
10096 try {
10197 update ( ( bodyEl ) => bodyEl . createEl ( "p" , { text : "Creating file..." } ) ) ;
10298
@@ -231,6 +227,23 @@ function getLocalFilePathFromLink(link: string): string | null {
231227 return null ;
232228}
233229
230+ async function inferFileExtensionFromDownload (
231+ episode : Episode ,
232+ blob : Blob ,
233+ ) : Promise < string | null > {
234+ const signatureExtension = await detectAudioFileExtension ( blob ) ;
235+ if ( signatureExtension ) {
236+ return signatureExtension ;
237+ }
238+
239+ const urlExtension = getUrlExtension ( episode . streamUrl ) ;
240+ if ( urlExtension ) {
241+ return urlExtension ;
242+ }
243+
244+ return getExtensionFromContentType ( blob . type ) ;
245+ }
246+
234247export async function downloadEpisode (
235248 episode : Episode ,
236249 downloadPathTemplate : string ,
@@ -286,11 +299,10 @@ async function getFileExtension(url: string): Promise<string> {
286299 const response = await fetch ( url , { method : "HEAD" } ) ;
287300 const contentType = response . headers . get ( "content-type" ) ;
288301
289- if ( contentType ?. includes ( "audio/mpeg" ) ) return "mp3" ;
290- if ( contentType ?. includes ( "audio/mp4" ) ) return "m4a" ;
291- if ( contentType ?. includes ( "audio/ogg" ) ) return "ogg" ;
292- if ( contentType ?. includes ( "audio/wav" ) ) return "wav" ;
293- if ( contentType ?. includes ( "audio/x-m4a" ) ) return "m4a" ;
302+ const extensionFromContentType = getExtensionFromContentType ( contentType ) ;
303+ if ( extensionFromContentType ) {
304+ return extensionFromContentType ;
305+ }
294306
295307 // Default to mp3 if we can't determine the type
296308 return "mp3" ;
0 commit comments