Object Kind (#397)

* add temp custom icons and tweak styles

* tweaks

* - added Object type structs
- added extension enums with variant macros
- improved explorer rendering
- added database migration hotfix

* - added dev only data folder
- improved explorer image loading

* macros do rule !

* remove baselining

Co-authored-by: Brendan Allan <brendonovich@outlook.com>
This commit is contained in:
Jamie PineandBrendan Allan authored and GitHub committed 2022-10-04 20:34:41 -07:00
1 parent 9bf071e5d9
commit d27ff3cd80
24 files changed
+848 -253

No files matched your search

+2 -2
View File
@@ -51,8 +51,8 @@ pub(crate) fn mount() -> RouterBuilder {
.spawn_job(Job::new(
ThumbnailJobInit {
location_id: args.id,
path: args.path,
background: false, // fix
path: PathBuf::new(),
background: true,
},
Box::new(ThumbnailJob {}),
))
+3
View File
@@ -46,6 +46,9 @@ const CONSOLE_LOG_FILTER: LevelFilter = LevelFilter::INFO;
impl Node {
pub async fn new(data_dir: impl AsRef<Path>) -> Result<(Arc<Node>, Arc<Router>), NodeError> {
let data_dir = data_dir.as_ref();
#[cfg(debug_assertions)]
let data_dir = data_dir.join("dev");
fs::create_dir_all(&data_dir).await?;
tracing_subscriber::registry()
+267
View File
@@ -0,0 +1,267 @@
use std::{
fmt::{Display, Formatter},
str::FromStr,
};
use int_enum::IntEnum;
use rspc::Type;
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Type, IntEnum)]
#[repr(u8)]
pub enum ObjectKind {
// A file that can not be identified by the indexer
Unknown = 0,
// A known filetype, but without specific support
Document = 1,
// A virtual filesystem directory
Folder = 2,
// A file that contains human-readable text
Text = 3,
// A virtual directory int
Package = 4,
// An image file
Image = 5,
// An audio file
Audio = 6,
// A video file
Video = 7,
// A compressed archive of data
Archive = 8,
// An executable, program or application
Executable = 9,
// A link to another object
Alias = 10,
// Raw bytes encrypted by Spacedrive with self contained metadata
Encrypted = 11,
// A link can open web pages, apps or Spaces
Key = 12,
// A link can open web pages, apps or Spaces
Link = 13,
// A special filetype that represents a preserved webpage
WebPageArchive = 14,
// A widget is a mini app that can be placed in a Space at various sizes, associated Widget struct required
Widget = 15,
// Albums can only have one level of children, and are associated with the Album struct
Album = 16,
// Its like a folder, but appears like a stack of files, designed for burst photos / associated groups of files
Collection = 17,
}
macro_rules! extension_enum {
(
Extension {
$( $variant:ident($type:ident), )*
}
) => {
#[derive(Debug, Serialize, Deserialize)]
pub enum Extension {
$( $variant($type), )*
}
// convert extension to object kind
impl From<Extension> for ObjectKind {
fn from(ext: Extension) -> Self {
match ext {
$( Extension::$variant(_) => ObjectKind::$variant, )*
}
}
}
impl Display for Extension {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
$( Extension::$variant(x) => write!(f, "{}", x), )*
}
}
}
};
}
extension_enum! {
Extension {
Unknown(String),
Video(VideoExtension),
Image(ImageExtension),
Audio(AudioExtension),
Archive(ArchiveExtension),
Executable(ExecutableExtension),
Text(TextExtension),
Encrypted(EncryptedExtension),
Key(KeyExtension),
}
}
/// Define a public enum with static array of all possible variants
/// including implementations to convert to/from string
macro_rules! enum_with_variants {
(
$(#[$enum_attr:meta])*
$enum_name:ident $static_array_name:ident {
$($(#[$variant_attr:meta])* $variant:ident, )*
}
) => {
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
#[serde(rename_all = "snake_case")]
$(#[$enum_attr])*
// construct enum
pub enum $enum_name {
$( $(#[$variant_attr])* $variant, )*
}
// a static array of all variants
pub static $static_array_name: &[$enum_name] = &[
$( $enum_name::$variant, )*
];
// convert from string
impl FromStr for $enum_name {
type Err = serde_json::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
serde_json::from_value(Value::String(s.to_string()))
}
}
// convert to string
impl std::fmt::Display for $enum_name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", serde_json::to_string(self).unwrap()) // SAFETY: This is safe
}
}
}
}
// video extensions
enum_with_variants! {
VideoExtension ALL_VIDEO_EXTENSIONS {
Avi,
Asf,
Mpeg,
Mts,
Mpg,
Mpe,
Qt,
Mov,
Swf,
Mjpeg,
Ts,
Mxf,
M2v,
M2ts,
Flv,
Wm,
#[serde(rename = "3gp")]
_3gp,
M4v,
Wmv,
Mp4,
Webm,
}
}
// image extensions
enum_with_variants! {
ImageExtension _ALL_IMAGE_EXTENSIONS {
Jpg,
Jpeg,
Png,
Gif,
Bmp,
Tiff,
Webp,
Svg,
Ico,
Heic,
}
}
// audio extensions
enum_with_variants! {
AudioExtension _ALL_AUDIO_EXTENSIONS {
Mp3,
M4a,
Wav,
Aiff,
Aif,
Flac,
Ogg,
Opus,
}
}
// archive extensions
enum_with_variants! {
ArchiveExtension _ALL_ARCHIVE_EXTENSIONS {
Zip,
Rar,
Tar,
Gz,
Bz2,
SevenZip,
}
}
// executable extensions
enum_with_variants! {
ExecutableExtension _ALL_EXECUTABLE_EXTENSIONS {
Exe,
App,
Apk,
Deb,
Dmg,
Pkg,
Rpm,
Msi,
}
}
// document extensions
enum_with_variants! {
DocumentExtension _ALL_DOCUMENT_EXTENSIONS {
Pdf,
Key,
Pages,
Numbers,
Doc,
Docx,
Xls,
Xlsx,
Ppt,
Pptx,
Odt,
Ods,
Odp,
Ics,
}
}
// text file extensions
enum_with_variants! {
TextExtension _ALL_TEXT_EXTENSIONS {
Txt,
Rtf,
Csv,
Html,
Css,
Json,
Yaml,
Xml,
Md,
}
}
// encrypted file extensions
enum_with_variants! {
EncryptedExtension _ALL_ENCRYPTED_EXTENSIONS {
Bit, // Spacedrive encrypted file
Box, // Spacedrive container
Block,// Spacedrive block storage,
}
}
// key extensions
enum_with_variants! {
KeyExtension _ALL_KEY_EXTENSIONS {
Pgp,
Pub,
Pem,
P12,
P8,
Keychain,
}
}
+1 -38
View File
@@ -1,5 +1,6 @@
pub mod cas;
pub mod identifier_job;
pub mod kind;
pub mod preview;
// Objects are primarily created by the identifier from Paths
@@ -31,41 +32,3 @@ pub enum ObjectData {
Object(Box<prisma::object::Data>),
Path(Box<prisma::file_path::Data>),
}
#[derive(Debug, Serialize, Deserialize, Type)]
pub enum ObjectKind {
// A file that can not be identified by the indexer
Unknown,
// A known filetype, but without specific support
Document,
// A virtual filesystem directory
Folder,
// A file that contains human-readable text
TextFile,
// A virtual directory int
Package,
// An image file
Image,
// An audio file
Audio,
// A video file
Video,
// A compressed archive of data
Archive,
// An executable, program or application
Executable,
// A link to another object
Alias,
// Raw bytes encrypted by Spacedrive with self contained metadata
EncryptedBytes,
// A link can open web pages, apps or Spaces
Link,
// A special filetype that represents a preserved webpage
WebPageArchive,
// A widget is a mini app that can be placed in a Space at various sizes, associated Widget struct required
Widget,
// Albums can only have one level of children, and are associated with the Album struct
Album,
// Its like a folder, but appears like a stack of files, designed for burst photos / associated groups of files
Collection,
}
+30 -40
View File
@@ -3,6 +3,7 @@ use crate::{
invalidate_query,
job::{JobError, JobReportUpdate, JobResult, JobState, StatefulJob, WorkerContext},
library::LibraryContext,
object::kind::{Extension, ImageExtension, VideoExtension, ALL_VIDEO_EXTENSIONS},
prisma::{file_path, location},
};
@@ -93,17 +94,20 @@ impl StatefulJob for ThumbnailJob {
let root_path = location.local_path.map(PathBuf::from).unwrap();
// query database for all image files in this location that need thumbnails
let image_files = get_files_by_extension(
let image_files = get_files_by_extensions(
&library_ctx,
state.init.location_id,
&state.init.path,
vec![
"png".to_string(),
"jpeg".to_string(),
"jpg".to_string(),
"gif".to_string(),
"webp".to_string(),
],
[
ImageExtension::Png,
ImageExtension::Jpeg,
ImageExtension::Jpg,
ImageExtension::Gif,
ImageExtension::Webp,
]
.into_iter()
.map(Extension::Image)
.collect(),
ThumbnailJobStepKind::Image,
)
.await?;
@@ -112,38 +116,16 @@ impl StatefulJob for ThumbnailJob {
#[cfg(feature = "ffmpeg")]
let all_files = {
// query database for all video files in this location that need thumbnails
let video_files = get_files_by_extension(
let video_files = get_files_by_extensions(
&library_ctx,
state.init.location_id,
&state.init.path,
// Some formats extracted from https://ffmpeg.org/ffmpeg-formats.html
vec![
"avi".to_string(),
"asf".to_string(),
"mpeg".to_string(),
// "mpg".to_string(),
"mts".to_string(),
"mpe".to_string(),
"vob".to_string(),
"qt".to_string(),
"mov".to_string(),
"asf".to_string(),
"asx".to_string(),
// "swf".to_string(),
"mjpeg".to_string(),
"ts".to_string(),
"mxf".to_string(),
// "m2v".to_string(),
"m2ts".to_string(),
"f4v".to_string(),
"wm".to_string(),
"3gp".to_string(),
"m4v".to_string(),
"wmv".to_string(),
"mp4".to_string(),
"webm".to_string(),
"flv".to_string(),
],
ALL_VIDEO_EXTENSIONS
.into_iter()
.map(Clone::clone)
.filter(can_generate_thumbnail_for_video)
.map(Extension::Video)
.collect(),
ThumbnailJobStepKind::Video,
)
.await?;
@@ -305,16 +287,16 @@ async fn generate_video_thumbnail<P: AsRef<Path>>(
Ok(())
}
async fn get_files_by_extension(
async fn get_files_by_extensions(
ctx: &LibraryContext,
location_id: i32,
path: impl AsRef<Path>,
extensions: Vec<String>,
extensions: Vec<Extension>,
kind: ThumbnailJobStepKind,
) -> Result<Vec<ThumbnailJobStep>, JobError> {
let mut params = vec![
file_path::location_id::equals(location_id),
file_path::extension::in_vec(extensions),
file_path::extension::in_vec(extensions.iter().map(|e| e.to_string()).collect()),
];
let path_str = path.as_ref().to_string_lossy().to_string();
@@ -334,3 +316,11 @@ async fn get_files_by_extension(
.map(|file_path| ThumbnailJobStep { file_path, kind })
.collect())
}
pub fn can_generate_thumbnail_for_video(video_extension: &VideoExtension) -> bool {
use VideoExtension::*;
match video_extension {
Mpg | Swf | M2v => false,
_ => true,
}
}