Something something like soundcloud but not like soundcloud.
Log in, upload records, done.
Simple, easy, KISS.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

229 lines
8.2 KiB

  1. from flask import Blueprint, render_template, request, redirect, url_for, flash, Response, abort, json
  2. from flask_babelex import gettext
  3. from flask_security import login_required, current_user
  4. from flask_uploads import UploadSet, AUDIO
  5. from little_boxes import activitypub as ap
  6. from forms import SoundUploadForm, SoundEditForm
  7. from models import db, User, Sound
  8. from utils import get_hashed_filename, InvalidUsage, add_user_log
  9. bp_sound = Blueprint("bp_sound", __name__)
  10. sounds = UploadSet("sounds", AUDIO)
  11. @bp_sound.route("/user/<string:username>/track/<string:soundslug>", methods=["GET"])
  12. def show(username, soundslug):
  13. user = User.query.filter(User.name == username).first()
  14. if not user:
  15. flash(gettext("User not found"), "error")
  16. return redirect(url_for("bp_main.home"))
  17. if current_user.is_authenticated and user.id == current_user.id:
  18. sound = Sound.query.filter(Sound.slug == soundslug, Sound.user_id == user.id).first()
  19. else:
  20. sound = Sound.query.filter(
  21. Sound.slug == soundslug, Sound.user_id == user.id, Sound.transcode_state == Sound.TRANSCODE_DONE
  22. ).first()
  23. if not sound:
  24. flash(gettext("Sound not found"), "error")
  25. return redirect(url_for("bp_users.profile", name=user.name))
  26. if sound.private:
  27. if current_user.is_authenticated:
  28. if sound.user_id != current_user.id:
  29. flash(gettext("Sound not found"), "error")
  30. return redirect(url_for("bp_users.profile", name=user.name))
  31. else:
  32. flash(gettext("Sound not found"), "error")
  33. return redirect(url_for("bp_users.profile", name=user.name))
  34. pcfg = {"title": sound.title}
  35. si = sound.sound_infos.first()
  36. if si:
  37. si_w = si.waveform
  38. else:
  39. si_w = None
  40. # if si and si.type == "FLAC":
  41. # flash(gettext("No HTML5 player supported actually"), 'info')
  42. return render_template("sound/show.jinja2", pcfg=pcfg, user=user, sound=sound, waveform=si_w)
  43. @bp_sound.route("/user/<string:username>/track/<string:soundslug>/waveform.json", methods=["GET"])
  44. def waveform_json(username, soundslug):
  45. user = User.query.filter(User.name == username).first()
  46. if not user:
  47. raise InvalidUsage("User not found", status_code=404)
  48. sound = Sound.query.filter(Sound.slug == soundslug, Sound.user_id == user.id).first()
  49. if not sound:
  50. raise InvalidUsage("Sound not found", status_code=404)
  51. if sound.private:
  52. if current_user:
  53. if sound.user_id != current_user.id:
  54. raise InvalidUsage("Sound not found", status_code=404)
  55. else:
  56. raise InvalidUsage("Sound not found", status_code=404)
  57. si = sound.sound_infos.first()
  58. if not si:
  59. return abort(404)
  60. wf = json.loads(si.waveform)
  61. wf["filename"] = sound.path_sound()
  62. wf["wf_png"] = sound.path_waveform()
  63. wf["title"] = sound.title
  64. return Response(json.dumps(wf), mimetype="application/json;charset=utf-8")
  65. @bp_sound.route("/sound/upload", methods=["GET", "POST"])
  66. @login_required
  67. def upload():
  68. pcfg = {"title": gettext("New upload")}
  69. user = User.query.filter(User.id == current_user.id).one()
  70. form = SoundUploadForm()
  71. if request.method == "POST" and "sound" in request.files:
  72. if form.validate_on_submit():
  73. filename_orig = request.files["sound"].filename
  74. filename_hashed = get_hashed_filename(filename_orig)
  75. sounds.save(request.files["sound"], folder=user.slug, name=filename_hashed)
  76. rec = Sound()
  77. rec.filename = filename_hashed
  78. rec.filename_orig = filename_orig
  79. rec.licence = form.licence.data
  80. if form.album.data:
  81. rec.album_id = form.album.data.id
  82. if not form.album.data.sounds:
  83. rec.album_order = 0
  84. else:
  85. rec.album_order = form.album.data.sounds.count() + 1
  86. rec.user_id = current_user.id
  87. if not form.title.data:
  88. rec.title = filename_orig
  89. else:
  90. rec.title = form.title.data
  91. rec.description = form.description.data
  92. rec.private = form.private.data
  93. if "flac" in request.files["sound"].mimetype or "ogg" in request.files["sound"].mimetype:
  94. rec.transcode_state = Sound.TRANSCODE_WAITING
  95. rec.transcode_needed = True
  96. db.session.add(rec)
  97. db.session.commit()
  98. # push the job in queue
  99. from tasks import upload_workflow
  100. upload_workflow.delay(rec.id)
  101. # log
  102. add_user_log(rec.id, user.id, "sounds", "info", "Uploaded {0} -- {1}".format(rec.id, rec.title))
  103. flash(gettext("Uploaded ! Processing will now follow."), "success")
  104. else:
  105. return render_template("sound/upload.jinja2", pcfg=pcfg, form=form, flash="Error with the file")
  106. return redirect(url_for("bp_sound.show", username=current_user.name, soundslug=rec.slug))
  107. # GET
  108. return render_template("sound/upload.jinja2", pcfg=pcfg, form=form)
  109. @bp_sound.route("/user/<string:username>/track/<string:soundslug>/edit", methods=["GET", "POST"])
  110. @login_required
  111. def edit(username, soundslug):
  112. sound = Sound.query.filter(Sound.user_id == current_user.id, Sound.slug == soundslug).first()
  113. if not sound:
  114. flash(gettext("Sound not found"), "error")
  115. return redirect(url_for("bp_users.profile", name=username))
  116. if sound.user.id != current_user.id:
  117. flash(gettext("Forbidden"), "error")
  118. return redirect(url_for("bp_users.profile", name=username))
  119. pcfg = {"title": gettext("Edit %(title)s", title=sound.title)}
  120. form = SoundEditForm(request.form, obj=sound)
  121. federate_new = False
  122. if form.validate_on_submit():
  123. if sound.private and not form.private.data:
  124. # Switched to public
  125. federate_new = True
  126. sound.private = form.private.data
  127. if not sound.private and form.private.data:
  128. # Can't switch back to private
  129. sound.private = False
  130. sound.title = form.title.data
  131. sound.description = form.description.data
  132. sound.licence = form.licence.data
  133. if form.album.data:
  134. sound.album_id = form.album.data.id
  135. if not sound.album_order:
  136. if not form.album.data.sounds:
  137. sound.album_order = 0
  138. else:
  139. sound.album_order = form.album.data.sounds.count() + 1
  140. db.session.commit()
  141. # log
  142. add_user_log(sound.id, sound.user.id, "sounds", "info", "Edited {0} -- {1}".format(sound.id, sound.title))
  143. if federate_new:
  144. # Switched from private to public: initial federation
  145. from tasks import federate_new_sound
  146. federate_new_sound(sound)
  147. else:
  148. # it's an update
  149. from tasks import send_update_sound
  150. send_update_sound(sound)
  151. return redirect(url_for("bp_sound.show", username=username, soundslug=sound.slug))
  152. if not sound.private:
  153. del form.private
  154. return render_template("sound/edit.jinja2", pcfg=pcfg, form=form, sound=sound)
  155. @bp_sound.route("/user/<string:username>/track/<string:soundslug>/delete", methods=["GET", "DELETE", "PUT"])
  156. @login_required
  157. def delete(username, soundslug):
  158. sound = Sound.query.filter(Sound.user_id == current_user.id, Sound.slug == soundslug).first()
  159. if not sound:
  160. flash(gettext("Sound not found"), "error")
  161. return redirect(url_for("bp_users.profile", name=username))
  162. if sound.user.id != current_user.id:
  163. flash(gettext("Forbidden"), "error")
  164. return redirect(url_for("bp_users.profile", name=username))
  165. # Federate Delete
  166. # TODO: Test
  167. from tasks import post_to_outbox
  168. activity = sound.activity
  169. delete_activity = ap.Delete(actor=sound.user.actor[0], object=ap.Tombstone(id=activity.id).to_dict(embed=True))
  170. post_to_outbox(delete_activity)
  171. db.session.delete(sound)
  172. db.session.commit()
  173. # log
  174. add_user_log(sound.id, sound.user.id, "sounds", "info", "Deleted {0} -- {1}".format(sound.id, sound.title))
  175. return redirect(url_for("bp_users.profile", name=username))