Infer file type for embeds for previews

This commit is contained in:
Sebastian Martinez
2024-12-19 20:55:30 +01:00
committed by Rūdolfs Ošiņš
parent 01416e8aa5
commit 153331f2e3
9 changed files with 91 additions and 21 deletions
Generated
+25 -1
View File
@@ -2544,6 +2544,12 @@ dependencies = [
"serde",
]
[[package]]
name = "infer"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "865e8a58ae8e24d2c4412c31344afa1d302a3740ad67528c10f50d6876cdcf55"
[[package]]
name = "infer"
version = "0.16.0"
@@ -2954,6 +2960,16 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "mime-infer"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91caed19dd472bc88bcd063571df18153529d49301a1918f4cf37f42332bee2e"
dependencies = [
"mime",
"unicase",
]
[[package]]
name = "miniz_oxide"
version = "0.8.0"
@@ -4120,7 +4136,9 @@ dependencies = [
"anyhow",
"axum",
"base64 0.22.1",
"infer 0.3.7",
"localtime",
"mime-infer",
"radicle",
"radicle-surf",
"serde",
@@ -5659,7 +5677,7 @@ dependencies = [
"glob",
"html5ever",
"http",
"infer",
"infer 0.16.0",
"json-patch",
"kuchikiki",
"log",
@@ -6379,6 +6397,12 @@ dependencies = [
"unic-common",
]
[[package]]
name = "unicase"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df"
[[package]]
name = "unicode-id-start"
version = "1.0.4"
+3 -2
View File
@@ -22,9 +22,10 @@ pub mod patch;
pub async fn get_embed(
ctx: tauri::State<'_, AppState>,
rid: identity::RepoId,
name: Option<String>,
oid: git::Oid,
) -> Result<Vec<u8>, Error> {
ctx.get_embed(rid, oid)
) -> Result<types::cobs::EmbedWithMimeType, Error> {
ctx.get_embed(rid, name, oid)
}
#[tauri::command]
+2
View File
@@ -8,6 +8,8 @@ anyhow = { version = "1.0.90" }
axum = { version = "0.7.5", default-features = false, features = ["json"] }
base64 = { version = "0.22.1" }
localtime = { version = "1.3.1" }
infer = { version = "0.3" }
mime-infer = { version = "3.0.0" }
radicle = { version = "0.14.0", features = ["test"] }
radicle-surf = { version = "0.22.1", features = ["serde"] }
serde = { version = "1.0.210", features = ["derive"] }
@@ -0,0 +1,3 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type Embed = { content: Array<number>; mimeType: string | null };
@@ -0,0 +1,6 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type EmbedWithMimeType = {
content: Array<number>;
mimeType: string | null;
};
+9
View File
@@ -30,6 +30,15 @@ impl Author {
}
}
#[derive(Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export)]
#[ts(export_to = "cob/")]
pub struct EmbedWithMimeType {
pub content: Vec<u8>,
pub mime_type: Option<String>,
}
#[derive(TS, Serialize)]
#[doc = "A type alias for the TS type `never`."]
#[ts(export)]
+21 -2
View File
@@ -15,12 +15,31 @@ use crate::error::Error;
use crate::traits::Profile;
pub trait Thread: Profile {
fn get_embed(&self, rid: identity::RepoId, oid: git::Oid) -> Result<Vec<u8>, Error> {
fn get_embed(
&self,
rid: identity::RepoId,
name: Option<String>,
oid: git::Oid,
) -> Result<cobs::EmbedWithMimeType, Error> {
let profile = self.profile();
let repo = profile.storage.repository(rid)?;
let blob = repo.blob(oid)?;
let content = blob.content();
let mime_type = match infer::get(content).map(|i| i.mime_type().to_string()) {
Some(mime_type) => Some(mime_type),
None if name.is_some() => {
let filename = name.unwrap();
mime_infer::from_path(&filename)
.first()
.map(|m| m.as_ref().to_string())
}
_ => None,
};
Ok::<_, Error>(blob.content().to_vec())
Ok::<_, Error>(cobs::EmbedWithMimeType {
content: content.to_vec(),
mime_type,
})
}
fn save_embed_to_disk(
+3 -2
View File
@@ -244,14 +244,15 @@ async fn activity_handler(
#[derive(Serialize, Deserialize)]
struct EmbedBody {
pub rid: identity::RepoId,
pub name: Option<String>,
pub oid: git::Oid,
}
async fn get_embeds_handler(
State(ctx): State<Context>,
Json(EmbedBody { rid, oid }): Json<EmbedBody>,
Json(EmbedBody { rid, name, oid }): Json<EmbedBody>,
) -> impl IntoResponse {
let embed = ctx.get_embed(rid, oid)?;
let embed = ctx.get_embed(rid, name, oid)?;
Ok::<_, Error>(Json(embed))
}
+19 -14
View File
@@ -1,4 +1,6 @@
<script lang="ts">
import type { Embed } from "@bindings/cob/Embed";
import dompurify from "dompurify";
import matter from "@radicle/gray-matter";
import { toDom } from "hast-util-to-dom";
@@ -68,7 +70,6 @@
// If the markdown link is an oid embed
if (href && isCommit(href)) {
e.style.display = "block";
e.onclick = event => {
event.preventDefault();
invoke("save_embed_to_disk", {
@@ -77,47 +78,51 @@
name: e.innerText,
}).catch(console.error);
};
void invoke<Uint8Array>("get_embed", {
void invoke<Embed>("get_embed", {
rid,
name: e.innerText,
oid: href,
})
.then(byteArray => {
const buffer = Buffer.from(byteArray);
.then(({ mimeType, content }) => {
const buffer = Buffer.from(content);
const blob = new Blob([buffer]);
const url = URL.createObjectURL(blob);
const ext = e.innerHTML.split(".").at(-1);
// Embed an img element below the link
if (ext?.match(/(gif|jpe?g|tiff?|png|webp|bmp)/)) {
if (mimeType?.startsWith("image")) {
const element = document.createElement("img");
element.setAttribute("src", url);
element.style.display = "block";
e.style.display = "block";
e.insertAdjacentElement("afterend", element);
// Embed an iframe to display pdf correctly element below the link
} else if (ext?.match(/(pdf)/)) {
} else if (mimeType?.startsWith("application")) {
const element = document.createElement("embed");
element.setAttribute("src", url);
element.type = "application/pdf";
element.type = mimeType;
element.style.overflow = "scroll";
element.style.height = "40rem";
element.style.overscrollBehavior = "contain";
e.style.display = "block";
e.insertAdjacentElement("afterend", element);
} else if (ext?.match(/(mp4|mov)/)) {
} else if (mimeType?.startsWith("video")) {
const element = document.createElement("video");
const node = document.createElement("source");
node.src = url;
element.controls = true;
node.type = `video/mp4`;
node.type = mimeType;
element.style.width = "100%";
e.style.display = "block";
element.appendChild(node);
e.insertAdjacentElement("afterend", element);
} else if (ext?.match(/(mp3)/)) {
} else if (mimeType?.startsWith("audio")) {
const element = document.createElement("audio");
element.style.display = "block";
element.src = url;
element.controls = true;
e.style.display = "block";
e.insertAdjacentElement("afterend", element);
} else {
console.warn(
`Not able to provide a preview for ${e.innerHTML}`,
);
console.warn(`Not able to provide a preview for this file.`);
}
})
.catch(console.error);