mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-20 17:02:53 -04:00
spz file uploads (#924)
This commit is contained in:
@@ -152,6 +152,7 @@ CREATE TABLE media_files (
|
||||
-- * `vmd`
|
||||
-- * `pmx`
|
||||
-- * `csv`
|
||||
-- * `spz` Gaussian splats
|
||||
media_type VARCHAR(16) NOT NULL,
|
||||
|
||||
-- Broad class of media:
|
||||
|
||||
+1
@@ -172,6 +172,7 @@ impl RawUserBookmarkRecord {
|
||||
MediaFileType::Fbx => Some("3d file".to_string()),
|
||||
MediaFileType::Glb => Some("3d file".to_string()),
|
||||
MediaFileType::Gltf => Some("3d file".to_string()),
|
||||
MediaFileType::Spz => Some("3d splat".to_string()),
|
||||
MediaFileType::SceneRon => Some("3d scene".to_string()),
|
||||
MediaFileType::SceneJson => Some("3d scene".to_string()),
|
||||
MediaFileType::Pmd => Some("polygon data".to_string()),
|
||||
|
||||
@@ -41,6 +41,11 @@ pub enum MediaFileType {
|
||||
/// glTF files (for Bevy)
|
||||
Gltf,
|
||||
|
||||
/// Spz Gaussian splats
|
||||
/// This is a standard file format for Gaussian Splats, used by World Labs.
|
||||
/// The mime type is application/gzip.
|
||||
Spz,
|
||||
|
||||
/// Bevy's scene files (in RON; Rusty Object Notation)
|
||||
/// This will be replaced with another format in future versions of Bevy
|
||||
SceneRon,
|
||||
@@ -130,6 +135,7 @@ impl MediaFileType {
|
||||
Self::Fbx => "fbx",
|
||||
Self::Glb => "glb",
|
||||
Self::Gltf => "gltf",
|
||||
Self::Spz => "spz",
|
||||
Self::SceneRon => "scene_ron",
|
||||
Self::SceneJson => "scene_json",
|
||||
Self::Pmd => "pmd",
|
||||
@@ -154,6 +160,7 @@ impl MediaFileType {
|
||||
"fbx" => Ok(Self::Fbx),
|
||||
"glb" => Ok(Self::Glb),
|
||||
"gltf" => Ok(Self::Gltf),
|
||||
"spz" => Ok(Self::Spz),
|
||||
"scene_ron" => Ok(Self::SceneRon),
|
||||
"scene_json" => Ok(Self::SceneJson),
|
||||
"pmd" => Ok(Self::Pmd),
|
||||
@@ -181,6 +188,7 @@ impl MediaFileType {
|
||||
Self::Fbx,
|
||||
Self::Glb,
|
||||
Self::Gltf,
|
||||
Self::Spz,
|
||||
Self::SceneRon,
|
||||
Self::SceneJson,
|
||||
Self::Pmd,
|
||||
@@ -237,6 +245,7 @@ mod tests {
|
||||
assert_serialization(MediaFileType::Fbx, "fbx");
|
||||
assert_serialization(MediaFileType::Glb, "glb");
|
||||
assert_serialization(MediaFileType::Gltf, "gltf");
|
||||
assert_serialization(MediaFileType::Spz, "spz");
|
||||
assert_serialization(MediaFileType::SceneRon, "scene_ron");
|
||||
assert_serialization(MediaFileType::SceneJson, "scene_json");
|
||||
assert_serialization(MediaFileType::Pmd, "pmd");
|
||||
@@ -264,6 +273,7 @@ mod tests {
|
||||
assert_eq!(MediaFileType::Fbx.to_str(), "fbx");
|
||||
assert_eq!(MediaFileType::Glb.to_str(), "glb");
|
||||
assert_eq!(MediaFileType::Gltf.to_str(), "gltf");
|
||||
assert_eq!(MediaFileType::Spz.to_str(), "spz");
|
||||
assert_eq!(MediaFileType::SceneRon.to_str(), "scene_ron");
|
||||
assert_eq!(MediaFileType::SceneJson.to_str(), "scene_json");
|
||||
assert_eq!(MediaFileType::Pmd.to_str(), "pmd");
|
||||
@@ -287,6 +297,7 @@ mod tests {
|
||||
assert_eq!(MediaFileType::from_str("fbx").unwrap(), MediaFileType::Fbx);
|
||||
assert_eq!(MediaFileType::from_str("glb").unwrap(), MediaFileType::Glb);
|
||||
assert_eq!(MediaFileType::from_str("gltf").unwrap(), MediaFileType::Gltf);
|
||||
assert_eq!(MediaFileType::from_str("spz").unwrap(), MediaFileType::Spz);
|
||||
assert_eq!(MediaFileType::from_str("scene_ron").unwrap(), MediaFileType::SceneRon);
|
||||
assert_eq!(MediaFileType::from_str("scene_json").unwrap(), MediaFileType::SceneJson);
|
||||
assert_eq!(MediaFileType::from_str("pmd").unwrap(), MediaFileType::Pmd);
|
||||
@@ -309,7 +320,7 @@ mod tests {
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
let mut variants = MediaFileType::all_variants();
|
||||
assert_eq!(variants.len(), 19);
|
||||
assert_eq!(variants.len(), 20);
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::Audio));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::Image));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::Video));
|
||||
@@ -317,6 +328,7 @@ mod tests {
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::Fbx));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::Glb));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::Gltf));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::Spz));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::SceneRon));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::SceneJson));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::Pmd));
|
||||
|
||||
+22
-1
@@ -26,6 +26,8 @@ use crate::state::server_state::ServerState;
|
||||
// TODO(bt,2023-12-20): THIS CODE NEEDS CLEANUP. This has been cargo culted three+ times.
|
||||
// It's ridiculous and complicated.
|
||||
|
||||
const GZIP_MIME_TYPE : &str = "application/gzip";
|
||||
|
||||
pub enum SuccessCase {
|
||||
MediaAlreadyUploaded {
|
||||
existing_media_file_token: MediaFileToken,
|
||||
@@ -168,9 +170,21 @@ pub async fn process_upload_media_file(
|
||||
let mut maybe_codec_name = None;
|
||||
let mut media_file_type = None;
|
||||
|
||||
let mut is_spz = false;
|
||||
|
||||
if let Some(mimetype) = maybe_mimetype.as_deref() {
|
||||
|
||||
if !allowed_mimetypes.contains(mimetype) {
|
||||
// `.spz` files are Gaussian splats
|
||||
let is_spz_extension = upload_media_request.file_name
|
||||
.as_deref()
|
||||
.map(|filename| filename.trim().to_ascii_lowercase().ends_with(".spz"))
|
||||
.unwrap_or(false);
|
||||
|
||||
is_spz = mimetype == GZIP_MIME_TYPE && is_spz_extension;
|
||||
|
||||
let is_allowed_mimetype_bypass = is_spz;
|
||||
|
||||
if !allowed_mimetypes.contains(mimetype) && !is_allowed_mimetype_bypass {
|
||||
// NB: Don't let our error message inject malicious strings
|
||||
let filtered_mimetype = mimetype
|
||||
.chars()
|
||||
@@ -240,6 +254,8 @@ pub async fn process_upload_media_file(
|
||||
"image/jpeg" => false,
|
||||
"image/png" => false,
|
||||
"image/webp" => false,
|
||||
// Don't decode gzip, gaussian splats (spz)
|
||||
"application/gzip" => false,
|
||||
_ => true,
|
||||
};
|
||||
|
||||
@@ -258,6 +274,10 @@ pub async fn process_upload_media_file(
|
||||
}
|
||||
}
|
||||
|
||||
if is_spz {
|
||||
media_file_type = Some(MediaFileType::Spz);
|
||||
}
|
||||
|
||||
if media_file_type.is_none() && maybe_mimetype.is_none() {
|
||||
// https://research.cs.wisc.edu/graphics/Courses/cs-838-1999/Jeff/BVH.html
|
||||
const BVH_HEADER : &[u8] = "HIERARCHY".as_bytes();
|
||||
@@ -298,6 +318,7 @@ pub async fn process_upload_media_file(
|
||||
MediaFileType::Fbx => MediaFileClass::Dimensional,
|
||||
MediaFileType::Glb => MediaFileClass::Dimensional,
|
||||
MediaFileType::Gltf => MediaFileClass::Dimensional,
|
||||
MediaFileType::Spz => MediaFileClass::Dimensional,
|
||||
MediaFileType::SceneRon => MediaFileClass::Dimensional,
|
||||
MediaFileType::SceneJson => MediaFileClass::Dimensional,
|
||||
MediaFileType::Pmd => MediaFileClass::Dimensional,
|
||||
|
||||
Reference in New Issue
Block a user