Compare commits

...

3 Commits

Author SHA1 Message Date
Isaac Abadi
c789ba9553 Server autocloses on crash
Thumbnails are now retrieved using file UID
2021-07-31 15:51:16 -06:00
Isaac Abadi
b8e1117ff6 Removed all __dirname references in backend to allow for electron to boot 2021-07-28 21:14:32 -06:00
Isaac Abadi
b64a001ae1 Electron almost boots, but errors presumably due to a filesystem issue (missing folder?) 2021-07-28 19:17:08 -06:00
7 changed files with 1653 additions and 49 deletions

View File

@@ -28,12 +28,22 @@ const { spawn } = require('child_process')
const read_last_lines = require('read-last-lines'); const read_last_lines = require('read-last-lines');
var ps = require('ps-node'); var ps = require('ps-node');
// needed if bin/details somehow gets deleted const using_electron = process.versions['electron'];
const DETAILS_BIN_PATH = 'node_modules/youtube-dl/bin/details'
if (!fs.existsSync(DETAILS_BIN_PATH)) fs.writeJSONSync(DETAILS_BIN_PATH, {"version":"2000.06.06","path":"node_modules\\youtube-dl\\bin\\youtube-dl.exe","exec":"youtube-dl.exe","downloader":"youtube-dl"}) // youtube-dl consts
const YOUTUBE_DL_PATH = using_electron ? 'youtube-dl' : 'node_modules/youtube-dl/bin';
const DETAILS_BIN_PATH = path.join(YOUTUBE_DL_PATH, 'details');
const DEFAULT_BIN_JSON = {"version":"2000.06.06","path": path.join(YOUTUBE_DL_PATH, "youtube-dl.exe"),"exec":"youtube-dl.exe","downloader":"youtube-dl"};
if (!fs.existsSync(DETAILS_BIN_PATH)) fs.writeJSONSync(DETAILS_BIN_PATH, DEFAULT_BIN_JSON);
var youtubedl = require('youtube-dl'); var youtubedl = require('youtube-dl');
if (using_electron) {
youtubedl.setYtdlBinary(YOUTUBE_DL_PATH);
}
// local APIs
var config_api = require('./config.js'); var config_api = require('./config.js');
var subscriptions_api = require('./subscriptions') var subscriptions_api = require('./subscriptions')
var categories_api = require('./categories'); var categories_api = require('./categories');
@@ -136,7 +146,7 @@ var downloadOnlyMode = null;
var useDefaultDownloadingAgent = null; var useDefaultDownloadingAgent = null;
var customDownloadingAgent = null; var customDownloadingAgent = null;
var allowSubscriptions = null; var allowSubscriptions = null;
var archivePath = path.join(__dirname, 'appdata', 'archives'); var archivePath = path.join('appdata', 'archives');
// other needed values // other needed values
var url_domain = null; var url_domain = null;
@@ -317,9 +327,14 @@ async function startServer() {
await setPortItemFromENV(); await setPortItemFromENV();
} }
app.listen(backendPort,function(){ const server = app.listen(backendPort,function(){
logger.info(`YoutubeDL-Material ${CONSTS['CURRENT_VERSION']} started on PORT ${backendPort}`); logger.info(`YoutubeDL-Material ${CONSTS['CURRENT_VERSION']} started on PORT ${backendPort}`);
}); });
process.on('uncaughtException', err => {
logger.error('Server has crashed!');
logger.error(err);
server.close();
})
} }
async function restartServer(is_update = false) { async function restartServer(is_update = false) {
@@ -389,8 +404,8 @@ async function downloadReleaseFiles(tag) {
await downloadReleaseZip(tag); await downloadReleaseZip(tag);
// deletes contents of public dir // deletes contents of public dir
fs.removeSync(path.join(__dirname, 'public')); fs.removeSync(path.join('public'));
fs.mkdirSync(path.join(__dirname, 'public')); fs.mkdirSync(path.join('public'));
let replace_ignore_list = ['youtubedl-material/appdata/default.json', let replace_ignore_list = ['youtubedl-material/appdata/default.json',
'youtubedl-material/appdata/db.json', 'youtubedl-material/appdata/db.json',
@@ -399,7 +414,7 @@ async function downloadReleaseFiles(tag) {
logger.info(`Installing update ${tag}...`) logger.info(`Installing update ${tag}...`)
// downloads new package.json and adds new public dir files from the downloaded zip // downloads new package.json and adds new public dir files from the downloaded zip
fs.createReadStream(path.join(__dirname, `youtubedl-material-release-${tag}.zip`)).pipe(unzipper.Parse()) fs.createReadStream(path.join(`youtubedl-material-release-${tag}.zip`)).pipe(unzipper.Parse())
.on('entry', function (entry) { .on('entry', function (entry) {
var fileName = entry.path; var fileName = entry.path;
var type = entry.type; // 'Directory' or 'File' var type = entry.type; // 'Directory' or 'File'
@@ -409,8 +424,8 @@ async function downloadReleaseFiles(tag) {
// get public folder files // get public folder files
var actualFileName = fileName.replace('youtubedl-material/public/', ''); var actualFileName = fileName.replace('youtubedl-material/public/', '');
if (actualFileName.length !== 0 && actualFileName.substring(actualFileName.length-1, actualFileName.length) !== '/') { if (actualFileName.length !== 0 && actualFileName.substring(actualFileName.length-1, actualFileName.length) !== '/') {
fs.ensureDirSync(path.join(__dirname, 'public', path.dirname(actualFileName))); fs.ensureDirSync(path.join('public', path.dirname(actualFileName)));
entry.pipe(fs.createWriteStream(path.join(__dirname, 'public', actualFileName))); entry.pipe(fs.createWriteStream(path.join('public', actualFileName)));
} else { } else {
entry.autodrain(); entry.autodrain();
} }
@@ -418,7 +433,7 @@ async function downloadReleaseFiles(tag) {
// get package.json // get package.json
var actualFileName = fileName.replace('youtubedl-material/', ''); var actualFileName = fileName.replace('youtubedl-material/', '');
logger.verbose('Downloading file ' + actualFileName); logger.verbose('Downloading file ' + actualFileName);
entry.pipe(fs.createWriteStream(path.join(__dirname, actualFileName))); entry.pipe(fs.createWriteStream(path.join(actualFileName)));
} else { } else {
entry.autodrain(); entry.autodrain();
} }
@@ -464,7 +479,7 @@ async function downloadReleaseZip(tag) {
const tag_without_v = tag.substring(1, tag.length); const tag_without_v = tag.substring(1, tag.length);
const zip_file_name = `youtubedl-material-${tag_without_v}.zip` const zip_file_name = `youtubedl-material-${tag_without_v}.zip`
const latest_zip_link = latest_release_link + zip_file_name; const latest_zip_link = latest_release_link + zip_file_name;
let output_path = path.join(__dirname, `youtubedl-material-release-${tag}.zip`); let output_path = path.join(`youtubedl-material-release-${tag}.zip`);
// download zip from release // download zip from release
await fetchFile(latest_zip_link, output_path, 'update ' + tag); await fetchFile(latest_zip_link, output_path, 'update ' + tag);
@@ -482,10 +497,10 @@ async function installDependencies() {
} }
async function backupServerLite() { async function backupServerLite() {
await fs.ensureDir(path.join(__dirname, 'appdata', 'backups')); await fs.ensureDir(path.join('appdata', 'backups'));
let output_path = path.join('appdata', 'backups', `backup-${Date.now()}.zip`); let output_path = path.join('appdata', 'backups', `backup-${Date.now()}.zip`);
logger.info(`Backing up your non-video/audio files to ${output_path}. This may take up to a few seconds/minutes.`); logger.info(`Backing up your non-video/audio files to ${output_path}. This may take up to a few seconds/minutes.`);
let output = fs.createWriteStream(path.join(__dirname, output_path)); let output = fs.createWriteStream(path.join(output_path));
await new Promise(resolve => { await new Promise(resolve => {
var archive = archiver('zip', { var archive = archiver('zip', {
@@ -1126,7 +1141,7 @@ async function generateArgs(url, type, options) {
} }
if (useCookies) { if (useCookies) {
if (await fs.pathExists(path.join(__dirname, 'appdata', 'cookies.txt'))) { if (await fs.pathExists(path.join('appdata', 'cookies.txt'))) {
downloadConfig.push('--cookies', path.join('appdata', 'cookies.txt')); downloadConfig.push('--cookies', path.join('appdata', 'cookies.txt'));
} else { } else {
logger.warn('Cookies file could not be found. You can either upload one, or disable \'use cookies\' in the Advanced tab in the settings.'); logger.warn('Cookies file could not be found. You can either upload one, or disable \'use cookies\' in the Advanced tab in the settings.');
@@ -1372,8 +1387,8 @@ async function autoUpdateYoutubeDL() {
let stored_binary_path = current_app_details['path']; let stored_binary_path = current_app_details['path'];
if (!stored_binary_path || typeof stored_binary_path !== 'string') { if (!stored_binary_path || typeof stored_binary_path !== 'string') {
// logger.info(`INFO: Failed to get youtube-dl binary path at location: ${DETAILS_BIN_PATH}, attempting to guess actual path...`); // logger.info(`INFO: Failed to get youtube-dl binary path at location: ${DETAILS_BIN_PATH}, attempting to guess actual path...`);
const guessed_base_path = 'node_modules/youtube-dl/bin/'; const guessed_base_path = YOUTUBE_DL_PATH;
const guessed_file_path = guessed_base_path + 'youtube-dl' + (is_windows ? '.exe' : ''); const guessed_file_path = path.join(guessed_base_path, 'youtube-dl' + (is_windows ? '.exe' : ''));
if (fs.existsSync(guessed_file_path)) { if (fs.existsSync(guessed_file_path)) {
stored_binary_path = guessed_file_path; stored_binary_path = guessed_file_path;
// logger.info('INFO: Guess successful! Update process continuing...') // logger.info('INFO: Guess successful! Update process continuing...')
@@ -1424,7 +1439,7 @@ async function downloadLatestYoutubeDLBinary(new_version) {
const file_ext = is_windows ? '.exe' : ''; const file_ext = is_windows ? '.exe' : '';
const download_url = `https://github.com/ytdl-org/youtube-dl/releases/latest/download/youtube-dl${file_ext}`; const download_url = `https://github.com/ytdl-org/youtube-dl/releases/latest/download/youtube-dl${file_ext}`;
const output_path = `node_modules/youtube-dl/bin/youtube-dl${file_ext}`; const output_path = path.join(YOUTUBE_DL_PATH, `youtube-dl${file_ext}`);
await fetchFile(download_url, output_path, `youtube-dl ${new_version}`); await fetchFile(download_url, output_path, `youtube-dl ${new_version}`);
@@ -1435,7 +1450,7 @@ async function downloadLatestYoutubeDLCBinary(new_version) {
const file_ext = is_windows ? '.exe' : ''; const file_ext = is_windows ? '.exe' : '';
const download_url = `https://github.com/blackjack4494/yt-dlc/releases/latest/download/youtube-dlc${file_ext}`; const download_url = `https://github.com/blackjack4494/yt-dlc/releases/latest/download/youtube-dlc${file_ext}`;
const output_path = `node_modules/youtube-dl/bin/youtube-dl${file_ext}`; const output_path = path.join(YOUTUBE_DL_PATH, `youtube-dl${file_ext}`);
await fetchFile(download_url, output_path, `youtube-dlc ${new_version}`); await fetchFile(download_url, output_path, `youtube-dlc ${new_version}`);
@@ -1446,7 +1461,8 @@ async function downloadLatestYoutubeDLPBinary(new_version) {
const file_ext = is_windows ? '.exe' : ''; const file_ext = is_windows ? '.exe' : '';
const download_url = `https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp${file_ext}`; const download_url = `https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp${file_ext}`;
const output_path = `node_modules/youtube-dl/bin/youtube-dl${file_ext}`; const output_path = path.join(YOUTUBE_DL_PATH, `youtube-dl${file_ext}`);
await fetchFile(download_url, output_path, `yt-dlp ${new_version}`); await fetchFile(download_url, output_path, `yt-dlp ${new_version}`);
@@ -2323,7 +2339,7 @@ app.post('/api/downloadFileFromServer', optionalJwt, async (req, res) => {
const file_obj = await db_api.getVideo(uid, uuid, sub_id) const file_obj = await db_api.getVideo(uid, uuid, sub_id)
file_path_to_download = file_obj.path; file_path_to_download = file_obj.path;
} }
if (!path.isAbsolute(file_path_to_download)) file_path_to_download = path.join(__dirname, file_path_to_download); if (!path.isAbsolute(file_path_to_download)) file_path_to_download = path.join(file_path_to_download);
res.sendFile(file_path_to_download, function (err) { res.sendFile(file_path_to_download, function (err) {
if (err) { if (err) {
logger.error(err); logger.error(err);
@@ -2352,9 +2368,9 @@ app.post('/api/downloadArchive', async (req, res) => {
}); });
var upload_multer = multer({ dest: __dirname + '/appdata/' }); var upload_multer = multer({ dest: './appdata/' });
app.post('/api/uploadCookies', upload_multer.single('cookies'), async (req, res) => { app.post('/api/uploadCookies', upload_multer.single('cookies'), async (req, res) => {
const new_path = path.join(__dirname, 'appdata', 'cookies.txt'); const new_path = path.join('appdata', 'cookies.txt');
if (await fs.pathExists(req.file.path)) { if (await fs.pathExists(req.file.path)) {
await fs.rename(req.file.path, new_path); await fs.rename(req.file.path, new_path);
@@ -2463,9 +2479,10 @@ app.get('/api/stream', optionalJwt, async (req, res) => {
} }
}); });
app.get('/api/thumbnail/:path', optionalJwt, async (req, res) => { app.get('/api/thumbnail/:uid', optionalJwt, async (req, res) => {
let file_path = decodeURIComponent(req.params.path); const file_obj = await db_api.getRecord('files', {uid: req.params.uid});
if (fs.existsSync(file_path)) path.isAbsolute(file_path) ? res.sendFile(file_path) : res.sendFile(path.join(__dirname, file_path)); const file_path = file_obj['thumbnailPath'];
if (fs.existsSync(file_path)) res.sendFile(file_path, { root: '.' });
else res.sendStatus(404); else res.sendStatus(404);
}); });
@@ -2636,7 +2653,7 @@ app.post('/api/deleteUser', optionalJwt, async (req, res) => {
try { try {
let success = false; let success = false;
let usersFileFolder = config_api.getConfigItem('ytdl_users_base_path'); let usersFileFolder = config_api.getConfigItem('ytdl_users_base_path');
const user_folder = path.join(__dirname, usersFileFolder, uid); const user_folder = path.join(usersFileFolder, uid);
const user_db_obj = await db_api.getRecord('users', {uid: uid}); const user_db_obj = await db_api.getRecord('users', {uid: uid});
if (user_db_obj) { if (user_db_obj) {
// user exists, let's delete // user exists, let's delete
@@ -2696,12 +2713,12 @@ app.use(function(req, res, next) {
return next(); return next();
} }
let index_path = path.join(__dirname, 'public', 'index.html'); let index_path = path.join('public', 'index.html');
fs.createReadStream(index_path).pipe(res); fs.createReadStream(index_path).pipe(res);
}); });
let public_dir = path.join(__dirname, 'public'); let public_dir = path.join('public');
app.use(express.static(public_dir)); app.use(express.static(public_dir));

View File

@@ -234,7 +234,7 @@ function generateFileObject(id, type, customPath = null, sub = null) {
const ext = (type === 'audio') ? '.mp3' : '.mp4' const ext = (type === 'audio') ? '.mp3' : '.mp4'
const file_path = utils.getTrueFileName(jsonobj['_filename'], type); // path.join(type === 'audio' ? audioFolderPath : videoFolderPath, id + ext); const file_path = utils.getTrueFileName(jsonobj['_filename'], type); // path.join(type === 'audio' ? audioFolderPath : videoFolderPath, id + ext);
// console. // console.
var stats = fs.statSync(path.join(__dirname, file_path)); var stats = fs.statSync(path.join(file_path));
var title = jsonobj.title; var title = jsonobj.title;
var url = jsonobj.webpage_url; var url = jsonobj.webpage_url;
@@ -519,8 +519,8 @@ exports.deleteFile = async (uid, uuid = null, blacklistMode = false) => {
var thumbnailPath = `${filePathNoExtension}.webp`; var thumbnailPath = `${filePathNoExtension}.webp`;
var altThumbnailPath = `${filePathNoExtension}.jpg`; var altThumbnailPath = `${filePathNoExtension}.jpg`;
jsonPath = path.join(__dirname, jsonPath); jsonPath = path.join(jsonPath);
altJSONPath = path.join(__dirname, altJSONPath); altJSONPath = path.join(altJSONPath);
let jsonExists = await fs.pathExists(jsonPath); let jsonExists = await fs.pathExists(jsonPath);
let thumbnailExists = await fs.pathExists(thumbnailPath); let thumbnailExists = await fs.pathExists(thumbnailPath);

View File

@@ -1,6 +1,7 @@
const { app, BrowserWindow } = require('electron'); const { app, BrowserWindow } = require('electron');
const path = require('path'); const path = require('path');
const url = require('url'); const url = require('url');
const server = require('./app');
let win; let win;
@@ -8,13 +9,7 @@ function createWindow() {
win = new BrowserWindow({ width: 800, height: 600 }); win = new BrowserWindow({ width: 800, height: 600 });
// load the dist folder from Angular // load the dist folder from Angular
win.loadURL( win.loadURL('http://localhost:17442') //ADD THIS
url.format({
pathname: path.join(__dirname, `/dist/index.html`),
protocol: 'file:',
slashes: true
})
);
// The following is optional and will open the DevTools: // The following is optional and will open the DevTools:
// win.webContents.openDevTools() // win.webContents.openDevTools()

1578
backend/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,11 +2,14 @@
"name": "backend", "name": "backend",
"version": "1.0.0", "version": "1.0.0",
"description": "backend for YoutubeDL-Material", "description": "backend for YoutubeDL-Material",
"main": "index.js", "main": "main.js",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"start": "nodemon app.js", "start": "nodemon app.js",
"debug": "set YTDL_MODE=debug && node app.js" "debug": "set YTDL_MODE=debug && node app.js",
"electron": "electron main.js",
"pack": "electron-builder --dir",
"dist": "electron-builder"
}, },
"nodemonConfig": { "nodemonConfig": {
"ignore": [ "ignore": [
@@ -19,6 +22,13 @@
"restart_general.json" "restart_general.json"
] ]
}, },
"build": {
"appId": "youtubedl.material",
"mac": {
"category": "public.app-category.utilities"
},
"files": ["!audio/*", "!video/*", "!users/*", "!subscriptions/*", "!appdata/*"]
},
"repository": { "repository": {
"type": "git", "type": "git",
"url": "" "url": ""
@@ -65,5 +75,9 @@
"uuidv4": "^6.0.6", "uuidv4": "^6.0.6",
"winston": "^3.2.1", "winston": "^3.2.1",
"youtube-dl": "^3.0.2" "youtube-dl": "^3.0.2"
},
"devDependencies": {
"electron": "^13.1.7",
"electron-builder": "^22.11.7"
} }
} }

View File

@@ -72,7 +72,7 @@ async function getSubscriptionInfo(sub, user_uid = null) {
let downloadConfig = ['--dump-json', '--playlist-end', '1']; let downloadConfig = ['--dump-json', '--playlist-end', '1'];
let useCookies = config_api.getConfigItem('ytdl_use_cookies'); let useCookies = config_api.getConfigItem('ytdl_use_cookies');
if (useCookies) { if (useCookies) {
if (await fs.pathExists(path.join(__dirname, 'appdata', 'cookies.txt'))) { if (await fs.pathExists(path.join('appdata', 'cookies.txt'))) {
downloadConfig.push('--cookies', path.join('appdata', 'cookies.txt')); downloadConfig.push('--cookies', path.join('appdata', 'cookies.txt'));
} else { } else {
logger.warn('Cookies file could not be found. You can either upload one, or disable \'use cookies\' in the Advanced tab in the settings.'); logger.warn('Cookies file could not be found. You can either upload one, or disable \'use cookies\' in the Advanced tab in the settings.');
@@ -117,7 +117,7 @@ async function getSubscriptionInfo(sub, user_uid = null) {
const useArchive = config_api.getConfigItem('ytdl_use_youtubedl_archive'); const useArchive = config_api.getConfigItem('ytdl_use_youtubedl_archive');
if (useArchive && !sub.archive) { if (useArchive && !sub.archive) {
// must create the archive // must create the archive
const archive_dir = path.join(__dirname, basePath, 'archives', sub.name); const archive_dir = path.join(basePath, 'archives', sub.name);
const archive_path = path.join(archive_dir, 'archive.txt'); const archive_path = path.join(archive_dir, 'archive.txt');
// creates archive directory and text file if it doesn't exist // creates archive directory and text file if it doesn't exist
@@ -185,10 +185,10 @@ async function deleteSubscriptionFile(sub, file, deleteForever, file_uid = null,
let filePath = appendedBasePath; let filePath = appendedBasePath;
const ext = (sub.type && sub.type === 'audio') ? '.mp3' : '.mp4' const ext = (sub.type && sub.type === 'audio') ? '.mp3' : '.mp4'
var jsonPath = path.join(__dirname,filePath,name+'.info.json'); var jsonPath = path.join(filePath,name+'.info.json');
var videoFilePath = path.join(__dirname,filePath,name+ext); var videoFilePath = path.join(filePath,name+ext);
var imageFilePath = path.join(__dirname,filePath,name+'.jpg'); var imageFilePath = path.join(filePath,name+'.jpg');
var altImageFilePath = path.join(__dirname,filePath,name+'.webp'); var altImageFilePath = path.join(filePath,name+'.webp');
const [jsonExists, videoFileExists, imageFileExists, altImageFileExists] = await Promise.all([ const [jsonExists, videoFileExists, imageFileExists, altImageFileExists] = await Promise.all([
fs.pathExists(jsonPath), fs.pathExists(jsonPath),
@@ -402,7 +402,7 @@ async function generateArgsForSubscription(sub, user_uid, redownload = false, de
let useCookies = config_api.getConfigItem('ytdl_use_cookies'); let useCookies = config_api.getConfigItem('ytdl_use_cookies');
if (useCookies) { if (useCookies) {
if (await fs.pathExists(path.join(__dirname, 'appdata', 'cookies.txt'))) { if (await fs.pathExists(path.join('appdata', 'cookies.txt'))) {
downloadConfig.push('--cookies', path.join('appdata', 'cookies.txt')); downloadConfig.push('--cookies', path.join('appdata', 'cookies.txt'));
} else { } else {
logger.warn('Cookies file could not be found. You can either upload one, or disable \'use cookies\' in the Advanced tab in the settings.'); logger.warn('Cookies file could not be found. You can either upload one, or disable \'use cookies\' in the Advanced tab in the settings.');

View File

@@ -72,7 +72,7 @@ export class UnifiedFileCardComponent implements OnInit {
} }
if (this.file_obj && this.file_obj.thumbnailPath) { if (this.file_obj && this.file_obj.thumbnailPath) {
this.thumbnailBlobURL = `${this.baseStreamPath}thumbnail/${encodeURIComponent(this.file_obj.thumbnailPath)}${this.jwtString}`; this.thumbnailBlobURL = `${this.baseStreamPath}thumbnail/${this.file_obj.uid}${this.jwtString}`;
/*const mime = getMimeByFilename(this.file_obj.thumbnailPath); /*const mime = getMimeByFilename(this.file_obj.thumbnailPath);
const blob = new Blob([new Uint8Array(this.file_obj.thumbnailBlob.data)], {type: mime}); const blob = new Blob([new Uint8Array(this.file_obj.thumbnailBlob.data)], {type: mime});
const bloburl = URL.createObjectURL(blob); const bloburl = URL.createObjectURL(blob);