88 lines
3.1 KiB
Python
88 lines
3.1 KiB
Python
import requests
|
|
from flask import Flask, Response
|
|
from flask_cors import CORS
|
|
import threading
|
|
|
|
app = Flask(__name__)
|
|
CORS(app) # Enable CORS for all routes
|
|
|
|
# List of M3U file URLs
|
|
URLS = [
|
|
"https://iptv-org.github.io/iptv/index.country.m3u",
|
|
"https://git.prigoana.com/frost/repo/src/branch/main/tv.m3u",
|
|
"https://raw.githubusercontent.com/Free-TV/IPTV/master/playlist.m3u8",
|
|
"https://git.nadeko.net/Mystique/MystiquePlus/raw/branch/main/mystique.m3u",
|
|
"https://m3u.ibert.me/fmml_itv.m3u",
|
|
"https://m3u.ibert.me/fmml_index.m3u",
|
|
"https://m3u.ibert.me/ycl_iptv.m3u",
|
|
"https://m3u.ibert.me/cymz6_lives.m3u",
|
|
"https://m3u.ibert.me/y_g.m3u",
|
|
"https://m3u.ibert.me/j_home.m3u",
|
|
"https://m3u.ibert.me/j_iptv.m3u",
|
|
"https://m3u.ibert.me/o_cn.m3u",
|
|
"https://m3u.ibert.me/o_s_cn_112114.m3u",
|
|
"https://m3u.ibert.me/o_s_cn_cctv.m3u",
|
|
"https://m3u.ibert.me/o_s_cn_cgtn.m3u",
|
|
"https://m3u.ibert.me/cn.m3u",
|
|
"https://m3u.ibert.me/cn_c.m3u",
|
|
"https://m3u.ibert.me/cn_p.m3u",
|
|
"https://m3u.ibert.me/q_bj_iptv_unicom.m3u",
|
|
"https://m3u.ibert.me/q_bj_iptv_unicom_m.m3u",
|
|
"https://m3u.ibert.me/q_bj_iptv_mobile.m3u",
|
|
"https://m3u.ibert.me/q_bj_iptv_mobile_m.m3u",
|
|
"https://tvpass.org/playlist/m3u"
|
|
]
|
|
|
|
# Cached M3U content and a lock for thread safety
|
|
combined_m3u = None
|
|
lock = threading.Lock()
|
|
|
|
def fetch_m3u(url):
|
|
"""Fetch M3U content from a given URL with error handling."""
|
|
try:
|
|
response = requests.get(url, timeout=5) # Set timeout to prevent long waits
|
|
response.raise_for_status() # Raise an error for bad responses
|
|
return response.text
|
|
except requests.RequestException as e:
|
|
print(f"Error fetching {url}: {e}")
|
|
return ""
|
|
|
|
def combine_m3u(urls):
|
|
"""Combine M3U content from multiple URLs and remove duplicates."""
|
|
unique_streams = {}
|
|
|
|
for url in urls:
|
|
m3u_content = fetch_m3u(url)
|
|
lines = m3u_content.splitlines()
|
|
current_entry = None
|
|
|
|
for line in lines:
|
|
if line.startswith('#EXTINF:'):
|
|
current_entry = line
|
|
elif line and current_entry:
|
|
stream_link = line.strip()
|
|
if stream_link not in unique_streams:
|
|
unique_streams[stream_link] = f"{current_entry}\n{stream_link}"
|
|
current_entry = None # Reset after adding
|
|
|
|
return '#EXTM3U\n' + '\n'.join(unique_streams.values())
|
|
|
|
@app.route('/all.m3u')
|
|
def serve_m3u():
|
|
"""Serve the cached combined M3U file, generating it if necessary."""
|
|
global combined_m3u
|
|
with lock:
|
|
if combined_m3u is None: # Generate if not already done
|
|
combined_m3u = combine_m3u(URLS)
|
|
return Response(combined_m3u, mimetype='application/vnd.apple.mpegurl')
|
|
|
|
@app.route('/generate')
|
|
def generate_m3u():
|
|
"""Force regeneration of the M3U file from the sources."""
|
|
global combined_m3u
|
|
with lock:
|
|
combined_m3u = combine_m3u(URLS) # Regenerate the M3U content
|
|
return Response("M3U playlist regenerated!", mimetype='text/plain')
|
|
|
|
if __name__ == '__main__':
|
|
app.run(host='0.0.0.0', port=5000, threaded=True)
|