Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 12 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions mlm_db/src/impls.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod categories;
pub mod flags;
pub mod language;
pub mod lists;
pub mod meta;
pub mod old_categories;
pub mod series;
Expand Down
1,055 changes: 856 additions & 199 deletions mlm_db/src/impls/categories.rs

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions mlm_db/src/impls/lists.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use crate::ListItemTorrent;

impl ListItemTorrent {
pub fn id(&self) -> String {
self.torrent_id
.clone()
.or_else(|| self.mam_id.map(|id| id.to_string()))
.unwrap_or_default()
Comment on lines +4 to +8
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Do not collapse missing IDs to an empty string.

Returning "" for missing torrent_id/mam_id makes distinct items share the same identifier and can cause invalid links or dedupe collisions. Prefer returning Option<String> and handling absence at call sites.

Proposed change
 impl ListItemTorrent {
-    pub fn id(&self) -> String {
+    pub fn id(&self) -> Option<String> {
         self.torrent_id
             .clone()
-            .or_else(|| self.mam_id.map(|id| id.to_string()))
-            .unwrap_or_default()
+            .or_else(|| self.mam_id.map(|id| id.to_string()))
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pub fn id(&self) -> String {
self.torrent_id
.clone()
.or_else(|| self.mam_id.map(|id| id.to_string()))
.unwrap_or_default()
pub fn id(&self) -> Option<String> {
self.torrent_id
.clone()
.or_else(|| self.mam_id.map(|id| id.to_string()))
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mlm_db/src/impls/lists.rs` around lines 4 - 8, The id() accessor currently
returns a String and collapses missing torrent_id/mam_id to "", causing
identifier collisions; change its signature from pub fn id(&self) -> String to
pub fn id(&self) -> Option<String>, remove the final unwrap_or_default(), and
return the combined Option (self.torrent_id.clone().or_else(||
self.mam_id.map(|id| id.to_string()))). Update all call sites that assume a
String to handle Option<String> (e.g., propagate the option, return None, or
explicitly handle missing IDs) so missing IDs are represented as None instead of
an empty string.

}
}
36 changes: 20 additions & 16 deletions mlm_db/src/impls/meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ use itertools::Itertools as _;

use crate::{
Flags, MediaType, MetadataSource, OldCategory, TorrentMeta, TorrentMetaDiff, TorrentMetaField,
VipStatus, impls::format_serie,
VipStatus, ids, impls::format_serie,
};

impl TorrentMeta {
pub fn mam_id(&self) -> Option<u64> {
self.ids.get(ids::MAM).and_then(|id| id.parse().ok())
}

pub fn matches(&self, other: &TorrentMeta) -> bool {
self.media_type.matches(other.media_type)
&& self.language == other.language
Expand Down Expand Up @@ -35,11 +39,16 @@ impl TorrentMeta {

pub fn diff(&self, other: &TorrentMeta) -> Vec<TorrentMetaDiff> {
let mut diff = vec![];
if self.mam_id != other.mam_id {
if self.ids != other.ids {
let format_ids = |ids: &std::collections::BTreeMap<String, String>| {
ids.iter()
.map(|(key, value)| format!("{key}: {value}"))
.join("\n")
};
diff.push(TorrentMetaDiff {
field: TorrentMetaField::MamId,
from: self.mam_id.to_string(),
to: other.mam_id.to_string(),
field: TorrentMetaField::Ids,
from: format_ids(&self.ids),
to: format_ids(&other.ids),
});
}
if self.vip_status != other.vip_status
Expand Down Expand Up @@ -96,16 +105,8 @@ impl TorrentMeta {
if self.categories != other.categories {
diff.push(TorrentMetaDiff {
field: TorrentMetaField::Categories,
from: self
.categories
.iter()
.map(|cat| cat.as_raw_str().to_string())
.join(", "),
to: other
.categories
.iter()
.map(|cat| cat.as_raw_str().to_string())
.join(", "),
from: self.categories.iter().map(ToString::to_string).join(", "),
to: other.categories.iter().map(ToString::to_string).join(", "),
});
}
if self.language != other.language {
Expand Down Expand Up @@ -218,12 +219,13 @@ impl MediaType {
impl std::fmt::Display for TorrentMetaField {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TorrentMetaField::MamId => write!(f, "mam_id"),
TorrentMetaField::Ids => write!(f, "ids"),
TorrentMetaField::Vip => write!(f, "vip"),
TorrentMetaField::Cat => write!(f, "cat"),
TorrentMetaField::MediaType => write!(f, "media_type"),
TorrentMetaField::MainCat => write!(f, "main_cat"),
TorrentMetaField::Categories => write!(f, "categories"),
TorrentMetaField::Tags => write!(f, "tags"),
TorrentMetaField::Language => write!(f, "language"),
TorrentMetaField::Flags => write!(f, "flags"),
TorrentMetaField::Filetypes => write!(f, "filetypes"),
Expand All @@ -243,6 +245,8 @@ impl fmt::Display for MetadataSource {
match self {
MetadataSource::Mam => write!(f, "MaM"),
MetadataSource::Manual => write!(f, "Manual"),
MetadataSource::File => write!(f, "File"),
MetadataSource::Match => write!(f, "Match"),
}
}
}
10 changes: 10 additions & 0 deletions mlm_db/src/impls/series.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ impl TryFrom<(String, String)> for Series {
}
}

impl std::fmt::Display for Series {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.name)?;
if !self.entries.0.is_empty() {
write!(f, " #{}", self.entries)?;
}
Ok(())
}
}

impl SeriesEntries {
pub fn contains(&self, num: f32) -> bool {
self.0.iter().any(|s| s.contains(num))
Expand Down
58 changes: 38 additions & 20 deletions mlm_db/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@ mod v14;
mod v15;
mod v16;
mod v17;
mod v18;

use std::collections::HashMap;

use anyhow::Result;
use mlm_parse::normalize_title;
use native_db::Models;
pub use native_db::Database;
use native_db::transaction::RwTransaction;
use native_db::{Database, ToInput, db_type};
use native_db::{ToInput, db_type};
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
use tokio::sync::MutexGuard;
Expand All @@ -33,6 +35,13 @@ pub static MODELS: Lazy<Models> = Lazy::new(|| {
let mut models = Models::new();
models.define::<v01::Config>().unwrap();

models.define::<v18::Torrent>().unwrap();
models.define::<v18::SelectedTorrent>().unwrap();
models.define::<v18::DuplicateTorrent>().unwrap();
models.define::<v18::ErroredTorrent>().unwrap();
models.define::<v18::Event>().unwrap();
models.define::<v18::ListItem>().unwrap();

models.define::<v17::Torrent>().unwrap();
models.define::<v17::SelectedTorrent>().unwrap();
models.define::<v17::DuplicateTorrent>().unwrap();
Expand Down Expand Up @@ -130,27 +139,27 @@ pub static MODELS: Lazy<Models> = Lazy::new(|| {
});

pub type Config = v01::Config;
pub type Torrent = v17::Torrent;
pub type TorrentKey = v17::TorrentKey;
pub type SelectedTorrent = v17::SelectedTorrent;
pub type SelectedTorrentKey = v17::SelectedTorrentKey;
pub type DuplicateTorrent = v17::DuplicateTorrent;
pub type ErroredTorrent = v17::ErroredTorrent;
pub type ErroredTorrentKey = v17::ErroredTorrentKey;
pub type Torrent = v18::Torrent;
pub type TorrentKey = v18::TorrentKey;
pub type SelectedTorrent = v18::SelectedTorrent;
pub type SelectedTorrentKey = v18::SelectedTorrentKey;
pub type DuplicateTorrent = v18::DuplicateTorrent;
pub type ErroredTorrent = v18::ErroredTorrent;
pub type ErroredTorrentKey = v18::ErroredTorrentKey;
pub type ErroredTorrentId = v11::ErroredTorrentId;
pub type Event = v17::Event;
pub type EventKey = v17::EventKey;
pub type EventType = v17::EventType;
pub type Event = v18::Event;
pub type EventKey = v18::EventKey;
pub type EventType = v18::EventType;
pub type List = v05::List;
pub type ListKey = v05::ListKey;
pub type ListItem = v05::ListItem;
pub type ListItemKey = v05::ListItemKey;
pub type ListItemTorrent = v04::ListItemTorrent;
pub type TorrentMeta = v17::TorrentMeta;
pub type TorrentMetaDiff = v17::TorrentMetaDiff;
pub type TorrentMetaField = v17::TorrentMetaField;
pub type ListItem = v18::ListItem;
pub type ListItemKey = v18::ListItemKey;
pub type ListItemTorrent = v18::ListItemTorrent;
pub type TorrentMeta = v18::TorrentMeta;
pub type TorrentMetaDiff = v18::TorrentMetaDiff;
pub type TorrentMetaField = v18::TorrentMetaField;
pub type VipStatus = v11::VipStatus;
pub type MetadataSource = v10::MetadataSource;
pub type MetadataSource = v18::MetadataSource;
pub type OldDbMainCat = v01::MainCat;
pub type MainCat = v12::MainCat;
pub type Uuid = v03::Uuid;
Expand All @@ -164,14 +173,14 @@ pub type Size = v03::Size;
pub type TorrentCost = v04::TorrentCost;
pub type TorrentStatus = v04::TorrentStatus;
pub type LibraryMismatch = v08::LibraryMismatch;
pub type ClientStatus = v08::ClientStatus;
pub type ClientStatus = v18::ClientStatus;
pub type AudiobookCategory = v06::AudiobookCategory;
pub type EbookCategory = v06::EbookCategory;
pub type MusicologyCategory = v16::MusicologyCategory;
pub type RadioCategory = v16::RadioCategory;
pub type OldCategory = v16::OldCategory;
pub type MediaType = v13::MediaType;
pub type Category = v15::Category;
pub use v18::Category;

#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum OldMainCat {
Expand Down Expand Up @@ -284,3 +293,12 @@ impl DatabaseExt for Database<'_> {
self
}
}

pub mod ids {
pub const ABS: &str = "abs";
pub const ASIN: &str = "asin";
pub const GOODREADS: &str = "goodreads";
pub const ISBN: &str = "isbn";
pub const MAM: &str = "mam";
pub const NEXTORY: &str = "nextory";
}
4 changes: 2 additions & 2 deletions mlm_db/src/v03.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::{v01, v02, v04, v05, v06};
use native_db::{Key, ToKey, native_db};
use native_model::{Model, native_model};
use native_db::{native_db, Key, ToKey};
use native_model::{native_model, Model};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use time::{OffsetDateTime, UtcDateTime};
Expand Down
12 changes: 11 additions & 1 deletion mlm_db/src/v04.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{v01, v03, v05, v06, v07};
use super::{v01, v03, v05, v06, v07, v18};
use native_db::{ToKey, native_db};
use native_model::{Model, native_model};
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -290,3 +290,13 @@ impl From<v07::EventType> for EventType {
}
}
}

impl From<v18::ListItemTorrent> for ListItemTorrent {
fn from(t: v18::ListItemTorrent) -> Self {
Self {
mam_id: t.mam_id.unwrap(),
status: t.status,
at: t.at,
}
}
}
24 changes: 23 additions & 1 deletion mlm_db/src/v05.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{v01, v03, v04, v06};
use super::{v01, v03, v04, v06, v18};
use native_db::{ToKey, native_db};
use native_model::{Model, native_model};
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -139,3 +139,25 @@ impl From<v06::Torrent> for Torrent {
}
}
}

impl From<v18::ListItem> for ListItem {
fn from(t: v18::ListItem) -> Self {
Self {
guid: t.guid,
list_id: t.list_id,
title: t.title,
authors: t.authors,
series: t.series,
cover_url: t.cover_url,
book_url: t.book_url,
isbn: t.isbn,
prefer_format: t.prefer_format,
allow_audio: t.allow_audio,
audio_torrent: t.audio_torrent.map(Into::into),
allow_ebook: t.allow_ebook,
ebook_torrent: t.ebook_torrent.map(Into::into),
created_at: t.created_at,
marked_done_at: t.marked_done_at,
}
}
}
11 changes: 10 additions & 1 deletion mlm_db/src/v08.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{v01, v03, v04, v05, v06, v07, v09, v10, v11};
use super::{v01, v03, v04, v05, v06, v07, v09, v10, v11, v18};
use native_db::{ToKey, native_db};
use native_model::{Model, native_model};
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -456,3 +456,12 @@ impl From<v11::TorrentMetaField> for TorrentMetaField {
}
}
}

impl From<v18::ClientStatus> for ClientStatus {
fn from(value: v18::ClientStatus) -> Self {
match value {
v18::ClientStatus::NotInClient => Self::NotInClient,
v18::ClientStatus::RemovedFromTracker => Self::RemovedFromMam,
}
}
}
Loading
Loading