Skip to content

Commit

Permalink
feat: newgrounds video & audio support
Browse files Browse the repository at this point in the history
  • Loading branch information
hyperdefined committed Oct 23, 2024
1 parent a3ee3d9 commit 985bedf
Show file tree
Hide file tree
Showing 7 changed files with 218 additions and 2 deletions.
1 change: 1 addition & 0 deletions api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ this list is not final and keeps expanding over time. if support for a service y
| instagram ||||||
| facebook ||||||
| loom ||||||
| newgrounds ||||||
| ok.ru ||||||
| pinterest ||||||
| reddit ||||||
Expand Down
1 change: 1 addition & 0 deletions api/src/processing/match-action.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ export default function({ r, host, audioFormat, isAudioOnly, isAudioMuted, disab
case "ok":
case "vk":
case "tiktok":
case "newgrounds":
params = { type: "proxy" };
break;

Expand Down
12 changes: 12 additions & 0 deletions api/src/processing/match.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import snapchat from "./services/snapchat.js";
import loom from "./services/loom.js";
import facebook from "./services/facebook.js";
import bluesky from "./services/bluesky.js";
import newgrounds from "./services/newgrounds.js";

let freebind;

Expand Down Expand Up @@ -243,6 +244,17 @@ export default async function({ host, patternMatch, params }) {
});
break;

case "newgrounds":
r = await newgrounds({
type: patternMatch.type,
method: patternMatch.method,
id: patternMatch.id,
quality: params.videoQuality,
isAudioOnly,
isAudioMuted
});
break;

default:
return createResponse("error", {
code: "error.api.service.unsupported"
Expand Down
3 changes: 3 additions & 0 deletions api/src/processing/service-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ export const services = {
"url_shortener/:shortLink"
],
},
newgrounds: {
patterns: [":type/:method/:id"]
},
reddit: {
patterns: [
"r/:sub/comments/:id/:title",
Expand Down
5 changes: 5 additions & 0 deletions api/src/processing/service-patterns.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,9 @@ export const testers = {

"bsky": pattern =>
pattern.user?.length <= 128 && pattern.post?.length <= 128,

"newgrounds": (patternMatch) =>
(patternMatch.type == 'portal' && patternMatch.method == 'view')
|| (patternMatch.type == 'audio' && patternMatch.method == 'listen')
&& patternMatch.id?.length >= 1,
}
157 changes: 157 additions & 0 deletions api/src/processing/services/newgrounds.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import { genericUserAgent } from "../../config.js";
import { cleanString } from "../../misc/utils.js";

const qualities = ["4k", "1440p", "1080p", "720p", "480p", "360p", "240p", "144p"];

const qualityMatch = {
2160: "4k",
1440: "1440p",
1080: "1080p",
720: "720p",
480: "480p",
360: "360p",
240: "240p",
144: "144p"
}

function getQuality(sources, requestedQuality) {
if (requestedQuality == "max") {
for (let quality of qualities) {
if (sources[quality]) {
return {
src: sources[quality][0].src,
quality: quality,
type: sources[quality][0].type,
}
}
}
}

let videoData = sources[qualityMatch[requestedQuality]];
if (videoData) {
return {
src: videoData[0].src,
quality: requestedQuality + "p",
type: videoData[0].type,
}
}

const qualityIndex = qualities.indexOf(qualityMatch[requestedQuality]);
if (qualityIndex !== -1) {
for (let i = qualityIndex; i >= 0; i--) {
if (sources[qualities[i]]) {
return {
src: sources[qualities[i]][0].src,
quality: qualities[i],
type: sources[qualities[i]][0].type,
}
}
}
for (let i = qualityIndex + 1; i < qualities.length; i++) {
if (sources[qualities[i]]) {
return {
src: sources[qualities[i]][0].src,
quality: qualities[i],
type: sources[qualities[i]][0].type,
}
}
}
}

return null;
}

async function getVideo(obj) {
let req = await fetch(`https://www.newgrounds.com/portal/video/${obj.id}`, {
headers: {
'User-Agent': genericUserAgent,
'X-Requested-With': 'XMLHttpRequest',
}
})
.then(request => request.text())
.catch(() => {});

if (!req) return { error: 'ErrorCouldntFetch' };

let json;
try {
json = JSON.parse(req);
} catch { return { error: 'ErrorEmptyDownload' }; }

const videoData = getQuality(json.sources, obj.quality);
if (videoData == null) {
return { error: 'ErrorCouldntFetch' };
}
if (!videoData.type.includes('mp4')) {
return { error: 'ErrorCouldntFetch' };
}

let fileMetadata = {
title: cleanString(decodeURIComponent(json.title)),
artist: cleanString(decodeURIComponent(json.author)),
}

return {
urls: videoData.src,
filenameAttributes: {
service: "newgrounds",
id: obj.id,
title: fileMetadata.title,
author: fileMetadata.artist,
extension: 'mp4',
qualityLabel: videoData.quality,
resolution: videoData.quality
},
fileMetadata,
}
}

async function getMusic(obj) {
let req = await fetch(`https://www.newgrounds.com/audio/listen/${obj.id}`, {
headers: {
'User-Agent': genericUserAgent,
}
})
.then(request => request.text())
.catch(() => {});

if (!req) return { error: 'ErrorCouldntFetch' };

const titleMatch = req.match(/"name"\s*:\s*"([^"]+)"/);
const artistMatch = req.match(/"artist"\s*:\s*"([^"]+)"/);
const urlMatch = req.match(/"filename"\s*:\s*"([^"]+)"/);

if (!titleMatch || !artistMatch || !urlMatch) {
return { error: 'ErrorCouldntFetch' };
}

const title = titleMatch[1];
const artist = artistMatch[1];
const url = urlMatch[1].replace(/\\\//g, '/');
let fileMetadata = {
title: cleanString(decodeURIComponent(title.trim())),
artist: cleanString(decodeURIComponent(artist.trim())),
}

return {
urls: url,
filenameAttributes: {
service: "newgrounds",
id: obj.id,
title: fileMetadata.title,
author: fileMetadata.artist,
},
fileMetadata,
isAudioOnly: true
}
}

export default function(obj) {
if (obj.type == 'portal') {
return getVideo(obj);
}
if (obj.type == 'audio') {
return getMusic(obj);
}
return { error: 'ErrorUnsupported' };
}
41 changes: 39 additions & 2 deletions api/src/util/tests.json
Original file line number Diff line number Diff line change
Expand Up @@ -1493,5 +1493,42 @@
"status": "error"
}
}
]
}
],
"newgrounds": [{
"name": "regular video",
"url": "https://www.newgrounds.com/portal/view/938050",
"params": {},
"expected": {
"code": 200,
"status": "tunnel"
}
}, {
"name": "regular video (audio only)",
"url": "https://www.newgrounds.com/portal/view/938050",
"params": {
"downloadMode": "audio"
},
"expected": {
"code": 200,
"status": "tunnel"
}
}, {
"name": "regular video (muted)",
"url": "https://www.newgrounds.com/portal/view/938050",
"params": {
"downloadMode": "mute"
},
"expected": {
"code": 200,
"status": "tunnel"
}
}, {
"name": "regular music",
"url": "https://www.newgrounds.com/audio/listen/500476",
"params": {},
"expected": {
"code": 200,
"status": "tunnel"
}
}]
}

0 comments on commit 985bedf

Please sign in to comment.