cursed
This commit is contained in:
parent
311b315990
commit
1979fc246e
16 changed files with 1644 additions and 1391 deletions
|
@ -1,10 +1,5 @@
|
|||
use rayon::prelude::IntoParallelIterator;
|
||||
use rayon::prelude::ParallelIterator;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use walkdir::WalkDir;
|
||||
|
||||
use func::is_video_file;
|
||||
|
@ -12,232 +7,193 @@ pub use video::Video;
|
|||
mod func;
|
||||
mod video;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! query_row_map {
|
||||
($db:ident, $query:expr, $param:expr, $map_fn:expr) => {{
|
||||
let mut state = $db.prepare($query).unwrap();
|
||||
|
||||
let r: Vec<_> = state
|
||||
.query_map($param, $map_fn)
|
||||
.unwrap()
|
||||
.flatten()
|
||||
.collect();
|
||||
r
|
||||
}};
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Library {
|
||||
conn: Arc<Mutex<rusqlite::Connection>>,
|
||||
conn: sqlx::PgPool,
|
||||
}
|
||||
|
||||
impl Library {
|
||||
pub fn new() -> Self {
|
||||
pub async fn new() -> Self {
|
||||
log::info!("Creating database connection");
|
||||
let conn = Arc::new(Mutex::new(rusqlite::Connection::open("videos.db").unwrap()));
|
||||
let conn = sqlx::postgres::PgPoolOptions::new()
|
||||
.max_connections(5)
|
||||
.connect(&std::env::var("DATABASE_URL").unwrap())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let s = Self { conn };
|
||||
|
||||
s.init_schema();
|
||||
sqlx::migrate!("./migrations").run(&s.conn).await.unwrap();
|
||||
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
// DB
|
||||
impl Library {
|
||||
pub fn init_schema(&self) {
|
||||
let mut con = self.conn.lock().unwrap();
|
||||
let tx = con.transaction().unwrap();
|
||||
tx.execute_batch(include_str!("../schema.sql")).unwrap();
|
||||
tx.commit().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// Functions
|
||||
impl Library {
|
||||
// directories
|
||||
|
||||
pub fn get_directories(&self) -> Vec<String> {
|
||||
let db = self.conn.lock().unwrap();
|
||||
query_row_map!(db, "SELECT DISTINCT directory FROM videos;", [], |x| {
|
||||
x.get::<usize, String>(0)
|
||||
})
|
||||
pub async fn get_directories(&self) -> Vec<String> {
|
||||
let res: Vec<(String,)> = sqlx::query_as("SELECT DISTINCT(directory) FROM videos;")
|
||||
.fetch_all(&self.conn)
|
||||
.await
|
||||
.unwrap();
|
||||
res.into_iter().map(|x| x.0).collect()
|
||||
}
|
||||
|
||||
pub fn get_directory_videos(&self, dir: &str) -> Vec<Video> {
|
||||
let db = self.conn.lock().unwrap();
|
||||
pub async fn get_directory_videos(&self, dir: &str) -> Vec<Video> {
|
||||
let videos_ids: Vec<(String, )> = sqlx::query_as(
|
||||
"SELECT sha256 FROM videos INNER JOIN youtube_meta ON youtube_meta.id = videos.youtube_id WHERE directory = ?1 ORDER BY youtube_meta.upload_date DESC;")
|
||||
.bind(dir)
|
||||
.fetch_all(&self.conn).await.unwrap();
|
||||
|
||||
let videos: Vec<_> = query_row_map!(
|
||||
db,
|
||||
"SELECT sha256 FROM videos INNER JOIN youtube_meta ON youtube_meta.id = videos.youtube_id WHERE directory = ?1 ORDER BY youtube_meta.upload_date DESC;",
|
||||
&[dir],
|
||||
|x| {
|
||||
Ok(Video::from_hash(
|
||||
&x.get::<usize, String>(0)?,
|
||||
self.conn.clone(),
|
||||
))
|
||||
}
|
||||
);
|
||||
let mut videos: Vec<Video<'_>> = Vec::new();
|
||||
|
||||
for video in videos_ids {
|
||||
videos.push(Video::from_hash(
|
||||
&video.0,
|
||||
&self.conn
|
||||
));
|
||||
}
|
||||
|
||||
videos
|
||||
}
|
||||
|
||||
// YT
|
||||
|
||||
pub fn get_channel_name_yt(&self, id: &str) -> String {
|
||||
let db = self.conn.lock().unwrap();
|
||||
pub async fn get_channel_name_yt(&self, id: &str) -> String {
|
||||
let name: (String,) =
|
||||
sqlx::query_as("SELECT uploader_name FROM youtube_meta WHERE uploader_id = $1")
|
||||
.bind(id)
|
||||
.fetch_one(&self.conn)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let res: Vec<String> = query_row_map!(
|
||||
db,
|
||||
"SELECT uploader_name FROM youtube_meta WHERE uploader_id = ?1",
|
||||
&[id],
|
||||
|x| { x.get(0) }
|
||||
);
|
||||
|
||||
res.first().unwrap().to_owned()
|
||||
name.0
|
||||
}
|
||||
|
||||
pub fn get_tags_yt(&self) -> Vec<String> {
|
||||
let db = self.conn.lock().unwrap();
|
||||
|
||||
let tags: Vec<_> =
|
||||
query_row_map!(db, "SELECT DISTINCT tag FROM youtube_meta_tags", [], |x| {
|
||||
x.get(0)
|
||||
});
|
||||
|
||||
tags
|
||||
pub async fn get_tags_yt(&self) -> Vec<String> {
|
||||
let res: Vec<(String,)> = sqlx::query_as("SELECT DISTINCT(tag) FROM youtube_meta_tags")
|
||||
.fetch_all(&self.conn)
|
||||
.await
|
||||
.unwrap();
|
||||
res.into_iter().map(|x| x.0).collect()
|
||||
}
|
||||
|
||||
pub fn get_videos_by_tag_yt(&self, tag: &str) -> Vec<Video> {
|
||||
let db = self.conn.lock().unwrap();
|
||||
pub async fn get_videos_by_tag_yt(&self, tag: &str) -> Vec<Video> {
|
||||
let videos_ids: Vec<(String, )> = sqlx::query_as(
|
||||
"SELECT sha256 FROM youtube_meta_tags INNER JOIN youtube_meta ON youtube_meta_tags.youtube_id = youtube_meta.id INNER JOIN videos ON videos.youtube_id = youtube_meta.id WHERE tag = $1;")
|
||||
.bind(tag)
|
||||
.fetch_all(&self.conn).await.unwrap();
|
||||
|
||||
let mut videos: Vec<Video<'_>> = Vec::new();
|
||||
|
||||
let videos: Vec<_> = query_row_map!(
|
||||
db,
|
||||
"SELECT sha256 FROM youtube_meta_tags INNER JOIN youtube_meta ON youtube_meta_tags.youtube_id = youtube_meta.id INNER JOIN videos ON videos.youtube_id = youtube_meta.id WHERE tag = ?1;",
|
||||
&[tag],
|
||||
|x| {
|
||||
Ok(Video::from_hash(
|
||||
&x.get::<usize, String>(0)?,
|
||||
self.conn.clone(),
|
||||
))
|
||||
for video in videos_ids {
|
||||
videos.push(Video::from_hash(
|
||||
&video.0,
|
||||
&self.conn
|
||||
));
|
||||
}
|
||||
);
|
||||
|
||||
videos
|
||||
|
||||
videos
|
||||
}
|
||||
|
||||
pub fn get_channel_videos_yt(&self, id: &str) -> Vec<Video> {
|
||||
let db = self.conn.lock().unwrap();
|
||||
pub async fn get_channel_videos_yt(&self, id: &str) -> Vec<Video> {
|
||||
let videos_ids: Vec<(String, )> = sqlx::query_as(
|
||||
"SELECT sha256 FROM youtube_meta INNER JOIN videos ON youtube_meta.id = videos.youtube_id WHERE uploader_id = $1 ORDER BY youtube_meta.upload_date DESC;")
|
||||
.bind(id)
|
||||
.fetch_all(&self.conn).await.unwrap();
|
||||
|
||||
let videos: Vec<_> = query_row_map!(
|
||||
db,
|
||||
"SELECT sha256 FROM youtube_meta INNER JOIN videos ON youtube_meta.id = videos.youtube_id WHERE uploader_id = ?1 ORDER BY youtube_meta.upload_date DESC;",
|
||||
&[id],
|
||||
|x| {
|
||||
Ok(Video::from_hash(
|
||||
&x.get::<usize, String>(0)?,
|
||||
self.conn.clone(),
|
||||
))
|
||||
let mut videos: Vec<Video<'_>> = Vec::new();
|
||||
|
||||
for video in videos_ids {
|
||||
videos.push(Video::from_hash(
|
||||
&video.0,
|
||||
&self.conn
|
||||
));
|
||||
}
|
||||
);
|
||||
|
||||
videos
|
||||
|
||||
videos
|
||||
}
|
||||
|
||||
// videos
|
||||
|
||||
pub fn get_random_videos(&self, n: usize) -> Vec<Video> {
|
||||
let db = self.conn.lock().unwrap();
|
||||
pub async fn get_random_videos(&self, n: i64) -> Vec<Video> {
|
||||
let videos_ids: Vec<(String, )> = sqlx::query_as(
|
||||
"SELECT sha256 FROM videos ORDER BY RANDOM() LIMIT $1;")
|
||||
.bind(n)
|
||||
.fetch_all(&self.conn).await.unwrap();
|
||||
|
||||
query_row_map!(
|
||||
db,
|
||||
"SELECT sha256 FROM videos ORDER BY RANDOM() LIMIT ?1;",
|
||||
[n],
|
||||
|x| {
|
||||
Ok(Video::from_hash(
|
||||
&x.get::<usize, String>(0)?,
|
||||
self.conn.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
let mut videos: Vec<Video<'_>> = Vec::new();
|
||||
|
||||
for video in videos_ids {
|
||||
videos.push(Video::from_hash(
|
||||
&video.0,
|
||||
&self.conn
|
||||
));
|
||||
}
|
||||
|
||||
videos
|
||||
}
|
||||
|
||||
pub async fn get_video_by_hash(&self, hash: &str) -> Option<(String, Video)> {
|
||||
let res: Vec<(String, Video)> = sqlx::query_as::<sqlx::Postgres, (String, String)>(
|
||||
"SELECT sha256, directory FROM videos WHERE sha256 = $1"
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_video_by_hash(&self, hash: &str) -> Option<(String, Video)> {
|
||||
let db = self.conn.lock().unwrap();
|
||||
|
||||
let res: Vec<(String, Video)> = query_row_map!(
|
||||
db,
|
||||
"SELECT sha256, directory FROM videos WHERE sha256 = ?1;",
|
||||
&[hash],
|
||||
|x| {
|
||||
Ok((
|
||||
x.get(1)?,
|
||||
Video::from_hash(&x.get::<usize, String>(0)?, self.conn.clone()),
|
||||
))
|
||||
}
|
||||
);
|
||||
if !res.is_empty() {
|
||||
return res.first().map(std::borrow::ToOwned::to_owned);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn get_video_by_youtube_id(&self, id: &str) -> Option<(String, Video)> {
|
||||
let db = self.conn.lock().unwrap();
|
||||
|
||||
let res = query_row_map!(
|
||||
db,
|
||||
"SELECT sha256, directory FROM videos WHERE youtube_id = ?1",
|
||||
&[id],
|
||||
|x| {
|
||||
Ok((
|
||||
x.get(1)?,
|
||||
Video::from_hash(&x.get::<usize, String>(0)?, self.conn.clone()),
|
||||
))
|
||||
}
|
||||
);
|
||||
.bind(hash)
|
||||
.fetch_all(&self.conn).await.unwrap().into_iter()
|
||||
.map(|x| {
|
||||
(x.1, Video::from_hash(&x.0, &self.conn))
|
||||
}).collect();
|
||||
|
||||
if !res.is_empty() {
|
||||
return res.first().map(std::borrow::ToOwned::to_owned);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn has_path(&self, path: &Path) -> bool {
|
||||
let db = self.conn.lock().unwrap();
|
||||
let mut state = db
|
||||
.prepare("SELECT path FROM videos WHERE path = ?1;")
|
||||
.unwrap();
|
||||
pub async fn get_video_by_youtube_id(&self, id: &str) -> Option<(String, Video)> {
|
||||
let res: Vec<(String, Video<'_>)> = sqlx::query_as(
|
||||
"SELECT sha256, directory FROM videos WHERE youtube_id = $1")
|
||||
.bind(id)
|
||||
.fetch_all(&self.conn).await.unwrap().into_iter().map(|x: (String, String)| {
|
||||
(
|
||||
x.1,
|
||||
Video::from_hash(&x.0, &self.conn),
|
||||
)
|
||||
}).collect();
|
||||
|
||||
let x = state
|
||||
.query_map([path.to_str().unwrap()], |x| {
|
||||
let r: String = x.get(0)?;
|
||||
Ok(r)
|
||||
})
|
||||
if !res.is_empty() {
|
||||
return res.first().map(std::borrow::ToOwned::to_owned);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub async fn has_path(&self, path: &Path) -> bool {
|
||||
sqlx::query("SELECT path FROM videos WHERE path = $1")
|
||||
.bind(path.display().to_string())
|
||||
.fetch_optional(&self.conn)
|
||||
.await
|
||||
.unwrap()
|
||||
.flatten()
|
||||
.next()
|
||||
.is_some();
|
||||
x
|
||||
.is_some()
|
||||
}
|
||||
|
||||
// search
|
||||
|
||||
pub fn search_video(&self, query: &str, start: usize, n: usize) -> Vec<Video> {
|
||||
let db = self.conn.lock().unwrap();
|
||||
pub async fn search_video(&self, query: &str, start: i64, n: i64) -> Vec<Video> {
|
||||
let query = format!("%{query}%");
|
||||
|
||||
query_row_map!(
|
||||
db,
|
||||
&format!(
|
||||
r#"SELECT DISTINCT
|
||||
sqlx::query_as(
|
||||
r#"SELECT DISTINCT
|
||||
vm.sha256,
|
||||
( -- Calculate a score for the video based on matches
|
||||
(ym.title LIKE ?1) +
|
||||
(ym.description LIKE ?1) +
|
||||
(ym.uploader_name LIKE ?1) +
|
||||
(vm.directory LIKE ?1)
|
||||
(ym.title LIKE $1) +
|
||||
(ym.description LIKE $1) +
|
||||
(ym.uploader_name LIKE $1) +
|
||||
(vm.directory LIKE $1)
|
||||
) AS score
|
||||
FROM
|
||||
youtube_meta AS ym
|
||||
|
@ -246,41 +202,41 @@ impl Library {
|
|||
LEFT JOIN
|
||||
youtube_meta_tags AS ymt ON ym.id = ymt.youtube_id
|
||||
WHERE
|
||||
(ym.title LIKE ?1) OR
|
||||
(ym.description LIKE ?1) OR
|
||||
(ym.uploader_name LIKE ?1) OR
|
||||
(vm.directory LIKE ?1) OR
|
||||
(ymt.tag LIKE ?1)
|
||||
(ym.title LIKE $1) OR
|
||||
(ym.description LIKE $1) OR
|
||||
(ym.uploader_name LIKE $1) OR
|
||||
(vm.directory LIKE $1) OR
|
||||
(ymt.tag LIKE $1)
|
||||
ORDER BY
|
||||
score DESC,
|
||||
ym.upload_date DESC LIMIT {n} OFFSET {start};"#
|
||||
),
|
||||
&[&query],
|
||||
|x| {
|
||||
Ok(Video::from_hash(
|
||||
&x.get::<usize, String>(0)?,
|
||||
self.conn.clone(),
|
||||
))
|
||||
}
|
||||
ym.upload_date DESC LIMIT $2 OFFSET $3;"#
|
||||
)
|
||||
.bind(query)
|
||||
.bind(n)
|
||||
.bind(start)
|
||||
.fetch_all(&self.conn).await.unwrap().into_iter().map(|x: (String, i64)| {
|
||||
Video::from_hash(
|
||||
&x.0,
|
||||
&self.conn
|
||||
)
|
||||
}).collect()
|
||||
}
|
||||
}
|
||||
|
||||
// video library scan
|
||||
impl Library {
|
||||
pub fn scan_dir(&self, dir: &PathBuf) {
|
||||
pub async fn scan_dir(&self, dir: &PathBuf) {
|
||||
log::info!("Scanning {dir:?}");
|
||||
let lib = self.get_video_paths(dir);
|
||||
let _: Vec<Video> = lib
|
||||
.into_par_iter()
|
||||
.map(|x| Video::insert_path_to_db(&self.conn.clone(), &x))
|
||||
.collect();
|
||||
let db = self.conn.lock().unwrap();
|
||||
db.flush_prepared_statement_cache();
|
||||
let lib = self.get_video_paths(dir).await;
|
||||
|
||||
for path in lib {
|
||||
Video::insert_path_to_db(&self.conn, &path).await;
|
||||
}
|
||||
|
||||
log::info!("Finished scanning {dir:?}");
|
||||
}
|
||||
|
||||
fn get_video_paths(&self, dir: &PathBuf) -> Vec<PathBuf> {
|
||||
async fn get_video_paths(&self, dir: &PathBuf) -> Vec<PathBuf> {
|
||||
let mut videos: Vec<PathBuf> = vec![];
|
||||
|
||||
for entry in WalkDir::new(dir).follow_links(true) {
|
||||
|
@ -292,7 +248,7 @@ impl Library {
|
|||
if is_video_file(&file_name) {
|
||||
let video_path = entry.path().to_path_buf();
|
||||
|
||||
if self.has_path(&video_path) {
|
||||
if self.has_path(&video_path).await {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::library::func::calculate_sha256_hash;
|
||||
use crate::query_row_map;
|
||||
use crate::yt_meta;
|
||||
|
||||
// todo : optimize
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct Video {
|
||||
pub struct Video<'a> {
|
||||
directory: Option<String>,
|
||||
path: Option<PathBuf>,
|
||||
title: Option<String>,
|
||||
|
@ -16,26 +16,23 @@ pub struct Video {
|
|||
description: Option<String>,
|
||||
uploader_name: Option<String>,
|
||||
uploader_id: Option<String>,
|
||||
duration: Option<usize>,
|
||||
views: Option<usize>,
|
||||
duration: Option<i64>,
|
||||
views: Option<i64>,
|
||||
categories: Option<Vec<String>>,
|
||||
tags: Option<Vec<String>>,
|
||||
upload_date: Option<String>,
|
||||
db: Option<Arc<Mutex<rusqlite::Connection>>>,
|
||||
upload_date: Option<chrono::NaiveDate>,
|
||||
db: Option<&'a sqlx::PgPool>,
|
||||
}
|
||||
|
||||
// Video properties
|
||||
impl Video {
|
||||
fn get_video_info(&mut self) {
|
||||
impl<'a> Video<'a> {
|
||||
async fn get_video_info(&mut self) {
|
||||
log::info!("Fetching Video Metadata for {}", self.hash);
|
||||
let db = self.db.as_mut().unwrap().lock().unwrap();
|
||||
|
||||
let res: Vec<(String, String)> = query_row_map!(
|
||||
db,
|
||||
"SELECT title, path FROM videos WHERE sha256 = ?1",
|
||||
&[&self.hash],
|
||||
|x| { Ok((x.get(0)?, x.get(1)?)) }
|
||||
);
|
||||
let res: Vec<(String, String)> = sqlx::query_as(
|
||||
"SELECT title, path FROM videos WHERE sha256 = $1")
|
||||
.bind(&self.hash)
|
||||
.fetch_all(&*self.db.unwrap()).await.unwrap();
|
||||
|
||||
let res = res.first().unwrap();
|
||||
|
||||
|
@ -43,19 +40,13 @@ impl Video {
|
|||
self.path = Some(std::path::Path::new(&res.1).to_path_buf());
|
||||
}
|
||||
|
||||
fn get_youtube_meta_info(&mut self) {
|
||||
async fn get_youtube_meta_info(&mut self) {
|
||||
log::info!("Fetching YouTube Metadata for {}", self.hash);
|
||||
let db = self.db.as_mut().unwrap().lock().unwrap();
|
||||
|
||||
let res: Vec<(String, String, String, String, String, usize)> = query_row_map!(
|
||||
db,
|
||||
"SELECT id, description, uploader_name, uploader_id, upload_date, views FROM youtube_meta WHERE id = (SELECT youtube_id FROM videos WHERE sha256 = ?1 LIMIT 1)",
|
||||
&[&self.hash],
|
||||
|x| { Ok(
|
||||
( x.get(0)? , x.get(1)?, x.get(2)?, x.get(3)?, x.get(4)?, x.get(5)? )
|
||||
)
|
||||
}
|
||||
);
|
||||
let res: Vec<(String, String, String, String, chrono::NaiveDate, i64)> = sqlx::query_as(
|
||||
"SELECT id, description, uploader_name, uploader_id, upload_date, views FROM youtube_meta WHERE id = (SELECT youtube_id FROM videos WHERE sha256 = $1 LIMIT 1)")
|
||||
.bind(&self.hash)
|
||||
.fetch_all(&**self.db.as_ref().unwrap()).await.unwrap();
|
||||
|
||||
if let Some(res) = res.first() {
|
||||
self.youtube_id = Some(res.0.clone());
|
||||
|
@ -65,92 +56,88 @@ impl Video {
|
|||
self.upload_date = Some(res.4.clone());
|
||||
self.views = Some(res.5);
|
||||
|
||||
let res: Vec<String> = query_row_map!(
|
||||
db,
|
||||
"SELECT category FROM youtube_meta_categories WHERE youtube_id = ?1",
|
||||
&[self.youtube_id.as_ref().unwrap()],
|
||||
|x| { x.get(0) }
|
||||
);
|
||||
let res: Vec<(String,)> = sqlx::query_as(
|
||||
"SELECT category FROM youtube_meta_categories WHERE youtube_id = $1")
|
||||
.bind(self.youtube_id.as_ref().unwrap())
|
||||
.fetch_all(&**self.db.as_ref().unwrap()).await.unwrap();
|
||||
|
||||
self.categories = Some(res);
|
||||
self.categories = Some(res.into_iter().map(|x| x.0).collect());
|
||||
|
||||
let res: Vec<String> = query_row_map!(
|
||||
db,
|
||||
"SELECT tag FROM youtube_meta_tags WHERE youtube_id = ?1",
|
||||
&[self.youtube_id.as_ref().unwrap()],
|
||||
|x| { x.get(0) }
|
||||
);
|
||||
let res: Vec<(String,)> = sqlx::query_as(
|
||||
"SELECT tag FROM youtube_meta_tags WHERE youtube_id = $1")
|
||||
.bind(self.youtube_id.as_ref().unwrap())
|
||||
.fetch_all(&**self.db.as_ref().unwrap()).await.unwrap();
|
||||
|
||||
self.tags = Some(res);
|
||||
self.tags = Some(res.into_iter().map(|x| x.0).collect());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn title(&mut self) -> Option<&str> {
|
||||
pub async fn title(&mut self) -> Option<&str> {
|
||||
if self.title.is_none() {
|
||||
self.get_video_info();
|
||||
self.get_video_info().await;
|
||||
}
|
||||
self.title.as_deref()
|
||||
}
|
||||
|
||||
pub fn path(&mut self) -> Option<PathBuf> {
|
||||
pub async fn path(&mut self) -> Option<PathBuf> {
|
||||
if self.path.is_none() {
|
||||
self.get_video_info();
|
||||
self.get_video_info().await;
|
||||
}
|
||||
self.path.as_ref().map(std::clone::Clone::clone)
|
||||
}
|
||||
|
||||
pub fn description(&mut self) -> Option<&str> {
|
||||
pub async fn description(&mut self) -> Option<&str> {
|
||||
if self.description.is_none() {
|
||||
self.get_youtube_meta_info();
|
||||
self.get_youtube_meta_info().await;
|
||||
}
|
||||
self.description.as_deref()
|
||||
}
|
||||
|
||||
pub fn views(&mut self) -> Option<usize> {
|
||||
pub async fn views(&mut self) -> Option<i64> {
|
||||
if self.views.is_none() {
|
||||
self.get_youtube_meta_info();
|
||||
self.get_youtube_meta_info().await;
|
||||
}
|
||||
self.views
|
||||
}
|
||||
|
||||
pub fn uploader_name(&mut self) -> Option<&str> {
|
||||
pub async fn uploader_name(&mut self) -> Option<&str> {
|
||||
if self.uploader_name.is_none() {
|
||||
self.get_youtube_meta_info();
|
||||
self.get_youtube_meta_info().await;
|
||||
}
|
||||
self.uploader_name.as_deref()
|
||||
}
|
||||
|
||||
pub fn uploader_id(&mut self) -> Option<&str> {
|
||||
pub async fn uploader_id(&mut self) -> Option<&str> {
|
||||
if self.uploader_id.is_none() {
|
||||
self.get_youtube_meta_info();
|
||||
self.get_youtube_meta_info().await;
|
||||
}
|
||||
self.uploader_id.as_deref()
|
||||
}
|
||||
|
||||
pub fn upload_date(&mut self) -> Option<&str> {
|
||||
pub async fn upload_date(&mut self) -> Option<chrono::NaiveDate> {
|
||||
if self.upload_date.is_none() {
|
||||
self.get_youtube_meta_info();
|
||||
self.get_youtube_meta_info().await;
|
||||
}
|
||||
self.upload_date.as_deref()
|
||||
self.upload_date
|
||||
}
|
||||
|
||||
pub fn categories(&mut self) -> Option<&Vec<String>> {
|
||||
pub async fn categories(&mut self) -> Option<&Vec<String>> {
|
||||
if self.categories.is_none() {
|
||||
self.get_youtube_meta_info();
|
||||
self.get_youtube_meta_info().await;
|
||||
}
|
||||
self.categories.as_ref()
|
||||
}
|
||||
|
||||
pub fn tags(&mut self) -> Option<&Vec<String>> {
|
||||
pub async fn tags(&mut self) -> Option<&Vec<String>> {
|
||||
if self.tags.is_none() {
|
||||
self.get_youtube_meta_info();
|
||||
self.get_youtube_meta_info().await;
|
||||
}
|
||||
self.tags.as_ref()
|
||||
}
|
||||
|
||||
pub fn youtube_id(&mut self) -> Option<&str> {
|
||||
pub async fn youtube_id(&mut self) -> Option<&str> {
|
||||
if self.youtube_id.is_none() {
|
||||
self.get_youtube_meta_info();
|
||||
self.get_youtube_meta_info().await;
|
||||
}
|
||||
self.youtube_id.as_deref()
|
||||
}
|
||||
|
@ -161,15 +148,16 @@ impl Video {
|
|||
}
|
||||
|
||||
// Video Init
|
||||
impl Video {
|
||||
pub fn from_hash(hash: &str, db: Arc<Mutex<rusqlite::Connection>>) -> Self {
|
||||
impl<'a> Video<'a> {
|
||||
pub fn from_hash(hash: &str, db: &'a sqlx::PgPool) -> Self {
|
||||
Self {
|
||||
hash: hash.to_owned(),
|
||||
db: Some(db),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
pub fn insert_path_to_db(db: &Arc<Mutex<rusqlite::Connection>>, v: &PathBuf) -> Self {
|
||||
|
||||
pub async fn insert_path_to_db(db: &'a sqlx::PgPool, v: &PathBuf) -> Option<Self> {
|
||||
log::info!("Add {v:?} to library");
|
||||
let id = calculate_sha256_hash(v.to_str().unwrap()).unwrap();
|
||||
let file_name = v.file_stem().unwrap().to_str().unwrap().to_owned();
|
||||
|
@ -182,52 +170,44 @@ impl Video {
|
|||
.unwrap()
|
||||
.to_owned();
|
||||
|
||||
let mut sdb = db.lock().unwrap();
|
||||
let tx = sdb.transaction().unwrap();
|
||||
let mut tx = db.begin().await.unwrap();
|
||||
|
||||
if let Some(meta) = yt_meta::get_youtube_metadata(v) {
|
||||
tx.execute(
|
||||
"INSERT INTO videos (sha256, directory, path, title, youtube_id) VALUES (?1, ?2, ?3, ?4, ?5)",
|
||||
[
|
||||
&id,
|
||||
&dir,
|
||||
v.to_str().unwrap(),
|
||||
&meta.title(),
|
||||
&meta.youtube_id().unwrap()
|
||||
]).unwrap();
|
||||
sqlx::query("INSERT INTO youtube_meta (id, title, description, uploader_name, uploader_id, duration, views, upload_date) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)")
|
||||
.bind(&meta.youtube_id().unwrap())
|
||||
.bind(&meta.title())
|
||||
.bind(&meta.description().unwrap())
|
||||
.bind(&meta.uploader_name().unwrap())
|
||||
.bind(&meta.uploader_id().unwrap())
|
||||
.bind(&meta.duration().unwrap())
|
||||
.bind(&meta.views().unwrap())
|
||||
.bind(&meta.upload_date())
|
||||
.execute(&mut *tx).await.unwrap();
|
||||
|
||||
let _ = tx.execute(
|
||||
"INSERT INTO youtube_meta (id, title, description, uploader_name, uploader_id, duration, views, upload_date) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)",
|
||||
[
|
||||
&meta.youtube_id().unwrap(),
|
||||
&meta.title(),
|
||||
&meta.description().unwrap(),
|
||||
&meta.uploader_name().unwrap(),
|
||||
&meta.uploader_id().unwrap(),
|
||||
&meta.duration().unwrap().to_string(),
|
||||
&meta.views().unwrap().to_string(),
|
||||
&meta.upload_date().unwrap()
|
||||
]);
|
||||
sqlx::query("INSERT INTO videos (sha256, directory, path, title, youtube_id) VALUES ($1, $2, $3, $4, $5)")
|
||||
.bind(&id)
|
||||
.bind(&dir)
|
||||
.bind(v.to_str().unwrap())
|
||||
.bind(meta.title())
|
||||
.bind(meta.youtube_id().unwrap())
|
||||
.execute(&mut *tx).await.unwrap();
|
||||
|
||||
for cat in meta.categories().unwrap() {
|
||||
let _ = tx.execute(
|
||||
"INSERT INTO youtube_meta_categories (youtube_id, category) VALUES (?1, ?2)",
|
||||
[&meta.youtube_id().unwrap(), &cat],
|
||||
);
|
||||
sqlx::query("INSERT INTO youtube_meta_categories (youtube_id, category) VALUES ($1, $2)")
|
||||
.bind(meta.youtube_id().unwrap()).bind(cat).execute(&mut *tx).await.unwrap();
|
||||
}
|
||||
|
||||
if let Some(tags) = meta.tags() {
|
||||
for tag in tags {
|
||||
let _ = tx.execute(
|
||||
"INSERT INTO youtube_meta_tags (youtube_id, tag) VALUES (?1, ?2)",
|
||||
[&meta.youtube_id().unwrap(), &tag],
|
||||
);
|
||||
sqlx::query(
|
||||
"INSERT INTO youtube_meta_tags (youtube_id, tag) VALUES ($1, $2)")
|
||||
.bind(&meta.youtube_id().unwrap()).bind(&tag).execute(&mut *tx).await.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
tx.commit().unwrap();
|
||||
tx.commit().await.unwrap();
|
||||
|
||||
return Self {
|
||||
return Some(Self {
|
||||
directory: Some(dir),
|
||||
path: Some(v.to_owned()),
|
||||
title: Some(meta.title()),
|
||||
|
@ -241,24 +221,43 @@ impl Video {
|
|||
categories: meta.categories(),
|
||||
tags: meta.tags(),
|
||||
upload_date: meta.upload_date(),
|
||||
db: Some(db.clone()),
|
||||
};
|
||||
db: Some(db),
|
||||
});
|
||||
}
|
||||
|
||||
tx.execute(
|
||||
"INSERT OR REPLACE INTO videos (sha256, directory, path, title) VALUES (?1, ?2, ?3, ?4)",
|
||||
[&id, &dir, v.to_str().unwrap(), &file_name],
|
||||
)
|
||||
.unwrap();
|
||||
sqlx::query(
|
||||
"INSERT INTO videos (sha256, directory, path, title) VALUES ($1, $2, $3, $4)")
|
||||
.bind(&id).bind(dir).bind(v.to_str().unwrap()).bind(file_name).execute(&mut *tx).await.unwrap();
|
||||
|
||||
tx.commit().unwrap();
|
||||
tx.commit().await.unwrap();
|
||||
|
||||
Self {
|
||||
Some(Self {
|
||||
path: Some(v.to_owned()),
|
||||
title: Some(v.file_stem().unwrap().to_str().unwrap().to_owned()),
|
||||
hash: id,
|
||||
db: Some(db.clone()),
|
||||
db: Some(db),
|
||||
..Self::default()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Video<'_> {
|
||||
pub async fn api(&mut self) -> serde_json::Value {
|
||||
self.get_video_info().await;
|
||||
self.get_youtube_meta_info().await;
|
||||
|
||||
json!({
|
||||
"directory": self.directory.as_ref().unwrap(),
|
||||
"title": self.title.as_ref().unwrap(),
|
||||
"hash": self.hash,
|
||||
"youtube_id": self.youtube_id,
|
||||
"uploader_name": self.uploader_name,
|
||||
"uploader_id": self.uploader_id,
|
||||
"duration": self.duration,
|
||||
"views": self.views,
|
||||
"categories": self.categories,
|
||||
"tags": self.tags,
|
||||
"upload_date": self.upload_date,
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue