feat: 添加 static 的模板,修复各种路径相关的问题
This commit is contained in:
@@ -2,12 +2,12 @@ use std::collections::HashMap;
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use dirs::config_dir;
|
||||||
use entity::{favorite, page, video};
|
use entity::{favorite, page, video};
|
||||||
use futures::stream::FuturesUnordered;
|
use futures::stream::FuturesUnordered;
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
use futures_util::{pin_mut, StreamExt};
|
use futures_util::{pin_mut, StreamExt};
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use sea_orm::entity::prelude::*;
|
use sea_orm::entity::prelude::*;
|
||||||
use sea_orm::ActiveValue::Set;
|
use sea_orm::ActiveValue::Set;
|
||||||
use sea_orm::TryIntoModel;
|
use sea_orm::TryIntoModel;
|
||||||
@@ -16,34 +16,32 @@ use tokio::fs;
|
|||||||
use tokio::sync::{Mutex, Semaphore};
|
use tokio::sync::{Mutex, Semaphore};
|
||||||
|
|
||||||
use super::status::{PageStatus, VideoStatus};
|
use super::status::{PageStatus, VideoStatus};
|
||||||
use super::utils::{unhandled_videos_pages, ModelWrapper, NFOMode, NFOSerializer};
|
use super::utils::{unhandled_videos_pages, ModelWrapper, NFOMode, NFOSerializer, TEMPLATE};
|
||||||
use crate::bilibili::{BestStream, BiliClient, FavoriteList, FilterOption, PageInfo, Video};
|
use crate::bilibili::{BestStream, BiliClient, FavoriteList, FilterOption, PageInfo, Video};
|
||||||
use crate::core::utils::{create_video_pages, create_videos, exist_labels, filter_videos, handle_favorite_info};
|
use crate::core::utils::{create_video_pages, create_videos, exist_labels, filter_videos, handle_favorite_info};
|
||||||
use crate::downloader::Downloader;
|
use crate::downloader::Downloader;
|
||||||
use crate::Result;
|
use crate::Result;
|
||||||
|
|
||||||
static TEMPLATE: Lazy<handlebars::Handlebars> = Lazy::new(|| {
|
|
||||||
let mut handlebars = handlebars::Handlebars::new();
|
|
||||||
// TODO: 此处开放自定义
|
|
||||||
handlebars.register_template_string("video", "{{bvid}}").unwrap();
|
|
||||||
handlebars.register_template_string("page", "{{bvid}}").unwrap();
|
|
||||||
handlebars
|
|
||||||
});
|
|
||||||
|
|
||||||
/// 处理某个收藏夹,首先刷新收藏夹信息,然后下载收藏夹中未下载成功的视频
|
/// 处理某个收藏夹,首先刷新收藏夹信息,然后下载收藏夹中未下载成功的视频
|
||||||
pub async fn process_favorite(bili_client: &BiliClient, fid: &str, connection: &DatabaseConnection) -> Result<()> {
|
pub async fn process_favorite(
|
||||||
let favorite_model = refresh_favorite(bili_client, fid, connection).await?;
|
bili_client: &BiliClient,
|
||||||
|
fid: &str,
|
||||||
|
path: &str,
|
||||||
|
connection: &DatabaseConnection,
|
||||||
|
) -> Result<()> {
|
||||||
|
let favorite_model = refresh_favorite(bili_client, fid, path, connection).await?;
|
||||||
download_favorite(bili_client, favorite_model, connection).await
|
download_favorite(bili_client, favorite_model, connection).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn refresh_favorite(
|
pub async fn refresh_favorite(
|
||||||
bili_client: &BiliClient,
|
bili_client: &BiliClient,
|
||||||
fid: &str,
|
fid: &str,
|
||||||
|
path: &str,
|
||||||
connection: &DatabaseConnection,
|
connection: &DatabaseConnection,
|
||||||
) -> Result<favorite::Model> {
|
) -> Result<favorite::Model> {
|
||||||
let bili_favorite_list = FavoriteList::new(bili_client, fid.to_owned());
|
let bili_favorite_list = FavoriteList::new(bili_client, fid.to_owned());
|
||||||
let favorite_list_info = bili_favorite_list.get_info().await?;
|
let favorite_list_info = bili_favorite_list.get_info().await?;
|
||||||
let favorite_model = handle_favorite_info(&favorite_list_info, connection).await?;
|
let favorite_model = handle_favorite_info(&favorite_list_info, path, connection).await?;
|
||||||
info!("Scan the favorite: {fid}");
|
info!("Scan the favorite: {fid}");
|
||||||
// 每十个视频一组,避免太多的数据库操作
|
// 每十个视频一组,避免太多的数据库操作
|
||||||
let video_stream = bili_favorite_list.into_video_stream().chunks(10);
|
let video_stream = bili_favorite_list.into_video_stream().chunks(10);
|
||||||
@@ -143,13 +141,20 @@ pub async fn download_video_pages(
|
|||||||
let mut status = VideoStatus::new(video_model.download_status);
|
let mut status = VideoStatus::new(video_model.download_status);
|
||||||
let seprate_status = status.should_run();
|
let seprate_status = status.should_run();
|
||||||
let base_path = Path::new(&video_model.path);
|
let base_path = Path::new(&video_model.path);
|
||||||
|
let upper_id = video_model.upper_id.to_string();
|
||||||
|
let base_upper_path = config_dir()
|
||||||
|
.unwrap()
|
||||||
|
.join("bili-sync")
|
||||||
|
.join("upper")
|
||||||
|
.join(upper_id.chars().next().unwrap().to_string())
|
||||||
|
.join(upper_id);
|
||||||
let is_single_page = video_model.single_page.unwrap();
|
let is_single_page = video_model.single_page.unwrap();
|
||||||
// 对于单页视频,page 的下载已经足够
|
// 对于单页视频,page 的下载已经足够
|
||||||
// 对于多页视频,page 下载仅包含了分集内容,需要额外补上视频的 poster 的 tvshow.nfo
|
// 对于多页视频,page 下载仅包含了分集内容,需要额外补上视频的 poster 的 tvshow.nfo
|
||||||
let tasks: Vec<Pin<Box<dyn Future<Output = Result<()>>>>> = vec![
|
let tasks: Vec<Pin<Box<dyn Future<Output = Result<()>>>>> = vec![
|
||||||
// 下载视频封面
|
// 下载视频封面
|
||||||
Box::pin(fetch_video_poster(
|
Box::pin(fetch_video_poster(
|
||||||
seprate_status[0],
|
seprate_status[0] && !is_single_page,
|
||||||
&video_model,
|
&video_model,
|
||||||
downloader,
|
downloader,
|
||||||
base_path.join("poster.jpg"),
|
base_path.join("poster.jpg"),
|
||||||
@@ -175,14 +180,14 @@ pub async fn download_video_pages(
|
|||||||
&video_model,
|
&video_model,
|
||||||
downloader,
|
downloader,
|
||||||
&upper_mutex.0,
|
&upper_mutex.0,
|
||||||
base_path.join("upper-face.jpg"),
|
base_upper_path.join("folder.jpg"),
|
||||||
)),
|
)),
|
||||||
// 生成 Up 主信息的 nfo
|
// 生成 Up 主信息的 nfo
|
||||||
Box::pin(generate_upper_nfo(
|
Box::pin(generate_upper_nfo(
|
||||||
seprate_status[4],
|
seprate_status[4],
|
||||||
&video_model,
|
&video_model,
|
||||||
&upper_mutex.1,
|
&upper_mutex.1,
|
||||||
base_path.join("upper.nfo"),
|
base_upper_path.join("person.nfo"),
|
||||||
)),
|
)),
|
||||||
];
|
];
|
||||||
let results = futures::future::join_all(tasks).await;
|
let results = futures::future::join_all(tasks).await;
|
||||||
@@ -294,7 +299,7 @@ pub async fn download_page(
|
|||||||
video_model,
|
video_model,
|
||||||
&page_model,
|
&page_model,
|
||||||
downloader,
|
downloader,
|
||||||
video_path,
|
video_path.clone(),
|
||||||
)),
|
)),
|
||||||
Box::pin(generate_page_nfo(seprate_status[2], video_model, &page_model, nfo_path)),
|
Box::pin(generate_page_nfo(seprate_status[2], video_model, &page_model, nfo_path)),
|
||||||
];
|
];
|
||||||
@@ -302,6 +307,7 @@ pub async fn download_page(
|
|||||||
status.update_status(&results);
|
status.update_status(&results);
|
||||||
let mut page_active_model: page::ActiveModel = page_model.into();
|
let mut page_active_model: page::ActiveModel = page_model.into();
|
||||||
page_active_model.download_status = Set(status.into());
|
page_active_model.download_status = Set(status.into());
|
||||||
|
page_active_model.path = Set(Some(video_path.to_str().unwrap().to_string()));
|
||||||
page_active_model = page_active_model.save(connection).await?;
|
page_active_model = page_active_model.save(connection).await?;
|
||||||
Ok(page_active_model.try_into_model().unwrap())
|
Ok(page_active_model.try_into_model().unwrap())
|
||||||
}
|
}
|
||||||
@@ -409,8 +415,8 @@ pub async fn fetch_upper_face(
|
|||||||
if !should_run {
|
if !should_run {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
// 这个锁是为了避免多个视频同时下载同一个 up 主的头像
|
// 这个锁只是为了避免多个视频同时下载同一个 up 主的头像,不携带实际内容
|
||||||
let _permit = upper_face_mutex.lock().await;
|
let _ = upper_face_mutex.lock().await;
|
||||||
if !upper_face_path.exists() {
|
if !upper_face_path.exists() {
|
||||||
return downloader.fetch(&video_model.upper_face, &upper_face_path).await;
|
return downloader.fetch(&video_model.upper_face, &upper_face_path).await;
|
||||||
}
|
}
|
||||||
@@ -426,7 +432,7 @@ pub async fn generate_upper_nfo(
|
|||||||
if !should_run {
|
if !should_run {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let _permit = upper_nfo_mutex.lock().await;
|
let _ = upper_nfo_mutex.lock().await;
|
||||||
if !nfo_path.exists() {
|
if !nfo_path.exists() {
|
||||||
let nfo_serializer = NFOSerializer(ModelWrapper::Video(video_model), NFOMode::UPPER);
|
let nfo_serializer = NFOSerializer(ModelWrapper::Video(video_model), NFOMode::UPPER);
|
||||||
return generate_nfo(nfo_serializer, nfo_path).await;
|
return generate_nfo(nfo_serializer, nfo_path).await;
|
||||||
|
|||||||
@@ -3,18 +3,33 @@ use std::path::Path;
|
|||||||
|
|
||||||
use entity::*;
|
use entity::*;
|
||||||
use migration::OnConflict;
|
use migration::OnConflict;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
use quick_xml::events::BytesText;
|
use quick_xml::events::BytesText;
|
||||||
use quick_xml::writer::Writer;
|
use quick_xml::writer::Writer;
|
||||||
use quick_xml::Error;
|
use quick_xml::Error;
|
||||||
use sea_orm::entity::prelude::*;
|
use sea_orm::entity::prelude::*;
|
||||||
use sea_orm::ActiveValue::Set;
|
use sea_orm::ActiveValue::Set;
|
||||||
use sea_orm::QuerySelect;
|
use sea_orm::QuerySelect;
|
||||||
|
use serde_json::json;
|
||||||
use tokio::io::AsyncWriteExt;
|
use tokio::io::AsyncWriteExt;
|
||||||
|
|
||||||
use super::status::Status;
|
use super::status::Status;
|
||||||
use crate::bilibili::{FavoriteListInfo, PageInfo, VideoInfo};
|
use crate::bilibili::{FavoriteListInfo, PageInfo, VideoInfo};
|
||||||
|
use crate::config::CONFIG;
|
||||||
use crate::Result;
|
use crate::Result;
|
||||||
|
|
||||||
|
pub static TEMPLATE: Lazy<handlebars::Handlebars> = Lazy::new(|| {
|
||||||
|
let mut handlebars = handlebars::Handlebars::new();
|
||||||
|
let config = CONFIG.lock().unwrap();
|
||||||
|
handlebars
|
||||||
|
.register_template_string("video", config.video_name.clone())
|
||||||
|
.unwrap();
|
||||||
|
handlebars
|
||||||
|
.register_template_string("page", config.page_name.clone())
|
||||||
|
.unwrap();
|
||||||
|
handlebars
|
||||||
|
});
|
||||||
|
|
||||||
pub enum NFOMode {
|
pub enum NFOMode {
|
||||||
MOVIE,
|
MOVIE,
|
||||||
TVSHOW,
|
TVSHOW,
|
||||||
@@ -30,21 +45,20 @@ pub enum ModelWrapper<'a> {
|
|||||||
pub struct NFOSerializer<'a>(pub ModelWrapper<'a>, pub NFOMode);
|
pub struct NFOSerializer<'a>(pub ModelWrapper<'a>, pub NFOMode);
|
||||||
|
|
||||||
/// 根据获得的收藏夹信息,插入或更新数据库中的收藏夹,并返回收藏夹对象
|
/// 根据获得的收藏夹信息,插入或更新数据库中的收藏夹,并返回收藏夹对象
|
||||||
pub async fn handle_favorite_info(info: &FavoriteListInfo, connection: &DatabaseConnection) -> Result<favorite::Model> {
|
pub async fn handle_favorite_info(
|
||||||
|
info: &FavoriteListInfo,
|
||||||
|
path: &str,
|
||||||
|
connection: &DatabaseConnection,
|
||||||
|
) -> Result<favorite::Model> {
|
||||||
favorite::Entity::insert(favorite::ActiveModel {
|
favorite::Entity::insert(favorite::ActiveModel {
|
||||||
f_id: Set(info.id),
|
f_id: Set(info.id),
|
||||||
name: Set(info.title.to_string()),
|
name: Set(info.title.to_string()),
|
||||||
path: Set("/home/amtoaer/Documents/code/rust/bili-sync/video".to_string()),
|
path: Set(path.to_owned()),
|
||||||
enabled: Set(true),
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.on_conflict(
|
.on_conflict(
|
||||||
OnConflict::column(favorite::Column::FId)
|
OnConflict::column(favorite::Column::FId)
|
||||||
.update_columns([
|
.update_columns([favorite::Column::Name, favorite::Column::Path])
|
||||||
favorite::Column::Name,
|
|
||||||
favorite::Column::Path,
|
|
||||||
favorite::Column::Enabled,
|
|
||||||
])
|
|
||||||
.to_owned(),
|
.to_owned(),
|
||||||
)
|
)
|
||||||
.exec(connection)
|
.exec(connection)
|
||||||
@@ -91,12 +105,24 @@ pub async fn create_videos(
|
|||||||
.map(move |v| video::ActiveModel {
|
.map(move |v| video::ActiveModel {
|
||||||
favorite_id: Set(favorite.id),
|
favorite_id: Set(favorite.id),
|
||||||
bvid: Set(v.bvid.clone()),
|
bvid: Set(v.bvid.clone()),
|
||||||
path: Set(Path::new(favorite.path.as_str())
|
name: Set(v.title.clone()),
|
||||||
.join(&v.bvid)
|
path: Set(Path::new(&favorite.path)
|
||||||
|
.join(
|
||||||
|
TEMPLATE
|
||||||
|
.render(
|
||||||
|
"video",
|
||||||
|
&json!({
|
||||||
|
"bvid": &v.bvid,
|
||||||
|
"title": &v.title,
|
||||||
|
"upper_name": &v.upper.name,
|
||||||
|
"upper_mid": &v.upper.mid,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.unwrap_or_else(|_| v.bvid.clone()),
|
||||||
|
)
|
||||||
.to_str()
|
.to_str()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_string()),
|
.to_owned()),
|
||||||
name: Set(v.title.clone()),
|
|
||||||
category: Set(v.vtype),
|
category: Set(v.vtype),
|
||||||
intro: Set(v.intro.clone()),
|
intro: Set(v.intro.clone()),
|
||||||
cover: Set(v.cover.clone()),
|
cover: Set(v.cover.clone()),
|
||||||
@@ -159,11 +185,6 @@ pub async fn create_video_pages(
|
|||||||
cid: Set(p.cid),
|
cid: Set(p.cid),
|
||||||
pid: Set(p.page),
|
pid: Set(p.page),
|
||||||
name: Set(p.name.clone()),
|
name: Set(p.name.clone()),
|
||||||
path: Set(Path::new(video_model.path.as_str())
|
|
||||||
.join(&p.name)
|
|
||||||
.to_str()
|
|
||||||
.unwrap()
|
|
||||||
.to_string()),
|
|
||||||
image: Set(p.first_frame.clone()),
|
image: Set(p.first_frame.clone()),
|
||||||
download_status: Set(0),
|
download_status: Set(0),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|||||||
Reference in New Issue
Block a user