diff --git a/src/bilibili/favorite_list.rs b/src/bilibili/favorite_list.rs index 84f49fb..457c6b9 100644 --- a/src/bilibili/favorite_list.rs +++ b/src/bilibili/favorite_list.rs @@ -41,6 +41,7 @@ pub struct VideoInfo { pub struct Upper { pub mid: i32, pub name: String, + pub face: String, } impl FavoriteList { pub fn new(client: Arc, fid: String) -> Self { diff --git a/src/core/command.rs b/src/core/command.rs index 8b05ee9..d1f4a26 100644 --- a/src/core/command.rs +++ b/src/core/command.rs @@ -1,43 +1,60 @@ use std::sync::Arc; +use entity::video; use futures_util::{pin_mut, StreamExt}; +use log::info; use sea_orm::entity::prelude::*; +use sea_orm::ActiveValue::Set; use crate::bilibili::{BiliClient, FavoriteList, Video}; use crate::core::utils::{ - create_video_pages, create_videos, exists_bvids_favtime, filter_videos, handle_favorite_info, + create_video_pages, create_videos, exist_labels, filter_videos, handle_favorite_info, }; use crate::Result; pub async fn process_favorite( bili_client: Arc, - fid: i32, + fid: &str, connection: Arc, ) -> Result<()> { - let favorite_list = FavoriteList::new(bili_client.clone(), fid.to_string()); - let info = favorite_list.get_info().await?; - let favorite_obj = handle_favorite_info(&info, connection.as_ref()).await?; - println!( - "Hi there! I'm going to scan this favorite: {:?}", - favorite_obj - ); - let video_stream = favorite_list.into_video_stream().chunks(10); + refresh_favorite(bili_client.clone(), fid, connection.clone()).await?; + download_favorite(bili_client.clone(), fid, connection.clone()).await?; + Ok(()) +} + +pub async fn refresh_favorite( + bili_client: Arc, + fid: &str, + connection: Arc, +) -> Result<()> { + let bili_favorite_list = FavoriteList::new(bili_client.clone(), fid.to_owned()); + let favorite_list_info = bili_favorite_list.get_info().await?; + let favorite_model = handle_favorite_info(&favorite_list_info, connection.as_ref()).await?; + info!("Scan the favorite: {fid}"); + let video_stream = bili_favorite_list.into_video_stream().chunks(10); pin_mut!(video_stream); while let Some(videos_info) = video_stream.next().await { - let exist_bvids_pubtimes = - exists_bvids_favtime(&videos_info, favorite_obj.id, connection.as_ref()).await?; + let exist_labels = exist_labels(&videos_info, &favorite_model, connection.as_ref()).await?; let should_break = videos_info .iter() - // 出现 bvid 和 fav_time 都相同的记录,说明已经到达了上次处理到的位置 - .any(|v| exist_bvids_pubtimes.contains(&(v.bvid.clone(), v.fav_time.naive_utc()))); - create_videos(&videos_info, &favorite_obj, connection.as_ref()).await?; - let all_unprocessed_videos = - filter_videos(&videos_info, &favorite_obj, true, true, connection.as_ref()).await?; - if !all_unprocessed_videos.is_empty() { - for video in all_unprocessed_videos { - let bili_video = Video::new(bili_client.clone(), video.bvid.clone()); - let pages = bili_video.get_pages().await?; - create_video_pages(&pages, &video, connection.as_ref()).await?; + .any(|v| exist_labels.contains(&(v.bvid.clone(), v.fav_time.naive_utc()))); + create_videos(&videos_info, &favorite_model, connection.as_ref()).await?; + let unrefreshed_video_models = filter_videos( + &videos_info, + &favorite_model, + true, + true, + connection.as_ref(), + ) + .await?; + if !unrefreshed_video_models.is_empty() { + for video_model in unrefreshed_video_models { + let bili_video = Video::new(bili_client.clone(), video_model.bvid.clone()); + let pages_info = bili_video.get_pages().await?; + create_video_pages(&pages_info, &video_model, connection.as_ref()).await?; + let mut video_active_model: video::ActiveModel = video_model.into(); + video_active_model.single_page = Set(Some(pages_info.len() == 1)); + video_active_model.save(connection.as_ref()).await?; } } if should_break { @@ -46,3 +63,12 @@ pub async fn process_favorite( } Ok(()) } + +#[allow(unused_variables)] +pub async fn download_favorite( + bili_client: Arc, + fid: &str, + connection: Arc, +) -> Result<()> { + todo!(); +} diff --git a/src/core/utils.rs b/src/core/utils.rs index ec85461..7107357 100644 --- a/src/core/utils.rs +++ b/src/core/utils.rs @@ -10,7 +10,7 @@ use sea_orm::QuerySelect; use crate::bilibili::{FavoriteListInfo, PageInfo, VideoInfo}; use crate::Result; -// 根据获得的收藏夹信息,插入或更新数据库中的收藏夹,并返回收藏夹对象 +/// 根据获得的收藏夹信息,插入或更新数据库中的收藏夹,并返回收藏夹对象 pub async fn handle_favorite_info( info: &FavoriteListInfo, connection: &DatabaseConnection, @@ -40,20 +40,21 @@ pub async fn handle_favorite_info( .unwrap()) } -// 获取数据库中存在的与该视频 favorite_id 和 bvid 重合的视频 -pub async fn exists_bvids_favtime( +/// 获取数据库中存在的与该视频 favorite_id 和 bvid 重合的视频中的 bvid 和 favtime +/// 如果 bvid 和 favtime 均相同,说明到达了上次处理到的位置 +pub async fn exist_labels( videos_info: &[VideoInfo], - fid: i32, + favorite_model: &favorite::Model, connection: &DatabaseConnection, ) -> Result> { let bvids = videos_info .iter() .map(|v| v.bvid.clone()) .collect::>(); - let exist_bvid_favtime = video::Entity::find() + let exist_labels = video::Entity::find() .filter( video::Column::FavoriteId - .eq(fid) + .eq(favorite_model.id) .and(video::Column::Bvid.is_in(bvids)), ) .select_only() @@ -63,21 +64,21 @@ pub async fn exists_bvids_favtime( .await? .into_iter() .collect::>(); - Ok(exist_bvid_favtime) + Ok(exist_labels) } -// 尝试创建 Video Model,如果发生冲突则忽略 +/// 尝试创建 Video Model,如果发生冲突则忽略 pub async fn create_videos( videos_info: &[VideoInfo], - favorite_obj: &favorite::Model, + favorite: &favorite::Model, connection: &DatabaseConnection, ) -> Result<()> { let video_models = videos_info .iter() .map(move |v| video::ActiveModel { - favorite_id: Set(favorite_obj.id), + favorite_id: Set(favorite.id), bvid: Set(v.bvid.clone()), - path: Set(Path::new(favorite_obj.path.as_str()) + path: Set(Path::new(favorite.path.as_str()) .join(&v.title) .to_str() .unwrap() @@ -95,6 +96,7 @@ pub async fn create_videos( single_page: Set(None), upper_id: Set(v.upper.mid), upper_name: Set(v.upper.name.clone()), + upper_face: Set(v.upper.face.clone()), ..Default::default() }) .collect::>(); @@ -110,10 +112,10 @@ pub async fn create_videos( Ok(()) } -// 筛选所有符合条件的视频 +/// 筛选所有符合条件的视频 pub async fn filter_videos( videos_info: &[VideoInfo], - favorite_obj: &favorite::Model, + favorite_model: &favorite::Model, only_unhandled: bool, only_no_page: bool, connection: &DatabaseConnection, @@ -123,7 +125,7 @@ pub async fn filter_videos( .map(|v| v.bvid.clone()) .collect::>(); let mut condition = video::Column::FavoriteId - .eq(favorite_obj.id) + .eq(favorite_model.id) .and(video::Column::Bvid.is_in(bvids)) .and(video::Column::Valid.eq(true)); if only_unhandled { @@ -137,26 +139,26 @@ pub async fn filter_videos( .all(connection) .await?) } - +/// 创建视频的所有分 P pub async fn create_video_pages( - pages: &[PageInfo], - video_obj: &video::Model, + pages_info: &[PageInfo], + video_model: &video::Model, connection: &DatabaseConnection, ) -> Result<()> { - let page_models = pages + let page_models = pages_info .iter() .map(move |p| page::ActiveModel { - video_id: Set(video_obj.id), + video_id: Set(video_model.id), cid: Set(p.cid), pid: Set(p.page), name: Set(p.name.clone()), - path: Set(Path::new(video_obj.path.as_str()) + path: Set(Path::new(video_model.path.as_str()) .join(&p.name) .to_str() .unwrap() .to_string()), image: Set(p.first_frame.clone()), - valid: Set(video_obj.valid), + valid: Set(video_model.valid), download_status: Set(0), ..Default::default() }) @@ -172,3 +174,19 @@ pub async fn create_video_pages( .await?; Ok(()) } + +/// 获取所有未处理的视频和页 +pub async fn unhandled_videos_pages( + favorite_model: &favorite::Model, + connection: &DatabaseConnection, +) -> Result> { + Ok(video::Entity::find() + .filter( + video::Column::FavoriteId + .eq(favorite_model.id) + .and(video::Column::Valid.eq(true)) + .and(video::Column::Handled.eq(false)), + ) + .all(connection) + .await?) +} diff --git a/src/main.rs b/src/main.rs index c5c36b5..e29a584 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,16 +3,18 @@ use std::sync::Arc; use bili_sync::bilibili::BiliClient; use bili_sync::core::command::process_favorite; use bili_sync::database::database_connection; +use log::error; #[tokio::main] async fn main() -> ! { + env_logger::init(); let connection = Arc::new(database_connection().await.unwrap()); let bili_client = Arc::new(BiliClient::new(None)); loop { - for fid in [52642258] { + for fid in ["52642258"] { let res = process_favorite(bili_client.clone(), fid, connection.clone()).await; if let Err(e) = res { - eprintln!("Error: {:?}", e); + error!("Error: {e}"); } } tokio::time::sleep(std::time::Duration::from_secs(600)).await;