ニコニコ動画の試聴URLからサムネイル画像URLを取得するAPI
from ✅ニコニコのURLからサムネイル画像を貼り付けるUserScriptを作る
lambdaにデプロイしたコード
code:hello-v2.ts
import {
APIGatewayProxyEventV2,
APIGatewayProxyResultV2,
Context,
} from "https://deno.land/x/lambda/mod.ts";
import {
DOMParser,
Element,
} from "https://deno.land/x/deno_dom/deno-dom-wasm.ts";
async function getThumUrlAndTitle(url: string): Promise<string[]> {
let res = await fetch(url);
let text = await res.text();
let doc = new DOMParser().parseFromString(text, "text/html");
// HTMLからog:imageを取得する
if (doc) {
const meta = doc.querySelector('metaproperty="og:image"');
if (meta) {
const thumbUrl = meta.getAttribute("content");
if (thumbUrl) {
const titleNode = doc.querySelector("head > title");
const title = titleNode ? titleNode.textContent : "";
return thumbUrl, title;
}
}
}
// fallback: 動画IDを抽出してXML APIから取得する
const videoIdMatch = url.match(/watch\/(^/?+)/);
if (!videoIdMatch) {
throw new Error("動画IDを抽出できませんでした");
}
const videoId = videoIdMatch1;
const fallbackUrl = https://ext.nicovideo.jp/api/getthumbinfo/${videoId};
res = await fetch(fallbackUrl);
text = await res.text();
// "text/xml" は未実装のため "text/html" を使用
const xmlDoc = new DOMParser().parseFromString(text, "text/html");
if (!xmlDoc) {
throw new Error("XMLのパースに失敗しました");
}
const thumbNode = xmlDoc.querySelector("thumbnail_url");
if (!thumbNode || !thumbNode.textContent) {
throw new Error("XMLからthumbnail_urlを取得できませんでした");
}
const thumbUrlFallback = thumbNode.textContent;
const titleNodeFallback = xmlDoc.querySelector("title");
const titleFallback = titleNodeFallback ? titleNodeFallback.textContent : "";
return thumbUrlFallback, titleFallback;
}
// deno-lint-ignore require-await
export async function handler(
_event: APIGatewayProxyEventV2,
_context: Context,
): Promise<APIGatewayProxyResultV2> {
const thumbnailUrl, title = await getThumUrlAndTitle(_event.queryStringParameters.watchUrl);
return {
statusCode: 200,
headers: { "content-type": "application/json;charset=utf8" },
body: JSON.stringify({
"thumbnailUrl": thumbnailUrl,
"title": title
}),
};
}
code:hello.ts
import {
APIGatewayProxyEventV2,
APIGatewayProxyResultV2,
Context,
} from "https://deno.land/x/lambda/mod.ts";
import {
DOMParser,
Element,
} from "https://deno.land/x/deno_dom/deno-dom-wasm.ts";
async function getThumUrlAndTitle(url: string): Promise<string[]> {
const res = await fetch(url);
const doc = new DOMParser().parseFromString(await res.text(), "text/html");
if (doc == null) {
throw new Error("document not found");
}
const node = doc.querySelectorAll('metaproperty="og:image"')0 as Element;
const thumbUrl = node.getAttribute("content");
if (thumbUrl == null) {
throw new Error("thumbnailUrl not found");
}
// タイトルがないことはない
const titleNode = doc.querySelectorAll('head > title')0 as Element;
return thumbUrl, titleNode.textContent;
}
// deno-lint-ignore require-await
export async function handler(
_event: APIGatewayProxyEventV2,
_context: Context,
): Promise<APIGatewayProxyResultV2> {
const thumbnailUrl, title = await getThumUrlAndTitle(_event.queryStringParameters.watchUrl);
return {
statusCode: 200,
headers: { "content-type": "application/json;charset=utf8" },
body: JSON.stringify({
"thumbnailUrl": ${thumbnailUrl},
"title": ${title}
}),
};
}
https://ap-northeast-1.console.aws.amazon.com/lambda/home?region=ap-northeast-1#/functions/getNicovideoThumbnailUrl?tab=code にデプロイされている