mirror of
https://github.com/narrrl/proton-stream.git
synced 2026-11-04 00:47:00 +01:00
bug fixes, windows build
This commit is contained in:
1 parent
a1b345a029
commit
14c48428a3
29 files changed
+2514
-170
No files matched your search
@@ -21,7 +21,7 @@ use crate::metadata::{EpisodeGuide, EpisodeMetadata, MetadataRecord, ProviderId,
|
||||
use crate::naming::{self, ParsedName};
|
||||
|
||||
/// Bump when adding a migration.
|
||||
pub const SCHEMA_VERSION: i64 = 4;
|
||||
pub const SCHEMA_VERSION: i64 = 5;
|
||||
|
||||
const MIGRATION_V1: &str = r#"
|
||||
CREATE TABLE nodes (
|
||||
@@ -118,6 +118,16 @@ CREATE TABLE episode_metadata (
|
||||
);
|
||||
"#;
|
||||
|
||||
/// Which rows the viewer chose themselves.
|
||||
///
|
||||
/// A column rather than a separate table of overrides, because it changes
|
||||
/// nothing about what the row *holds* — only about who put it there, and
|
||||
/// therefore whether a match run may replace it. See
|
||||
/// [`MetadataRecord::manual`].
|
||||
const MIGRATION_V5: &str = r#"
|
||||
ALTER TABLE title_metadata ADD COLUMN manual INTEGER NOT NULL DEFAULT 0;
|
||||
"#;
|
||||
|
||||
/// What `season = -1` means in `episode_metadata`: no season at all.
|
||||
const ABSOLUTE_SEASON: i64 = -1;
|
||||
|
||||
@@ -219,6 +229,13 @@ impl Catalog {
|
||||
tx.commit()?;
|
||||
}
|
||||
|
||||
if version < 5 {
|
||||
let tx = self.conn.transaction()?;
|
||||
tx.execute_batch(MIGRATION_V5)?;
|
||||
tx.pragma_update(None, "user_version", 5)?;
|
||||
tx.commit()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -384,7 +401,8 @@ impl Catalog {
|
||||
pub fn all_metadata(&self) -> Result<HashMap<String, MetadataRecord>> {
|
||||
let mut statement = self.conn.prepare(
|
||||
"SELECT title_key, provider, matched, fetched_at, remote_id, name, original_name,
|
||||
overview, year, kind, poster_url, backdrop_url, rating, genres, episodes, url
|
||||
overview, year, kind, poster_url, backdrop_url, rating, genres, episodes, url,
|
||||
manual
|
||||
FROM title_metadata",
|
||||
)?;
|
||||
let rows = statement.query_map([], row_to_metadata)?;
|
||||
@@ -404,7 +422,8 @@ impl Catalog {
|
||||
.conn
|
||||
.query_row(
|
||||
"SELECT title_key, provider, matched, fetched_at, remote_id, name, original_name,
|
||||
overview, year, kind, poster_url, backdrop_url, rating, genres, episodes, url
|
||||
overview, year, kind, poster_url, backdrop_url, rating, genres, episodes,
|
||||
url, manual
|
||||
FROM title_metadata WHERE title_key = ?1",
|
||||
params![title_key],
|
||||
row_to_metadata,
|
||||
@@ -422,8 +441,9 @@ impl Catalog {
|
||||
self.conn.execute(
|
||||
"INSERT INTO title_metadata (
|
||||
title_key, provider, matched, fetched_at, remote_id, name, original_name,
|
||||
overview, year, kind, poster_url, backdrop_url, rating, genres, episodes, url
|
||||
) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16)
|
||||
overview, year, kind, poster_url, backdrop_url, rating, genres, episodes, url,
|
||||
manual
|
||||
) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17)
|
||||
ON CONFLICT (title_key) DO UPDATE SET
|
||||
provider = excluded.provider,
|
||||
matched = excluded.matched,
|
||||
@@ -439,7 +459,8 @@ impl Catalog {
|
||||
rating = excluded.rating,
|
||||
genres = excluded.genres,
|
||||
episodes = excluded.episodes,
|
||||
url = excluded.url",
|
||||
url = excluded.url,
|
||||
manual = excluded.manual",
|
||||
params![
|
||||
record.title_key,
|
||||
record.provider.as_str(),
|
||||
@@ -457,11 +478,31 @@ impl Catalog {
|
||||
data.map(|data| data.genres.join("\n")),
|
||||
data.and_then(|data| data.episodes),
|
||||
data.and_then(|data| data.url.as_deref()),
|
||||
record.manual as i64,
|
||||
],
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Forget one title's answer entirely, episodes included.
|
||||
///
|
||||
/// Different from storing a miss: a miss says "asked, nothing there" and is
|
||||
/// trusted for a while, where this leaves no row at all and the next match
|
||||
/// run treats the title as one it has never seen. That is what "match this
|
||||
/// one automatically again" has to mean after a hand-picked match, since a
|
||||
/// hand-picked one never expires.
|
||||
pub fn forget_metadata(&self, title_key: &str) -> Result<()> {
|
||||
self.conn.execute(
|
||||
"DELETE FROM title_metadata WHERE title_key = ?1",
|
||||
params![title_key],
|
||||
)?;
|
||||
self.conn.execute(
|
||||
"DELETE FROM episode_metadata WHERE title_key = ?1",
|
||||
params![title_key],
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Forget every stored answer.
|
||||
///
|
||||
/// What "match again" and "turn enrichment off" both do. Deliberately not
|
||||
@@ -618,6 +659,7 @@ fn row_to_metadata(row: &rusqlite::Row<'_>) -> rusqlite::Result<Option<MetadataR
|
||||
provider,
|
||||
metadata,
|
||||
fetched_at,
|
||||
manual: row.get::<_, i64>(16)? != 0,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -893,6 +935,7 @@ mod tests {
|
||||
provider: ProviderId::AniList,
|
||||
metadata: Some(found()),
|
||||
fetched_at: 1_700_000_000,
|
||||
manual: false,
|
||||
};
|
||||
|
||||
catalog.set_metadata(&record).expect("store");
|
||||
@@ -907,6 +950,76 @@ mod tests {
|
||||
assert_eq!(catalog.all_metadata().expect("read all").len(), 1);
|
||||
}
|
||||
|
||||
/// A hand-picked match has to read back as hand-picked, or the next match
|
||||
/// run overwrites the one thing the viewer corrected themselves.
|
||||
#[test]
|
||||
fn a_hand_picked_match_says_so_when_it_is_read_back() {
|
||||
let catalog = Catalog::in_memory().expect("open");
|
||||
let record = MetadataRecord {
|
||||
title_key: "fate stay night heavens feel".into(),
|
||||
provider: ProviderId::AniList,
|
||||
metadata: Some(found()),
|
||||
fetched_at: 1_700_000_000,
|
||||
manual: true,
|
||||
};
|
||||
catalog.set_metadata(&record).expect("store");
|
||||
|
||||
let read = catalog.metadata(&record.title_key).expect("read");
|
||||
assert_eq!(read.as_ref().map(|read| read.manual), Some(true));
|
||||
assert_eq!(read, Some(record.clone()));
|
||||
|
||||
// And an automatic write over the top says so in turn: the flag is the
|
||||
// row's, not the title's.
|
||||
catalog
|
||||
.set_metadata(&MetadataRecord {
|
||||
manual: false,
|
||||
..record.clone()
|
||||
})
|
||||
.expect("store again");
|
||||
assert_eq!(
|
||||
catalog
|
||||
.metadata(&record.title_key)
|
||||
.expect("read")
|
||||
.map(|read| read.manual),
|
||||
Some(false)
|
||||
);
|
||||
}
|
||||
|
||||
/// Forgetting is not the same as storing a miss: it must leave no row at
|
||||
/// all, so the next match run treats the title as one it never asked about.
|
||||
#[test]
|
||||
fn forgetting_a_title_leaves_neither_its_match_nor_its_episodes() {
|
||||
let mut catalog = Catalog::in_memory().expect("open");
|
||||
catalog
|
||||
.set_metadata(&MetadataRecord {
|
||||
title_key: "attack on titan".into(),
|
||||
provider: ProviderId::AniList,
|
||||
metadata: Some(found()),
|
||||
fetched_at: 1,
|
||||
manual: true,
|
||||
})
|
||||
.expect("store");
|
||||
catalog
|
||||
.set_episode_metadata(
|
||||
"attack on titan",
|
||||
ProviderId::AniList,
|
||||
1,
|
||||
&[EpisodeMetadata {
|
||||
season: None,
|
||||
number: 1,
|
||||
name: Some("To You, in 2000 Years".into()),
|
||||
overview: None,
|
||||
still_url: None,
|
||||
air_date: None,
|
||||
}],
|
||||
)
|
||||
.expect("store episodes");
|
||||
|
||||
catalog.forget_metadata("attack on titan").expect("forget");
|
||||
assert!(catalog.metadata("attack on titan").expect("read").is_none());
|
||||
assert!(catalog.all_episode_metadata().expect("read").is_empty());
|
||||
}
|
||||
|
||||
/// The negative answer is the one that has to survive a round trip: without
|
||||
/// it every render re-asks about every unmatched title.
|
||||
#[test]
|
||||
@@ -917,6 +1030,7 @@ mod tests {
|
||||
provider: ProviderId::AniList,
|
||||
metadata: None,
|
||||
fetched_at: 1_700_000_000,
|
||||
manual: false,
|
||||
};
|
||||
catalog.set_metadata(&record).expect("store");
|
||||
|
||||
@@ -959,6 +1073,7 @@ mod tests {
|
||||
provider: ProviderId::AniList,
|
||||
metadata: Some(found()),
|
||||
fetched_at: 1,
|
||||
manual: false,
|
||||
})
|
||||
.expect("store");
|
||||
|
||||
|
||||
Reference in new issue
Block a user