spz file uploads (#924)

This commit is contained in:
Brandon Thomas
2025-12-09 00:33:14 -05:00
committed by GitHub
parent cd283bf8de
commit 99fd390bc5
4 changed files with 37 additions and 2 deletions
+1
View File
@@ -152,6 +152,7 @@ CREATE TABLE media_files (
-- * `vmd`
-- * `pmx`
-- * `csv`
-- * `spz` Gaussian splats
media_type VARCHAR(16) NOT NULL,
-- Broad class of media:
@@ -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));
@@ -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,