Reputation: 11
I'm building a Flask app that serves m3u8 streaming video files using m3u8 and requests. However, I'm encountering CORS issues when trying to fetch m3u8 files from external URLs. The CORS headers are not being set correctly, and I need to ensure that the video player can fetch the playlist and TS files from my server without encountering CORS errors.
Here's my current setup:
from flask import Flask, request, jsonify, render_template
import m3u8
import requests
from flask_cors import CORS, cross_origin
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
@app.route('/extract', methods=['POST'])
def extract_quality():
m3u8_url = request.json.get('url')
if not m3u8_url:
return jsonify({"error": "No URL provided"}), 400
try:
playlist = m3u8.load(m3u8_url)
qualities = []
for playlist_variant in playlist.playlists:
resolution = playlist_variant.stream_info.resolution
bandwidth = playlist_variant.stream_info.bandwidth
uri = playlist_variant.uri
qualities.append({
"resolution": resolution,
"bandwidth": bandwidth,
"uri": uri
})
return jsonify(qualities)
except Exception as e:
return jsonify({"error": f"An error occurred: {str(e)}"}), 500
CORS(app, resources={r"/*": {"origins": "*"}})
@app.route('/proxy', methods=['GET'])
@cross_origin()
def proxy():
url = request.args.get('url')
if not url:
return jsonify({"error": "No URL provided"}), 400
try:
response = requests.get(url)
if response.status_code == 200:
return response.content, response.status_code, {
'Access-Control-Allow-Origin': '*',
'Content-Type': response.headers.get('Content-Type', 'application/octet-stream')
}
else:
return jsonify({"error": f"Failed to fetch URL, status code: {response.status_code}"}), 500
except requests.exceptions.RequestException as e:
return jsonify({"error": f"An error occurred: {str(e)}"}), 500
@app.route('/tsproxy', methods=['GET'])
@cross_origin()
def ts_proxy():
ts_url = request.args.get('url')
if not ts_url:
return jsonify({"error": "No URL provided"}), 400
try:
response = requests.get(ts_url)
if response.status_code == 200:
return response.content, response.status_code, {
'Access-Control-Allow-Origin': '*',
'Content-Type': 'video/MP2T'
}
else:
return jsonify({"error": f"Failed to fetch TS file, status code: {response.status_code}"}), 500
except requests.exceptions.RequestException as e:
return jsonify({"error": f"An error occurred: {str(e)}"}), 500
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=5500)
I'm using Hls.js and Plyr on the frontend to play the video. Here is the JavaScript code for the player:
// JavaScript to handle fetching and playing the video
function getQualities() {
const url = document.getElementById("m3u8_url").value;
if (!url) {
alert("Please enter a URL!");
return;
}
fetch("/extract", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ url: url }),
})
.then((response) => response.json())
.then((data) => {
if (data.error) {
alert(data.error);
} else {
const selector = document.getElementById("quality_selector");
selector.innerHTML = ""; // Clear previous results
data.forEach((item) => {
const option = document.createElement("option");
option.value = item.uri;
option.textContent = `${item.resolution ? item.resolution.join("x") : "N/A"} - ${item.bandwidth} bps`;
selector.appendChild(option);
});
document.getElementById("quality_options").classList.remove("hidden");
}
})
.catch((error) => {
console.error("Error:", error);
alert("An error occurred while fetching the data");
});
}
function playVideo() {
const video = document.getElementById("video");
const url = document.getElementById("quality_selector").value;
if (!url) {
alert("Please select a quality!");
return;
}
const proxyUrl = `http://192.168.1.15:5500/proxy?url=${encodeURIComponent(url)}`;
if (Hls.isSupported()) {
const hls = new Hls({
loader: function (config) {
const originalLoader = new Hls.DefaultConfig.loader(config);
const originalLoad = originalLoader.load.bind(originalLoader);
// Update the TS segment URL to use the '/tsproxy' route
originalLoader.load = function (context, config, callbacks) {
if (context.type === "fragment") {
context.url = `http://192.168.1.15:5500/tsproxy?url=${encodeURIComponent(context.url)}`;
}
return originalLoad(context, config, callbacks);
};
return originalLoader;
},
});
hls.loadSource(proxyUrl);
hls.attachMedia(video);
new Plyr(video);
} else if (video.canPlayType("application/vnd.apple.mpegurl")) {
video.src = proxyUrl;
new Plyr(video);
} else {
alert("Your browser does not support HLS!");
}
}
} The Issue: When I try to fetch m3u8 and TS files from external sources, I'm encountering CORS errors despite using the CORS library in Flask. I am proxying the requests through my Flask app using the /proxy and /tsproxy routes, but it seems the CORS headers are not being set correctly or the browser is still blocking the requests.
I added the CORS headers manually in the proxy responses. I tried using @cross_origin() on the proxy routes. I've checked the browser's developer tools, and it shows CORS-related errors when trying to fetch the video segments. How can I resolve the CORS issue and successfully stream m3u8 videos from external URLs in my Flask app?
Upvotes: 0
Views: 127
Reputation: 425
<video>
tag..m3u8
and edit the listed file paths inside it.PHP (which should already be installed on your server) might be a better tool than Flask for fetching files, so tell your Flask code to access the PHP script's path instead of direct file paths.
Instead of using Flask (Python), you could instead try creating a server-side PHP script that acts as a proxy (middle man) that fetches the data and sends it back to your own HTML page.
proxy.php (or can be get_file.php)
<?php
$url = $_GET['url'];
// Set to your desired referrer
$referrer = 'http://www.example.com/';
// Set the referrer header
$opts = [
'http' => [
'header' => 'Referer: ' . $referrer
]
];
// Create stream context with referrer header
$context = stream_context_create($opts);
// Fetch video with the specified referrer
$videoContent = file_get_contents($url, false, $context);
// Set the Content-Type header
header('Content-Type: application/x-mpegURL');
echo $videoContent;
?>
Point your HTML to the server-side script's URL.
<body>
<video id="video" style="width: 100%; height: 100%;" controls="">
<source src="proxy.php?url=https://yoursite.com/video/720p/playlist.m3u8" type="application/x-mpegURL">
</video>
<script src="player.js"></script>
</body>
This is the only way I know to do this, there may be other ways. The concept is handling the request entirely server side, before it ever reaches the application.
Upvotes: 2