Fix clippy lint errors
Some checks failed
Push Workflows / test (push) Successful in 1m33s
Push Workflows / docs (push) Successful in 1m45s
Push Workflows / clippy (push) Failing after 2m25s
Push Workflows / build (push) Successful in 4m0s
Push Workflows / leptos-test (push) Successful in 6m33s
Push Workflows / docker-build (push) Successful in 7m44s
Push Workflows / nix-build (push) Successful in 19m55s
Some checks failed
Push Workflows / test (push) Successful in 1m33s
Push Workflows / docs (push) Successful in 1m45s
Push Workflows / clippy (push) Failing after 2m25s
Push Workflows / build (push) Successful in 4m0s
Push Workflows / leptos-test (push) Successful in 6m33s
Push Workflows / docker-build (push) Successful in 7m44s
Push Workflows / nix-build (push) Successful in 19m55s
This commit is contained in:
@ -29,7 +29,7 @@ pub async fn add_album(album_title: String, release_date: Option<String>, image_
|
||||
|
||||
let parsed_release_date = match release_date {
|
||||
Some(date) => {
|
||||
match NaiveDate::parse_from_str(&date.trim(), "%Y-%m-%d") {
|
||||
match NaiveDate::parse_from_str(date.trim(), "%Y-%m-%d") {
|
||||
Ok(parsed_date) => Some(parsed_date),
|
||||
Err(_e) => return Err(ServerFnError::<NoCustomError>::ServerError("Invalid release date".to_string()))
|
||||
}
|
||||
@ -37,16 +37,7 @@ pub async fn add_album(album_title: String, release_date: Option<String>, image_
|
||||
None => None
|
||||
};
|
||||
|
||||
let image_path_arg = match image_path {
|
||||
Some(image_path) => {
|
||||
if image_path.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(image_path)
|
||||
}
|
||||
},
|
||||
None => None
|
||||
};
|
||||
let image_path_arg = image_path.filter(|image_path| !image_path.is_empty());
|
||||
|
||||
let new_album = Album {
|
||||
id: None,
|
||||
|
@ -92,7 +92,7 @@ pub async fn top_songs_by_artist(artist_id: i32, limit: Option<i64>) -> Result<V
|
||||
};
|
||||
|
||||
let song_play_counts: HashMap<i32, i64> = song_play_counts.into_iter().collect();
|
||||
let top_song_ids: Vec<i32> = song_play_counts.iter().map(|(song_id, _)| *song_id).collect();
|
||||
let top_song_ids: Vec<i32> = song_play_counts.keys().copied().collect();
|
||||
|
||||
let top_songs: Vec<(Song, Option<Album>, Option<Artist>, Option<(i32, i32)>, Option<(i32, i32)>)>
|
||||
= songs::table
|
||||
@ -131,20 +131,20 @@ pub async fn top_songs_by_artist(artist_id: i32, limit: Option<i64>) -> Result<V
|
||||
};
|
||||
|
||||
let image_path = song.image_path.unwrap_or(
|
||||
album.as_ref().map(|album| album.image_path.clone()).flatten()
|
||||
album.as_ref().and_then(|album| album.image_path.clone())
|
||||
.unwrap_or("/assets/images/placeholders/MusicPlaceholder.svg".to_string()));
|
||||
|
||||
let songdata = frontend::Song {
|
||||
id: song_id,
|
||||
title: song.title,
|
||||
artists: artist.map(|artist| vec![artist]).unwrap_or_default(),
|
||||
album: album,
|
||||
album,
|
||||
track: song.track,
|
||||
duration: song.duration,
|
||||
release_date: song.release_date,
|
||||
song_path: song.storage_path,
|
||||
image_path: image_path,
|
||||
like_dislike: like_dislike,
|
||||
image_path,
|
||||
like_dislike,
|
||||
added_date: song.added_date.unwrap(),
|
||||
};
|
||||
|
||||
@ -155,7 +155,7 @@ pub async fn top_songs_by_artist(artist_id: i32, limit: Option<i64>) -> Result<V
|
||||
}
|
||||
}
|
||||
|
||||
let mut top_songs: Vec<(frontend::Song, i64)> = top_songs_map.into_iter().map(|(_, v)| v).collect();
|
||||
let mut top_songs: Vec<(frontend::Song, i64)> = top_songs_map.into_values().collect();
|
||||
top_songs.sort_by(|(_, plays1), (_, plays2)| plays2.cmp(plays1));
|
||||
Ok(top_songs)
|
||||
}
|
||||
@ -205,7 +205,7 @@ pub async fn albums_by_artist(artist_id: i32, limit: Option<i64>) -> Result<Vec<
|
||||
}
|
||||
}
|
||||
|
||||
let mut albums: Vec<frontend::Album> = albums_map.into_iter().map(|(_, v)| v).collect();
|
||||
let mut albums: Vec<frontend::Album> = albums_map.into_values().collect();
|
||||
albums.sort_by(|a1, a2| a2.release_date.cmp(&a1.release_date));
|
||||
Ok(albums)
|
||||
}
|
||||
|
@ -114,9 +114,9 @@ pub async fn check_auth() -> Result<bool, ServerFnError> {
|
||||
/// use libretunes::api::auth::require_auth;
|
||||
/// #[server(endpoint = "protected_route")]
|
||||
/// pub async fn protected_route() -> Result<(), ServerFnError> {
|
||||
/// require_auth().await?;
|
||||
/// // Continue with protected route
|
||||
/// Ok(())
|
||||
/// require_auth().await?;
|
||||
/// // Continue with protected route
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(feature = "ssr")]
|
||||
@ -125,7 +125,7 @@ pub async fn require_auth() -> Result<(), ServerFnError> {
|
||||
if logged_in {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ServerFnError::<NoCustomError>::ServerError(format!("Unauthorized")))
|
||||
Err(ServerFnError::<NoCustomError>::ServerError("Unauthorized".to_string()))
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -139,10 +139,10 @@ pub async fn require_auth() -> Result<(), ServerFnError> {
|
||||
/// use libretunes::api::auth::get_user;
|
||||
/// #[server(endpoint = "user_route")]
|
||||
/// pub async fn user_route() -> Result<(), ServerFnError> {
|
||||
/// let user = get_user().await?;
|
||||
/// println!("Logged in as: {}", user.username);
|
||||
/// // Do something with the user
|
||||
/// Ok(())
|
||||
/// let user = get_user().await?;
|
||||
/// println!("Logged in as: {}", user.username);
|
||||
/// // Do something with the user
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(feature = "ssr")]
|
||||
@ -184,9 +184,9 @@ pub async fn check_admin() -> Result<bool, ServerFnError> {
|
||||
/// use libretunes::api::auth::require_admin;
|
||||
/// #[server(endpoint = "protected_admin_route")]
|
||||
/// pub async fn protected_admin_route() -> Result<(), ServerFnError> {
|
||||
/// require_admin().await?;
|
||||
/// // Continue with protected route
|
||||
/// Ok(())
|
||||
/// require_admin().await?;
|
||||
/// // Continue with protected route
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(feature = "ssr")]
|
||||
@ -195,7 +195,7 @@ pub async fn require_admin() -> Result<(), ServerFnError> {
|
||||
if is_admin {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ServerFnError::<NoCustomError>::ServerError(format!("Unauthorized")))
|
||||
Err(ServerFnError::<NoCustomError>::ServerError("Unauthorized".to_string()))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -126,20 +126,20 @@ pub async fn recent_songs(for_user_id: i32, limit: Option<i64>) -> Result<Vec<(N
|
||||
};
|
||||
|
||||
let image_path = song.image_path.unwrap_or(
|
||||
album.as_ref().map(|album| album.image_path.clone()).flatten()
|
||||
album.as_ref().and_then(|album| album.image_path.clone())
|
||||
.unwrap_or("/assets/images/placeholders/MusicPlaceholder.svg".to_string()));
|
||||
|
||||
let songdata = frontend::Song {
|
||||
id: song_id,
|
||||
title: song.title,
|
||||
artists: artist.map(|artist| vec![artist]).unwrap_or_default(),
|
||||
album: album,
|
||||
album,
|
||||
track: song.track,
|
||||
duration: song.duration,
|
||||
release_date: song.release_date,
|
||||
song_path: song.storage_path,
|
||||
image_path: image_path,
|
||||
like_dislike: like_dislike,
|
||||
image_path,
|
||||
like_dislike,
|
||||
added_date: song.added_date.unwrap(),
|
||||
};
|
||||
|
||||
@ -186,7 +186,7 @@ pub async fn top_songs(for_user_id: i32, start_date: NaiveDateTime, end_date: Na
|
||||
};
|
||||
|
||||
let history_counts: HashMap<i32, i64> = history_counts.into_iter().collect();
|
||||
let history_song_ids = history_counts.iter().map(|(song_id, _)| *song_id).collect::<Vec<i32>>();
|
||||
let history_song_ids = history_counts.keys().copied().collect::<Vec<i32>>();
|
||||
|
||||
// Get the song data for the songs listened to in the date range
|
||||
let history_songs: Vec<(Song, Option<Album>, Option<Artist>, Option<(i32, i32)>, Option<(i32, i32)>)>
|
||||
@ -227,20 +227,20 @@ pub async fn top_songs(for_user_id: i32, start_date: NaiveDateTime, end_date: Na
|
||||
};
|
||||
|
||||
let image_path = song.image_path.unwrap_or(
|
||||
album.as_ref().map(|album| album.image_path.clone()).flatten()
|
||||
album.as_ref().and_then(|album| album.image_path.clone())
|
||||
.unwrap_or("/assets/images/placeholders/MusicPlaceholder.svg".to_string()));
|
||||
|
||||
let songdata = frontend::Song {
|
||||
id: song_id,
|
||||
title: song.title,
|
||||
artists: artist.map(|artist| vec![artist]).unwrap_or_default(),
|
||||
album: album,
|
||||
album,
|
||||
track: song.track,
|
||||
duration: song.duration,
|
||||
release_date: song.release_date,
|
||||
song_path: song.storage_path,
|
||||
image_path: image_path,
|
||||
like_dislike: like_dislike,
|
||||
image_path,
|
||||
like_dislike,
|
||||
added_date: song.added_date.unwrap(),
|
||||
};
|
||||
|
||||
|
@ -84,8 +84,7 @@ pub async fn get_song_by_id(song_id: i32) -> Result<Option<frontend::Song>, Serv
|
||||
.load(db_con)?;
|
||||
|
||||
let song = song_parts.first().cloned();
|
||||
let artists = song_parts.into_iter().map(|(_, _, artist, _, _)| artist)
|
||||
.filter_map(|artist| artist).collect::<Vec<_>>();
|
||||
let artists = song_parts.into_iter().filter_map(|(_, _, artist, _, _)| artist).collect::<Vec<_>>();
|
||||
|
||||
match song {
|
||||
Some((song, album, _artist, like, dislike)) => {
|
||||
@ -99,13 +98,13 @@ pub async fn get_song_by_id(song_id: i32) -> Result<Option<frontend::Song>, Serv
|
||||
Ok(Some(frontend::Song {
|
||||
id: song.id.unwrap(),
|
||||
title: song.title.clone(),
|
||||
artists: artists,
|
||||
album: album.clone().map(|album| album.into()),
|
||||
artists,
|
||||
album: album.clone(),
|
||||
track: song.track,
|
||||
duration: song.duration,
|
||||
release_date: song.release_date,
|
||||
song_path: song.storage_path.clone(),
|
||||
image_path: image_path,
|
||||
image_path,
|
||||
like_dislike: Some((like.is_some(), dislike.is_some())),
|
||||
added_date: song.added_date.unwrap(),
|
||||
}))
|
||||
|
@ -108,13 +108,13 @@ async fn validate_track_number(track_number: Field<'static>) -> Result<Option<i3
|
||||
|
||||
if let Ok(track_number) = track_number.parse::<i32>() {
|
||||
if track_number < 0 {
|
||||
return Err(ServerFnError::<NoCustomError>::
|
||||
ServerError("Track number must be positive or 0".to_string()));
|
||||
Err(ServerFnError::<NoCustomError>::
|
||||
ServerError("Track number must be positive or 0".to_string()))
|
||||
} else {
|
||||
Ok(Some(track_number))
|
||||
}
|
||||
} else {
|
||||
return Err(ServerFnError::<NoCustomError>::ServerError("Error parsing track number".to_string()));
|
||||
Err(ServerFnError::<NoCustomError>::ServerError("Error parsing track number".to_string()))
|
||||
}
|
||||
},
|
||||
Err(e) => Err(ServerFnError::<NoCustomError>::ServerError(format!("Error reading track number: {}", e)))?,
|
||||
@ -131,7 +131,7 @@ async fn validate_release_date(release_date: Field<'static>) -> Result<Option<Na
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let release_date = NaiveDate::parse_from_str(&release_date.trim(), "%Y-%m-%d");
|
||||
let release_date = NaiveDate::parse_from_str(release_date.trim(), "%Y-%m-%d");
|
||||
|
||||
match release_date {
|
||||
Ok(release_date) => Ok(Some(release_date)),
|
||||
@ -192,10 +192,11 @@ pub async fn upload(data: MultipartData) -> Result<(), ServerFnError> {
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.open(upload_path.clone())?;
|
||||
|
||||
while let Some(chunk) = field.chunk().await? {
|
||||
file.write(&chunk)?;
|
||||
file.write_all(&chunk)?;
|
||||
}
|
||||
|
||||
file.flush()?;
|
||||
|
@ -42,7 +42,7 @@ pub fn AddAlbum(open: RwSignal<bool>) -> impl IntoView {
|
||||
release_date.set("".to_string());
|
||||
image_path.set("".to_string());
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
view! {
|
||||
|
@ -34,7 +34,7 @@ pub fn AddArtist(open: RwSignal<bool>) -> impl IntoView {
|
||||
log!("Added artist: {:?}", artist);
|
||||
artist_name.set("".to_string());
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
view! {
|
||||
|
@ -26,9 +26,7 @@ pub fn Profile() -> impl IntoView {
|
||||
let user_profile_picture = move || {
|
||||
user.get().and_then(|user| {
|
||||
if let Some(user) = user {
|
||||
if user.id.is_none() {
|
||||
return None;
|
||||
}
|
||||
user.id?;
|
||||
Some(format!("/assets/images/profile/{}.webp", user.id.unwrap()))
|
||||
} else {
|
||||
None
|
||||
|
@ -96,13 +96,11 @@ pub fn set_playing(play: bool) {
|
||||
status.playing = true;
|
||||
log!("Successfully played audio");
|
||||
}
|
||||
} else if let Err(e) = audio.pause() {
|
||||
error!("Unable to pause audio: {:?}", e);
|
||||
} else {
|
||||
if let Err(e) = audio.pause() {
|
||||
error!("Unable to pause audio: {:?}", e);
|
||||
} else {
|
||||
status.playing = false;
|
||||
log!("Successfully paused audio");
|
||||
}
|
||||
status.playing = false;
|
||||
log!("Successfully paused audio");
|
||||
}
|
||||
} else {
|
||||
error!("Unable to play/pause audio: Audio element not available");
|
||||
@ -239,7 +237,7 @@ fn MediaInfo() -> impl IntoView {
|
||||
|
||||
let artist = Signal::derive(move || {
|
||||
status.with(|status| {
|
||||
status.queue.front().map_or("".into(), |song| format!("{}", Artist::display_list(&song.artists)))
|
||||
status.queue.front().map_or("".into(), |song| Artist::display_list(&song.artists).to_string())
|
||||
})
|
||||
});
|
||||
|
||||
@ -324,7 +322,6 @@ fn LikeDislike() -> impl IntoView {
|
||||
},
|
||||
_ => {
|
||||
log!("Unable to like song: No song in queue");
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -364,7 +361,6 @@ fn LikeDislike() -> impl IntoView {
|
||||
},
|
||||
_ => {
|
||||
log!("Unable to dislike song: No song in queue");
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -532,13 +528,13 @@ pub fn PlayBar() -> impl IntoView {
|
||||
if let Some(src) = src {
|
||||
GlobalState::play_status().with_untracked(|status| {
|
||||
if let Some(audio) = status.get_audio() {
|
||||
audio.set_src(&src);
|
||||
audio.set_src(src);
|
||||
} else {
|
||||
error!("Unable to set audio source: Audio element not available");
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
// Track the last song that was added to the history to prevent duplicates
|
||||
|
@ -158,7 +158,15 @@ pub fn SongArtists(artists: Vec<Artist>) -> impl IntoView {
|
||||
Either::Right(view! { <span>{artist.name.clone()}</span> })
|
||||
}
|
||||
}
|
||||
{if i < num_artists - 2 { ", " } else if i == num_artists - 2 { " & " } else { "" }}
|
||||
{
|
||||
use std::cmp::Ordering;
|
||||
|
||||
match i.cmp(&(num_artists - 2)) {
|
||||
Ordering::Less => ", ",
|
||||
Ordering::Equal => " & ",
|
||||
Ordering::Greater => "",
|
||||
}
|
||||
}
|
||||
}
|
||||
}).collect::<Vec<_>>()
|
||||
}
|
||||
@ -224,13 +232,10 @@ pub fn SongLikeDislike(
|
||||
// If an error occurs, check the like/dislike status again to ensure consistency
|
||||
let check_like_dislike = move || {
|
||||
spawn_local(async move {
|
||||
match get_like_dislike_song(song_id.get_untracked()).await {
|
||||
Ok((like, dislike)) => {
|
||||
liked.set(like);
|
||||
disliked.set(dislike);
|
||||
},
|
||||
Err(_) => {}
|
||||
}
|
||||
if let Ok((like, dislike)) = get_like_dislike_song(song_id.get_untracked()).await {
|
||||
liked.set(like);
|
||||
disliked.set(dislike);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -60,7 +60,7 @@ pub fn Upload(open: RwSignal<bool>) -> impl IntoView {
|
||||
|
||||
set_filtered_artists.update(|value| *value = artists);
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
// Create a filter function to handle filtering albums
|
||||
// Allow users to search for albums by title, converts the album title to album id to be handed off to backend
|
||||
@ -80,7 +80,7 @@ pub fn Upload(open: RwSignal<bool>) -> impl IntoView {
|
||||
log!("Filtered albums: {:?}", albums);
|
||||
set_filtered_albums.update(|value| *value = albums);
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
let handle_response = Arc::new(move |response: &Response| {
|
||||
@ -116,12 +116,12 @@ pub fn Upload(open: RwSignal<bool>) -> impl IntoView {
|
||||
<span>Artists</span>
|
||||
</div>
|
||||
<Show
|
||||
when=move || {filtered_artists.get().len() > 0}
|
||||
when=move || {!filtered_artists.get().is_empty()}
|
||||
fallback=move || view! {}
|
||||
>
|
||||
<ul class="artist_results search-results">
|
||||
{
|
||||
move || filtered_artists.get().iter().enumerate().map(|(_index,filtered_artist)| view! {
|
||||
move || filtered_artists.get().iter().map(|filtered_artist| view! {
|
||||
<Artist artist=filtered_artist.clone() artists=artists set_artists=set_artists set_filtered=set_filtered_artists/>
|
||||
}).collect::<Vec<_>>()
|
||||
}
|
||||
@ -134,12 +134,12 @@ pub fn Upload(open: RwSignal<bool>) -> impl IntoView {
|
||||
<span>Album ID</span>
|
||||
</div>
|
||||
<Show
|
||||
when=move || {filtered_albums.get().len() > 0}
|
||||
when=move || {!filtered_albums.get().is_empty()}
|
||||
fallback=move || view! {}
|
||||
>
|
||||
<ul class="album_results search-results">
|
||||
{
|
||||
move || filtered_albums.get().iter().enumerate().map(|(_index,filtered_album)| view! {
|
||||
move || filtered_albums.get().iter().map(|filtered_album| view! {
|
||||
<Album album=filtered_album.clone() _albums=albums set_albums=set_albums set_filtered=set_filtered_albums/>
|
||||
}).collect::<Vec<_>>()
|
||||
}
|
||||
@ -190,12 +190,12 @@ pub fn Artist(artist: Artist, artists: ReadSignal<String>, set_artists: WriteSig
|
||||
let mut ids: Vec<&str> = all_artirts.split(",").collect();
|
||||
//If there is only one artist in the input, get their id equivalent and add it to the string
|
||||
if ids.len() == 1 {
|
||||
let value_str = match artist.id.clone() {
|
||||
let value_str = match artist.id {
|
||||
Some(v) => v.to_string(),
|
||||
None => String::from("None"),
|
||||
};
|
||||
s.push_str(&value_str);
|
||||
s.push_str(",");
|
||||
s.push(',');
|
||||
set_artists.update(|value| *value = s);
|
||||
//If there are multiple artists in the input, pop the last artist by string off the vector,
|
||||
//get their id equivalent, and add it to the string
|
||||
@ -203,14 +203,14 @@ pub fn Artist(artist: Artist, artists: ReadSignal<String>, set_artists: WriteSig
|
||||
ids.pop();
|
||||
for id in ids {
|
||||
s.push_str(id);
|
||||
s.push_str(",");
|
||||
s.push(',');
|
||||
}
|
||||
let value_str = match artist.id.clone() {
|
||||
let value_str = match artist.id {
|
||||
Some(v) => v.to_string(),
|
||||
None => String::from("None"),
|
||||
};
|
||||
s.push_str(&value_str);
|
||||
s.push_str(",");
|
||||
s.push(',');
|
||||
set_artists.update(|value| *value = s);
|
||||
}
|
||||
//Clear the search results
|
||||
@ -227,7 +227,7 @@ pub fn Artist(artist: Artist, artists: ReadSignal<String>, set_artists: WriteSig
|
||||
pub fn Album(album: Album, _albums: ReadSignal<String>, set_albums: WriteSignal<String>, set_filtered: WriteSignal<Vec<Album>>) -> impl IntoView {
|
||||
//Converts album title to album id to upload a song
|
||||
let add_album = move |_| {
|
||||
let value_str = match album.id.clone() {
|
||||
let value_str = match album.id {
|
||||
Some(v) => v.to_string(),
|
||||
None => String::from("None"),
|
||||
};
|
||||
|
@ -12,7 +12,7 @@ pub enum AppError {
|
||||
}
|
||||
|
||||
impl AppError {
|
||||
pub fn status_code(&self) -> StatusCode {
|
||||
pub const fn status_code(&self) -> StatusCode {
|
||||
match self {
|
||||
AppError::NotFound => StatusCode::NOT_FOUND,
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ async fn main() {
|
||||
debug!("Connecting to Redis...");
|
||||
|
||||
let redis_url = std::env::var("REDIS_URL").expect("REDIS_URL must be set");
|
||||
let redis_config = RedisConfig::from_url(&redis_url).expect(&format!("Unable to parse Redis URL: {}", redis_url));
|
||||
let redis_config = RedisConfig::from_url(&redis_url).unwrap_or_else(|_| panic!("Unable to parse Redis URL: {}", redis_url));
|
||||
let redis_pool = RedisPool::new(redis_config, None, None, None, 1).expect("Unable to create Redis pool");
|
||||
redis_pool.connect();
|
||||
redis_pool.wait_for_connect().await.expect("Unable to connect to Redis");
|
||||
@ -72,7 +72,7 @@ async fn main() {
|
||||
.fallback(file_and_error_handler)
|
||||
.with_state(leptos_options);
|
||||
|
||||
let listener = tokio::net::TcpListener::bind(&addr).await.expect(&format!("Could not bind to {}", &addr));
|
||||
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap_or_else(|_| panic!("Could not bind to {}", &addr));
|
||||
|
||||
info!("Listening on http://{}", &addr);
|
||||
|
||||
|
@ -153,8 +153,8 @@ impl Album {
|
||||
duration: song.duration,
|
||||
release_date: song.release_date,
|
||||
song_path: song.storage_path,
|
||||
image_path: image_path,
|
||||
like_dislike: like_dislike,
|
||||
image_path,
|
||||
like_dislike,
|
||||
added_date: song.added_date.unwrap(),
|
||||
};
|
||||
|
||||
|
@ -26,7 +26,7 @@ impl Artist {
|
||||
///
|
||||
/// For one artist, displays [artist1]. For two artists, displays [artist1] & [artist2].
|
||||
/// For three or more artists, displays [artist1], [artist2], & [artist3].
|
||||
pub fn display_list(artists: &Vec<Artist>) -> String {
|
||||
pub fn display_list(artists: &[Artist]) -> String {
|
||||
let mut artist_list = String::new();
|
||||
|
||||
for (i, artist) in artists.iter().enumerate() {
|
||||
|
@ -14,8 +14,8 @@ cfg_if! {
|
||||
|
||||
// Model for a "User", used for querying the database
|
||||
/// Various fields are wrapped in Options, because they are not always wanted for inserts/retrieval
|
||||
/// Using deserialize_as makes Diesel use the specified type when deserializing from the database,
|
||||
/// and then call .into() to convert it into the Option
|
||||
/// Using `deserialize_as` makes Diesel use the specified type when deserializing from the database,
|
||||
/// and then call `.into()` to convert it into the Option
|
||||
#[cfg_attr(feature = "ssr", derive(Queryable, Selectable, Insertable))]
|
||||
#[cfg_attr(feature = "ssr", diesel(table_name = crate::schema::users))]
|
||||
#[cfg_attr(feature = "ssr", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
@ -55,10 +55,10 @@ impl User {
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Result<Vec<HistoryEntry>, Box<dyn Error>>` -
|
||||
/// A result indicating success with a vector of history entries, or an error
|
||||
/// A result indicating success with a vector of history entries, or an error
|
||||
///
|
||||
#[cfg(feature = "ssr")]
|
||||
pub fn get_history(self: &Self, limit: Option<i64>, conn: &mut PgPooledConn) ->
|
||||
pub fn get_history(&self, limit: Option<i64>, conn: &mut PgPooledConn) ->
|
||||
Result<Vec<HistoryEntry>, Box<dyn Error>> {
|
||||
use crate::schema::song_history::dsl::*;
|
||||
|
||||
@ -94,10 +94,10 @@ impl User {
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Result<Vec<(SystemTime, Song)>, Box<dyn Error>>` -
|
||||
/// A result indicating success with a vector of listen dates and songs, or an error
|
||||
/// A result indicating success with a vector of listen dates and songs, or an error
|
||||
///
|
||||
#[cfg(feature = "ssr")]
|
||||
pub fn get_history_songs(self: &Self, limit: Option<i64>, conn: &mut PgPooledConn) ->
|
||||
pub fn get_history_songs(&self, limit: Option<i64>, conn: &mut PgPooledConn) ->
|
||||
Result<Vec<(NaiveDateTime, Song)>, Box<dyn Error>> {
|
||||
use crate::schema::songs::dsl::*;
|
||||
use crate::schema::song_history::dsl::*;
|
||||
@ -140,7 +140,7 @@ impl User {
|
||||
/// * `Result<(), Box<dyn Error>>` - A result indicating success with an empty value, or an error
|
||||
///
|
||||
#[cfg(feature = "ssr")]
|
||||
pub fn add_history(self: &Self, song_id: i32, conn: &mut PgPooledConn) -> Result<(), Box<dyn Error>> {
|
||||
pub fn add_history(&self, song_id: i32, conn: &mut PgPooledConn) -> Result<(), Box<dyn Error>> {
|
||||
use crate::schema::song_history;
|
||||
|
||||
let my_id = self.id.ok_or("Artist id must be present (Some) to add history")?;
|
||||
@ -155,7 +155,7 @@ impl User {
|
||||
/// Like or unlike a song for this user
|
||||
/// If likeing a song, remove dislike if it exists
|
||||
#[cfg(feature = "ssr")]
|
||||
pub async fn set_like_song(self: &Self, song_id: i32, like: bool, conn: &mut PgPooledConn) ->
|
||||
pub async fn set_like_song(&self, song_id: i32, like: bool, conn: &mut PgPooledConn) ->
|
||||
Result<(), Box<dyn Error>> {
|
||||
use log::*;
|
||||
debug!("Setting like for song {} to {}", song_id, like);
|
||||
@ -184,7 +184,7 @@ impl User {
|
||||
|
||||
/// Get the like status of a song for this user
|
||||
#[cfg(feature = "ssr")]
|
||||
pub async fn get_like_song(self: &Self, song_id: i32, conn: &mut PgPooledConn) -> Result<bool, Box<dyn Error>> {
|
||||
pub async fn get_like_song(&self, song_id: i32, conn: &mut PgPooledConn) -> Result<bool, Box<dyn Error>> {
|
||||
use crate::schema::song_likes;
|
||||
|
||||
let my_id = self.id.ok_or("User id must be present (Some) to get like status of a song")?;
|
||||
@ -201,7 +201,7 @@ impl User {
|
||||
/// Dislike or remove dislike from a song for this user
|
||||
/// If disliking a song, remove like if it exists
|
||||
#[cfg(feature = "ssr")]
|
||||
pub async fn set_dislike_song(self: &Self, song_id: i32, dislike: bool, conn: &mut PgPooledConn) ->
|
||||
pub async fn set_dislike_song(&self, song_id: i32, dislike: bool, conn: &mut PgPooledConn) ->
|
||||
Result<(), Box<dyn Error>> {
|
||||
use log::*;
|
||||
debug!("Setting dislike for song {} to {}", song_id, dislike);
|
||||
@ -231,7 +231,7 @@ impl User {
|
||||
|
||||
/// Get the dislike status of a song for this user
|
||||
#[cfg(feature = "ssr")]
|
||||
pub async fn get_dislike_song(self: &Self, song_id: i32, conn: &mut PgPooledConn) -> Result<bool, Box<dyn Error>> {
|
||||
pub async fn get_dislike_song(&self, song_id: i32, conn: &mut PgPooledConn) -> Result<bool, Box<dyn Error>> {
|
||||
use crate::schema::song_dislikes;
|
||||
|
||||
let my_id = self.id.ok_or("User id must be present (Some) to get dislike status of a song")?;
|
||||
|
@ -23,13 +23,13 @@ pub struct Album {
|
||||
pub image_path: String,
|
||||
}
|
||||
|
||||
impl Into<DashboardTile> for Album {
|
||||
fn into(self) -> DashboardTile {
|
||||
impl From<Album> for DashboardTile {
|
||||
fn from(val: Album) -> Self {
|
||||
DashboardTile {
|
||||
image_path: self.image_path.into(),
|
||||
title: self.title.into(),
|
||||
link: format!("/album/{}", self.id).into(),
|
||||
description: Some(format!("Album • {}", Artist::display_list(&self.artists)).into()),
|
||||
image_path: val.image_path.into(),
|
||||
title: val.title.into(),
|
||||
link: format!("/album/{}", val.id).into(),
|
||||
description: Some(format!("Album • {}", Artist::display_list(&val.artists)).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,12 +15,12 @@ pub struct Artist {
|
||||
pub image_path: String,
|
||||
}
|
||||
|
||||
impl Into<DashboardTile> for Artist {
|
||||
fn into(self) -> DashboardTile {
|
||||
impl From<Artist> for DashboardTile {
|
||||
fn from(val: Artist) -> Self {
|
||||
DashboardTile {
|
||||
image_path: self.image_path.into(),
|
||||
title: self.name.into(),
|
||||
link: format!("/artist/{}", self.id).into(),
|
||||
image_path: val.image_path.into(),
|
||||
title: val.name.into(),
|
||||
link: format!("/artist/{}", val.id).into(),
|
||||
description: Some("Artist".into()),
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ use std::collections::VecDeque;
|
||||
|
||||
use crate::models::frontend;
|
||||
|
||||
/// Represents the global state of the audio player feature of LibreTunes
|
||||
/// Represents the global state of the audio player feature of `LibreTunes`
|
||||
pub struct PlayStatus {
|
||||
/// Whether or not the audio player is currently playing
|
||||
pub playing: bool,
|
||||
@ -27,9 +27,9 @@ impl PlayStatus {
|
||||
/// use leptos::prelude::*;
|
||||
/// let status = libretunes::models::frontend::PlayStatus::default();
|
||||
/// if let Some(audio) = status.audio_player {
|
||||
/// if let Some(audio) = audio.get() {
|
||||
/// let _ = audio.play();
|
||||
/// }
|
||||
/// if let Some(audio) = audio.get() {
|
||||
/// let _ = audio.play();
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
@ -37,7 +37,7 @@ impl PlayStatus {
|
||||
/// ```
|
||||
/// let status = libretunes::models::frontend::PlayStatus::default();
|
||||
/// if let Some(audio) = status.get_audio() {
|
||||
/// let _ = audio.play();
|
||||
/// let _ = audio.play();
|
||||
/// }
|
||||
/// ```
|
||||
pub fn get_audio(&self) -> Option<HtmlAudioElement> {
|
||||
@ -52,7 +52,7 @@ impl PlayStatus {
|
||||
}
|
||||
|
||||
impl Default for PlayStatus {
|
||||
/// Creates a paused PlayStatus with no audio player, no progress update handle, and empty queue/history
|
||||
/// Creates a paused `PlayStatus` with no audio player, no progress update handle, and empty queue/history
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
playing: false,
|
||||
|
@ -39,10 +39,10 @@ pub struct Song {
|
||||
impl TryInto<backend::Song> for Song {
|
||||
type Error = Box<dyn std::error::Error>;
|
||||
|
||||
/// Convert a SongData object into a Song object
|
||||
/// Convert a `SongData` object into a Song object
|
||||
///
|
||||
/// The SongData/Song conversions are also not truly reversible,
|
||||
/// due to the way the image_path data is handled.
|
||||
/// due to the way the `image_path` data is handled.
|
||||
fn try_into(self) -> Result<backend::Song, Self::Error> {
|
||||
Ok(backend::Song {
|
||||
id: Some(self.id),
|
||||
@ -67,13 +67,13 @@ impl TryInto<backend::Song> for Song {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<DashboardTile> for Song {
|
||||
fn into(self) -> DashboardTile {
|
||||
impl From<Song> for DashboardTile {
|
||||
fn from(val: Song) -> Self {
|
||||
DashboardTile {
|
||||
image_path: self.image_path.into(),
|
||||
title: self.title.into(),
|
||||
link: format!("/song/{}", self.id).into(),
|
||||
description: Some(format!("Song • {}", Artist::display_list(&self.artists)).into()),
|
||||
image_path: val.image_path.into(),
|
||||
title: val.title.into(),
|
||||
link: format!("/song/{}", val.id).into(),
|
||||
description: Some(format!("Song • {}", Artist::display_list(&val.artists)).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ pub fn AlbumPage() -> impl IntoView {
|
||||
|value| async move {
|
||||
match value {
|
||||
Ok(v) => {get_songs(v).await},
|
||||
Err(e) => {Err(ServerFnError::Request(format!("Error getting song data: {}", e).into()))},
|
||||
Err(e) => {Err(ServerFnError::Request(format!("Error getting song data: {}", e)))},
|
||||
}
|
||||
},
|
||||
);
|
||||
@ -38,7 +38,7 @@ pub fn AlbumPage() -> impl IntoView {
|
||||
|value| async move {
|
||||
match value {
|
||||
Ok(v) => {get_album(v).await},
|
||||
Err(e) => {Err(ServerFnError::Request(format!("Error getting song data: {}", e).into()))},
|
||||
Err(e) => {Err(ServerFnError::Request(format!("Error getting song data: {}", e)))},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
@ -152,9 +152,7 @@ fn AlbumsByArtist(#[prop(into)] artist_id: Signal<i32>) -> impl IntoView {
|
||||
let albums = albums_by_artist(artist_id, None).await;
|
||||
|
||||
albums.map(|albums| {
|
||||
albums.into_iter().map(|album| {
|
||||
album
|
||||
}).collect::<Vec<_>>()
|
||||
albums.into_iter().collect::<Vec<_>>()
|
||||
})
|
||||
});
|
||||
|
||||
|
@ -96,7 +96,7 @@ pub fn Login() -> impl IntoView {
|
||||
<span>Password</span>
|
||||
<i></i>
|
||||
<Show
|
||||
when=move || {show_password() == false}
|
||||
when=move || {!show_password()}
|
||||
fallback=move || view!{ <button on:click=toggle_password class="login-password-visibility">
|
||||
<Icon icon={icondata::AiEyeInvisibleFilled} />
|
||||
</button> /> }
|
||||
|
@ -56,7 +56,7 @@ pub fn Signup() -> impl IntoView {
|
||||
// Redirect to the login page
|
||||
log!("Signed up successfully!");
|
||||
leptos_router::hooks::use_navigate()("/", Default::default());
|
||||
log!("Navigated to home page after signup")
|
||||
log!("Navigated to home page after signup");
|
||||
}
|
||||
|
||||
loading.set(false);
|
||||
@ -102,7 +102,7 @@ pub fn Signup() -> impl IntoView {
|
||||
<span>Password</span>
|
||||
<i></i>
|
||||
<Show
|
||||
when=move || {show_password() == false}
|
||||
when=move || {!show_password()}
|
||||
fallback=move || view!{ <button on:click=toggle_password class="password-visibility"> <Icon icon={icondata::AiEyeInvisibleFilled} /></button> /> }
|
||||
>
|
||||
<button on:click=toggle_password class="password-visibility">
|
||||
|
@ -146,7 +146,7 @@ fn SongOverview(song: frontend::Song) -> impl IntoView {
|
||||
|
||||
#[component]
|
||||
fn SongPlays(#[prop(into)] id: Signal<i32>) -> impl IntoView {
|
||||
let plays = Resource::new(move || id.get(), move |id| songs::get_song_plays(id));
|
||||
let plays = Resource::new(move || id.get(), songs::get_song_plays);
|
||||
|
||||
view! {
|
||||
<Transition
|
||||
@ -175,7 +175,7 @@ fn SongPlays(#[prop(into)] id: Signal<i32>) -> impl IntoView {
|
||||
|
||||
#[component]
|
||||
fn MySongPlays(#[prop(into)] id: Signal<i32>) -> impl IntoView {
|
||||
let plays = Resource::new(move || id.get(), move |id| songs::get_my_song_plays(id));
|
||||
let plays = Resource::new(move || id.get(), songs::get_my_song_plays);
|
||||
|
||||
view! {
|
||||
<Transition
|
||||
|
@ -28,10 +28,10 @@ lazy_static! {
|
||||
|
||||
/// Initialize the database pool
|
||||
///
|
||||
/// Uses DATABASE_URL environment variable to connect to the database if set,
|
||||
/// Uses `DATABASE_URL` environment variable to connect to the database if set,
|
||||
/// otherwise builds a connection string from other environment variables.
|
||||
///
|
||||
/// Will panic if either the DATABASE_URL or POSTGRES_HOST environment variables
|
||||
/// Will panic if either the `DATABASE_URL` or `POSTGRES_HOST` environment variables
|
||||
/// are not set, or if there is an error creating the pool.
|
||||
///
|
||||
/// # Returns
|
||||
@ -48,14 +48,14 @@ fn init_db_pool() -> PgPool {
|
||||
log_url.push_str(&user);
|
||||
|
||||
if let Ok(password) = env::var("POSTGRES_PASSWORD") {
|
||||
url.push_str(":");
|
||||
log_url.push_str(":");
|
||||
url.push(':');
|
||||
log_url.push(':');
|
||||
url.push_str(&password);
|
||||
log_url.push_str("********");
|
||||
}
|
||||
|
||||
url.push_str("@");
|
||||
log_url.push_str("@");
|
||||
url.push('@');
|
||||
log_url.push('@');
|
||||
}
|
||||
|
||||
let host = env::var("POSTGRES_HOST").expect("DATABASE_URL or POSTGRES_HOST must be set");
|
||||
@ -64,16 +64,16 @@ fn init_db_pool() -> PgPool {
|
||||
log_url.push_str(&host);
|
||||
|
||||
if let Ok(port) = env::var("POSTGRES_PORT") {
|
||||
url.push_str(":");
|
||||
url.push(':');
|
||||
url.push_str(&port);
|
||||
log_url.push_str(":");
|
||||
log_url.push(':');
|
||||
log_url.push_str(&port);
|
||||
}
|
||||
|
||||
if let Ok(dbname) = env::var("POSTGRES_DB") {
|
||||
url.push_str("/");
|
||||
url.push('/');
|
||||
url.push_str(&dbname);
|
||||
log_url.push_str("/");
|
||||
log_url.push('/');
|
||||
log_url.push_str(&dbname);
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,7 @@ pub async fn get_static_file(uri: Uri, root: &str) -> Result<Response<Body>, (St
|
||||
Some(res) => Ok(res.into_response()),
|
||||
None => Err((
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Something went wrong"),
|
||||
"Something went wrong".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
@ -59,7 +59,7 @@ pub async fn get_asset_file(filename: String, asset_type: AssetType) -> Result<R
|
||||
Ok(uri) => get_static_file(uri, root.as_str()).await,
|
||||
Err(_) => Err((
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Attempted to serve an invalid file"),
|
||||
"Attempted to serve an invalid file".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ use axum::extract::FromRequestParts;
|
||||
const ALLOWED_PATHS: [&str; 5] = ["/login", "/signup", "/api/login", "/api/signup", "/favicon.ico"];
|
||||
|
||||
/**
|
||||
* Middleware to require authentication for all paths except those in ALLOWED_PATHS
|
||||
* Middleware to require authentication for all paths except those in `ALLOWED_PATHS`
|
||||
*
|
||||
* If a user is not authenticated, they will be redirected to the login page
|
||||
*/
|
||||
|
@ -7,7 +7,7 @@ use crate::api::auth::get_logged_in_user;
|
||||
|
||||
/// Global front-end state
|
||||
/// Contains anything frequently needed across multiple components
|
||||
/// Behaves like a singleton, in that provide/expect_context will
|
||||
/// Behaves like a singleton, in that `provide_context`/`expect_context` will
|
||||
/// always return the same instance
|
||||
#[derive(Clone)]
|
||||
pub struct GlobalState {
|
||||
@ -47,3 +47,10 @@ impl GlobalState {
|
||||
expect_context::<Self>().play_status
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for GlobalState {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user