Pārlūkot izejas kodu

Refactor fetching of URL headers to determine URL type

JC Brand 4 mēneši atpakaļ
vecāks
revīzija
aa1ee0d002

+ 4 - 3
src/headless/types/utils/index.d.ts

@@ -27,14 +27,15 @@ declare const _default: {
     checkFileTypes(types: string[], url: string | URL): boolean;
     isURLWithImageExtension(url: string | URL): boolean;
     isGIFURL(url: string | URL): boolean;
-    isAudioURL(url: string | URL): boolean;
+    isAudioURL(url: string | URL, headers?: Headers): boolean;
     isVideoURL(url: string | URL): boolean;
     isImageURL(url: string | URL): boolean;
     isEncryptedFileURL(url: string | URL): boolean;
     withinString(string: string, callback: Function, options?: import("./types.js").ProcessStringOptions): string;
-    getMediaURLsMetadata(text: string, offset?: number): {
+    getHeaders(url: string): Promise<Headers>;
+    getMediaURLsMetadata(text: string, offset?: number, fetch_headers?: boolean): Promise<{
         media_urls?: import("./types.js").MediaURLMetadata[];
-    };
+    }>;
     getMediaURLs(arr: Array<import("./types.js").MediaURLMetadata>, text: string): import("./types.js").MediaURLMetadata[];
     addMediaURLsOffset(arr: Array<import("./types.js").MediaURLMetadata>, text: string, offset?: number): import("./types.js").MediaURLMetadata[];
     firstCharToUpperCase(text: string): string;

+ 3 - 1
src/headless/types/utils/types.d.ts

@@ -12,9 +12,11 @@ export type ProcessStringOptions = {
 };
 export type MediaURLMetadata = {
     is_audio?: boolean;
+    is_encrypted?: boolean;
+    is_gif?: boolean;
     is_image?: boolean;
     is_video?: boolean;
-    is_encrypted?: boolean;
+    content_type?: string;
     end?: number;
     start?: number;
     url: string;

+ 10 - 4
src/headless/types/utils/url.d.ts

@@ -30,8 +30,9 @@ export function isURLWithImageExtension(url: string | URL): boolean;
 export function isGIFURL(url: string | URL): boolean;
 /**
  * @param {string|URL} url
+ * @param {Headers} [headers]
  */
-export function isAudioURL(url: string | URL): boolean;
+export function isAudioURL(url: string | URL, headers?: Headers): boolean;
 /**
  * @param {string|URL} url
  */
@@ -58,14 +59,19 @@ export function isEncryptedFileURL(url: string | URL): boolean;
  * @returns {string} The modified string after processing all matches.
  */
 export function withinString(string: string, callback: Function, options?: import("./types").ProcessStringOptions): string;
+/**
+ * @param {string} url
+ * @returns {Promise<Headers>}
+ */
+export function getHeaders(url: string): Promise<Headers>;
 /**
  * @param {string} text
  * @param {number} offset
- * @returns {{media_urls?: import("./types").MediaURLMetadata[]}}
+ * @returns {Promise<{media_urls?: import("./types").MediaURLMetadata[]}>}
  */
-export function getMediaURLsMetadata(text: string, offset?: number): {
+export function getMediaURLsMetadata(text: string, offset?: number, fetch_headers?: boolean): Promise<{
     media_urls?: import("./types").MediaURLMetadata[];
-};
+}>;
 /**
  * @param {Array<import("./types").MediaURLMetadata>} arr
  * @param {string} text

+ 3 - 1
src/headless/utils/types.ts

@@ -14,9 +14,11 @@ export type ProcessStringOptions = {
 
 export type MediaURLMetadata = {
     is_audio?: boolean;
+    is_encrypted?: boolean;
+    is_gif?: boolean;
     is_image?: boolean;
     is_video?: boolean;
-    is_encrypted?: boolean;
+    content_type?: string;
     end?: number;
     start?: number;
     url: string;

+ 33 - 9
src/headless/utils/url.js

@@ -78,8 +78,12 @@ export function isGIFURL(url) {
 
 /**
  * @param {string|URL} url
+ * @param {Headers} [headers]
  */
-export function isAudioURL(url) {
+export function isAudioURL(url, headers) {
+    if (headers?.get("content-type")?.startsWith("audio")) {
+        return true;
+    }
     return checkFileTypes([".ogg", ".mp3", ".m4a"], url);
 }
 
@@ -174,12 +178,27 @@ export function withinString(string, callback, options) {
     return string;
 }
 
+/**
+ * @param {string} url
+ * @returns {Promise<Headers>}
+ */
+export async function getHeaders(url) {
+    try {
+        const response = await fetch(url, { method: "HEAD" });
+        return response.headers;
+    } catch (e) {
+        console.debug(`Error calling HEAD on url ${url}: ${e}`);
+        return null;
+    }
+}
+
+
 /**
  * @param {string} text
  * @param {number} offset
- * @returns {{media_urls?: import("./types").MediaURLMetadata[]}}
+ * @returns {Promise<{media_urls?: import("./types").MediaURLMetadata[]}>}
  */
-export function getMediaURLsMetadata(text, offset = 0) {
+export async function getMediaURLsMetadata(text, offset = 0, fetch_headers = false) {
     const objs = [];
     if (!text) {
         return {};
@@ -213,12 +232,17 @@ export function getMediaURLsMetadata(text, offset = 0) {
         log.debug(error);
     }
 
-    const media_urls = objs.map((o) => ({
-        ...o,
-        is_audio: isAudioURL(o.url),
-        is_image: isImageURL(o.url),
-        is_video: isVideoURL(o.url),
-        is_encrypted: isEncryptedFileURL(o.url),
+    const media_urls = await Promise.all(objs.map(async (o) => {
+
+        const headers = fetch_headers ? await getHeaders(o.url) : null;
+        return {
+            ...o,
+            is_gif: isGIFURL(o.url),
+            is_audio: isAudioURL(o.url, headers),
+            is_image: isImageURL(o.url),
+            is_video: isVideoURL(o.url),
+            is_encrypted: isEncryptedFileURL(o.url),
+        }
     }));
     return media_urls.length ? { media_urls } : {};
 }

+ 0 - 1
src/plugins/omemo/utils.js

@@ -282,7 +282,6 @@ function getTemplateForObjectURL(file_url, obj_url, richtext) {
     if (isError(obj_url)) {
         return html`<p class="error">${/** @type {Error} */(obj_url).message}</p>`;
     }
-
     if (isImageURL(file_url)) {
         return tplImage({
             src: obj_url,

+ 0 - 1
src/shared/autocomplete/autocomplete.js

@@ -237,7 +237,6 @@ export class AutoComplete extends EventEmitter(Object) {
                 this.close({'reason': 'esc'});
                 return true;
             } else if ([converse.keycodes.UP_ARROW, converse.keycodes.DOWN_ARROW].includes(ev.key)) {
-                debugger;
                 ev.preventDefault();
                 ev.stopPropagation();
                 this[ev.key === converse.keycodes.UP_ARROW ? "previous" : "next"]();

+ 5 - 17
src/shared/texture/texture.js

@@ -14,7 +14,6 @@ import {
     collapseLineBreaks,
     containsDirectives,
     getDirectiveAndLength,
-    getHeaders,
     isQuoteDirective,
     isSpotifyTrack,
     isString,
@@ -29,10 +28,6 @@ const {
     getCodePointReferences,
     getMediaURLsMetadata,
     getShortnameReferences,
-    isAudioURL,
-    isGIFURL,
-    isImageURL,
-    isVideoURL,
 } = u;
 
 /**
@@ -134,9 +129,9 @@ export class Texture extends String {
         const { url } = url_obj;
         const filtered_url = filterQueryParamsFromURL(url);
         let template;
-        if (isGIFURL(url) && this.shouldRenderMedia(url, "image")) {
+        if (url_obj.is_gif && this.shouldRenderMedia(url, "image")) {
             template = tplGif(filtered_url, this.hide_media_urls);
-        } else if (isImageURL(url) && this.shouldRenderMedia(url, "image")) {
+        } else if (url_obj.is_image && this.shouldRenderMedia(url, "image")) {
             template = tplImage({
                 src: filtered_url,
                 // XXX: bit of an abuse of `hide_media_urls`, might want a dedicated option here
@@ -144,20 +139,13 @@ export class Texture extends String {
                 onClick: this.onImgClick,
                 onLoad: this.onImgLoad,
             });
-        } else if (isVideoURL(url) && this.shouldRenderMedia(url, "video")) {
+        } else if (url_obj.is_video && this.shouldRenderMedia(url, "video")) {
             template = tplVideo(filtered_url, this.hide_media_urls);
-        } else if (isAudioURL(url) && this.shouldRenderMedia(url, "audio")) {
+        } else if (url_obj.is_audio && this.shouldRenderMedia(url, "audio")) {
             template = tplAudio(filtered_url, this.hide_media_urls);
         } else if (api.settings.get("embed_3rd_party_media_players") && isSpotifyTrack(url)) {
             const song_id = url.split("/track/")[1];
             template = tplSpotify(song_id, url, this.hide_media_urls);
-        } else {
-            if (this.shouldRenderMedia(url, "audio") && api.settings.get("fetch_url_headers")) {
-                const headers = await getHeaders(url);
-                if (headers?.get("content-type")?.startsWith("audio")) {
-                    template = tplAudio(filtered_url, this.hide_media_urls, headers.get("Icy-Name"));
-                }
-            }
         }
         return template || getHyperlinkTemplate(filtered_url);
     }
@@ -171,7 +159,7 @@ export class Texture extends String {
      */
     async addHyperlinks(text, local_offset) {
         const media_urls = addMediaURLsOffset(
-            getMediaURLsMetadata(text, local_offset).media_urls || [],
+            (await getMediaURLsMetadata(text, local_offset)).media_urls || [],
             text,
             local_offset
         );

+ 0 - 14
src/shared/texture/utils.js

@@ -24,20 +24,6 @@ export function isSpotifyTrack(url) {
     }
 }
 
-/**
- * @param {string} url
- * @returns {Promise<Headers>}
- */
-export async function getHeaders(url) {
-    try {
-        const response = await fetch(url, { method: "HEAD" });
-        return response.headers;
-    } catch (e) {
-        console.debug(`Error calling HEAD on url ${url}: ${e}`);
-        return null;
-    }
-}
-
 /**
  * We don't render more than two line-breaks, replace extra line-breaks with
  * the zero-width whitespace character

+ 0 - 5
src/types/shared/texture/utils.d.ts

@@ -8,11 +8,6 @@ export function isString(s: any): boolean;
  * @returns {boolean}
  */
 export function isSpotifyTrack(url: string): boolean;
-/**
- * @param {string} url
- * @returns {Promise<Headers>}
- */
-export function getHeaders(url: string): Promise<Headers>;
 /**
  * We don't render more than two line-breaks, replace extra line-breaks with
  * the zero-width whitespace character

+ 8 - 6
src/types/utils/index.d.ts

@@ -11,14 +11,15 @@ declare const _default: {
     checkFileTypes(types: string[], url: string | URL): boolean;
     isURLWithImageExtension(url: string | URL): boolean;
     isGIFURL(url: string | URL): boolean;
-    isAudioURL(url: string | URL): boolean;
+    isAudioURL(url: string | URL, headers?: Headers): boolean;
     isVideoURL(url: string | URL): boolean;
     isImageURL(url: string | URL): boolean;
     isEncryptedFileURL(url: string | URL): boolean;
     withinString(string: string, callback: Function, options?: import("headless/types/utils/types.js").ProcessStringOptions): string;
-    getMediaURLsMetadata(text: string, offset?: number): {
+    getHeaders(url: string): Promise<Headers>;
+    getMediaURLsMetadata(text: string, offset?: number, fetch_headers?: boolean): Promise<{
         media_urls?: import("headless/types/utils/types.js").MediaURLMetadata[];
-    };
+    }>;
     getMediaURLs(arr: Array<import("headless/types/utils/types.js").MediaURLMetadata>, text: string): import("headless/types/utils/types.js").MediaURLMetadata[];
     addMediaURLsOffset(arr: Array<import("headless/types/utils/types.js").MediaURLMetadata>, text: string, offset?: number): import("headless/types/utils/types.js").MediaURLMetadata[];
     firstCharToUpperCase(text: string): string;
@@ -118,14 +119,15 @@ declare const _default: {
         checkFileTypes(types: string[], url: string | URL): boolean;
         isURLWithImageExtension(url: string | URL): boolean;
         isGIFURL(url: string | URL): boolean;
-        isAudioURL(url: string | URL): boolean;
+        isAudioURL(url: string | URL, headers?: Headers): boolean;
         isVideoURL(url: string | URL): boolean;
         isImageURL(url: string | URL): boolean;
         isEncryptedFileURL(url: string | URL): boolean;
         withinString(string: string, callback: Function, options?: import("headless/types/utils/types.js").ProcessStringOptions): string;
-        getMediaURLsMetadata(text: string, offset?: number): {
+        getHeaders(url: string): Promise<Headers>;
+        getMediaURLsMetadata(text: string, offset?: number, fetch_headers?: boolean): Promise<{
             media_urls?: import("headless/types/utils/types.js").MediaURLMetadata[];
-        };
+        }>;
         getMediaURLs(arr: Array<import("headless/types/utils/types.js").MediaURLMetadata>, text: string): import("headless/types/utils/types.js").MediaURLMetadata[];
         addMediaURLsOffset(arr: Array<import("headless/types/utils/types.js").MediaURLMetadata>, text: string, offset?: number): import("headless/types/utils/types.js").MediaURLMetadata[];
         firstCharToUpperCase(text: string): string;