Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev/#107 Authentication tokens for APIs pt. 2 #156

Merged
merged 8 commits into from
Oct 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
326 changes: 8 additions & 318 deletions arcsi/api/item.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from datetime import datetime, timedelta

from flask import jsonify, make_response, request, redirect
from flask_security import login_required, auth_token_required, roles_required
from flask_security import auth_token_required, roles_required
from marshmallow import fields, post_load, Schema
from sqlalchemy import func

Expand Down Expand Up @@ -65,6 +65,7 @@ def make_item(self, data, **kwargs):

@arcsi.route("/item", methods=["GET"])
@arcsi.route("/item/all", methods=["GET"])
@auth_token_required
def list_items():
do = DoArchive()
items = Item.query.all()
Expand All @@ -78,6 +79,7 @@ def list_items():


@arcsi.route("/item/latest", methods=["GET"])
@auth_token_required
def list_items_latest():
do = DoArchive()
page = request.args.get('page', 1, type=int)
Expand All @@ -96,6 +98,7 @@ def list_items_latest():


@arcsi.route("/item/<id>", methods=["GET"])
@auth_token_required
def view_item(id):
item_query = Item.query.filter_by(id=id)
item = item_query.first_or_404()
Expand All @@ -112,7 +115,6 @@ def view_item(id):


@arcsi.route("/item/add", methods=["POST"])
@login_required
@roles_required("admin")
def add_item():
no_error = True
Expand Down Expand Up @@ -278,174 +280,8 @@ def add_item():
return "Some error happened, check server logs for details. Note that your media may have been uploaded (to DO and/or Azurcast)."


@arcsi.route("/item/add_api", methods=["POST"])
@auth_token_required
@roles_required("admin")
def add_item_api():
no_error = True
if request.is_json:
return make_response(
jsonify("Only accepts multipart/form-data for now, sorry"), 503, headers
)
# work around ImmutableDict type
item_metadata = request.form.to_dict()
# TODO if we could send JSON payloads w/ ajax then this prevalidation isn't needed
item_metadata["shows"] = [
{"id": item_metadata["shows"], "name": item_metadata["show_name"]}
]
item_metadata.pop("show_name", None)
# validate payload
err = item_schema.validate(item_metadata)
if err:
return make_response(
jsonify("Invalid data sent to add item, see: {}".format(err)), 500, headers
)
else:
item_metadata = item_schema.load(item_metadata)
download_count = 0
length = 0
archived = False
image_file_name = None
image_file = None
image_url = ""
play_file = None
play_file_name = None
archive_lahmastore_canonical_url = ""
archive_mixcloud_canonical_url = ""
shows = (
db.session.query(Show)
.filter(Show.id.in_((show.id for show in item_metadata.shows)))
.all()
)

new_item = Item(
number=item_metadata.number,
name=item_metadata.name,
description=item_metadata.description,
language=item_metadata.language,
play_date=item_metadata.play_date,
image_url=image_url,
play_file_name=play_file_name,
length=length,
live=item_metadata.live,
broadcast=item_metadata.broadcast,
archive_lahmastore=item_metadata.archive_lahmastore,
archive_lahmastore_canonical_url=archive_lahmastore_canonical_url,
archive_mixcloud=item_metadata.archive_mixcloud,
archive_mixcloud_canonical_url=archive_mixcloud_canonical_url,
archived=archived,
download_count=download_count,
uploader=item_metadata.uploader,
shows=shows,
)

#Check for duplicate files
name_occurrence = item_duplications_number(new_item)

db.session.add(new_item)
db.session.flush()

# TODO get show cover img and set as fallback
if request.files:
# Defend against possible duplicate files
if name_occurrence:
version_prefix = uuid4()
item_name = "{}-{}".format(new_item.name,version_prefix)
else:
item_name = new_item.name

# process files first
if request.files["play_file"]:
if request.files["play_file"] != "":
play_file = request.files["play_file"]

new_item.play_file_name = save_file(
archive_base=new_item.shows[0].archive_lahmastore_base_url,
archive_idx=new_item.number,
archive_file=play_file,
archive_file_name=(new_item.shows[0].name, item_name),
)

if request.files["image_file"]:
if request.files["image_file"] != "":
image_file = request.files["image_file"]

image_file_name = save_file(
archive_base=new_item.shows[0].archive_lahmastore_base_url,
archive_idx=new_item.number,
archive_file=image_file,
archive_file_name=(new_item.shows[0].name, item_name),
)

if new_item.broadcast:
# we require both image and audio if broadcast (Azuracast) is set
if not (image_file_name and new_item.play_file_name):
no_error = False
# this branch is typically used for pre-uploading live episodes (no audio)
else:
if not image_file_name:
no_error = False

# archive files if asked
if new_item.archive_lahmastore:
if no_error and (play_file or image_file):
if image_file_name:
new_item.image_url = archive(
archive_base=new_item.shows[0].archive_lahmastore_base_url,
archive_file_name=image_file_name,
archive_idx=new_item.number,
)
if not new_item.image_url:
no_error = False
if new_item.play_file_name:
new_item.archive_lahmastore_canonical_url = archive(
archive_base=new_item.shows[0].archive_lahmastore_base_url,
archive_file_name=new_item.play_file_name,
archive_idx=new_item.number,
)

if new_item.archive_lahmastore_canonical_url:
# Only set archived if there is audio data otherwise it's live episode
new_item.archived = True
else: # Upload didn't succeed
no_error = False

# broadcast episode if asked
if new_item.broadcast and no_error:
if not (play_file and image_file):
no_error = False
else:
new_item.airing = broadcast_audio(
archive_base=new_item.shows[0].archive_lahmastore_base_url,
archive_idx=new_item.number,
broadcast_file_name=new_item.play_file_name,
broadcast_playlist=new_item.shows[0].playlist_name,
broadcast_show=new_item.shows[0].name,
broadcast_title=new_item.name,
image_file_name=image_file_name,
)
if not new_item.airing:
no_error = False

# TODO some mp3 error
# TODO Maybe I used vanilla mp3 not from azuracast
# item_audio_obj = MP3(item_path)
# return item_audio_obj.filename
# item_length = item_audio_obj.info.length

db.session.commit()
# TODO no_error is just bandaid for proper exc handling
if no_error:
return make_response(
jsonify(item_schema.dump(new_item)),
200,
headers,
)
else:
return "Some error happened, check server logs for details. Note that your media may have been uploaded (to DO and/or Azurcast)."


@arcsi.route("item/<id>/listen", methods=["GET"])
gammaw marked this conversation as resolved.
Show resolved Hide resolved
@auth_token_required
def listen_play_file(id):
do = DoArchive()
item_query = Item.query.filter_by(id=id)
Expand All @@ -457,6 +293,7 @@ def listen_play_file(id):


@arcsi.route("/item/<id>/download", methods=["GET"])
@auth_token_required
def download_play_file(id):
do = DoArchive()
item_query = Item.query.filter_by(id=id)
Expand All @@ -468,7 +305,7 @@ def download_play_file(id):


@arcsi.route("/item/<id>", methods=["DELETE"])
@auth_token_required
@roles_required("admin")
def delete_item(id):
item_query = Item.query.filter_by(id=id)
item = item_query.first_or_404()
Expand All @@ -478,7 +315,6 @@ def delete_item(id):


@arcsi.route("/item/<id>/edit", methods=["POST"])
@login_required
@roles_required("admin")
def edit_item(id):
no_error = True
Expand Down Expand Up @@ -624,154 +460,8 @@ def edit_item(id):
return "Some error happened, check server logs for details. Note that your media may have been uploaded (to DO and/or Azurcast)."


@arcsi.route("/item/<id>/edit_api", methods=["POST"])
@auth_token_required
@roles_required("admin")
def edit_item_api(id):
no_error = True
image_file = None
image_file_name = None
play_file = None

item_query = Item.query.filter_by(id=id)
item = item_query.first_or_404()

# work around ImmutableDict type
item_metadata = request.form.to_dict()

# TODO if we could send JSON payloads w/ ajax then this prevalidation isn't needed
item_metadata["shows"] = [
{"id": item_metadata["shows"], "name": item_metadata["show_name"]}
]
item_metadata.pop("show_name", None)

# validate payload
# TODO handle what happens on f.e: empty payload?
# if err: -- need to check files {put IMG, put AUDIO} first
err = item_schema.validate(item_metadata)
if err:
return make_response(
jsonify("Invalid data sent to edit item, see: {}".format(err)),
500,
headers,
)
else:
# TODO edit uploaded media -- remove re-up etc.
# TODO broadcast / airing
item_metadata = item_schema.load(item_metadata)

#Check for duplicate files (before item is updated!)
name_occurrence = item_duplications_number(item_metadata)

item.number = item_metadata.number
item.name = item_metadata.name
item.description = item_metadata.description
item.language = item_metadata.language
item.play_date = item_metadata.play_date
item.live = item_metadata.live
item.broadcast = item_metadata.broadcast
item.airing = item_metadata.airing
item.uploader = item_metadata.uploader
item.archive_lahmastore = item_metadata.archive_lahmastore
item.archive_mixcloud = item_metadata.archive_mixcloud

# conflict between shows from detached object load(item_metadata) added to session vs original persistent object item from query
item.shows = (
db.session.query(Show)
.filter(Show.id.in_((show.id for show in item_metadata.shows)))
.all()
)

db.session.add(item)
db.session.flush()

if request.files:
# Defend against possible duplicate files
if name_occurrence:
version_prefix = uuid4()
item_name = "{}-{}".format(item.name,version_prefix)
else:
item_name = item.name

# process files first
if request.files["image_file"]:
if request.files["image_file"] != "":
image_file = request.files["image_file"]

image_file_name = save_file(
archive_base=item.shows[0].archive_lahmastore_base_url,
archive_idx=item.number,
archive_file=image_file,
archive_file_name=(item.shows[0].name, item_name),
)

if request.files["play_file"]:
if request.files["play_file"] != "":
play_file = request.files["play_file"]

item.play_file_name = save_file(
archive_base=item.shows[0].archive_lahmastore_base_url,
archive_idx=item.number,
archive_file=play_file,
archive_file_name=(item.shows[0].name, item_name),
)
if item.broadcast:
# we require both image and audio if broadcast (Azuracast) is set
if not (image_file_name and item.play_file_name):
no_error = False
# this branch is typically used for pre-uploading live episodes (no audio)
else:
if not image_file_name:
no_error = False

# archive files if asked
if item.archive_lahmastore:
if no_error and (play_file or image_file):
if image_file_name:
item.image_url = archive(
archive_base=item.shows[0].archive_lahmastore_base_url,
archive_file_name=image_file_name,
archive_idx=item.number,
)
if not item.image_url:
no_error = False
if item.play_file_name:
item.archive_lahmastore_canonical_url = archive(
archive_base=item.shows[0].archive_lahmastore_base_url,
archive_file_name=item.play_file_name,
archive_idx=item.number,
)
# Only set archived if there is audio data otherwise it's live episode
if item.archive_lahmastore_canonical_url:
item.archived = True
else:
no_error = False
# broadcast episode if asked
if item.broadcast and no_error:
if not (play_file and image_file):
no_error = False
else:
item.airing = broadcast_audio(
archive_base=item.shows[0].archive_lahmastore_base_url,
archive_idx=item.number,
broadcast_file_name=item.play_file_name,
broadcast_playlist=item.shows[0].playlist_name,
broadcast_show=item.shows[0].name,
broadcast_title=item.name,
image_file_name=image_file_name,
)
if not item.airing:
no_error = False

db.session.commit()
if no_error:
return make_response(
jsonify(item_partial_schema.dump(item)), 200, headers
)
return "Some error happened, check server logs for details. Note that your media may have been uploaded (to DO and/or Azurcast)."


@arcsi.route("/item/search", methods=["GET"])
@auth_token_required
def search_item():
do = DoArchive()
page = request.args.get('page', 1, type=int)
Expand Down
Loading