+from dataclasses import dataclass
import os
import shutil
import sqlite3
songs_tags=tags,
songs_collaborators=collabs)
-@app.post("/uploadsong")
-def upload_song():
- if not "username" in session:
- abort(401)
+@app.get("/edit-song/<int:songid>")
+def edit_song(songid=None):
+ if not "userid" in session:
+ return redirect("/login") # Must be logged in to edit
+
+ if songid:
+ try:
+ song = Song.from_db(songid)
+ except ValueError:
+ abort(404)
+
+ return render_template("edit-song.html", song=song)
+
+
+@app.post("/upload-song/<int:songid>")
+def upload_song(songid=None):
+ if not "userid" in session:
+ return redirect("/login") # Must be logged in to edit
+
+ error = validate_song_form()
- username = session["username"]
- userid = session["userid"]
+ if not error:
+ userid = session["userid"]
+ if songid:
+ error = update_song(file, userid, title, description, tags, collaborators, songid)
+ else:
+ error = create_song(file, userid, title, description, tags, collaborators)
+
+ if not error:
+ username = session["username"]
+ return redirect("/users/{username}")
+
+ else:
+ return redirect(request.referrer)
+def validate_song_form():
file = request.files["song"]
title = request.form["title"]
description = request.form["description"]
flash(f"'{collab}' is not a valid collaborator name", "error")
error = True
- if not error:
- if "songid" in request.args:
- # Update existing song
- update_song(file, userid, title, description, tags, collaborators)
- else:
- # Uploading new song
- create_song(file, userid, title, description, tags, collaborators)
-
- return redirect(request.referrer)
+ return error
def get_user_path(userid):
userpath = DATA_DIR / "songs" / str(userid)
except ValueError:
abort(400)
+ # Make sure song exists and the logged-in user owns it
+ song_data = query_db("select userid from songs where songid = ?", [songid], one=True)
+ if song_data is None:
+ abort(400)
+ elif userid != song_data["userid"]:
+ abort(401)
+
if file:
with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
file.save(tmp_file)
import secrets
print(secrets.token_hex())
+@dataclass
+class Song:
+ id: int
+ title: str
+ description: str
+ tags: list[str]
+ collaborators: list[str]
+
+ @classmethod
+ def from_db(cls, songid):
+ song_data = query_db("select * from songs where songid = ?", [songid], one=True)
+ if song_data is None:
+ raise ValueError(f"No song for ID {songid:d}")
+
+ tags_data = query_db("select * from song_tags where songid = ?", [songid])
+ collaborators_data = query_db("select * from song_collaborators where songid = ?", [song])
+
+ tags = [t["tag"] for t in tags_data]
+ collabs = [c["name"] for c in collaborators_data]
+
+ return cls(song_data["songid"], song_data["title"], song_data["description"], tags, collabs)
+
--- /dev/null
+{% extends "base.html" %}
+
+{% block title %}{% if song %}Edit Song{% else %}Upload Song{% endif %}{% endblock %}
+
+{% block body %}
+
+<form action="/uploadsong" method="post" enctype="multipart/form-data">
+ <h2>Upload a new song</h2>
+ <div class="upload-form">
+ <label for="song">{% if editing %}Replace {% endif %}mp3 File</label>
+ <input type="file" name="song" accept=".mp3" id="file-select" required></input>
+ </div>
+ <div class="upload-form">
+ <label for="title">Title</label>
+ <input type="text" name="title" id="song-title" value="{{ title }}" required></input>
+ </div>
+ <div class="upload-form">
+ <label for="description">Description</label>
+ <textarea name="description" value="{{ description }}"></textarea>
+ </div>
+ <div class="upload-form">
+ <label for="tags">Tags</label>
+ <input type="text" name="tags" placeholder="country, extratone, vocals, ..." value="{{ tags }}"></input>
+ </div>
+ <div class="upload-form">
+ <label for="collabs">Collaborators</label>
+ <input type="text" name="collabs" placeholder="@fren_user, John Doe, ..." value="{{ collaborators }}"></input>
+ </div>
+ <div class="upload-form">
+ <input type="submit" value="Upload"></input>
+ </div>
+</form>
+
+<!-- Automatically set song name from file name -->
+<script>
+document.getElementById("file-select").addEventListener("change", function(e) {
+ var songTitle = document.getElementById("song-title");
+ if (e.target.files[0] && !songTitle.value) {
+ var name = e.target.files[0].name;
+ songTitle.value = name.substring(0, name.length - 4);
+ }
+});
+</script>
+{% endif %}
+
+{% endblock %}
<h1 class="profile-name">{{ name }}</h1>
-{% if name == username %}
-<form action="/uploadsong" method="post" enctype="multipart/form-data">
- <h2>Upload a new song</h2>
- <div class="upload-form">
- <label for="song">mp3 File</label>
- <input type="file" name="song" accept=".mp3" id="file-select" required></input>
- </div>
- <div class="upload-form">
- <label for="title">Title</label>
- <input type="text" name="title" id="song-title" required></input>
- </div>
- <div class="upload-form">
- <label for="title">Description</label>
- <textarea name="description"></textarea>
- </div>
- <div class="upload-form">
- <label for="tags">Tags</label>
- <input type="text" name="tags" placeholder="country, extratone, vocals, ..."></input>
- </div>
- <div class="upload-form">
- <label for="collabs">Collaborators</label>
- <input type="text" name="collabs" placeholder="@fren_user, John Doe, ..."></input>
- </div>
- <div class="upload-form">
- <input type="submit" value="Upload"></input>
- </div>
-</form>
-{% endif %}
+<h2>Songs</h2>
-<!-- Automatically set song name from file name -->
-<script>
-document.getElementById("file-select").addEventListener("change", function(e) {
- var songTitle = document.getElementById("song-title");
- if (e.target.files[0] && !songTitle.value) {
- var name = e.target.files[0].name;
- songTitle.value = name.substring(0, name.length - 4);
- }
-});
-</script>
+<!-- Upload New Song button -->
+{% if session["userid"] == userid %}
+<a href="/edit-song">Upload New Song</a>
+{% endif %}
-<h2>Songs</h2>
<!-- TODO: This is duplicated in songs-by-tag.html -->
{% for song in songs %}
<div class="song-title"><h3>{{ song["title"] }}</h3></div>
<!-- Owner-Specific Buttons (Edit/Delete) -->
- {% if name == username %}
- <!-- TODO: Edit button -->
+ {% if session["userid"] == userid %}
+ <div class="song-edit-button">
+ <a href="/edit-song/{{ song["userid"] }}/{{ song["songid"] }}">Edit</a>
+ </div>
<div class="song-delete-button">
<a href="/delete-song/{{ song["userid"] }}/{{ song["songid"] }}">Delete</a>
</div>
{% endfor %}
{% endblock %}
+
+- edit-song.html: use song object
- delete song
- edit song info
- user bio