@ -7,7 +7,7 @@ var path = require('path');
var youtubedl = require ( 'youtube-dl' ) ;
var ffmpeg = require ( 'fluent-ffmpeg' ) ;
var compression = require ( 'compression' ) ;
var https = require ( 'https' ) ;
var glob = require ( "glob" )
var multer = require ( 'multer' ) ;
var express = require ( "express" ) ;
var bodyParser = require ( "body-parser" ) ;
@ -1146,12 +1146,29 @@ async function downloadFileByURL_exec(url, type, options, sessionID = null) {
type : type ,
percent _complete : 0 ,
is _playlist : url . includes ( 'playlist' ) ,
timestamp _start : Date . now ( )
timestamp _start : Date . now ( ) ,
filesize : null
} ;
const download = downloads [ session ] [ download _uid ] ;
updateDownloads ( ) ;
// get video info prior to download
const info = await getVideoInfoByURL ( url , downloadConfig , download ) ;
if ( ! info ) {
resolve ( false ) ;
return ;
} else {
// store info in download for future use
download [ '_filename' ] = info [ '_filename' ] ; // .substring(fileFolderPath.length, info['_filename'].length-4);
download [ 'filesize' ] = utils . getExpectedFileSize ( info ) ;
}
const download _checker = setInterval ( ( ) => checkDownloadPercent ( download ) , 1000 ) ;
// download file
youtubedl . exec ( url , downloadConfig , { } , function ( err , output ) {
clearInterval ( download _checker ) ; // stops the download checker from running as the download finished (or errored)
download [ 'downloading' ] = false ;
download [ 'timestamp_end' ] = Date . now ( ) ;
var file _uid = null ;
@ -1164,7 +1181,7 @@ async function downloadFileByURL_exec(url, type, options, sessionID = null) {
download [ 'error' ] = err . stderr ;
updateDownloads ( ) ;
resolve ( false ) ;
throw err ;
return ;
} else if ( output ) {
if ( output . length === 0 || output [ 0 ] . length === 0 ) {
download [ 'error' ] = 'No output. Check if video already exists in your archive.' ;
@ -1407,7 +1424,7 @@ async function generateArgs(url, type, options) {
var youtubePassword = options . youtubePassword ;
let downloadConfig = null ;
let qualityPath = ( is _audio && ! options . skip _audio _args ) ? [ '-f' , 'bestaudio' ] : [ '-f' , 'best [ext=mp4] '] ;
let qualityPath = ( is _audio && ! options . skip _audio _args ) ? [ '-f' , 'bestaudio' ] : [ '-f' , 'best video+bestaudio', '--merge-output-format' , 'mp4 '] ;
const is _youtube = url . includes ( 'youtu' ) ;
if ( ! is _audio && ! is _youtube ) {
// tiktok videos fail when using the default format
@ -1485,6 +1502,10 @@ async function generateArgs(url, type, options) {
downloadConfig . push ( '--download-archive' , merged _path ) ;
}
if ( config _api . getConfigItem ( 'ytdl_include_thumbnail' ) ) {
downloadConfig . push ( '--write-thumbnail' ) ;
}
if ( globalArgs && globalArgs !== '' ) {
// adds global args
if ( downloadConfig . indexOf ( '-o' ) !== - 1 && globalArgs . split ( ',,' ) . indexOf ( '-o' ) !== - 1 ) {
@ -1497,11 +1518,36 @@ async function generateArgs(url, type, options) {
}
logger . verbose ( ` youtube-dl args being used: ${ downloadConfig . join ( ',' ) } ` ) ;
// downloadConfig.map((arg) => `"${arg}"`);
resolve ( downloadConfig ) ;
} ) ;
}
async function getVideoInfoByURL ( url , args = [ ] , download = null ) {
return new Promise ( resolve => {
// remove bad args
const new _args = [ ... args ] ;
const archiveArgIndex = new _args . indexOf ( '--download-archive' ) ;
if ( archiveArgIndex !== - 1 ) {
new _args . splice ( archiveArgIndex , 2 ) ;
}
// actually get info
youtubedl . getInfo ( url , new _args , ( err , output ) => {
if ( output ) {
resolve ( output ) ;
} else {
logger . error ( ` Error while retrieving info on video with URL ${ url } with the following message: ${ err } ` ) ;
if ( download ) {
download [ 'error' ] = ` Failed pre-check for video info: ${ err } ` ;
updateDownloads ( ) ;
}
resolve ( null ) ;
}
} ) ;
} ) ;
}
// currently only works for single urls
async function getUrlInfos ( urls ) {
let startDate = Date . now ( ) ;
@ -1559,47 +1605,26 @@ function updateDownloads() {
db . assign ( { downloads : downloads } ) . write ( ) ;
}
/ *
function checkDownloads ( ) {
for ( let [ session _id , session _downloads ] of Object . entries ( downloads ) ) {
for ( let [ download _uid , download _obj ] of Object . entries ( session _downloads ) ) {
if ( download _obj && ! download _obj [ 'complete' ] && ! download _obj [ 'error' ]
&& download _obj . timestamp _start > timestamp _server _start ) {
// download is still running (presumably)
download _obj . percent _complete = getDownloadPercent ( download _obj ) ;
}
}
}
}
* /
function checkDownloadPercent ( download ) {
const file _id = download [ 'file_id' ] ;
const filename = path . format ( path . parse ( download [ '_filename' ] . substring ( 0 , download [ '_filename' ] . length - 4 ) ) ) ;
const resulting _file _size = download [ 'filesize' ] ;
function getDownloadPercent ( download _obj ) {
if ( ! download _obj . final _size ) {
if ( fs . existsSync ( download _obj . expected _json _path ) ) {
const file _json = JSON . parse ( fs . readFileSync ( download _obj . expected _json _path , 'utf8' ) ) ;
let calculated _filesize = null ;
if ( file _json [ 'format_id' ] ) {
calculated _filesize = 0 ;
const formats _used = file _json [ 'format_id' ] . split ( '+' ) ;
for ( let i = 0 ; i < file _json [ 'formats' ] . length ; i ++ ) {
if ( formats _used . includes ( file _json [ 'formats' ] [ i ] [ 'format_id' ] ) ) {
calculated _filesize += file _json [ 'formats' ] [ i ] [ 'filesize' ] ;
}
}
}
download _obj . final _size = calculated _filesize ;
} else {
console . log ( 'could not find json file' ) ;
}
glob ( ` ${ filename } * ` , ( err , files ) => {
let sum _size = 0 ;
files . forEach ( file => {
try {
const file _stats = fs . statSync ( file ) ;
if ( file _stats && file _stats . size ) {
sum _size += file _stats . size ;
}
if ( fs . existsSync ( download _obj . expected _path ) ) {
const stats = fs . statSync ( download _obj . expected _path ) ;
const size = stats . size ;
return ( size / download _obj . final _size ) * 100 ;
} else {
console . log ( 'could not find file' ) ;
return 0 ;
} catch ( e ) {
}
} ) ;
download [ 'percent_complete' ] = ( sum _size / resulting _file _size * 100 ) . toFixed ( 2 ) ;
updateDownloads ( ) ;
} ) ;
}
// youtube-dl functions
@ -1821,7 +1846,7 @@ app.post('/api/tomp3', optionalJwt, async function(req, res) {
const is _playlist = url . includes ( 'playlist' ) ;
let result _obj = null ;
if ( safeDownloadOverride || is _playlist || options . customQualityConfiguration || options . customArgs || options . maxBitrate )
if ( true || safeDownloadOverride || is _playlist || options . customQualityConfiguration || options . customArgs || options . maxBitrate )
result _obj = await downloadFileByURL _exec ( url , 'audio' , options , req . query . sessionID ) ;
else
result _obj = await downloadFileByURL _normal ( url , 'audio' , options , req . query . sessionID ) ;
@ -1833,6 +1858,7 @@ app.post('/api/tomp3', optionalJwt, async function(req, res) {
} ) ;
app . post ( '/api/tomp4' , optionalJwt , async function ( req , res ) {
req . setTimeout ( 0 ) ; // remove timeout in case of long videos
var url = req . body . url ;
var options = {
customArgs : req . body . customArgs ,
@ -1850,7 +1876,7 @@ app.post('/api/tomp4', optionalJwt, async function(req, res) {
const is _playlist = url . includes ( 'playlist' ) ;
let result _obj = null ;
if ( safeDownloadOverride || is _playlist || options . customQualityConfiguration || options . customArgs || options . selectedHeight || ! url . includes ( 'youtu' ) )
if ( true || safeDownloadOverride || is _playlist || options . customQualityConfiguration || options . customArgs || options . selectedHeight || ! url . includes ( 'youtu' ) )
result _obj = await downloadFileByURL _exec ( url , 'video' , options , req . query . sessionID ) ;
else
result _obj = await downloadFileByURL _normal ( url , 'video' , options , req . query . sessionID ) ;
@ -1878,6 +1904,12 @@ app.get('/api/getMp3s', optionalJwt, function(req, res) {
playlists = auth _api . getUserPlaylists ( req . user . uid , 'audio' ) ;
}
// add thumbnails if present
mp3s . forEach ( mp3 => {
if ( mp3 [ 'thumbnailPath' ] && fs . existsSync ( mp3 [ 'thumbnailPath' ] ) )
mp3 [ 'thumbnailBlob' ] = fs . readFileSync ( mp3 [ 'thumbnailPath' ] ) ;
} ) ;
res . send ( {
mp3s : mp3s ,
playlists : playlists
@ -1897,6 +1929,12 @@ app.get('/api/getMp4s', optionalJwt, function(req, res) {
playlists = auth _api . getUserPlaylists ( req . user . uid , 'video' ) ;
}
// add thumbnails if present
mp4s . forEach ( mp4 => {
if ( mp4 [ 'thumbnailPath' ] && fs . existsSync ( mp4 [ 'thumbnailPath' ] ) )
mp4 [ 'thumbnailBlob' ] = fs . readFileSync ( mp4 [ 'thumbnailPath' ] ) ;
} ) ;
res . send ( {
mp4s : mp4s ,
playlists : playlists
@ -1981,6 +2019,12 @@ app.post('/api/getAllFiles', optionalJwt, function (req, res) {
files = files . concat ( sub . videos ) ;
}
// add thumbnails if present
files . forEach ( file => {
if ( file [ 'thumbnailPath' ] && fs . existsSync ( file [ 'thumbnailPath' ] ) )
file [ 'thumbnailBlob' ] = fs . readFileSync ( file [ 'thumbnailPath' ] ) ;
} ) ;
res . send ( {
files : files ,
playlists : playlists