Better video ID handling + Instance::new improvements
This commit is contained in:
parent
3fc3eb8d68
commit
461ccaa4b9
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -33,7 +33,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "almond"
|
name = "almond-api"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"axum",
|
"axum",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "almond"
|
name = "almond-api"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
|
@ -4,13 +4,11 @@ use serde::{Deserialize, Serialize};
|
|||||||
use sqlx::SqlitePool;
|
use sqlx::SqlitePool;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::video::Video;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub host: String,
|
pub host: String,
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
pub password: Option<String>,
|
pub password: String,
|
||||||
pub videos_per_page: usize,
|
pub videos_per_page: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -19,7 +17,7 @@ impl Default for Config {
|
|||||||
Self {
|
Self {
|
||||||
host: "0.0.0.0".into(),
|
host: "0.0.0.0".into(),
|
||||||
port: 3000,
|
port: 3000,
|
||||||
password: None,
|
password: "123456".into(),
|
||||||
videos_per_page: 10,
|
videos_per_page: 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -36,10 +34,10 @@ pub struct Instance {
|
|||||||
pub enum NewInstanceError {
|
pub enum NewInstanceError {
|
||||||
#[error("Failed to open TOML configuration file")]
|
#[error("Failed to open TOML configuration file")]
|
||||||
ConfigLoad(#[from] io::Error),
|
ConfigLoad(#[from] io::Error),
|
||||||
#[error("Could not parse TOML configuration: {0}")]
|
#[error("Could not parse TOML configuration")]
|
||||||
ConfigParse(#[from] toml::de::Error),
|
ConfigParse(#[from] toml::de::Error),
|
||||||
#[error("Failed to fetch entries from database: {0}")]
|
#[error("Failed to create connection pool")]
|
||||||
Populate(#[from] sqlx::Error),
|
PoolConnect(#[from] sqlx::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Instance {
|
impl Instance {
|
||||||
@ -52,10 +50,4 @@ impl Instance {
|
|||||||
|
|
||||||
Ok(Self { config, pool })
|
Ok(Self { config, pool })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn fetch_videos(&self) -> Result<Vec<Video>, sqlx::Error> {
|
|
||||||
sqlx::query_as!(Video, "SELECT * FROM video")
|
|
||||||
.fetch_all(&self.pool)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ use axum::{
|
|||||||
routing::{get, post},
|
routing::{get, post},
|
||||||
};
|
};
|
||||||
use instance::Instance;
|
use instance::Instance;
|
||||||
use routes::{list_entries, upload_video};
|
use routes::{list_videos, upload_video};
|
||||||
use tokio::signal;
|
use tokio::signal;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
let address = format!("{}:{}", instance.config.host, instance.config.port);
|
let address = format!("{}:{}", instance.config.host, instance.config.port);
|
||||||
|
|
||||||
let almond = Router::new()
|
let almond = Router::new()
|
||||||
.route("/", get(list_entries))
|
.route("/", get(list_videos))
|
||||||
.route("/upload", post(upload_video))
|
.route("/upload", post(upload_video))
|
||||||
.with_state(instance);
|
.with_state(instance);
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use axum::{
|
use axum::{
|
||||||
Json, debug_handler,
|
Json,
|
||||||
extract::{Query, State},
|
extract::{Query, State},
|
||||||
http::StatusCode,
|
http::StatusCode,
|
||||||
};
|
};
|
||||||
@ -12,12 +12,12 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct ListEntriesQuery {
|
pub struct ListVideosQuery {
|
||||||
page: Option<usize>,
|
page: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
pub struct ListEntriesResponse {
|
pub struct ListVideosResponse {
|
||||||
videos: Vec<Video>,
|
videos: Vec<Video>,
|
||||||
page: usize,
|
page: usize,
|
||||||
per_page: usize,
|
per_page: usize,
|
||||||
@ -26,12 +26,14 @@ pub struct ListEntriesResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve video list as JSON (paged)
|
/// Retrieve video list as JSON (paged)
|
||||||
#[debug_handler]
|
pub async fn list_videos(
|
||||||
pub async fn list_entries(
|
|
||||||
State(state): State<Instance>,
|
State(state): State<Instance>,
|
||||||
Query(query): Query<ListEntriesQuery>,
|
Query(query): Query<ListVideosQuery>,
|
||||||
) -> Result<Json<ListEntriesResponse>, StatusCode> {
|
) -> Result<Json<ListVideosResponse>, StatusCode> {
|
||||||
let Ok(videos) = state.fetch_videos().await else {
|
let Ok(videos) = sqlx::query_as!(Video, "SELECT * FROM video")
|
||||||
|
.fetch_all(&state.pool)
|
||||||
|
.await
|
||||||
|
else {
|
||||||
error!("Could not fetch videos from database!");
|
error!("Could not fetch videos from database!");
|
||||||
return Err(StatusCode::INTERNAL_SERVER_ERROR);
|
return Err(StatusCode::INTERNAL_SERVER_ERROR);
|
||||||
};
|
};
|
||||||
@ -50,7 +52,7 @@ pub async fn list_entries(
|
|||||||
vec![]
|
vec![]
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Json(ListEntriesResponse {
|
Ok(Json(ListVideosResponse {
|
||||||
videos,
|
videos,
|
||||||
page,
|
page,
|
||||||
per_page,
|
per_page,
|
||||||
@ -70,12 +72,18 @@ pub async fn upload_video(
|
|||||||
State(state): State<Instance>,
|
State(state): State<Instance>,
|
||||||
Query(query): Query<UploadVideoQuery>,
|
Query(query): Query<UploadVideoQuery>,
|
||||||
) -> StatusCode {
|
) -> StatusCode {
|
||||||
let Ok(videos) = state.fetch_videos().await else {
|
let id = match sqlx::query_scalar!("SELECT MAX(id) FROM video")
|
||||||
error!("Could not fetch videos from database!");
|
.fetch_one(&state.pool)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(Some(max_id)) => max_id + 1,
|
||||||
|
Ok(None) => 0,
|
||||||
|
Err(_) => {
|
||||||
return StatusCode::INTERNAL_SERVER_ERROR;
|
return StatusCode::INTERNAL_SERVER_ERROR;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let new_video = Video::from_url(&query.url, videos.len(), query.cookie.as_deref())
|
let new_video = Video::from_url(&query.url, id, query.cookie.as_deref())
|
||||||
.await
|
.await
|
||||||
.map_err(|e| match e {
|
.map_err(|e| match e {
|
||||||
VideoError::InvalidUrl | VideoError::UrlParse(_) => StatusCode::BAD_REQUEST,
|
VideoError::InvalidUrl | VideoError::UrlParse(_) => StatusCode::BAD_REQUEST,
|
||||||
|
@ -95,8 +95,7 @@ impl Video {
|
|||||||
Some(query_v.1.to_string())
|
Some(query_v.1.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::cast_possible_wrap)]
|
pub async fn from_url(url: &str, id: i64, cookie: Option<&str>) -> Result<Self, VideoError> {
|
||||||
pub async fn from_url(url: &str, id: usize, cookie: Option<&str>) -> Result<Self, VideoError> {
|
|
||||||
let url = Url::parse(url)?;
|
let url = Url::parse(url)?;
|
||||||
info!("Parsed argument as URL");
|
info!("Parsed argument as URL");
|
||||||
|
|
||||||
@ -141,8 +140,6 @@ impl Video {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
let id = id as i64;
|
|
||||||
|
|
||||||
let url = url.to_string();
|
let url = url.to_string();
|
||||||
let description = format!("{file_stem}.description");
|
let description = format!("{file_stem}.description");
|
||||||
let title = get_info_value("title").to_string();
|
let title = get_info_value("title").to_string();
|
||||||
@ -175,6 +172,7 @@ impl Video {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let sha256 = format!("{:x}", Sha3_256::digest(&buffer));
|
let sha256 = format!("{:x}", Sha3_256::digest(&buffer));
|
||||||
|
#[allow(clippy::cast_possible_wrap)]
|
||||||
let file_size = buffer.len() as i64;
|
let file_size = buffer.len() as i64;
|
||||||
let author_id = get_info_value("channel_id").to_string();
|
let author_id = get_info_value("channel_id").to_string();
|
||||||
let author_url = get_info_value("channel_url").to_string();
|
let author_url = get_info_value("channel_url").to_string();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user