@ -79,9 +79,9 @@ const logger = winston.createLogger({
} ) ;
config _api . initialize ( logger ) ;
auth _api . initialize ( db , users _db , logger ) ;
db _api . initialize ( db , users _db , logger ) ;
subscriptions _api . initialize ( db , users _db , logger , db _api ) ;
auth _api . initialize ( db _api , logger ) ;
subscriptions _api . initialize ( db _api , logger ) ;
categories _api . initialize ( db , users _db , logger , db _api ) ;
// Set some defaults
@ -183,7 +183,7 @@ if (writeConfigMode) {
loadConfig ( ) ;
}
var downloads = {} ;
var downloads = [] ;
app . use ( bodyParser . urlencoded ( { extended : false } ) ) ;
app . use ( bodyParser . json ( ) ) ;
@ -210,10 +210,12 @@ async function checkMigrations() {
if ( ! simplified _db _migration _complete ) {
logger . info ( 'Beginning migration: 4.1->4.2+' )
let success = await simplifyDBFileStructure ( ) ;
success = success && await addMetadataPropertyToDB ( 'view_count' ) ;
success = success && await addMetadataPropertyToDB ( 'description' ) ;
success = success && await addMetadataPropertyToDB ( 'height' ) ;
success = success && await addMetadataPropertyToDB ( 'abr' ) ;
success = success && await db _api . addMetadataPropertyToDB ( 'view_count' ) ;
success = success && await db _api . addMetadataPropertyToDB ( 'description' ) ;
success = success && await db _api . addMetadataPropertyToDB ( 'height' ) ;
success = success && await db _api . addMetadataPropertyToDB ( 'abr' ) ;
// sets migration to complete
db . set ( 'simplified_db_migration_complete' , true ) . write ( ) ;
if ( success ) { logger . info ( '4.1->4.2+ migration complete!' ) ; }
else { logger . error ( 'Migration failed: 4.1->4.2+' ) ; }
}
@ -290,28 +292,6 @@ async function simplifyDBFileStructure() {
return true ;
}
async function addMetadataPropertyToDB ( property _key ) {
try {
const dirs _to _check = db _api . getFileDirectoriesAndDBs ( ) ;
for ( const dir _to _check of dirs _to _check ) {
// recursively get all files in dir's path
const files = await utils . getDownloadedFilesByType ( dir _to _check . basePath , dir _to _check . type , true ) ;
for ( const file of files ) {
if ( file [ property _key ] ) {
dir _to _check . dbPath . find ( { id : file . id } ) . assign ( { [ property _key ] : file [ property _key ] } ) . write ( ) ;
}
}
}
// sets migration to complete
db . set ( 'simplified_db_migration_complete' , true ) . write ( ) ;
return true ;
} catch ( err ) {
logger . error ( err ) ;
return false ;
}
}
async function startServer ( ) {
if ( process . env . USING _HEROKU && process . env . PORT ) {
// default to heroku port if using heroku
@ -612,6 +592,9 @@ async function setConfigFromEnv() {
async function loadConfig ( ) {
loadConfigValues ( ) ;
// connect to DB
await db _api . connectToDB ( ) ;
// creates archive path if missing
await fs . ensureDir ( archivePath ) ;
@ -624,7 +607,7 @@ async function loadConfig() {
// get subscriptions
if ( allowSubscriptions ) {
// set downloading to false
let subscriptions = subscriptions _api . getAllSubscriptions ( ) ;
let subscriptions = await subscriptions _api . getAllSubscriptions ( ) ;
subscriptions _api . updateSubscriptionPropertyMultiple ( subscriptions , { downloading : false } ) ;
// runs initially, then runs every ${subscriptionCheckInterval} seconds
watchSubscriptions ( ) ;
@ -633,10 +616,10 @@ async function loadConfig() {
} , subscriptionsCheckInterval * 1000 ) ;
}
db _api . importUnregisteredFiles ( ) ;
await db _api . importUnregisteredFiles ( ) ;
// load in previous downloads
downloads = db . get ( 'downloads' ) . value ( ) ;
downloads = await db _api . get Records ( 'downloads' ) ;
// start the server here
startServer ( ) ;
@ -684,7 +667,7 @@ function calculateSubcriptionRetrievalDelay(subscriptions_amount) {
}
async function watchSubscriptions ( ) {
let subscriptions = subscriptions _api . getAllSubscriptions ( ) ;
let subscriptions = await subscriptions _api . getAllSubscriptions ( ) ;
if ( ! subscriptions ) return ;
@ -903,8 +886,12 @@ async function downloadFileByURL_exec(url, type, options, sessionID = null) {
// adds download to download helper
const download _uid = uuid ( ) ;
const session = sessionID ? sessionID : 'undeclared' ;
if ( ! downloads [ session ] ) downloads [ session ] = { } ;
downloads [ session ] [ download _uid ] = {
let session _downloads = downloads . find ( potential _session _downloads => potential _session _downloads [ 'session_id' ] === session ) ;
if ( ! session _downloads ) {
session _downloads = { session _id : session } ;
downloads . push ( session _downloads ) ;
}
session _downloads [ download _uid ] = {
uid : download _uid ,
ui _uid : options . ui _uid ,
downloading : true ,
@ -916,7 +903,7 @@ async function downloadFileByURL_exec(url, type, options, sessionID = null) {
timestamp _start : Date . now ( ) ,
filesize : null
} ;
const download = downloads[ session ] [ download _uid ] ;
const download = session_ downloads[ download _uid ] ;
updateDownloads ( ) ;
let download _checker = null ;
@ -1027,7 +1014,7 @@ async function downloadFileByURL_exec(url, type, options, sessionID = null) {
}
// registers file in DB
const file _obj = db _api . registerFileDB ( file _path , type , multiUserMode , null , customPath , category , options . cropFileSettings ) ;
const file _obj = await db _api . registerFileDB2 ( full _file _path , options . user , category , null , options . cropFileSettings ) ;
// TODO: remove the following line
if ( file _name ) file _names . push ( file _name ) ;
@ -1070,146 +1057,6 @@ async function downloadFileByURL_exec(url, type, options, sessionID = null) {
} ) ;
}
async function downloadFileByURL _normal ( url , type , options , sessionID = null ) {
return new Promise ( async resolve => {
var date = Date . now ( ) ;
var file _uid = null ;
const is _audio = type === 'audio' ;
const ext = is _audio ? '.mp3' : '.mp4' ;
var fileFolderPath = is _audio ? audioFolderPath : videoFolderPath ;
if ( is _audio && url . includes ( 'youtu' ) ) { options . skip _audio _args = true ; }
// prepend with user if needed
let multiUserMode = null ;
if ( options . user ) {
let usersFileFolder = config _api . getConfigItem ( 'ytdl_users_base_path' ) ;
const user _path = path . join ( usersFileFolder , options . user , type ) ;
fs . ensureDirSync ( user _path ) ;
fileFolderPath = user _path + path . sep ;
multiUserMode = {
user : options . user ,
file _path : fileFolderPath
}
options . customFileFolderPath = fileFolderPath ;
}
options . downloading _method = 'normal' ;
const downloadConfig = await generateArgs ( url , type , options ) ;
// adds download to download helper
const download _uid = uuid ( ) ;
const session = sessionID ? sessionID : 'undeclared' ;
if ( ! downloads [ session ] ) downloads [ session ] = { } ;
downloads [ session ] [ download _uid ] = {
uid : download _uid ,
ui _uid : options . ui _uid ,
downloading : true ,
complete : false ,
url : url ,
type : type ,
percent _complete : 0 ,
is _playlist : url . includes ( 'playlist' ) ,
timestamp _start : Date . now ( )
} ;
const download = downloads [ session ] [ download _uid ] ;
updateDownloads ( ) ;
const video = youtubedl ( url ,
// Optional arguments passed to youtube-dl.
downloadConfig ,
// Additional options can be given for calling `child_process.execFile()`.
{ cwd : _ _dirname } ) ;
let video _info = null ;
let file _size = 0 ;
// Will be called when the download starts.
video . on ( 'info' , function ( info ) {
video _info = info ;
file _size = video _info . size ;
const json _path = utils . removeFileExtension ( video _info . _filename ) + '.info.json' ;
fs . ensureFileSync ( json _path ) ;
fs . writeJSONSync ( json _path , video _info ) ;
video . pipe ( fs . createWriteStream ( video _info . _filename , { flags : 'w' } ) )
} ) ;
// Will be called if download was already completed and there is nothing more to download.
video . on ( 'complete' , function complete ( info ) {
'use strict'
logger . info ( 'file ' + info . _filename + ' already downloaded.' )
} )
let download _pos = 0 ;
video . on ( 'data' , function data ( chunk ) {
download _pos += chunk . length
// `size` should not be 0 here.
if ( file _size ) {
let percent = ( download _pos / file _size * 100 ) . toFixed ( 2 )
download [ 'percent_complete' ] = percent ;
}
} ) ;
video . on ( 'end' , async function ( ) {
let new _date = Date . now ( ) ;
let difference = ( new _date - date ) / 1000 ;
logger . debug ( ` Video download delay: ${ difference } seconds. ` ) ;
download [ 'timestamp_end' ] = Date . now ( ) ;
download [ 'fileNames' ] = [ utils . removeFileExtension ( video _info . _filename ) + ext ] ;
download [ 'complete' ] = true ;
updateDownloads ( ) ;
// audio-only cleanup
if ( is _audio ) {
// filename fix
video _info [ '_filename' ] = utils . removeFileExtension ( video _info [ '_filename' ] ) + '.mp3' ;
// ID3 tagging
let tags = {
title : video _info [ 'title' ] ,
artist : video _info [ 'artist' ] ? video _info [ 'artist' ] : video _info [ 'uploader' ]
}
let success = NodeID3 . write ( tags , video _info . _filename ) ;
if ( ! success ) logger . error ( 'Failed to apply ID3 tag to audio file ' + video _info . _filename ) ;
const possible _webm _path = utils . removeFileExtension ( video _info [ '_filename' ] ) + '.webm' ;
const possible _mp4 _path = utils . removeFileExtension ( video _info [ '_filename' ] ) + '.mp4' ;
// check if audio file is webm
if ( fs . existsSync ( possible _webm _path ) ) await convertFileToMp3 ( possible _webm _path , video _info [ '_filename' ] ) ;
else if ( fs . existsSync ( possible _mp4 _path ) ) await convertFileToMp3 ( possible _mp4 _path , video _info [ '_filename' ] ) ;
}
// registers file in DB
const base _file _name = video _info . _filename . substring ( fileFolderPath . length , video _info . _filename . length ) ;
file _uid = db _api . registerFileDB ( base _file _name , type , multiUserMode ) ;
if ( options . merged _string !== null && options . merged _string !== undefined ) {
let current _merged _archive = fs . readFileSync ( path . join ( fileFolderPath , ` merged_ ${ type } .txt ` ) , 'utf8' ) ;
let diff = current _merged _archive . replace ( options . merged _string , '' ) ;
const archive _path = options . user ? path . join ( fileFolderPath , 'archives' , ` archive_ ${ type } .txt ` ) : path . join ( archivePath , ` archive_ ${ type } .txt ` ) ;
fs . appendFileSync ( archive _path , diff ) ;
}
videopathEncoded = encodeURIComponent ( utils . removeFileExtension ( base _file _name ) ) ;
resolve ( {
encodedPath : videopathEncoded ,
file _names : /*is_playlist ? file_names :*/ null , // playlist support is not ready
uid : file _uid
} ) ;
} ) ;
video . on ( 'error' , function error ( err ) {
logger . error ( err ) ;
download [ error ] = err ;
updateDownloads ( ) ;
resolve ( false ) ;
} ) ;
} ) ;
}
async function generateArgs ( url , type , options ) {
var videopath = config _api . getConfigItem ( 'ytdl_default_file_output' ) ? config _api . getConfigItem ( 'ytdl_default_file_output' ) : '%(title)s' ;
var globalArgs = config _api . getConfigItem ( 'ytdl_custom_args' ) ;
@ -1250,8 +1097,8 @@ async function generateArgs(url, type, options) {
qualityPath = [ '-f' , customQualityConfiguration ] ;
} else if ( selectedHeight && selectedHeight !== '' && ! is _audio ) {
qualityPath = [ '-f' , ` '(mp4)[height= ${ selectedHeight } ' ` ] ;
} else if ( maxBitrate && is_audio ) {
qualityPath = [ '--audio-quality' , maxBitrate ]
} else if ( is_audio ) {
qualityPath = [ '--audio-quality' , maxBitrate ? maxBitrate : '0' ]
}
if ( customOutput ) {
@ -1401,23 +1248,6 @@ async function getUrlInfos(urls) {
// ffmpeg helper functions
async function convertFileToMp3 ( input _file , output _file ) {
logger . verbose ( ` Converting ${ input _file } to ${ output _file } ... ` ) ;
return new Promise ( resolve => {
ffmpeg ( input _file ) . noVideo ( ) . toFormat ( 'mp3' )
. on ( 'end' , ( ) => {
logger . verbose ( ` Conversion for ' ${ output _file } ' complete. ` ) ;
fs . unlinkSync ( input _file )
resolve ( true ) ;
} )
. on ( 'error' , ( err ) => {
logger . error ( 'Failed to convert audio file to the correct format.' ) ;
logger . error ( err ) ;
resolve ( false ) ;
} ) . save ( output _file ) ;
} ) ;
}
async function cropFile ( file _path , start , end , ext ) {
return new Promise ( resolve => {
const temp _file _path = ` ${ file _path } .cropped ${ ext } ` ;
@ -1456,8 +1286,9 @@ async function writeToBlacklist(type, line) {
// download management functions
function updateDownloads ( ) {
db . assign ( { downloads : downloads } ) . write ( ) ;
async function updateDownloads ( ) {
await db _api . removeAllRecords ( 'downloads' ) ;
if ( downloads . length !== 0 ) await db _api . insertRecordsIntoTable ( 'downloads' , downloads ) ;
}
function checkDownloadPercent ( download ) {
@ -1489,7 +1320,6 @@ function checkDownloadPercent(download) {
}
} ) ;
download [ 'percent_complete' ] = ( sum _size / resulting _file _size * 100 ) . toFixed ( 2 ) ;
updateDownloads ( ) ;
} ) ;
}
@ -1715,6 +1545,37 @@ app.post('/api/restartServer', optionalJwt, (req, res) => {
res . send ( { success : true } ) ;
} ) ;
app . post ( '/api/getDBInfo' , optionalJwt , async ( req , res ) => {
const db _info = await db _api . getDBStats ( ) ;
res . send ( { db _info : db _info } ) ;
} ) ;
app . post ( '/api/transferDB' , optionalJwt , async ( req , res ) => {
const local _to _remote = req . body . local _to _remote ;
let success = null ;
let error = '' ;
if ( local _to _remote === config _api . getConfigItem ( 'ytdl_use_local_db' ) ) {
success = await db _api . transferDB ( local _to _remote ) ;
if ( ! success ) error = 'Unknown error' ;
else config _api . setConfigItem ( 'ytdl_use_local_db' , ! local _to _remote ) ;
} else {
success = false ;
error = ` Failed to transfer DB as it cannot transition into its current status: ${ local _to _remote ? 'MongoDB' : 'Local DB' } ` ;
logger . error ( error ) ;
}
res . send ( { success : success , error : error } ) ;
} ) ;
app . post ( '/api/testConnectionString' , optionalJwt , async ( req , res ) => {
let success = null ;
let error = '' ;
success = await db _api . connectToDB ( 5 , true ) ;
if ( ! success ) error = 'Connection string failed.' ;
res . send ( { success : success , error : error } ) ;
} ) ;
app . post ( '/api/downloadFile' , optionalJwt , async function ( req , res ) {
req . setTimeout ( 0 ) ; // remove timeout in case of long videos
const url = req . body . url ;
@ -1731,15 +1592,7 @@ app.post('/api/downloadFile', optionalJwt, async function(req, res) {
cropFileSettings : req . body . cropFileSettings
}
const safeDownloadOverride = config _api . getConfigItem ( 'ytdl_safe_download_override' ) || config _api . globalArgsRequiresSafeDownload ( ) ;
if ( safeDownloadOverride ) logger . verbose ( 'Download is running with the safe download override.' ) ;
const is _playlist = url . includes ( 'playlist' ) ;
let result _obj = null ;
if ( true || safeDownloadOverride || is _playlist || options . customQualityConfiguration || options . customArgs || options . selectedHeight || ! url . includes ( 'youtu' ) )
result _obj = await downloadFileByURL _exec ( url , type , options , req . query . sessionID ) ;
else
result _obj = await downloadFileByURL _normal ( url , 'video' , options , req . query . sessionID ) ;
let result _obj = await downloadFileByURL _exec ( url , type , options , req . query . sessionID ) ;
if ( result _obj ) {
res . send ( result _obj ) ;
} else {
@ -1752,29 +1605,17 @@ app.post('/api/killAllDownloads', optionalJwt, async function(req, res) {
res . send ( result _obj ) ;
} ) ;
/ * *
* add thumbnails if present
* @ param files - List of files with thumbnailPath property .
* /
async function addThumbnails ( files ) {
await Promise . all ( files . map ( async file => {
const thumbnailPath = file [ 'thumbnailPath' ] ;
if ( thumbnailPath && ( await fs . pathExists ( thumbnailPath ) ) ) {
file [ 'thumbnailBlob' ] = await fs . readFile ( thumbnailPath ) ;
}
} ) ) ;
}
// gets all download mp3s
app . get ( '/api/getMp3s' , optionalJwt , async function ( req , res ) {
var mp3s = db . get ( 'files' ) . value ( ) . filter ( file => file . isAudio === true ) ;
var playlists = db . get ( 'playlists' ) . value ( ) ;
// TODO: simplify
let mp3s = await db _api . getRecords ( 'files' , { isAudio : true } ) ;
let playlists = await db _api . getRecords ( 'playlists' ) ;
const is _authenticated = req . isAuthenticated ( ) ;
if ( is _authenticated ) {
// get user audio files/playlists
auth _api . passport . authenticate ( 'jwt' )
mp3s = auth _api . getUserVideos ( req . user . uid , 'audio' ) ;
playlists = auth _api . getUserPlaylists ( req . user . uid );
mp3s = await db _api . getRecords ( 'files' , { user _uid : req . user . uid , isAudio : true } ) ;
playlists = await db _api . getRecords ( 'playlists' , { user _uid : req . user . uid } ) ; // TODO: remove?
}
mp3s = JSON . parse ( JSON . stringify ( mp3s ) ) ;
@ -1787,15 +1628,15 @@ app.get('/api/getMp3s', optionalJwt, async function(req, res) {
// gets all download mp4s
app . get ( '/api/getMp4s' , optionalJwt , async function ( req , res ) {
var mp4s = db . get ( 'files' ) . value ( ) . filter ( file => file . isAudio === false ) ;
var playlists = db . get ( 'playlists' ) . value ( ) ;
let mp4s = await db _api . getRecords ( 'files' , { isAudio : false } ) ;
let playlists = await db _api . getRecords ( 'playlists' ) ;
const is _authenticated = req . isAuthenticated ( ) ;
if ( is _authenticated ) {
// get user videos/playlists
auth _api . passport . authenticate ( 'jwt' )
mp4s = auth _api . getUserVideos ( req . user . uid , 'video' ) ;
playlists = auth _api . getUserPlaylists ( req . user . uid );
mp4s = await db _api . getRecords ( 'files' , { user _uid : req . user . uid , isAudio : false } ) ;
playlists = await db _api . getRecords ( 'playlists' , { user _uid : req . user . uid } ); // TODO: remove?
}
mp4s = JSON . parse ( JSON . stringify ( mp4s ) ) ;
@ -1806,20 +1647,14 @@ app.get('/api/getMp4s', optionalJwt, async function(req, res) {
} ) ;
} ) ;
app . post ( '/api/getFile' , optionalJwt , function ( req , res ) {
app . post ( '/api/getFile' , optionalJwt , async function ( req , res ) {
var uid = req . body . uid ;
var type = req . body . type ;
var uuid = req . body . uuid ;
var file = null ;
var file = await db _api . getRecord ( 'files' , { uid : uid } ) ;
if ( req . isAuthenticated ( ) ) {
file = auth _api . getUserVideo ( req . user . uid , uid ) ;
} else if ( uuid ) {
file = auth _api . getUserVideo ( uuid , uid , true ) ;
} else {
file = db . get ( 'files' ) . find ( { uid : uid } ) . value ( ) ;
}
if ( uuid && ! file [ 'sharingEnabled' ] ) file = null ;
// check if chat exists for twitch videos
if ( file && file [ 'url' ] . includes ( 'twitch.tv' ) ) file [ 'chat_exists' ] = fs . existsSync ( file [ 'path' ] . substring ( 0 , file [ 'path' ] . length - 4 ) + '.twitch_chat.json' ) ;
@ -1842,18 +1677,12 @@ app.post('/api/getAllFiles', optionalJwt, async function (req, res) {
let playlists = null ;
const uuid = req . isAuthenticated ( ) ? req . user . uid : null ;
let subscriptions = config _api . getConfigItem ( 'ytdl_allow_subscriptions' ) ? ( subscriptions _api . getSubscriptions ( req . isAuthenticated ( ) ? req . user . uid : null ) ) : [ ] ;
let subscriptions = config _api . getConfigItem ( 'ytdl_allow_subscriptions' ) ? ( await subscriptions _api . getSubscriptions ( req . isAuthenticated ( ) ? req . user . uid : null ) ) : [ ] ;
// get basic info depending on multi-user mode being enabled
if ( uuid ) {
files = auth _api . getUserVideos ( req . user . uid ) ;
playlists = auth _api . getUserPlaylists ( req . user . uid , files ) ;
} else {
files = db . get ( 'files' ) . value ( ) ;
playlists = JSON . parse ( JSON . stringify ( db . get ( 'playlists' ) . value ( ) ) ) ;
}
files = await db _api . getRecords ( 'files' , { user _uid : uuid } ) ;
playlists = await db _api . getRecords ( 'playlists' , { user _uid : uuid } ) ;
const categories = categories _api . getCategoriesAsPlaylists ( files ) ;
const categories = await categories _api . getCategoriesAsPlaylists ( files ) ;
if ( categories ) {
playlists = playlists . concat ( categories ) ;
}
@ -1872,11 +1701,6 @@ app.post('/api/getAllFiles', optionalJwt, async function (req, res) {
files = JSON . parse ( JSON . stringify ( files ) ) ;
if ( config _api . getConfigItem ( 'ytdl_include_thumbnail' ) ) {
// add thumbnails if present
// await addThumbnails(files);
}
res . send ( {
files : files ,
playlists : playlists
@ -1952,7 +1776,7 @@ app.post('/api/downloadTwitchChatByVODID', optionalJwt, async (req, res) => {
} ) ;
// video sharing
app . post ( '/api/enableSharing' , optionalJwt , function ( req , res ) {
app . post ( '/api/enableSharing' , optionalJwt , async ( req , res ) => {
var uid = req . body . uid ;
var is _playlist = req . body . is _playlist ;
let success = false ;
@ -1968,15 +1792,9 @@ app.post('/api/enableSharing', optionalJwt, function(req, res) {
try {
success = true ;
if ( ! is _playlist ) {
db . get ( ` files ` )
. find ( { uid : uid } )
. assign ( { sharingEnabled : true } )
. write ( ) ;
await db _api . updateRecord ( 'files' , { uid : uid } , { sharingEnabled : true } )
} else if ( is _playlist ) {
db . get ( ` playlists ` )
. find ( { id : uid } )
. assign ( { sharingEnabled : true } )
. write ( ) ;
await db _api . updateRecord ( ` playlists ` , { id : uid } , { sharingEnabled : true } ) ;
} else if ( false ) {
// TODO: Implement. Main blocker right now is subscription videos are not stored in the DB, they are searched for every
// time they are requested from the subscription directory.
@ -1995,7 +1813,7 @@ app.post('/api/enableSharing', optionalJwt, function(req, res) {
} ) ;
} ) ;
app . post ( '/api/disableSharing' , optionalJwt , function ( req , res ) {
app . post ( '/api/disableSharing' , optionalJwt , async function ( req , res ) {
var type = req . body . type ;
var uid = req . body . uid ;
var is _playlist = req . body . is _playlist ;
@ -2012,15 +1830,9 @@ app.post('/api/disableSharing', optionalJwt, function(req, res) {
try {
success = true ;
if ( ! is _playlist && type !== 'subscription' ) {
db . get ( ` files ` )
. find ( { uid : uid } )
. assign ( { sharingEnabled : false } )
. write ( ) ;
await db _api . updateRecord ( 'files' , { uid : uid } , { sharingEnabled : false } )
} else if ( is _playlist ) {
db . get ( ` playlists ` )
. find ( { id : uid } )
. assign ( { sharingEnabled : false } )
. write ( ) ;
await db _api . updateRecord ( ` playlists ` , { id : uid } , { sharingEnabled : false } ) ;
} else if ( type === 'subscription' ) {
// TODO: Implement. Main blocker right now is subscription videos are not stored in the DB, they are searched for every
// time they are requested from the subscription directory.
@ -2062,7 +1874,7 @@ app.post('/api/incrementViewCount', optionalJwt, async (req, res) => {
// categories
app . post ( '/api/getAllCategories' , optionalJwt , async ( req , res ) => {
const categories = db . get ( 'categories' ) . value ( ) ;
const categories = await db _api . get Records ( 'categories' ) ;
res . send ( { categories : categories } ) ;
} ) ;
@ -2075,7 +1887,7 @@ app.post('/api/createCategory', optionalJwt, async (req, res) => {
custom _output : ''
} ;
db . get ( 'categories' ) . push ( new _category ) . write ( ) ;
await db _api . insertRecordIntoTable ( 'categories' , new _category ) ;
res . send ( {
new _category : new _category ,
@ -2086,7 +1898,7 @@ app.post('/api/createCategory', optionalJwt, async (req, res) => {
app . post ( '/api/deleteCategory' , optionalJwt , async ( req , res ) => {
const category _uid = req . body . category _uid ;
db . get ( 'categories' ) . remove ( { uid : category _uid } ) . write ( ) ;
await db _api . removeRecord ( 'categories' , { uid : category _uid } ) ;
res . send ( {
success : true
@ -2095,13 +1907,14 @@ app.post('/api/deleteCategory', optionalJwt, async (req, res) => {
app . post ( '/api/updateCategory' , optionalJwt , async ( req , res ) => {
const category = req . body . category ;
db . get ( 'categories' ) . find ( { uid : category . uid } ) . assign ( category ) . write ( ) ;
await db _api . updateRecord ( 'categories' , { uid : category . uid } , category )
res . send ( { success : true } ) ;
} ) ;
app . post ( '/api/updateCategories' , optionalJwt , async ( req , res ) => {
const categories = req . body . categories ;
db . get ( 'categories' ) . assign ( categories ) . write ( ) ;
await db _api . removeAllRecords ( 'categories' ) ;
await db _api . insertRecordsIntoTable ( 'categories' , categories ) ;
res . send ( { success : true } ) ;
} ) ;
@ -2200,9 +2013,9 @@ app.post('/api/getSubscription', optionalJwt, async (req, res) => {
// get sub from db
let subscription = null ;
if ( subID ) {
subscription = subscriptions _api . getSubscription ( subID , user _uid )
subscription = await subscriptions _api . getSubscription ( subID , user _uid )
} else if ( subName ) {
subscription = subscriptions _api . getSubscriptionByName ( subName , user _uid )
subscription = await subscriptions _api . getSubscriptionByName ( subName , user _uid )
}
if ( ! subscription ) {
@ -2213,7 +2026,8 @@ app.post('/api/getSubscription', optionalJwt, async (req, res) => {
// get sub videos
if ( subscription . name && ! subscription . streamingOnly ) {
var parsed _files = subscription . videos ;
var parsed _files = await db _api . getRecords ( 'files' , { sub _id : subscription . id } ) ; // subscription.videos;
subscription [ 'videos' ] = parsed _files ;
if ( ! parsed _files ) {
parsed _files = [ ] ;
let base _path = null ;
@ -2310,7 +2124,7 @@ app.post('/api/getSubscriptions', optionalJwt, async (req, res) => {
let user _uid = req . isAuthenticated ( ) ? req . user . uid : null ;
// get subs from api
let subscriptions = subscriptions _api . getSubscriptions ( user _uid ) ;
let subscriptions = await subscriptions _api . getSubscriptions ( user _uid ) ;
res . send ( {
subscriptions : subscriptions
@ -2343,7 +2157,8 @@ app.post('/api/getPlaylist', optionalJwt, async (req, res) => {
for ( let i = 0 ; i < playlist [ 'uids' ] . length ; i ++ ) {
const uid = playlist [ 'uids' ] [ i ] ;
const file _obj = await db _api . getVideo ( uid , uuid ) ;
file _objs . push ( file _obj ) ;
if ( file _obj ) file _objs . push ( file _obj ) ;
// TODO: remove file from playlist if could not be found
}
}
@ -2364,10 +2179,7 @@ app.post('/api/updatePlaylistFiles', optionalJwt, async (req, res) => {
if ( req . isAuthenticated ( ) ) {
auth _api . updatePlaylistFiles ( req . user . uid , playlistID , uids ) ;
} else {
db . get ( ` playlists ` )
. find ( { id : playlistID } )
. assign ( { uids : uids } )
. write ( ) ;
await db _api . updateRecord ( 'playlists' , { id : playlistID } , { uids : uids } )
}
success = true ;
@ -2393,14 +2205,8 @@ app.post('/api/deletePlaylist', optionalJwt, async (req, res) => {
let success = null ;
try {
if ( req . isAuthenticated ( ) ) {
auth _api . removePlaylist ( req . user . uid , playlistID ) ;
} else {
// removes playlist from playlists
db . get ( ` playlists ` )
. remove ( { id : playlistID } )
. write ( ) ;
}
// removes playlist from playlists
await db _api . removeRecord ( 'playlists' , { id : playlistID } )
success = true ;
} catch ( e ) {
@ -2611,20 +2417,14 @@ app.get('/api/thumbnail/:path', optionalJwt, async (req, res) => {
} ) ;
app . post ( '/api/download' , async ( req , res ) => {
var session _id = req . body . session _id ;
var download _id = req . body . download _id ;
const session _id = req . body . session _id ;
const download _id = req . body . download _id ;
const session _downloads = downloads . find ( potential _session _downloads => potential _session _downloads [ 'session_id' ] === session _id ) ;
let found _download = null ;
// find download
if ( downloads [ session _id ] && Object . keys ( downloads [ session _id ] ) ) {
let session _downloads = Object . values ( downloads [ session _id ] ) ;
for ( let i = 0 ; i < session _downloads . length ; i ++ ) {
let session _download = session _downloads [ i ] ;
if ( session _download && session _download [ 'ui_uid' ] === download _id ) {
found _download = session _download ;
break ;
}
}
if ( session _downloads && Object . keys ( session _downloads ) ) {
found _download = Object . values ( session _downloads ) . find ( session _download => session _download [ 'ui_uid' ] === download _id ) ;
}
if ( found _download ) {
@ -2642,26 +2442,22 @@ app.get('/api/thumbnail/:path', optionalJwt, async (req, res) => {
var download _id = req . body . download _id ;
if ( delete _all ) {
// delete all downloads
downloads = {} ;
downloads = [] ;
success = true ;
} else if ( download _id ) {
// delete just 1 download
if ( downloads [ session _id ] [ download _id ] ) {
delete downloads [ session _id ] [ download _id ] ;
const session _downloads = downloads . find ( session => session [ 'session_id' ] === session _id ) ;
if ( session _downloads && session _downloads [ download _id ] ) {
delete session _downloads [ download _id ] ;
success = true ;
} else if ( ! downloads[ session _id ] ) {
} else if ( ! session_downloads ) {
logger . error ( ` Session ${ session _id } has no downloads. ` )
} else if ( ! downloads[ session _id ] [ download _id ] ) {
} else if ( ! session_downloads [ download _id ] ) {
logger . error ( ` Download ' ${ download _id } ' for session ' ${ session _id } ' could not be found ` ) ;
}
} else if ( session _id ) {
// delete a session's downloads
if ( downloads [ session _id ] ) {
delete downloads [ session _id ] ;
success = true ;
} else {
logger . error ( ` Session ${ session _id } has no downloads. ` )
}
downloads = downloads . filter ( session => session [ 'session_id' ] !== session _id ) ;
}
updateDownloads ( ) ;
res . send ( { success : success , downloads : downloads } ) ;
@ -2746,29 +2542,28 @@ app.post('/api/auth/changePassword', optionalJwt, async (req, res) => {
res . send ( { success : success } ) ;
} ) ;
app . post ( '/api/auth/adminExists' , async ( req , res ) => {
let exists = auth _api . adminExists ( ) ;
let exists = await auth _api . adminExists ( ) ;
res . send ( { exists : exists } ) ;
} ) ;
// user management
app . post ( '/api/getUsers' , optionalJwt , async ( req , res ) => {
let users = users _db . get ( 'users' ) . value ( ) ;
let users = await db _api . getRecords ( 'users' ) ;
res . send ( { users : users } ) ;
} ) ;
app . post ( '/api/getRoles' , optionalJwt , async ( req , res ) => {
let roles = users _db . get ( 'roles' ) . value ( ) ;
let roles = await db _api . getRecords ( 'roles' ) ;
res . send ( { roles : roles } ) ;
} ) ;
app . post ( '/api/updateUser' , optionalJwt , async ( req , res ) => {
let change _obj = req . body . change _object ;
try {
const user _db _obj = users _db . get ( 'users' ) . find ( { uid : change _obj . uid } ) ;
if ( change _obj . name ) {
user _db _obj . assign ( { name : change _obj . name } ) . write ( ) ;
await db _api . updateRecord ( 'users' , { uid : change _obj . uid } , { name : change _obj . name } ) ;
}
if ( change _obj . role ) {
user _db _obj . assign ( { role : change _obj . role } ) . write ( ) ;
await db _api . updateRecord ( 'users' , { uid : change _obj . uid } , { role : change _obj . role } ) ;
}
res . send ( { success : true } ) ;
} catch ( err ) {
@ -2780,13 +2575,17 @@ app.post('/api/updateUser', optionalJwt, async (req, res) => {
app . post ( '/api/deleteUser' , optionalJwt , async ( req , res ) => {
let uid = req . body . uid ;
try {
let success = false ;
let usersFileFolder = config _api . getConfigItem ( 'ytdl_users_base_path' ) ;
const user _folder = path . join ( _ _dirname , usersFileFolder , uid ) ;
const user _db _obj = users _db . get ( 'users' ) . find ( { uid : uid } ) ;
if ( user _db _obj .value ( ) ) {
const user _db _obj = await db _api . getRecord ( 'users' , { uid : uid } ) ;
if ( user _db _obj ) {
// user exists, let's delete
await fs . remove ( user _folder ) ;
users _db . get ( 'users' ) . remove ( { uid : uid } ) . write ( ) ;
await db _api . removeRecord ( 'users' , { uid : uid } ) ;
success = true ;
} else {
logger . error ( ` Could not find user with uid ${ uid } ` ) ;
}
res . send ( { success : true } ) ;
} catch ( err ) {
@ -2805,7 +2604,7 @@ app.post('/api/changeUserPermissions', optionalJwt, async (req, res) => {
return ;
}
const success = auth _api . changeUserPermissions ( user _uid , permission , new _value ) ;
const success = await auth _api . changeUserPermissions ( user _uid , permission , new _value ) ;
res . send ( { success : success } ) ;
} ) ;
@ -2820,7 +2619,7 @@ app.post('/api/changeRolePermissions', optionalJwt, async (req, res) => {
return ;
}
const success = auth _api . changeRolePermissions ( role , permission , new _value ) ;
const success = await auth _api . changeRolePermissions ( role , permission , new _value ) ;
res . send ( { success : success } ) ;
} ) ;