2023-12-14 23:03:41 +00:00
|
|
|
import { services } from "../config.js";
|
2023-12-09 11:00:54 +00:00
|
|
|
import { strict as assert } from "node:assert";
|
|
|
|
import psl from "psl";
|
2023-08-20 11:11:16 +01:00
|
|
|
|
2023-12-09 11:00:54 +00:00
|
|
|
export function aliasURL(url) {
|
|
|
|
assert(url instanceof URL);
|
|
|
|
|
|
|
|
const host = psl.parse(url.hostname);
|
|
|
|
const parts = url.pathname.split('/');
|
|
|
|
|
|
|
|
switch (host.sld) {
|
2023-08-25 19:03:24 +01:00
|
|
|
case "youtube":
|
2023-12-09 11:00:54 +00:00
|
|
|
if (url.pathname.startsWith('/live/') || url.pathname.startsWith('/shorts/')) {
|
|
|
|
url.pathname = '/watch';
|
2023-12-14 23:37:10 +00:00
|
|
|
// parts := ['', 'live' || 'shorts', id, ...rest]
|
2023-12-09 11:00:54 +00:00
|
|
|
url.search = `?v=${encodeURIComponent(parts[2])}`
|
2023-09-16 11:27:53 +01:00
|
|
|
}
|
2023-08-25 19:03:24 +01:00
|
|
|
break;
|
2023-08-20 11:11:16 +01:00
|
|
|
case "youtu":
|
2023-12-12 23:19:01 +00:00
|
|
|
if (url.hostname === 'youtu.be' && parts.length >= 2) {
|
2023-12-09 11:00:54 +00:00
|
|
|
/* youtu.be urls can be weird, e.g. https://youtu.be/<id>//asdasd// still works
|
|
|
|
** but we only care about the 1st segment of the path */
|
|
|
|
url = new URL(`https://youtube.com/watch?v=${
|
|
|
|
encodeURIComponent(parts[1])
|
|
|
|
}`)
|
2023-08-20 11:11:16 +01:00
|
|
|
}
|
|
|
|
break;
|
2023-12-09 11:00:54 +00:00
|
|
|
|
2023-08-20 11:11:16 +01:00
|
|
|
case "vxtwitter":
|
2023-12-25 12:04:52 +00:00
|
|
|
case "fixvx":
|
2023-08-20 11:11:16 +01:00
|
|
|
case "x":
|
2023-12-25 12:04:52 +00:00
|
|
|
if (services.twitter.altDomains.includes(url.hostname)) {
|
2023-12-09 11:00:54 +00:00
|
|
|
url.hostname = 'twitter.com'
|
2023-08-20 11:11:16 +01:00
|
|
|
}
|
|
|
|
break;
|
2023-12-09 11:00:54 +00:00
|
|
|
|
2023-09-16 11:27:53 +01:00
|
|
|
case "twitch":
|
2023-12-09 11:00:54 +00:00
|
|
|
if (url.hostname === 'clips.twitch.tv' && parts.length >= 2) {
|
|
|
|
url = new URL(`https://twitch.tv/_/clip/${parts[1]}`);
|
2023-09-16 11:27:53 +01:00
|
|
|
}
|
|
|
|
break;
|
2023-08-20 11:11:16 +01:00
|
|
|
}
|
2023-12-09 11:00:54 +00:00
|
|
|
|
2023-12-14 23:14:22 +00:00
|
|
|
return url
|
2023-12-09 11:00:54 +00:00
|
|
|
}
|
|
|
|
|
2023-12-14 23:14:22 +00:00
|
|
|
export function cleanURL(url) {
|
2023-12-09 11:00:54 +00:00
|
|
|
assert(url instanceof URL);
|
2023-12-14 23:14:22 +00:00
|
|
|
const host = psl.parse(url.hostname).sld;
|
2023-12-09 11:00:54 +00:00
|
|
|
let stripQuery = true;
|
|
|
|
|
|
|
|
if (host === 'pinterest') {
|
|
|
|
url.hostname = 'pinterest.com'
|
|
|
|
} else if (host === 'vk' && url.pathname.includes('/clip')) {
|
|
|
|
if (url.searchParams.get('z'))
|
|
|
|
url.search = '?z=' + encodeURIComponent(url.searchParams.get('z'));
|
|
|
|
stripQuery = false;
|
|
|
|
} else if (host === 'youtube' && url.searchParams.get('v')) {
|
|
|
|
url.search = '?v=' + encodeURIComponent(url.searchParams.get('v'));
|
|
|
|
stripQuery = false;
|
2023-08-20 11:11:16 +01:00
|
|
|
}
|
2023-12-09 11:00:54 +00:00
|
|
|
|
|
|
|
if (stripQuery) {
|
2023-12-14 23:57:00 +00:00
|
|
|
url.search = ''
|
2023-12-09 11:00:54 +00:00
|
|
|
}
|
|
|
|
|
2023-12-14 23:57:00 +00:00
|
|
|
url.username = url.password = url.port = url.hash = ''
|
|
|
|
|
2023-12-09 11:00:54 +00:00
|
|
|
if (url.pathname.endsWith('/'))
|
|
|
|
url.pathname = url.pathname.slice(0, -1);
|
|
|
|
|
|
|
|
return url
|
|
|
|
}
|
|
|
|
|
|
|
|
export function normalizeURL(url) {
|
|
|
|
return cleanURL(
|
|
|
|
aliasURL(
|
|
|
|
new URL(url.replace(/^https\/\//, 'https://'))
|
|
|
|
)
|
|
|
|
);
|
2023-08-20 11:11:16 +01:00
|
|
|
}
|
2023-12-09 11:00:54 +00:00
|
|
|
|
2023-12-14 22:51:24 +00:00
|
|
|
export function getHostIfValid(url) {
|
2023-12-09 11:00:54 +00:00
|
|
|
const host = psl.parse(url.hostname);
|
2023-12-14 22:51:24 +00:00
|
|
|
if (host.error) return;
|
2023-12-09 11:00:54 +00:00
|
|
|
|
|
|
|
const service = services[host.sld];
|
2023-12-14 22:51:24 +00:00
|
|
|
if (!service) return;
|
|
|
|
if ((service.tld ?? 'com') !== host.tld) return;
|
2023-12-09 11:00:54 +00:00
|
|
|
|
|
|
|
const anySubdomainAllowed = service.subdomains === '*';
|
|
|
|
const validSubdomain = [null, 'www', ...(service.subdomains ?? [])].includes(host.subdomain);
|
2023-12-14 22:51:24 +00:00
|
|
|
if (!validSubdomain && !anySubdomainAllowed) return;
|
2023-12-09 11:00:54 +00:00
|
|
|
|
2023-12-14 22:51:24 +00:00
|
|
|
return host.sld;
|
2023-12-09 11:00:54 +00:00
|
|
|
}
|