add song and add album working now
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -20,3 +20,4 @@ target/
|
|||||||
*.sqlite-*
|
*.sqlite-*
|
||||||
.env
|
.env
|
||||||
.env.production
|
.env.production
|
||||||
|
uploads/
|
||||||
|
|||||||
@@ -40,6 +40,27 @@
|
|||||||
<textarea name="description" rows="6" class="textarea textarea-bordered w-full"></textarea>
|
<textarea name="description" rows="6" class="textarea textarea-bordered w-full"></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label"><span class="label-text">Songs</span></label>
|
||||||
|
{% if available_tracks | length > 0 %}
|
||||||
|
<div class="divide-y divide-base-300 rounded border border-base-300">
|
||||||
|
{% for song in available_tracks %}
|
||||||
|
<label class="flex cursor-pointer items-center gap-3 p-2 hover:bg-base-200">
|
||||||
|
<input type="checkbox" name="track_ids" value="{{ song.id }}" class="checkbox checkbox-sm">
|
||||||
|
<span class="min-w-0 flex-1">
|
||||||
|
<span class="block truncate font-medium">{{ song.title }}</span>
|
||||||
|
<span class="block truncate text-xs opacity-70">{{ song.audio_file_id }}</span>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="rounded border border-base-300 p-2 text-sm opacity-70">
|
||||||
|
No ungrouped songs available.
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
<label class="label cursor-pointer justify-start gap-2">
|
<label class="label cursor-pointer justify-start gap-2">
|
||||||
<input type="checkbox" name="published" class="checkbox checkbox-sm">
|
<input type="checkbox" name="published" class="checkbox checkbox-sm">
|
||||||
<span class="label-text">Published</span>
|
<span class="label-text">Published</span>
|
||||||
|
|||||||
@@ -17,6 +17,20 @@
|
|||||||
|
|
||||||
<div class="card border border-base-300 bg-base-100 shadow-sm">
|
<div class="card border border-base-300 bg-base-100 shadow-sm">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
{% if available_tracks | length > 0 %}
|
||||||
|
<form method="post" action="/admin/audio/albums/{{ album.id }}/tracks/add" class="mb-4 flex flex-wrap items-end gap-2">
|
||||||
|
<div class="form-control flex-1">
|
||||||
|
<label class="label"><span class="label-text">Add existing song</span></label>
|
||||||
|
<select name="track_id" required class="select select-bordered w-full">
|
||||||
|
{% for song in available_tracks %}
|
||||||
|
<option value="{{ song.id }}">{{ song.title }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-neutral btn-sm">Add to album</button>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if tracks | length > 0 %}
|
{% if tracks | length > 0 %}
|
||||||
<div class="overflow-x-auto">
|
<div class="overflow-x-auto">
|
||||||
<table class="table">
|
<table class="table">
|
||||||
@@ -62,6 +76,9 @@
|
|||||||
<button type="submit" class="btn btn-ghost btn-sm">Publish</button>
|
<button type="submit" class="btn btn-ghost btn-sm">Publish</button>
|
||||||
</form>
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<form method="post" action="/admin/audio/tracks/{{ track.id }}/remove-from-album">
|
||||||
|
<button type="submit" class="btn btn-ghost btn-sm">Remove</button>
|
||||||
|
</form>
|
||||||
<form method="post" action="/admin/audio/tracks/{{ track.id }}/delete">
|
<form method="post" action="/admin/audio/tracks/{{ track.id }}/delete">
|
||||||
<button type="submit" class="btn btn-ghost btn-sm">Delete</button>
|
<button type="submit" class="btn btn-ghost btn-sm">Delete</button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -44,6 +44,13 @@ struct AlbumForm {
|
|||||||
artist: Option<String>,
|
artist: Option<String>,
|
||||||
release_date: Option<String>,
|
release_date: Option<String>,
|
||||||
published: Option<String>,
|
published: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
track_ids: Vec<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct AlbumSongForm {
|
||||||
|
track_id: Uuid,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
@@ -538,7 +545,12 @@ async fn admin_tracks(
|
|||||||
#[debug_handler]
|
#[debug_handler]
|
||||||
async fn admin_album_new(auth: auth::JWT, ViewEngine(v): ViewEngine<TeraView>, State(ctx): State<AppContext>) -> Result<Response> {
|
async fn admin_album_new(auth: auth::JWT, ViewEngine(v): ViewEngine<TeraView>, State(ctx): State<AppContext>) -> Result<Response> {
|
||||||
admin::current_admin(auth, &ctx).await?;
|
admin::current_admin(auth, &ctx).await?;
|
||||||
format::view(&v, "admin/audio/new_album.html", json!({}))
|
let available_tracks = audio_tracks::Entity::find()
|
||||||
|
.filter(audio_tracks::Column::AlbumId.is_null())
|
||||||
|
.order_by_asc(audio_tracks::Column::Title)
|
||||||
|
.all(&ctx.db)
|
||||||
|
.await?;
|
||||||
|
format::view(&v, "admin/audio/new_album.html", json!({ "available_tracks": available_tracks }))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[debug_handler]
|
#[debug_handler]
|
||||||
@@ -552,7 +564,7 @@ async fn admin_album_create(
|
|||||||
let release_date = normalize_empty(params.release_date)
|
let release_date = normalize_empty(params.release_date)
|
||||||
.and_then(|date| NaiveDate::parse_from_str(&date, "%Y-%m-%d").ok());
|
.and_then(|date| NaiveDate::parse_from_str(&date, "%Y-%m-%d").ok());
|
||||||
|
|
||||||
audio_albums::ActiveModel {
|
let album = audio_albums::ActiveModel {
|
||||||
id: Set(Uuid::new_v4()),
|
id: Set(Uuid::new_v4()),
|
||||||
title: Set(params.title.clone()),
|
title: Set(params.title.clone()),
|
||||||
slug: Set(unique_album_slug(&ctx, ¶ms.title).await?),
|
slug: Set(unique_album_slug(&ctx, ¶ms.title).await?),
|
||||||
@@ -569,6 +581,17 @@ async fn admin_album_create(
|
|||||||
.insert(&ctx.db)
|
.insert(&ctx.db)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
for track_id in params.track_ids {
|
||||||
|
let track = track_by_id(&ctx, track_id).await?;
|
||||||
|
if track.album_id.is_some() {
|
||||||
|
return Err(Error::BadRequest("selected song already belongs to an album".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut active = track.into_active_model();
|
||||||
|
active.album_id = Set(Some(album.id));
|
||||||
|
active.update(&ctx.db).await?;
|
||||||
|
}
|
||||||
|
|
||||||
format::redirect("/admin/audio/albums")
|
format::redirect("/admin/audio/albums")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -587,14 +610,62 @@ async fn admin_album_tracks(
|
|||||||
.order_by_asc(audio_tracks::Column::Title)
|
.order_by_asc(audio_tracks::Column::Title)
|
||||||
.all(&ctx.db)
|
.all(&ctx.db)
|
||||||
.await?;
|
.await?;
|
||||||
|
let available_tracks = audio_tracks::Entity::find()
|
||||||
|
.filter(audio_tracks::Column::AlbumId.is_null())
|
||||||
|
.order_by_asc(audio_tracks::Column::Title)
|
||||||
|
.all(&ctx.db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
format::view(
|
format::view(
|
||||||
&v,
|
&v,
|
||||||
"admin/audio/tracks.html",
|
"admin/audio/tracks.html",
|
||||||
json!({ "album": album, "tracks": tracks }),
|
json!({ "album": album, "tracks": tracks, "available_tracks": available_tracks }),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
async fn admin_album_add_track(
|
||||||
|
auth: auth::JWT,
|
||||||
|
Path(album_id): Path<Uuid>,
|
||||||
|
State(ctx): State<AppContext>,
|
||||||
|
Form(params): Form<AlbumSongForm>,
|
||||||
|
) -> Result<Response> {
|
||||||
|
admin::current_admin(auth, &ctx).await?;
|
||||||
|
album_by_id(&ctx, album_id).await?;
|
||||||
|
let track = track_by_id(&ctx, params.track_id).await?;
|
||||||
|
|
||||||
|
if track.album_id.is_some() {
|
||||||
|
return Err(Error::BadRequest("song already belongs to an album".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut active = track.into_active_model();
|
||||||
|
active.album_id = Set(Some(album_id));
|
||||||
|
active.update(&ctx.db).await?;
|
||||||
|
|
||||||
|
format::redirect(&format!("/admin/audio/albums/{album_id}/tracks"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
async fn admin_track_remove_from_album(
|
||||||
|
auth: auth::JWT,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
State(ctx): State<AppContext>,
|
||||||
|
) -> Result<Response> {
|
||||||
|
admin::current_admin(auth, &ctx).await?;
|
||||||
|
let track = track_by_id(&ctx, id).await?;
|
||||||
|
let album_id = track.album_id;
|
||||||
|
let mut active = track.into_active_model();
|
||||||
|
active.album_id = Set(None);
|
||||||
|
active.track_number = Set(None);
|
||||||
|
active.update(&ctx.db).await?;
|
||||||
|
|
||||||
|
if let Some(album_id) = album_id {
|
||||||
|
format::redirect(&format!("/admin/audio/albums/{album_id}/tracks"))
|
||||||
|
} else {
|
||||||
|
format::redirect("/admin/audio/tracks")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[debug_handler]
|
#[debug_handler]
|
||||||
async fn admin_track_upload_form(
|
async fn admin_track_upload_form(
|
||||||
auth: auth::JWT,
|
auth: auth::JWT,
|
||||||
@@ -856,9 +927,11 @@ pub fn routes() -> Routes {
|
|||||||
.add("/admin/audio/tracks/upload", get(admin_song_upload_form))
|
.add("/admin/audio/tracks/upload", get(admin_song_upload_form))
|
||||||
.add("/admin/audio/tracks/upload-file", post(admin_song_upload).layer(DefaultBodyLimit::max(AUDIO_MAX_BYTES + 1024 * 1024)))
|
.add("/admin/audio/tracks/upload-file", post(admin_song_upload).layer(DefaultBodyLimit::max(AUDIO_MAX_BYTES + 1024 * 1024)))
|
||||||
.add("/admin/audio/albums/{album_id}/tracks", get(admin_album_tracks))
|
.add("/admin/audio/albums/{album_id}/tracks", get(admin_album_tracks))
|
||||||
|
.add("/admin/audio/albums/{album_id}/tracks/add", post(admin_album_add_track))
|
||||||
.add("/admin/audio/albums/{album_id}/tracks/upload", get(admin_track_upload_form))
|
.add("/admin/audio/albums/{album_id}/tracks/upload", get(admin_track_upload_form))
|
||||||
.add("/admin/audio/albums/{album_id}/tracks/upload-file", post(admin_track_upload).layer(DefaultBodyLimit::max(AUDIO_MAX_BYTES + 1024 * 1024)))
|
.add("/admin/audio/albums/{album_id}/tracks/upload-file", post(admin_track_upload).layer(DefaultBodyLimit::max(AUDIO_MAX_BYTES + 1024 * 1024)))
|
||||||
.add("/admin/audio/tracks/{id}/publish", post(admin_track_publish))
|
.add("/admin/audio/tracks/{id}/publish", post(admin_track_publish))
|
||||||
.add("/admin/audio/tracks/{id}/unpublish", post(admin_track_unpublish))
|
.add("/admin/audio/tracks/{id}/unpublish", post(admin_track_unpublish))
|
||||||
|
.add("/admin/audio/tracks/{id}/remove-from-album", post(admin_track_remove_from_album))
|
||||||
.add("/admin/audio/tracks/{id}/delete", post(admin_track_delete))
|
.add("/admin/audio/tracks/{id}/delete", post(admin_track_delete))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user