From 515fc0237ed60b75faa31efff1eadf671ce48096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADtor=20Vasconcellos?= Date: Tue, 16 Apr 2024 22:48:15 -0300 Subject: [PATCH] Add relation between MediaInfo and FilePath - Remove shared properties from MediaInfo and related structs - Implement Iterator for FFmpegDict --- core/prisma/schema.prisma | 39 +++++++++------------ crates/ffmpeg/src/dict.rs | 72 ++++++++++++++++++++++++++++++-------- crates/ffmpeg/src/probe.rs | 2 +- 3 files changed, 75 insertions(+), 38 deletions(-) diff --git a/core/prisma/schema.prisma b/core/prisma/schema.prisma index 2ff182b12995..3dfb05797504 100644 --- a/core/prisma/schema.prisma +++ b/core/prisma/schema.prisma @@ -188,6 +188,9 @@ model FilePath { date_modified DateTime? date_indexed DateTime? + media_info MediaInfo? @relation(fields: [media_info_id], references: [id], onDelete: SetNull) + media_info_id Int? + // key Key? @relation(fields: [key_id], references: [id]) @@unique([location_id, materialized_path, name, extension]) @@ -270,7 +273,6 @@ model ExifData { @@map("exif_data") } -/// @shared(id: object) model MediaInfo { id Int @id @default(autoincrement()) @@ -283,47 +285,46 @@ model MediaInfo { chapters MediaChapter[] programs MediaProgram[] - metadata MediaMetadata @relation(fields: [metadata_id], references: [id]) + metadata MediaMetadata @relation(fields: [metadata_id], references: [id], onDelete: Cascade) metadata_id Int @unique + files FilePath[] + @@map("media_info") } -/// @shared(id: object) model MediaChapter { chapter_id Int start BigInt? end BigInt? - metadata MediaMetadata @relation(fields: [metadata_id], references: [id]) + metadata MediaMetadata @relation(fields: [metadata_id], references: [id], onDelete: Cascade) metadata_id Int @unique - media_info MediaInfo @relation(fields: [media_info_id], references: [id]) + media_info MediaInfo @relation(fields: [media_info_id], references: [id], onDelete: Cascade) media_info_id Int @@id(name: "likeId", [media_info_id, chapter_id]) @@map("media_chapter") } -/// @shared(id: object) model MediaProgram { program_id Int name String? streams MediaStream[] - metadata MediaMetadata @relation(fields: [metadata_id], references: [id]) + metadata MediaMetadata @relation(fields: [metadata_id], references: [id], onDelete: Cascade) metadata_id Int @unique - media_info MediaInfo @relation(fields: [media_info_id], references: [id]) + media_info MediaInfo @relation(fields: [media_info_id], references: [id], onDelete: Cascade) media_info_id Int @@id(name: "likeId", [media_info_id, program_id]) @@map("media_program") } -/// @shared(id: object) model MediaStream { stream_id Int @@ -335,10 +336,10 @@ model MediaStream { time_base_real Decimal? dispositions String? - metadata MediaMetadata @relation(fields: [metadata_id], references: [id]) + metadata MediaMetadata @relation(fields: [metadata_id], references: [id], onDelete: Cascade) metadata_id Int @unique - program MediaProgram @relation(fields: [media_info_id, program_id], references: [media_info_id, program_id]) + program MediaProgram @relation(fields: [media_info_id, program_id], references: [media_info_id, program_id], onDelete: Cascade) program_id Int media_info_id Int @@ -347,7 +348,6 @@ model MediaStream { @@map("media_stream") } -/// @shared(id: object) model MediaCodec { id Int @id @default(autoincrement()) @@ -361,7 +361,7 @@ model MediaCodec { audio_props MediaAudioProps? subtitle_props MediaSubtitleProps? - codec MediaStream @relation(fields: [media_info_id, program_id, stream_id], references: [media_info_id, program_id, stream_id]) + codec MediaStream @relation(fields: [media_info_id, program_id, stream_id], references: [media_info_id, program_id, stream_id], onDelete: Cascade) stream_id Int program_id Int media_info_id Int @@ -370,7 +370,6 @@ model MediaCodec { @@map("media_codec") } -/// @shared(id: object) model MediaVideoProps { id Int @id @default(autoincrement()) @@ -388,13 +387,12 @@ model MediaVideoProps { aspect_ratio_Den Int? properties String - codec MediaCodec @relation(fields: [codec_id], references: [id]) + codec MediaCodec @relation(fields: [codec_id], references: [id], onDelete: Cascade) codec_id Int @unique @@map("media_video") } -/// @shared(id: object) model MediaAudioProps { id Int @id @default(autoincrement()) @@ -405,26 +403,24 @@ model MediaAudioProps { bit_per_sample Int? channel_layout String? - codec MediaCodec @relation(fields: [codec_id], references: [id]) + codec MediaCodec @relation(fields: [codec_id], references: [id], onDelete: Cascade) codec_id Int @unique @@map("media_audio") } -/// @shared(id: object) model MediaSubtitleProps { id Int @id @default(autoincrement()) width Int? height Int? - codec MediaCodec @relation(fields: [codec_id], references: [id]) + codec MediaCodec @relation(fields: [codec_id], references: [id], onDelete: Cascade) codec_id Int @unique @@map("media_subtitle") } -/// @shared(id: object) model MediaMetadata { id Int @id @default(autoincrement()) @@ -467,14 +463,13 @@ model MediaMetadata { @@map("media_metadata") } -/// @shared(id: object) model CustomMetadata { id Int @id @default(autoincrement()) key String? value String? - media_metadata MediaMetadata @relation(fields: [media_metadata_id], references: [id]) + media_metadata MediaMetadata @relation(fields: [media_metadata_id], references: [id], onDelete: Cascade) media_metadata_id Int @unique @@map("custom_metadata") diff --git a/crates/ffmpeg/src/dict.rs b/crates/ffmpeg/src/dict.rs index 83cabc6e5bd7..931e243da3f1 100644 --- a/crates/ffmpeg/src/dict.rs +++ b/crates/ffmpeg/src/dict.rs @@ -1,28 +1,29 @@ use crate::{error::Error, utils::check_error}; -use std::{ffi::CString, ptr}; +use std::{ + ffi::{CStr, CString}, + ptr, +}; -use ffmpeg_sys_next::{av_dict_free, av_dict_set, AVDictionary}; +use ffmpeg_sys_next::{ + av_dict_free, av_dict_iterate, av_dict_set, AVDictionary, AVDictionaryEntry, +}; #[derive(Debug)] -pub(crate) struct FFmpegDict { - data: *mut AVDictionary, -} +pub(crate) struct FFmpegDict(*mut AVDictionary); impl FFmpegDict { - pub(crate) fn new() -> Self { - Self { - data: ptr::null_mut(), - } + pub(crate) fn new(av_dict: Option<*mut AVDictionary>) -> Self { + Self(av_dict.unwrap_or(ptr::null_mut())) } pub(crate) fn as_mut_ptr(&mut self) -> *mut AVDictionary { - self.data + self.0 } pub(crate) fn set(&mut self, key: CString, value: CString) -> Result<(), Error> { check_error( - unsafe { av_dict_set(&mut self.data, key.as_ptr(), value.as_ptr(), 0) }, + unsafe { av_dict_set(&mut self.0, key.as_ptr(), value.as_ptr(), 0) }, "Fail to set dictionary key-value pair", )?; @@ -31,7 +32,7 @@ impl FFmpegDict { pub(crate) fn reset(&mut self, key: CString) -> Result<(), Error> { check_error( - unsafe { av_dict_set(&mut self.data, key.as_ptr(), ptr::null(), 0) }, + unsafe { av_dict_set(&mut self.0, key.as_ptr(), ptr::null(), 0) }, "Fail to set dictionary key-value pair", )?; @@ -41,9 +42,50 @@ impl FFmpegDict { impl Drop for FFmpegDict { fn drop(&mut self) { - if !self.data.is_null() { - unsafe { av_dict_free(&mut self.data) }; - self.data = std::ptr::null_mut(); + if !self.0.is_null() { + unsafe { av_dict_free(&mut self.0) }; + self.0 = std::ptr::null_mut(); + } + } +} + +impl<'a> IntoIterator for &'a FFmpegDict { + type Item = (String, String); + type IntoIter = FFmpegDictIter<'a>; + + #[inline] + fn into_iter(self) -> FFmpegDictIter<'a> { + FFmpegDictIter { + dict: self.0, + prev: std::ptr::null(), + _lifetime: std::marker::PhantomData, } } } + +pub(crate) struct FFmpegDictIter<'a> { + dict: *mut AVDictionary, + prev: *const AVDictionaryEntry, + _lifetime: std::marker::PhantomData<&'a ()>, +} + +impl<'a> Iterator for FFmpegDictIter<'a> { + type Item = (String, String); + + fn next(&mut self) -> Option<(String, String)> { + while unsafe { + self.prev = av_dict_iterate(self.dict, self.prev); + self.prev + } != ptr::null() + { + let key = unsafe { CStr::from_ptr((*self.prev).key) }; + let value = unsafe { CStr::from_ptr((*self.prev).value) }; + return Some(( + key.to_string_lossy().into_owned(), + value.to_string_lossy().into_owned(), + )); + } + + None + } +} diff --git a/crates/ffmpeg/src/probe.rs b/crates/ffmpeg/src/probe.rs index e87d9573c785..730a9c40aa78 100644 --- a/crates/ffmpeg/src/probe.rs +++ b/crates/ffmpeg/src/probe.rs @@ -14,7 +14,7 @@ pub fn probe(filename: impl AsRef) -> Result<(), Error> { unsafe { av_log_set_level(AV_LOG_FATAL) }; // Dictionary to store format options - let mut format_opts = FFmpegDict::new(); + let mut format_opts = FFmpegDict::new(None); // Some MPEGTS specific option (copied and pasted from ffprobe) let scan_all_pmts = CString::new("scan_all_pmts").expect(CSTRING_ERROR_MSG); format_opts.set(