from app import app, db, limiter from app.sound_models import YouTubeAudio, SoundcloudAudio, CouldNotDecode, CouldNotDownload, CouldNotProcess from flask import Response, send_file, redirect, url_for, render_template, request, jsonify from multiprocessing import Value from mutagen.mp3 import MP3 import os import re import json import subprocess # Selection of Lambdas for creating new responses # Not sure if Responses change based on Request Context, but it doesn't hurt. getBadRequest = lambda : Response('Bad request', status=400, mimetype='text/plain') getNotImplemented = lambda : Response('Not implemented', status=501, mimetype='text/plain') getInvalidID = lambda : Response('Invalid ID', status=400, mimetype='text/plain') getNotDownloaded = lambda : Response('Media not yet downloaded', status=400, mimetype='text/plain') # Retrieves the YouTubeAudio object relevant to the mediaid if available. If not, it facilitates the creation and writing of one. # Also helps with access times. def get_youtube(mediaid): audio = YouTubeAudio.query.get(mediaid) if audio is not None: audio.access() return audio # sets the access time to now else: audio = YouTubeAudio(id=mediaid) audio.fill_metadata() audio.download() # Commit and save new audio object into the database db.session.add(audio) db.session.commit() return audio basic_responses = { CouldNotDecode : 'Could not decode process response.', CouldNotDownload : 'Could not download video.', CouldNotProcess : 'Could not process.' } # A simple function among the routes to determine what should be returned. def errorCheck(e, auth=False): response = str(e) print(response) if auth: if type(e) in basic_responses.keys(): response += f'\n{basic_responses[type(e)]}' else: raise e return Response(response, status=200, mimetype='text/plain') # Under the request context, it grabs the same args needed to decide whether the stream has been downloaded previously # It applies rate limiting differently based on service, and whether the stream has been accessed previously def downloadLimiter(): if request.view_args['service'] == 'youtube': if YouTubeAudio.query.get(request.view_args['mediaid']) is not None: return '5/minute' else: return '1/30seconds' else: return '10/minute' # Streams back the specified media back to the client @app.route('/stream//') @limiter.limit(downloadLimiter, lambda : 'global', error_message='429 Too Many Requests') def stream(service, mediaid): if service == 'youtube': if YouTubeAudio.isValid(mediaid): try: audio = get_youtube(mediaid) except Exception as e: return errorCheck(e) return send_file(audio.getPath(alt=True), attachment_filename=audio.filename) else: return getInvalidID() elif service == 'soundcloud': return getNotImplemented() elif service == 'spotify': return getNotImplemented() else: return getBadRequest() # Returns the duration of a specific media @app.route('/duration//') def duration(service, mediaid): if service == 'youtube': duration = get_youtube(mediaid).duration return Response(str(duration), status=200, mimetype='text/plain') elif service == 'soundcloud': return getNotImplemented() elif service == 'spotify': return getNotImplemented() else: return getBadRequest() # Returns a detailed JSON export of a specific database entry. # Will not create a new database entry where one didn't exist before. @app.route('/status//') def status(service, mediaid): if service == 'youtube': audio = YouTubeAudio.query.get(mediaid) if audio is None: if YouTubeAudio.isValid(mediaid): return getNotDownloaded() else: return getInvalidID() else: return Response(audio.toJSON(), status=200, mimetype='application/json') elif service == 'soundcloud': return getNotImplemented() elif service == 'spotify': return getNotImplemented() else: return getBadRequest() @app.route('/list/') def list(service): if service == 'youtube': audios = YouTubeAudio.query.all() return Response(','.join(audio.id for audio in audios), status=200, mimetype='text/plain') elif service == 'soundcloud': return getNotImplemented() elif service == 'spotify': return getNotImplemented() else: return getBadRequest() @app.route('/all/') def all(service): if service == 'youtube': audios = YouTubeAudio.query.all() return jsonify([audio.toJSON(True) for audio in audios]) return Response(jsonify([audio.toJSON(True) for audio in audios]), status=200, mimetype='application/json') elif service == 'soundcloud': return getNotImplemented() elif service == 'spotify': return getNotImplemented() else: return getBadRequest()