mirror of
https://github.com/Tzahi12345/YoutubeDL-Material.git
synced 2026-04-17 10:41:29 +03:00
Merge pull request #31 from Tzahi12345/use-youtubedl-archive-with-downloader
Implements youtube-dl archive functionality for downloader
This commit is contained in:
133
backend/app.js
133
backend/app.js
@@ -8,6 +8,7 @@ var https = require('https');
|
|||||||
var express = require("express");
|
var express = require("express");
|
||||||
var bodyParser = require("body-parser");
|
var bodyParser = require("body-parser");
|
||||||
var archiver = require('archiver');
|
var archiver = require('archiver');
|
||||||
|
var mergeFiles = require('merge-files');
|
||||||
const low = require('lowdb')
|
const low = require('lowdb')
|
||||||
var URL = require('url').URL;
|
var URL = require('url').URL;
|
||||||
const shortid = require('shortid')
|
const shortid = require('shortid')
|
||||||
@@ -40,6 +41,7 @@ var usingEncryption = null;
|
|||||||
var basePath = null;
|
var basePath = null;
|
||||||
var audioFolderPath = null;
|
var audioFolderPath = null;
|
||||||
var videoFolderPath = null;
|
var videoFolderPath = null;
|
||||||
|
var useYoutubeDLArchive = null;
|
||||||
var downloadOnlyMode = null;
|
var downloadOnlyMode = null;
|
||||||
var useDefaultDownloadingAgent = null;
|
var useDefaultDownloadingAgent = null;
|
||||||
var customDownloadingAgent = null;
|
var customDownloadingAgent = null;
|
||||||
@@ -132,6 +134,7 @@ async function loadConfig() {
|
|||||||
usingEncryption = config_api.getConfigItem('ytdl_use_encryption');
|
usingEncryption = config_api.getConfigItem('ytdl_use_encryption');
|
||||||
audioFolderPath = config_api.getConfigItem('ytdl_audio_folder_path');
|
audioFolderPath = config_api.getConfigItem('ytdl_audio_folder_path');
|
||||||
videoFolderPath = config_api.getConfigItem('ytdl_video_folder_path');
|
videoFolderPath = config_api.getConfigItem('ytdl_video_folder_path');
|
||||||
|
useYoutubeDLArchive = config_api.getConfigItem('ytdl_use_youtubedl_archive');
|
||||||
downloadOnlyMode = config_api.getConfigItem('ytdl_download_only_mode');
|
downloadOnlyMode = config_api.getConfigItem('ytdl_download_only_mode');
|
||||||
useDefaultDownloadingAgent = config_api.getConfigItem('ytdl_use_default_downloading_agent');
|
useDefaultDownloadingAgent = config_api.getConfigItem('ytdl_use_default_downloading_agent');
|
||||||
customDownloadingAgent = config_api.getConfigItem('ytdl_custom_downloading_agent');
|
customDownloadingAgent = config_api.getConfigItem('ytdl_custom_downloading_agent');
|
||||||
@@ -382,7 +385,7 @@ async function createPlaylistZipFile(fileNames, type, outputName) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteAudioFile(name) {
|
async function deleteAudioFile(name, blacklistMode = false) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
// TODO: split descriptors into audio and video descriptors, as deleting an audio file will close all video file streams
|
// TODO: split descriptors into audio and video descriptors, as deleting an audio file will close all video file streams
|
||||||
var jsonPath = path.join(audioFolderPath,name+'.mp3.info.json');
|
var jsonPath = path.join(audioFolderPath,name+'.mp3.info.json');
|
||||||
@@ -403,7 +406,19 @@ function deleteAudioFile(name) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (useYoutubeDLArchive) {
|
||||||
|
const archive_path = audioFolderPath + 'archive.txt';
|
||||||
|
|
||||||
|
// get ID from JSON
|
||||||
|
|
||||||
|
var jsonobj = getJSONMp3(name);
|
||||||
|
let id = null;
|
||||||
|
if (jsonobj) id = jsonobj.id;
|
||||||
|
|
||||||
|
// use subscriptions API to remove video from the archive file, and write it to the blacklist
|
||||||
|
const line = id ? subscriptions_api.removeIDFromArchive(archive_path, id) : null;
|
||||||
|
if (blacklistMode && line) writeToBlacklist('audio', line);
|
||||||
|
}
|
||||||
|
|
||||||
if (jsonExists) fs.unlinkSync(jsonPath);
|
if (jsonExists) fs.unlinkSync(jsonPath);
|
||||||
if (audioFileExists) {
|
if (audioFileExists) {
|
||||||
@@ -422,7 +437,7 @@ function deleteAudioFile(name) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteVideoFile(name, customPath = null) {
|
async function deleteVideoFile(name, customPath = null, blacklistMode = false) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
let filePath = customPath ? customPath : videoFolderPath;
|
let filePath = customPath ? customPath : videoFolderPath;
|
||||||
var jsonPath = path.join(filePath,name+'.info.json');
|
var jsonPath = path.join(filePath,name+'.info.json');
|
||||||
@@ -443,7 +458,19 @@ async function deleteVideoFile(name, customPath = null) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (useYoutubeDLArchive) {
|
||||||
|
const archive_path = videoFolderPath + 'archive.txt';
|
||||||
|
|
||||||
|
// get ID from JSON
|
||||||
|
|
||||||
|
var jsonobj = getJSONMp4(name);
|
||||||
|
let id = null;
|
||||||
|
if (jsonobj) id = jsonobj.id;
|
||||||
|
|
||||||
|
// use subscriptions API to remove video from the archive file, and write it to the blacklist
|
||||||
|
const line = id ? subscriptions_api.removeIDFromArchive(archive_path, id) : null;
|
||||||
|
if (blacklistMode && line) writeToBlacklist('video', line);
|
||||||
|
}
|
||||||
|
|
||||||
if (jsonExists) fs.unlinkSync(jsonPath);
|
if (jsonExists) fs.unlinkSync(jsonPath);
|
||||||
if (videoFileExists) {
|
if (videoFileExists) {
|
||||||
@@ -549,6 +576,13 @@ async function getUrlInfos(urls) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function writeToBlacklist(type, line) {
|
||||||
|
let blacklistBasePath = (type === 'audio') ? audioFolderPath : videoFolderPath;
|
||||||
|
// adds newline to the beginning of the line
|
||||||
|
line = '\n' + line;
|
||||||
|
fs.appendFileSync(blacklistBasePath + 'blacklist.txt', line);
|
||||||
|
}
|
||||||
|
|
||||||
app.use(function(req, res, next) {
|
app.use(function(req, res, next) {
|
||||||
res.header("Access-Control-Allow-Origin", getOrigin());
|
res.header("Access-Control-Allow-Origin", getOrigin());
|
||||||
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
|
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
|
||||||
@@ -583,7 +617,7 @@ app.get('/api/using-encryption', function(req, res) {
|
|||||||
res.send(usingEncryption);
|
res.send(usingEncryption);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post('/api/tomp3', function(req, res) {
|
app.post('/api/tomp3', async function(req, res) {
|
||||||
var url = req.body.url;
|
var url = req.body.url;
|
||||||
var date = Date.now();
|
var date = Date.now();
|
||||||
var audiopath = '%(title)s';
|
var audiopath = '%(title)s';
|
||||||
@@ -596,10 +630,11 @@ app.post('/api/tomp3', function(req, res) {
|
|||||||
var youtubeUsername = req.body.youtubeUsername;
|
var youtubeUsername = req.body.youtubeUsername;
|
||||||
var youtubePassword = req.body.youtubePassword;
|
var youtubePassword = req.body.youtubePassword;
|
||||||
|
|
||||||
|
|
||||||
let downloadConfig = null;
|
let downloadConfig = null;
|
||||||
let qualityPath = '-f bestaudio';
|
let qualityPath = '-f bestaudio';
|
||||||
|
|
||||||
|
let merged_string = null;
|
||||||
|
|
||||||
if (customArgs) {
|
if (customArgs) {
|
||||||
downloadConfig = customArgs.split(' ');
|
downloadConfig = customArgs.split(' ');
|
||||||
} else {
|
} else {
|
||||||
@@ -628,6 +663,29 @@ app.post('/api/tomp3', function(req, res) {
|
|||||||
downloadConfig.splice(0, 0, '--external-downloader', 'aria2c');
|
downloadConfig.splice(0, 0, '--external-downloader', 'aria2c');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (useYoutubeDLArchive) {
|
||||||
|
let archive_path = audioFolderPath + 'archive.txt';
|
||||||
|
// create archive file if it doesn't exist
|
||||||
|
if (!fs.existsSync(archive_path)) {
|
||||||
|
fs.closeSync(fs.openSync(archive_path, 'w'));
|
||||||
|
}
|
||||||
|
|
||||||
|
let blacklist_path = audioFolderPath + 'blacklist.txt';
|
||||||
|
// create blacklist file if it doesn't exist
|
||||||
|
if (!fs.existsSync(blacklist_path)) {
|
||||||
|
fs.closeSync(fs.openSync(blacklist_path, 'w'));
|
||||||
|
}
|
||||||
|
|
||||||
|
let merged_path = audioFolderPath + 'merged.txt';
|
||||||
|
// merges blacklist and regular archive
|
||||||
|
let inputPathList = [archive_path, blacklist_path];
|
||||||
|
let status = await mergeFiles(inputPathList, merged_path);
|
||||||
|
|
||||||
|
merged_string = fs.readFileSync(merged_path, "utf8");
|
||||||
|
|
||||||
|
downloadConfig.push('--download-archive', merged_path);
|
||||||
|
}
|
||||||
|
|
||||||
if (globalArgs && globalArgs !== '') {
|
if (globalArgs && globalArgs !== '') {
|
||||||
// adds global args
|
// adds global args
|
||||||
downloadConfig = downloadConfig.concat(globalArgs.split(' '));
|
downloadConfig = downloadConfig.concat(globalArgs.split(' '));
|
||||||
@@ -647,6 +705,10 @@ app.post('/api/tomp3', function(req, res) {
|
|||||||
throw err;
|
throw err;
|
||||||
} else if (output) {
|
} else if (output) {
|
||||||
var file_names = [];
|
var file_names = [];
|
||||||
|
if (output.length === 0 || output[0].length === 0) {
|
||||||
|
res.sendStatus(500);
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (let i = 0; i < output.length; i++) {
|
for (let i = 0; i < output.length; i++) {
|
||||||
let output_json = null;
|
let output_json = null;
|
||||||
try {
|
try {
|
||||||
@@ -660,13 +722,19 @@ app.post('/api/tomp3', function(req, res) {
|
|||||||
}
|
}
|
||||||
var file_name = output_json['_filename'].replace(/^.*[\\\/]/, '');
|
var file_name = output_json['_filename'].replace(/^.*[\\\/]/, '');
|
||||||
var file_path = output_json['_filename'].substring(audioFolderPath.length, output_json['_filename'].length);
|
var file_path = output_json['_filename'].substring(audioFolderPath.length, output_json['_filename'].length);
|
||||||
var alternate_file_path = file_path.substring(0, file_path.length-4);
|
// remove extension from file path
|
||||||
|
var alternate_file_path = file_path.replace(/\.[^/.]+$/, "")
|
||||||
var alternate_file_name = file_name.substring(0, file_name.length-4);
|
var alternate_file_name = file_name.substring(0, file_name.length-4);
|
||||||
if (alternate_file_path) file_names.push(alternate_file_path);
|
if (alternate_file_path) file_names.push(alternate_file_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
let is_playlist = file_names.length > 1;
|
let is_playlist = file_names.length > 1;
|
||||||
// if (!is_playlist) audiopath = file_names[0];
|
|
||||||
|
if (merged_string !== null) {
|
||||||
|
let current_merged_archive = fs.readFileSync(audioFolderPath + 'merged.txt', 'utf8');
|
||||||
|
let diff = current_merged_archive.replace(merged_string, '');
|
||||||
|
fs.appendFileSync(audioFolderPath + 'archive.txt', diff);
|
||||||
|
}
|
||||||
|
|
||||||
var audiopathEncoded = encodeURIComponent(file_names[0]);
|
var audiopathEncoded = encodeURIComponent(file_names[0]);
|
||||||
res.send({
|
res.send({
|
||||||
@@ -677,7 +745,7 @@ app.post('/api/tomp3', function(req, res) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post('/api/tomp4', function(req, res) {
|
app.post('/api/tomp4', async function(req, res) {
|
||||||
var url = req.body.url;
|
var url = req.body.url;
|
||||||
var date = Date.now();
|
var date = Date.now();
|
||||||
var path = videoFolderPath;
|
var path = videoFolderPath;
|
||||||
@@ -691,6 +759,8 @@ app.post('/api/tomp4', function(req, res) {
|
|||||||
var youtubeUsername = req.body.youtubeUsername;
|
var youtubeUsername = req.body.youtubeUsername;
|
||||||
var youtubePassword = req.body.youtubePassword;
|
var youtubePassword = req.body.youtubePassword;
|
||||||
|
|
||||||
|
let merged_string = null;
|
||||||
|
|
||||||
let downloadConfig = null;
|
let downloadConfig = null;
|
||||||
let qualityPath = 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/mp4';
|
let qualityPath = 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/mp4';
|
||||||
|
|
||||||
@@ -717,10 +787,34 @@ app.post('/api/tomp4', function(req, res) {
|
|||||||
downloadConfig.splice(0, 0, '--external-downloader', 'aria2c');
|
downloadConfig.splice(0, 0, '--external-downloader', 'aria2c');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (useYoutubeDLArchive) {
|
||||||
|
let archive_path = videoFolderPath + 'archive.txt';
|
||||||
|
// create archive file if it doesn't exist
|
||||||
|
if (!fs.existsSync(archive_path)) {
|
||||||
|
fs.closeSync(fs.openSync(archive_path, 'w'));
|
||||||
|
}
|
||||||
|
|
||||||
|
let blacklist_path = videoFolderPath + 'blacklist.txt';
|
||||||
|
// create blacklist file if it doesn't exist
|
||||||
|
if (!fs.existsSync(blacklist_path)) {
|
||||||
|
fs.closeSync(fs.openSync(blacklist_path, 'w'));
|
||||||
|
}
|
||||||
|
|
||||||
|
let merged_path = videoFolderPath + 'merged.txt';
|
||||||
|
// merges blacklist and regular archive
|
||||||
|
let inputPathList = [archive_path, blacklist_path];
|
||||||
|
let status = await mergeFiles(inputPathList, merged_path);
|
||||||
|
|
||||||
|
merged_string = fs.readFileSync(merged_path, "utf8");
|
||||||
|
|
||||||
|
downloadConfig.push('--download-archive', merged_path);
|
||||||
|
}
|
||||||
|
|
||||||
if (globalArgs && globalArgs !== '') {
|
if (globalArgs && globalArgs !== '') {
|
||||||
// adds global args
|
// adds global args
|
||||||
downloadConfig = downloadConfig.concat(globalArgs.split(' '));
|
downloadConfig = downloadConfig.concat(globalArgs.split(' '));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
youtubedl.exec(url, downloadConfig, {}, function(err, output) {
|
youtubedl.exec(url, downloadConfig, {}, function(err, output) {
|
||||||
@@ -735,7 +829,11 @@ app.post('/api/tomp4', function(req, res) {
|
|||||||
res.sendStatus(500);
|
res.sendStatus(500);
|
||||||
throw err;
|
throw err;
|
||||||
} else if (output) {
|
} else if (output) {
|
||||||
var file_names = [];
|
if (output.length === 0 || output[0].length === 0) {
|
||||||
|
res.sendStatus(500);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var file_names = [];
|
||||||
for (let i = 0; i < output.length; i++) {
|
for (let i = 0; i < output.length; i++) {
|
||||||
let output_json = null;
|
let output_json = null;
|
||||||
try {
|
try {
|
||||||
@@ -759,12 +857,19 @@ app.post('/api/tomp4', function(req, res) {
|
|||||||
}
|
}
|
||||||
var alternate_file_name = file_name.substring(0, file_name.length-4);
|
var alternate_file_name = file_name.substring(0, file_name.length-4);
|
||||||
var file_path = output_json['_filename'].substring(audioFolderPath.length, output_json['_filename'].length);
|
var file_path = output_json['_filename'].substring(audioFolderPath.length, output_json['_filename'].length);
|
||||||
var alternate_file_path = file_path.substring(0, file_path.length-4);
|
// remove extension from file path
|
||||||
|
var alternate_file_path = file_path.replace(/\.[^/.]+$/, "")
|
||||||
if (alternate_file_name) file_names.push(alternate_file_path);
|
if (alternate_file_name) file_names.push(alternate_file_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
let is_playlist = file_names.length > 1;
|
let is_playlist = file_names.length > 1;
|
||||||
if (!is_playlist) audiopath = file_names[0];
|
if (!is_playlist) audiopath = file_names[0];
|
||||||
|
|
||||||
|
if (merged_string !== null) {
|
||||||
|
let current_merged_archive = fs.readFileSync(videoFolderPath + 'merged.txt', 'utf8');
|
||||||
|
let diff = current_merged_archive.replace(merged_string, '');
|
||||||
|
fs.appendFileSync(videoFolderPath + 'archive.txt', diff);
|
||||||
|
}
|
||||||
|
|
||||||
var videopathEncoded = encodeURIComponent(file_names[0]);
|
var videopathEncoded = encodeURIComponent(file_names[0]);
|
||||||
res.send({
|
res.send({
|
||||||
@@ -1091,11 +1196,12 @@ app.post('/api/deletePlaylist', async (req, res) => {
|
|||||||
// deletes mp3 file
|
// deletes mp3 file
|
||||||
app.post('/api/deleteMp3', async (req, res) => {
|
app.post('/api/deleteMp3', async (req, res) => {
|
||||||
var name = req.body.name;
|
var name = req.body.name;
|
||||||
|
var blacklistMode = req.body.blacklistMode;
|
||||||
var fullpath = audioFolderPath + name + ".mp3";
|
var fullpath = audioFolderPath + name + ".mp3";
|
||||||
var wasDeleted = false;
|
var wasDeleted = false;
|
||||||
if (fs.existsSync(fullpath))
|
if (fs.existsSync(fullpath))
|
||||||
{
|
{
|
||||||
deleteAudioFile(name);
|
deleteAudioFile(name, blacklistMode);
|
||||||
wasDeleted = true;
|
wasDeleted = true;
|
||||||
res.send(wasDeleted);
|
res.send(wasDeleted);
|
||||||
res.end("yes");
|
res.end("yes");
|
||||||
@@ -1111,11 +1217,12 @@ app.post('/api/deleteMp3', async (req, res) => {
|
|||||||
// deletes mp4 file
|
// deletes mp4 file
|
||||||
app.post('/api/deleteMp4', async (req, res) => {
|
app.post('/api/deleteMp4', async (req, res) => {
|
||||||
var name = req.body.name;
|
var name = req.body.name;
|
||||||
|
var blacklistMode = req.body.blacklistMode;
|
||||||
var fullpath = videoFolderPath + name + ".mp4";
|
var fullpath = videoFolderPath + name + ".mp4";
|
||||||
var wasDeleted = false;
|
var wasDeleted = false;
|
||||||
if (fs.existsSync(fullpath))
|
if (fs.existsSync(fullpath))
|
||||||
{
|
{
|
||||||
wasDeleted = await deleteVideoFile(name);
|
wasDeleted = await deleteVideoFile(name, null, blacklistMode);
|
||||||
// wasDeleted = true;
|
// wasDeleted = true;
|
||||||
res.send(wasDeleted);
|
res.send(wasDeleted);
|
||||||
res.end("yes");
|
res.end("yes");
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
"Downloader": {
|
"Downloader": {
|
||||||
"path-audio": "audio/",
|
"path-audio": "audio/",
|
||||||
"path-video": "video/",
|
"path-video": "video/",
|
||||||
|
"use_youtubedl_archive": false,
|
||||||
"custom_args": ""
|
"custom_args": ""
|
||||||
},
|
},
|
||||||
"Extra": {
|
"Extra": {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
"Downloader": {
|
"Downloader": {
|
||||||
"path-audio": "audio/",
|
"path-audio": "audio/",
|
||||||
"path-video": "video/",
|
"path-video": "video/",
|
||||||
|
"use_youtubedl_archive": false,
|
||||||
"custom_args": ""
|
"custom_args": ""
|
||||||
},
|
},
|
||||||
"Extra": {
|
"Extra": {
|
||||||
|
|||||||
@@ -34,6 +34,10 @@ let CONFIG_ITEMS = {
|
|||||||
'key': 'ytdl_video_folder_path',
|
'key': 'ytdl_video_folder_path',
|
||||||
'path': 'YoutubeDLMaterial.Downloader.path-video'
|
'path': 'YoutubeDLMaterial.Downloader.path-video'
|
||||||
},
|
},
|
||||||
|
'ytdl_use_youtubedl_archive': {
|
||||||
|
'key': 'ytdl_use_youtubedl_archive',
|
||||||
|
'path': 'YoutubeDLMaterial.Downloader.use_youtubedl_archive'
|
||||||
|
},
|
||||||
'ytdl_custom_args': {
|
'ytdl_custom_args': {
|
||||||
'key': 'ytdl_custom_args',
|
'key': 'ytdl_custom_args',
|
||||||
'path': 'YoutubeDLMaterial.Downloader.custom_args'
|
'path': 'YoutubeDLMaterial.Downloader.custom_args'
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
"exe": "^1.0.2",
|
"exe": "^1.0.2",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"lowdb": "^1.0.0",
|
"lowdb": "^1.0.0",
|
||||||
|
"merge-files": "^0.1.2",
|
||||||
"shortid": "^2.2.15",
|
"shortid": "^2.2.15",
|
||||||
"uuidv4": "^6.0.6",
|
"uuidv4": "^6.0.6",
|
||||||
"youtube-dl": "^3.0.2"
|
"youtube-dl": "^3.0.2"
|
||||||
|
|||||||
@@ -279,30 +279,30 @@ const deleteFolderRecursive = function(folder_to_delete) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function removeIDFromArchive(archive_path, id) {
|
function removeIDFromArchive(archive_path, id) {
|
||||||
fs.readFile(archive_path, {encoding: 'utf-8'}, function(err, data) {
|
let data = fs.readFileSync(archive_path, {encoding: 'utf-8'});
|
||||||
if (err) throw error;
|
if (!data) {
|
||||||
|
console.log('Archive could not be found.');
|
||||||
let dataArray = data.split('\n'); // convert file data in an array
|
return;
|
||||||
const searchKeyword = id; // we are looking for a line, contains, key word id in the file
|
}
|
||||||
let lastIndex = -1; // let say, we have not found the keyword
|
|
||||||
|
let dataArray = data.split('\n'); // convert file data in an array
|
||||||
for (let index=0; index<dataArray.length; index++) {
|
const searchKeyword = id; // we are looking for a line, contains, key word id in the file
|
||||||
if (dataArray[index].includes(searchKeyword)) { // check if a line contains the id keyword
|
let lastIndex = -1; // let say, we have not found the keyword
|
||||||
lastIndex = index; // found a line includes a id keyword
|
|
||||||
break;
|
for (let index=0; index<dataArray.length; index++) {
|
||||||
}
|
if (dataArray[index].includes(searchKeyword)) { // check if a line contains the id keyword
|
||||||
|
lastIndex = index; // found a line includes a id keyword
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
dataArray.splice(lastIndex, 1); // remove the keyword id from the data Array
|
|
||||||
|
const line = dataArray.splice(lastIndex, 1); // remove the keyword id from the data Array
|
||||||
// UPDATE FILE WITH NEW DATA
|
|
||||||
const updatedData = dataArray.join('\n');
|
// UPDATE FILE WITH NEW DATA
|
||||||
fs.writeFile(archive_path, updatedData, (err) => {
|
const updatedData = dataArray.join('\n');
|
||||||
if (err) throw err;
|
fs.writeFileSync(archive_path, updatedData);
|
||||||
// console.log ('Successfully updated the file data');
|
if (line) return line;
|
||||||
});
|
if (err) throw err;
|
||||||
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@@ -311,5 +311,6 @@ module.exports = {
|
|||||||
subscribe : subscribe,
|
subscribe : subscribe,
|
||||||
unsubscribe : unsubscribe,
|
unsubscribe : unsubscribe,
|
||||||
deleteSubscriptionFile : deleteSubscriptionFile,
|
deleteSubscriptionFile : deleteSubscriptionFile,
|
||||||
getVideosForSub : getVideosForSub
|
getVideosForSub : getVideosForSub,
|
||||||
|
removeIDFromArchive : removeIDFromArchive
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { NgModule, LOCALE_ID } from '@angular/core';
|
import { NgModule, LOCALE_ID } from '@angular/core';
|
||||||
import { registerLocaleData } from '@angular/common';
|
import { registerLocaleData } from '@angular/common';
|
||||||
import { LocaleService } from '@soluling/angular';
|
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatButtonToggleModule } from '@angular/material/button-toggle';
|
import { MatButtonToggleModule } from '@angular/material/button-toggle';
|
||||||
import { MatCardModule } from '@angular/material/card';
|
import { MatCardModule } from '@angular/material/card';
|
||||||
@@ -111,9 +110,7 @@ export function isVisible({ event, element, scrollContainer, offset }: IsVisible
|
|||||||
AppRoutingModule,
|
AppRoutingModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
PostsService,
|
PostsService
|
||||||
LocaleService,
|
|
||||||
{ provide: LOCALE_ID, deps: [LocaleService], useFactory: (service: LocaleService) => service.localeId },
|
|
||||||
],
|
],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -15,5 +15,10 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button (click)="deleteFile()" class="deleteButton" mat-icon-button><mat-icon>delete_forever</mat-icon></button>
|
<button *ngIf="!use_youtubedl_archive" (click)="deleteFile()" class="deleteButton" mat-icon-button><mat-icon>delete_forever</mat-icon></button>
|
||||||
|
<button [matMenuTriggerFor]="action_menu" *ngIf="use_youtubedl_archive" class="deleteButton" mat-icon-button><mat-icon>more_vert</mat-icon></button>
|
||||||
|
<mat-menu #action_menu="matMenu">
|
||||||
|
<button (click)="deleteFile()" mat-menu-item><mat-icon>delete</mat-icon><ng-container i18n="Delete video button">Delete</ng-container></button>
|
||||||
|
<button (click)="deleteFile(true)" mat-menu-item><mat-icon>delete_forever</mat-icon><ng-container i18n="Delete and blacklist video button">Delete and blacklist</ng-container></button>
|
||||||
|
</mat-menu>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export class FileCardComponent implements OnInit {
|
|||||||
@Output() removeFile: EventEmitter<string> = new EventEmitter<string>();
|
@Output() removeFile: EventEmitter<string> = new EventEmitter<string>();
|
||||||
@Input() isPlaylist = false;
|
@Input() isPlaylist = false;
|
||||||
@Input() count = null;
|
@Input() count = null;
|
||||||
|
@Input() use_youtubedl_archive = false;
|
||||||
type;
|
type;
|
||||||
image_loaded = false;
|
image_loaded = false;
|
||||||
image_errored = false;
|
image_errored = false;
|
||||||
@@ -40,9 +41,9 @@ export class FileCardComponent implements OnInit {
|
|||||||
this.type = this.isAudio ? 'audio' : 'video';
|
this.type = this.isAudio ? 'audio' : 'video';
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteFile() {
|
deleteFile(blacklistMode = false) {
|
||||||
if (!this.isPlaylist) {
|
if (!this.isPlaylist) {
|
||||||
this.postsService.deleteFile(this.name, this.isAudio).subscribe(result => {
|
this.postsService.deleteFile(this.name, this.isAudio, blacklistMode).subscribe(result => {
|
||||||
if (result === true) {
|
if (result === true) {
|
||||||
this.openSnackBar('Delete success!', 'OK.');
|
this.openSnackBar('Delete success!', 'OK.');
|
||||||
this.removeFile.emit(this.name);
|
this.removeFile.emit(this.name);
|
||||||
|
|||||||
@@ -204,7 +204,7 @@
|
|||||||
<mat-grid-list style="margin-bottom: 15px;" (window:resize)="onResize($event)" [cols]="files_cols" rowHeight="150px">
|
<mat-grid-list style="margin-bottom: 15px;" (window:resize)="onResize($event)" [cols]="files_cols" rowHeight="150px">
|
||||||
<mat-grid-tile *ngFor="let file of mp3s; index as i;">
|
<mat-grid-tile *ngFor="let file of mp3s; index as i;">
|
||||||
<app-file-card #audiofilecard (removeFile)="removeFromMp3($event)" [title]="file.title" [name]="file.id" [thumbnailURL]="file.thumbnailURL"
|
<app-file-card #audiofilecard (removeFile)="removeFromMp3($event)" [title]="file.title" [name]="file.id" [thumbnailURL]="file.thumbnailURL"
|
||||||
[length]="file.duration" [isAudio]="true"></app-file-card>
|
[length]="file.duration" [isAudio]="true" [use_youtubedl_archive]="use_youtubedl_archive"></app-file-card>
|
||||||
<mat-progress-bar *ngIf="downloading_content['audio'][file.id]" class="download-progress-bar" mode="indeterminate"></mat-progress-bar>
|
<mat-progress-bar *ngIf="downloading_content['audio'][file.id]" class="download-progress-bar" mode="indeterminate"></mat-progress-bar>
|
||||||
</mat-grid-tile>
|
</mat-grid-tile>
|
||||||
</mat-grid-list>
|
</mat-grid-list>
|
||||||
@@ -215,7 +215,7 @@
|
|||||||
<mat-grid-list *ngIf="playlists.audio.length > 0" (window:resize)="onResize($event)" [cols]="files_cols" rowHeight="150px">
|
<mat-grid-list *ngIf="playlists.audio.length > 0" (window:resize)="onResize($event)" [cols]="files_cols" rowHeight="150px">
|
||||||
<mat-grid-tile *ngFor="let playlist of playlists.audio; let i = index;">
|
<mat-grid-tile *ngFor="let playlist of playlists.audio; let i = index;">
|
||||||
<app-file-card #audiofilecard (removeFile)="removePlaylistMp3(playlist.id, i)" [title]="playlist.name" [name]="playlist.id" [thumbnailURL]="playlist_thumbnails[playlist.id]"
|
<app-file-card #audiofilecard (removeFile)="removePlaylistMp3(playlist.id, i)" [title]="playlist.name" [name]="playlist.id" [thumbnailURL]="playlist_thumbnails[playlist.id]"
|
||||||
[length]="null" [isAudio]="true" [isPlaylist]="true" [count]="playlist.fileNames.length"></app-file-card>
|
[length]="null" [isAudio]="true" [isPlaylist]="true" [count]="playlist.fileNames.length" [use_youtubedl_archive]="use_youtubedl_archive"></app-file-card>
|
||||||
<mat-progress-bar *ngIf="downloading_content['audio'][playlist.id]" class="download-progress-bar" mode="indeterminate"></mat-progress-bar>
|
<mat-progress-bar *ngIf="downloading_content['audio'][playlist.id]" class="download-progress-bar" mode="indeterminate"></mat-progress-bar>
|
||||||
</mat-grid-tile>
|
</mat-grid-tile>
|
||||||
</mat-grid-list>
|
</mat-grid-list>
|
||||||
@@ -245,7 +245,7 @@
|
|||||||
<mat-grid-list style="margin-bottom: 15px;" (window:resize)="onResize($event)" [cols]="files_cols" rowHeight="150px">
|
<mat-grid-list style="margin-bottom: 15px;" (window:resize)="onResize($event)" [cols]="files_cols" rowHeight="150px">
|
||||||
<mat-grid-tile *ngFor="let file of mp4s; index as i;">
|
<mat-grid-tile *ngFor="let file of mp4s; index as i;">
|
||||||
<app-file-card #videofilecard (removeFile)="removeFromMp4($event)" [title]="file.title" [name]="file.id" [thumbnailURL]="file.thumbnailURL"
|
<app-file-card #videofilecard (removeFile)="removeFromMp4($event)" [title]="file.title" [name]="file.id" [thumbnailURL]="file.thumbnailURL"
|
||||||
[length]="file.duration" [isAudio]="false"></app-file-card>
|
[length]="file.duration" [isAudio]="false" [use_youtubedl_archive]="use_youtubedl_archive"></app-file-card>
|
||||||
<mat-progress-bar *ngIf="downloading_content['video'][file.id]" class="download-progress-bar" mode="indeterminate"></mat-progress-bar>
|
<mat-progress-bar *ngIf="downloading_content['video'][file.id]" class="download-progress-bar" mode="indeterminate"></mat-progress-bar>
|
||||||
</mat-grid-tile>
|
</mat-grid-tile>
|
||||||
</mat-grid-list>
|
</mat-grid-list>
|
||||||
@@ -257,7 +257,7 @@
|
|||||||
<mat-grid-list *ngIf="playlists.video.length > 0" (window:resize)="onResize($event)" [cols]="files_cols" rowHeight="150px">
|
<mat-grid-list *ngIf="playlists.video.length > 0" (window:resize)="onResize($event)" [cols]="files_cols" rowHeight="150px">
|
||||||
<mat-grid-tile *ngFor="let playlist of playlists.video; let i = index;">
|
<mat-grid-tile *ngFor="let playlist of playlists.video; let i = index;">
|
||||||
<app-file-card #videofilecard (removeFile)="removePlaylistMp4(playlist.id, i)" [title]="playlist.name" [name]="playlist.id" [thumbnailURL]="playlist_thumbnails[playlist.id]"
|
<app-file-card #videofilecard (removeFile)="removePlaylistMp4(playlist.id, i)" [title]="playlist.name" [name]="playlist.id" [thumbnailURL]="playlist_thumbnails[playlist.id]"
|
||||||
[length]="null" [isAudio]="false" [isPlaylist]="true" [count]="playlist.fileNames.length"></app-file-card>
|
[length]="null" [isAudio]="false" [isPlaylist]="true" [count]="playlist.fileNames.length" [use_youtubedl_archive]="use_youtubedl_archive"></app-file-card>
|
||||||
<mat-progress-bar *ngIf="downloading_content['video'][playlist.id]" class="download-progress-bar" mode="indeterminate"></mat-progress-bar>
|
<mat-progress-bar *ngIf="downloading_content['video'][playlist.id]" class="download-progress-bar" mode="indeterminate"></mat-progress-bar>
|
||||||
</mat-grid-tile>
|
</mat-grid-tile>
|
||||||
</mat-grid-list>
|
</mat-grid-list>
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ export class MainComponent implements OnInit {
|
|||||||
allowMultiDownloadMode = false;
|
allowMultiDownloadMode = false;
|
||||||
audioFolderPath;
|
audioFolderPath;
|
||||||
videoFolderPath;
|
videoFolderPath;
|
||||||
|
use_youtubedl_archive = false;
|
||||||
globalCustomArgs = null;
|
globalCustomArgs = null;
|
||||||
allowAdvancedDownload = false;
|
allowAdvancedDownload = false;
|
||||||
useDefaultDownloadingAgent = true;
|
useDefaultDownloadingAgent = true;
|
||||||
@@ -241,6 +242,7 @@ export class MainComponent implements OnInit {
|
|||||||
this.allowMultiDownloadMode = result['YoutubeDLMaterial']['Extra']['allow_multi_download_mode'];
|
this.allowMultiDownloadMode = result['YoutubeDLMaterial']['Extra']['allow_multi_download_mode'];
|
||||||
this.audioFolderPath = result['YoutubeDLMaterial']['Downloader']['path-audio'];
|
this.audioFolderPath = result['YoutubeDLMaterial']['Downloader']['path-audio'];
|
||||||
this.videoFolderPath = result['YoutubeDLMaterial']['Downloader']['path-video'];
|
this.videoFolderPath = result['YoutubeDLMaterial']['Downloader']['path-video'];
|
||||||
|
this.use_youtubedl_archive = result['YoutubeDLMaterial']['Downloader']['use_youtubedl_archive'];
|
||||||
this.globalCustomArgs = result['YoutubeDLMaterial']['Downloader']['custom_args'];
|
this.globalCustomArgs = result['YoutubeDLMaterial']['Downloader']['custom_args'];
|
||||||
this.youtubeSearchEnabled = result['YoutubeDLMaterial']['API'] && result['YoutubeDLMaterial']['API']['use_youtube_API'] &&
|
this.youtubeSearchEnabled = result['YoutubeDLMaterial']['API'] && result['YoutubeDLMaterial']['API']['use_youtube_API'] &&
|
||||||
result['YoutubeDLMaterial']['API']['youtube_API_key'];
|
result['YoutubeDLMaterial']['API']['youtube_API_key'];
|
||||||
@@ -594,6 +596,8 @@ export class MainComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}, error => { // can't access server
|
}, error => { // can't access server
|
||||||
this.downloadingfile = false;
|
this.downloadingfile = false;
|
||||||
|
this.current_download = null;
|
||||||
|
new_download['downloading'] = false;
|
||||||
this.openSnackBar('Download failed!', 'OK.');
|
this.openSnackBar('Download failed!', 'OK.');
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -626,6 +630,8 @@ export class MainComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}, error => { // can't access server
|
}, error => { // can't access server
|
||||||
this.downloadingfile = false;
|
this.downloadingfile = false;
|
||||||
|
this.current_download = null;
|
||||||
|
new_download['downloading'] = false;
|
||||||
this.openSnackBar('Download failed!', 'OK.');
|
this.openSnackBar('Download failed!', 'OK.');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -879,6 +885,10 @@ export class MainComponent implements OnInit {
|
|||||||
full_string_array.push(...additional_params);
|
full_string_array.push(...additional_params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.use_youtubedl_archive) {
|
||||||
|
full_string_array.push('--download-archive', 'archive.txt');
|
||||||
|
}
|
||||||
|
|
||||||
if (globalArgsExists) {
|
if (globalArgsExists) {
|
||||||
full_string_array = full_string_array.concat(this.globalCustomArgs.split(' '));
|
full_string_array = full_string_array.concat(this.globalCustomArgs.split(' '));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,11 +98,11 @@ export class PostsService {
|
|||||||
return this.http.post(this.path + 'setConfig', {new_config_file: config});
|
return this.http.post(this.path + 'setConfig', {new_config_file: config});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteFile(name: string, isAudio: boolean) {
|
deleteFile(name: string, isAudio: boolean, blacklistMode = false) {
|
||||||
if (isAudio) {
|
if (isAudio) {
|
||||||
return this.http.post(this.path + 'deleteMp3', {name: name});
|
return this.http.post(this.path + 'deleteMp3', {name: name, blacklistMode: blacklistMode});
|
||||||
} else {
|
} else {
|
||||||
return this.http.post(this.path + 'deleteMp4', {name: name});
|
return this.http.post(this.path + 'deleteMp4', {name: name, blacklistMode: blacklistMode});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -93,6 +93,11 @@
|
|||||||
<mat-hint><ng-container i18n="Custom args setting input hint">Global custom args for downloads on the home page.</ng-container></mat-hint>
|
<mat-hint><ng-container i18n="Custom args setting input hint">Global custom args for downloads on the home page.</ng-container></mat-hint>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 mt-4">
|
||||||
|
<mat-checkbox color="accent" [(ngModel)]="new_config['Downloader']['use_youtubedl_archive']"><ng-container i18n="Use youtubedl archive setting">Use youtube-dl archive</ng-container></mat-checkbox>
|
||||||
|
<p>Note: This setting only applies to downloads on the Home page. If you would like to use youtube-dl archive functionality in subscriptions, head down to the Subscriptions section.</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<ng-container i18n="Video duration label">Length:</ng-container> {{formattedDuration}}
|
<ng-container i18n="Video duration label">Length:</ng-container> {{formattedDuration}}
|
||||||
</div>
|
</div>
|
||||||
<button [matMenuTriggerFor]="action_menu" class="menuButton" mat-icon-button><mat-icon>more_vert</mat-icon></button>
|
<button [matMenuTriggerFor]="action_menu" class="menuButton" mat-icon-button><mat-icon>more_vert</mat-icon></button>
|
||||||
<mat-menu #action_menu="matMenu">
|
<mat-menu #action_menu="matMenu">
|
||||||
<button (click)="deleteAndRedownload()" mat-menu-item><mat-icon>restore</mat-icon><ng-container i18n="Delete and redownload subscription video button">Delete and redownload</ng-container></button>
|
<button (click)="deleteAndRedownload()" mat-menu-item><mat-icon>restore</mat-icon><ng-container i18n="Delete and redownload subscription video button">Delete and redownload</ng-container></button>
|
||||||
<button (click)="deleteForever()" mat-menu-item *ngIf="sub.archive && use_youtubedl_archive"><mat-icon>delete_forever</mat-icon><ng-container i18n="Delete forever subscription video button">Delete forever</ng-container></button>
|
<button (click)="deleteForever()" mat-menu-item *ngIf="sub.archive && use_youtubedl_archive"><mat-icon>delete_forever</mat-icon><ng-container i18n="Delete forever subscription video button">Delete forever</ng-container></button>
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
|
|||||||
@@ -1,44 +1,45 @@
|
|||||||
{
|
{
|
||||||
"YoutubeDLMaterial": {
|
"YoutubeDLMaterial": {
|
||||||
"Host": {
|
"Host": {
|
||||||
"url": "http://localhost",
|
"url": "http://localhost",
|
||||||
"port": "17442"
|
"port": "17442"
|
||||||
},
|
},
|
||||||
"Encryption": {
|
"Encryption": {
|
||||||
"use-encryption": false,
|
"use-encryption": false,
|
||||||
"cert-file-path": "/etc/letsencrypt/live/example.com/fullchain.pem",
|
"cert-file-path": "/etc/letsencrypt/live/example.com/fullchain.pem",
|
||||||
"key-file-path": "/etc/letsencrypt/live/example.com/privkey.pem"
|
"key-file-path": "/etc/letsencrypt/live/example.com/privkey.pem"
|
||||||
},
|
},
|
||||||
"Downloader": {
|
"Downloader": {
|
||||||
"path-audio": "audio/",
|
"path-audio": "audio/",
|
||||||
"path-video": "video/",
|
"path-video": "video/",
|
||||||
"custom_args": ""
|
"use_youtubedl_archive": false,
|
||||||
},
|
"custom_args": ""
|
||||||
"Extra": {
|
},
|
||||||
"title_top": "Youtube Downloader",
|
"Extra": {
|
||||||
"file_manager_enabled": true,
|
"title_top": "Youtube Downloader",
|
||||||
"allow_quality_select": true,
|
"file_manager_enabled": true,
|
||||||
"download_only_mode": false,
|
"allow_quality_select": true,
|
||||||
"allow_multi_download_mode": true
|
"download_only_mode": false,
|
||||||
},
|
"allow_multi_download_mode": true
|
||||||
"API": {
|
},
|
||||||
"use_youtube_API": false,
|
"API": {
|
||||||
"youtube_API_key": ""
|
"use_youtube_API": false,
|
||||||
},
|
"youtube_API_key": ""
|
||||||
"Themes": {
|
},
|
||||||
"default_theme": "default",
|
"Themes": {
|
||||||
"allow_theme_change": true
|
"default_theme": "default",
|
||||||
},
|
"allow_theme_change": true
|
||||||
"Subscriptions": {
|
},
|
||||||
"allow_subscriptions": true,
|
"Subscriptions": {
|
||||||
"subscriptions_base_path": "subscriptions/",
|
"allow_subscriptions": true,
|
||||||
"subscriptions_check_interval": "300",
|
"subscriptions_base_path": "subscriptions/",
|
||||||
"subscriptions_use_youtubedl_archive": true
|
"subscriptions_check_interval": "300",
|
||||||
},
|
"subscriptions_use_youtubedl_archive": true
|
||||||
"Advanced": {
|
},
|
||||||
"use_default_downloading_agent": true,
|
"Advanced": {
|
||||||
"custom_downloading_agent": "",
|
"use_default_downloading_agent": true,
|
||||||
"allow_advanced_download": true
|
"custom_downloading_agent": "",
|
||||||
}
|
"allow_advanced_download": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user