24 July 2024 - 1 minute
How to download videos & music from Newgrounds with magic.
Newgrounds is great site to see classic old internet history in the form of videos & music. Recently, I’ve decided to take on the task of adding support for Newgrounds media to cobalt. Here is how I was able to read Newgrounds pages to find media.
Let’s use this test video here: https://www.newgrounds.com/portal/view/938050
. I first decided to see my browser’s network tab, and clicking play shows this interesting request.
It looks like when you play a video, Newgrounds fetches this URL (noticed how view
changed into video
) to see playback information. Interesting enough, we need to pass in the header X-Requested-With: XMLHttpRequest
. If you do not pass in this header, you will not get back a JSON response. Let’s see what contents it has:
{
"id": 938050,
"title": "mmm... beeEEeeRrrr",
"author": "DonkTK",
"sources": {
"1080p": [
{
"type": "video\/mp4",
"src": "https:\/\/uploads.ungrounded.net\/alternate\/5847000\/5847247_alternate_270656.1080p.mp4?1720232179"
}
],
"720p": [
{
"type": "video\/mp4",
"src": "https:\/\/uploads.ungrounded.net\/alternate\/5847000\/5847247_alternate_270656.720p.mp4?1720232179"
}
],
"360p": [
{
"type": "video\/mp4",
"src": "https:\/\/uploads.ungrounded.net\/alternate\/5847000\/5847247_alternate_270656.360p.mp4?1720232179"
}
]
}
}
Perfect, we can see where the video is hosted and the qualities the video offers. Visiting any of the links in this request takes you to the direct video.
For this example, I am going to use this song: https://www.newgrounds.com/audio/listen/500476
. Handling audio downloads is a bit more tricky. However, inspecting the source code of an audio page reveals this interesting section:
<script>
var embed_controller = new embedController([{
"url": "https:\/\/audio.ngfiles.com\/500000\/500476_Stereo-Madness.mp3?f1355466273",
"is_published": true,
"portal_id": 2,
"file_id": 0,
"project_id": 599992,
"item_id": 500476,
"description": "Audio File",
"width": null,
"height": null,
"filesize": 7922486,
"params": {
"filename": "https:\/\/audio.ngfiles.com\/500000\/500476_Stereo-Madness.mp3?f1355466273",
"name": "Stereo%20Madness",
"length": "199",
"loop": 0,
"artist": "ForeverBound",
"icon": "https:\/\/aicon.ngfiles.com\/500\/500476.png?f1364657339",
"images": {
"condensed": {
"completed": {
"url": "\/\/img.ngfiles.com\/audio_peaks\/3\/500000\/500476.1355466273-1129.condensed.completed.png",
"rel_path": "img\/audio_peaks\/3\/500000\/500476.1355466273-1129.condensed.completed.png"
},
"playing": {
"url": "\/\/img.ngfiles.com\/audio_peaks\/3\/500000\/500476.1355466273-1129.condensed.png",
"rel_path": "img\/audio_peaks\/3\/500000\/500476.1355466273-1129.condensed.png"
}
},
"listen": {
"completed": {
"url": "\/\/img.ngfiles.com\/audio_peaks\/3\/500000\/500476.1355466273-1129.listen.completed.png",
"rel_path": "img\/audio_peaks\/3\/500000\/500476.1355466273-1129.listen.completed.png"
},
"playing": {
"url": "\/\/img.ngfiles.com\/audio_peaks\/3\/500000\/500476.1355466273-1129.listen.png",
"rel_path": "img\/audio_peaks\/3\/500000\/500476.1355466273-1129.listen.png"
}
}
},
"duration": 199
},
"portal_item_requirements": [5],
"html": "\n\n<div id=\"audio-listen-player\" class=\"audio-listen-player\">\n\t<div id=\"audio-listen-wrapper\" class=\"audio-listen-wrapper\">\n\n\t\t<div id=\"waveform\" class=\"audio-listen-container\"><\/div>\n\n\t\t<div class=\"outer-frame\"><\/div>\n\n\t\t<p id=\"cant-play-mp3\" style=\"display:none\">Your Browser does not support html5\/mp3 audio playback.!!!<\/p>\n\n\t\t<p id=\"loading-audio\">\n\t\t\t<em class=\"fa fa-spin fa-spinner\"><\/em> LOADING...\n\t\t<\/p>\n\t<\/div>\n\n\t<div class=\"audio-listen-controls\">\n\t\t<div class=\"play-controls\">\n\t\t\t<button class=\"audio-listen-btn\" id=\"audio-listen-play\" disabled>\n\t\t\t\t<i class=\"fa fa-play\"><\/i>\n\t\t\t<\/button>\n\n\t\t\t<button class=\"audio-listen-btn\" id=\"audio-listen-pause\" disabled>\n\t\t\t\t<i class=\"fa fa-pause\"><\/i>\n\t\t\t<\/button>\n\n\t\t<\/div>\n\t\t<div class=\"playback-info\">\n\t\t\t<span id=\"audio-listen-progress\">00.00<\/span>\n\t\t\t\/\n\t\t\t<span id=\"audio-listen-duration\">00.00<\/span>\n\t\t<\/div>\n\t\t<div class=\"sound-controls\">\n\t\t\t<button class=\"audio-listen-btn\" id=\"audio-listen-repeat\">\n\t\t\t\t<i class=\"fa fa-retweet\"><\/i>\n\t\t\t<\/button>\n\n\t\t\t\t\t\t\t<button class=\"audio-listen-btn\" id=\"audio-listen-volumeToggle\">\n\t\t\t\t\t<i class=\"fa fa-volume-off\"><\/i>\n\t\t\t\t<\/button>\n\n\t\t\t\t<div class=\"off\" id=\"audio-listen-volume\"><\/div>\n\t\t\t\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n",
callback: function() {
(function($) {
var player = NgAudioPlayer.fromListenPage({
'generic_id': 500476,
'type_id': 3,
'url': "https:\/\/audio.ngfiles.com\/500000\/500476_Stereo-Madness.mp3?f1355466273",
'version': 1355466273,
'duration': 199,
'loop': false,
'images': {
"condensed": {
"completed": {
"url": "\/\/img.ngfiles.com\/audio_peaks\/3\/500000\/500476.1355466273-1129.condensed.completed.png",
"rel_path": "img\/audio_peaks\/3\/500000\/500476.1355466273-1129.condensed.completed.png"
},
"playing": {
"url": "\/\/img.ngfiles.com\/audio_peaks\/3\/500000\/500476.1355466273-1129.condensed.png",
"rel_path": "img\/audio_peaks\/3\/500000\/500476.1355466273-1129.condensed.png"
}
},
"listen": {
"completed": {
"url": "\/\/img.ngfiles.com\/audio_peaks\/3\/500000\/500476.1355466273-1129.listen.completed.png",
"rel_path": "img\/audio_peaks\/3\/500000\/500476.1355466273-1129.listen.completed.png"
},
"playing": {
"url": "\/\/img.ngfiles.com\/audio_peaks\/3\/500000\/500476.1355466273-1129.listen.png",
"rel_path": "img\/audio_peaks\/3\/500000\/500476.1355466273-1129.listen.png"
}
}
},
'playlist': 'listen'
}, 128);
})(jQuery);
}
}], null, false, true);
</script>
In short, this script builds the media player’s metadata. The section we want to look at is the params
object in this JSON.
"params": {
"filename": "https:\/\/audio.ngfiles.com\/500000\/500476_Stereo-Madness.mp3?f1355466273",
"name": "Stereo%20Madness",
"length": "199",
"loop": 0,
"artist": "ForeverBound",
"icon": "https:\/\/aicon.ngfiles.com\/500\/500476.png?f1364657339",
"images": {
"condensed": {
"completed": {
"url": "\/\/img.ngfiles.com\/audio_peaks\/3\/500000\/500476.1355466273-1129.condensed.completed.png",
"rel_path": "img\/audio_peaks\/3\/500000\/500476.1355466273-1129.condensed.completed.png"
},
"playing": {
"url": "\/\/img.ngfiles.com\/audio_peaks\/3\/500000\/500476.1355466273-1129.condensed.png",
"rel_path": "img\/audio_peaks\/3\/500000\/500476.1355466273-1129.condensed.png"
}
},
"listen": {
"completed": {
"url": "\/\/img.ngfiles.com\/audio_peaks\/3\/500000\/500476.1355466273-1129.listen.completed.png",
"rel_path": "img\/audio_peaks\/3\/500000\/500476.1355466273-1129.listen.completed.png"
},
"playing": {
"url": "\/\/img.ngfiles.com\/audio_peaks\/3\/500000\/500476.1355466273-1129.listen.png",
"rel_path": "img\/audio_peaks\/3\/500000\/500476.1355466273-1129.listen.png"
}
}
},
"duration": 199
}
As you can see, the direct link to this file is the filename
key. Visiting the link reveals the direct mp3
file to listen or download.
This was a cool experiment to see how Newgrounds media is handled for cobalt. It was a lot of fun trying and having fun with this side project.