Compare commits

...

5 Commits

Author SHA1 Message Date
TheErrorExe
898eba358c
add device pairing 2025-08-12 19:01:32 +02:00
TheErrorExe
cdf7f6f2b7
Update youtubei.py 2025-08-12 18:58:49 +02:00
TheErrorExe
dff7bba236
make videos load faster 2025-08-12 14:57:04 +02:00
TheErrorExe
6eaf1f6e16
Delete apiplayer.swf 2025-08-12 14:56:41 +02:00
TheErrorExe
d83733fb10
make videos load faster 2025-08-12 14:55:13 +02:00
3 changed files with 202 additions and 2 deletions

Binary file not shown.

View File

@ -151,6 +151,61 @@ def get_video_info():
video_info = GetVideoInfo().build(video_id)
return video_info
@app.route('/feeds/api/videos/<video_id>')
def video_details(video_id):
try:
video_info = youtubei.get_video_info(video_id)
if not video_info:
return Response(
'<error>Video not found</error>',
mimetype='text/xml',
status=404
)
ns = {
'media': 'http://search.yahoo.com/mrss/',
'yt': 'http://www.youtube.com/xml/schemas/2015'
}
root = ET.Element('entry')
ET.SubElement(root, 'id').text = f"http://127.0.0.1:5005/feeds/api/videos/{video_id}"
ET.SubElement(root, 'title').text = video_info.get('title', '')
ET.SubElement(root, 'published').text = video_info.get('publishedText', '')
author = ET.SubElement(root, 'author')
ET.SubElement(author, 'name').text = video_info.get('author', '')
media_group = ET.SubElement(root, 'media:group')
ET.SubElement(
media_group,
'media:thumbnail',
attrib={
'yt:name': 'hqdefault',
'url': f"http://i.ytimg.com/vi/{video_id}/hqdefault.jpg",
'width': '320',
'height': '240'
}
)
ET.SubElement(
media_group,
'yt:duration',
attrib={'seconds': str(video_info.get('lengthSeconds', 0))}
)
ET.SubElement(media_group, 'yt:videoid').text = video_id
stats = ET.SubElement(root, 'yt:statistics')
stats.set('viewCount', str(video_info.get('viewCount', 0)))
stats.set('likeCount', str(video_info.get('likeCount', 0)))
xml_str = ET.tostring(root, encoding='utf-8', method='xml').decode()
xml_str = f'<?xml version="1.0" encoding="UTF-8"?>\n{xml_str}'
return Response(xml_str, mimetype='text/xml')
except Exception as e:
return Response(
f'<error>{str(e)}</error>',
mimetype='text/xml',
status=500
)
@app.route("/wiitv")
def wiitv():
return send_from_directory(".", "leanbacklite_wii.swf", mimetype='application/x-shockwave-flash')
@ -204,6 +259,92 @@ def completesearch():
except Exception as e:
return jsonify({"error": f"Error processing suggestions: {str(e)}"}), 500
CONTENT_LENGTH = 50000000
@app.route('/git_video', methods=['GET'])
def git_video():
video_id = request.args.get('video_id')
if not video_id:
return "", 400
ytdlp_cmd = [
'yt-dlp',
f'https://www.youtube.com/watch?v={video_id}',
'-f', '5/18/best[ext=mp4]/best[height<=240]',
'--cookies', 'c.txt',
'-g'
]
try:
result = subprocess.run(ytdlp_cmd, capture_output=True, text=True)
if result.returncode != 0:
return f"yt-dlp error: {result.stderr}", 500
video_url = result.stdout.strip()
if not video_url:
return "No video URL found", 500
except Exception as e:
return f"yt-dlp error: {e}", 500
range_header = request.environ.get('HTTP_RANGE', '')
range_start = 0
range_end = CONTENT_LENGTH - 1
status = '200 OK'
headers = [
('Content-Type', 'video/x-flv'),
('Content-Disposition', f'attachment; filename="{video_id}.flv"'),
('Accept-Ranges', 'bytes'),
('Content-Length', str(CONTENT_LENGTH))
]
if range_header:
match = re.match(r'bytes=(\d+)-(\d*)', range_header)
if match:
range_start = int(match.group(1))
range_end = int(match.group(2)) if match.group(2) else CONTENT_LENGTH - 1
if range_start >= CONTENT_LENGTH or range_end >= CONTENT_LENGTH:
return "Range Not Satisfiable", 416
status = '206 Partial Content'
headers = [
('Content-Type', 'video/x-flv'),
('Content-Disposition', f'attachment; filename="{video_id}.flv"'),
('Accept-Ranges', 'bytes'),
('Content-Range', f'bytes {range_start}-{range_end}/{CONTENT_LENGTH}'),
('Content-Length', str(range_end - range_start + 1))
]
total_bitrate = 500000 + 96000
bytes_per_second = total_bitrate / 8
start_time = range_start / bytes_per_second
duration = (range_end - range_start + 1) / bytes_per_second
ffmpeg_cmd = [
'ffmpeg', '-i', video_url,
'-ss', str(start_time),
'-t', str(duration),
'-c:v', 'flv1', '-b:v', '500k', '-vf', 'scale=-1:240',
'-c:a', 'mp3', '-b:a', '96k',
'-r', '24', '-g', '24',
'-f', 'flv', 'pipe:1'
]
def generate(environ, start_response):
start_response(status, headers)
try:
process = subprocess.Popen(ffmpeg_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
while True:
chunk = process.stdout.read(8192)
if not chunk:
break
yield chunk
stderr = process.communicate()[1]
if process.returncode != 0:
yield f"ffmpeg error: {stderr.decode()}".encode()
except Exception as e:
yield f"ffmpeg error: {str(e)}".encode()
return generate
@app.route('/get_video', methods=['GET'])
def get_video():

View File

@ -3,7 +3,7 @@
import requests
YOUTUBE_INNERTUBE_API_KEY = "test"
YOUTUBE_INNERTUBE_API_KEY = "key"
YOUTUBE_API_URL = f"https://www.youtube.com/youtubei/v1/search?key={YOUTUBE_INNERTUBE_API_KEY}&region=US"
YOUTUBE_API_BROWSE_URL = f"https://www.youtube.com/youtubei/v1/browse?key={YOUTUBE_INNERTUBE_API_KEY}"
@ -28,6 +28,65 @@ def safe_int(value, default=0):
except (ValueError, TypeError):
return default
def get_video_info(video_id):
url = "https://www.youtube.com/youtubei/v1/player"
headers = {
"User-Agent": "Mozilla/5.0",
"Accept-Language": "en-US,en;q=0.9",
"Content-Type": "application/json",
"X-YouTube-Client-Name": "1",
"X-YouTube-Client-Version": "2.20231221"
}
payload = {
"context": {
"client": {
"hl": "en",
"gl": "US",
"clientName": "WEB",
"clientVersion": "2.20231221"
}
},
"videoId": video_id
}
try:
response = requests.post(url, json=payload, headers=headers)
response.raise_for_status()
data = response.json()
video_details = data.get('videoDetails', {})
microformat = data.get('microformat', {}).get('playerMicroformatRenderer', {})
engagement = data.get('engagement', {})
result = {
'videoId': video_id,
'title': video_details.get('title', ''),
'author': video_details.get('author', ''),
'authorId': video_details.get('channelId', ''),
'lengthSeconds': video_details.get('lengthSeconds', 0),
'viewCount': video_details.get('viewCount', 0),
'description': video_details.get('shortDescription', ''),
'publishedText': microformat.get('publishDate', ''),
'averageRating': engagement.get('averageRating', 0),
'likeCount': engagement.get('likeCount', 0),
'dislikeCount': engagement.get('dislikeCount', 0),
'keywords': video_details.get('keywords', []),
'isLive': video_details.get('isLive', False),
'thumbnail': {
'thumbnails': video_details.get('thumbnail', {}).get('thumbnails', [])
}
}
return result
except Exception as e:
print(f"Error fetching video info: {str(e)}")
return None
def extract_length_text_and_seconds(vd):
length_obj = vd.get("lengthText", {})
simple_text = length_obj.get("simpleText")
@ -239,5 +298,5 @@ def innertube_trending(trending_type=None, region="US", max_results=50):
return [parse_video(v) for v in videos]
except Exception as e:
print("error:", e)
print("Trending Parsing Error:", e)
return []