owoembed
(oEmbed)
This commit is contained in:
parent
e9eb88c13f
commit
e48159ce14
124
app.py
124
app.py
@ -3,7 +3,7 @@ import http.cookiejar
|
|||||||
import json
|
import json
|
||||||
import requests
|
import requests
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from flask import Flask, Response
|
from flask import Flask, Response, request, jsonify
|
||||||
from diskcache import Cache
|
from diskcache import Cache
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -13,6 +13,7 @@ import tempfile
|
|||||||
import nndownload
|
import nndownload
|
||||||
import boto3
|
import boto3
|
||||||
from botocore.client import Config as BotoConfig
|
from botocore.client import Config as BotoConfig
|
||||||
|
import urllib.parse
|
||||||
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
@ -21,6 +22,7 @@ logging.basicConfig(level=logging.INFO)
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
HOST = os.environ.get('NICONICOGAY_HOST', 'https://nicovideo.gay')
|
||||||
S3_BUCKET_NAME = os.environ.get('NICONICOGAY_S3_BUCKET_NAME')
|
S3_BUCKET_NAME = os.environ.get('NICONICOGAY_S3_BUCKET_NAME')
|
||||||
S3_REGION = os.environ.get('NICONICOGAY_S3_REGION')
|
S3_REGION = os.environ.get('NICONICOGAY_S3_REGION')
|
||||||
CDN_BASE_URL = os.environ.get('NICONICOGAY_CDN_BASE_URL')
|
CDN_BASE_URL = os.environ.get('NICONICOGAY_CDN_BASE_URL')
|
||||||
@ -179,6 +181,58 @@ def get_video_quality(params, quality_level_threshold=3):
|
|||||||
return None
|
return None
|
||||||
return str(max(eligible_videos, key=lambda x: int(x['qualityLevel']))['id'])
|
return str(max(eligible_videos, key=lambda x: int(x['qualityLevel']))['id'])
|
||||||
|
|
||||||
|
def get_data(video_id, real_url):
|
||||||
|
"""Get the server response for a given video ID"""
|
||||||
|
try:
|
||||||
|
logger.info(f"Fetching content from URL: {real_url}")
|
||||||
|
r = s.get(real_url, timeout=10)
|
||||||
|
r.raise_for_status()
|
||||||
|
except requests.RequestException as e:
|
||||||
|
logger.error(f"Error fetching the page for video ID '{video_id}': {e}")
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
soup = BeautifulSoup(r.text, "html.parser")
|
||||||
|
try:
|
||||||
|
server_response = soup.find("meta", {"name": "server-response"})
|
||||||
|
if server_response:
|
||||||
|
params = json.loads(server_response["content"])["data"]["response"] # type: ignore
|
||||||
|
return params, soup
|
||||||
|
except (KeyError, json.JSONDecodeError) as e:
|
||||||
|
logger.warning(f"Failed to extract thumbnail info for video ID '{video_id}': {e}")
|
||||||
|
pass
|
||||||
|
|
||||||
|
return None, soup
|
||||||
|
|
||||||
|
def get_oembed_url(params):
|
||||||
|
"""Get the oEmbed (/owoembed) URL based on the given params (server response)"""
|
||||||
|
author_id = params.get('owner', {}).get('id')
|
||||||
|
author_name = params.get('owner', {}).get('nickname')
|
||||||
|
video_id = params.get('video', {}).get('id')
|
||||||
|
|
||||||
|
if not video_id:
|
||||||
|
return None
|
||||||
|
|
||||||
|
view_count = str(params.get('video', {}).get('count', {}).get('view')) or "n/a"
|
||||||
|
comment_count = str(params.get('video', {}).get('count', {}).get('comment')) or "n/a"
|
||||||
|
like_count = str(params.get('video', {}).get('count', {}).get('like')) or "n/a"
|
||||||
|
mylist_count = str(params.get('video', {}).get('count', {}).get('mylist')) or "n/a"
|
||||||
|
provder_stats = f"👁️ {view_count} 💬 {comment_count} ❤️ {like_count} 📋 {mylist_count}"
|
||||||
|
|
||||||
|
author_name_encoded = urllib.parse.quote(author_name)
|
||||||
|
provider_stats_encoded = urllib.parse.quote(provder_stats)
|
||||||
|
|
||||||
|
oembed_url = (
|
||||||
|
f"{HOST}/owoembed?"
|
||||||
|
f"author_id={author_id}&"
|
||||||
|
f"author_name={author_name_encoded}&"
|
||||||
|
f"video_id={video_id}&"
|
||||||
|
f"provider={provider_stats_encoded}"
|
||||||
|
)
|
||||||
|
|
||||||
|
return oembed_url
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/watch/<video_id>")
|
@app.route("/watch/<video_id>")
|
||||||
def proxy(video_id):
|
def proxy(video_id):
|
||||||
logger.info(f"Received request for video ID: {video_id}")
|
logger.info(f"Received request for video ID: {video_id}")
|
||||||
@ -191,30 +245,14 @@ def proxy(video_id):
|
|||||||
|
|
||||||
# Not in cache or cache expired; fetch from nicovideo.jp
|
# Not in cache or cache expired; fetch from nicovideo.jp
|
||||||
real_url = f"https://www.nicovideo.jp/watch/{video_id}"
|
real_url = f"https://www.nicovideo.jp/watch/{video_id}"
|
||||||
try:
|
params, soup = get_data(video_id, real_url)
|
||||||
logger.info(f"Fetching content from URL: {real_url}")
|
thumbnail_url = (
|
||||||
r = s.get(real_url, timeout=10)
|
params["video"]["thumbnail"].get("ogp") or
|
||||||
r.raise_for_status()
|
params["video"]["thumbnail"].get("player") or
|
||||||
except requests.RequestException as e:
|
params["video"]["thumbnail"].get("largeUrl") or
|
||||||
logger.error(f"Error fetching the page for video ID '{video_id}': {e}")
|
params["video"]["thumbnail"].get("middleUrl") or
|
||||||
return Response(status=500)
|
params["video"]["thumbnail"].get("url")
|
||||||
|
) if params else None
|
||||||
soup = BeautifulSoup(r.text, "html.parser")
|
|
||||||
thumbnail_url = None
|
|
||||||
try:
|
|
||||||
server_response = soup.find("meta", {"name": "server-response"})
|
|
||||||
if server_response:
|
|
||||||
params = json.loads(server_response["content"])["data"]["response"] # type: ignore
|
|
||||||
thumbnail_url = (
|
|
||||||
params["video"]["thumbnail"].get("ogp") or
|
|
||||||
params["video"]["thumbnail"].get("player") or
|
|
||||||
params["video"]["thumbnail"].get("largeUrl") or
|
|
||||||
params["video"]["thumbnail"].get("middleUrl") or
|
|
||||||
params["video"]["thumbnail"].get("url")
|
|
||||||
)
|
|
||||||
except (KeyError, json.JSONDecodeError) as e:
|
|
||||||
logger.warning(f"Failed to extract thumbnail info for video ID '{video_id}': {e}")
|
|
||||||
pass
|
|
||||||
|
|
||||||
download_allowed = allow_download(params) if params else False
|
download_allowed = allow_download(params) if params else False
|
||||||
video_quality = get_video_quality(params) if params else None
|
video_quality = get_video_quality(params) if params else None
|
||||||
@ -254,6 +292,7 @@ def proxy(video_id):
|
|||||||
og_tags_str += f'\n<meta content="{og_video_width}" property="twitter:player:width"/>'
|
og_tags_str += f'\n<meta content="{og_video_width}" property="twitter:player:width"/>'
|
||||||
if og_video_height:
|
if og_video_height:
|
||||||
og_tags_str += f'\n<meta content="{og_video_height}" property="twitter:player:height"/>'
|
og_tags_str += f'\n<meta content="{og_video_height}" property="twitter:player:height"/>'
|
||||||
|
og_tags_str += f'\n<link rel="alternate" href="{get_oembed_url(params)}" type="application/json+oembed" title="{video_id}"/>'
|
||||||
html_response = f"""<!DOCTYPE html>
|
html_response = f"""<!DOCTYPE html>
|
||||||
<!--
|
<!--
|
||||||
niconico proxy - brought to you by https://mmaker.moe
|
niconico proxy - brought to you by https://mmaker.moe
|
||||||
@ -271,3 +310,38 @@ if you want to download videos, please consider using a tool like nndownload: ht
|
|||||||
cache.set(video_id, html_response, expire=CACHE_EXPIRATION_SECONDS)
|
cache.set(video_id, html_response, expire=CACHE_EXPIRATION_SECONDS)
|
||||||
|
|
||||||
return Response(html_response, mimetype="text/html")
|
return Response(html_response, mimetype="text/html")
|
||||||
|
|
||||||
|
@app.route("/owoembed")
|
||||||
|
def owoembed():
|
||||||
|
"""
|
||||||
|
Handles oEmbed requests with parameters in the URL
|
||||||
|
Returns JSON payload in oEmbed format
|
||||||
|
"""
|
||||||
|
logger.info("Received request for oEmbed endpoint")
|
||||||
|
|
||||||
|
# Get parameters from query string
|
||||||
|
author_id = request.args.get('author_id', '')
|
||||||
|
author_name = request.args.get('author_name', '')
|
||||||
|
video_id = request.args.get('video_id', '')
|
||||||
|
provider = request.args.get('provider', '')
|
||||||
|
|
||||||
|
author_name_decoded = urllib.parse.unquote(author_name)
|
||||||
|
provider_decoded = urllib.parse.unquote(provider)
|
||||||
|
|
||||||
|
# Create the author_url and provider_url
|
||||||
|
author_url = f"https://www.nicovideo.jp/user/{author_id}"
|
||||||
|
video_url = f"https://www.nicovideo.jp/watch/{video_id}"
|
||||||
|
|
||||||
|
# Create oEmbed response
|
||||||
|
oembed_response = {
|
||||||
|
"author_name": author_name_decoded,
|
||||||
|
"author_url": author_url,
|
||||||
|
"provider_name": provider_decoded,
|
||||||
|
"provider_url": video_url,
|
||||||
|
"title": "Embed",
|
||||||
|
"type": "link",
|
||||||
|
"version": "1.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(f"Returning oEmbed response for video ID: {video_id}")
|
||||||
|
return jsonify(oembed_response)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user