mirror of
https://github.com/Tzahi12345/YoutubeDL-Material.git
synced 2026-04-14 15:21:30 +03:00
Merge pull request #136 from Tzahi12345/subscriptions-custom-path
Subscriptions V3
This commit is contained in:
226
backend/app.js
226
backend/app.js
@@ -13,6 +13,8 @@ var express = require("express");
|
|||||||
var bodyParser = require("body-parser");
|
var bodyParser = require("body-parser");
|
||||||
var archiver = require('archiver');
|
var archiver = require('archiver');
|
||||||
var unzipper = require('unzipper');
|
var unzipper = require('unzipper');
|
||||||
|
var db_api = require('./db')
|
||||||
|
var utils = require('./utils')
|
||||||
var mergeFiles = require('merge-files');
|
var mergeFiles = require('merge-files');
|
||||||
const low = require('lowdb')
|
const low = require('lowdb')
|
||||||
var ProgressBar = require('progress');
|
var ProgressBar = require('progress');
|
||||||
@@ -73,8 +75,9 @@ const logger = winston.createLogger({
|
|||||||
});
|
});
|
||||||
|
|
||||||
config_api.initialize(logger);
|
config_api.initialize(logger);
|
||||||
subscriptions_api.initialize(db, users_db, logger);
|
|
||||||
auth_api.initialize(users_db, logger);
|
auth_api.initialize(users_db, logger);
|
||||||
|
db_api.initialize(db, users_db, logger);
|
||||||
|
subscriptions_api.initialize(db, users_db, logger, db_api);
|
||||||
|
|
||||||
// var GithubContent = require('github-content');
|
// var GithubContent = require('github-content');
|
||||||
|
|
||||||
@@ -191,27 +194,12 @@ app.use(bodyParser.json());
|
|||||||
// use passport
|
// use passport
|
||||||
app.use(auth_api.passport.initialize());
|
app.use(auth_api.passport.initialize());
|
||||||
|
|
||||||
// objects
|
|
||||||
|
|
||||||
function File(id, title, thumbnailURL, isAudio, duration, url, uploader, size, path, upload_date) {
|
|
||||||
this.id = id;
|
|
||||||
this.title = title;
|
|
||||||
this.thumbnailURL = thumbnailURL;
|
|
||||||
this.isAudio = isAudio;
|
|
||||||
this.duration = duration;
|
|
||||||
this.url = url;
|
|
||||||
this.uploader = uploader;
|
|
||||||
this.size = size;
|
|
||||||
this.path = path;
|
|
||||||
this.upload_date = upload_date;
|
|
||||||
}
|
|
||||||
|
|
||||||
// actual functions
|
// actual functions
|
||||||
|
|
||||||
async function checkMigrations() {
|
async function checkMigrations() {
|
||||||
return new Promise(async resolve => {
|
return new Promise(async resolve => {
|
||||||
// 3.5->3.6 migration
|
// 3.5->3.6 migration
|
||||||
const files_to_db_migration_complete = db.get('files_to_db_migration_complete').value();
|
const files_to_db_migration_complete = true; // migration phased out! previous code: db.get('files_to_db_migration_complete').value();
|
||||||
|
|
||||||
if (!files_to_db_migration_complete) {
|
if (!files_to_db_migration_complete) {
|
||||||
logger.info('Beginning migration: 3.5->3.6+')
|
logger.info('Beginning migration: 3.5->3.6+')
|
||||||
@@ -236,7 +224,7 @@ async function runFilesToDBMigration() {
|
|||||||
const file_already_in_db = db.get('files.audio').find({id: file_obj.id}).value();
|
const file_already_in_db = db.get('files.audio').find({id: file_obj.id}).value();
|
||||||
if (!file_already_in_db) {
|
if (!file_already_in_db) {
|
||||||
logger.verbose(`Migrating file ${file_obj.id}`);
|
logger.verbose(`Migrating file ${file_obj.id}`);
|
||||||
registerFileDB(file_obj.id + '.mp3', 'audio');
|
db_api.registerFileDB(file_obj.id + '.mp3', 'audio');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,7 +233,7 @@ async function runFilesToDBMigration() {
|
|||||||
const file_already_in_db = db.get('files.video').find({id: file_obj.id}).value();
|
const file_already_in_db = db.get('files.video').find({id: file_obj.id}).value();
|
||||||
if (!file_already_in_db) {
|
if (!file_already_in_db) {
|
||||||
logger.verbose(`Migrating file ${file_obj.id}`);
|
logger.verbose(`Migrating file ${file_obj.id}`);
|
||||||
registerFileDB(file_obj.id + '.mp4', 'video');
|
db_api.registerFileDB(file_obj.id + '.mp4', 'video');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -654,6 +642,11 @@ async function watchSubscriptions() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!sub.name) {
|
||||||
|
logger.verbose(`Subscription: skipped check for subscription with uid ${sub.id} as name has not been retrieved yet.`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
logger.verbose('Watching ' + sub.name + ' with delay interval of ' + delay_interval);
|
logger.verbose('Watching ' + sub.name + ' with delay interval of ' + delay_interval);
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
await subscriptions_api.getVideosForSub(sub, sub.user_uid);
|
await subscriptions_api.getVideosForSub(sub, sub.user_uid);
|
||||||
@@ -700,7 +693,7 @@ function getMp3s() {
|
|||||||
var stats = fs.statSync(file);
|
var stats = fs.statSync(file);
|
||||||
|
|
||||||
var id = file_path.substring(0, file_path.length-4);
|
var id = file_path.substring(0, file_path.length-4);
|
||||||
var jsonobj = getJSONMp3(id);
|
var jsonobj = utils.getJSONMp3(id, audioFolderPath);
|
||||||
if (!jsonobj) continue;
|
if (!jsonobj) continue;
|
||||||
var title = jsonobj.title;
|
var title = jsonobj.title;
|
||||||
var url = jsonobj.webpage_url;
|
var url = jsonobj.webpage_url;
|
||||||
@@ -713,7 +706,7 @@ function getMp3s() {
|
|||||||
var thumbnail = jsonobj.thumbnail;
|
var thumbnail = jsonobj.thumbnail;
|
||||||
var duration = jsonobj.duration;
|
var duration = jsonobj.duration;
|
||||||
var isaudio = true;
|
var isaudio = true;
|
||||||
var file_obj = new File(id, title, thumbnail, isaudio, duration, url, uploader, size, file, upload_date);
|
var file_obj = new utils.File(id, title, thumbnail, isaudio, duration, url, uploader, size, file, upload_date);
|
||||||
mp3s.push(file_obj);
|
mp3s.push(file_obj);
|
||||||
}
|
}
|
||||||
return mp3s;
|
return mp3s;
|
||||||
@@ -729,7 +722,7 @@ function getMp4s(relative_path = true) {
|
|||||||
var stats = fs.statSync(file);
|
var stats = fs.statSync(file);
|
||||||
|
|
||||||
var id = file_path.substring(0, file_path.length-4);
|
var id = file_path.substring(0, file_path.length-4);
|
||||||
var jsonobj = getJSONMp4(id);
|
var jsonobj = utils.getJSONMp4(id, videoFolderPath);
|
||||||
if (!jsonobj) continue;
|
if (!jsonobj) continue;
|
||||||
var title = jsonobj.title;
|
var title = jsonobj.title;
|
||||||
var url = jsonobj.webpage_url;
|
var url = jsonobj.webpage_url;
|
||||||
@@ -742,7 +735,7 @@ function getMp4s(relative_path = true) {
|
|||||||
var size = stats.size;
|
var size = stats.size;
|
||||||
|
|
||||||
var isaudio = false;
|
var isaudio = false;
|
||||||
var file_obj = new File(id, title, thumbnail, isaudio, duration, url, uploader, size, file, upload_date);
|
var file_obj = new utils.File(id, title, thumbnail, isaudio, duration, url, uploader, size, file, upload_date);
|
||||||
mp4s.push(file_obj);
|
mp4s.push(file_obj);
|
||||||
}
|
}
|
||||||
return mp4s;
|
return mp4s;
|
||||||
@@ -750,14 +743,14 @@ function getMp4s(relative_path = true) {
|
|||||||
|
|
||||||
function getThumbnailMp3(name)
|
function getThumbnailMp3(name)
|
||||||
{
|
{
|
||||||
var obj = getJSONMp3(name);
|
var obj = utils.getJSONMp3(name, audioFolderPath);
|
||||||
var thumbnailLink = obj.thumbnail;
|
var thumbnailLink = obj.thumbnail;
|
||||||
return thumbnailLink;
|
return thumbnailLink;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getThumbnailMp4(name)
|
function getThumbnailMp4(name)
|
||||||
{
|
{
|
||||||
var obj = getJSONMp4(name);
|
var obj = utils.getJSONMp4(name, videoFolderPath);
|
||||||
var thumbnailLink = obj.thumbnail;
|
var thumbnailLink = obj.thumbnail;
|
||||||
return thumbnailLink;
|
return thumbnailLink;
|
||||||
}
|
}
|
||||||
@@ -794,54 +787,6 @@ function getFileSizeMp4(name)
|
|||||||
return filesize;
|
return filesize;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getJSONMp3(name, customPath = null, openReadPerms = false)
|
|
||||||
{
|
|
||||||
var jsonPath = audioFolderPath+name+".info.json";
|
|
||||||
var alternateJsonPath = audioFolderPath+name+".mp3.info.json";
|
|
||||||
if (!customPath) {
|
|
||||||
jsonPath = audioFolderPath + name + ".info.json";
|
|
||||||
} else {
|
|
||||||
jsonPath = customPath + name + ".info.json";
|
|
||||||
alternateJsonPath = customPath + name + ".mp3.info.json";
|
|
||||||
}
|
|
||||||
var obj = null;
|
|
||||||
if (fs.existsSync(jsonPath)) {
|
|
||||||
obj = JSON.parse(fs.readFileSync(jsonPath, 'utf8'));
|
|
||||||
if (!is_windows && openReadPerms) fs.chmodSync(jsonPath, 0o755);
|
|
||||||
}
|
|
||||||
else if (fs.existsSync(alternateJsonPath)) {
|
|
||||||
obj = JSON.parse(fs.readFileSync(alternateJsonPath, 'utf8'));
|
|
||||||
if (!is_windows && openReadPerms) fs.chmodSync(alternateJsonPath, 0o755);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
obj = 0;
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getJSONMp4(name, customPath = null, openReadPerms = false)
|
|
||||||
{
|
|
||||||
var obj = null; // output
|
|
||||||
let jsonPath = null;
|
|
||||||
var alternateJsonPath = videoFolderPath + name + ".mp4.info.json";
|
|
||||||
if (!customPath) {
|
|
||||||
jsonPath = videoFolderPath + name + ".info.json";
|
|
||||||
} else {
|
|
||||||
jsonPath = customPath + name + ".info.json";
|
|
||||||
alternateJsonPath = customPath + name + ".mp4.info.json";
|
|
||||||
}
|
|
||||||
if (fs.existsSync(jsonPath))
|
|
||||||
{
|
|
||||||
obj = JSON.parse(fs.readFileSync(jsonPath, 'utf8'));
|
|
||||||
if (openReadPerms) fs.chmodSync(jsonPath, 0o644);
|
|
||||||
} else if (fs.existsSync(alternateJsonPath)) {
|
|
||||||
obj = JSON.parse(fs.readFileSync(alternateJsonPath, 'utf8'));
|
|
||||||
if (openReadPerms) fs.chmodSync(alternateJsonPath, 0o644);
|
|
||||||
}
|
|
||||||
else obj = 0;
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAmountDownloadedMp3(name)
|
function getAmountDownloadedMp3(name)
|
||||||
{
|
{
|
||||||
var partPath = audioFolderPath+name+".mp3.part";
|
var partPath = audioFolderPath+name+".mp3.part";
|
||||||
@@ -965,7 +910,7 @@ async function deleteAudioFile(name, blacklistMode = false) {
|
|||||||
|
|
||||||
// get ID from JSON
|
// get ID from JSON
|
||||||
|
|
||||||
var jsonobj = getJSONMp3(name);
|
var jsonobj = utils.getJSONMp3(name, audioFolderPath);
|
||||||
let id = null;
|
let id = null;
|
||||||
if (jsonobj) id = jsonobj.id;
|
if (jsonobj) id = jsonobj.id;
|
||||||
|
|
||||||
@@ -1023,7 +968,7 @@ async function deleteVideoFile(name, customPath = null, blacklistMode = false) {
|
|||||||
|
|
||||||
// get ID from JSON
|
// get ID from JSON
|
||||||
|
|
||||||
var jsonobj = getJSONMp4(name);
|
var jsonobj = utils.getJSONMp4(name, videoFolderPath);
|
||||||
let id = null;
|
let id = null;
|
||||||
if (jsonobj) id = jsonobj.id;
|
if (jsonobj) id = jsonobj.id;
|
||||||
|
|
||||||
@@ -1077,7 +1022,7 @@ function recFindByExt(base,ext,files,result)
|
|||||||
)
|
)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
function registerFileDB(file_path, type, multiUserMode = null) {
|
function registerFileDB(file_path, type, multiUserMode = null) {
|
||||||
const file_id = file_path.substring(0, file_path.length-4);
|
const file_id = file_path.substring(0, file_path.length-4);
|
||||||
const file_object = generateFileObject(file_id, type, multiUserMode && multiUserMode.file_path);
|
const file_object = generateFileObject(file_id, type, multiUserMode && multiUserMode.file_path);
|
||||||
@@ -1094,7 +1039,7 @@ function registerFileDB(file_path, type, multiUserMode = null) {
|
|||||||
|
|
||||||
if (multiUserMode) {
|
if (multiUserMode) {
|
||||||
auth_api.registerUserFile(multiUserMode.user, file_object, type);
|
auth_api.registerUserFile(multiUserMode.user, file_object, type);
|
||||||
} else {
|
} else if (type === 'audio' || type === 'video') {
|
||||||
// remove existing video if overwriting
|
// remove existing video if overwriting
|
||||||
db.get(`files.${type}`)
|
db.get(`files.${type}`)
|
||||||
.remove({
|
.remove({
|
||||||
@@ -1104,13 +1049,15 @@ function registerFileDB(file_path, type, multiUserMode = null) {
|
|||||||
db.get(`files.${type}`)
|
db.get(`files.${type}`)
|
||||||
.push(file_object)
|
.push(file_object)
|
||||||
.write();
|
.write();
|
||||||
|
} else if (type == 'subscription') {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return file_object['uid'];
|
return file_object['uid'];
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateFileObject(id, type, customPath = null) {
|
function generateFileObject(id, type, customPath = null) {
|
||||||
var jsonobj = (type === 'audio') ? getJSONMp3(id, customPath, true) : getJSONMp4(id, customPath, true);
|
var jsonobj = (type === 'audio') ? utils.getJSONMp3(id, customPath, true) : utils.getJSONMp4(id, customPath, true);
|
||||||
if (!jsonobj) {
|
if (!jsonobj) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -1130,10 +1077,10 @@ function generateFileObject(id, type, customPath = null) {
|
|||||||
var thumbnail = jsonobj.thumbnail;
|
var thumbnail = jsonobj.thumbnail;
|
||||||
var duration = jsonobj.duration;
|
var duration = jsonobj.duration;
|
||||||
var isaudio = type === 'audio';
|
var isaudio = type === 'audio';
|
||||||
var file_obj = new File(id, title, thumbnail, isaudio, duration, url, uploader, size, file_path, upload_date);
|
var file_obj = new utils.File(id, title, thumbnail, isaudio, duration, url, uploader, size, file_path, upload_date);
|
||||||
return file_obj;
|
return file_obj;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
// replaces .webm with appropriate extension
|
// replaces .webm with appropriate extension
|
||||||
function getTrueFileName(unfixed_path, type) {
|
function getTrueFileName(unfixed_path, type) {
|
||||||
let fixed_path = unfixed_path;
|
let fixed_path = unfixed_path;
|
||||||
@@ -1290,7 +1237,7 @@ async function downloadFileByURL_exec(url, type, options, sessionID = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// registers file in DB
|
// registers file in DB
|
||||||
file_uid = registerFileDB(full_file_path.substring(fileFolderPath.length, full_file_path.length), type, multiUserMode);
|
file_uid = db_api.registerFileDB(full_file_path.substring(fileFolderPath.length, full_file_path.length), type, multiUserMode);
|
||||||
|
|
||||||
if (file_name) file_names.push(file_name);
|
if (file_name) file_names.push(file_name);
|
||||||
}
|
}
|
||||||
@@ -1429,7 +1376,7 @@ async function downloadFileByURL_normal(url, type, options, sessionID = null) {
|
|||||||
|
|
||||||
// registers file in DB
|
// registers file in DB
|
||||||
const base_file_name = video_info._filename.substring(fileFolderPath.length, video_info._filename.length);
|
const base_file_name = video_info._filename.substring(fileFolderPath.length, video_info._filename.length);
|
||||||
file_uid = registerFileDB(base_file_name, type, multiUserMode);
|
file_uid = db_api.registerFileDB(base_file_name, type, multiUserMode);
|
||||||
|
|
||||||
if (options.merged_string !== null && options.merged_string !== undefined) {
|
if (options.merged_string !== null && options.merged_string !== undefined) {
|
||||||
let current_merged_archive = fs.readFileSync(path.join(fileFolderPath, `merged_${type}.txt`), 'utf8');
|
let current_merged_archive = fs.readFileSync(path.join(fileFolderPath, `merged_${type}.txt`), 'utf8');
|
||||||
@@ -2152,14 +2099,17 @@ app.post('/api/subscribe', optionalJwt, async (req, res) => {
|
|||||||
let url = req.body.url;
|
let url = req.body.url;
|
||||||
let timerange = req.body.timerange;
|
let timerange = req.body.timerange;
|
||||||
let streamingOnly = req.body.streamingOnly;
|
let streamingOnly = req.body.streamingOnly;
|
||||||
|
let audioOnly = req.body.audioOnly;
|
||||||
|
let customArgs = req.body.customArgs;
|
||||||
|
let customOutput = req.body.customFileOutput;
|
||||||
let user_uid = req.isAuthenticated() ? req.user.uid : null;
|
let user_uid = req.isAuthenticated() ? req.user.uid : null;
|
||||||
|
|
||||||
const new_sub = {
|
const new_sub = {
|
||||||
name: name,
|
name: name,
|
||||||
url: url,
|
url: url,
|
||||||
id: uuid(),
|
id: uuid(),
|
||||||
streamingOnly: streamingOnly,
|
streamingOnly: streamingOnly,
|
||||||
user_uid: user_uid
|
user_uid: user_uid,
|
||||||
|
type: audioOnly ? 'audio' : 'video'
|
||||||
};
|
};
|
||||||
|
|
||||||
// adds timerange if it exists, otherwise all videos will be downloaded
|
// adds timerange if it exists, otherwise all videos will be downloaded
|
||||||
@@ -2167,6 +2117,14 @@ app.post('/api/subscribe', optionalJwt, async (req, res) => {
|
|||||||
new_sub.timerange = timerange;
|
new_sub.timerange = timerange;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (customArgs && customArgs !== '') {
|
||||||
|
new_sub.custom_args = customArgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (customOutput && customOutput !== '') {
|
||||||
|
new_sub.custom_output = customOutput;
|
||||||
|
}
|
||||||
|
|
||||||
const result_obj = await subscriptions_api.subscribe(new_sub, user_uid);
|
const result_obj = await subscriptions_api.subscribe(new_sub, user_uid);
|
||||||
|
|
||||||
if (result_obj.success) {
|
if (result_obj.success) {
|
||||||
@@ -2202,10 +2160,11 @@ app.post('/api/unsubscribe', optionalJwt, async (req, res) => {
|
|||||||
app.post('/api/deleteSubscriptionFile', optionalJwt, async (req, res) => {
|
app.post('/api/deleteSubscriptionFile', optionalJwt, async (req, res) => {
|
||||||
let deleteForever = req.body.deleteForever;
|
let deleteForever = req.body.deleteForever;
|
||||||
let file = req.body.file;
|
let file = req.body.file;
|
||||||
|
let file_uid = req.body.file_uid;
|
||||||
let sub = req.body.sub;
|
let sub = req.body.sub;
|
||||||
let user_uid = req.isAuthenticated() ? req.user.uid : null;
|
let user_uid = req.isAuthenticated() ? req.user.uid : null;
|
||||||
|
|
||||||
let success = await subscriptions_api.deleteSubscriptionFile(sub, file, deleteForever, user_uid);
|
let success = await subscriptions_api.deleteSubscriptionFile(sub, file, deleteForever, file_uid, user_uid);
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
res.send({
|
res.send({
|
||||||
@@ -2232,45 +2191,49 @@ app.post('/api/getSubscription', optionalJwt, async (req, res) => {
|
|||||||
|
|
||||||
// get sub videos
|
// get sub videos
|
||||||
if (subscription.name && !subscription.streamingOnly) {
|
if (subscription.name && !subscription.streamingOnly) {
|
||||||
let base_path = null;
|
var parsed_files = subscription.videos;
|
||||||
if (user_uid)
|
if (!parsed_files) {
|
||||||
base_path = path.join(config_api.getConfigItem('ytdl_users_base_path'), user_uid, 'subscriptions');
|
parsed_files = [];
|
||||||
else
|
let base_path = null;
|
||||||
base_path = config_api.getConfigItem('ytdl_subscriptions_base_path');
|
if (user_uid)
|
||||||
|
base_path = path.join(config_api.getConfigItem('ytdl_users_base_path'), user_uid, 'subscriptions');
|
||||||
|
else
|
||||||
|
base_path = config_api.getConfigItem('ytdl_subscriptions_base_path');
|
||||||
|
|
||||||
let appended_base_path = path.join(base_path, (subscription.isPlaylist ? 'playlists' : 'channels'), subscription.name, '/');
|
let appended_base_path = path.join(base_path, (subscription.isPlaylist ? 'playlists' : 'channels'), subscription.name, '/');
|
||||||
let files;
|
let files;
|
||||||
try {
|
try {
|
||||||
files = recFindByExt(appended_base_path, 'mp4');
|
files = recFindByExt(appended_base_path, 'mp4');
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
files = null;
|
files = null;
|
||||||
logger.info('Failed to get folder for subscription: ' + subscription.name + ' at path ' + appended_base_path);
|
logger.info('Failed to get folder for subscription: ' + subscription.name + ' at path ' + appended_base_path);
|
||||||
res.sendStatus(500);
|
res.sendStatus(500);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
for (let i = 0; i < files.length; i++) {
|
||||||
|
let file = files[i];
|
||||||
|
var file_path = file.substring(appended_base_path.length, file.length);
|
||||||
|
var stats = fs.statSync(file);
|
||||||
|
|
||||||
|
var id = file_path.substring(0, file_path.length-4);
|
||||||
|
var jsonobj = utils.getJSONMp4(id, appended_base_path);
|
||||||
|
if (!jsonobj) continue;
|
||||||
|
var title = jsonobj.title;
|
||||||
|
|
||||||
|
var thumbnail = jsonobj.thumbnail;
|
||||||
|
var duration = jsonobj.duration;
|
||||||
|
var url = jsonobj.webpage_url;
|
||||||
|
var uploader = jsonobj.uploader;
|
||||||
|
var upload_date = jsonobj.upload_date;
|
||||||
|
upload_date = `${upload_date.substring(0, 4)}-${upload_date.substring(4, 6)}-${upload_date.substring(6, 8)}`;
|
||||||
|
var size = stats.size;
|
||||||
|
|
||||||
|
var isaudio = false;
|
||||||
|
var file_obj = new utils.File(id, title, thumbnail, isaudio, duration, url, uploader, size, file, upload_date);
|
||||||
|
parsed_files.push(file_obj);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
var parsed_files = [];
|
|
||||||
for (let i = 0; i < files.length; i++) {
|
|
||||||
let file = files[i];
|
|
||||||
var file_path = file.substring(appended_base_path.length, file.length);
|
|
||||||
var stats = fs.statSync(file);
|
|
||||||
|
|
||||||
var id = file_path.substring(0, file_path.length-4);
|
|
||||||
var jsonobj = getJSONMp4(id, appended_base_path);
|
|
||||||
if (!jsonobj) continue;
|
|
||||||
var title = jsonobj.title;
|
|
||||||
|
|
||||||
var thumbnail = jsonobj.thumbnail;
|
|
||||||
var duration = jsonobj.duration;
|
|
||||||
var url = jsonobj.webpage_url;
|
|
||||||
var uploader = jsonobj.uploader;
|
|
||||||
var upload_date = jsonobj.upload_date;
|
|
||||||
upload_date = `${upload_date.substring(0, 4)}-${upload_date.substring(4, 6)}-${upload_date.substring(6, 8)}`;
|
|
||||||
var size = stats.size;
|
|
||||||
|
|
||||||
var isaudio = false;
|
|
||||||
var file_obj = new File(id, title, thumbnail, isaudio, duration, url, uploader, size, file, upload_date);
|
|
||||||
parsed_files.push(file_obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.send({
|
res.send({
|
||||||
subscription: subscription,
|
subscription: subscription,
|
||||||
@@ -2282,7 +2245,7 @@ app.post('/api/getSubscription', optionalJwt, async (req, res) => {
|
|||||||
if (subscription.videos) {
|
if (subscription.videos) {
|
||||||
for (let i = 0; i < subscription.videos.length; i++) {
|
for (let i = 0; i < subscription.videos.length; i++) {
|
||||||
const video = subscription.videos[i];
|
const video = subscription.videos[i];
|
||||||
parsed_files.push(new File(video.title, video.title, video.thumbnail, false, video.duration, video.url, video.uploader, video.size, null, null, video.upload_date));
|
parsed_files.push(new utils.File(video.title, video.title, video.thumbnail, false, video.duration, video.url, video.uploader, video.size, null, null, video.upload_date));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res.send({
|
res.send({
|
||||||
@@ -2522,7 +2485,8 @@ app.post('/api/downloadFile', optionalJwt, async (req, res) => {
|
|||||||
basePath = path.join(usersFileFolder, req.user.uid, 'subscriptions');
|
basePath = path.join(usersFileFolder, req.user.uid, 'subscriptions');
|
||||||
else
|
else
|
||||||
basePath = config_api.getConfigItem('ytdl_subscriptions_base_path');
|
basePath = config_api.getConfigItem('ytdl_subscriptions_base_path');
|
||||||
file = path.join(__dirname, basePath, (subscriptionPlaylist === 'true' ? 'playlists' : 'channels'), subscriptionName, fileNames + '.mp4')
|
|
||||||
|
file = path.join(__dirname, basePath, (subscriptionPlaylist === 'true' ? 'playlists' : 'channels'), subscriptionName, fileNames + ext);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (let i = 0; i < fileNames.length; i++) {
|
for (let i = 0; i < fileNames.length; i++) {
|
||||||
@@ -2723,9 +2687,21 @@ app.get('/api/audio/:id', optionalJwt, function(req , res){
|
|||||||
var head;
|
var head;
|
||||||
let id = decodeURIComponent(req.params.id);
|
let id = decodeURIComponent(req.params.id);
|
||||||
let file_path = "audio/" + id + '.mp3';
|
let file_path = "audio/" + id + '.mp3';
|
||||||
|
let usersFileFolder = config_api.getConfigItem('ytdl_users_base_path');
|
||||||
|
let optionalParams = url_api.parse(req.url,true).query;
|
||||||
if (req.isAuthenticated()) {
|
if (req.isAuthenticated()) {
|
||||||
let usersFileFolder = config_api.getConfigItem('ytdl_users_base_path');
|
if (optionalParams['subName']) {
|
||||||
file_path = path.join(usersFileFolder, req.user.uid, 'audio', id + '.mp3');
|
const isPlaylist = optionalParams['subPlaylist'];
|
||||||
|
file_path = path.join(usersFileFolder, req.user.uid, 'subscriptions', (isPlaylist === 'true' ? 'playlists/' : 'channels/'),optionalParams['subName'], id + '.mp3')
|
||||||
|
} else {
|
||||||
|
let usersFileFolder = config_api.getConfigItem('ytdl_users_base_path');
|
||||||
|
file_path = path.join(usersFileFolder, req.user.uid, 'audio', id + '.mp3');
|
||||||
|
}
|
||||||
|
} else if (optionalParams['subName']) {
|
||||||
|
let basePath = config_api.getConfigItem('ytdl_subscriptions_base_path');
|
||||||
|
const isPlaylist = optionalParams['subPlaylist'];
|
||||||
|
basePath += (isPlaylist === 'true' ? 'playlists/' : 'channels/');
|
||||||
|
file_path = basePath + optionalParams['subName'] + '/' + id + '.mp3';
|
||||||
}
|
}
|
||||||
file_path = file_path.replace(/\"/g, '\'');
|
file_path = file_path.replace(/\"/g, '\'');
|
||||||
const stat = fs.statSync(file_path)
|
const stat = fs.statSync(file_path)
|
||||||
|
|||||||
103
backend/db.js
Normal file
103
backend/db.js
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
var fs = require('fs-extra')
|
||||||
|
var path = require('path')
|
||||||
|
var utils = require('./utils')
|
||||||
|
const { uuid } = require('uuidv4');
|
||||||
|
const config_api = require('./config');
|
||||||
|
|
||||||
|
var logger = null;
|
||||||
|
var db = null;
|
||||||
|
var users_db = null;
|
||||||
|
function setDB(input_db, input_users_db) { db = input_db; users_db = input_users_db }
|
||||||
|
function setLogger(input_logger) { logger = input_logger; }
|
||||||
|
|
||||||
|
function initialize(input_db, input_users_db, input_logger) {
|
||||||
|
setDB(input_db, input_users_db);
|
||||||
|
setLogger(input_logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerFileDB(file_path, type, multiUserMode = null, sub = null) {
|
||||||
|
const file_id = file_path.substring(0, file_path.length-4);
|
||||||
|
const file_object = generateFileObject(file_id, type, multiUserMode && multiUserMode.file_path, sub);
|
||||||
|
if (!file_object) {
|
||||||
|
logger.error(`Could not find associated JSON file for ${type} file ${file_id}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add additional info
|
||||||
|
file_object['uid'] = uuid();
|
||||||
|
file_object['registered'] = Date.now();
|
||||||
|
path_object = path.parse(file_object['path']);
|
||||||
|
file_object['path'] = path.format(path_object);
|
||||||
|
|
||||||
|
if (!sub) {
|
||||||
|
if (multiUserMode) {
|
||||||
|
const user_uid = multiUserMode.user;
|
||||||
|
users_db.get('users').find({uid: user_uid}).get(`files.${type}`)
|
||||||
|
.remove({
|
||||||
|
path: file_object['path']
|
||||||
|
}).write();
|
||||||
|
|
||||||
|
users_db.get('users').find({uid: user_uid}).get(`files.${type}`)
|
||||||
|
.push(file_object)
|
||||||
|
.write();
|
||||||
|
} else {
|
||||||
|
// remove existing video if overwriting
|
||||||
|
db.get(`files.${type}`)
|
||||||
|
.remove({
|
||||||
|
path: file_object['path']
|
||||||
|
}).write();
|
||||||
|
|
||||||
|
db.get(`files.${type}`)
|
||||||
|
.push(file_object)
|
||||||
|
.write();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sub_db = null;
|
||||||
|
if (multiUserMode) {
|
||||||
|
const user_uid = multiUserMode.user;
|
||||||
|
sub_db = users_db.get('users').find({uid: user_uid}).get('subscriptions').find({id: sub.id});
|
||||||
|
} else {
|
||||||
|
sub_db = db.get('subscriptions').find({id: sub.id});
|
||||||
|
}
|
||||||
|
sub_db.get('videos').push(file_object).write();
|
||||||
|
}
|
||||||
|
|
||||||
|
return file_object['uid'];
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateFileObject(id, type, customPath = null, sub = null) {
|
||||||
|
if (!customPath && sub) {
|
||||||
|
customPath = getAppendedBasePathSub(sub, config_api.getConfigItem('ytdl_subscriptions_base_path'));
|
||||||
|
}
|
||||||
|
var jsonobj = (type === 'audio') ? utils.getJSONMp3(id, customPath, true) : utils.getJSONMp4(id, customPath, true);
|
||||||
|
if (!jsonobj) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const ext = (type === 'audio') ? '.mp3' : '.mp4'
|
||||||
|
const file_path = utils.getTrueFileName(jsonobj['_filename'], type); // path.join(type === 'audio' ? audioFolderPath : videoFolderPath, id + ext);
|
||||||
|
// console.
|
||||||
|
var stats = fs.statSync(path.join(__dirname, file_path));
|
||||||
|
|
||||||
|
var title = jsonobj.title;
|
||||||
|
var url = jsonobj.webpage_url;
|
||||||
|
var uploader = jsonobj.uploader;
|
||||||
|
var upload_date = jsonobj.upload_date;
|
||||||
|
upload_date = upload_date ? `${upload_date.substring(0, 4)}-${upload_date.substring(4, 6)}-${upload_date.substring(6, 8)}` : 'N/A';
|
||||||
|
|
||||||
|
var size = stats.size;
|
||||||
|
|
||||||
|
var thumbnail = jsonobj.thumbnail;
|
||||||
|
var duration = jsonobj.duration;
|
||||||
|
var isaudio = type === 'audio';
|
||||||
|
var file_obj = new utils.File(id, title, thumbnail, isaudio, duration, url, uploader, size, file_path, upload_date);
|
||||||
|
return file_obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAppendedBasePathSub(sub, base_path) {
|
||||||
|
return path.join(base_path, (sub.isPlaylist ? 'playlists/' : 'channels/'), sub.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
initialize: initialize,
|
||||||
|
registerFileDB: registerFileDB
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
1
backend/public/1-es5.58a075d60532093bfbbf.js
Normal file
1
backend/public/1-es5.58a075d60532093bfbbf.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -14,5 +14,5 @@
|
|||||||
<link rel="stylesheet" href="styles.5112d6db78cf21541598.css"></head>
|
<link rel="stylesheet" href="styles.5112d6db78cf21541598.css"></head>
|
||||||
<body>
|
<body>
|
||||||
<app-root></app-root>
|
<app-root></app-root>
|
||||||
<script src="runtime-es2015.c1a751ca64bf9326c132.js" type="module"></script><script src="runtime-es5.c1a751ca64bf9326c132.js" nomodule defer></script><script src="polyfills-es5.7f923c8f5afda210edd3.js" nomodule defer></script><script src="polyfills-es2015.5b408f108bcea938a7e2.js" type="module"></script><script src="main-es2015.0cbc545a4a3bee376826.js" type="module"></script><script src="main-es5.0cbc545a4a3bee376826.js" nomodule defer></script></body>
|
<script src="runtime-es2015.d59790a263b5bfaf4a25.js" type="module"></script><script src="runtime-es5.d59790a263b5bfaf4a25.js" nomodule defer></script><script src="polyfills-es5.7f923c8f5afda210edd3.js" nomodule defer></script><script src="polyfills-es2015.5b408f108bcea938a7e2.js" type="module"></script><script src="main-es2015.0cbc545a4a3bee376826.js" type="module"></script><script src="main-es5.0cbc545a4a3bee376826.js" nomodule defer></script></body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
!function(e){function r(r){for(var n,a,i=r[0],c=r[1],l=r[2],p=0,s=[];p<i.length;p++)a=i[p],Object.prototype.hasOwnProperty.call(o,a)&&o[a]&&s.push(o[a][0]),o[a]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(r);s.length;)s.shift()();return u.push.apply(u,l||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++)0!==o[t[i]]&&(n=!1);n&&(u.splice(r--,1),e=a(a.s=t[0]))}return e}var n={},o={0:0},u=[];function a(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,i=document.createElement("script");i.charset="utf-8",i.timeout=120,a.nc&&i.setAttribute("nonce",a.nc),i.src=function(e){return a.p+""+({}[e]||e)+"-es2015."+{1:"a4914bd653aa99fdc1ed"}[e]+".js"}(e);var c=new Error;u=function(r){i.onerror=i.onload=null,clearTimeout(l);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:i})}),12e4);i.onerror=i.onload=u,document.head.appendChild(i)}return Promise.all(r)},a.m=e,a.c=n,a.d=function(e,r,t){a.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,r){if(1&r&&(e=a(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)a.d(t,n,(function(r){return e[r]}).bind(null,n));return t},a.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(r,"a",r),r},a.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},a.p="",a.oe=function(e){throw console.error(e),e};var i=window.webpackJsonp=window.webpackJsonp||[],c=i.push.bind(i);i.push=r,i=i.slice();for(var l=0;l<i.length;l++)r(i[l]);var f=c;t()}([]);
|
|
||||||
1
backend/public/runtime-es2015.d59790a263b5bfaf4a25.js
Normal file
1
backend/public/runtime-es2015.d59790a263b5bfaf4a25.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
!function(e){function r(r){for(var n,i,a=r[0],c=r[1],l=r[2],p=0,s=[];p<a.length;p++)i=a[p],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&s.push(o[i][0]),o[i]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(r);s.length;)s.shift()();return u.push.apply(u,l||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,a=1;a<t.length;a++)0!==o[t[a]]&&(n=!1);n&&(u.splice(r--,1),e=i(i.s=t[0]))}return e}var n={},o={0:0},u=[];function i(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,i),t.l=!0,t.exports}i.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,a=document.createElement("script");a.charset="utf-8",a.timeout=120,i.nc&&a.setAttribute("nonce",i.nc),a.src=function(e){return i.p+""+({}[e]||e)+"-es2015."+{1:"58a075d60532093bfbbf"}[e]+".js"}(e);var c=new Error;u=function(r){a.onerror=a.onload=null,clearTimeout(l);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:a})}),12e4);a.onerror=a.onload=u,document.head.appendChild(a)}return Promise.all(r)},i.m=e,i.c=n,i.d=function(e,r,t){i.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,r){if(1&r&&(e=i(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(i.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)i.d(t,n,(function(r){return e[r]}).bind(null,n));return t},i.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(r,"a",r),r},i.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},i.p="",i.oe=function(e){throw console.error(e),e};var a=window.webpackJsonp=window.webpackJsonp||[],c=a.push.bind(a);a.push=r,a=a.slice();for(var l=0;l<a.length;l++)r(a[l]);var f=c;t()}([]);
|
||||||
@@ -1 +0,0 @@
|
|||||||
!function(e){function r(r){for(var n,a,i=r[0],c=r[1],l=r[2],p=0,s=[];p<i.length;p++)a=i[p],Object.prototype.hasOwnProperty.call(o,a)&&o[a]&&s.push(o[a][0]),o[a]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(r);s.length;)s.shift()();return u.push.apply(u,l||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++)0!==o[t[i]]&&(n=!1);n&&(u.splice(r--,1),e=a(a.s=t[0]))}return e}var n={},o={0:0},u=[];function a(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,i=document.createElement("script");i.charset="utf-8",i.timeout=120,a.nc&&i.setAttribute("nonce",a.nc),i.src=function(e){return a.p+""+({}[e]||e)+"-es5."+{1:"a4914bd653aa99fdc1ed"}[e]+".js"}(e);var c=new Error;u=function(r){i.onerror=i.onload=null,clearTimeout(l);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:i})}),12e4);i.onerror=i.onload=u,document.head.appendChild(i)}return Promise.all(r)},a.m=e,a.c=n,a.d=function(e,r,t){a.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,r){if(1&r&&(e=a(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)a.d(t,n,(function(r){return e[r]}).bind(null,n));return t},a.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(r,"a",r),r},a.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},a.p="",a.oe=function(e){throw console.error(e),e};var i=window.webpackJsonp=window.webpackJsonp||[],c=i.push.bind(i);i.push=r,i=i.slice();for(var l=0;l<i.length;l++)r(i[l]);var f=c;t()}([]);
|
|
||||||
1
backend/public/runtime-es5.d59790a263b5bfaf4a25.js
Normal file
1
backend/public/runtime-es5.d59790a263b5bfaf4a25.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
!function(e){function r(r){for(var n,i,a=r[0],c=r[1],l=r[2],p=0,s=[];p<a.length;p++)i=a[p],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&s.push(o[i][0]),o[i]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(r);s.length;)s.shift()();return u.push.apply(u,l||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,a=1;a<t.length;a++)0!==o[t[a]]&&(n=!1);n&&(u.splice(r--,1),e=i(i.s=t[0]))}return e}var n={},o={0:0},u=[];function i(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,i),t.l=!0,t.exports}i.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,a=document.createElement("script");a.charset="utf-8",a.timeout=120,i.nc&&a.setAttribute("nonce",i.nc),a.src=function(e){return i.p+""+({}[e]||e)+"-es5."+{1:"58a075d60532093bfbbf"}[e]+".js"}(e);var c=new Error;u=function(r){a.onerror=a.onload=null,clearTimeout(l);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:a})}),12e4);a.onerror=a.onload=u,document.head.appendChild(a)}return Promise.all(r)},i.m=e,i.c=n,i.d=function(e,r,t){i.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,r){if(1&r&&(e=i(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(i.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)i.d(t,n,(function(r){return e[r]}).bind(null,n));return t},i.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(r,"a",r),r},i.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},i.p="",i.oe=function(e){throw console.error(e),e};var a=window.webpackJsonp=window.webpackJsonp||[],c=a.push.bind(a);a.push=r,a=a.slice();for(var l=0;l<a.length;l++)r(a[l]);var f=c;t()}([]);
|
||||||
@@ -6,17 +6,20 @@ var path = require('path');
|
|||||||
|
|
||||||
var youtubedl = require('youtube-dl');
|
var youtubedl = require('youtube-dl');
|
||||||
const config_api = require('./config');
|
const config_api = require('./config');
|
||||||
|
var utils = require('./utils')
|
||||||
|
|
||||||
const debugMode = process.env.YTDL_MODE === 'debug';
|
const debugMode = process.env.YTDL_MODE === 'debug';
|
||||||
|
|
||||||
var logger = null;
|
var logger = null;
|
||||||
var db = null;
|
var db = null;
|
||||||
var users_db = null;
|
var users_db = null;
|
||||||
function setDB(input_db, input_users_db) { db = input_db; users_db = input_users_db }
|
var db_api = null;
|
||||||
|
|
||||||
|
function setDB(input_db, input_users_db, input_db_api) { db = input_db; users_db = input_users_db; db_api = input_db_api }
|
||||||
function setLogger(input_logger) { logger = input_logger; }
|
function setLogger(input_logger) { logger = input_logger; }
|
||||||
|
|
||||||
function initialize(input_db, input_users_db, input_logger) {
|
function initialize(input_db, input_users_db, input_logger, input_db_api) {
|
||||||
setDB(input_db, input_users_db);
|
setDB(input_db, input_users_db, input_db_api);
|
||||||
setLogger(input_logger);
|
setLogger(input_logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,6 +31,7 @@ async function subscribe(sub, user_uid = null) {
|
|||||||
return new Promise(async resolve => {
|
return new Promise(async resolve => {
|
||||||
// sub should just have url and name. here we will get isPlaylist and path
|
// sub should just have url and name. here we will get isPlaylist and path
|
||||||
sub.isPlaylist = sub.url.includes('playlist');
|
sub.isPlaylist = sub.url.includes('playlist');
|
||||||
|
sub.videos = [];
|
||||||
|
|
||||||
let url_exists = false;
|
let url_exists = false;
|
||||||
|
|
||||||
@@ -44,15 +48,25 @@ async function subscribe(sub, user_uid = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add sub to db
|
// add sub to db
|
||||||
if (user_uid)
|
let sub_db = null;
|
||||||
|
if (user_uid) {
|
||||||
users_db.get('users').find({uid: user_uid}).get('subscriptions').push(sub).write();
|
users_db.get('users').find({uid: user_uid}).get('subscriptions').push(sub).write();
|
||||||
else
|
sub_db = users_db.get('users').find({uid: user_uid}).get('subscriptions').find({id: sub.id});
|
||||||
|
} else {
|
||||||
db.get('subscriptions').push(sub).write();
|
db.get('subscriptions').push(sub).write();
|
||||||
|
sub_db = db.get('subscriptions').find({id: sub.id});
|
||||||
|
}
|
||||||
let success = await getSubscriptionInfo(sub, user_uid);
|
let success = await getSubscriptionInfo(sub, user_uid);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
sub = sub_db.value();
|
||||||
|
getVideosForSub(sub, user_uid);
|
||||||
|
} else {
|
||||||
|
logger.error('Subscribe: Failed to get subscription info. Subscribe failed.')
|
||||||
|
};
|
||||||
|
|
||||||
result_obj.success = success;
|
result_obj.success = success;
|
||||||
result_obj.sub = sub;
|
result_obj.sub = sub;
|
||||||
getVideosForSub(sub, user_uid);
|
|
||||||
resolve(result_obj);
|
resolve(result_obj);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -160,20 +174,26 @@ async function unsubscribe(sub, deleteMode, user_uid = null) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteSubscriptionFile(sub, file, deleteForever, user_uid = null) {
|
async function deleteSubscriptionFile(sub, file, deleteForever, file_uid = null, user_uid = null) {
|
||||||
let basePath = null;
|
let basePath = null;
|
||||||
if (user_uid)
|
let sub_db = null;
|
||||||
|
if (user_uid) {
|
||||||
basePath = path.join(config_api.getConfigItem('ytdl_users_base_path'), user_uid, 'subscriptions');
|
basePath = path.join(config_api.getConfigItem('ytdl_users_base_path'), user_uid, 'subscriptions');
|
||||||
else
|
sub_db = users_db.get('users').find({uid: user_uid}).get('subscriptions').find({id: sub.id});
|
||||||
|
} else {
|
||||||
basePath = config_api.getConfigItem('ytdl_subscriptions_base_path');
|
basePath = config_api.getConfigItem('ytdl_subscriptions_base_path');
|
||||||
|
sub_db = db.get('subscriptions').find({id: sub.id});
|
||||||
|
}
|
||||||
const useArchive = config_api.getConfigItem('ytdl_subscriptions_use_youtubedl_archive');
|
const useArchive = config_api.getConfigItem('ytdl_subscriptions_use_youtubedl_archive');
|
||||||
const appendedBasePath = getAppendedBasePath(sub, basePath);
|
const appendedBasePath = getAppendedBasePath(sub, basePath);
|
||||||
const name = file;
|
const name = file;
|
||||||
let retrievedID = null;
|
let retrievedID = null;
|
||||||
|
sub_db.get('videos').remove({uid: file_uid}).write();
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
let filePath = appendedBasePath;
|
let filePath = appendedBasePath;
|
||||||
|
const ext = (sub.type && sub.type === 'audio') ? '.mp3' : '.mp4'
|
||||||
var jsonPath = path.join(__dirname,filePath,name+'.info.json');
|
var jsonPath = path.join(__dirname,filePath,name+'.info.json');
|
||||||
var videoFilePath = path.join(__dirname,filePath,name+'.mp4');
|
var videoFilePath = path.join(__dirname,filePath,name+ext);
|
||||||
var imageFilePath = path.join(__dirname,filePath,name+'.jpg');
|
var imageFilePath = path.join(__dirname,filePath,name+'.jpg');
|
||||||
|
|
||||||
jsonExists = fs.existsSync(jsonPath);
|
jsonExists = fs.existsSync(jsonPath);
|
||||||
@@ -237,13 +257,45 @@ async function getVideosForSub(sub, user_uid = null) {
|
|||||||
const useArchive = config_api.getConfigItem('ytdl_subscriptions_use_youtubedl_archive');
|
const useArchive = config_api.getConfigItem('ytdl_subscriptions_use_youtubedl_archive');
|
||||||
|
|
||||||
let appendedBasePath = null
|
let appendedBasePath = null
|
||||||
if (sub.name) {
|
appendedBasePath = getAppendedBasePath(sub, basePath);
|
||||||
appendedBasePath = getAppendedBasePath(sub, basePath);
|
|
||||||
} else {
|
let multiUserMode = null;
|
||||||
appendedBasePath = path.join(basePath, (sub.isPlaylist ? 'playlists/%(playlist_title)s' : 'channels/%(uploader)s'));
|
if (user_uid) {
|
||||||
|
multiUserMode = {
|
||||||
|
user: user_uid,
|
||||||
|
file_path: appendedBasePath
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let downloadConfig = ['-o', appendedBasePath + '/%(title)s.mp4', '-f', 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/mp4', '-ciw', '--write-info-json', '--print-json'];
|
const ext = (sub.type && sub.type === 'audio') ? '.mp3' : '.mp4'
|
||||||
|
|
||||||
|
let fullOutput = appendedBasePath + '/%(title)s' + ext;
|
||||||
|
if (sub.custom_output) {
|
||||||
|
fullOutput = appendedBasePath + '/' + sub.custom_output + ext;
|
||||||
|
}
|
||||||
|
|
||||||
|
let downloadConfig = ['-o', fullOutput, '-ciw', '--write-info-json', '--print-json'];
|
||||||
|
|
||||||
|
let qualityPath = null;
|
||||||
|
if (sub.type && sub.type === 'audio') {
|
||||||
|
qualityPath = ['-f', 'bestaudio']
|
||||||
|
qualityPath.push('-x');
|
||||||
|
qualityPath.push('--audio-format', 'mp3');
|
||||||
|
} else {
|
||||||
|
qualityPath = ['-f', 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/mp4']
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadConfig.push(...qualityPath)
|
||||||
|
|
||||||
|
if (sub.custom_args) {
|
||||||
|
customArgsArray = sub.custom_args.split(',,');
|
||||||
|
if (customArgsArray.indexOf('-f') !== -1) {
|
||||||
|
// if custom args has a custom quality, replce the original quality with that of custom args
|
||||||
|
const original_output_index = downloadConfig.indexOf('-f');
|
||||||
|
downloadConfig.splice(original_output_index, 2);
|
||||||
|
}
|
||||||
|
downloadConfig.push(...customArgsArray);
|
||||||
|
}
|
||||||
|
|
||||||
let archive_dir = null;
|
let archive_dir = null;
|
||||||
let archive_path = null;
|
let archive_path = null;
|
||||||
@@ -286,7 +338,7 @@ async function getVideosForSub(sub, user_uid = null) {
|
|||||||
const outputs = err.stdout.split(/\r\n|\r|\n/);
|
const outputs = err.stdout.split(/\r\n|\r|\n/);
|
||||||
for (let i = 0; i < outputs.length; i++) {
|
for (let i = 0; i < outputs.length; i++) {
|
||||||
const output = JSON.parse(outputs[i]);
|
const output = JSON.parse(outputs[i]);
|
||||||
handleOutputJSON(sub, sub_db, output, i === 0)
|
handleOutputJSON(sub, sub_db, output, i === 0, multiUserMode)
|
||||||
if (err.stderr.includes(output['id']) && archive_path) {
|
if (err.stderr.includes(output['id']) && archive_path) {
|
||||||
// we found a video that errored! add it to the archive to prevent future errors
|
// we found a video that errored! add it to the archive to prevent future errors
|
||||||
fs.appendFileSync(archive_path, output['id']);
|
fs.appendFileSync(archive_path, output['id']);
|
||||||
@@ -315,7 +367,7 @@ async function getVideosForSub(sub, user_uid = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const reset_videos = i === 0;
|
const reset_videos = i === 0;
|
||||||
handleOutputJSON(sub, sub_db, output_json, reset_videos);
|
handleOutputJSON(sub, sub_db, output_json, multiUserMode, reset_videos);
|
||||||
|
|
||||||
// TODO: Potentially store downloaded files in db?
|
// TODO: Potentially store downloaded files in db?
|
||||||
|
|
||||||
@@ -323,10 +375,12 @@ async function getVideosForSub(sub, user_uid = null) {
|
|||||||
resolve(true);
|
resolve(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}, err => {
|
||||||
|
logger.error(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleOutputJSON(sub, sub_db, output_json, reset_videos = false) {
|
function handleOutputJSON(sub, sub_db, output_json, multiUserMode = null, reset_videos = false) {
|
||||||
if (sub.streamingOnly) {
|
if (sub.streamingOnly) {
|
||||||
if (reset_videos) {
|
if (reset_videos) {
|
||||||
sub_db.assign({videos: []}).write();
|
sub_db.assign({videos: []}).write();
|
||||||
@@ -337,6 +391,9 @@ function handleOutputJSON(sub, sub_db, output_json, reset_videos = false) {
|
|||||||
|
|
||||||
// add to db
|
// add to db
|
||||||
sub_db.get('videos').push(output_json).write();
|
sub_db.get('videos').push(output_json).write();
|
||||||
|
} else {
|
||||||
|
// TODO: make multiUserMode obj
|
||||||
|
db_api.registerFileDB(path.basename(output_json['_filename']), sub.type, multiUserMode, sub);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
78
backend/utils.js
Normal file
78
backend/utils.js
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
var fs = require('fs-extra')
|
||||||
|
var path = require('path')
|
||||||
|
const config_api = require('./config');
|
||||||
|
|
||||||
|
const is_windows = process.platform === 'win32';
|
||||||
|
|
||||||
|
function getTrueFileName(unfixed_path, type) {
|
||||||
|
let fixed_path = unfixed_path;
|
||||||
|
|
||||||
|
const new_ext = (type === 'audio' ? 'mp3' : 'mp4');
|
||||||
|
let unfixed_parts = unfixed_path.split('.');
|
||||||
|
const old_ext = unfixed_parts[unfixed_parts.length-1];
|
||||||
|
|
||||||
|
|
||||||
|
if (old_ext !== new_ext) {
|
||||||
|
unfixed_parts[unfixed_parts.length-1] = new_ext;
|
||||||
|
fixed_path = unfixed_parts.join('.');
|
||||||
|
}
|
||||||
|
return fixed_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getJSONMp4(name, customPath, openReadPerms = false) {
|
||||||
|
var obj = null; // output
|
||||||
|
if (!customPath) customPath = config_api.getConfigItem('ytdl_video_folder_path');
|
||||||
|
var jsonPath = path.join(customPath, name + ".info.json");
|
||||||
|
var alternateJsonPath = path.join(customPath, name + ".mp4.info.json");
|
||||||
|
if (fs.existsSync(jsonPath))
|
||||||
|
{
|
||||||
|
obj = JSON.parse(fs.readFileSync(jsonPath, 'utf8'));
|
||||||
|
if (openReadPerms) fs.chmodSync(jsonPath, 0o644);
|
||||||
|
} else if (fs.existsSync(alternateJsonPath)) {
|
||||||
|
obj = JSON.parse(fs.readFileSync(alternateJsonPath, 'utf8'));
|
||||||
|
if (openReadPerms) fs.chmodSync(alternateJsonPath, 0o644);
|
||||||
|
}
|
||||||
|
else obj = 0;
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getJSONMp3(name, customPath, openReadPerms = false) {
|
||||||
|
var obj = null;
|
||||||
|
if (!customPath) customPath = config_api.getConfigItem('ytdl_audio_folder_path');
|
||||||
|
var jsonPath = path.join(customPath, name + ".info.json");
|
||||||
|
var alternateJsonPath = path.join(customPath, name + ".mp3.info.json");
|
||||||
|
if (fs.existsSync(jsonPath)) {
|
||||||
|
obj = JSON.parse(fs.readFileSync(jsonPath, 'utf8'));
|
||||||
|
if (!is_windows && openReadPerms) fs.chmodSync(jsonPath, 0o755);
|
||||||
|
}
|
||||||
|
else if (fs.existsSync(alternateJsonPath)) {
|
||||||
|
obj = JSON.parse(fs.readFileSync(alternateJsonPath, 'utf8'));
|
||||||
|
if (!is_windows && openReadPerms) fs.chmodSync(alternateJsonPath, 0o755);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
obj = 0;
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
// objects
|
||||||
|
|
||||||
|
function File(id, title, thumbnailURL, isAudio, duration, url, uploader, size, path, upload_date) {
|
||||||
|
this.id = id;
|
||||||
|
this.title = title;
|
||||||
|
this.thumbnailURL = thumbnailURL;
|
||||||
|
this.isAudio = isAudio;
|
||||||
|
this.duration = duration;
|
||||||
|
this.url = url;
|
||||||
|
this.uploader = uploader;
|
||||||
|
this.size = size;
|
||||||
|
this.path = path;
|
||||||
|
this.upload_date = upload_date;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getJSONMp3: getJSONMp3,
|
||||||
|
getJSONMp4: getJSONMp4,
|
||||||
|
getTrueFileName: getTrueFileName,
|
||||||
|
File: File
|
||||||
|
}
|
||||||
@@ -3,16 +3,20 @@
|
|||||||
<mat-dialog-content>
|
<mat-dialog-content>
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12 mb-4">
|
||||||
<mat-form-field color="accent">
|
<mat-form-field color="accent">
|
||||||
<input [(ngModel)]="url" matInput placeholder="URL" i18n-placeholder="Subscription URL input placeholder" required aria-required="true">
|
<input [(ngModel)]="url" matInput placeholder="URL" i18n-placeholder="Subscription URL input placeholder" required aria-required="true">
|
||||||
<mat-hint><ng-container i18n="Subscription URL input hint">The playlist or channel URL</ng-container></mat-hint>
|
<mat-hint><ng-container i18n="Subscription URL input hint">The playlist or channel URL</ng-container></mat-hint>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<mat-form-field color="accent">
|
<mat-form-field color="accent">
|
||||||
<input [(ngModel)]="name" matInput placeholder="Custom name" i18n-placeholder="Subscription custom name placeholder">
|
<input [(ngModel)]="name" matInput placeholder="Custom name" i18n-placeholder="Subscription custom name placeholder">
|
||||||
<mat-hint><ng-container i18n="Custom name input hint">This is optional</ng-container></mat-hint>
|
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 mt-3">
|
<div class="col-12 mt-3">
|
||||||
@@ -31,9 +35,33 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div>
|
<div>
|
||||||
<mat-checkbox [(ngModel)]="streamingOnlyMode"><ng-container i18n="Streaming-only mode">Streaming-only mode</ng-container></mat-checkbox>
|
<mat-checkbox [(ngModel)]="audioOnlyMode"><ng-container i18n="Streaming-only mode">Audio-only mode</ng-container></mat-checkbox>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<div>
|
||||||
|
<mat-checkbox [disabled]="audioOnlyMode" [(ngModel)]="streamingOnlyMode"><ng-container i18n="Streaming-only mode">Streaming-only mode</ng-container></mat-checkbox>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 mb-3">
|
||||||
|
<mat-form-field color="accent">
|
||||||
|
<input [(ngModel)]="customArgs" matInput placeholder="Custom args" i18n-placeholder="Subscription custom args placeholder">
|
||||||
|
<button class="args-edit-button" (click)="openArgsModifierDialog()" mat-icon-button><mat-icon>edit</mat-icon></button>
|
||||||
|
<mat-hint>
|
||||||
|
<ng-container i18n="Custom args hint">These are added after the standard args.</ng-container>
|
||||||
|
</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<mat-form-field color="accent">
|
||||||
|
<input [(ngModel)]="customFileOutput" matInput placeholder="Custom file output" i18n-placeholder="Subscription custom file output placeholder">
|
||||||
|
<mat-hint>
|
||||||
|
<a target="_blank" href="https://github.com/ytdl-org/youtube-dl/blob/master/README.md#output-template">
|
||||||
|
<ng-container i18n="Custom output template documentation link">Documentation</ng-container></a>.
|
||||||
|
<ng-container i18n="Custom Output input hint">Path is relative to the config download path. Don't include extension.</ng-container>
|
||||||
|
</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
|
|||||||
@@ -6,3 +6,8 @@
|
|||||||
.mat-spinner {
|
.mat-spinner {
|
||||||
margin-left: 5%;
|
margin-left: 5%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.args-edit-button {
|
||||||
|
position: absolute;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { MatDialogRef } from '@angular/material/dialog';
|
import { MatDialogRef, MatDialog } from '@angular/material/dialog';
|
||||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||||
import { PostsService } from 'app/posts.services';
|
import { PostsService } from 'app/posts.services';
|
||||||
|
import { ArgModifierDialogComponent } from '../arg-modifier-dialog/arg-modifier-dialog.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-subscribe-dialog',
|
selector: 'app-subscribe-dialog',
|
||||||
@@ -22,6 +23,12 @@ export class SubscribeDialogComponent implements OnInit {
|
|||||||
// no videos actually downloaded, just streamed
|
// no videos actually downloaded, just streamed
|
||||||
streamingOnlyMode = false;
|
streamingOnlyMode = false;
|
||||||
|
|
||||||
|
// audio only mode
|
||||||
|
audioOnlyMode = false;
|
||||||
|
|
||||||
|
customFileOutput = '';
|
||||||
|
customArgs = '';
|
||||||
|
|
||||||
time_units = [
|
time_units = [
|
||||||
'day',
|
'day',
|
||||||
'week',
|
'week',
|
||||||
@@ -31,6 +38,7 @@ export class SubscribeDialogComponent implements OnInit {
|
|||||||
|
|
||||||
constructor(private postsService: PostsService,
|
constructor(private postsService: PostsService,
|
||||||
private snackBar: MatSnackBar,
|
private snackBar: MatSnackBar,
|
||||||
|
private dialog: MatDialog,
|
||||||
public dialogRef: MatDialogRef<SubscribeDialogComponent>) { }
|
public dialogRef: MatDialogRef<SubscribeDialogComponent>) { }
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
@@ -49,7 +57,8 @@ export class SubscribeDialogComponent implements OnInit {
|
|||||||
if (!this.download_all) {
|
if (!this.download_all) {
|
||||||
timerange = 'now-' + this.timerange_amount.toString() + this.timerange_unit;
|
timerange = 'now-' + this.timerange_amount.toString() + this.timerange_unit;
|
||||||
}
|
}
|
||||||
this.postsService.createSubscription(this.url, this.name, timerange, this.streamingOnlyMode).subscribe(res => {
|
this.postsService.createSubscription(this.url, this.name, timerange, this.streamingOnlyMode,
|
||||||
|
this.audioOnlyMode, this.customArgs, this.customFileOutput).subscribe(res => {
|
||||||
this.subscribing = false;
|
this.subscribing = false;
|
||||||
if (res['new_sub']) {
|
if (res['new_sub']) {
|
||||||
this.dialogRef.close(res['new_sub']);
|
this.dialogRef.close(res['new_sub']);
|
||||||
@@ -63,6 +72,20 @@ export class SubscribeDialogComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// modify custom args
|
||||||
|
openArgsModifierDialog() {
|
||||||
|
const dialogRef = this.dialog.open(ArgModifierDialogComponent, {
|
||||||
|
data: {
|
||||||
|
initial_args: this.customArgs
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dialogRef.afterClosed().subscribe(new_args => {
|
||||||
|
if (new_args !== null && new_args !== undefined) {
|
||||||
|
this.customArgs = new_args;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public openSnackBar(message: string, action = '') {
|
public openSnackBar(message: string, action = '') {
|
||||||
this.snackBar.open(message, action, {
|
this.snackBar.open(message, action, {
|
||||||
duration: 2000,
|
duration: 2000,
|
||||||
|
|||||||
@@ -439,10 +439,11 @@ export class MainComponent implements OnInit {
|
|||||||
|
|
||||||
public removeFromMp3(name: string) {
|
public removeFromMp3(name: string) {
|
||||||
for (let i = 0; i < this.mp3s.length; i++) {
|
for (let i = 0; i < this.mp3s.length; i++) {
|
||||||
if (this.mp3s[i].id === name) {
|
if (this.mp3s[i].id === name || this.mp3s[i].id + '.mp3' === name) {
|
||||||
this.mp3s.splice(i, 1);
|
this.mp3s.splice(i, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.getMp3s();
|
||||||
}
|
}
|
||||||
|
|
||||||
public removePlaylistMp3(playlistID, index) {
|
public removePlaylistMp3(playlistID, index) {
|
||||||
@@ -457,10 +458,11 @@ export class MainComponent implements OnInit {
|
|||||||
|
|
||||||
public removeFromMp4(name: string) {
|
public removeFromMp4(name: string) {
|
||||||
for (let i = 0; i < this.mp4s.length; i++) {
|
for (let i = 0; i < this.mp4s.length; i++) {
|
||||||
if (this.mp4s[i].id === name) {
|
if (this.mp4s[i].id === name || this.mp4s[i].id + '.mp4' === name) {
|
||||||
this.mp4s.splice(i, 1);
|
this.mp4s.splice(i, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.getMp4s();
|
||||||
}
|
}
|
||||||
|
|
||||||
public removePlaylistMp4(playlistID, index) {
|
public removePlaylistMp4(playlistID, index) {
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ export class PlayerComponent implements OnInit {
|
|||||||
this.currentItem = this.playlist[0];
|
this.currentItem = this.playlist[0];
|
||||||
this.currentIndex = 0;
|
this.currentIndex = 0;
|
||||||
this.show_player = true;
|
this.show_player = true;
|
||||||
} else if (this.type === 'subscription' || this.fileNames) {
|
} else if (this.subscriptionName || this.fileNames) {
|
||||||
this.show_player = true;
|
this.show_player = true;
|
||||||
this.parseFileNames();
|
this.parseFileNames();
|
||||||
}
|
}
|
||||||
@@ -181,9 +181,6 @@ export class PlayerComponent implements OnInit {
|
|||||||
fileType = 'audio/mp3';
|
fileType = 'audio/mp3';
|
||||||
} else if (this.type === 'video') {
|
} else if (this.type === 'video') {
|
||||||
fileType = 'video/mp4';
|
fileType = 'video/mp4';
|
||||||
} else if (this.type === 'subscription') {
|
|
||||||
// only supports mp4 for now
|
|
||||||
fileType = 'video/mp4';
|
|
||||||
} else {
|
} else {
|
||||||
// error
|
// error
|
||||||
console.error('Must have valid file type! Use \'audio\', \'video\', or \'subscription\'.');
|
console.error('Must have valid file type! Use \'audio\', \'video\', or \'subscription\'.');
|
||||||
@@ -198,7 +195,7 @@ export class PlayerComponent implements OnInit {
|
|||||||
fullLocation = this.baseStreamPath + baseLocation + encodeURIComponent(fileName);
|
fullLocation = this.baseStreamPath + baseLocation + encodeURIComponent(fileName);
|
||||||
} else {
|
} else {
|
||||||
// default to video but include subscription name param
|
// default to video but include subscription name param
|
||||||
baseLocation = 'video/';
|
baseLocation = this.type === 'audio' ? 'audio/' : 'video/';
|
||||||
fullLocation = this.baseStreamPath + baseLocation + encodeURIComponent(fileName) + '?subName=' + this.subscriptionName +
|
fullLocation = this.baseStreamPath + baseLocation + encodeURIComponent(fileName) + '?subName=' + this.subscriptionName +
|
||||||
'&subPlaylist=' + this.subPlaylist;
|
'&subPlaylist=' + this.subPlaylist;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -280,17 +280,18 @@ export class PostsService implements CanActivate {
|
|||||||
return this.http.post(this.path + 'deletePlaylist', {playlistID: playlistID, type: type}, this.httpOptions);
|
return this.http.post(this.path + 'deletePlaylist', {playlistID: playlistID, type: type}, this.httpOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
createSubscription(url, name, timerange = null, streamingOnly = false) {
|
createSubscription(url, name, timerange = null, streamingOnly = false, audioOnly = false, customArgs = null, customFileOutput = null) {
|
||||||
return this.http.post(this.path + 'subscribe', {url: url, name: name, timerange: timerange, streamingOnly: streamingOnly},
|
return this.http.post(this.path + 'subscribe', {url: url, name: name, timerange: timerange, streamingOnly: streamingOnly,
|
||||||
this.httpOptions);
|
audioOnly: audioOnly, customArgs: customArgs, customFileOutput: customFileOutput}, this.httpOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsubscribe(sub, deleteMode = false) {
|
unsubscribe(sub, deleteMode = false) {
|
||||||
return this.http.post(this.path + 'unsubscribe', {sub: sub, deleteMode: deleteMode}, this.httpOptions)
|
return this.http.post(this.path + 'unsubscribe', {sub: sub, deleteMode: deleteMode}, this.httpOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteSubscriptionFile(sub, file, deleteForever) {
|
deleteSubscriptionFile(sub, file, deleteForever, file_uid) {
|
||||||
return this.http.post(this.path + 'deleteSubscriptionFile', {sub: sub, file: file, deleteForever: deleteForever}, this.httpOptions)
|
return this.http.post(this.path + 'deleteSubscriptionFile', {sub: sub, file: file, deleteForever: deleteForever,
|
||||||
|
file_uid: file_uid}, this.httpOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
getSubscription(id) {
|
getSubscription(id) {
|
||||||
|
|||||||
@@ -71,14 +71,14 @@ export class SubscriptionFileCardComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
deleteAndRedownload() {
|
deleteAndRedownload() {
|
||||||
this.postsService.deleteSubscriptionFile(this.sub, this.file.id, false).subscribe(res => {
|
this.postsService.deleteSubscriptionFile(this.sub, this.file.id, false, this.file.uid).subscribe(res => {
|
||||||
this.reloadSubscription.emit(true);
|
this.reloadSubscription.emit(true);
|
||||||
this.openSnackBar(`Successfully deleted file: '${this.file.id}'`, 'Dismiss.');
|
this.openSnackBar(`Successfully deleted file: '${this.file.id}'`, 'Dismiss.');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteForever() {
|
deleteForever() {
|
||||||
this.postsService.deleteSubscriptionFile(this.sub, this.file.id, true).subscribe(res => {
|
this.postsService.deleteSubscriptionFile(this.sub, this.file.id, true, this.file.uid).subscribe(res => {
|
||||||
this.reloadSubscription.emit(true);
|
this.reloadSubscription.emit(true);
|
||||||
this.openSnackBar(`Successfully deleted file: '${this.file.id}'`, 'Dismiss.');
|
this.openSnackBar(`Successfully deleted file: '${this.file.id}'`, 'Dismiss.');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -92,8 +92,9 @@ export class SubscriptionComponent implements OnInit {
|
|||||||
if (this.subscription.streamingOnly) {
|
if (this.subscription.streamingOnly) {
|
||||||
this.router.navigate(['/player', {name: name, url: url}]);
|
this.router.navigate(['/player', {name: name, url: url}]);
|
||||||
} else {
|
} else {
|
||||||
this.router.navigate(['/player', {fileNames: name, type: 'subscription', subscriptionName: this.subscription.name,
|
this.router.navigate(['/player', {fileNames: name,
|
||||||
subPlaylist: this.subscription.isPlaylist, uuid: this.postsService.user ? this.postsService.user.uid : null}]);
|
type: this.subscription.type ? this.subscription.type : 'video', subscriptionName: this.subscription.name,
|
||||||
|
subPlaylist: this.subscription.isPlaylist, uuid: this.postsService.user ? this.postsService.user.uid : null}]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user