From df11aca1e0d4fe5bc7991944f0ec117100e2422b Mon Sep 17 00:00:00 2001 From: Isaac Grynsztein Date: Mon, 30 Mar 2020 18:35:44 -0400 Subject: [PATCH 01/18] Added preliminary support for updating YoutubeDL-Material Config items that are not found use and set the default value Fixed potential error while updated youtube-dl binaries --- backend/app.js | 195 +++++++++++++++++++++++++++++++++++++++++-- backend/config.js | 8 +- backend/consts.js | 5 +- backend/package.json | 4 +- 4 files changed, 203 insertions(+), 9 deletions(-) diff --git a/backend/app.js b/backend/app.js index 5c6c5e4..607e97c 100644 --- a/backend/app.js +++ b/backend/app.js @@ -1,6 +1,6 @@ var async = require('async'); const { uuid } = require('uuidv4'); -var fs = require('fs'); +var fs = require('fs-extra'); var path = require('path'); var youtubedl = require('youtube-dl'); var compression = require('compression'); @@ -8,6 +8,7 @@ var https = require('https'); var express = require("express"); var bodyParser = require("body-parser"); var archiver = require('archiver'); +var unzipper = require('unzipper'); var mergeFiles = require('merge-files'); const low = require('lowdb') var md5 = require('md5'); @@ -19,6 +20,7 @@ const shortid = require('shortid') const url_api = require('url'); var config_api = require('./config.js'); var subscriptions_api = require('./subscriptions') +const CONSTS = require('./consts') var app = express(); @@ -26,6 +28,8 @@ const FileSync = require('lowdb/adapters/FileSync') const adapter = new FileSync('./appdata/db.json'); const db = low(adapter) +// var GithubContent = require('github-content'); + // Set some defaults db.defaults( { @@ -131,6 +135,170 @@ async function startServer() { console.log("HTTP: Started on PORT " + backendPort); }); } + + // getLatestVersion(); + // updateServer(); +} + +async function restartServer() { + const restartProcess = () => { + spawn(process.argv[1], process.argv.slice(2), { + detached: true, + stdio: ['ignore', out, err] + }).unref() + process.exit() + } +} + +async function updateServer() { + const new_version_available = await isNewVersionAvailable(); + if (!new_version_available) { + console.log('ERROR: Failed to update - no update is available.'); + return false; + } + return new Promise(async resolve => { + // backup current dir + let backup_succeeded = await backupServerLite(); + if (!backup_succeeded) { + resolve(false); + return false; + } + + // grab new package.json and public folder + await downloadUpdateFiles(); + }); +} + +async function downloadUpdateFiles() { + let tag = await getLatestVersion(); + return new Promise(async resolve => { + var options = { + owner: 'tzahi12345', + repo: 'YoutubeDL-Material', + branch: tag + }; + + // downloads the latest release zip file + await downloadLatestRelease(tag); + + // deletes contents of public dir + fs.removeSync(path.join(__dirname, 'public')); + fs.mkdirSync(path.join(__dirname, 'public')); + + // downloads new package.json and adds new public dir files from the downloaded zip + fs.createReadStream(path.join(__dirname, 'youtubedl-material-latest-release.zip')).pipe(unzipper.Parse()) + .on('entry', function (entry) { + var fileName = entry.path; + var type = entry.type; // 'Directory' or 'File' + var size = entry.size; + if (fileName.includes('youtubedl-material/public/')) { + // get public folder files + var actualFileName = fileName.replace('youtubedl-material/public/', ''); + if (actualFileName.length !== 0 && actualFileName.substring(actualFileName.length-1, actualFileName.length) !== '/') { + fs.ensureDirSync(path.join(__dirname, 'public', path.dirname(actualFileName))); + entry.pipe(fs.createWriteStream(path.join(__dirname, 'public', actualFileName))); + } else { + entry.autodrain(); + } + } else if (fileName === 'youtubedl-material/package.json') { + // get package.json + entry.pipe(fs.createWriteStream(path.join(__dirname, 'package.json'))); + } else { + entry.autodrain(); + } + }); + }); +} + +async function downloadLatestRelease(tag) { + return new Promise(async resolve => { + // get name of latest zip file, which depends on the version + const latest_release_link = 'https://github.com/Tzahi12345/YoutubeDL-Material/releases/latest/download/'; + const tag_without_v = tag.substring(1, tag.length); + const zip_file_name = `youtubedl-material-${tag_without_v}.zip` + const latest_zip_link = latest_release_link + zip_file_name; + let output_path = path.join(__dirname, `youtubedl-material-latest-release.zip`); + + // download zip from release + await fetchFile(latest_zip_link, output_path); + resolve(true); + }); + +} +// helper function to download file using fetch +const fetchFile = (async (url, path) => { + const res = await fetch(url); + const fileStream = fs.createWriteStream(path); + await new Promise((resolve, reject) => { + res.body.pipe(fileStream); + res.body.on("error", (err) => { + reject(err); + }); + fileStream.on("finish", function() { + resolve(); + }); + }); + }); + +async function backupServerLite() { + return new Promise(async resolve => { + let output_path = `backup-${Date.now()}.zip`; + console.log(`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)); + var archive = archiver('zip', { + gzip: true, + zlib: { level: 9 } // Sets the compression level. + }); + + archive.on('error', function(err) { + console.log(err); + resolve(false); + }); + + // pipe archive data to the output file + archive.pipe(output); + + // ignore certain directories (ones with video or audio files) + const files_to_ignore = [path.join(config_api.getConfigItem('ytdl_subscriptions_base_path'), '**'), + path.join(config_api.getConfigItem('ytdl_audio_folder_path'), '**'), + path.join(config_api.getConfigItem('ytdl_video_folder_path'), '**'), + 'backup-*.zip']; + + archive.glob('**/*', { + ignore: files_to_ignore + }); + + await archive.finalize(); + + // wait a tiny bit for the zip to reload in fs + setTimeout(function() { + resolve(true); + }, 100); + }); +} + +async function isNewVersionAvailable() { + return new Promise(async resolve => { + // gets tag of the latest version of youtubedl-material, compare to current version + const latest_tag = await getLatestVersion(); + const current_tag = CONSTS['CURRENT_VERSION']; + if (latest_tag > current_tag) { + resolve(true); + } else { + resolve(false); + } + }); +} + +async function getLatestVersion() { + return new Promise(resolve => { + fetch('https://api.github.com/repos/tzahi12345/youtubedl-material/releases/latest', {method: 'Get'}) + .then(async res => res.json()) + .then(async (json) => { + resolve(json['tag_name']); + return; + }); + }); } async function setPortItemFromENV() { @@ -143,7 +311,6 @@ async function setPortItemFromENV() { async function setAndLoadConfig() { await setConfigFromEnv(); await loadConfig(); - // console.log(backendUrl); } async function setConfigFromEnv() { @@ -162,9 +329,6 @@ async function setConfigFromEnv() { async function loadConfig() { return new Promise(resolve => { - // get config library - // config = require('config'); - url = !debugMode ? config_api.getConfigItem('ytdl_url') : 'http://localhost:4200'; backendPort = config_api.getConfigItem('ytdl_port'); usingEncryption = config_api.getConfigItem('ytdl_use_encryption'); @@ -676,7 +840,11 @@ async function autoUpdateYoutubeDL() { .then(async res => res.json()) .then(async (json) => { // check if the versions are different - const latest_update_version = json[0]['name']; + if (!json || !json[0]) { + resolve(false); + return false; + } + const latest_update_version = json[0]['name']; if (current_version !== latest_update_version) { let binary_path = 'node_modules/youtube-dl/bin'; // versions different, download new update @@ -729,6 +897,21 @@ async function checkExistsWithTimeout(filePath, timeout) { }); } +// https://stackoverflow.com/a/32197381/8088021 +const deleteFolderRecursive = function(folder_to_delete) { + if (fs.existsSync(folder_to_delete)) { + fs.readdirSync(folder_to_delete).forEach((file, index) => { + const curPath = path.join(folder_to_delete, file); + if (fs.lstatSync(curPath).isDirectory()) { // recurse + deleteFolderRecursive(curPath); + } else { // delete file + fs.unlinkSync(curPath); + } + }); + fs.rmdirSync(folder_to_delete); + } +}; + app.use(function(req, res, next) { res.header("Access-Control-Allow-Origin", getOrigin()); res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); diff --git a/backend/config.js b/backend/config.js index 14d9b12..33d425d 100644 --- a/backend/config.js +++ b/backend/config.js @@ -68,10 +68,16 @@ function setConfigFile(config) { function getConfigItem(key) { let config_json = getConfigFile(); if (!CONFIG_ITEMS[key]) { - console.log('cannot find config with key ' + key); + console.log(`ERROR: Config item with key '${key}' is not recognized.`); return null; } let path = CONFIG_ITEMS[key]['path']; + const val = Object.byString(config_json, path); + if (val === undefined && Object.byString(DEFAULT_CONFIG, path)) { + console.log(`WARNING: Cannot find config with key '${key}'. Creating one with the default value...`); + setConfigItem(key, Object.byString(DEFAULT_CONFIG, path)); + return Object.byString(DEFAULT_CONFIG, path); + } return Object.byString(config_json, path); }; diff --git a/backend/consts.js b/backend/consts.js index 0483521..0cc0f44 100644 --- a/backend/consts.js +++ b/backend/consts.js @@ -124,4 +124,7 @@ let CONFIG_ITEMS = { }, }; -module.exports.CONFIG_ITEMS = CONFIG_ITEMS; \ No newline at end of file +module.exports = { + CONFIG_ITEMS: CONFIG_ITEMS, + CURRENT_VERSION: 'v3.5.1' +} \ No newline at end of file diff --git a/backend/package.json b/backend/package.json index 80426a5..70684d0 100644 --- a/backend/package.json +++ b/backend/package.json @@ -24,12 +24,14 @@ "config": "^3.2.3", "exe": "^1.0.2", "express": "^4.17.1", + "fs-extra": "^9.0.0", "lowdb": "^1.0.0", "md5": "^2.2.1", - "node-id3": "^0.1.14", "merge-files": "^0.1.2", "node-fetch": "^2.6.0", + "node-id3": "^0.1.14", "shortid": "^2.2.15", + "unzipper": "^0.10.10", "uuidv4": "^6.0.6", "youtube-dl": "^3.0.2" } From 38774d859347a0da4bfbc71962aa3c13c0c18fb9 Mon Sep 17 00:00:00 2001 From: Tzahi12345 Date: Mon, 30 Mar 2020 23:15:31 -0400 Subject: [PATCH 02/18] Updater now grab new backend files youtube-dl auto updater now guesses binary path, which makes the update process work much more reliably --- backend/app.js | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/backend/app.js b/backend/app.js index 607e97c..5038dcc 100644 --- a/backend/app.js +++ b/backend/app.js @@ -185,6 +185,9 @@ async function downloadUpdateFiles() { fs.removeSync(path.join(__dirname, 'public')); fs.mkdirSync(path.join(__dirname, 'public')); + let replace_ignore_list = ['youtubedl-material/appdata/default.json', + 'youtubedl-material/appdata/db.json'] + // downloads new package.json and adds new public dir files from the downloaded zip fs.createReadStream(path.join(__dirname, 'youtubedl-material-latest-release.zip')).pipe(unzipper.Parse()) .on('entry', function (entry) { @@ -200,7 +203,7 @@ async function downloadUpdateFiles() { } else { entry.autodrain(); } - } else if (fileName === 'youtubedl-material/package.json') { + } else if (!replace_ignore_list.includes(fileName)) { // get package.json entry.pipe(fs.createWriteStream(path.join(__dirname, 'package.json'))); } else { @@ -821,7 +824,7 @@ async function autoUpdateYoutubeDL() { let current_app_details_path = 'node_modules/youtube-dl/bin/details'; let current_app_details_exists = fs.existsSync(current_app_details_path); if (!current_app_details_exists) { - console.log(`Failed to get youtube-dl binary details at location: ${current_app_details_path}. Cancelling update check.`); + console.log(`ERROR: Failed to get youtube-dl binary details at location '${current_app_details_path}'. Cancelling update check.`); resolve(false); return; } @@ -829,9 +832,17 @@ async function autoUpdateYoutubeDL() { let current_version = current_app_details['version']; let stored_binary_path = current_app_details['path']; if (!stored_binary_path || typeof stored_binary_path !== 'string') { - console.log(`Failed to get youtube-dl binary path at location: ${current_app_details_path}. Cancelling update check.`); - resolve(false); - return; + // console.log(`INFO: Failed to get youtube-dl binary path at location: ${current_app_details_path}, attempting to guess actual path...`); + const guessed_base_path = 'node_modules/youtube-dl/bin/'; + const guessed_file_path = guessed_base_path + 'youtube-dl' + (process.platform === 'win32' ? '.exe' : ''); + if (fs.existsSync(guessed_file_path)) { + stored_binary_path = guessed_file_path; + // console.log('INFO: Guess successful! Update process continuing...') + } else { + console.log(`ERROR: Guess '${guessed_file_path}' is not correct. Cancelling update check. Verify that your youtube-dl binaries exist by running npm install.`); + resolve(false); + return; + } } // got version, now let's check the latest version from the youtube-dl API From 785306c59a8211b83a12b07f141a175e4d85dea8 Mon Sep 17 00:00:00 2001 From: Isaac Grynsztein Date: Mon, 30 Mar 2020 23:20:52 -0400 Subject: [PATCH 03/18] Added debug statements --- backend/app.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/backend/app.js b/backend/app.js index 607e97c..7b273da 100644 --- a/backend/app.js +++ b/backend/app.js @@ -137,10 +137,11 @@ async function startServer() { } // getLatestVersion(); - // updateServer(); + updateServer(); } async function restartServer() { + console.log('Restarting server...'); const restartProcess = () => { spawn(process.argv[1], process.argv.slice(2), { detached: true, @@ -166,12 +167,15 @@ async function updateServer() { // grab new package.json and public folder await downloadUpdateFiles(); + + restartServer(); }); } async function downloadUpdateFiles() { let tag = await getLatestVersion(); return new Promise(async resolve => { + console.log('Downloading new files...') var options = { owner: 'tzahi12345', repo: 'YoutubeDL-Material', @@ -185,6 +189,8 @@ async function downloadUpdateFiles() { fs.removeSync(path.join(__dirname, 'public')); fs.mkdirSync(path.join(__dirname, 'public')); + console.log(`Installing update ${tag}...`) + // downloads new package.json and adds new public dir files from the downloaded zip fs.createReadStream(path.join(__dirname, 'youtubedl-material-latest-release.zip')).pipe(unzipper.Parse()) .on('entry', function (entry) { @@ -206,6 +212,9 @@ async function downloadUpdateFiles() { } else { entry.autodrain(); } + }) + .on('close', function () { + resolve(true); }); }); } From b3dbdd179050cd9b0f48670167fd58a2291eb42d Mon Sep 17 00:00:00 2001 From: Tzahi12345 Date: Mon, 30 Mar 2020 23:24:33 -0400 Subject: [PATCH 04/18] Cleaning working folder --- backend/app.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/backend/app.js b/backend/app.js index 5038dcc..f4f0b2d 100644 --- a/backend/app.js +++ b/backend/app.js @@ -137,7 +137,7 @@ async function startServer() { } // getLatestVersion(); - // updateServer(); + updateServer(); } async function restartServer() { @@ -194,7 +194,8 @@ async function downloadUpdateFiles() { var fileName = entry.path; var type = entry.type; // 'Directory' or 'File' var size = entry.size; - if (fileName.includes('youtubedl-material/public/')) { + var is_dir = fileName.substring(fileName.length-1, fileName.length) !== '/' + if (!is_dir && fileName.includes('youtubedl-material/public/')) { // get public folder files var actualFileName = fileName.replace('youtubedl-material/public/', ''); if (actualFileName.length !== 0 && actualFileName.substring(actualFileName.length-1, actualFileName.length) !== '/') { @@ -203,8 +204,9 @@ async function downloadUpdateFiles() { } else { entry.autodrain(); } - } else if (!replace_ignore_list.includes(fileName)) { + } else if (!is_dir && !replace_ignore_list.includes(fileName)) { // get package.json + console.log('Downloading file ' + fileName); entry.pipe(fs.createWriteStream(path.join(__dirname, 'package.json'))); } else { entry.autodrain(); From af4de440167abe526fd9c311b9d4163c05e66283 Mon Sep 17 00:00:00 2001 From: Tzahi12345 Date: Mon, 30 Mar 2020 23:25:58 -0400 Subject: [PATCH 05/18] Further merge --- backend/app.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/backend/app.js b/backend/app.js index 61bfc6b..760ac31 100644 --- a/backend/app.js +++ b/backend/app.js @@ -189,12 +189,9 @@ async function downloadUpdateFiles() { fs.removeSync(path.join(__dirname, 'public')); fs.mkdirSync(path.join(__dirname, 'public')); -<<<<<<< HEAD let replace_ignore_list = ['youtubedl-material/appdata/default.json', 'youtubedl-material/appdata/db.json'] -======= console.log(`Installing update ${tag}...`) ->>>>>>> 785306c59a8211b83a12b07f141a175e4d85dea8 // downloads new package.json and adds new public dir files from the downloaded zip fs.createReadStream(path.join(__dirname, 'youtubedl-material-latest-release.zip')).pipe(unzipper.Parse()) From 82df232f037119ef4f72b30d7b2cf434537a94f6 Mon Sep 17 00:00:00 2001 From: Tzahi12345 Date: Mon, 30 Mar 2020 23:36:35 -0400 Subject: [PATCH 06/18] Update process now properly gets required backend files --- backend/app.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/backend/app.js b/backend/app.js index 760ac31..13056c1 100644 --- a/backend/app.js +++ b/backend/app.js @@ -199,7 +199,7 @@ async function downloadUpdateFiles() { var fileName = entry.path; var type = entry.type; // 'Directory' or 'File' var size = entry.size; - var is_dir = fileName.substring(fileName.length-1, fileName.length) !== '/' + var is_dir = fileName.substring(fileName.length-1, fileName.length) === '/' if (!is_dir && fileName.includes('youtubedl-material/public/')) { // get public folder files var actualFileName = fileName.replace('youtubedl-material/public/', ''); @@ -211,8 +211,9 @@ async function downloadUpdateFiles() { } } else if (!is_dir && !replace_ignore_list.includes(fileName)) { // get package.json - console.log('Downloading file ' + fileName); - entry.pipe(fs.createWriteStream(path.join(__dirname, 'package.json'))); + var actualFileName = fileName.replace('youtubedl-material/', ''); + if (debugMode) console.log('Downloading file ' + actualFileName); + entry.pipe(fs.createWriteStream(path.join(__dirname, actualFileName))); } else { entry.autodrain(); } From 1b09bf4881ea075855aabe0fb4349c7cb8353b5e Mon Sep 17 00:00:00 2001 From: Isaac Grynsztein Date: Tue, 31 Mar 2020 01:56:15 -0400 Subject: [PATCH 07/18] nodemon now supported --- backend/package.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/backend/package.json b/backend/package.json index 70684d0..b2b57e6 100644 --- a/backend/package.json +++ b/backend/package.json @@ -5,7 +5,10 @@ "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "start": "node app.js" + "start": "nodemon --verbose app.js" + }, + "nodemonConfig": { + "ignore": ["*"] }, "repository": { "type": "git", @@ -30,6 +33,7 @@ "merge-files": "^0.1.2", "node-fetch": "^2.6.0", "node-id3": "^0.1.14", + "nodemon": "^2.0.2", "shortid": "^2.2.15", "unzipper": "^0.10.10", "uuidv4": "^6.0.6", From b71bdfcec29fa4c1e281ced0e4cf613a16fbe233 Mon Sep 17 00:00:00 2001 From: Isaac Grynsztein Date: Tue, 31 Mar 2020 04:19:42 -0400 Subject: [PATCH 08/18] Updated nodemon package.json config --- backend/package.json | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/backend/package.json b/backend/package.json index b2b57e6..5093a8c 100644 --- a/backend/package.json +++ b/backend/package.json @@ -5,10 +5,17 @@ "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "start": "nodemon --verbose app.js" + "start": "nodemon app.js" }, "nodemonConfig": { - "ignore": ["*"] + "ignore": [ + "*.js", + "appdata/*", + "public/*" + ], + "watch": [ + "restart.json" + ] }, "repository": { "type": "git", From 37614a161192475ae87a854eddee3d4da4868347 Mon Sep 17 00:00:00 2001 From: Isaac Grynsztein Date: Tue, 31 Mar 2020 04:20:46 -0400 Subject: [PATCH 09/18] Changed backend logging for server start to give more information (namely version) --- backend/app.js | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/backend/app.js b/backend/app.js index 13056c1..f2b023d 100644 --- a/backend/app.js +++ b/backend/app.js @@ -21,6 +21,7 @@ const url_api = require('url'); var config_api = require('./config.js'); var subscriptions_api = require('./subscriptions') const CONSTS = require('./consts') +const { spawn } = require('child_process') var app = express(); @@ -126,29 +127,33 @@ async function startServer() { if (usingEncryption) { https.createServer(options, app).listen(backendPort, function() { - console.log('HTTPS: Started on PORT ' + backendPort); + console.log(`YoutubeDL-Material ${CONSTS['CURRENT_VERSION']} started on port ${backendPort} - using SSL`); }); } else { app.listen(backendPort,function(){ - console.log("HTTP: Started on PORT " + backendPort); + console.log(`YoutubeDL-Material ${CONSTS['CURRENT_VERSION']} started on PORT ${backendPort}`); }); } // getLatestVersion(); + // restartServer(); updateServer(); } async function restartServer() { - console.log('Restarting server...'); const restartProcess = () => { - spawn(process.argv[1], process.argv.slice(2), { + spawn('node', ['app.js'], { detached: true, - stdio: ['ignore', out, err] + stdio: 'inherit' }).unref() process.exit() - } + } + console.log('Update complete! Restarting server...'); + + // the following line restarts the server through nodemon + fs.writeFileSync('restart.json', 'internal use only'); } async function updateServer() { @@ -309,6 +314,12 @@ async function getLatestVersion() { fetch('https://api.github.com/repos/tzahi12345/youtubedl-material/releases/latest', {method: 'Get'}) .then(async res => res.json()) .then(async (json) => { + if (json['message']) { + // means there's an error in getting latest version + console.log(`ERROR: Received the following message from GitHub's API:`); + console.log(json['message']); + if (json['documentation_url']) console.log(`Associated URL: ${json['documentation_url']}`) + } resolve(json['tag_name']); return; }); From c2ee6b6230f2b1f721769cb0673371592b804454 Mon Sep 17 00:00:00 2001 From: Isaac Grynsztein Date: Wed, 1 Apr 2020 01:06:55 -0400 Subject: [PATCH 10/18] update package.json version --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 3f80dd9..cd98ad6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "youtube-dl-material", - "version": "3.5.0", + "version": "3.5.1", "license": "MIT", "scripts": { "ng": "ng", @@ -47,8 +47,8 @@ "zone.js": "~0.10.2" }, "devDependencies": { - "@angular-devkit/build-angular": "~0.900.6", - "@angular/cli": "^9.0.6", + "@angular-devkit/build-angular": "^0.901.0", + "@angular/cli": "^9.1.0", "@angular/compiler-cli": "^9.0.6", "@angular/language-service": "^9.0.6", "@locl/cli": "0.0.1-beta.6", From a5224f80a8c42c9ca2f60c1431001cbbf146f568 Mon Sep 17 00:00:00 2001 From: Isaac Grynsztein Date: Wed, 1 Apr 2020 01:07:08 -0400 Subject: [PATCH 11/18] nodemon now runs silently --- backend/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/package.json b/backend/package.json index 5093a8c..c3a814d 100644 --- a/backend/package.json +++ b/backend/package.json @@ -5,7 +5,7 @@ "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "start": "nodemon app.js" + "start": "nodemon -q app.js" }, "nodemonConfig": { "ignore": [ From 58718b6e3b67380fb1bce782c0734bbabcdb0b28 Mon Sep 17 00:00:00 2001 From: Isaac Grynsztein Date: Wed, 1 Apr 2020 01:11:26 -0400 Subject: [PATCH 12/18] Removed @ngular/http dependency --- package.json | 1 - src/app/app.module.ts | 2 -- 2 files changed, 3 deletions(-) diff --git a/package.json b/package.json index cd98ad6..544d2cf 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,6 @@ "@angular/compiler": "^9.0.6", "@angular/core": "^9.0.6", "@angular/forms": "^9.0.6", - "@angular/http": "^7.2.15", "@angular/localize": "^9.0.6", "@angular/material": "^9.1.2", "@angular/platform-browser": "^9.0.6", diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 2d2bd77..6ca713b 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -27,7 +27,6 @@ import { MatToolbarModule } from '@angular/material/toolbar'; import {DragDropModule} from '@angular/cdk/drag-drop'; import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import { AppComponent } from './app.component'; -import { HttpModule } from '@angular/http'; import { HttpClientModule, HttpClient } from '@angular/common/http'; import { PostsService } from 'app/posts.services'; import { FileCardComponent } from './file-card/file-card.component'; @@ -90,7 +89,6 @@ export function isVisible({ event, element, scrollContainer, offset }: IsVisible MatInputModule, MatSelectModule, ReactiveFormsModule, - HttpModule, HttpClientModule, MatToolbarModule, MatCardModule, From 89ececdbeb1d73f4eb7152b27fcc255a6a32fa8d Mon Sep 17 00:00:00 2001 From: Isaac Grynsztein Date: Wed, 1 Apr 2020 19:31:41 -0400 Subject: [PATCH 13/18] Dependencies now install during update --- backend/app.js | 16 +++++++++++++--- package.json | 24 ++++++++++++------------ 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/backend/app.js b/backend/app.js index f2b023d..33e1647 100644 --- a/backend/app.js +++ b/backend/app.js @@ -137,9 +137,6 @@ async function startServer() { }); } - // getLatestVersion(); - // restartServer(); - updateServer(); } async function restartServer() { @@ -173,6 +170,9 @@ async function updateServer() { // grab new package.json and public folder await downloadUpdateFiles(); + // run npm install + await installDependencies(); + restartServer(); }); } @@ -244,6 +244,16 @@ async function downloadLatestRelease(tag) { }); } + +async function installDependencies() { + return new Promise(resolve => { + var child_process = require('child_process'); + child_process.execSync('npm install',{stdio:[0,1,2]}); + resolve(true); + }); + +} + // helper function to download file using fetch const fetchFile = (async (url, path) => { const res = await fetch(url); diff --git a/package.json b/package.json index 544d2cf..d98f1de 100644 --- a/package.json +++ b/package.json @@ -19,17 +19,17 @@ "private": true, "dependencies": { "@angular-devkit/core": "^9.0.6", - "@angular/animations": "^9.0.6", + "@angular/animations": "^9.1.0", "@angular/cdk": "^9.1.2", - "@angular/common": "^9.0.6", - "@angular/compiler": "^9.0.6", - "@angular/core": "^9.0.6", - "@angular/forms": "^9.0.6", - "@angular/localize": "^9.0.6", - "@angular/material": "^9.1.2", - "@angular/platform-browser": "^9.0.6", - "@angular/platform-browser-dynamic": "^9.0.6", - "@angular/router": "^9.0.6", + "@angular/common": "^9.1.0", + "@angular/compiler": "^9.1.0", + "@angular/core": "^9.1.0", + "@angular/forms": "^9.1.0", + "@angular/localize": "^9.1.0", + "@angular/material": "^9.2.0", + "@angular/platform-browser": "^9.1.0", + "@angular/platform-browser-dynamic": "^9.1.0", + "@angular/router": "^9.1.0", "@locl/core": "0.0.1-beta.2", "core-js": "^2.4.1", "file-saver": "^2.0.2", @@ -48,8 +48,8 @@ "devDependencies": { "@angular-devkit/build-angular": "^0.901.0", "@angular/cli": "^9.1.0", - "@angular/compiler-cli": "^9.0.6", - "@angular/language-service": "^9.0.6", + "@angular/compiler-cli": "^9.1.0", + "@angular/language-service": "^9.1.0", "@locl/cli": "0.0.1-beta.6", "@types/core-js": "^2.5.2", "@types/file-saver": "^2.0.1", From a5474141bb4705df364cee3413d8894632f8406c Mon Sep 17 00:00:00 2001 From: Isaac Grynsztein Date: Wed, 1 Apr 2020 19:36:57 -0400 Subject: [PATCH 14/18] Removed unused dependencies --- package.json | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index d98f1de..dffd0c5 100644 --- a/package.json +++ b/package.json @@ -20,23 +20,20 @@ "dependencies": { "@angular-devkit/core": "^9.0.6", "@angular/animations": "^9.1.0", - "@angular/cdk": "^9.1.2", + "@angular/cdk": "^9.2.0", "@angular/common": "^9.1.0", "@angular/compiler": "^9.1.0", - "@angular/core": "^9.1.0", + "@angular/core": "^9.0.7", "@angular/forms": "^9.1.0", "@angular/localize": "^9.1.0", "@angular/material": "^9.2.0", "@angular/platform-browser": "^9.1.0", "@angular/platform-browser-dynamic": "^9.1.0", "@angular/router": "^9.1.0", - "@locl/core": "0.0.1-beta.2", "core-js": "^2.4.1", "file-saver": "^2.0.2", "filesize": "^6.1.0", "ng-lazyload-image": "^7.0.1", - "ng4-configure": "^0.1.7", - "ngx-content-loading": "^0.1.3", "ngx-videogular": "^9.0.1", "rxjs": "^6.5.3", "rxjs-compat": "^6.0.0-rc.0", @@ -47,10 +44,9 @@ }, "devDependencies": { "@angular-devkit/build-angular": "^0.901.0", - "@angular/cli": "^9.1.0", - "@angular/compiler-cli": "^9.1.0", - "@angular/language-service": "^9.1.0", - "@locl/cli": "0.0.1-beta.6", + "@angular/cli": "^9.0.7", + "@angular/compiler-cli": "^9.0.7", + "@angular/language-service": "^9.0.7", "@types/core-js": "^2.5.2", "@types/file-saver": "^2.0.1", "@types/jasmine": "2.5.45", From 6fa52cecbc43920f990b8dfb2f7bf02f2ee075a4 Mon Sep 17 00:00:00 2001 From: Isaac Grynsztein Date: Wed, 1 Apr 2020 19:42:21 -0400 Subject: [PATCH 15/18] Updated docker compose version --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 0e4673c..548a5d8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,4 +13,4 @@ services: - ./subscriptions:/app/subscriptions ports: - "8998:17442" - image: tzahi12345/youtubedl-material:experimental \ No newline at end of file + image: tzahi12345/youtubedl-material:3.5 \ No newline at end of file From c40513ba4a8fe452cfdb20b61d7b6a562528124d Mon Sep 17 00:00:00 2001 From: Isaac Grynsztein Date: Wed, 1 Apr 2020 19:42:44 -0400 Subject: [PATCH 16/18] docker-compose now uses latest version tag --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 548a5d8..fd48e61 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,4 +13,4 @@ services: - ./subscriptions:/app/subscriptions ports: - "8998:17442" - image: tzahi12345/youtubedl-material:3.5 \ No newline at end of file + image: tzahi12345/youtubedl-material:latest \ No newline at end of file From 458e4b45f8f317a340d47c7e96e3ced0a695c12d Mon Sep 17 00:00:00 2001 From: Isaac Grynsztein Date: Wed, 1 Apr 2020 19:44:22 -0400 Subject: [PATCH 17/18] Removed @locl dependency for translations Added CommonModule to fix intellisense Added ability to load json assets by name, and an http call to update youtubedl-material --- src/app/app.module.ts | 5 ++--- src/app/file-card/file-card.component.html | 4 +--- src/app/posts.services.ts | 9 +++++++++ src/main.ts | 4 +--- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 6ca713b..e7191a7 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,7 +1,7 @@ import { BrowserModule } from '@angular/platform-browser'; import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; import { NgModule, LOCALE_ID } from '@angular/core'; -import { registerLocaleData } from '@angular/common'; +import { registerLocaleData, CommonModule } from '@angular/common'; import { MatButtonModule } from '@angular/material/button'; import { MatButtonToggleModule } from '@angular/material/button-toggle'; import { MatCardModule } from '@angular/material/card'; @@ -37,7 +37,6 @@ import { PlayerComponent } from './player/player.component'; import {VgCoreModule, VgControlsModule, VgOverlayPlayModule, VgBufferingModule} from 'ngx-videogular'; import { InputDialogComponent } from './input-dialog/input-dialog.component'; import { LazyLoadImageModule, IsVisibleProps } from 'ng-lazyload-image'; -import { NgxContentLoadingModule } from 'ngx-content-loading'; import { audioFilesMouseHovering, videoFilesMouseHovering, audioFilesOpened, videoFilesOpened } from './main/main.component'; import { CreatePlaylistComponent } from './create-playlist/create-playlist.component'; import { DownloadItemComponent } from './download-item/download-item.component'; @@ -81,6 +80,7 @@ export function isVisible({ event, element, scrollContainer, offset }: IsVisible HighlightPipe ], imports: [ + CommonModule, BrowserModule, BrowserAnimationsModule, MatNativeDateModule, @@ -116,7 +116,6 @@ export function isVisible({ event, element, scrollContainer, offset }: IsVisible VgOverlayPlayModule, VgBufferingModule, LazyLoadImageModule.forRoot({ isVisible }), - NgxContentLoadingModule, RouterModule, AppRoutingModule, ], diff --git a/src/app/file-card/file-card.component.html b/src/app/file-card/file-card.component.html index ef79cf6..92116dc 100644 --- a/src/app/file-card/file-card.component.html +++ b/src/app/file-card/file-card.component.html @@ -10,9 +10,7 @@
Thumbnail - - - +
diff --git a/src/app/posts.services.ts b/src/app/posts.services.ts index 54e464d..9907ed9 100644 --- a/src/app/posts.services.ts +++ b/src/app/posts.services.ts @@ -94,6 +94,10 @@ export class PostsService { } } + loadAsset(name) { + return this.http.get(`./assets/${name}`); + } + setConfig(config) { return this.http.post(this.path + 'setConfig', {new_config_file: config}); } @@ -180,6 +184,11 @@ export class PostsService { return this.http.post(this.path + 'getAllSubscriptions', {}); } + // updates the server to the latest version + updateServer() { + return this.http.post(this.path + 'updateServer', {}); + } + // gets tag of the latest version of youtubedl-material getLatestGithubRelease() { return this.http.get('https://api.github.com/repos/tzahi12345/youtubedl-material/releases/latest'); diff --git a/src/main.ts b/src/main.ts index 00f55c9..3617189 100644 --- a/src/main.ts +++ b/src/main.ts @@ -6,7 +6,6 @@ import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { environment } from './environments/environment'; import { loadTranslations } from '@angular/localize'; -import { getTranslations, ParsedTranslationBundle } from '@locl/core'; if (environment.production) { enableProdMode(); @@ -17,8 +16,7 @@ if (!locale) { localStorage.setItem('locale', 'en'); } if (locale && locale !== 'en') { - getTranslations(`./assets/i18n/messages.${locale}.json`).then( - (data: ParsedTranslationBundle) => { + fetch(`./assets/i18n/messages.${locale}.json`).then(res => res.json()).then((data) => { loadTranslations(data as any); import('./app/app.module').then(module => { platformBrowserDynamic() From c36867d368b338d934ba492e3f9e2efa63b5509b Mon Sep 17 00:00:00 2001 From: Isaac Grynsztein Date: Thu, 2 Apr 2020 21:53:08 -0400 Subject: [PATCH 18/18] Added progress bar to file downloads Added two new API calls, to update the server to a particular version and to get the updater status You can now update through the UI, and a status dialog displays after --- backend/app.js | 143 +++++++++++++----- backend/package.json | 1 + src/app/app.module.ts | 6 +- src/app/consts.ts | 1 + .../about-dialog/about-dialog.component.html | 2 +- .../about-dialog/about-dialog.component.ts | 3 +- .../update-progress-dialog.component.html | 18 +++ .../update-progress-dialog.component.scss | 0 .../update-progress-dialog.component.spec.ts | 25 +++ .../update-progress-dialog.component.ts | 44 ++++++ src/app/posts.services.ts | 13 +- src/app/settings/settings.component.html | 12 ++ src/app/settings/settings.component.ts | 12 ++ src/app/updater/updater.component.html | 18 +++ src/app/updater/updater.component.scss | 0 src/app/updater/updater.component.spec.ts | 25 +++ src/app/updater/updater.component.ts | 62 ++++++++ 17 files changed, 345 insertions(+), 40 deletions(-) create mode 100644 src/app/consts.ts create mode 100644 src/app/dialogs/update-progress-dialog/update-progress-dialog.component.html create mode 100644 src/app/dialogs/update-progress-dialog/update-progress-dialog.component.scss create mode 100644 src/app/dialogs/update-progress-dialog/update-progress-dialog.component.spec.ts create mode 100644 src/app/dialogs/update-progress-dialog/update-progress-dialog.component.ts create mode 100644 src/app/updater/updater.component.html create mode 100644 src/app/updater/updater.component.scss create mode 100644 src/app/updater/updater.component.spec.ts create mode 100644 src/app/updater/updater.component.ts diff --git a/backend/app.js b/backend/app.js index 33e1647..745016d 100644 --- a/backend/app.js +++ b/backend/app.js @@ -11,6 +11,7 @@ var archiver = require('archiver'); var unzipper = require('unzipper'); var mergeFiles = require('merge-files'); const low = require('lowdb') +var ProgressBar = require('progress'); var md5 = require('md5'); const NodeID3 = require('node-id3') const downloader = require('youtube-dl/lib/downloader') @@ -61,12 +62,23 @@ var archivePath = path.join(__dirname, 'appdata', 'archives'); // other needed values var options = null; // encryption options var url_domain = null; +var updaterStatus = null; // check if debug mode let debugMode = process.env.YTDL_MODE === 'debug'; if (debugMode) console.log('YTDL-Material in debug mode!'); +// check if just updated +const just_restarted = fs.existsSync('restart.json'); +if (just_restarted) { + updaterStatus = { + updating: false, + details: 'Update complete! You are now on ' + CONSTS['CURRENT_VERSION'] + } + fs.unlinkSync('restart.json'); +} + // updates & starts youtubedl startYoutubeDL(); @@ -153,42 +165,63 @@ async function restartServer() { fs.writeFileSync('restart.json', 'internal use only'); } -async function updateServer() { - const new_version_available = await isNewVersionAvailable(); - if (!new_version_available) { - console.log('ERROR: Failed to update - no update is available.'); - return false; +async function updateServer(tag) { + // no tag provided means update to the latest version + if (!tag) { + const new_version_available = await isNewVersionAvailable(); + if (!new_version_available) { + console.log('ERROR: Failed to update - no update is available.'); + return false; + } } + return new Promise(async resolve => { // backup current dir + updaterStatus = { + updating: true, + 'details': 'Backing up key server files...' + } let backup_succeeded = await backupServerLite(); if (!backup_succeeded) { resolve(false); return false; } + updaterStatus = { + updating: true, + 'details': 'Downloading requested release...' + } // grab new package.json and public folder - await downloadUpdateFiles(); + // await downloadReleaseFiles(tag); + updaterStatus = { + updating: true, + 'details': 'Installing new dependencies...' + } // run npm install await installDependencies(); + updaterStatus = { + updating: true, + 'details': 'Update complete! Restarting server...' + } restartServer(); + }, err => { + updaterStatus = { + updating: false, + error: true, + 'details': 'Update failed. Check error logs for more info.' + } }); } -async function downloadUpdateFiles() { - let tag = await getLatestVersion(); +async function downloadReleaseFiles(tag) { + tag = tag ? tag : await getLatestVersion(); return new Promise(async resolve => { console.log('Downloading new files...') - var options = { - owner: 'tzahi12345', - repo: 'YoutubeDL-Material', - branch: tag - }; // downloads the latest release zip file - await downloadLatestRelease(tag); + await downloadReleaseZip(tag); // deletes contents of public dir fs.removeSync(path.join(__dirname, 'public')); @@ -199,7 +232,7 @@ async function downloadUpdateFiles() { console.log(`Installing update ${tag}...`) // downloads new package.json and adds new public dir files from the downloaded zip - fs.createReadStream(path.join(__dirname, 'youtubedl-material-latest-release.zip')).pipe(unzipper.Parse()) + fs.createReadStream(path.join(__dirname, `youtubedl-material-latest-release-${tag}.zip`)).pipe(unzipper.Parse()) .on('entry', function (entry) { var fileName = entry.path; var type = entry.type; // 'Directory' or 'File' @@ -229,17 +262,46 @@ async function downloadUpdateFiles() { }); } -async function downloadLatestRelease(tag) { +// helper function to download file using fetch +async function fetchFile(url, path, file_label) { + var len = null; + const res = await fetch(url); + + len = parseInt(res.headers.get("Content-Length"), 10); + + var bar = new ProgressBar(` Downloading ${file_label} [:bar] :percent :etas`, { + complete: '=', + incomplete: ' ', + width: 20, + total: len + }); + const fileStream = fs.createWriteStream(path); + await new Promise((resolve, reject) => { + res.body.pipe(fileStream); + res.body.on("error", (err) => { + reject(err); + }); + res.body.on('data', function (chunk) { + bar.tick(chunk.length); + }); + fileStream.on("finish", function() { + resolve(); + }); + }); + } + +async function downloadReleaseZip(tag) { + console.log('downloading'); return new Promise(async resolve => { - // get name of latest zip file, which depends on the version - const latest_release_link = 'https://github.com/Tzahi12345/YoutubeDL-Material/releases/latest/download/'; + // get name of zip file, which depends on the version + const latest_release_link = `https://github.com/Tzahi12345/YoutubeDL-Material/releases/download/${tag}/`; const tag_without_v = tag.substring(1, tag.length); const zip_file_name = `youtubedl-material-${tag_without_v}.zip` const latest_zip_link = latest_release_link + zip_file_name; - let output_path = path.join(__dirname, `youtubedl-material-latest-release.zip`); + let output_path = path.join(__dirname, `youtubedl-material-release-${tag}.zip`); // download zip from release - await fetchFile(latest_zip_link, output_path); + await fetchFile(latest_zip_link, output_path, 'update ' + tag); resolve(true); }); @@ -254,21 +316,6 @@ async function installDependencies() { } -// helper function to download file using fetch -const fetchFile = (async (url, path) => { - const res = await fetch(url); - const fileStream = fs.createWriteStream(path); - await new Promise((resolve, reject) => { - res.body.pipe(fileStream); - res.body.on("error", (err) => { - reject(err); - }); - fileStream.on("finish", function() { - resolve(); - }); - }); - }); - async function backupServerLite() { return new Promise(async resolve => { let output_path = `backup-${Date.now()}.zip`; @@ -1695,6 +1742,32 @@ app.post('/api/downloadArchive', async (req, res) => { }); +// Updater API calls + +app.get('/api/updaterStatus', async (req, res) => { + let status = updaterStatus; + + if (status) { + res.send(updaterStatus); + } else { + res.sendStatus(404); + } + +}); + +app.post('/api/updateServer', async (req, res) => { + let tag = req.body.tag; + + updateServer(tag); + + res.send({ + success: true + }); + +}); + +// Pin API calls + app.post('/api/isPinSet', async (req, res) => { let stored_pin = db.get('pin_md5').value(); let is_set = false; diff --git a/backend/package.json b/backend/package.json index c3a814d..40cff74 100644 --- a/backend/package.json +++ b/backend/package.json @@ -41,6 +41,7 @@ "node-fetch": "^2.6.0", "node-id3": "^0.1.14", "nodemon": "^2.0.2", + "progress": "^2.0.3", "shortid": "^2.2.15", "unzipper": "^0.10.10", "uuidv4": "^6.0.6", diff --git a/src/app/app.module.ts b/src/app/app.module.ts index e7191a7..90c1ac8 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -52,6 +52,8 @@ import es from '@angular/common/locales/es'; import { AboutDialogComponent } from './dialogs/about-dialog/about-dialog.component'; import { VideoInfoDialogComponent } from './dialogs/video-info-dialog/video-info-dialog.component'; import { ArgModifierDialogComponent, HighlightPipe } from './dialogs/arg-modifier-dialog/arg-modifier-dialog.component'; +import { UpdaterComponent } from './updater/updater.component'; +import { UpdateProgressDialogComponent } from './dialogs/update-progress-dialog/update-progress-dialog.component'; registerLocaleData(es, 'es'); export function isVisible({ event, element, scrollContainer, offset }: IsVisibleProps) { @@ -77,7 +79,9 @@ export function isVisible({ event, element, scrollContainer, offset }: IsVisible AboutDialogComponent, VideoInfoDialogComponent, ArgModifierDialogComponent, - HighlightPipe + HighlightPipe, + UpdaterComponent, + UpdateProgressDialogComponent ], imports: [ CommonModule, diff --git a/src/app/consts.ts b/src/app/consts.ts new file mode 100644 index 0000000..5b1ec6c --- /dev/null +++ b/src/app/consts.ts @@ -0,0 +1 @@ +export const CURRENT_VERSION = 'v3.5'; diff --git a/src/app/dialogs/about-dialog/about-dialog.component.html b/src/app/dialogs/about-dialog/about-dialog.component.html index e46d4ca..9c898fe 100644 --- a/src/app/dialogs/about-dialog/about-dialog.component.html +++ b/src/app/dialogs/about-dialog/about-dialog.component.html @@ -18,7 +18,7 @@
Installation details:

Installed version: {{current_version_tag}} -  Checking for updates... - done  Update available - {{latestGithubRelease['tag_name']}} + done  Update available - {{latestGithubRelease['tag_name']}}. You can update from the settings menu. You are up to date.

diff --git a/src/app/dialogs/about-dialog/about-dialog.component.ts b/src/app/dialogs/about-dialog/about-dialog.component.ts index ba34fcd..1637d60 100644 --- a/src/app/dialogs/about-dialog/about-dialog.component.ts +++ b/src/app/dialogs/about-dialog/about-dialog.component.ts @@ -1,5 +1,6 @@ import { Component, OnInit } from '@angular/core'; import { PostsService } from 'app/posts.services'; +import { CURRENT_VERSION } from 'app/consts'; @Component({ selector: 'app-about-dialog', @@ -14,7 +15,7 @@ export class AboutDialogComponent implements OnInit { latestGithubRelease = null; checking_for_updates = true; - current_version_tag = 'v3.5.1'; + current_version_tag = CURRENT_VERSION; constructor(private postsService: PostsService) { } diff --git a/src/app/dialogs/update-progress-dialog/update-progress-dialog.component.html b/src/app/dialogs/update-progress-dialog/update-progress-dialog.component.html new file mode 100644 index 0000000..fca82ee --- /dev/null +++ b/src/app/dialogs/update-progress-dialog/update-progress-dialog.component.html @@ -0,0 +1,18 @@ +

Updater

+ + +
+
+
Update in progress
+
Update failed
+
Update succeeded!
+
+ + +

{{updateStatus['details']}}

+
+
+ + + + \ No newline at end of file diff --git a/src/app/dialogs/update-progress-dialog/update-progress-dialog.component.scss b/src/app/dialogs/update-progress-dialog/update-progress-dialog.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/dialogs/update-progress-dialog/update-progress-dialog.component.spec.ts b/src/app/dialogs/update-progress-dialog/update-progress-dialog.component.spec.ts new file mode 100644 index 0000000..60ce881 --- /dev/null +++ b/src/app/dialogs/update-progress-dialog/update-progress-dialog.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { UpdateProgressDialogComponent } from './update-progress-dialog.component'; + +describe('UpdateProgressDialogComponent', () => { + let component: UpdateProgressDialogComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ UpdateProgressDialogComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(UpdateProgressDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/dialogs/update-progress-dialog/update-progress-dialog.component.ts b/src/app/dialogs/update-progress-dialog/update-progress-dialog.component.ts new file mode 100644 index 0000000..a69bdac --- /dev/null +++ b/src/app/dialogs/update-progress-dialog/update-progress-dialog.component.ts @@ -0,0 +1,44 @@ +import { Component, OnInit } from '@angular/core'; +import { PostsService } from 'app/posts.services'; +import { MatSnackBar } from '@angular/material/snack-bar'; + +@Component({ + selector: 'app-update-progress-dialog', + templateUrl: './update-progress-dialog.component.html', + styleUrls: ['./update-progress-dialog.component.scss'] +}) +export class UpdateProgressDialogComponent implements OnInit { + + updateStatus = null; + updateInterval = 250; + errored = false; + + constructor(private postsService: PostsService, private snackBar: MatSnackBar) { } + + ngOnInit(): void { + this.getUpdateProgress(); + setInterval(() => { + this.getUpdateProgress(); + }, 250); + } + + getUpdateProgress() { + this.postsService.getUpdaterStatus().subscribe(res => { + this.updateStatus = res; + if (!this.updateStatus) { + // update complete? + console.log('Update complete? or not started'); + } + if (this.updateStatus && this.updateStatus['error']) { + this.openSnackBar('Update failed. Check logs for more details.'); + } + }); + } + + public openSnackBar(message: string, action: string = '') { + this.snackBar.open(message, action, { + duration: 2000, + }); + } + +} diff --git a/src/app/posts.services.ts b/src/app/posts.services.ts index 9907ed9..752cc9b 100644 --- a/src/app/posts.services.ts +++ b/src/app/posts.services.ts @@ -185,14 +185,23 @@ export class PostsService { } // updates the server to the latest version - updateServer() { - return this.http.post(this.path + 'updateServer', {}); + updateServer(tag) { + return this.http.post(this.path + 'updateServer', {tag: tag}); + } + + getUpdaterStatus() { + return this.http.get(this.path + 'updaterStatus'); } // gets tag of the latest version of youtubedl-material getLatestGithubRelease() { return this.http.get('https://api.github.com/repos/tzahi12345/youtubedl-material/releases/latest'); } + + getAvailableRelease() { + return this.http.get('https://api.github.com/repos/tzahi12345/youtubedl-material/releases'); + } + } diff --git a/src/app/settings/settings.component.html b/src/app/settings/settings.component.html index d36e64f..1cc4e6e 100644 --- a/src/app/settings/settings.component.html +++ b/src/app/settings/settings.component.html @@ -247,6 +247,18 @@ + + + + + Version + + +
+ +
+
+ diff --git a/src/app/settings/settings.component.ts b/src/app/settings/settings.component.ts index 4a631f0..c2b846f 100644 --- a/src/app/settings/settings.component.ts +++ b/src/app/settings/settings.component.ts @@ -6,6 +6,7 @@ import { MatSnackBar } from '@angular/material/snack-bar'; import {DomSanitizer} from '@angular/platform-browser'; import { MatDialog } from '@angular/material/dialog'; import { ArgModifierDialogComponent } from 'app/dialogs/arg-modifier-dialog/arg-modifier-dialog.component'; +import { CURRENT_VERSION } from 'app/consts'; @Component({ selector: 'app-settings', @@ -24,6 +25,9 @@ export class SettingsComponent implements OnInit { _settingsSame = true; + latestGithubRelease = null; + CURRENT_VERSION = CURRENT_VERSION + get settingsAreTheSame() { this._settingsSame = this.settingsSame() return this._settingsSame; @@ -40,6 +44,8 @@ export class SettingsComponent implements OnInit { this.getConfig(); this.generated_bookmarklet_code = this.sanitizer.bypassSecurityTrustUrl(this.generateBookmarkletCode()); + + this.getLatestGithubRelease(); } getConfig() { @@ -129,6 +135,12 @@ export class SettingsComponent implements OnInit { }); } + getLatestGithubRelease() { + this.postsService.getLatestGithubRelease().subscribe(res => { + this.latestGithubRelease = res; + }); + } + // snackbar helper public openSnackBar(message: string, action: string = '') { this.snackBar.open(message, action, { diff --git a/src/app/updater/updater.component.html b/src/app/updater/updater.component.html new file mode 100644 index 0000000..80d02d2 --- /dev/null +++ b/src/app/updater/updater.component.html @@ -0,0 +1,18 @@ +
+
+ Select a version: +
+
+ + + + {{version['tag_name'] + (version === latestStableRelease ? ' - Latest Stable' : '') + (version['tag_name'] === CURRENT_VERSION ? ' - Current Version' : '')}} + + + +
+
+ +
+
\ No newline at end of file diff --git a/src/app/updater/updater.component.scss b/src/app/updater/updater.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/updater/updater.component.spec.ts b/src/app/updater/updater.component.spec.ts new file mode 100644 index 0000000..62ae61d --- /dev/null +++ b/src/app/updater/updater.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { UpdaterComponent } from './updater.component'; + +describe('UpdaterComponent', () => { + let component: UpdaterComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ UpdaterComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(UpdaterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/updater/updater.component.ts b/src/app/updater/updater.component.ts new file mode 100644 index 0000000..7c63024 --- /dev/null +++ b/src/app/updater/updater.component.ts @@ -0,0 +1,62 @@ +import { Component, OnInit } from '@angular/core'; +import { PostsService } from 'app/posts.services'; +import { CURRENT_VERSION } from 'app/consts'; +import { MatDialog } from '@angular/material/dialog'; +import { UpdateProgressDialogComponent } from 'app/dialogs/update-progress-dialog/update-progress-dialog.component'; +@Component({ + selector: 'app-updater', + templateUrl: './updater.component.html', + styleUrls: ['./updater.component.scss'] +}) +export class UpdaterComponent implements OnInit { + + availableVersions = null; + availableVersionsFiltered = []; + versionsShowLimit = 5; + latestStableRelease = null; + selectedVersion = null; + CURRENT_VERSION = CURRENT_VERSION; + + constructor(private postsService: PostsService, private dialog: MatDialog) { } + + ngOnInit(): void { + this.getAvailableVersions(); + } + + updateServer() { + this.postsService.updateServer(this.selectedVersion).subscribe(res => { + if (res['success']) { + this.openUpdateProgressDialog(); + } + }); + } + + getAvailableVersions() { + this.availableVersionsFiltered = []; + this.postsService.getAvailableRelease().subscribe(res => { + this.availableVersions = res; + for (let i = 0; i < this.availableVersions.length; i++) { + const currentVersion = this.availableVersions[i]; + // if a stable release has not been found and the version is not "rc" (meaning it's stable) then set it as the stable release + if (!this.latestStableRelease && !currentVersion.tag_name.includes('rc')) { + this.latestStableRelease = currentVersion; + this.selectedVersion = this.latestStableRelease.tag_name; + } + + if (this.latestStableRelease && i >= this.versionsShowLimit) { + break; + } + + this.availableVersionsFiltered.push(currentVersion); + } + }); + } + + openUpdateProgressDialog() { + this.dialog.open(UpdateProgressDialogComponent, { + minWidth: '300px', + minHeight: '200px' + }); + } + +}