lyrics
This commit is contained in:
parent
70d45e42a8
commit
d309c5d8c1
3 changed files with 59 additions and 2 deletions
|
@ -1,7 +1,7 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use sqlx::prelude::FromRow;
|
use sqlx::prelude::FromRow;
|
||||||
use std::str::FromStr;
|
use std::{io::Read, str::FromStr};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
get_pg,
|
get_pg,
|
||||||
|
@ -325,6 +325,47 @@ impl Track {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_lyrics(&self) -> Option<serde_json::Value> {
|
||||||
|
let base_path = std::path::Path::new(&self.path).with_extension("");
|
||||||
|
let extensions = vec!["lrc", "txt"];
|
||||||
|
|
||||||
|
for ext in &extensions {
|
||||||
|
let candidate = base_path.with_extension(ext);
|
||||||
|
if candidate.exists() {
|
||||||
|
if let Ok(mut file) = std::fs::File::open(&candidate) {
|
||||||
|
let mut contents = String::new();
|
||||||
|
if file.read_to_string(&mut contents).is_ok() {
|
||||||
|
// Determine if it's timed lyrics or plain text
|
||||||
|
let lyrics_type = if ext == &"lrc" { "timed" } else { "plain" };
|
||||||
|
|
||||||
|
return Some(json!({
|
||||||
|
"lyrics": contents,
|
||||||
|
"source": "file",
|
||||||
|
"type": lyrics_type
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(lyrics) = self.meta.as_ref()?.get("lyrics").and_then(|l| l.as_str()) {
|
||||||
|
// Check if lyrics seem to be timed or plain text
|
||||||
|
let lyrics_type = if lyrics.contains('[') && lyrics.contains(']') {
|
||||||
|
"timed" // A rough check for LRC-style brackets indicating timing
|
||||||
|
} else {
|
||||||
|
"plain"
|
||||||
|
};
|
||||||
|
|
||||||
|
return Some(json!({
|
||||||
|
"lyrics": lyrics,
|
||||||
|
"type": lyrics_type
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// No lyrics found
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
/// Finds the first track of a given album.
|
/// Finds the first track of a given album.
|
||||||
pub async fn find_first_of_album(album: &uuid::Uuid) -> Option<Self> {
|
pub async fn find_first_of_album(album: &uuid::Uuid) -> Option<Self> {
|
||||||
sqlx::query_as("SELECT * FROM track WHERE album = $1 LIMIT 1")
|
sqlx::query_as("SELECT * FROM track WHERE album = $1 LIMIT 1")
|
||||||
|
|
|
@ -90,7 +90,8 @@ async fn rocket() -> _ {
|
||||||
route::admin::get_orphans_route,
|
route::admin::get_orphans_route,
|
||||||
route::admin::get_singles_route,
|
route::admin::get_singles_route,
|
||||||
route::event::event_report_route,
|
route::event::event_report_route,
|
||||||
route::search::search_route
|
route::search::search_route,
|
||||||
|
route::track::track_lyrics_route
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
.manage(lib)
|
.manage(lib)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use super::api_error;
|
use super::api_error;
|
||||||
|
use super::no_uuid_error;
|
||||||
use super::to_uuid;
|
use super::to_uuid;
|
||||||
use super::FallibleApiResponse;
|
use super::FallibleApiResponse;
|
||||||
use super::ToAPI;
|
use super::ToAPI;
|
||||||
|
@ -60,3 +61,17 @@ pub async fn track_audio_aac128_route(track_id: &str, lib: &State<Libary>) -> Op
|
||||||
.await?;
|
.await?;
|
||||||
NamedFile::open(track.get_aac(128)?).await.ok()
|
NamedFile::open(track.get_aac(128)?).await.ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[get("/track/<track_id>/lyrics")]
|
||||||
|
pub async fn track_lyrics_route(track_id: &str, lib: &State<Libary>) -> FallibleApiResponse {
|
||||||
|
let track = lib
|
||||||
|
.get_track_by_id(&uuid::Uuid::from_str(track_id).map_err(|_| no_uuid_error())?)
|
||||||
|
.await
|
||||||
|
.ok_or_else(|| api_error("No such track"))?;
|
||||||
|
|
||||||
|
if let Some(lyrics) = track.get_lyrics().await {
|
||||||
|
return Ok(lyrics);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(json!({}))
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue