mirror of
https://github.com/ReviveMii/revivetube
synced 2025-04-29 12:39:25 -04:00
Merge pull request #1 from rmilooo/main
This commit is contained in:
commit
7fe1bf52d7
10 changed files with 459 additions and 402 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
token.txt
|
||||||
|
.venv
|
||||||
|
.idea
|
||||||
|
__pycache__
|
|
@ -1,6 +1,7 @@
|
||||||
import os
|
import os
|
||||||
import time
|
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
def get_folder_size(folder_path):
|
def get_folder_size(folder_path):
|
||||||
total_size = 0
|
total_size = 0
|
||||||
|
@ -11,6 +12,7 @@ def get_folder_size(folder_path):
|
||||||
total_size += os.path.getsize(filepath)
|
total_size += os.path.getsize(filepath)
|
||||||
return total_size
|
return total_size
|
||||||
|
|
||||||
|
|
||||||
def delete_files(folder_path, extensions):
|
def delete_files(folder_path, extensions):
|
||||||
os.system('sudo pkill -f revivetube.py')
|
os.system('sudo pkill -f revivetube.py')
|
||||||
process = subprocess.Popen(['sudo', 'nohup', 'python3', 'revivetube.py'])
|
process = subprocess.Popen(['sudo', 'nohup', 'python3', 'revivetube.py'])
|
||||||
|
@ -23,6 +25,7 @@ def delete_files(folder_path, extensions):
|
||||||
except:
|
except:
|
||||||
print("ERROR")
|
print("ERROR")
|
||||||
|
|
||||||
|
|
||||||
def monitor_folder(folder_path, size_limit_gb, check_interval):
|
def monitor_folder(folder_path, size_limit_gb, check_interval):
|
||||||
size_limit_bytes = size_limit_gb * 1024 * 1024 * 1024
|
size_limit_bytes = size_limit_gb * 1024 * 1024 * 1024
|
||||||
while True:
|
while True:
|
||||||
|
@ -31,6 +34,7 @@ def monitor_folder(folder_path, size_limit_gb, check_interval):
|
||||||
delete_files(folder_path, [".flv", ".mp4"])
|
delete_files(folder_path, [".flv", ".mp4"])
|
||||||
time.sleep(check_interval)
|
time.sleep(check_interval)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
folder_to_monitor = "./sigma/videos/"
|
folder_to_monitor = "./sigma/videos/"
|
||||||
size_limit = 7
|
size_limit = 7
|
||||||
|
|
1
generateRequirements.sh
Normal file
1
generateRequirements.sh
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pipreqs . --force --ignore .venv
|
57
helper.py
Normal file
57
helper.py
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
|
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)}"
|
||||||
|
|
||||||
|
|
||||||
|
def get_video_duration_from_file(video_path):
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
['ffprobe', '-v', 'error', '-show_format', '-show_streams', '-of', 'json', video_path],
|
||||||
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||||
|
)
|
||||||
|
|
||||||
|
video_info = json.loads(result.stdout)
|
||||||
|
|
||||||
|
duration = float(video_info['format']['duration'])
|
||||||
|
|
||||||
|
return duration
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Can't fetch Video-Duration: {str(e)}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def format_duration(seconds):
|
||||||
|
minutes = seconds // 60
|
||||||
|
seconds = seconds % 60
|
||||||
|
return f"{minutes}:{str(seconds).zfill(2)}"
|
||||||
|
|
||||||
|
|
||||||
|
def get_file_size(file_path):
|
||||||
|
return os.path.getsize(file_path)
|
||||||
|
|
||||||
|
|
||||||
|
def get_range(file_path, byte_range):
|
||||||
|
with open(file_path, 'rb') as f:
|
||||||
|
f.seek(byte_range[0])
|
||||||
|
return f.read(byte_range[1] - byte_range[0] + 1)
|
||||||
|
|
||||||
|
|
||||||
|
def get_api_key():
|
||||||
|
try:
|
||||||
|
with open("token.txt", "r") as f:
|
||||||
|
return f.read().strip()
|
||||||
|
except FileNotFoundError:
|
||||||
|
raise FileNotFoundError("Missing token.txt. Please go to README.md")
|
3
requirements.txt
Normal file
3
requirements.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
Flask==3.1.0
|
||||||
|
Requests==2.32.3
|
||||||
|
yt_dlp==2024.12.23
|
464
revivetube.py
464
revivetube.py
|
@ -14,29 +14,32 @@ If you use this Code, you agree to https://revivemii.fr.to/revivetube/t-and-p.ht
|
||||||
ReviveMii Project: https://revivemii.fr.to/
|
ReviveMii Project: https://revivemii.fr.to/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
import helper
|
||||||
|
|
||||||
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 +51,16 @@ 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 won’t 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 = helper.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):
|
|
||||||
return os.path.getsize(file_path)
|
|
||||||
|
|
||||||
def get_range(file_path, byte_range):
|
|
||||||
with open(file_path, 'rb') as f:
|
|
||||||
f.seek(byte_range[0])
|
|
||||||
return f.read(byte_range[1] - byte_range[0] + 1)
|
|
||||||
|
|
||||||
def get_api_key():
|
|
||||||
try:
|
|
||||||
with open("token.txt", "r") as f:
|
|
||||||
return f.read().strip()
|
|
||||||
except FileNotFoundError:
|
|
||||||
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 +70,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 +86,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 = helper.read_file(f"site_storage{FILE_SEPARATOR}index_template.html")
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
WATCH_STANDARD_TEMPLATE = helper.read_file(f"site_storage{FILE_SEPARATOR}watch_standard_template.html")
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
WATCH_WII_TEMPLATE = helper.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,15 +112,16 @@ 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 = helper.get_api_key()
|
||||||
|
|
||||||
params = {
|
params = {
|
||||||
"part": "snippet",
|
"part": "snippet",
|
||||||
"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 +146,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 +165,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 +183,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")
|
||||||
|
@ -527,7 +205,7 @@ def index():
|
||||||
"thumbnail": f"/thumbnail/{entry['videoId']}",
|
"thumbnail": f"/thumbnail/{entry['videoId']}",
|
||||||
"viewCount": entry.get("viewCountText", "Unbekannt"),
|
"viewCount": entry.get("viewCountText", "Unbekannt"),
|
||||||
"published": entry.get("publishedText", "Unbekannt"),
|
"published": entry.get("publishedText", "Unbekannt"),
|
||||||
"duration": format_duration(entry.get("lengthSeconds", 0)) # Video Dauer formatiert
|
"duration": helper.format_duration(entry.get("lengthSeconds", 0)) # Video Dauer formatiert
|
||||||
}
|
}
|
||||||
for entry in data
|
for entry in data
|
||||||
if entry.get("videoId")
|
if entry.get("videoId")
|
||||||
|
@ -537,27 +215,6 @@ def index():
|
||||||
|
|
||||||
return render_template_string(INDEX_TEMPLATE, results=results)
|
return render_template_string(INDEX_TEMPLATE, results=results)
|
||||||
|
|
||||||
def format_duration(seconds):
|
|
||||||
minutes = seconds // 60
|
|
||||||
seconds = seconds % 60
|
|
||||||
return f"{minutes}:{str(seconds).zfill(2)}"
|
|
||||||
|
|
||||||
def get_video_duration_from_file(video_path):
|
|
||||||
try:
|
|
||||||
result = subprocess.run(
|
|
||||||
['ffprobe', '-v', 'error', '-show_format', '-show_streams', '-of', 'json', video_path],
|
|
||||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
|
||||||
)
|
|
||||||
|
|
||||||
video_info = json.loads(result.stdout)
|
|
||||||
|
|
||||||
duration = float(video_info['format']['duration'])
|
|
||||||
|
|
||||||
return duration
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Can't fetch Video-Duration: {str(e)}")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/watch", methods=["GET"])
|
@app.route("/watch", methods=["GET"])
|
||||||
def watch():
|
def watch():
|
||||||
|
@ -583,7 +240,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)
|
||||||
|
@ -592,7 +248,7 @@ def watch():
|
||||||
comments = []
|
comments = []
|
||||||
|
|
||||||
if os.path.exists(video_mp4_path):
|
if os.path.exists(video_mp4_path):
|
||||||
video_duration = get_video_duration_from_file(video_flv_path)
|
video_duration = helper.get_video_duration_from_file(video_flv_path)
|
||||||
alert_script = ""
|
alert_script = ""
|
||||||
if video_duration > 420:
|
if video_duration > 420:
|
||||||
alert_script = """
|
alert_script = """
|
||||||
|
@ -600,28 +256,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 +289,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")
|
||||||
|
@ -645,7 +302,7 @@ def process_video(video_id):
|
||||||
command = [
|
command = [
|
||||||
"yt-dlp",
|
"yt-dlp",
|
||||||
"-f worstvideo+worstaudio",
|
"-f worstvideo+worstaudio",
|
||||||
"--proxy", "http://localhost:4000",
|
"http://localhost:4000",
|
||||||
"-o", temp_video_path,
|
"-o", temp_video_path,
|
||||||
f"https://m.youtube.com/watch?v={video_id}"
|
f"https://m.youtube.com/watch?v={video_id}"
|
||||||
]
|
]
|
||||||
|
@ -703,13 +360,15 @@ 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 = helper.get_api_key()
|
||||||
|
|
||||||
params = {
|
params = {
|
||||||
"part": "snippet,statistics",
|
"part": "snippet,statistics",
|
||||||
|
@ -719,7 +378,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 +409,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)
|
||||||
|
@ -757,7 +417,7 @@ def serve_video(filename):
|
||||||
if not os.path.exists(file_path):
|
if not os.path.exists(file_path):
|
||||||
return "File not found.", 404
|
return "File not found.", 404
|
||||||
|
|
||||||
file_size = get_file_size(file_path)
|
file_size = helper.get_file_size(file_path)
|
||||||
|
|
||||||
range_header = request.headers.get('Range', None)
|
range_header = request.headers.get('Range', None)
|
||||||
if range_header:
|
if range_header:
|
||||||
|
@ -767,9 +427,9 @@ 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 = helper.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}"
|
||||||
|
|
||||||
response = Response(
|
response = Response(
|
||||||
|
@ -785,10 +445,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 +458,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 +466,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 +477,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)
|
||||||
|
|
116
site_storage/index_template.html
Normal file
116
site_storage/index_template.html
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta content="width=device-width, initial-scale=1.0" name="viewport">
|
||||||
|
<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" name="query" placeholder="Search YouTube" type="text">
|
||||||
|
<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 alt="{{ video['title'] }}" src="{{ video['thumbnail'] }}">
|
||||||
|
<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>
|
93
site_storage/loading_template.html
Normal file
93
site_storage/loading_template.html
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta content="width=device-width, initial-scale=1.0" name="viewport">
|
||||||
|
<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 alt="Loading..." id="loadingGif" src="loading.gif"/>
|
||||||
|
<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 won’t 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>
|
49
site_storage/watch_standard_template.html
Normal file
49
site_storage/watch_standard_template.html
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta content="width=device-width, initial-scale=1.0" name="viewport">
|
||||||
|
<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>
|
68
site_storage/watch_wii_template.html
Normal file
68
site_storage/watch_wii_template.html
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta content="width=device-width, initial-scale=1.0" name="viewport">
|
||||||
|
<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 data="/player.swf" height="256" type="application/x-shockwave-flash" width="384">
|
||||||
|
<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 class="comments" id="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>
|
Loading…
Add table
Reference in a new issue