Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ pub mod utils;
pub mod db;
pub mod queue;
pub mod providers;
pub mod models;

pub struct AppState {
pool: Pool<SqliteConnectionManager>,
Expand Down
1 change: 1 addition & 0 deletions server/src/models.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod priority;
18 changes: 18 additions & 0 deletions server/src/models/priority.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use std::fmt::Display;
use serde::{self, Deserialize, Serialize};

#[derive(Copy, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Priority {
Synced,
Plain
}

impl Display for Priority {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Priority::Synced => write!(f, "synced"),
Priority::Plain => write!(f, "plain"),
}
}
}
31 changes: 28 additions & 3 deletions server/src/repositories/track_repository.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use indoc::indoc;
use crate::{
entities::{lyrics::SimpleLyrics, track::SimpleTrack},
utils::prepare_input,
models::priority::Priority,
};
use chrono::prelude::*;
use rusqlite::params_from_iter;
Expand Down Expand Up @@ -117,6 +118,7 @@ pub fn get_track_by_metadata(
artist_name_lower: &str,
album_name_lower: Option<&str>,
duration: Option<f64>,
priority: Option<Priority>,
conn: &mut Connection,
) -> Result<Option<SimpleTrack>> {
// Start building the SQL query
Expand Down Expand Up @@ -162,11 +164,22 @@ pub fn get_track_by_metadata(
params.push(album_name_lower.to_string().into());
}

// Put tracks with synced or plain lyrics at the beginning
let mut order_clauses = vec![];
if let Some(priority) = priority {
match priority {
Priority::Synced => order_clauses.push("lyrics.has_synced_lyrics IS 0".to_string()),
Priority::Plain => order_clauses.push("lyrics.has_plain_lyrics IS 0".to_string())
}
}

// Combine all parts of the query
order_clauses.push("lyrics.id".to_string());
let query = format!(
"{select} WHERE {where_clause} ORDER BY tracks.id",
"{select} WHERE {where_clause} ORDER BY {order_clause}",
select = select_query,
where_clause = where_clauses.join(" AND ")
where_clause = where_clauses.join(" AND "),
order_clause = order_clauses.join(", ")
);

// Prepare and execute the statement
Expand Down Expand Up @@ -201,6 +214,7 @@ pub fn get_tracks_by_keyword(
track_name: Option<&str>,
artist_name: Option<&str>,
album_name: Option<&str>,
priority: Option<Priority>,
conn: &mut Connection,
) -> Result<Vec<SimpleTrack>> {
// To search track by keyword, at least q or track_name must be present
Expand All @@ -224,6 +238,15 @@ pub fn get_tracks_by_keyword(
indoc! {"SELECT rowid FROM tracks_fts WHERE tracks_fts MATCH ? LIMIT 20"}.to_string()
};

// Put tracks with synced or plain lyrics at the beginning
let mut order_clauses = vec![];
if let Some(priority) = priority {
match priority {
Priority::Synced => order_clauses.push("lyrics.has_synced_lyrics IS 0"),
Priority::Plain => order_clauses.push("lyrics.has_plain_lyrics IS 0")
}
};

// Build the complete query using the subquery
let query = format!(
"SELECT
Expand All @@ -239,8 +262,10 @@ pub fn get_tracks_by_keyword(
({subquery}) AS search_results
LEFT JOIN tracks ON search_results.rowid = tracks.id
LEFT JOIN lyrics ON tracks.last_lyrics_id = lyrics.id
{order_clause}
",
subquery = subquery
subquery = subquery,
order_clause = if !order_clauses.is_empty() { format!("ORDER BY {}", order_clauses.join(", ")) } else { String::new() }
);

let mut statement = conn.prepare(&query)?;
Expand Down
7 changes: 5 additions & 2 deletions server/src/routes/get_lyrics_by_metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::{
errors::ApiError,
repositories::track_repository::get_track_by_metadata,
utils::process_param,
models::priority::Priority,
AppState,
};
use axum_macros::debug_handler;
Expand All @@ -23,6 +24,7 @@ pub struct QueryParams {
album_name: Option<String>,
#[validate(range(min = 1.0, max = 3600.0, message = "must be between 1 and 3600"))]
duration: Option<f64>,
prioritize: Option<Priority>,
}

#[derive(Serialize)]
Expand Down Expand Up @@ -52,7 +54,7 @@ pub async fn route(Query(params): Query<QueryParams>, State(state): State<Arc<Ap

if let (Some(track_name_lower), Some(artist_name_lower)) = (track_name_lower, artist_name_lower) {
// Attempt to fetch the track with all provided metadata
if let Some(track) = fetch_track(&track_name_lower, &artist_name_lower, album_name_lower.as_deref(), params.duration, &mut conn).await? {
if let Some(track) = fetch_track(&track_name_lower, &artist_name_lower, album_name_lower.as_deref(), params.duration, params.prioritize, &mut conn).await? {
return Ok(Json(create_response(track)));
}

Expand All @@ -72,12 +74,13 @@ pub async fn route(Query(params): Query<QueryParams>, State(state): State<Arc<Ap
Err(ApiError::TrackNotFoundError)
}

async fn fetch_track(track_name_lower: &str, artist_name_lower: &str, album_name_lower: Option<&str>, duration: Option<f64>, conn: &mut Connection) -> Result<Option<SimpleTrack>> {
async fn fetch_track(track_name_lower: &str, artist_name_lower: &str, album_name_lower: Option<&str>, duration: Option<f64>, priority: Option<Priority>, conn: &mut Connection) -> Result<Option<SimpleTrack>> {
get_track_by_metadata(
track_name_lower,
artist_name_lower,
album_name_lower,
duration,
priority,
conn,
)
}
Expand Down
17 changes: 15 additions & 2 deletions server/src/routes/search_lyrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::{
errors::ApiError,
repositories::track_repository::get_tracks_by_keyword,
utils::process_param,
models::priority::Priority,
AppState,
};

Expand All @@ -17,6 +18,7 @@ pub struct QueryParams {
track_name: Option<String>,
artist_name: Option<String>,
album_name: Option<String>,
prioritize: Option<Priority>,
}

#[derive(Serialize, Deserialize, Clone)]
Expand Down Expand Up @@ -44,14 +46,21 @@ pub async fn route(Query(params): Query<QueryParams>, State(state): State<Arc<Ap
let track_name = process_param(params.track_name.as_deref());
let artist_name = process_param(params.artist_name.as_deref());
let album_name = process_param(params.album_name.as_deref());
let priority = params.prioritize;

let priority_str = match priority {
Some(p) => p.to_string(),
None => String::new(),
};

// Generate a cache key based on query parameters
let cache_key = format!(
"{}:{}:{}:{}",
"{}:{}:{}:{}:{}",
q.as_deref().unwrap_or_default(),
track_name.as_deref().unwrap_or_default(),
artist_name.as_deref().unwrap_or_default(),
album_name.as_deref().unwrap_or_default()
album_name.as_deref().unwrap_or_default(),
priority_str
);

let cached_result: Option<CachedResult> = {
Expand Down Expand Up @@ -85,6 +94,7 @@ pub async fn route(Query(params): Query<QueryParams>, State(state): State<Arc<Ap
track_name_clone.as_deref(),
artist_name_clone.as_deref(),
album_name_clone.as_deref(),
priority,
).await;
});
}
Expand All @@ -99,6 +109,7 @@ pub async fn route(Query(params): Query<QueryParams>, State(state): State<Arc<Ap
track_name.as_deref(),
artist_name.as_deref(),
album_name.as_deref(),
priority
).await?;

Ok(Json(response))
Expand Down Expand Up @@ -144,13 +155,15 @@ async fn fetch_and_cache_tracks(
track_name: Option<&str>,
artist_name: Option<&str>,
album_name: Option<&str>,
priority: Option<Priority>
) -> Result<Vec<TrackResponse>, ApiError> {
let mut conn = state.pool.get()?;
let tracks = get_tracks_by_keyword(
q,
track_name,
artist_name,
album_name,
priority,
&mut conn,
)?;

Expand Down