variphx commited on
Commit
68fc6df
·
1 Parent(s): 6b23458

fix: change table schemas to match submission requirements and migrate some module names

Browse files
Cargo.lock CHANGED
@@ -63,6 +63,7 @@ dependencies = [
63
  "tower-http",
64
  "tracing",
65
  "tracing-subscriber",
 
66
  "utoipa",
67
  "utoipa-axum",
68
  "utoipa-swagger-ui",
@@ -941,6 +942,15 @@ dependencies = [
941
  "windows-sys 0.60.2",
942
  ]
943
 
 
 
 
 
 
 
 
 
 
944
  [[package]]
945
  name = "http"
946
  version = "1.3.1"
@@ -1336,6 +1346,33 @@ version = "0.1.2"
1336
  source = "registry+https://github.com/rust-lang/crates.io-index"
1337
  checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
1338
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1339
  [[package]]
1340
  name = "macro_rules_attribute"
1341
  version = "0.2.2"
@@ -2058,6 +2095,7 @@ dependencies = [
2058
  "base64 0.22.1",
2059
  "bytes",
2060
  "encoding_rs",
 
2061
  "futures-core",
2062
  "futures-util",
2063
  "h2",
@@ -2943,6 +2981,21 @@ dependencies = [
2943
  "tracing-log",
2944
  ]
2945
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2946
  [[package]]
2947
  name = "try-lock"
2948
  version = "0.2.5"
@@ -3031,6 +3084,18 @@ dependencies = [
3031
  "percent-encoding",
3032
  ]
3033
 
 
 
 
 
 
 
 
 
 
 
 
 
3034
  [[package]]
3035
  name = "utf8_iter"
3036
  version = "1.0.4"
 
63
  "tower-http",
64
  "tracing",
65
  "tracing-subscriber",
66
+ "translators",
67
  "utoipa",
68
  "utoipa-axum",
69
  "utoipa-swagger-ui",
 
942
  "windows-sys 0.60.2",
943
  ]
944
 
945
+ [[package]]
946
+ name = "html-escape"
947
+ version = "0.2.13"
948
+ source = "registry+https://github.com/rust-lang/crates.io-index"
949
+ checksum = "6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476"
950
+ dependencies = [
951
+ "utf8-width",
952
+ ]
953
+
954
  [[package]]
955
  name = "http"
956
  version = "1.3.1"
 
1346
  source = "registry+https://github.com/rust-lang/crates.io-index"
1347
  checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
1348
 
1349
+ [[package]]
1350
+ name = "macon"
1351
+ version = "1.3.0"
1352
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1353
+ checksum = "4e2360fdc60235ad3db2d65bb74539e0813c20a626f7e330ce9a64b2d4e513f0"
1354
+ dependencies = [
1355
+ "macon_api",
1356
+ "macon_derive",
1357
+ ]
1358
+
1359
+ [[package]]
1360
+ name = "macon_api"
1361
+ version = "1.3.0"
1362
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1363
+ checksum = "2330ee9375fc537d78cc438ed16695a5d0201f0be9d977bf6bc4ac11e5b4e177"
1364
+
1365
+ [[package]]
1366
+ name = "macon_derive"
1367
+ version = "1.3.0"
1368
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1369
+ checksum = "ccd7e1436ba066ad10c8b43ac4a00d304322831b90f039f1d83815f846fee024"
1370
+ dependencies = [
1371
+ "proc-macro2",
1372
+ "quote",
1373
+ "syn",
1374
+ ]
1375
+
1376
  [[package]]
1377
  name = "macro_rules_attribute"
1378
  version = "0.2.2"
 
2095
  "base64 0.22.1",
2096
  "bytes",
2097
  "encoding_rs",
2098
+ "futures-channel",
2099
  "futures-core",
2100
  "futures-util",
2101
  "h2",
 
2981
  "tracing-log",
2982
  ]
2983
 
2984
+ [[package]]
2985
+ name = "translators"
2986
+ version = "0.1.5"
2987
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2988
+ checksum = "2957ae11f61a19fdc8c93cef9e0af3e8638c4672dce5f8d578e6232f55220873"
2989
+ dependencies = [
2990
+ "futures",
2991
+ "html-escape",
2992
+ "macon",
2993
+ "regex",
2994
+ "reqwest",
2995
+ "tokio",
2996
+ "urlencoding",
2997
+ ]
2998
+
2999
  [[package]]
3000
  name = "try-lock"
3001
  version = "0.2.5"
 
3084
  "percent-encoding",
3085
  ]
3086
 
3087
+ [[package]]
3088
+ name = "urlencoding"
3089
+ version = "2.1.3"
3090
+ source = "registry+https://github.com/rust-lang/crates.io-index"
3091
+ checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
3092
+
3093
+ [[package]]
3094
+ name = "utf8-width"
3095
+ version = "0.1.7"
3096
+ source = "registry+https://github.com/rust-lang/crates.io-index"
3097
+ checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3"
3098
+
3099
  [[package]]
3100
  name = "utf8_iter"
3101
  version = "1.0.4"
Cargo.toml CHANGED
@@ -22,6 +22,7 @@ tower = "0.5.2"
22
  tower-http = { version = "0.6.6", features = ["cors", "trace"] }
23
  tracing = "0.1.41"
24
  tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
 
25
  utoipa = "5.4.0"
26
  utoipa-axum = "0.2.0"
27
  utoipa-swagger-ui = { version = "9.0.2", features = ["axum"] }
 
22
  tower-http = { version = "0.6.6", features = ["cors", "trace"] }
23
  tracing = "0.1.41"
24
  tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
25
+ translators = { version = "0.1.5", features = ["google", "tokio-async"] }
26
  utoipa = "5.4.0"
27
  utoipa-axum = "0.2.0"
28
  utoipa-swagger-ui = { version = "9.0.2", features = ["axum"] }
migrations/2025-08-19-034235_create_videos/up.sql CHANGED
@@ -1,7 +1,5 @@
1
  CREATE TABLE videos (
2
  id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
3
- l SMALLINT NOT NULL,
4
- v SMALLINT NOT NULL,
5
- watch_url TEXT NOT NULL,
6
- UNIQUE (l, v)
7
  )
 
1
  CREATE TABLE videos (
2
  id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
3
+ name TEXT NOT NULL UNIQUE,
4
+ watch_url TEXT NOT NULL
 
 
5
  )
migrations/2025-08-19-041510_create_keyframes/up.sql CHANGED
@@ -1,7 +1,8 @@
1
  CREATE TABLE keyframes (
2
  id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
 
 
 
3
  video_id BIGINT NOT NULL REFERENCES videos (id),
4
- video_related_frame_id SMALLINT NOT NULL,
5
- video_related_frame_timestamp REAL NOT NULL,
6
- UNIQUE (video_id, video_related_frame_id)
7
  )
 
1
  CREATE TABLE keyframes (
2
  id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
3
+ name TEXT NOT NULL,
4
+ frame_index BIGINT NOT NULL,
5
+ frame_timestamp REAL NOT NULL,
6
  video_id BIGINT NOT NULL REFERENCES videos (id),
7
+ UNIQUE (video_id, frame_index)
 
 
8
  )
src/constants/mod.rs CHANGED
@@ -1,4 +1,2 @@
1
  pub const QDRANT_KEYFRAME_COLLECTION_NAME: &str = "keyframes";
2
- pub const QDRANT_KEYFRAME_COLLECTION_IMAGE_VECTOR_NAME: &str = "images";
3
- pub const QDRANT_KEYFRAME_COLLECTION_OBJECT_VECTOR_NAME: &str = "objects";
4
  pub const OPENAPI_TAG: &str = "AIC Server";
 
1
  pub const QDRANT_KEYFRAME_COLLECTION_NAME: &str = "keyframes";
 
 
2
  pub const OPENAPI_TAG: &str = "AIC Server";
src/controllers/v1/mod.rs CHANGED
@@ -3,12 +3,12 @@ use utoipa_axum::router::OpenApiRouter;
3
  use crate::models::states::AppState;
4
 
5
  mod keyframes;
6
- mod vectors;
7
  mod videos;
8
 
9
  pub fn router() -> OpenApiRouter<AppState> {
10
  OpenApiRouter::new()
11
  .nest("/keyframes", keyframes::router())
12
  .nest("/videos", videos::router())
13
- .nest("/vectors", vectors::router())
14
  }
 
3
  use crate::models::states::AppState;
4
 
5
  mod keyframes;
6
+ mod queries;
7
  mod videos;
8
 
9
  pub fn router() -> OpenApiRouter<AppState> {
10
  OpenApiRouter::new()
11
  .nest("/keyframes", keyframes::router())
12
  .nest("/videos", videos::router())
13
+ .nest("/vectors", queries::router())
14
  }
src/controllers/v1/{vectors → queries}/keyframes/mod.rs RENAMED
@@ -7,7 +7,7 @@ use crate::{
7
  dtos::vectors::keyframes::{VectorizedKeyframeDto, VectorizedKeyframeRequestDto},
8
  states::AppState,
9
  },
10
- services::vector_queries::keyframes::VectorizedKeyframeService,
11
  };
12
 
13
  pub fn router() -> OpenApiRouter<AppState> {
 
7
  dtos::vectors::keyframes::{VectorizedKeyframeDto, VectorizedKeyframeRequestDto},
8
  states::AppState,
9
  },
10
+ services::queries::keyframes::VectorizedKeyframeService,
11
  };
12
 
13
  pub fn router() -> OpenApiRouter<AppState> {
src/controllers/v1/{vectors → queries}/mod.rs RENAMED
File without changes
src/models/dtos/keyframes/mod.rs CHANGED
@@ -4,23 +4,22 @@ use crate::models::entities::{keyframes::KeyframeEntity, videos::VideoEntity};
4
  pub struct KeyframeDto {
5
  id: i64,
6
  path: String,
7
- timestamp: f32,
 
8
  }
9
 
10
  impl From<(KeyframeEntity, VideoEntity)> for KeyframeDto {
11
  fn from((keyframe, video): (KeyframeEntity, VideoEntity)) -> Self {
12
  let id = keyframe.id();
13
- let timestamp = keyframe.video_related_frame_timestamp();
14
- let path = format!(
15
- "/static/keyframes/L{}_V{:0>3}/{:0>3}.jpg",
16
- video.l(),
17
- video.v(),
18
- keyframe.video_related_frame_id()
19
- );
20
  Self {
21
  id,
22
  path,
23
- timestamp,
 
24
  }
25
  }
26
  }
 
4
  pub struct KeyframeDto {
5
  id: i64,
6
  path: String,
7
+ frame_index: i64,
8
+ frame_timestamp: f32,
9
  }
10
 
11
  impl From<(KeyframeEntity, VideoEntity)> for KeyframeDto {
12
  fn from((keyframe, video): (KeyframeEntity, VideoEntity)) -> Self {
13
  let id = keyframe.id();
14
+ let frame_index = keyframe.frame_index();
15
+ let frame_timestamp = keyframe.frame_timestamp();
16
+ let video_id = video.id();
17
+ let path = format!("/static/keyframes/{}/{:0>3}.jpg", video_id, keyframe.name());
 
 
 
18
  Self {
19
  id,
20
  path,
21
+ frame_index,
22
+ frame_timestamp,
23
  }
24
  }
25
  }
src/models/dtos/vectors/keyframes/requests/mod.rs CHANGED
@@ -2,6 +2,7 @@
2
  pub struct VectorizedKeyframeRequestDto {
3
  prompt: String,
4
  top_k: u64,
 
5
  }
6
 
7
  impl VectorizedKeyframeRequestDto {
@@ -12,4 +13,8 @@ impl VectorizedKeyframeRequestDto {
12
  pub fn top_k(&self) -> u64 {
13
  self.top_k
14
  }
 
 
 
 
15
  }
 
2
  pub struct VectorizedKeyframeRequestDto {
3
  prompt: String,
4
  top_k: u64,
5
+ object_threshold: f64,
6
  }
7
 
8
  impl VectorizedKeyframeRequestDto {
 
13
  pub fn top_k(&self) -> u64 {
14
  self.top_k
15
  }
16
+
17
+ pub fn object_threshold(&self) -> f64 {
18
+ self.object_threshold
19
+ }
20
  }
src/models/entities/keyframes/mod.rs CHANGED
@@ -3,8 +3,9 @@
3
  #[diesel(check_for_backend(diesel::pg::Pg))]
4
  pub struct KeyframeEntity {
5
  id: i64,
6
- video_related_frame_id: i16,
7
- video_related_frame_timestamp: f32,
 
8
  }
9
 
10
  impl KeyframeEntity {
@@ -12,11 +13,19 @@ impl KeyframeEntity {
12
  self.id
13
  }
14
 
15
- pub fn video_related_frame_id(&self) -> i16 {
16
- self.video_related_frame_id
17
  }
18
 
19
- pub fn video_related_frame_timestamp(&self) -> f32 {
20
- self.video_related_frame_timestamp
 
 
 
 
 
 
 
 
21
  }
22
  }
 
3
  #[diesel(check_for_backend(diesel::pg::Pg))]
4
  pub struct KeyframeEntity {
5
  id: i64,
6
+ name: String,
7
+ frame_index: i64,
8
+ frame_timestamp: f32,
9
  }
10
 
11
  impl KeyframeEntity {
 
13
  self.id
14
  }
15
 
16
+ pub fn name(&self) -> &str {
17
+ &self.name
18
  }
19
 
20
+ pub fn frame_index(&self) -> i64 {
21
+ self.frame_index
22
+ }
23
+
24
+ pub fn frame_timestamp(&self) -> f32 {
25
+ self.frame_timestamp
26
+ }
27
+
28
+ pub fn name_mut(&mut self) -> &mut String {
29
+ &mut self.name
30
  }
31
  }
src/models/entities/videos/mod.rs CHANGED
@@ -3,8 +3,7 @@
3
  #[diesel(check_for_backend(diesel::pg::Pg))]
4
  pub struct VideoEntity {
5
  id: i64,
6
- l: i16,
7
- v: i16,
8
  watch_url: String,
9
  }
10
 
@@ -13,15 +12,11 @@ impl VideoEntity {
13
  self.id
14
  }
15
 
16
- pub fn l(&self) -> i16 {
17
- self.l
18
- }
19
-
20
- pub fn v(&self) -> i16 {
21
- self.v
22
- }
23
-
24
  pub fn watch_url_mut(&mut self) -> &mut String {
25
  &mut self.watch_url
26
  }
 
 
 
 
27
  }
 
3
  #[diesel(check_for_backend(diesel::pg::Pg))]
4
  pub struct VideoEntity {
5
  id: i64,
6
+ name: String,
 
7
  watch_url: String,
8
  }
9
 
 
12
  self.id
13
  }
14
 
 
 
 
 
 
 
 
 
15
  pub fn watch_url_mut(&mut self) -> &mut String {
16
  &mut self.watch_url
17
  }
18
+
19
+ pub fn name_mut(&mut self) -> &mut String {
20
+ &mut self.name
21
+ }
22
  }
src/models/states/mod.rs CHANGED
@@ -5,11 +5,13 @@ use deadpool_diesel::{
5
  postgres::{Manager, Pool},
6
  };
7
  use qdrant_client::Qdrant;
 
8
 
9
  #[derive(Clone)]
10
  pub struct AppState {
11
  diesel_pool: Pool,
12
  qdrant_client: Arc<Qdrant>,
 
13
  }
14
 
15
  impl AppState {
@@ -17,6 +19,7 @@ impl AppState {
17
  Ok(Self {
18
  diesel_pool: Self::diesel_pool_helper()?,
19
  qdrant_client: Self::qdrant_client_helper().await?,
 
20
  })
21
  }
22
 
@@ -45,4 +48,8 @@ impl AppState {
45
  pub fn qdrant_client(&self) -> &Qdrant {
46
  &self.qdrant_client
47
  }
 
 
 
 
48
  }
 
5
  postgres::{Manager, Pool},
6
  };
7
  use qdrant_client::Qdrant;
8
+ use translators::GoogleTranslator;
9
 
10
  #[derive(Clone)]
11
  pub struct AppState {
12
  diesel_pool: Pool,
13
  qdrant_client: Arc<Qdrant>,
14
+ translator: GoogleTranslator,
15
  }
16
 
17
  impl AppState {
 
19
  Ok(Self {
20
  diesel_pool: Self::diesel_pool_helper()?,
21
  qdrant_client: Self::qdrant_client_helper().await?,
22
+ translator: GoogleTranslator::default(),
23
  })
24
  }
25
 
 
48
  pub fn qdrant_client(&self) -> &Qdrant {
49
  &self.qdrant_client
50
  }
51
+
52
+ pub fn translator(&self) -> &GoogleTranslator {
53
+ &self.translator
54
+ }
55
  }
src/schema.rs CHANGED
@@ -3,24 +3,21 @@
3
  diesel::table! {
4
  keyframes (id) {
5
  id -> Int8,
 
6
  video_id -> Int8,
7
- video_related_frame_id -> Int2,
8
- video_related_frame_timestamp -> Float4,
9
  }
10
  }
11
 
12
  diesel::table! {
13
  videos (id) {
14
  id -> Int8,
15
- l -> Int2,
16
- v -> Int2,
17
  watch_url -> Text,
18
  }
19
  }
20
 
21
  diesel::joinable!(keyframes -> videos (video_id));
22
 
23
- diesel::allow_tables_to_appear_in_same_query!(
24
- keyframes,
25
- videos,
26
- );
 
3
  diesel::table! {
4
  keyframes (id) {
5
  id -> Int8,
6
+ name -> Text,
7
  video_id -> Int8,
8
+ frame_index -> Int8,
9
+ frame_timestamp -> Float4,
10
  }
11
  }
12
 
13
  diesel::table! {
14
  videos (id) {
15
  id -> Int8,
16
+ name -> Text,
 
17
  watch_url -> Text,
18
  }
19
  }
20
 
21
  diesel::joinable!(keyframes -> videos (video_id));
22
 
23
+ diesel::allow_tables_to_appear_in_same_query!(keyframes, videos,);
 
 
 
src/services/mod.rs CHANGED
@@ -1,4 +1,4 @@
1
  pub mod embeddings;
2
  pub mod keyframes;
3
- pub mod vector_queries;
4
  pub mod videos;
 
1
  pub mod embeddings;
2
  pub mod keyframes;
3
+ pub mod queries;
4
  pub mod videos;
src/services/{vector_queries → queries}/keyframes/mod.rs RENAMED
@@ -3,12 +3,10 @@ use qdrant_client::{
3
  qdrant::{PrefetchQueryBuilder, Query, QueryPointsBuilder},
4
  };
5
  use rayon::iter::{IntoParallelIterator, ParallelIterator};
 
6
 
7
  use crate::{
8
- constants::{
9
- QDRANT_KEYFRAME_COLLECTION_IMAGE_VECTOR_NAME, QDRANT_KEYFRAME_COLLECTION_NAME,
10
- QDRANT_KEYFRAME_COLLECTION_OBJECT_VECTOR_NAME,
11
- },
12
  models::{dtos::vectors::keyframes::VectorizedKeyframeDto, states::AppState},
13
  services::embeddings::texts::TextEmbeddingService,
14
  };
@@ -16,12 +14,14 @@ use crate::{
16
  #[derive(Clone, Copy)]
17
  pub struct VectorizedKeyframeService<'a> {
18
  client: &'a Qdrant,
 
19
  }
20
 
21
  impl<'a> From<&'a AppState> for VectorizedKeyframeService<'a> {
22
  fn from(value: &'a AppState) -> Self {
23
  Self {
24
  client: value.qdrant_client(),
 
25
  }
26
  }
27
  }
@@ -37,7 +37,8 @@ impl<'a> VectorizedKeyframeService<'a> {
37
  text: &str,
38
  top_k: u64,
39
  ) -> anyhow::Result<Vec<VectorizedKeyframeDto>> {
40
- let embeddings = self.embed_text(text).await?;
 
41
 
42
  let query_result = self
43
  .client
@@ -46,11 +47,11 @@ impl<'a> VectorizedKeyframeService<'a> {
46
  .add_prefetch(
47
  PrefetchQueryBuilder::default()
48
  .query(Query::new_nearest(embeddings.clone()))
49
- .using(QDRANT_KEYFRAME_COLLECTION_IMAGE_VECTOR_NAME)
50
- .limit(100u64),
51
  )
52
  .query(Query::new_nearest(embeddings))
53
- .using(QDRANT_KEYFRAME_COLLECTION_OBJECT_VECTOR_NAME)
54
  .limit(top_k),
55
  )
56
  .await?
 
3
  qdrant::{PrefetchQueryBuilder, Query, QueryPointsBuilder},
4
  };
5
  use rayon::iter::{IntoParallelIterator, ParallelIterator};
6
+ use translators::{GoogleTranslator, Translator};
7
 
8
  use crate::{
9
+ constants::QDRANT_KEYFRAME_COLLECTION_NAME,
 
 
 
10
  models::{dtos::vectors::keyframes::VectorizedKeyframeDto, states::AppState},
11
  services::embeddings::texts::TextEmbeddingService,
12
  };
 
14
  #[derive(Clone, Copy)]
15
  pub struct VectorizedKeyframeService<'a> {
16
  client: &'a Qdrant,
17
+ translator: &'a GoogleTranslator,
18
  }
19
 
20
  impl<'a> From<&'a AppState> for VectorizedKeyframeService<'a> {
21
  fn from(value: &'a AppState) -> Self {
22
  Self {
23
  client: value.qdrant_client(),
24
+ translator: value.translator(),
25
  }
26
  }
27
  }
 
37
  text: &str,
38
  top_k: u64,
39
  ) -> anyhow::Result<Vec<VectorizedKeyframeDto>> {
40
+ let translated_text = self.translator.translate_async(text, "vi", "en").await?;
41
+ let embeddings = self.embed_text(&translated_text).await?;
42
 
43
  let query_result = self
44
  .client
 
47
  .add_prefetch(
48
  PrefetchQueryBuilder::default()
49
  .query(Query::new_nearest(embeddings.clone()))
50
+ .using("images")
51
+ .limit(top_k * 10),
52
  )
53
  .query(Query::new_nearest(embeddings))
54
+ .using("objects")
55
  .limit(top_k),
56
  )
57
  .await?
src/services/{vector_queries → queries}/mod.rs RENAMED
File without changes