mirror of
https://github.com/misskey-dev/summaly.git
synced 2025-05-09 23:57:20 +09:00
playerを使うように
This commit is contained in:
parent
84f96c3961
commit
4c45ed716e
15
README.md
15
README.md
@ -60,7 +60,7 @@ interface IPlugin {
|
|||||||
|
|
||||||
A Promise of an Object that contains properties below:
|
A Promise of an Object that contains properties below:
|
||||||
|
|
||||||
※ Almost all values are nullable. player shoud not be null.
|
※ Almost all values are nullable. player should not be null.
|
||||||
|
|
||||||
#### Root
|
#### Root
|
||||||
|
|
||||||
@ -77,18 +77,11 @@ A Promise of an Object that contains properties below:
|
|||||||
|
|
||||||
#### Player
|
#### Player
|
||||||
|
|
||||||
| Property | Type | Description |
|
|
||||||
| :-------------- | :------- | :--------------------------------------- |
|
|
||||||
| **url** | *string* | The url of the player |
|
|
||||||
| **width** | *number* | The width of the player |
|
|
||||||
| **height** | *number* | The height of the player |
|
|
||||||
|
|
||||||
#### oEmbed
|
|
||||||
|
|
||||||
| Property | Type | Description |
|
| Property | Type | Description |
|
||||||
| :-------------- | :--------- | :---------------------------------------------- |
|
| :-------------- | :--------- | :---------------------------------------------- |
|
||||||
| **src** | *string* | The source for the iframe |
|
| **url** | *string* | The url of the player |
|
||||||
| **height** | *number* | The height of the iframe |
|
| **width** | *number* | The width of the player |
|
||||||
|
| **height** | *number* | The height of the player |
|
||||||
| **allow** | *string[]* | The names of the allowed permissions for iframe |
|
| **allow** | *string[]* | The names of the allowed permissions for iframe |
|
||||||
|
|
||||||
Currently the possible items in `allow` are:
|
Currently the possible items in `allow` are:
|
||||||
|
@ -5,7 +5,7 @@ import cleanupTitle from './utils/cleanup-title.js';
|
|||||||
import { decode as decodeHtml } from 'html-entities';
|
import { decode as decodeHtml } from 'html-entities';
|
||||||
|
|
||||||
import { get, head, scpaping } from './utils/got.js';
|
import { get, head, scpaping } from './utils/got.js';
|
||||||
import type { default as Summary, OEmbedRichIframe } from './summary.js';
|
import type { default as Summary, Player } from './summary.js';
|
||||||
import * as cheerio from 'cheerio';
|
import * as cheerio from 'cheerio';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -14,7 +14,7 @@ import * as cheerio from 'cheerio';
|
|||||||
*
|
*
|
||||||
* Width should always be 100%.
|
* Width should always be 100%.
|
||||||
*/
|
*/
|
||||||
async function getOEmbedRich($: cheerio.CheerioAPI, pageUrl: string): Promise<OEmbedRichIframe | null> {
|
async function getOEmbedPlayer($: cheerio.CheerioAPI, pageUrl: string): Promise<Player | null> {
|
||||||
const href = $('link[type="application/json+oembed"]').attr('href');
|
const href = $('link[type="application/json+oembed"]').attr('href');
|
||||||
if (!href) {
|
if (!href) {
|
||||||
return null;
|
return null;
|
||||||
@ -29,7 +29,7 @@ async function getOEmbedRich($: cheerio.CheerioAPI, pageUrl: string): Promise<OE
|
|||||||
} catch {}
|
} catch {}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
if (!body || body.version !== '1.0' || body.type !== 'rich') {
|
if (!body || body.version !== '1.0' || !['rich', 'video'].includes(body.type)) {
|
||||||
// Not a well formed rich oEmbed
|
// Not a well formed rich oEmbed
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -52,15 +52,14 @@ async function getOEmbedRich($: cheerio.CheerioAPI, pageUrl: string): Promise<OE
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const src = iframe.attr('src');
|
const url = iframe.attr('src');
|
||||||
if (!src) {
|
if (!url) {
|
||||||
// No src?
|
// No src?
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: Use global URL object instead of the deprecated `node:url`
|
// XXX: Use global URL object instead of the deprecated `node:url`
|
||||||
const url = URL.parse(src);
|
if (URL.parse(url).protocol !== 'https:') {
|
||||||
if (url.protocol !== 'https:') {
|
|
||||||
// Allow only HTTPS for best security
|
// Allow only HTTPS for best security
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -87,7 +86,8 @@ async function getOEmbedRich($: cheerio.CheerioAPI, pageUrl: string): Promise<OE
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
src,
|
url,
|
||||||
|
width: null,
|
||||||
height,
|
height,
|
||||||
allow: allowedFeatures
|
allow: allowedFeatures
|
||||||
}
|
}
|
||||||
@ -199,8 +199,7 @@ export default async (url: URL.Url, lang: string | null = null): Promise<Summary
|
|||||||
|
|
||||||
const [icon, oEmbed] = await Promise.all([
|
const [icon, oEmbed] = await Promise.all([
|
||||||
getIcon(),
|
getIcon(),
|
||||||
// playerあるならoEmbedは必要ない
|
getOEmbedPlayer($, url.href),
|
||||||
!playerUrl ? getOEmbedRich($, url.href) : null,
|
|
||||||
])
|
])
|
||||||
|
|
||||||
// Clean up the title
|
// Clean up the title
|
||||||
@ -215,13 +214,13 @@ export default async (url: URL.Url, lang: string | null = null): Promise<Summary
|
|||||||
icon: icon || null,
|
icon: icon || null,
|
||||||
description: description || null,
|
description: description || null,
|
||||||
thumbnail: image || null,
|
thumbnail: image || null,
|
||||||
player: {
|
player: oEmbed ?? {
|
||||||
url: playerUrl || null,
|
url: playerUrl || null,
|
||||||
width: Number.isNaN(playerWidth) ? null : playerWidth,
|
width: Number.isNaN(playerWidth) ? null : playerWidth,
|
||||||
height: Number.isNaN(playerHeight) ? null : playerHeight
|
height: Number.isNaN(playerHeight) ? null : playerHeight,
|
||||||
|
allow: ['fullscreen', 'encrypted-media'],
|
||||||
},
|
},
|
||||||
sitename: siteName || null,
|
sitename: siteName || null,
|
||||||
sensitive,
|
sensitive,
|
||||||
oEmbed,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -33,11 +33,6 @@ type Summary = {
|
|||||||
* Possibly sensitive
|
* Possibly sensitive
|
||||||
*/
|
*/
|
||||||
sensitive?: boolean;
|
sensitive?: boolean;
|
||||||
|
|
||||||
/**
|
|
||||||
* The iframe information of oEmbed data from that web page
|
|
||||||
*/
|
|
||||||
oEmbed: OEmbedRichIframe | null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Summary;
|
export default Summary;
|
||||||
@ -57,25 +52,9 @@ export type Player = {
|
|||||||
* The height of the player
|
* The height of the player
|
||||||
*/
|
*/
|
||||||
height: number | null;
|
height: number | null;
|
||||||
};
|
|
||||||
|
/**
|
||||||
/**
|
* The allowed permissions of the iframe
|
||||||
* Extracted iframe information from OEmbed html field.
|
*/
|
||||||
* `width` is omitted here as it should always be 100%.
|
allow: string[];
|
||||||
*/
|
|
||||||
export type OEmbedRichIframe = {
|
|
||||||
/**
|
|
||||||
* The src of the iframe
|
|
||||||
*/
|
|
||||||
src: string,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The height of the iframe
|
|
||||||
*/
|
|
||||||
height: number,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The allowed feature list of the iframe
|
|
||||||
*/
|
|
||||||
allow: string[],
|
|
||||||
};
|
};
|
||||||
|
@ -213,6 +213,7 @@ describe('TwitterCard', () => {
|
|||||||
|
|
||||||
const summary = await summaly(host);
|
const summary = await summaly(host);
|
||||||
expect(summary.player.url).toBe('https://example.com/embedurl');
|
expect(summary.player.url).toBe('https://example.com/embedurl');
|
||||||
|
expect(summary.player.allow).toStrictEqual(['fullscreen', 'encrypted-media']);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Player detection - Pleroma:video => video', async () => {
|
test('Player detection - Pleroma:video => video', async () => {
|
||||||
@ -224,6 +225,7 @@ describe('TwitterCard', () => {
|
|||||||
|
|
||||||
const summary = await summaly(host);
|
const summary = await summaly(host);
|
||||||
expect(summary.player.url).toBe('https://example.com/embedurl');
|
expect(summary.player.url).toBe('https://example.com/embedurl');
|
||||||
|
expect(summary.player.allow).toStrictEqual(['fullscreen', 'encrypted-media']);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Player detection - Pleroma:image => image', async () => {
|
test('Player detection - Pleroma:image => image', async () => {
|
||||||
@ -256,44 +258,44 @@ describe("oEmbed", () => {
|
|||||||
test(`Invalidity test: ${filename}`, async () => {
|
test(`Invalidity test: ${filename}`, async () => {
|
||||||
await setUpFastify(`invalid/${filename}`);
|
await setUpFastify(`invalid/${filename}`);
|
||||||
const summary = await summaly(host);
|
const summary = await summaly(host);
|
||||||
expect(summary.oEmbed).toBe(null);
|
expect(summary.player.url).toBe(null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
test('src', async () => {
|
test('src', async () => {
|
||||||
await setUpFastify('oembed.json');
|
await setUpFastify('oembed.json');
|
||||||
const summary = await summaly(host);
|
const summary = await summaly(host);
|
||||||
expect(summary.oEmbed?.src).toBe('https://example.com/');
|
expect(summary.player.url).toBe('https://example.com/');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('max height', async () => {
|
test('max height', async () => {
|
||||||
await setUpFastify('oembed-too-tall.json');
|
await setUpFastify('oembed-too-tall.json');
|
||||||
const summary = await summaly(host);
|
const summary = await summaly(host);
|
||||||
expect(summary.oEmbed?.height).toBe(1024);
|
expect(summary.player.height).toBe(1024);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('children are ignored', async () => {
|
test('children are ignored', async () => {
|
||||||
await setUpFastify('oembed-iframe-child.json');
|
await setUpFastify('oembed-iframe-child.json');
|
||||||
const summary = await summaly(host);
|
const summary = await summaly(host);
|
||||||
expect(summary.oEmbed?.src).toBe('https://example.com/');
|
expect(summary.player.url).toBe('https://example.com/');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('allows fullscreen', async () => {
|
test('allows fullscreen', async () => {
|
||||||
await setUpFastify('oembed-allow-fullscreen.json');
|
await setUpFastify('oembed-allow-fullscreen.json');
|
||||||
const summary = await summaly(host);
|
const summary = await summaly(host);
|
||||||
expect(summary.oEmbed?.src).toBe('https://example.com/');
|
expect(summary.player.url).toBe('https://example.com/');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('allows safelisted features', async () => {
|
test('allows safelisted features', async () => {
|
||||||
await setUpFastify('oembed-allow-safelisted-features.json');
|
await setUpFastify('oembed-allow-safelisted-features.json');
|
||||||
const summary = await summaly(host);
|
const summary = await summaly(host);
|
||||||
expect(summary.oEmbed?.src).toBe('https://example.com/');
|
expect(summary.player.url).toBe('https://example.com/');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('oEmbed with relative path', async () => {
|
test('oEmbed with relative path', async () => {
|
||||||
await setUpFastify('oembed.json', 'htmls/oembed-relative.html');
|
await setUpFastify('oembed.json', 'htmls/oembed-relative.html');
|
||||||
const summary = await summaly(host);
|
const summary = await summaly(host);
|
||||||
expect(summary.oEmbed?.src).toBe('https://example.com/');
|
expect(summary.player.url).toBe('https://example.com/');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('oEmbed with nonexistent path', async () => {
|
test('oEmbed with nonexistent path', async () => {
|
||||||
@ -309,21 +311,21 @@ describe("oEmbed", () => {
|
|||||||
test('oEmbed with OpenGraph', async () => {
|
test('oEmbed with OpenGraph', async () => {
|
||||||
await setUpFastify('oembed.json', 'htmls/oembed-and-og.html');
|
await setUpFastify('oembed.json', 'htmls/oembed-and-og.html');
|
||||||
const summary = await summaly(host);
|
const summary = await summaly(host);
|
||||||
expect(summary.oEmbed?.src).toBe('https://example.com/');
|
expect(summary.player.url).toBe('https://example.com/');
|
||||||
expect(summary.description).toBe('blobcats rule the world');
|
expect(summary.description).toBe('blobcats rule the world');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Invalid oEmbed with valid OpenGraph', async () => {
|
test('Invalid oEmbed with valid OpenGraph', async () => {
|
||||||
await setUpFastify('invalid/oembed-insecure.json', 'htmls/oembed-and-og.html');
|
await setUpFastify('invalid/oembed-insecure.json', 'htmls/oembed-and-og.html');
|
||||||
const summary = await summaly(host);
|
const summary = await summaly(host);
|
||||||
expect(summary.oEmbed).toBe(null);
|
expect(summary.player.url).toBe(null);
|
||||||
expect(summary.description).toBe('blobcats rule the world');
|
expect(summary.description).toBe('blobcats rule the world');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('oEmbed with og:video', async () => {
|
test('oEmbed with og:video', async () => {
|
||||||
await setUpFastify('oembed.json', 'htmls/oembed-and-og-video.html');
|
await setUpFastify('oembed.json', 'htmls/oembed-and-og-video.html');
|
||||||
const summary = await summaly(host);
|
const summary = await summaly(host);
|
||||||
expect(summary.oEmbed).toBe(null);
|
expect(summary.player.url).toBe('https://example.com/');
|
||||||
expect(summary.player.url).toBe('https://example.com/embedurl');
|
expect(summary.player.allow).toStrictEqual([]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user