var assert = require ( 'assert' ) ;
const low = require ( 'lowdb' )
var winston = require ( 'winston' ) ;
process . chdir ( './backend' )
const FileSync = require ( 'lowdb/adapters/FileSync' ) ;
const adapter = new FileSync ( './appdata/db.json' ) ;
const db = low ( adapter )
const users _adapter = new FileSync ( './appdata/users.json' ) ;
const users _db = low ( users _adapter ) ;
const defaultFormat = winston . format . printf ( ( { level , message , label , timestamp } ) => {
return ` ${ timestamp } ${ level . toUpperCase ( ) } : ${ message } ` ;
} ) ;
let debugMode = process . env . YTDL _MODE === 'debug' ;
const logger = winston . createLogger ( {
level : 'info' ,
format : winston . format . combine ( winston . format . timestamp ( ) , defaultFormat ) ,
defaultMeta : { } ,
transports : [
//
// - Write to all logs with level `info` and below to `combined.log`
// - Write all logs error (and below) to `error.log`.
//
new winston . transports . File ( { filename : 'appdata/logs/error.log' , level : 'error' } ) ,
new winston . transports . File ( { filename : 'appdata/logs/combined.log' } ) ,
new winston . transports . Console ( { level : 'debug' , name : 'console' } )
]
} ) ;
var auth _api = require ( '../authentication/auth' ) ;
var db _api = require ( '../db' ) ;
const utils = require ( '../utils' ) ;
const subscriptions _api = require ( '../subscriptions' ) ;
const fs = require ( 'fs-extra' ) ;
const { uuid } = require ( 'uuidv4' ) ;
db _api . initialize ( db , users _db ) ;
describe ( 'Database' , async function ( ) {
describe ( 'Import' , async function ( ) {
it ( 'Migrate' , async function ( ) {
await db _api . connectToDB ( ) ;
await db _api . removeAllRecords ( ) ;
const success = await db _api . importJSONToDB ( db . value ( ) , users _db . value ( ) ) ;
assert ( success ) ;
} ) ;
it ( 'Transfer to remote' , async function ( ) {
await db _api . removeAllRecords ( 'test' ) ;
await db _api . insertRecordIntoTable ( 'test' , { test : 'test' } ) ;
await db _api . transferDB ( true ) ;
const success = await db _api . getRecord ( 'test' , { test : 'test' } ) ;
assert ( success ) ;
} ) ;
it ( 'Transfer to local' , async function ( ) {
await db _api . connectToDB ( ) ;
await db _api . removeAllRecords ( 'test' ) ;
await db _api . insertRecordIntoTable ( 'test' , { test : 'test' } ) ;
await db _api . transferDB ( false ) ;
const success = await db _api . getRecord ( 'test' , { test : 'test' } ) ;
assert ( success ) ;
} ) ;
it ( 'Restore db' , async function ( ) {
const db _stats = await db _api . getDBStats ( ) ;
const file _name = await db _api . backupDB ( ) ;
await db _api . restoreDB ( file _name ) ;
const new _db _stats = await db _api . getDBStats ( ) ;
assert ( JSON . stringify ( db _stats ) , JSON . stringify ( new _db _stats ) ) ;
} ) ;
} ) ;
describe ( 'Export' , function ( ) {
} ) ;
describe ( 'Basic functions' , async function ( ) {
beforeEach ( async function ( ) {
await db _api . connectToDB ( ) ;
await db _api . removeAllRecords ( 'test' ) ;
} ) ;
it ( 'Add and read record' , async function ( ) {
this . timeout ( 120000 ) ;
await db _api . insertRecordIntoTable ( 'test' , { test _add : 'test' , test _undefined : undefined , test _null : undefined } ) ;
const added _record = await db _api . getRecord ( 'test' , { test _add : 'test' , test _undefined : undefined , test _null : null } ) ;
assert ( added _record [ 'test_add' ] === 'test' ) ;
await db _api . removeRecord ( 'test' , { test _add : 'test' } ) ;
} ) ;
it ( 'Find duplicates by key' , async function ( ) {
const test _duplicates = [
{
test : 'testing' ,
key : '1'
} ,
{
test : 'testing' ,
key : '2'
} ,
{
test : 'testing_missing' ,
key : '3'
} ,
{
test : 'testing' ,
key : '4'
}
] ;
await db _api . insertRecordsIntoTable ( 'test' , test _duplicates ) ;
const duplicates = await db _api . findDuplicatesByKey ( 'test' , 'test' ) ;
console . log ( duplicates ) ;
} ) ;
it ( 'Update record' , async function ( ) {
await db _api . insertRecordIntoTable ( 'test' , { test _update : 'test' } ) ;
await db _api . updateRecord ( 'test' , { test _update : 'test' } , { added _field : true } ) ;
const updated _record = await db _api . getRecord ( 'test' , { test _update : 'test' } ) ;
assert ( updated _record [ 'added_field' ] ) ;
await db _api . removeRecord ( 'test' , { test _update : 'test' } ) ;
} ) ;
it ( 'Remove record' , async function ( ) {
await db _api . insertRecordIntoTable ( 'test' , { test _remove : 'test' } ) ;
const delete _succeeded = await db _api . removeRecord ( 'test' , { test _remove : 'test' } ) ;
assert ( delete _succeeded ) ;
const deleted _record = await db _api . getRecord ( 'test' , { test _remove : 'test' } ) ;
assert ( ! deleted _record ) ;
} ) ;
it ( 'Push to record array' , async function ( ) {
await db _api . insertRecordIntoTable ( 'test' , { test : 'test' , test _array : [ ] } ) ;
await db _api . pushToRecordsArray ( 'test' , { test : 'test' } , 'test_array' , 'test_item' ) ;
const record = await db _api . getRecord ( 'test' , { test : 'test' } ) ;
assert ( record ) ;
assert ( record [ 'test_array' ] . length === 1 ) ;
} ) ;
it ( 'Pull from record array' , async function ( ) {
await db _api . insertRecordIntoTable ( 'test' , { test : 'test' , test _array : [ 'test_item' ] } ) ;
await db _api . pullFromRecordsArray ( 'test' , { test : 'test' } , 'test_array' , 'test_item' ) ;
const record = await db _api . getRecord ( 'test' , { test : 'test' } ) ;
assert ( record ) ;
assert ( record [ 'test_array' ] . length === 0 ) ;
} ) ;
it ( 'Bulk add' , async function ( ) {
this . timeout ( 120000 ) ;
const NUM _RECORDS _TO _ADD = 2002 ; // max batch ops is 1000
const test _records = [ ] ;
for ( let i = 0 ; i < NUM _RECORDS _TO _ADD ; i ++ ) {
test _records . push ( {
uid : uuid ( )
} ) ;
}
const succcess = await db _api . bulkInsertRecordsIntoTable ( 'test' , test _records ) ;
const received _records = await db _api . getRecords ( 'test' ) ;
assert ( succcess && received _records && received _records . length === NUM _RECORDS _TO _ADD ) ;
} ) ;
it ( 'Bulk update' , async function ( ) {
// bulk add records
const NUM _RECORDS _TO _ADD = 100 ; // max batch ops is 1000
const test _records = [ ] ;
const update _obj = { } ;
for ( let i = 0 ; i < NUM _RECORDS _TO _ADD ; i ++ ) {
const test _uid = uuid ( ) ;
test _records . push ( {
uid : test _uid
} ) ;
update _obj [ test _uid ] = { added _field : true } ;
}
let success = await db _api . bulkInsertRecordsIntoTable ( 'test' , test _records ) ;
assert ( success ) ;
// makes sure they are added
const received _records = await db _api . getRecords ( 'test' ) ;
assert ( received _records && received _records . length === NUM _RECORDS _TO _ADD ) ;
success = await db _api . bulkUpdateRecords ( 'test' , 'uid' , update _obj ) ;
assert ( success ) ;
const received _updated _records = await db _api . getRecords ( 'test' ) ;
for ( let i = 0 ; i < received _updated _records . length ; i ++ ) {
success &= received _updated _records [ i ] [ 'added_field' ] ;
}
assert ( success ) ;
} ) ;
it ( 'Stats' , async function ( ) {
const stats = await db _api . getDBStats ( ) ;
assert ( stats ) ;
} ) ;
it ( 'Query speed' , async function ( ) {
this . timeout ( 120000 ) ;
const NUM _RECORDS _TO _ADD = 300004 ; // max batch ops is 1000
const test _records = [ ] ;
let random _uid = '06241f83-d1b8-4465-812c-618dfa7f2943' ;
for ( let i = 0 ; i < NUM _RECORDS _TO _ADD ; i ++ ) {
const uid = uuid ( ) ;
if ( i === NUM _RECORDS _TO _ADD / 2 ) random _uid = uid ;
test _records . push ( { "id" : "A$AP Mob - Yamborghini High (Official Music Video) ft. Juicy J" , "title" : "A$AP Mob - Yamborghini High (Official Music Video) ft. Juicy J" , "thumbnailURL" : "https://i.ytimg.com/vi/tt7gP_IW-1w/maxresdefault.jpg" , "isAudio" : true , "duration" : 312 , "url" : "https://www.youtube.com/watch?v=tt7gP_IW-1w" , "uploader" : "asapmobVEVO" , "size" : 5060157 , "path" : "audio\\A$AP Mob - Yamborghini High (Official Music Video) ft. Juicy J.mp3" , "upload_date" : "2016-05-11" , "description" : "A$AP Mob ft. Juicy J - \"Yamborghini High\" Get it now on:\niTunes: http://smarturl.it/iYAMH?IQid=yt\nListen on Spotify: http://smarturl.it/sYAMH?IQid=yt\nGoogle Play: http://smarturl.it/gYAMH?IQid=yt\nAmazon: http://smarturl.it/aYAMH?IQid=yt\n\nFollow A$AP Mob:\nhttps://www.facebook.com/asapmobofficial\nhttps://twitter.com/ASAPMOB\nhttp://instagram.com/asapmob \nhttp://www.asapmob.com/\n\n#AsapMob #YamborghiniHigh #Vevo #HipHop #OfficialMusicVideo #JuicyJ" , "view_count" : 118689353 , "height" : null , "abr" : 160 , "uid" : uid , "registered" : 1626672120632 } ) ;
}
const insert _start = Date . now ( ) ;
let success = await db _api . bulkInsertRecordsIntoTable ( 'test' , test _records ) ;
const insert _end = Date . now ( ) ;
console . log ( ` Insert time: ${ ( insert _end - insert _start ) / 1000 } s ` ) ;
const query _start = Date . now ( ) ;
const random _record = await db _api . getRecord ( 'test' , { uid : random _uid } ) ;
const query _end = Date . now ( ) ;
console . log ( random _record )
console . log ( ` Query time: ${ ( query _end - query _start ) / 1000 } s ` ) ;
success = ! ! random _record ;
assert ( success ) ;
} ) ;
} ) ;
} ) ;
describe ( 'Multi User' , async function ( ) {
let user = null ;
const user _to _test = 'admin' ;
const sub _to _test = 'dc834388-3454-41bf-a618-e11cb8c7de1c' ;
const playlist _to _test = 'ysabVZz4x' ;
beforeEach ( async function ( ) {
await db _api . connectToDB ( ) ;
auth _api . initialize ( db _api , logger ) ;
subscriptions _api . initialize ( db _api , logger ) ;
user = await auth _api . login ( 'admin' , 'pass' ) ;
} ) ;
describe ( 'Authentication' , function ( ) {
it ( 'login' , async function ( ) {
assert ( user ) ;
} ) ;
} ) ;
describe ( 'Video player - normal' , function ( ) {
const video _to _test = 'ebbcfffb-d6f1-4510-ad25-d1ec82e0477e' ;
it ( 'Get video' , async function ( ) {
const video _obj = db _api . getVideo ( video _to _test , 'admin' ) ;
assert ( video _obj ) ;
} ) ;
it ( 'Video access - disallowed' , async function ( ) {
await db _api . setVideoProperty ( video _to _test , { sharingEnabled : false } , user _to _test ) ;
const video _obj = auth _api . getUserVideo ( 'admin' , video _to _test , true ) ;
assert ( ! video _obj ) ;
} ) ;
it ( 'Video access - allowed' , async function ( ) {
await db _api . setVideoProperty ( video _to _test , { sharingEnabled : true } , user _to _test ) ;
const video _obj = auth _api . getUserVideo ( 'admin' , video _to _test , true ) ;
assert ( video _obj ) ;
} ) ;
} ) ;
describe ( 'Zip generators' , function ( ) {
it ( 'Playlist zip generator' , async function ( ) {
const playlist = await db _api . getPlaylist ( playlist _to _test , user _to _test ) ;
assert ( playlist ) ;
const playlist _files _to _download = [ ] ;
for ( let i = 0 ; i < playlist [ 'uids' ] . length ; i ++ ) {
const uid = playlist [ 'uids' ] [ i ] ;
const playlist _file = await db _api . getVideo ( uid , user _to _test ) ;
playlist _files _to _download . push ( playlist _file ) ;
}
const zip _path = await utils . createContainerZipFile ( playlist , playlist _files _to _download ) ;
const zip _exists = fs . pathExistsSync ( zip _path ) ;
assert ( zip _exists ) ;
if ( zip _exists ) fs . unlinkSync ( zip _path ) ;
} ) ;
it ( 'Subscription zip generator' , async function ( ) {
const sub = await subscriptions _api . getSubscription ( sub _to _test , user _to _test ) ;
const sub _videos = await db _api . getRecords ( 'files' , { sub _id : sub . id } ) ;
assert ( sub ) ;
const sub _files _to _download = [ ] ;
for ( let i = 0 ; i < sub _videos . length ; i ++ ) {
const sub _file = sub _videos [ i ] ;
sub _files _to _download . push ( sub _file ) ;
}
const zip _path = await utils . createContainerZipFile ( sub , sub _files _to _download ) ;
const zip _exists = fs . pathExistsSync ( zip _path ) ;
assert ( zip _exists ) ;
if ( zip _exists ) fs . unlinkSync ( zip _path ) ;
} ) ;
} ) ;
// describe('Video player - subscription', function() {
// const sub_to_test = '';
// const video_to_test = 'ebbcfffb-d6f1-4510-ad25-d1ec82e0477e';
// it('Get video', async function() {
// const video_obj = db_api.getVideo(video_to_test, 'admin', );
// assert(video_obj);
// });
// it('Video access - disallowed', async function() {
// await db_api.setVideoProperty(video_to_test, {sharingEnabled: false}, user_to_test, sub_to_test);
// const video_obj = auth_api.getUserVideo('admin', video_to_test, true);
// assert(!video_obj);
// });
// it('Video access - allowed', async function() {
// await db_api.setVideoProperty(video_to_test, {sharingEnabled: true}, user_to_test, sub_to_test);
// const video_obj = auth_api.getUserVideo('admin', video_to_test, true);
// assert(video_obj);
// });
// });
} ) ;
describe ( 'Downloader' , function ( ) {
const downloader _api = require ( '../downloader' ) ;
const url = 'https://www.youtube.com/watch?v=dQw4w9WgXcQ' ;
const sub _id = 'dc834388-3454-41bf-a618-e11cb8c7de1c' ;
const options = {
ui _uid : uuid ( ) ,
user : 'admin'
}
beforeEach ( async function ( ) {
await db _api . connectToDB ( ) ;
await db _api . removeAllRecords ( 'download_queue' ) ;
} ) ;
it ( 'Get file info' , async function ( ) {
} ) ;
it ( 'Download file' , async function ( ) {
this . timeout ( 300000 ) ;
const returned _download = await downloader _api . createDownload ( url , 'video' , options ) ;
console . log ( returned _download ) ;
await utils . wait ( 20000 ) ;
} ) ;
it ( 'Queue file' , async function ( ) {
this . timeout ( 300000 ) ;
const returned _download = await downloader _api . createDownload ( url , 'video' , options ) ;
console . log ( returned _download ) ;
await utils . wait ( 20000 ) ;
} ) ;
it ( 'Pause file' , async function ( ) {
} ) ;
it ( 'Generate args' , async function ( ) {
const args = await downloader _api . generateArgs ( url , 'video' , options ) ;
console . log ( args ) ;
} ) ;
it ( 'Generate args - subscription' , async function ( ) {
subscriptions _api . initialize ( db _api , logger ) ;
const sub = await subscriptions _api . getSubscription ( sub _id ) ;
const sub _options = subscriptions _api . generateOptionsForSubscriptionDownload ( sub , 'admin' ) ;
const args = await downloader _api . generateArgs ( url , 'video' , sub _options , 'admin' ) ;
console . log ( args ) ;
} ) ;
it ( 'Generate kodi NFO file' , async function ( ) {
const nfo _file _path = './test/sample.nfo' ;
if ( fs . existsSync ( nfo _file _path ) ) {
fs . unlinkSync ( nfo _file _path ) ;
}
const sample _json = fs . readJSONSync ( './test/sample.info.json' ) ;
downloader _api . generateNFOFile ( sample _json , nfo _file _path ) ;
assert ( fs . existsSync ( nfo _file _path ) , true ) ;
fs . unlinkSync ( nfo _file _path ) ;
} ) ;
it ( 'Inject args' , async function ( ) {
const original _args = [ '--no-resize-buffer' , '-o' , '%(title)s' , '--no-mtime' ] ;
const new _args = [ '--age-limit' , '25' , '--yes-playlist' , '--abort-on-error' , '-o' , '%(id)s' ] ;
const updated _args = utils . injectArgs ( original _args , new _args ) ;
assert ( updated _args , [ '--no-resize-buffer' , '--no-mtime' , '--age-limit' , '25' , '--yes-playlist' , '--abort-on-error' , '-o' , '%(id)s' ] ) ;
} ) ;
} ) ;
describe ( 'Tasks' , function ( ) {
const tasks _api = require ( '../tasks' ) ;
beforeEach ( async function ( ) {
await db _api . connectToDB ( ) ;
await db _api . removeAllRecords ( 'tasks' ) ;
const dummy _task = {
run : async ( ) => { await utils . wait ( 500 ) ; return true ; } ,
confirm : async ( ) => { await utils . wait ( 500 ) ; return true ; } ,
title : 'Dummy task' ,
job : null
} ;
tasks _api . TASKS [ 'dummy_task' ] = dummy _task ;
await tasks _api . initialize ( ) ;
} ) ;
it ( 'Backup db' , async function ( ) {
const backups _original = await utils . recFindByExt ( 'appdata' , 'bak' ) ;
const original _length = backups _original . length ;
await tasks _api . executeTask ( 'backup_local_db' ) ;
const backups _new = await utils . recFindByExt ( 'appdata' , 'bak' ) ;
const new _length = backups _new . length ;
assert ( original _length , new _length - 1 ) ;
} ) ;
it ( 'Check for missing files' , async function ( ) {
await db _api . removeAllRecords ( 'files' , { uid : 'test' } ) ;
const test _missing _file = { uid : 'test' , path : 'test/missing_file.mp4' } ;
await db _api . insertRecordIntoTable ( 'files' , test _missing _file ) ;
await tasks _api . executeTask ( 'missing_files_check' ) ;
const task _obj = await db _api . getRecord ( 'tasks' , { key : 'missing_files_check' } ) ;
assert ( task _obj [ 'data' ] && task _obj [ 'data' ] [ 'uids' ] && task _obj [ 'data' ] [ 'uids' ] . length >= 1 , true ) ;
} ) ;
it ( 'Check for duplicate files' , async function ( ) {
this . timeout ( 300000 ) ;
await db _api . removeAllRecords ( 'files' , { uid : 'test1' } ) ;
await db _api . removeAllRecords ( 'files' , { uid : 'test2' } ) ;
const test _duplicate _file1 = { uid : 'test1' , path : 'test/missing_file.mp4' } ;
const test _duplicate _file2 = { uid : 'test2' , path : 'test/missing_file.mp4' } ;
const test _duplicate _file3 = { uid : 'test3' , path : 'test/missing_file.mp4' } ;
await db _api . insertRecordIntoTable ( 'files' , test _duplicate _file1 ) ;
await db _api . insertRecordIntoTable ( 'files' , test _duplicate _file2 ) ;
await db _api . insertRecordIntoTable ( 'files' , test _duplicate _file3 ) ;
await tasks _api . executeTask ( 'duplicate_files_check' ) ;
const task _obj = await db _api . getRecord ( 'tasks' , { key : 'duplicate_files_check' } ) ;
const duplicated _record _count = await db _api . getRecords ( 'files' , { path : 'test/missing_file.mp4' } , true ) ;
assert ( task _obj [ 'data' ] && task _obj [ 'data' ] [ 'uids' ] && task _obj [ 'data' ] [ 'uids' ] . length >= 1 , true ) ;
assert ( duplicated _record _count == 1 , true ) ;
} ) ;
it ( 'Import unregistered files' , async function ( ) {
this . timeout ( 300000 ) ;
// pre-test cleanup
await db _api . removeAllRecords ( 'files' , { title : 'Sample File' } ) ;
if ( fs . existsSync ( 'video/sample.info.json' ) ) fs . unlinkSync ( 'video/sample.info.json' ) ;
if ( fs . existsSync ( 'video/sample.mp4' ) ) fs . unlinkSync ( 'video/sample.mp4' ) ;
// copies in files
fs . copyFileSync ( 'test/sample.info.json' , 'video/sample.info.json' ) ;
fs . copyFileSync ( 'test/sample.mp4' , 'video/sample.mp4' ) ;
await tasks _api . executeTask ( 'missing_db_records' ) ;
const imported _file = await db _api . getRecord ( 'files' , { title : 'Sample File' } ) ;
assert ( ! ! imported _file , true ) ;
// post-test cleanup
if ( fs . existsSync ( 'video/sample.info.json' ) ) fs . unlinkSync ( 'video/sample.info.json' ) ;
if ( fs . existsSync ( 'video/sample.mp4' ) ) fs . unlinkSync ( 'video/sample.mp4' ) ;
} ) ;
it ( 'Schedule and cancel task' , async function ( ) {
const today _4 _hours = new Date ( ) ;
today _4 _hours . setHours ( today _4 _hours . getHours ( ) + 4 ) ;
await tasks _api . updateTaskSchedule ( 'dummy_task' , today _4 _hours ) ;
assert ( ! ! tasks _api . TASKS [ 'dummy_task' ] [ 'job' ] , true ) ;
await tasks _api . updateTaskSchedule ( 'dummy_task' , null ) ;
assert ( ! ! tasks _api . TASKS [ 'dummy_task' ] [ 'job' ] , false ) ;
} ) ;
it ( 'Schedule and run task' , async function ( ) {
this . timeout ( 5000 ) ;
const today _1 _second = new Date ( ) ;
today _1 _second . setSeconds ( today _1 _second . getSeconds ( ) + 1 ) ;
await tasks _api . updateTaskSchedule ( 'dummy_task' , today _1 _second ) ;
assert ( ! ! tasks _api . TASKS [ 'dummy_task' ] [ 'job' ] , true ) ;
await utils . wait ( 2000 ) ;
const dummy _task _obj = await db _api . getRecord ( 'tasks' , { key : 'dummy_task' } ) ;
assert ( dummy _task _obj [ 'data' ] , true ) ;
} ) ;
} ) ;