entertainarr_adapter_sqlite/
media.rs1use std::path::PathBuf;
2
3use anyhow::Context;
4use entertainarr_domain::media::entity::MediaFile;
5
6use crate::Wrapper;
7
8impl<'r> crate::Wrapper<MediaFile> {
9 pub(crate) fn from_index_row(
10 row: &'r sqlx::sqlite::SqliteRow,
11 idx: &mut crate::IndexIter,
12 ) -> Result<Self, sqlx::Error> {
13 use sqlx::Row;
14
15 Ok(crate::Wrapper(MediaFile {
16 id: row.try_get(idx.next())?,
17 disk: row.try_get(idx.next())?,
18 filepath: row
19 .try_get(idx.next())
20 .map(|value: String| PathBuf::from(value))?,
21 filename: row.try_get(idx.next())?,
22 content_type: row.try_get(idx.next())?,
23 file_size: row.try_get(idx.next())?,
24 created_at: row.try_get(idx.next())?,
25 updated_at: row.try_get(idx.next())?,
26 }))
27 }
28}
29
30impl<'r> sqlx::FromRow<'r, sqlx::sqlite::SqliteRow> for crate::Wrapper<MediaFile> {
31 fn from_row(row: &'r sqlx::sqlite::SqliteRow) -> Result<Self, sqlx::Error> {
32 let mut idx = crate::IndexIter::default();
33 Self::from_index_row(row, &mut idx)
34 }
35}
36
37const FIND_MEDIA_FILE_QUERY: &str = r#"select mf.id, mf.disk, mf.filepath, mf.filename, mf.content_type, mf.file_size, mf.created_at, mf.updated_at
38from media_files as mf
39where mf.id = ?"#;
40
41impl entertainarr_domain::media::prelude::MediaRepository for crate::Pool {
42 #[tracing::instrument(
43 skip_all,
44 fields(
45 otel.kind = "client",
46 db.system = "sqlite",
47 db.name = "media",
48 db.operation = "insert",
49 db.sql.table = "media_files",
50 db.query.text = FIND_MEDIA_FILE_QUERY,
51 db.response.returned_rows = tracing::field::Empty,
52 error.type = tracing::field::Empty,
53 error.message = tracing::field::Empty,
54 error.stacktrace = tracing::field::Empty,
55 ),
56 err(Debug),
57 )]
58 async fn find_by_id(&self, media_id: u64) -> anyhow::Result<Option<MediaFile>> {
59 sqlx::query_as(FIND_MEDIA_FILE_QUERY)
60 .bind(media_id as i64)
61 .fetch_optional(self.as_ref())
62 .await
63 .inspect(crate::record_optional)
64 .inspect_err(crate::record_error)
65 .map(Wrapper::maybe_inner)
66 .context("unable to fetch media")
67 }
68
69 #[tracing::instrument(
70 skip_all,
71 fields(
72 otel.kind = "client",
73 db.system = "sqlite",
74 db.name = "media",
75 db.operation = "insert",
76 db.sql.table = "media_files",
77 db.query.text = tracing::field::Empty,
78 db.response.returned_rows = tracing::field::Empty,
79 error.type = tracing::field::Empty,
80 error.message = tracing::field::Empty,
81 error.stacktrace = tracing::field::Empty,
82 ),
83 err(Debug),
84 )]
85 async fn upsert(
86 &self,
87 disk: &str,
88 input: &[entertainarr_domain::media::entity::MediaFileInput],
89 ) -> anyhow::Result<u64> {
90 let mut qb = sqlx::QueryBuilder::new(
91 "insert into media_files (disk, filepath, filename, content_type, file_size) ",
92 );
93 qb.push_values(input.iter(), |mut b, input| {
94 b.push_bind(disk)
95 .push_bind(input.filepath.to_string_lossy())
96 .push_bind(&input.filename)
97 .push_bind(&input.content_type)
98 .push_bind(input.file_size as i64);
99 });
100 qb.push(" on conflict (disk, filepath) do update set updated_at = current_timestamp");
101 tracing::Span::current().record("db.query.text", qb.sql());
102 qb.build()
103 .execute(self.as_ref())
104 .await
105 .map(|res| res.rows_affected())
106 .context("unable to insert files")
107 }
108}