From f654ea2585884694ffec2c37049d0a55e09eecc2 Mon Sep 17 00:00:00 2001 From: Nachtalb Date: Sat, 3 Apr 2021 23:23:33 +0200 Subject: [PATCH 1/5] Add filename to media previews --- client/components/LinkPreview.vue | 5 +++++ server/plugins/irc-events/link.ts | 14 +++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/client/components/LinkPreview.vue b/client/components/LinkPreview.vue index 9a124ab8..d848f389 100644 --- a/client/components/LinkPreview.vue +++ b/client/components/LinkPreview.vue @@ -125,6 +125,11 @@ + diff --git a/server/plugins/irc-events/link.ts b/server/plugins/irc-events/link.ts index 63a86dc0..2c612d2b 100644 --- a/server/plugins/irc-events/link.ts +++ b/server/plugins/irc-events/link.ts @@ -243,6 +243,7 @@ function parse(msg: Msg, chan: Chan, preview: LinkPreview, res: FetchRequest, cl let promise: Promise | null = null; preview.size = res.size; + preview.filename = res.filename; switch (res.type) { case "text/html": @@ -431,6 +432,7 @@ function fetch(uri: string, headers: Record) { let contentLength = 0; let contentType: string | undefined; let limit = Config.values.prefetchMaxImageSize * 1024; + let filename: string | null = null; try { const gotStream = got.stream(uri, { @@ -444,6 +446,16 @@ function fetch(uri: string, headers: Record) { .on("response", function (res) { contentLength = parseInt(res.headers["content-length"], 10) || 0; contentType = res.headers["content-type"]; + filename = + "content-disposition" in res.headers + ? contentDisposition.parse(res.headers["content-disposition"]) + .parameters.filename || null + : null; + + if (filename === null) { + const basename = decodeURI(path.basename(new URL(uri).pathname)); + filename = basename.indexOf(".") > 0 ? basename : null; + } if (contentType && imageTypeRegex.test(contentType)) { // response is an image @@ -488,7 +500,7 @@ function fetch(uri: string, headers: Record) { type = contentType.split(/ *; */).shift() || ""; } - resolve({data: buffer, type, size}); + resolve({data: buffer, type, size, filename}); }); } catch (e: any) { return reject(e); From ad6dc63a6a3ee003cc73a114f73b54bf4343db5c Mon Sep 17 00:00:00 2001 From: Nachtalb Date: Sun, 11 Apr 2021 15:31:50 +0200 Subject: [PATCH 2/5] Individual improved styling for each media elements filename --- client/components/LinkPreview.vue | 48 ++++++++++++++++++++++--------- client/css/style.css | 42 +++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 14 deletions(-) diff --git a/client/components/LinkPreview.vue b/client/components/LinkPreview.vue index d848f389..2a32f259 100644 --- a/client/components/LinkPreview.vue +++ b/client/components/LinkPreview.vue @@ -8,7 +8,11 @@ >
-
diff --git a/client/css/style.css b/client/css/style.css index 0285ef5a..27266b10 100644 --- a/client/css/style.css +++ b/client/css/style.css @@ -1718,8 +1718,50 @@ textarea.input { max-width: 100%; } +#chat .toggle-type-image > a { + position: relative; + font-weight: 700; + color: inherit; +} + +#chat .toggle-type-image.with-filename .image-filename { + padding: 8px 10px 10px; + display: flex; +} + +#chat .toggle-type-image .inner-image-filename, +#chat .toggle-type-audio .inner-audio-filename { + width: 0; + flex-grow: 1; +} + +#chat .toggle-content.toggle-type-audio.with-filename { + padding: 8px 10px 10px; +} + +#chat .toggle-content.toggle-type-audio .audio-filename { + font-weight: 700; + padding-bottom: 10px; + color: inherit; + display: flex; +} + +#chat .toggle-type-audio .head { + padding-bottom: 10px; +} + #chat .toggle-type-video { max-width: 640px; + position: relative; +} + +#chat .toggle-type-video .video-filename { + position: absolute; + top: 0; + width: 100%; + padding: 1em; + background: linear-gradient(black, transparent); + color: white; } #chat video { From 5f015529cd915c57168336326e10835d7d4a3288 Mon Sep 17 00:00:00 2001 From: Nachtalb Date: Mon, 12 Apr 2021 18:26:46 +0200 Subject: [PATCH 3/5] Properly support inline flac files I accidentally didn't quite do the flac support right in 3a42b5385e786fdc7d872cfab6fa46647be119af as I only added known default filename in content-disposition for flac files. Now we actually fully support inline flac files --- server/plugins/uploader.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/server/plugins/uploader.ts b/server/plugins/uploader.ts index 0a5e53a8..04fe659c 100644 --- a/server/plugins/uploader.ts +++ b/server/plugins/uploader.ts @@ -20,6 +20,7 @@ const inlineContentDispositionTypes = { "audio/mpeg": "audio.mp3", "audio/ogg": "audio.ogg", "audio/vnd.wave": "audio.wav", + "audio/flac": "audio.flac", "audio/x-flac": "audio.flac", "audio/x-m4a": "audio.m4a", "image/bmp": "image.bmp", @@ -124,6 +125,12 @@ class Uploader { detectedMimeType = "video/mp4"; } + if (detectedMimeType === "audio/x-flac") { + // Send a more common mime type for wave audio files + // so that browsers can play them correctly + detectedMimeType = "audio/flac"; + } + res.setHeader("Content-Disposition", disposition); res.setHeader("Cache-Control", "max-age=86400"); res.contentType(detectedMimeType); From bfca8f6237ca7708dca1b292d4ca828889c56699 Mon Sep 17 00:00:00 2001 From: Nachtalb Date: Fri, 30 Apr 2021 03:28:49 +0200 Subject: [PATCH 4/5] Move image filename to the side for slim images With this we vastly improve the readability of filenames for slim images. The breakpoint is at an aspect ratio of 16:10. --- client/components/LinkPreview.vue | 24 +++++++++++++++++++++++- client/css/style.css | 18 ++++++++++++++++++ server/plugins/irc-events/link.ts | 8 +++++++- 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/client/components/LinkPreview.vue b/client/components/LinkPreview.vue index 2a32f259..89072e08 100644 --- a/client/components/LinkPreview.vue +++ b/client/components/LinkPreview.vue @@ -69,7 +69,7 @@ { // cancel the navigation if the user is trying to close the image viewer @@ -242,6 +245,20 @@ export default defineComponent({ } }; + const updateWideImageViewDecision = () => { + if (window.innerWidth < 480) { + // Mobile + useWideImageView.value = + (image.value && image.value.naturalWidth / image.value.naturalHeight <= 1.34) || + false; // aspect ratio around 4:3 and slimmer + } else { + // Desktop + useWideImageView.value = + (image.value && image.value.naturalWidth / image.value.naturalHeight <= 1.6) || + false; // aspect ratio 16:10 and slimmer + } + }; + const onPreviewUpdate = () => { // Don't display previews while they are loading on the server if (props.link.type === "loading") { @@ -258,6 +275,8 @@ export default defineComponent({ handleResize(); props.keepScrollPosition(); } + + updateWideImageViewDecision(); }; const onThumbnailError = () => { @@ -346,8 +365,11 @@ export default defineComponent({ onPreviewUpdate, showMoreButton, isContentShown, + useWideImageView, + image, content, container, + updateWideImageViewDecision, }; }, }); diff --git a/client/css/style.css b/client/css/style.css index 27266b10..a0cc98be 100644 --- a/client/css/style.css +++ b/client/css/style.css @@ -1624,6 +1624,24 @@ textarea.input { white-space: normal; } +#chat .toggle-content .wide-view { + display: flex; + flex-direction: row; +} + +#chat .toggle-content .wide-view .image-filename { + order: 2; +} + +#chat .toggle-content .wide-view .image-filename span { + width: auto; + max-width: 500px; +} + +#chat .toggle-content .wide-view img { + order: 1; +} + /* This applies to images of preview-type-image and thumbnails of preview-type-link */ #chat .toggle-content img { max-width: 100%; diff --git a/server/plugins/irc-events/link.ts b/server/plugins/irc-events/link.ts index 2c612d2b..a6277025 100644 --- a/server/plugins/irc-events/link.ts +++ b/server/plugins/irc-events/link.ts @@ -10,11 +10,14 @@ import storage from "../storage"; import Client from "../../client"; import Chan from "../../models/chan"; import Msg from "../../models/msg"; +import contentDisposition from "content-disposition"; +import path from "path"; type FetchRequest = { data: Buffer; type: string; size: number; + filename: string | null; }; const currentFetchPromises = new Map>(); const imageTypeRegex = /^image\/.+/; @@ -30,6 +33,7 @@ export type LinkPreview = { shown?: boolean | null; error?: string; message?: string; + filename: string | null; media?: string; mediaType?: string; @@ -68,6 +72,7 @@ export default function (client: Client, chan: Chan, msg: Msg, cleanText: string size: -1, link: link.link, // Send original matched link to the client shown: null, + filename: null, }; cleanLinks.push(preview); @@ -446,9 +451,10 @@ function fetch(uri: string, headers: Record) { .on("response", function (res) { contentLength = parseInt(res.headers["content-length"], 10) || 0; contentType = res.headers["content-type"]; + filename = "content-disposition" in res.headers - ? contentDisposition.parse(res.headers["content-disposition"]) + ? contentDisposition?.parse(res.headers["content-disposition"]) .parameters.filename || null : null; From 57141f983ab05b8508bc9314e67af5be5e47f154 Mon Sep 17 00:00:00 2001 From: Nachtalb Date: Sun, 28 Jan 2024 20:38:46 +0100 Subject: [PATCH 5/5] Update tests with the new link filename attribute --- test/plugins/link.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/plugins/link.ts b/test/plugins/link.ts index 26f6070d..1b15cb7a 100644 --- a/test/plugins/link.ts +++ b/test/plugins/link.ts @@ -56,6 +56,7 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`; expect(message.previews).to.deep.equal([ { body: "", + filename: null, head: "", link: url, thumb: "", @@ -93,6 +94,7 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`; expect(message.previews).to.deep.equal([ { body: "", + filename: null, head: "", link: url, thumb: "", @@ -425,6 +427,7 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`; expect(message.previews).to.eql([ { body: "", + filename: null, head: "", link: url_one, thumb: "", @@ -434,6 +437,7 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`; }, { body: "", + filename: null, head: "", link: url_two, thumb: "", @@ -608,6 +612,7 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`; expect(message.previews).to.deep.equal([ { type: "loading", + filename: null, head: "", body: "", thumb: "",