Skip to content

Commit

Permalink
Get it working enough again so lists update n such
Browse files Browse the repository at this point in the history
  • Loading branch information
iAverages committed Sep 1, 2024
1 parent 2f3f754 commit 1428053
Show file tree
Hide file tree
Showing 16 changed files with 742 additions and 630 deletions.
932 changes: 466 additions & 466 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion apps/api/src/consts.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
// The number of parameters in MySQL must fit in a `u16`.
pub const BIND_LIMIT: usize = 65535;
pub const MYSQL_PARAM_BIND_LIMIT: usize = 65535;
22 changes: 22 additions & 0 deletions apps/api/src/importer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ use tokio::time::sleep;
use crate::anilist::{
get_anime_from_anilist_result, get_animes_from_anilist, MAX_ANILIST_PER_QUERY,
};
use crate::consts::MYSQL_PARAM_BIND_LIMIT;
use crate::models::anime::{insert_animes, InsertAnime};
use crate::models::anime_relations::create_anime_relation;
use crate::models::anime_users::link_user_to_anime;

#[derive(Clone, Debug, Serialize)]
Expand Down Expand Up @@ -336,9 +338,29 @@ impl Importer {

let _ = insert_animes(&self.db, formatted).await;
let _ = link_user_to_anime(&self.db, items).await;

let _ = self.proces_relations().await;
}
}

async fn proces_relations(&self) {
let insert_items: Vec<(u32, u32, String)> = self
.relation_cache
.iter()
.take(MYSQL_PARAM_BIND_LIMIT / 3)
.flat_map(|entry| {
let anime_id = entry.0;
let relations = entry.1;

relations
.iter()
.map(|rel| (*anime_id as u32, rel.0, rel.1.clone()))
})
.collect();

let _ = create_anime_relation(&self.db, insert_items).await;
}

fn get_items_to_process(&self, max: usize) -> Vec<(u32, Vec<AnimeUserEntry>)> {
self.queue
.iter()
Expand Down
5 changes: 2 additions & 3 deletions apps/api/src/mal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ pub struct AnimePicture {

#[derive(Deserialize, Serialize, Clone)]
pub struct AnimeBroadcast {
pub day_of_the_week: String,
pub start_time: String,
pub day_of_the_week: Option<String>,
pub start_time: Option<String>,
}

#[derive(Deserialize, Serialize, Clone)]
Expand Down Expand Up @@ -68,7 +68,6 @@ pub async fn get_mal_user_list(
let text = res.text().await?;
let anime: MalAnimeListResponse = serde_json::from_str(&text)
.with_context(|| format!("Unable to deserialise response. Body was: \"{}\"", text))?;

let paging = anime.paging.clone();

tracing::info!("Got {} anime from MAL", anime.data.len());
Expand Down
31 changes: 31 additions & 0 deletions apps/api/src/models/anime_relations.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use sqlx::{MySql, Pool, QueryBuilder};

pub async fn create_anime_relation(
db: &Pool<MySql>,
items: Vec<(u32, u32, String)>,
) -> Result<(), anyhow::Error> {
if items.is_empty() {
return Ok(());
}
let mut query_builder = QueryBuilder::new(
r#"
INSERT INTO anime_relations (anime_id, relation_id, relation)
"#,
);

query_builder.push_values(items.iter(), |mut b, item| {
b.push_bind(item.0)
.push_bind(item.1)
.push_bind(item.2.clone());
});

query_builder.push("ON DUPLICATE KEY UPDATE relation = VALUES(relation)");

let q = query_builder.build();

q.execute(db).await.expect("Failed to insert anime");

tracing::info!("Inserted {} anime relations", items.len());

Ok(())
}
7 changes: 4 additions & 3 deletions apps/api/src/models/anime_users.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use chrono::NaiveDateTime;
use serde::Deserialize;
use sqlx::{MySql, Pool, QueryBuilder};

use crate::consts::BIND_LIMIT;
use crate::consts::MYSQL_PARAM_BIND_LIMIT;
use crate::importer::{AnimeUserEntry, AnimeWatchStatus};

pub struct DBAnimeUser {
Expand Down Expand Up @@ -45,7 +45,8 @@ pub async fn link_user_to_anime(
.push_bind(0);
});

query_builder.push("ON DUPLICATE KEY UPDATE status = VALUES(status), watch_priority = VALUES(watch_priority), updated_at = VALUES(updated_at)");
query_builder
.push("ON DUPLICATE KEY UPDATE status = VALUES(status), updated_at = VALUES(updated_at)");

let q = query_builder.build();

Expand All @@ -69,7 +70,7 @@ pub async fn update_watch_priority(db: &Pool<MySql>, user_id: String, data: Watc
let mut index = 1;
let user_id = Arc::new(user_id);

let groups = data.ids.chunks(BIND_LIMIT / 3);
let groups = data.ids.chunks(MYSQL_PARAM_BIND_LIMIT / 3);

for group in groups {
query_builder.push_values(group.iter(), |mut b, id| {
Expand Down
1 change: 1 addition & 0 deletions apps/api/src/models/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod anime;
pub mod anime_relations;
pub mod anime_users;
pub mod user;
1 change: 1 addition & 0 deletions apps/api/src/models/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ pub struct DBUser {
pub mal_id: i32,
pub mal_access_token: String,
pub mal_refresh_token: String,
pub list_last_update: NaiveDateTime,
pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime,
pub deleted_at: Option<NaiveDateTime>,
Expand Down
40 changes: 23 additions & 17 deletions apps/api/src/routes/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,23 +117,29 @@ pub async fn handle_mal_callback(
let reqwest = state.reqwest.clone();
let mal_user_list = get_mal_user_list(reqwest, user).await;

if let Ok(mal) = mal_user_list {
let ids = mal
.data
.iter()
.map(|item| AnimeUserEntry {
status: item
.list_status
.status
.parse::<AnimeWatchStatus>()
.map_err(|_| AnimeWatchStatus::Watching)
.expect("Failed to parse watch status"),
user_id: user_id.clone(),
anime_id: item.node.id,
})
.collect::<Vec<_>>();
let mut importer = state.importer.lock().await;
importer.add_all(ids);
match mal_user_list {
Ok(mal) => {
let ids = mal
.data
.iter()
.map(|item| AnimeUserEntry {
status: item
.list_status
.status
.parse::<AnimeWatchStatus>()
.map_err(|_| AnimeWatchStatus::Watching)
.expect("Failed to parse watch status"),
user_id: user_id.clone(),
anime_id: item.node.id,
})
.collect::<Vec<_>>();
let mut importer = state.importer.lock().await;
importer.add_all(ids);
}
Err(err) => {
// TODO: Handle better?
tracing::error!("{:?}", err)
}
}

let html = Html::from("<html><script>window.close()</script></html>");
Expand Down
42 changes: 42 additions & 0 deletions apps/api/src/routes/user.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use axum::extract::State;
use axum::Json;
use axum::{http::StatusCode, response::IntoResponse, Extension};
use chrono::{Duration, Utc};
use serde::Serialize;
use serde_json::json;

use crate::helpers::json_response;
use crate::importer::{AnimeUserEntry, AnimeWatchStatus};
use crate::mal::get_mal_user_list;
use crate::models::anime::get_released_animes_by_id;
use crate::models::anime_users::{
get_user_entrys, link_user_to_anime, update_watch_priority, DBAnimeUser, WatchPriorityUpdate,
Expand All @@ -31,6 +34,45 @@ pub async fn get_list(
Extension(user): Extension<DBUser>,
) -> impl IntoResponse {
let user_id = user.id.clone();

let now = Utc::now().naive_utc();
let five_minutes_ago = now - Duration::minutes(5);

if user.list_last_update < five_minutes_ago {
// Update list in background
let user = user.clone();
let user_id = user.id.clone();
let state = state.clone();
tokio::spawn(async move {
let mal_user_list = get_mal_user_list(state.reqwest, user).await;

match mal_user_list {
Ok(mal) => {
let ids = mal
.data
.iter()
.map(|item| AnimeUserEntry {
status: item
.list_status
.status
.parse::<AnimeWatchStatus>()
.map_err(|_| AnimeWatchStatus::Watching)
.expect("Failed to parse watch status"),
user_id: user_id.clone(),
anime_id: item.node.id,
})
.collect::<Vec<_>>();
let mut importer = state.importer.lock().await;
importer.add_all(ids);
}
Err(err) => {
// TODO: Handle better?
tracing::error!("{:?}", err)
}
}
});
}

// TODO: handle unwrap
let entries = get_user_entrys(&state.db, user_id).await.unwrap();

Expand Down
6 changes: 5 additions & 1 deletion apps/web/src/hooks/useAnimeList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,14 @@ export const useAnimeList = ({
const ap = aEntry.watch_priority;
const bp = bEntry.watch_priority;

if (ap === 4 || bp === 4) {
console.log({ aEntry, bEntry, ap, bp });
}

// Ensure newly added entries are shown last rather than first;
// Happens when a new anime is added to the list
// or changes from releasing to complete
if (ap == 0) return -1;
if (ap == 0) return 1;
if (bp == 0) return -1;

if (ap < bp) return -1;
Expand Down
20 changes: 10 additions & 10 deletions flake.lock

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

12 changes: 8 additions & 4 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,14 @@
pkgs,
system,
}: {
default = pkgs.mkShell {
shellHook = ''
export PKG_CONFIG_PATH="${pkgs.openssl.dev}/lib/pkgconfig";
'';
default = with pkgs; mkShell {
shellHook = ''
export PKG_CONFIG_PATH="${openssl.dev}/lib/pkgconfig";
export PRISMA_QUERY_ENGINE_BINARY="${prisma-engines}/bin/query-engine";
export PRISMA_SCHEMA_ENGINE_BINARY="${prisma-engines}/bin/schema-engine";
export PRISMA_FMT_BINARY="${prisma-engines}/bin/prisma-fmt"
export PRISMA_QUERY_ENGINE_LIBRARY="${prisma-engines}/lib/libquery_engine.node"
'';
packages = with pkgs; [
pkg-config
openssl
Expand Down
4 changes: 2 additions & 2 deletions packages/prisma/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
"with-env": "dotenv -e ../../.env"
},
"dependencies": {
"@prisma/client": "5.9.0",
"prisma": "^5.9.0"
"@prisma/client": "5.12",
"prisma": "5.12"
},
"devDependencies": {
"dotenv": "^16.4.1",
Expand Down
Loading

0 comments on commit 1428053

Please sign in to comment.