Serving media file to podcast.app (iOS/MacOS) for download - python

I'm trying to create a simple podcast hosting web server with flask.
Streaming media files from the server works beautifully. However whenever I try and download the media for offline viewing my podcast app throws "Download error".
I've tried all sorts of headers and responses.
End point I'm using with relevant code (I hardcoded the filename for testing):
import os
import logging
from streamlink.exceptions import (PluginError)
from flask import Flask, redirect, abort, request
from flask import send_from_directory
def run_server(host, port, file_dir):
#app.route('/media', methods=['GET'])
def media():
return send_from_directory(file_dir, '6TfLVL5GeE4.mp4')
app.run(host=host, port=port, debug=True)
Podcast.app error log states:
Download failed due to error: Invalid asset: The original extension and resolved extension were not playable for episode url Optional(http://10.0.0.115:8084/media?id=6TfLVL5GeE4&provider=tw).
I can't for the life of me figure why it would fail for downloading but stream perfectly fine.
Please help!

Found out that you can not use parameters for a podcast feed url.
The url must include a filename.
For example: http://localhost/file.mp3

Related

400 Bad Request when uploading files [Flask on Cloud9]

I'm having trouble resolving this issue. I've got a very simple file upload form that I'm getting the following error for:
Bad Request
The browser (or proxy) sent a request that this server could not understand.
I've followed a basic tutorial and copied it exactly. I'm wondering if it's because of some permissions thing as I'm using the Cloud9 IDE and just trying to upload the files into a folder I've created in the root of the site.
application.py is as follows:
from cs50 import SQL
import os
from flask import Flask, jsonify, redirect, render_template, request
# Configure application
app = Flask(__name__)
#get the absolute directry of the server path for file uploads
APP_ROOT = os.path.dirname(os.path.abspath(__file__))
[further in the code]
#app.route("/create_staff", methods=["POST"])
def create_staff():
#define upload path
target = os.path.join(APP_ROOT, 'images/')
#check the folder exists, if it doesn't, create it
if not os.path.isdir(target):
os.mkdir(target)
print("PREPPING TO UPLOAD FILE \n")
f = request.files['file']
filename = f.filename
print(filename)
destination = "/".join([target, filename])
f.save(destination)
return redirect("/addstaff")
Any suggestions on how to troubleshoot this?
i had exact same issue, and not found the solution yet, but make sure that:
your input html tag has name='file' as this will refer to request.files['file']
your <form> tag is marked with enctype='multipart/form-data'
https://flask.palletsprojects.com/en/1.1.x/patterns/fileuploads/

Boto3 S3 NosuchKey error when downloading file

I am accessing the Project Gutenberg API and I wrote a Python program that creates a text file out of a randomly chosen book from the API. Since the txt file is different each time the document in the S3 bucket should be blank. I want to be able to store an object I can constantly write over in S3 then pull from it in Flask and put it on the user's computer.
So far I have been using boto3 and I set up an AWS account with a specific bucket. I loaded a trial .txt file in there but when the program is accessed now it only downloads the file I put in there with the specific text, it doesn't change based on my program like it should.
Boto seems to be throwing me for a loop so if there's another way I am open to it.
My code right now is a mess. I'm throwing everything I can at it to make it work but I know I've reach the point where I need some help.
from flask import Flask, render_template
from gutenberg.acquire import load_etext
from gutenberg.cleanup import strip_headers
import random
import os
from flask import send_from_directory
import boto3
from flask import Flask, request
from flask import Flask, Response
from boto3 import client
import botocore
app = Flask(__name__, static_url_path='/static')
def get_client():
return client(
's3',
'us-east-1',
aws_access_key_id='XXXXXXX',
aws_secret_access_key='XXXXXXX'
)
#app.route('/')
def welcome():
return 'Welcome to the server'
#app.route('/favicon.ico')
def favicon():
return send_from_directory(os.path.join(app.root_path, 'static'), 'favicon.ico', mimetype='image/vnd.microsoft.icon')
#app.route('/roulette', methods=['POST', 'GET'])
def roulette():
s3 = get_client()
f = open("GutProject.txt", "w")
file = s3.get_object(Bucket='book-roulette', Key='GutProject.txt')
for x in range(1):
y = (random.randint(0, 59000))
text = strip_headers(load_etext(y)).strip()
s3_client = boto3.client('s3')
open('GutProject.txt').write(text)
s3_client.upload_file('GutProject.txt', 'book-roulette', 'Gut-remote.txt')
s3_client.download_file('book-roulette', 'hello-remote.txt', 'hello2.txt')
print(open('hello2.txt').read())s3_client.download_file('MyBucket', 'hello-remote.txt', 'hello2.txt')
print(open('hello2.txt').read())
return Response(
file['Body'].read(),
mimetype='text/plain',
headers={"Content-Disposition": "attachment;filename=GutProject.txt"}
)
if __name__ == "__main__":
app.run(debug=True)
My app should let the user click a button on the URL page and it will download a random file to their computer. The HTML works great and the Python/Flask worked before but the file wasn't downloading (I'm on Heroku).
I keep getting these errors:
botocore.errorfactory.NoSuchKey
botocore.errorfactory.NoSuchKey: An error occurred (NoSuchKey) when calling the GetObject operation: The specified key does not exist.
If the error stems from the line: s3_client.download_file('book-roulette', 'hello-remote.txt', 'hello2.txt'), then the error NoSuchKey is trying to say it cannot find the file s3://book-roulette/hello-remote.txt in the s3 region you specify.
I would suggest checking that s3 path to make sure it exists, or that the specified bucket and key are correct.
Edit: I notice that you create the s3_client object within your loop and overwrite the one where you specify your region and credentials, so it's possible it might not be checking in the right region anymore, but that might result in an access denied error or bucket not found error instead

How to write path in flask of folder in remote server

I'm creating a RESTful service using Flask,that parses files into a .zip folder, and I want to return that file. this is a snippet of the code.
from flask import Flask, send_from_directory
import zipfile
#app.route('/get_files', methods=['GET'])
def download():
zf = zipfile.ZipFile('gen.zip', mode = 'w')
zf.write('generated/file1.c')
zf.write('generated/file2.c')
zf.write('generated/file3.c')
zf.write('generated/file4.h')
zf.close()
return send_from_directory(/home/localadmin/test", "gen.zip", as_attachment=True)
it's working perfectly on localhost. when I run it on a remote server, I get 404 Not Found. So I think the issue is from the path to the directory?
EDIT:
I have tried returning a string instead of the generated files, and it's working perfectly too. what comes to my mind are two issues:
1. the path to the server directory is wrong
2. the server's proxy configurations does not permit file downloads maybe?

Flask Page not found

I am new to flask. I am making a simple web-based app. However, one of the url is not working. All the other pages are working fine
My python code
from flask import Flask, render_template, request
from openpyxl import load_workbook
app = Flask(__name__)
# Report Main Page
#app.route('/report')
def report():
return render_template('report.html')
if __name__ == '__main__':
app.run(debug=True)
It have some other pages. All are working fine. However /report is not working, It returns a 404 error
Not Found The requested URL was not found on the server. If you
entered the URL manually please check your spelling and try again.

Serving HTTP client with versioned resource only if it has changed - using Flask

I am running a webserver based on Flask, which serves a resource being versioned (e.g. installation file of some versioned program). I want to serve my HTTP client with new resource only in case, it already does not have the current version available. If there is new version, I want the client to download the resource and install it.
my Flask server looks like this
import json
import redis
import math
import requests
from flask import Flask,render_template,request
app=Flask(__name__)
#app.route('/version', methods=['GET','POST'])
def getversion():
r_server=redis.Redis("127.0.0.1")
if request.method == 'POST':
jsonobj_recieve=request.data
data=json.loads(jsonobj)
currentversion=r_server.hget('version')
if data == currentversion:
#code to return a 'ok'
else:
#code to return 'not ok' also should send the updated file to the client
else:
return r_server.hget('version')
if __name__ == '__main__':
app.run(
debug=True,
host="127.0.0.1",
port=80
)
my client is very basic:
import sys
import json
import requests
url="http://127.0.0.1/version"
jsonobj=json.dumps(str(sys.argv[1]))
print jsonobj
r=requests.post(url,data=jsonobj)
I will likely have to recode the entire client, this is not a problem but I really have no idea where to start....
Requirements Review
have web app, serving a versioned resource. It can be e.g. file with an applications.
have client, which allows fetching the resource only in case, the version of resource on the server and what client has locally already available differ
the client is aware of version string of the resource
allow client to learn new version string if new version is available
HTTP like design of your solution
If you want to allow downloading an application only in case, the client does not have it already, following design could be used:
use etag header. This usually contains some string describing unique status of resource you want to get from that url. In your case it could be current version number of your application.
in your request, use header "if-none-match", providing version number of your application present at client. This will result in HTTP Status code 306 - Not Modified in case, your client and server share the same version of resource. In case it differs, you would simply provide the content of the resource and use it. Your resource shall also denote in etag current version of the resource and your client shall take note of it, or find new version name from other sources (like from the downloaded file).
This design follows HTTP principles.
Flask serving resource with declaring version in etag
This is focusing on showing the principle, you shall elaborate on providing real content of the resource.
from flask import Flask, Response, request
import werkzeug.exceptions
app = Flask(__name__)
class NotModified(werkzeug.exceptions.HTTPException):
code = 304
def get_response(self, environment):
return Response(status=304)
#app.route('/download/app')
def downloadapp():
currver = "1.0"
if request.if_none_match and currver in request.if_none_match:
raise NotModified
def generate():
yield "app_file_part 1"
yield "app_file_part 2"
yield "app_file_part 3"
return Response(generate(), headers={"etag": currver})
if __name__ == '__main__':
app.run(debug=True)
Client getting resource only, if it is new
import requests
ver = "1.0"
url = "http://localhost:5000/download/app"
req = requests.get(url, headers={"If-None-Match": ver})
if req.status_code == 200:
print "new content of resource", req.content
new_ver = req.headers["etag"]
else:
print "resource did not change since last time"
Alternative solution of web part using web server (e.g. NGINX)
Assuming the resource is static file, which updates only sometime, you shall be able configuring your web server, e.g. NGINX, to serve that resource and declaring in your configuration explicit value for etag header to the version string.
Note, that as it was not requested, this alternative solution is not elaborated here (and was not tested).
Client implementation would not be modified by that (here it pays back the design is following HTTP concepts).
There are multiple ways of achieving this but as this is a Flask app, here's one using HTTP.
If the version is OK, just return a relevant status code, like a 200 OK. You can add a JSON response in the body if that's necessary. If you return a string with flask, the status code will be 200 OK and you can inspect that in your client.
If the version differs, return the URL where the file is located. The client will have to
download the file. That's pretty simple using requests. Here's a typical example for downloading file by streaming requests:
def get(url, chunk_size=1024):
""" Download a file in chunks of n bytes """
fn = url.split("/")[-1] # if you're url is complicated, use urlparse.
stream = requests.get(url, stream=True)
with open(fn, "wb") as local:
for chunk in stream.iter_content(chunk_size=chunk_size):
if chunk:
f.write(chunk)
return fn
This is very simplified. If your file is not static and cannot live on the server (like software update patches probably shouldn't) then you'll have to figure out a way to get the file from a database or generate it on the fly.

Categories