Organized a bit and added helper file helper.py

This commit is contained in:
rmilooo 2025-01-01 21:53:59 +01:00
parent 07c69a3fc7
commit a59776b1da
14 changed files with 440 additions and 361 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
token.txt

3
.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

7
.idea/misc.xml generated Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="Python 3.12" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12 (revivetube)" project-jdk-type="Python SDK" />
</project>

8
.idea/modules.xml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/revivetube.iml" filepath="$PROJECT_DIR$/.idea/revivetube.iml" />
</modules>
</component>
</project>

10
.idea/revivetube.iml generated Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.venv" />
</content>
<orderEntry type="jdk" jdkName="Python 3.12 (revivetube)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

Binary file not shown.

11
helper.py Normal file
View File

@ -0,0 +1,11 @@
def read_file(path):
assert isinstance(path, str), "Path must be a string"
try:
with open(path, 'r', encoding='utf-8') as file:
content = file.read()
return content
except FileNotFoundError:
return "Error: File not found."
except Exception as e:
return f"Error: {str(e)}"

View File

@ -15,28 +15,32 @@ ReviveMii Project: https://revivemii.fr.to/
""" """
import json import json
import isodate
from flask import Flask, request, render_template_string, send_file, Response, abort, jsonify
import tempfile
import shutil
import yt_dlp
import requests
import subprocess
import os import os
import shutil
import subprocess
import tempfile
import threading import threading
from threading import Thread
import time import time
from threading import Thread
import requests
import yt_dlp
from flask import Flask, request, render_template_string, send_file, Response, abort, jsonify
from helper import read_file
app = Flask(__name__) app = Flask(__name__)
def check_and_create_folder(): def check_and_create_folder():
while True: while True:
folder_path = './sigma/videos' folder_path = './sigma/videos'
if not os.path.exists(folder_path): if not os.path.exists(folder_path):
os.makedirs(folder_path) os.makedirs(folder_path)
print(f"Ordner {folder_path} wurde erstellt.") print(f"Folder {folder_path} got created.")
time.sleep(10) time.sleep(10)
def start_folder_check(): def start_folder_check():
thread = Thread(target=check_and_create_folder) thread = Thread(target=check_and_create_folder)
thread.daemon = True thread.daemon = True
@ -48,118 +52,34 @@ API_BASE_URL = "https://y.com.sb/api/v1/"
YOUTUBE_API_URL = "https://www.googleapis.com/youtube/v3/videos" YOUTUBE_API_URL = "https://www.googleapis.com/youtube/v3/videos"
video_status = {} video_status = {}
LOADING_TEMPLATE = """ FILE_SEPARATOR = os.sep
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Loading...</title>
<style>
body {
text-align: center;
font-family: Arial, sans-serif;
}
#loadingGif {
width: 50px;
height: 50px;
margin: 20px auto;
}
#goButton {
display: none;
margin-top: 20px;
padding: 10px 20px;
font-size: 16px;
background-color: #4caf50;
color: white;
border: none;
cursor: pointer;
}
#goButton:disabled {
background-color: gray;
cursor: not-allowed;
}
</style>
</head>
<body>
<h1>Loading</h1>
<img id="loadingGif" src="loading.gif" alt="Loading..." />
<p id="progressText">Fetching Info...</p>
<button id="goButton" onclick="startVideo()">Go</button>
<br>
<small style="color: grey">Loading Screen will NOT work in Dolphin Emulator.<br><br>Long Video = Longer Download and Converting.<br><br>For videos longer than 7 minutes, there is a chance that they wont play.</small>
<script type="text/javascript">
var goButton = document.getElementById('goButton');
var loadingGif = document.getElementById('loadingGif');
var progressText = document.getElementById('progressText');
var videoId = "{{ video_id }}";
function simulateLoading() { LOADING_TEMPLATE = read_file(f"site_storage{FILE_SEPARATOR}loading_template.html")
setInterval(checkStatus, 1000);
}
function checkStatus() {
var xhr = new XMLHttpRequest();
xhr.open('GET', '/status/' + videoId, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
var response;
try {
response = eval('(' + xhr.responseText + ')');
} catch (e) {
response = { status: 'error' };
}
updateProgress(response);
}
};
xhr.send();
}
function updateProgress(status) {
if (status.status === 'complete') {
loadingGif.style.display = 'none';
progressText.innerHTML = 'Done!';
goButton.style.display = 'inline';
} else if (status.status === 'downloading') {
progressText.innerHTML = 'The Server is Downloading...';
} else if (status.status === 'converting') {
progressText.innerHTML = 'The Server is Converting video...';
} else if (status.status === 'converting for Wii') {
progressText.innerHTML = 'The Server is Converting for Wii...';
} else {
progressText.innerHTML = 'The Server was unable to process the video! Report the Bug in the Discord Server. <br> Error Details for Developers: {{ video_id }}_unable_1.<br>Discord Server on ReviveMii Homepage Footer';
}
}
function startVideo() {
window.location.href = '/watch?video_id=' + videoId;
}
window.onload = function () {
simulateLoading();
};
</script>
</body>
</html>"""
def get_file_size(file_path): def get_file_size(file_path):
return os.path.getsize(file_path) return os.path.getsize(file_path)
def get_range(file_path, byte_range): def get_range(file_path, byte_range):
with open(file_path, 'rb') as f: with open(file_path, 'rb') as f:
f.seek(byte_range[0]) f.seek(byte_range[0])
return f.read(byte_range[1] - byte_range[0] + 1) return f.read(byte_range[1] - byte_range[0] + 1)
def get_api_key(): def get_api_key():
try: try:
with open("token.txt", "r") as f: with open("token.txt", "r") as f:
return f.read().strip() return f.read().strip()
except FileNotFoundError: except FileNotFoundError:
raise FileNotFoundError("Missing token.txt. Please go to README.md") raise FileNotFoundError("Missing token.txt. Please go to README.md")
os.makedirs(VIDEO_FOLDER, exist_ok=True) os.makedirs(VIDEO_FOLDER, exist_ok=True)
MAX_VIDEO_SIZE = 1 * 1024 * 1024 * 1024 MAX_VIDEO_SIZE = 1 * 1024 * 1024 * 1024
MAX_FOLDER_SIZE = 5 * 1024 * 1024 * 1024 MAX_FOLDER_SIZE = 5 * 1024 * 1024 * 1024
def get_folder_size(path): def get_folder_size(path):
total_size = 0 total_size = 0
@ -169,7 +89,10 @@ def get_folder_size(path):
total_size += os.path.getsize(file_path) total_size += os.path.getsize(file_path)
return total_size return total_size
""" """
[UNUSED IN THE CURRENT VERSION]
def delete_videos_periodically(): def delete_videos_periodically():
while True: while True:
time.sleep(86400) time.sleep(86400)
@ -182,244 +105,13 @@ def delete_videos_periodically():
threading.Thread(target=delete_videos_periodically, daemon=True).start() threading.Thread(target=delete_videos_periodically, daemon=True).start()
""" """
INDEX_TEMPLATE = """ INDEX_TEMPLATE = read_file(f"site_storage{FILE_SEPARATOR}index_template.html")
<!DOCTYPE html>
<html lang="en"> WATCH_STANDARD_TEMPLATE = read_file(f"site_storage{FILE_SEPARATOR}watch_standard_template.html")
<head>
<meta charset="UTF-8"> WATCH_WII_TEMPLATE = read_file(f"site_storage{FILE_SEPARATOR}watch_wii_template.html")
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ReviveTube by ReviveMii</title>
<style>
body {
font-family: 'Arial', sans-serif;
color: #fff;
background-color: #181818;
text-align: center;
}
h1 {
color: #ff0000;
font-size: 28px;
margin-bottom: 20px;
}
p, h2 {
font-size: 16px;
margin-bottom: 10px;
}
.search-bar {
width: 300px;
padding: 10px;
font-size: 16px;
border: 1px solid #ccc;
border-radius: 4px;
display: block;
margin: 0 auto;
}
button {
padding: 10px 20px;
font-size: 16px;
background-color: #333333;
color: white;
border: none;
cursor: pointer;
border-radius: 4px;
display: block;
margin: 10px auto;
}
.video-item {
margin-bottom: 20px;
text-align: center;
}
.video-item img {
width: 320px;
height: 180px;
border-radius: 8px;
}
.video-item-title {
color: #fff;
font-weight: bold;
font-size: 16px;
text-align: center;
}
.video-item-uploader {
color: #ccc;
font-size: 14px;
text-align: center;
}
.video-item-duration {
color: #ccc;
font-size: 14px;
text-align: center;
margin-top: 5px;
}
.dark-mode {
background-color: #181818;
color: #fff;
}
.dark-mode a {
color: #1e90ff;
}
</style>
</head>
<body class="dark-mode" id="page-body">
<h1>ReviveTube by ReviveMii</h1>
<p>A YouTube App for the Wii</p>
<form action="/" method="get">
<input class="search-bar" type="text" name="query" placeholder="Search YouTube">
<button type="submit">Go</button>
</form>
{% if results %}
<h2>Search Results</h2>
<div>
{% for video in results %}
<div class="video-item">
<a href="/watch?video_id={{ video['id'] }}">
<img src="{{ video['thumbnail'] }}" alt="{{ video['title'] }}">
<div class="video-item-title">{{ video['title'] }}</div>
<div class="video-item-uploader">By: {{ video['uploader'] }}</div>
<div class="video-item-duration">Duration: {{ video['duration'] }}</div>
</a>
</div>
{% endfor %}
</div>
{% endif %}
<p><a href="http://revivemii.errexe.xyz" target="_blank">Visit ReviveMii</a></p>
<p style="color: red;">\/ Scroll down \/</p>
<p style="font-size: 12px;">We are NOT affiliated with Nintendo or YouTube. This app uses code from Wiinet.xyz. For more information, scroll down to Open Source Software.</p>
<p style="color: blue">It's recommended to bookmark this page. Some sites may take longer to load.</p>
<a href="http://revivetube.errexe.xyz/revivetube/t-and-p.html">Terms of Service and Privacy Policy (Last Updated: 7. Dec 2024 12:41 CET)</a><br><br>
<a href="https://github.com/ReviveMii/revivetube/" target="_blank">Source Code</a><br><br>
<a href="http://revivetube.errexe.xyz/discord-redirect.html">Discord Server [Use a Compatible Device]</a>
<p>Version: v2 Beta (Sometimes I forget to update the Version Number)</p>
<a href="/licenses.html">Open Source Software Used in This App</a>
<br>
<a href="mailto:theerrorexe@gmail.com">Contact</a>
<br>
<a href="https://revivemii.errexe.xyz/feedback.html">Report Bugs & Feedback</a>
</body>
</html>
"""
WATCH_STANDARD_TEMPLATE = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ title }}</title>
<style>
body { font-family: 'Arial', sans-serif; text-align: center; color: #fff; background-color: #181818; }
video { margin-top: 20px; }
h1 { color: #ff0000; font-size: 24px; }
h3, p { font-size: 16px; }
.comments { text-align: left; margin: 0 auto; width: 80%; font-size: 14px; }
.comment { margin-bottom: 15px; padding: 10px; border-bottom: 1px solid #ddd; }
.comment p { font-size: 14px; }
.dark-mode { background-color: #181818; color: #fff; }
.dark-mode a { color: #1e90ff; }
</style>
</head>
<!-- <body class="dark-mode" id="page-body">
<video width="640" height="360" controls>
<source src="{{ video_mp4 }}" type="video/mp4">
Your browser does not support the video tag.
</video>
<h1>{{ title }}</h1>
<h3>Uploaded by: <a href="/channel?channel_id={{ channelId }}">{{ uploader }}</a></h3>
<p><strong>Views:</strong> {{ viewCount }}</p>
<p><strong>Likes:</strong> {{ likeCount }}</p>
<p><strong>Upload Date:</strong> {{ publishedAt }}</p>
<a href="#comments">Skip Description</a>
<h3>Description:</h3>
<p>{{ description | safe }}</p>
<h3 id="comments" class="comments">Comments:</h3>
<div class="comments">
{% if comments %}
{% for comment in comments %}
<div class="comment">
<p><strong>{{ comment.author }}</strong> posted:</p>
<p>{{ comment.text|safe }}</p>
<p style="color: gray; font-size: 12px;">Likes: {{ comment.likeCount }} | Post date: {{ comment.publishedAt }}</p>
</div>
{% endfor %}
{% else %}
<p>No Comments.</p>
{% endif %}
</div> -->
Please access this Site on a Wii
</body>
</html>
"""
WATCH_WII_TEMPLATE = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ title }}</title>
<style>
body {
font-family: 'Arial', sans-serif;
text-align: center; /* Zentriert den Text */
color: #fff;
background-color: #181818;
}
.dark-mode {
background-color: #181818;
color: #fff;
}
.dark-mode a {
color: #1e90ff;
}
.comments {
margin: 0 auto;
width: 80%;
}
h1 {
color: red;
text-align: center;
}
h3 {
color: white;
text-align: center;
}
</style>
<script src="https://unpkg.com/@ruffle-rs/ruffle"></script>
</head>
<body class="dark-mode" id="page-body">
<div style="width: 100%; background-color: #000; text-align: center;">
<object type="application/x-shockwave-flash" data="/player.swf" width="384" height="256">
<param name="wmode" value="transparent">
<param name="allowFullScreen" value="false">
<param name="flashvars" value="filename={{ video_flv }}">
</object>
</div>
<h1 style="color: red">{{ title }}</h1>
<h3>Uploaded by: <a href="/channel?channel_id={{ channelId }}">{{ uploader }}</a></h3>
<p><strong>Views:</strong> {{ viewCount }}</p>
<p><strong>Likes:</strong> {{ likeCount }}</p>
<p><strong>Upload Date:</strong> {{ publishedAt }}</p>
<a href="#comments">Skip Description</a>
<h3 style="color: red">Description:</h3>
<p>{{ description | safe }}</p>
<h3 id="comments" class="comments" style="color: red">Comments:</h3>
<div class="comments">
{% if comments %}
{% for comment in comments %}
<div class="comment">
<p><strong>{{ comment.author }}</strong> posted:</p>
<p>{{ comment.text|safe }}</p>
<p style="color: gray; font-size: 12px;">Likes: {{ comment.likeCount }} | Post date: {{ comment.publishedAt }}</p>
</div>
{% endfor %}
{% else %}
<p>No Comments.</p>
{% endif %}
</div>
</body>
</html>
"""
@app.route("/thumbnail/<video_id>") @app.route("/thumbnail/<video_id>")
def get_thumbnail(video_id): def get_thumbnail(video_id):
thumbnail_url = f"https://img.youtube.com/vi/{video_id}/hqdefault.jpg" thumbnail_url = f"https://img.youtube.com/vi/{video_id}/hqdefault.jpg"
@ -439,6 +131,7 @@ def get_thumbnail(video_id):
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
return f"Error fetching thumbnail: {str(e)}", 500 return f"Error fetching thumbnail: {str(e)}", 500
def get_video_comments(video_id, max_results=20): def get_video_comments(video_id, max_results=20):
api_key = get_api_key() api_key = get_api_key()
@ -447,7 +140,7 @@ def get_video_comments(video_id, max_results=20):
"videoId": video_id, "videoId": video_id,
"key": api_key, "key": api_key,
"maxResults": max_results, "maxResults": max_results,
"order": "relevance" "order": "relevance"
} }
try: try:
@ -472,6 +165,8 @@ def get_video_comments(video_id, max_results=20):
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
print(f"Fehler beim Abrufen der Kommentare: {str(e)}") print(f"Fehler beim Abrufen der Kommentare: {str(e)}")
return [] return []
@app.route("/switch_wii", methods=["GET"]) @app.route("/switch_wii", methods=["GET"])
def switch_wii(): def switch_wii():
video_id = request.args.get("video_id") video_id = request.args.get("video_id")
@ -489,6 +184,7 @@ def switch_wii():
else: else:
return "Can't start DEBUG Mode.", 500 return "Can't start DEBUG Mode.", 500
@app.route("/switch_n", methods=["GET"]) @app.route("/switch_n", methods=["GET"])
def switch_n(): def switch_n():
video_id = request.args.get("video_id") video_id = request.args.get("video_id")
@ -506,6 +202,7 @@ def switch_n():
else: else:
return "Can't start DEBUG Mode.", 500 return "Can't start DEBUG Mode.", 500
@app.route("/", methods=["GET"]) @app.route("/", methods=["GET"])
def index(): def index():
query = request.args.get("query") query = request.args.get("query")
@ -537,22 +234,24 @@ def index():
return render_template_string(INDEX_TEMPLATE, results=results) return render_template_string(INDEX_TEMPLATE, results=results)
def format_duration(seconds): def format_duration(seconds):
minutes = seconds // 60 minutes = seconds // 60
seconds = seconds % 60 seconds = seconds % 60
return f"{minutes}:{str(seconds).zfill(2)}" return f"{minutes}:{str(seconds).zfill(2)}"
def get_video_duration_from_file(video_path): def get_video_duration_from_file(video_path):
try: try:
result = subprocess.run( result = subprocess.run(
['ffprobe', '-v', 'error', '-show_format', '-show_streams', '-of', 'json', video_path], ['ffprobe', '-v', 'error', '-show_format', '-show_streams', '-of', 'json', video_path],
stdout=subprocess.PIPE, stderr=subprocess.PIPE stdout=subprocess.PIPE, stderr=subprocess.PIPE
) )
video_info = json.loads(result.stdout) video_info = json.loads(result.stdout)
duration = float(video_info['format']['duration']) duration = float(video_info['format']['duration'])
return duration return duration
except Exception as e: except Exception as e:
print(f"Can't fetch Video-Duration: {str(e)}") print(f"Can't fetch Video-Duration: {str(e)}")
@ -583,7 +282,6 @@ def watch():
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
return f"Can't connect to Metadata-API: {str(e)}", 500 return f"Can't connect to Metadata-API: {str(e)}", 500
comments = [] comments = []
try: try:
comments = get_video_comments(video_id) comments = get_video_comments(video_id)
@ -600,28 +298,28 @@ def watch():
alert("This Video is long. There is a chance that the Wii will not play the Video. Try a Video under 7 minutes or something like that."); alert("This Video is long. There is a chance that the Wii will not play the Video. Try a Video under 7 minutes or something like that.");
</script> </script>
""" """
if is_wii and os.path.exists(video_flv_path): if is_wii and os.path.exists(video_flv_path):
return render_template_string(WATCH_WII_TEMPLATE + alert_script, return render_template_string(WATCH_WII_TEMPLATE + alert_script,
title=metadata['title'], title=metadata['title'],
uploader=metadata['uploader'], uploader=metadata['uploader'],
channelId=metadata['channelId'], channelId=metadata['channelId'],
description=metadata['description'].replace("\n", "<br>"), description=metadata['description'].replace("\n", "<br>"),
viewCount=metadata['viewCount'], viewCount=metadata['viewCount'],
likeCount=metadata['likeCount'], likeCount=metadata['likeCount'],
publishedAt=metadata['publishedAt'], publishedAt=metadata['publishedAt'],
comments=comments, comments=comments,
video_id=video_id, video_id=video_id,
video_flv=f"/sigma/videos/{video_id}.flv", video_flv=f"/sigma/videos/{video_id}.flv",
alert_message="") alert_message="")
return render_template_string(WATCH_WII_TEMPLATE, return render_template_string(WATCH_WII_TEMPLATE,
title=metadata['title'], title=metadata['title'],
uploader=metadata['uploader'], uploader=metadata['uploader'],
channelId=metadata['channelId'], channelId=metadata['channelId'],
description=metadata['description'].replace("\n", "<br>"), description=metadata['description'].replace("\n", "<br>"),
viewCount=metadata['viewCount'], viewCount=metadata['viewCount'],
likeCount=metadata['likeCount'], likeCount=metadata['likeCount'],
publishedAt=metadata['publishedAt'], publishedAt=metadata['publishedAt'],
comments=comments, comments=comments,
video_id=video_id, video_id=video_id,
@ -633,6 +331,7 @@ def watch():
threading.Thread(target=process_video, args=(video_id,)).start() threading.Thread(target=process_video, args=(video_id,)).start()
return render_template_string(LOADING_TEMPLATE, video_id=video_id) return render_template_string(LOADING_TEMPLATE, video_id=video_id)
def process_video(video_id): def process_video(video_id):
video_mp4_path = os.path.join(VIDEO_FOLDER, f"{video_id}.mp4") video_mp4_path = os.path.join(VIDEO_FOLDER, f"{video_id}.mp4")
video_flv_path = os.path.join(VIDEO_FOLDER, f"{video_id}.flv") video_flv_path = os.path.join(VIDEO_FOLDER, f"{video_id}.flv")
@ -703,10 +402,12 @@ def process_video(video_id):
except Exception as e: except Exception as e:
video_status[video_id] = {"status": "error", "message": str(e)} video_status[video_id] = {"status": "error", "message": str(e)}
@app.route("/status/<video_id>") @app.route("/status/<video_id>")
def check_status(video_id): def check_status(video_id):
return jsonify(video_status.get(video_id, {"status": "pending"})) return jsonify(video_status.get(video_id, {"status": "pending"}))
@app.route("/video_metadata/<video_id>") @app.route("/video_metadata/<video_id>")
def video_metadata(video_id): def video_metadata(video_id):
api_key = get_api_key() api_key = get_api_key()
@ -719,7 +420,7 @@ def video_metadata(video_id):
try: try:
response = requests.get(YOUTUBE_API_URL, params=params, timeout=1) response = requests.get(YOUTUBE_API_URL, params=params, timeout=1)
response.raise_for_status() response.raise_for_status()
data = response.json() data = response.json()
@ -750,6 +451,7 @@ def video_metadata(video_id):
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
return f"Fehler bei der API-Anfrage: {str(e)}", 500 return f"Fehler bei der API-Anfrage: {str(e)}", 500
@app.route("/<path:filename>") @app.route("/<path:filename>")
def serve_video(filename): def serve_video(filename):
file_path = os.path.join(filename) file_path = os.path.join(filename)
@ -767,7 +469,7 @@ def serve_video(filename):
end_byte = int(end_byte) if end_byte else file_size - 1 end_byte = int(end_byte) if end_byte else file_size - 1
if start_byte >= file_size or end_byte >= file_size: if start_byte >= file_size or end_byte >= file_size:
abort(416) abort(416)
data = get_range(file_path, (start_byte, end_byte)) data = get_range(file_path, (start_byte, end_byte))
content_range = f"bytes {start_byte}-{end_byte}/{file_size}" content_range = f"bytes {start_byte}-{end_byte}/{file_size}"
@ -785,10 +487,11 @@ def serve_video(filename):
return send_file(file_path) return send_file(file_path)
@app.route('/channel', methods=['GET']) @app.route('/channel', methods=['GET'])
def channel_m(): def channel_m():
channel_id = request.args.get('channel_id', None) channel_id = request.args.get('channel_id', None)
if not channel_id: if not channel_id:
return "Channel ID is required.", 400 return "Channel ID is required.", 400
@ -797,7 +500,7 @@ def channel_m():
'extract_flat': True, 'extract_flat': True,
'playlistend': 20, 'playlistend': 20,
} }
try: try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl: with yt_dlp.YoutubeDL(ydl_opts) as ydl:
url = f"https://www.youtube.com/channel/{channel_id}/videos" url = f"https://www.youtube.com/channel/{channel_id}/videos"
@ -805,7 +508,7 @@ def channel_m():
if 'entries' not in info: if 'entries' not in info:
return "No videos found.", 404 return "No videos found.", 404
results = [ results = [
{ {
'id': video['id'], 'id': video['id'],
@ -816,11 +519,12 @@ def channel_m():
} }
for video in info['entries'] for video in info['entries']
] ]
return render_template_string(INDEX_TEMPLATE, results=results) return render_template_string(INDEX_TEMPLATE, results=results)
except Exception as e: except Exception as e:
return f"An error occurred: {str(e)}", 500 return f"An error occurred: {str(e)}", 500
if __name__ == "__main__": if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True, port=5000) app.run(host="0.0.0.0", debug=True, port=5000)

View File

@ -0,0 +1,114 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ReviveTube by ReviveMii</title>
<style>
body {
font-family: 'Arial', sans-serif;
color: #fff;
background-color: #181818;
text-align: center;
}
h1 {
color: #ff0000;
font-size: 28px;
margin-bottom: 20px;
}
p, h2 {
font-size: 16px;
margin-bottom: 10px;
}
.search-bar {
width: 300px;
padding: 10px;
font-size: 16px;
border: 1px solid #ccc;
border-radius: 4px;
display: block;
margin: 0 auto;
}
button {
padding: 10px 20px;
font-size: 16px;
background-color: #333333;
color: white;
border: none;
cursor: pointer;
border-radius: 4px;
display: block;
margin: 10px auto;
}
.video-item {
margin-bottom: 20px;
text-align: center;
}
.video-item img {
width: 320px;
height: 180px;
border-radius: 8px;
}
.video-item-title {
color: #fff;
font-weight: bold;
font-size: 16px;
text-align: center;
}
.video-item-uploader {
color: #ccc;
font-size: 14px;
text-align: center;
}
.video-item-duration {
color: #ccc;
font-size: 14px;
text-align: center;
margin-top: 5px;
}
.dark-mode {
background-color: #181818;
color: #fff;
}
.dark-mode a {
color: #1e90ff;
}
</style>
</head>
<body class="dark-mode" id="page-body">
<h1>ReviveTube by ReviveMii</h1>
<p>A YouTube App for the Wii</p>
<form action="/" method="get">
<input class="search-bar" type="text" name="query" placeholder="Search YouTube">
<button type="submit">Go</button>
</form>
{% if results %}
<h2>Search Results</h2>
<div>
{% for video in results %}
<div class="video-item">
<a href="/watch?video_id={{ video['id'] }}">
<img src="{{ video['thumbnail'] }}" alt="{{ video['title'] }}">
<div class="video-item-title">{{ video['title'] }}</div>
<div class="video-item-uploader">By: {{ video['uploader'] }}</div>
<div class="video-item-duration">Duration: {{ video['duration'] }}</div>
</a>
</div>
{% endfor %}
</div>
{% endif %}
<p><a href="http://revivemii.errexe.xyz" target="_blank">Visit ReviveMii</a></p>
<p style="color: red;">\/ Scroll down \/</p>
<p style="font-size: 12px;">We are NOT affiliated with Nintendo or YouTube. This app uses code from Wiinet.xyz. For more information, scroll down to Open Source Software.</p>
<p style="color: blue">It's recommended to bookmark this page. Some sites may take longer to load.</p>
<a href="http://revivetube.errexe.xyz/revivetube/t-and-p.html">Terms of Service and Privacy Policy (Last Updated: 7. Dec 2024 12:41 CET)</a><br><br>
<a href="https://github.com/ReviveMii/revivetube/" target="_blank">Source Code</a><br><br>
<a href="http://revivetube.errexe.xyz/discord-redirect.html">Discord Server [Use a Compatible Device]</a>
<p>Version: v2 Beta (Sometimes I forget to update the Version Number)</p>
<a href="/licenses.html">Open Source Software Used in This App</a>
<br>
<a href="mailto:theerrorexe@gmail.com">Contact</a>
<br>
<a href="https://revivemii.errexe.xyz/feedback.html">Report Bugs & Feedback</a>
</body>
</html>

View File

@ -0,0 +1,92 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Loading...</title>
<style>
body {
text-align: center;
font-family: Arial, sans-serif;
}
#loadingGif {
width: 50px;
height: 50px;
margin: 20px auto;
}
#goButton {
display: none;
margin-top: 20px;
padding: 10px 20px;
font-size: 16px;
background-color: #4caf50;
color: white;
border: none;
cursor: pointer;
}
#goButton:disabled {
background-color: gray;
cursor: not-allowed;
}
</style>
</head>
<body>
<h1>Loading</h1>
<img id="loadingGif" src="loading.gif" alt="Loading..." />
<p id="progressText">Fetching Info...</p>
<button id="goButton" onclick="startVideo()">Go</button>
<br>
<small style="color: grey">Loading Screen will NOT work in Dolphin Emulator.<br><br>Long Video = Longer Download and Converting.<br><br>For videos longer than 7 minutes, there is a chance that they wont play.</small>
<script type="text/javascript">
var goButton = document.getElementById('goButton');
var loadingGif = document.getElementById('loadingGif');
var progressText = document.getElementById('progressText');
var videoId = "{{ video_id }}";
function simulateLoading() {
setInterval(checkStatus, 1000);
}
function checkStatus() {
var xhr = new XMLHttpRequest();
xhr.open('GET', '/status/' + videoId, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
var response;
try {
response = eval('(' + xhr.responseText + ')');
} catch (e) {
response = { status: 'error' };
}
updateProgress(response);
}
};
xhr.send();
}
function updateProgress(status) {
if (status.status === 'complete') {
loadingGif.style.display = 'none';
progressText.innerHTML = 'Done!';
goButton.style.display = 'inline';
} else if (status.status === 'downloading') {
progressText.innerHTML = 'The Server is Downloading...';
} else if (status.status === 'converting') {
progressText.innerHTML = 'The Server is Converting video...';
} else if (status.status === 'converting for Wii') {
progressText.innerHTML = 'The Server is Converting for Wii...';
} else {
progressText.innerHTML = 'The Server was unable to process the video! Report the Bug in the Discord Server. <br> Error Details for Developers: {{ video_id }}_unable_1.<br>Discord Server on ReviveMii Homepage Footer';
}
}
function startVideo() {
window.location.href = '/watch?video_id=' + videoId;
}
window.onload = function () {
simulateLoading();
};
</script>
</body>
</html>

View File

@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ title }}</title>
<style>
body { font-family: 'Arial', sans-serif; text-align: center; color: #fff; background-color: #181818; }
video { margin-top: 20px; }
h1 { color: #ff0000; font-size: 24px; }
h3, p { font-size: 16px; }
.comments { text-align: left; margin: 0 auto; width: 80%; font-size: 14px; }
.comment { margin-bottom: 15px; padding: 10px; border-bottom: 1px solid #ddd; }
.comment p { font-size: 14px; }
.dark-mode { background-color: #181818; color: #fff; }
.dark-mode a { color: #1e90ff; }
</style>
</head>
<!-- <body class="dark-mode" id="page-body">
<video width="640" height="360" controls>
<source src="{{ video_mp4 }}" type="video/mp4">
Your browser does not support the video tag.
</video>
<h1>{{ title }}</h1>
<h3>Uploaded by: <a href="/channel?channel_id={{ channelId }}">{{ uploader }}</a></h3>
<p><strong>Views:</strong> {{ viewCount }}</p>
<p><strong>Likes:</strong> {{ likeCount }}</p>
<p><strong>Upload Date:</strong> {{ publishedAt }}</p>
<a href="#comments">Skip Description</a>
<h3>Description:</h3>
<p>{{ description | safe }}</p>
<h3 id="comments" class="comments">Comments:</h3>
<div class="comments">
{% if comments %}
{% for comment in comments %}
<div class="comment">
<p><strong>{{ comment.author }}</strong> posted:</p>
<p>{{ comment.text|safe }}</p>
<p style="color: gray; font-size: 12px;">Likes: {{ comment.likeCount }} | Post date: {{ comment.publishedAt }}</p>
</div>
{% endfor %}
{% else %}
<p>No Comments.</p>
{% endif %}
</div> -->
Please access this Site on a Wii
</body>
</html>

View File

@ -0,0 +1,67 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ title }}</title>
<style>
body {
font-family: 'Arial', sans-serif;
text-align: center; /* Zentriert den Text */
color: #fff;
background-color: #181818;
}
.dark-mode {
background-color: #181818;
color: #fff;
}
.dark-mode a {
color: #1e90ff;
}
.comments {
margin: 0 auto;
width: 80%;
}
h1 {
color: red;
text-align: center;
}
h3 {
color: white;
text-align: center;
}
</style>
<script src="https://unpkg.com/@ruffle-rs/ruffle"></script>
</head>
<body class="dark-mode" id="page-body">
<div style="width: 100%; background-color: #000; text-align: center;">
<object type="application/x-shockwave-flash" data="/player.swf" width="384" height="256">
<param name="wmode" value="transparent">
<param name="allowFullScreen" value="false">
<param name="flashvars" value="filename={{ video_flv }}">
</object>
</div>
<h1 style="color: red">{{ title }}</h1>
<h3>Uploaded by: <a href="/channel?channel_id={{ channelId }}">{{ uploader }}</a></h3>
<p><strong>Views:</strong> {{ viewCount }}</p>
<p><strong>Likes:</strong> {{ likeCount }}</p>
<p><strong>Upload Date:</strong> {{ publishedAt }}</p>
<a href="#comments">Skip Description</a>
<h3 style="color: red">Description:</h3>
<p>{{ description | safe }}</p>
<h3 id="comments" class="comments" style="color: red">Comments:</h3>
<div class="comments">
{% if comments %}
{% for comment in comments %}
<div class="comment">
<p><strong>{{ comment.author }}</strong> posted:</p>
<p>{{ comment.text|safe }}</p>
<p style="color: gray; font-size: 12px;">Likes: {{ comment.likeCount }} | Post date: {{ comment.publishedAt }}</p>
</div>
{% endfor %}
{% else %}
<p>No Comments.</p>
{% endif %}
</div>
</body>
</html>