feat: add dailymotion support

closes #343
This commit is contained in:
dumbmoron
2024-02-21 00:50:48 +00:00
parent 77df90412b
commit a97733d257
8 changed files with 150 additions and 1 deletions

View File

@ -24,6 +24,7 @@ import pinterest from "./services/pinterest.js";
import streamable from "./services/streamable.js";
import twitch from "./services/twitch.js";
import rutube from "./services/rutube.js";
import dailymotion from "./services/dailymotion.js";
export default async function(host, patternMatch, url, lang, obj) {
assert(url instanceof URL);
@ -156,6 +157,9 @@ export default async function(host, patternMatch, url, lang, obj) {
isAudioOnly: isAudioOnly
});
break;
case "dailymotion":
r = await dailymotion(patternMatch);
break;
default:
return apiJSON(0, { t: errorUnsupported(lang) });
}

View File

@ -0,0 +1,107 @@
import HLSParser from 'hls-parser';
import { maxVideoDuration } from '../../config.js';
let _token;
function getExp(token) {
return JSON.parse(
Buffer.from(token.split('.')[1], 'base64')
).exp * 1000;
}
const getToken = async () => {
if (_token && getExp(_token) > new Date().getTime()) {
return _token;
}
const req = await fetch('https://graphql.api.dailymotion.com/oauth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
'User-Agent': 'dailymotion/240213162706 CFNetwork/1492.0.1 Darwin/23.3.0',
'Authorization': 'Basic MGQyZDgyNjQwOWFmOWU3MmRiNWQ6ODcxNmJmYTVjYmEwMmUwMGJkYTVmYTg1NTliNDIwMzQ3NzIyYWMzYQ=='
},
body: 'traffic_segment=&grant_type=client_credentials'
}).then(r => r.json()).catch(() => {});
if (req.access_token) {
return _token = req.access_token;
}
}
export default async function({ id }) {
const token = await getToken();
if (!token) return { error: 'ErrorSomethingWentWrong' };
const req = await fetch('https://graphql.api.dailymotion.com/',
{
method: 'POST',
headers: {
'User-Agent': 'dailymotion/240213162706 CFNetwork/1492.0.1 Darwin/23.3.0',
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
'X-DM-AppInfo-Version': '7.16.0_240213162706',
'X-DM-AppInfo-Type': 'iosapp',
'X-DM-AppInfo-Id': 'com.dailymotion.dailymotion'
},
body: JSON.stringify({
operationName: "Media",
query: `
query Media($xid: String!, $password: String) {
media(xid: $xid, password: $password) {
__typename
... on Video {
xid
hlsURL
duration
title
channel {
displayName
}
}
}
}
`,
variables: { xid: id }
})
}
).then(r => r.status === 200 && r.json()).catch(() => {});
const media = req?.data?.media;
if (media?.__typename !== 'Video' || !media.hlsURL) {
return { error: 'ErrorEmptyDownload' }
}
if (media.duration * 1000 > maxVideoDuration) {
return { error: ['ErrorLengthLimit', maxVideoDuration / 60000] };
}
const manifest = await fetch(media.hlsURL).then(r => r.text()).catch(() => {});
if (!manifest) return { error: 'ErrorSomethingWentWrong' };
const bestQuality = HLSParser.parse(manifest).variants
.filter(v => v.codecs.includes('avc1'))
.reduce((a, b) => a.bandwidth > b.bandwidth ? a : b);
if (!bestQuality) return { error: 'ErrorEmptyDownload' }
const fileMetadata = {
title: media.title,
artist: media.channel.displayName
}
return {
urls: bestQuality.uri,
isM3U8: true,
filenameAttributes: {
service: 'dailymotion',
id: media.xid,
title: fileMetadata.title,
author: fileMetadata.artist,
resolution: `${bestQuality.resolution.width}x${bestQuality.resolution.height}`,
qualityLabel: `${bestQuality.resolution.height}p`,
extension: 'mp4'
},
fileMetadata
}
}

View File

@ -109,6 +109,11 @@
"tld": "ru",
"patterns": ["video/:id", "play/embed/:id"],
"enabled": true
},
"dailymotion": {
"alias": "dailymotion videos",
"patterns": ["video/:id"],
"enabled": true
}
}
}

View File

@ -3,6 +3,8 @@ export const testers = {
patternMatch.comId?.length <= 12 || patternMatch.comShortLink?.length <= 16
|| patternMatch.tvId?.length <= 24,
"dailymotion": (patternMatch) => patternMatch.id?.length <= 32,
"instagram": (patternMatch) =>
patternMatch.postId?.length <= 12
|| (patternMatch.username?.length <= 30 && patternMatch.storyId?.length <= 24),

View File

@ -59,6 +59,11 @@ export function aliasURL(url) {
url = new URL(`https://bilibili.com/_shortLink/${parts[1]}`)
}
break;
case "dai":
if (url.hostname === 'dai.ly' && parts.length === 2) {
url = new URL(`https://dailymotion.com/video/${parts[1]}`)
}
}
return url