diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6b578d4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ + +token.txt \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..da5d2d0 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..a078ea6 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/revivetube.iml b/.idea/revivetube.iml new file mode 100644 index 0000000..10df758 --- /dev/null +++ b/.idea/revivetube.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/__pycache__/helper.cpython-312.pyc b/__pycache__/helper.cpython-312.pyc new file mode 100644 index 0000000..2626013 Binary files /dev/null and b/__pycache__/helper.cpython-312.pyc differ diff --git a/helper.py b/helper.py new file mode 100644 index 0000000..a514fcc --- /dev/null +++ b/helper.py @@ -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)}" diff --git a/revivetube.py b/revivetube.py index c49a344..09b58a3 100644 --- a/revivetube.py +++ b/revivetube.py @@ -15,28 +15,32 @@ 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 shutil +import subprocess +import tempfile import threading -from threading import Thread 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__) + def check_and_create_folder(): while True: folder_path = './sigma/videos' if not os.path.exists(folder_path): os.makedirs(folder_path) - print(f"Ordner {folder_path} wurde erstellt.") + print(f"Folder {folder_path} got created.") time.sleep(10) + def start_folder_check(): thread = Thread(target=check_and_create_folder) 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" video_status = {} -LOADING_TEMPLATE = """ - - - - - - Loading... - - - -

Loading

- Loading... -

Fetching Info...

- -
- Loading Screen will NOT work in Dolphin Emulator.

Long Video = Longer Download and Converting.

For videos longer than 7 minutes, there is a chance that they won’t play.
- - -""" 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() + return f.read().strip() except FileNotFoundError: raise FileNotFoundError("Missing token.txt. Please go to README.md") + os.makedirs(VIDEO_FOLDER, exist_ok=True) -MAX_VIDEO_SIZE = 1 * 1024 * 1024 * 1024 -MAX_FOLDER_SIZE = 5 * 1024 * 1024 * 1024 +MAX_VIDEO_SIZE = 1 * 1024 * 1024 * 1024 +MAX_FOLDER_SIZE = 5 * 1024 * 1024 * 1024 + def get_folder_size(path): total_size = 0 @@ -169,7 +89,10 @@ def get_folder_size(path): total_size += os.path.getsize(file_path) return total_size + """ +[UNUSED IN THE CURRENT VERSION] + def delete_videos_periodically(): while True: time.sleep(86400) @@ -182,244 +105,13 @@ def delete_videos_periodically(): threading.Thread(target=delete_videos_periodically, daemon=True).start() """ -INDEX_TEMPLATE = """ - - - - - - ReviveTube by ReviveMii - - - -

ReviveTube by ReviveMii

-

A YouTube App for the Wii

-
- - -
- {% if results %} -

Search Results

-
- {% for video in results %} -
- - {{ video['title'] }} -
{{ video['title'] }}
-
By: {{ video['uploader'] }}
-
Duration: {{ video['duration'] }}
-
-
- {% endfor %} -
- {% endif %} -

Visit ReviveMii

-

\/ Scroll down \/

-

We are NOT affiliated with Nintendo or YouTube. This app uses code from Wiinet.xyz. For more information, scroll down to Open Source Software.

-

It's recommended to bookmark this page. Some sites may take longer to load.

- Terms of Service and Privacy Policy (Last Updated: 7. Dec 2024 12:41 CET)

- Source Code

- Discord Server [Use a Compatible Device] -

Version: v2 Beta (Sometimes I forget to update the Version Number)

- Open Source Software Used in This App -
- Contact -
- Report Bugs & Feedback - - -""" +INDEX_TEMPLATE = read_file(f"site_storage{FILE_SEPARATOR}index_template.html") + +WATCH_STANDARD_TEMPLATE = read_file(f"site_storage{FILE_SEPARATOR}watch_standard_template.html") + +WATCH_WII_TEMPLATE = read_file(f"site_storage{FILE_SEPARATOR}watch_wii_template.html") -WATCH_STANDARD_TEMPLATE = """ - - - - - - {{ title }} - - - -Please access this Site on a Wii - - -""" -WATCH_WII_TEMPLATE = """ - - - - - - {{ title }} - - - - -
- - - - - -
-

{{ title }}

-

Uploaded by: {{ uploader }}

-

Views: {{ viewCount }}

-

Likes: {{ likeCount }}

-

Upload Date: {{ publishedAt }}

- Skip Description -

Description:

-

{{ description | safe }}

-

Comments:

-
- {% if comments %} - {% for comment in comments %} -
-

{{ comment.author }} posted:

-

{{ comment.text|safe }}

-

Likes: {{ comment.likeCount }} | Post date: {{ comment.publishedAt }}

-
- {% endfor %} - {% else %} -

No Comments.

- {% endif %} -
- - -""" @app.route("/thumbnail/") def get_thumbnail(video_id): 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: return f"Error fetching thumbnail: {str(e)}", 500 + def get_video_comments(video_id, max_results=20): api_key = get_api_key() @@ -447,7 +140,7 @@ def get_video_comments(video_id, max_results=20): "videoId": video_id, "key": api_key, "maxResults": max_results, - "order": "relevance" + "order": "relevance" } try: @@ -472,6 +165,8 @@ def get_video_comments(video_id, max_results=20): except requests.exceptions.RequestException as e: print(f"Fehler beim Abrufen der Kommentare: {str(e)}") return [] + + @app.route("/switch_wii", methods=["GET"]) def switch_wii(): video_id = request.args.get("video_id") @@ -489,6 +184,7 @@ def switch_wii(): else: return "Can't start DEBUG Mode.", 500 + @app.route("/switch_n", methods=["GET"]) def switch_n(): video_id = request.args.get("video_id") @@ -506,6 +202,7 @@ def switch_n(): else: return "Can't start DEBUG Mode.", 500 + @app.route("/", methods=["GET"]) def index(): query = request.args.get("query") @@ -537,22 +234,24 @@ def index(): 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)}") @@ -583,7 +282,6 @@ def watch(): except requests.exceptions.RequestException as e: return f"Can't connect to Metadata-API: {str(e)}", 500 - comments = [] try: 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."); """ - + if is_wii and os.path.exists(video_flv_path): - return render_template_string(WATCH_WII_TEMPLATE + alert_script, - title=metadata['title'], - uploader=metadata['uploader'], + return render_template_string(WATCH_WII_TEMPLATE + alert_script, + title=metadata['title'], + uploader=metadata['uploader'], channelId=metadata['channelId'], description=metadata['description'].replace("\n", "
"), - viewCount=metadata['viewCount'], + viewCount=metadata['viewCount'], likeCount=metadata['likeCount'], publishedAt=metadata['publishedAt'], comments=comments, - video_id=video_id, + video_id=video_id, video_flv=f"/sigma/videos/{video_id}.flv", alert_message="") - return render_template_string(WATCH_WII_TEMPLATE, - title=metadata['title'], - uploader=metadata['uploader'], + return render_template_string(WATCH_WII_TEMPLATE, + title=metadata['title'], + uploader=metadata['uploader'], channelId=metadata['channelId'], description=metadata['description'].replace("\n", "
"), - viewCount=metadata['viewCount'], - likeCount=metadata['likeCount'], + viewCount=metadata['viewCount'], + likeCount=metadata['likeCount'], publishedAt=metadata['publishedAt'], comments=comments, video_id=video_id, @@ -633,6 +331,7 @@ def watch(): threading.Thread(target=process_video, args=(video_id,)).start() return render_template_string(LOADING_TEMPLATE, video_id=video_id) + def process_video(video_id): video_mp4_path = os.path.join(VIDEO_FOLDER, f"{video_id}.mp4") 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: video_status[video_id] = {"status": "error", "message": str(e)} + @app.route("/status/") def check_status(video_id): return jsonify(video_status.get(video_id, {"status": "pending"})) + @app.route("/video_metadata/") def video_metadata(video_id): api_key = get_api_key() @@ -719,7 +420,7 @@ def video_metadata(video_id): try: response = requests.get(YOUTUBE_API_URL, params=params, timeout=1) - response.raise_for_status() + response.raise_for_status() data = response.json() @@ -750,6 +451,7 @@ def video_metadata(video_id): except requests.exceptions.RequestException as e: return f"Fehler bei der API-Anfrage: {str(e)}", 500 + @app.route("/") def serve_video(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 if start_byte >= file_size or end_byte >= file_size: - abort(416) + abort(416) data = get_range(file_path, (start_byte, end_byte)) content_range = f"bytes {start_byte}-{end_byte}/{file_size}" @@ -785,10 +487,11 @@ def serve_video(filename): return send_file(file_path) + @app.route('/channel', methods=['GET']) def channel_m(): channel_id = request.args.get('channel_id', None) - + if not channel_id: return "Channel ID is required.", 400 @@ -797,7 +500,7 @@ def channel_m(): 'extract_flat': True, 'playlistend': 20, } - + try: with yt_dlp.YoutubeDL(ydl_opts) as ydl: url = f"https://www.youtube.com/channel/{channel_id}/videos" @@ -805,7 +508,7 @@ def channel_m(): if 'entries' not in info: return "No videos found.", 404 - + results = [ { 'id': video['id'], @@ -816,11 +519,12 @@ def channel_m(): } for video in info['entries'] ] - + return render_template_string(INDEX_TEMPLATE, results=results) - + except Exception as e: return f"An error occurred: {str(e)}", 500 + if __name__ == "__main__": app.run(host="0.0.0.0", debug=True, port=5000) diff --git a/site_storage/index_template.html b/site_storage/index_template.html new file mode 100644 index 0000000..37e948e --- /dev/null +++ b/site_storage/index_template.html @@ -0,0 +1,114 @@ + + + + + + ReviveTube by ReviveMii + + + +

ReviveTube by ReviveMii

+

A YouTube App for the Wii

+
+ + +
+ {% if results %} +

Search Results

+
+ {% for video in results %} + + {% endfor %} +
+ {% endif %} +

Visit ReviveMii

+

\/ Scroll down \/

+

We are NOT affiliated with Nintendo or YouTube. This app uses code from Wiinet.xyz. For more information, scroll down to Open Source Software.

+

It's recommended to bookmark this page. Some sites may take longer to load.

+ Terms of Service and Privacy Policy (Last Updated: 7. Dec 2024 12:41 CET)

+ Source Code

+ Discord Server [Use a Compatible Device] +

Version: v2 Beta (Sometimes I forget to update the Version Number)

+ Open Source Software Used in This App +
+ Contact +
+ Report Bugs & Feedback + + \ No newline at end of file diff --git a/site_storage/loading_template.html b/site_storage/loading_template.html new file mode 100644 index 0000000..6f07ebe --- /dev/null +++ b/site_storage/loading_template.html @@ -0,0 +1,92 @@ + + + + + + Loading... + + + +

Loading

+ Loading... +

Fetching Info...

+ +
+ Loading Screen will NOT work in Dolphin Emulator.

Long Video = Longer Download and Converting.

For videos longer than 7 minutes, there is a chance that they won’t play.
+ + + \ No newline at end of file diff --git a/site_storage/watch_standard_template.html b/site_storage/watch_standard_template.html new file mode 100644 index 0000000..59b2f29 --- /dev/null +++ b/site_storage/watch_standard_template.html @@ -0,0 +1,49 @@ + + + + + + {{ title }} + + + +Please access this Site on a Wii + + \ No newline at end of file diff --git a/site_storage/watch_wii_template.html b/site_storage/watch_wii_template.html new file mode 100644 index 0000000..22dfca2 --- /dev/null +++ b/site_storage/watch_wii_template.html @@ -0,0 +1,67 @@ + + + + + + {{ title }} + + + + +
+ + + + + +
+

{{ title }}

+

Uploaded by: {{ uploader }}

+

Views: {{ viewCount }}

+

Likes: {{ likeCount }}

+

Upload Date: {{ publishedAt }}

+ Skip Description +

Description:

+

{{ description | safe }}

+

Comments:

+
+ {% if comments %} + {% for comment in comments %} +
+

{{ comment.author }} posted:

+

{{ comment.text|safe }}

+

Likes: {{ comment.likeCount }} | Post date: {{ comment.publishedAt }}

+
+ {% endfor %} + {% else %} +

No Comments.

+ {% endif %} +
+ + \ No newline at end of file