If the video does not play smoothly, restart the Internet Channel by pressing the Home button and then Reset. It's a bug. It happens if you visit too many Sites
+
{{ title }}
+
Uploaded by: {{ uploader }}
+
Description:
+
{{ description }}
+
+
+"""
+@app.route("/thumbnail/")
+def get_thumbnail(video_id):
+ thumbnail_url = f"https://img.youtube.com/vi/{video_id}/hqdefault.jpg"
+
+ try:
+ # Thumbnail von YouTube abrufen
+ response = requests.get(thumbnail_url, stream=True, timeout=5)
+ if response.status_code == 200:
+ # Content-Type weiterleiten
+ return send_file(
+ response.raw,
+ mimetype=response.headers.get("Content-Type", "image/jpeg"),
+ as_attachment=False,
+ )
+ else:
+ return f"Failed to fetch thumbnail. Status: {response.status_code}", 500
+ except requests.exceptions.RequestException as e:
+ return f"Error fetching thumbnail: {str(e)}", 500
+
+@app.route("/", methods=["GET"])
+def index():
+ query = request.args.get("query")
+ results = None
+
+ if query:
+ # API-Anfrage an y.com.sb für die Suchergebnisse
+ response = requests.get(f"{API_BASE_URL}search?q={query}", timeout=3)
+ try:
+ data = response.json() # Parst die JSON-Antwort der API
+ except ValueError:
+ return "Fehler beim Parsen der API-Antwort.", 500
+
+ # Ergebnisse verarbeiten, falls die API-Antwort erfolgreich und im erwarteten Format ist
+ if response.status_code == 200 and isinstance(data, list):
+ results = [
+ {
+ "id": entry.get("videoId"), # Die Video-ID
+ "title": entry.get("title"), # Der Titel des Videos
+ "uploader": entry.get("author", "Unbekannt"), # Der Name des Uploaders
+ "thumbnail": f"/thumbnail/{entry['videoId']}",
+ "viewCount": entry.get("viewCountText", "Unbekannt"), # Anzahl der Aufrufe
+ "published": entry.get("publishedText", "Unbekannt") # Veröffentlichungsdatum
+ }
+ for entry in data # Iteriere durch jedes Video
+ if entry.get("videoId") # Sicherstellen, dass ein VideoID vorhanden ist
+ ]
+ else:
+ return "Keine Ergebnisse gefunden oder Fehler in der API-Antwort.", 404
+
+ return render_template_string(INDEX_TEMPLATE, results=results)
+
+@app.route("/watch", methods=["GET"])
+def watch():
+ video_id = request.args.get("video_id")
+ if not video_id:
+ return "Missing video ID.", 400
+
+ # Metadaten abrufen
+ metadata_response = requests.get(f"http://127.0.0.1:5000/video_metadata/{video_id}")
+ if metadata_response.status_code != 200:
+ return f"Failed to fetch video metadata: {metadata_response.text}", 500
+
+ metadata = metadata_response.json()
+
+ # User-Agent prüfen
+ user_agent = request.headers.get("User-Agent", "").lower()
+ is_wii = "wii" in user_agent and "wiiu" not in user_agent
+
+ # Video-Pfade
+ 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 herunterladen, falls nicht vorhanden
+ if not os.path.exists(video_mp4_path):
+ video_url = f"{RIITUBE_BASE_URL}video/wii/?q={video_id}"
+
+ try:
+ response = requests.get(video_url, stream=True, timeout=10)
+ if response.status_code != 200:
+ return f"Failed to download video. HTTP Status: {response.status_code}, Reason: {response.reason}", 500
+
+ # Check file size during download
+ total_size = 0
+ with open(video_mp4_path, "wb") as f:
+ for chunk in response.iter_content(chunk_size=8192):
+ f.write(chunk)
+ total_size += len(chunk)
+ if total_size > MAX_VIDEO_SIZE:
+ os.remove(video_mp4_path)
+ return "Video exceeds 1 GB in size.", 400
+
+ except requests.exceptions.RequestException as e:
+ return f"An error occurred while downloading the video: {str(e)}", 500
+
+ # Für Wii in FLV umwandeln
+ if is_wii and not os.path.exists(video_flv_path):
+ try:
+ subprocess.run(
+ [
+ "ffmpeg",
+ "-i", video_mp4_path,
+ "-ar", "22050",
+ "-f", "flv",
+ "-s", "320x240",
+ "-ab", "32k",
+ "-filter:v", "fps=fps=15",
+ video_flv_path
+ ],
+ check=True
+ )
+ except subprocess.CalledProcessError as e:
+ return f"Failed to convert video to FLV for Wii: {str(e)}", 500
+
+ # HTML basierend auf User-Agent rendern
+ if is_wii:
+ return render_template_string(WATCH_WII_TEMPLATE, **metadata, video_flv=f"/sigma/videos/{video_id}.flv")
+ else:
+ return render_template_string(WATCH_STANDARD_TEMPLATE, **metadata, video_mp4=f"/sigma/videos/{video_id}.mp4")
+
+# Video-Metadaten zurückgeben (Simulation für Metadaten)
+@app.route("/video_metadata/")
+def video_metadata(video_id):
+ api_key = get_api_key()
+
+ # API-Anfrage an YouTube Data API v3
+ params = {
+ "part": "snippet,statistics",
+ "id": video_id,
+ "key": api_key
+ }
+
+ try:
+ response = requests.get(YOUTUBE_API_URL, params=params, timeout=2)
+ response.raise_for_status() # Raise HTTPError für schlechte Antworten
+
+ data = response.json()
+
+ # Überprüfen, ob Video-Daten vorhanden sind
+ if "items" not in data or len(data["items"]) == 0:
+ return f"Video mit ID {video_id} wurde nicht gefunden.", 404
+
+ # Metadaten extrahieren
+ video_data = data["items"][0]
+ title = video_data["snippet"]["title"]
+ description = video_data["snippet"]["description"]
+ uploader = video_data["snippet"]["channelTitle"]
+ view_count = video_data["statistics"].get("viewCount", "Unknown")
+ like_count = video_data["statistics"].get("likeCount", "Unknown")
+ dislike_count = video_data["statistics"].get("dislikeCount", "Unknown")
+
+ # Metadaten als JSON zurückgeben
+ return {
+ "title": title,
+ "uploader": uploader,
+ "description": description,
+ "viewCount": view_count,
+ "likeCount": like_count,
+ "dislikeCount": dislike_count
+ }
+
+ 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)
+
+ # Überprüfen, ob die Datei existiert
+ if not os.path.exists(file_path):
+ return "File not found.", 404
+
+ file_size = get_file_size(file_path)
+
+ # Überprüfen, ob ein Range-Header vorhanden ist
+ range_header = request.headers.get('Range', None)
+ if range_header:
+ # Range-Header parsen (Beispiel: 'bytes=0-499')
+ byte_range = range_header.strip().split('=')[1]
+ start_byte, end_byte = byte_range.split('-')
+ start_byte = int(start_byte)
+ end_byte = int(end_byte) if end_byte else file_size - 1
+
+ # Wenn der angeforderte Bereich ungültig ist
+ if start_byte >= file_size or end_byte >= file_size:
+ abort(416) # 416 Range Not Satisfiable
+
+ # Abschnitt der Datei zurückgeben
+ data = get_range(file_path, (start_byte, end_byte))
+ content_range = f"bytes {start_byte}-{end_byte}/{file_size}"
+
+ # Antwort mit Status 206 (Partial Content)
+ response = Response(
+ data,
+ status=206,
+ mimetype="video/mp4",
+ content_type="video/mp4",
+ direct_passthrough=True
+ )
+ response.headers["Content-Range"] = content_range
+ response.headers["Content-Length"] = str(len(data))
+ return response
+
+ # Wenn kein Range-Header vorhanden ist, wird die ganze Datei gesendet
+ return send_file(file_path)
+
+if __name__ == "__main__":
+ app.run(debug=True)