__pycache__
database.db
songs
+images
app.log*
bio=profile_bio,
song_list=render_template("song-list.html", songs=songs))
-@app.post("/update-bio")
-def update_bio():
+@app.post("/edit-profile")
+def edit_profile():
+ if not "userid" in session:
+ abort(401)
+
query_db(
"update users set bio = ? where userid = ?",
[request.form["bio"], session["userid"]])
get_db().commit()
- flash("Bio updated successfully")
+
+ if request.files["pfp"]:
+ pfp_path = get_user_images_path(session["userid"]) / "pfp"
+ request.files["pfp"].save(pfp_path)
+
+ flash("Profile updated successfully")
app.logger.info(f"{session['username']} updated bio")
return redirect(f"/users/{session['username']}")
+@app.get("/pfp/<int:userid>")
+def pfp(userid):
+ return send_from_directory(DATA_DIR / "images" / str(userid), "pfp")
+
@app.get("/edit-song")
def edit_song():
if not "userid" in session:
return error
-def get_user_path(userid):
+def get_user_songs_path(userid):
userpath = DATA_DIR / "songs" / str(userid)
if not userpath.exists():
os.makedirs(userpath)
return userpath
+def get_user_images_path(userid):
+ userpath = DATA_DIR / "images" / str(userid)
+ if not userpath.exists():
+ os.makedirs(userpath)
+ return userpath
+
def update_song():
songid = request.args["songid"]
try:
if passed:
# Move file to permanent location
- filepath = get_user_path(session["userid"]) / (str(song_data["songid"]) + ".mp3")
+ filepath = get_user_songs_path(session["userid"]) / (str(song_data["songid"]) + ".mp3")
shutil.move(tmp_file.name, filepath)
else:
error = True
"insert into songs (userid, title, description, created) values (?, ?, ?, ?) returning (songid)",
[session["userid"], title, description, timestamp], one=True)
songid = song_data["songid"]
- filepath = get_user_path(session["userid"]) / (str(song_data["songid"]) + ".mp3")
+ filepath = get_user_songs_path(session["userid"]) / (str(song_data["songid"]) + ".mp3")
# Move file to permanent location
shutil.move(tmp_file.name, filepath)
font-family: sans-serif;
font-size: 16px;
font-weight: bold;
+ text-decoration: none;
color: var(--yellow);
background: var(--purple);
border: 0px;
font-size: 40px;
}
+.big-pfp-container {
+ margin: 0 auto;
+ width: 200px;
+ max-width: 80%;
+ background: var(--purple);
+ padding: 5px;
+ border-radius: 10px;
+}
+
+.big-pfp {
+ width: 100%;
+ max-height: 200px;
+ margin: 0px;
+ padding: 0px;
+ display: block;
+ border-radius: 5px;
+}
+
.profile-action {
- margin: 10px;
+ margin-bottom: 20px;
}
.profile-edit-buttons {
{% block body %}
+<!-- Username -->
<h1 class="profile-name">{{ name }}</h1>
+<!-- Profile Picture -->
+<div class="big-pfp-container">
+ <img src="/pfp/{{ userid }}" onerror="hidePfp(this)" class="big-pfp">
+</div>
+
+<script>
+ function hidePfp(pfp) {
+ pfp.parentElement.hidden = true;
+ }
+</script>
+
<!-- Bio -->
<div class="profile-bio" id="profile-bio">{{ (bio.replace("\n", "<br>"))|safe }}</div>
-<!-- Bio edit form -->
+<!-- Profile edit form -->
{% if session["userid"] == userid %}
<div class="profile-action">
- <a href="javascript:showEditForm();" id="profile-bio-edit-btn">Edit Bio</a>
+ <button class="button" onclick="showEditForm()" id="profile-bio-edit-btn">Edit Profile</button>
</div>
-<form id="profile-edit-form" action="/update-bio" method="post" hidden>
+<form id="profile-edit-form" action="/edit-profile" method="post" enctype="multipart/form-data" hidden>
+ <h2> Profile Picture </h2>
+ <input type="file" name="pfp" accept="image/png, image/jpg, image/gif, image/svg" />
+
<h2> Edit Bio </h2>
<p>Common HTML tags (<a>, <b>, <i>, <img>, etc.) are allowed.</p>
<p>Examples:</p>
</div>
</form>
-<!-- Show/hide bio edit form -->
+<!-- Show/hide profile edit form -->
<script>
function showEditForm() {
document.getElementById("profile-bio").hidden = true;
<!-- Upload New Song button -->
{% if session["userid"] == userid %}
<div class="profile-action">
- <a href="/edit-song">Upload New Song</a>
+ <a class="button" href="/edit-song">Upload New Song</a>
</div>
{% endif %}
def test_update_bio(client):
_create_user(client, "user", "password", login=True)
- response = client.post("/update-bio", data={"bio": "this is the bio"})
+ response = client.post("/edit-profile", data={"bio": "this is the bio", "pfp": (b"", "", "aplication/octet-stream")})
assert response.status_code == 302
assert response.headers["Location"] == "/users/user"
response = client.get("/users/user")
assert b'<div class="profile-bio" id="profile-bio">this is the bio</div>' in response.data
+def test_upload_pfp(client):
+ _create_user(client, "user", "password", login=True)
+ response = client.post("/edit-profile", data={"bio": "", "pfp": open("lsp_notes.png", "rb")})
+ assert response.status_code == 302
+
+def test_edit_profile_not_logged_in(client):
+ response = client.post("/edit-profile", data={"bio": "", "pfp": open("lsp_notes.png", "rb")})
+ assert response.status_code == 401
+
+def test_get_pfp(client):
+ _create_user(client, "user", "password", login=True)
+ client.post("/edit-profile", data={"bio": "", "pfp": open("lsp_notes.png", "rb")})
+
+ response = client.get("/pfp/1")
+ assert response.status_code == 200
+ with open("lsp_notes.png", "rb") as expected:
+ assert expected.read() == response.data
+
+def test_get_pfp_no_file(client):
+ _create_user(client, "user", "password", login=True)
+ # User exists but doesn't have a pfp
+ response = client.get("/pfp/1")
+ assert response.status_code == 404
+
+def test_get_pfp_invalid_user(client):
+ response = client.get("/pfp/1")
+ # User doesn't exist
+ assert response.status_code == 404
+
################################################################################
# Upload Song
################################################################################
NOW
-- Profile pictures
+- PFP in song list
+- Additional song info in player (collabs, description, tags, pfp)
- Dark mode/site color customization
- YouTube importer
-- Additional song info in player (collabs, description, tags)
SOON
- Shuffle all