mirror of
https://github.com/Tzahi12345/YoutubeDL-Material.git
synced 2026-04-05 19:41:28 +03:00
Merge branch 'master' of https://github.com/Tzahi12345/YoutubeDL-Material into streaming-only-mode
This commit is contained in:
464
backend/app.js
464
backend/app.js
@@ -32,6 +32,9 @@ const FileSync = require('lowdb/adapters/FileSync')
|
|||||||
const adapter = new FileSync('./appdata/db.json');
|
const adapter = new FileSync('./appdata/db.json');
|
||||||
const db = low(adapter)
|
const db = low(adapter)
|
||||||
|
|
||||||
|
// check if debug mode
|
||||||
|
let debugMode = process.env.YTDL_MODE === 'debug';
|
||||||
|
|
||||||
// logging setup
|
// logging setup
|
||||||
|
|
||||||
// console format
|
// console format
|
||||||
@@ -49,7 +52,7 @@ const logger = winston.createLogger({
|
|||||||
//
|
//
|
||||||
new winston.transports.File({ filename: 'appdata/logs/error.log', level: 'error' }),
|
new winston.transports.File({ filename: 'appdata/logs/error.log', level: 'error' }),
|
||||||
new winston.transports.File({ filename: 'appdata/logs/combined.log' }),
|
new winston.transports.File({ filename: 'appdata/logs/combined.log' }),
|
||||||
new winston.transports.Console({level: 'info'})
|
new winston.transports.Console({level: !debugMode ? 'info' : 'debug'})
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -65,9 +68,14 @@ db.defaults(
|
|||||||
audio: [],
|
audio: [],
|
||||||
video: []
|
video: []
|
||||||
},
|
},
|
||||||
|
files: {
|
||||||
|
audio: [],
|
||||||
|
video: []
|
||||||
|
},
|
||||||
configWriteFlag: false,
|
configWriteFlag: false,
|
||||||
subscriptions: [],
|
subscriptions: [],
|
||||||
pin_md5: ''
|
pin_md5: '',
|
||||||
|
files_to_db_migration_complete: false
|
||||||
}).write();
|
}).write();
|
||||||
|
|
||||||
// config values
|
// config values
|
||||||
@@ -90,9 +98,6 @@ var options = null; // encryption options
|
|||||||
var url_domain = null;
|
var url_domain = null;
|
||||||
var updaterStatus = null;
|
var updaterStatus = null;
|
||||||
|
|
||||||
// check if debug mode
|
|
||||||
let debugMode = process.env.YTDL_MODE === 'debug';
|
|
||||||
|
|
||||||
if (debugMode) logger.info('YTDL-Material in debug mode!');
|
if (debugMode) logger.info('YTDL-Material in debug mode!');
|
||||||
|
|
||||||
// check if just updated
|
// check if just updated
|
||||||
@@ -154,6 +159,56 @@ function File(id, title, thumbnailURL, isAudio, duration, url, uploader, size, p
|
|||||||
|
|
||||||
// actual functions
|
// actual functions
|
||||||
|
|
||||||
|
async function checkMigrations() {
|
||||||
|
return new Promise(async resolve => {
|
||||||
|
// 3.5->3.6 migration
|
||||||
|
const files_to_db_migration_complete = db.get('files_to_db_migration_complete').value();
|
||||||
|
|
||||||
|
if (!files_to_db_migration_complete) {
|
||||||
|
logger.info('Beginning migration: 3.5->3.6+')
|
||||||
|
runFilesToDBMigration().then(success => {
|
||||||
|
if (success) { logger.info('3.5->3.6+ migration complete!'); }
|
||||||
|
else { logger.error('Migration failed: 3.5->3.6+'); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runFilesToDBMigration() {
|
||||||
|
return new Promise(async resolve => {
|
||||||
|
try {
|
||||||
|
let mp3s = getMp3s();
|
||||||
|
let mp4s = getMp4s();
|
||||||
|
|
||||||
|
for (let i = 0; i < mp3s.length; i++) {
|
||||||
|
let file_obj = mp3s[i];
|
||||||
|
const file_already_in_db = db.get('files.audio').find({id: file_obj.id}).value();
|
||||||
|
if (!file_already_in_db) {
|
||||||
|
logger.verbose(`Migrating file ${file_obj.id}`);
|
||||||
|
registerFileDB(file_obj.id + '.mp3', 'audio');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < mp4s.length; i++) {
|
||||||
|
let file_obj = mp4s[i];
|
||||||
|
const file_already_in_db = db.get('files.video').find({id: file_obj.id}).value();
|
||||||
|
if (!file_already_in_db) {
|
||||||
|
logger.verbose(`Migrating file ${file_obj.id}`);
|
||||||
|
registerFileDB(file_obj.id + '.mp4', 'video');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sets migration to complete
|
||||||
|
db.set('files_to_db_migration_complete', true).write();
|
||||||
|
resolve(true);
|
||||||
|
} catch(err) {
|
||||||
|
resolve(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function startServer() {
|
async function startServer() {
|
||||||
if (process.env.USING_HEROKU && process.env.PORT) {
|
if (process.env.USING_HEROKU && process.env.PORT) {
|
||||||
// default to heroku port if using heroku
|
// default to heroku port if using heroku
|
||||||
@@ -218,7 +273,7 @@ async function updateServer(tag) {
|
|||||||
'details': 'Downloading requested release...'
|
'details': 'Downloading requested release...'
|
||||||
}
|
}
|
||||||
// grab new package.json and public folder
|
// grab new package.json and public folder
|
||||||
// await downloadReleaseFiles(tag);
|
await downloadReleaseFiles(tag);
|
||||||
|
|
||||||
updaterStatus = {
|
updaterStatus = {
|
||||||
updating: true,
|
updating: true,
|
||||||
@@ -258,7 +313,7 @@ async function downloadReleaseFiles(tag) {
|
|||||||
logger.info(`Installing update ${tag}...`)
|
logger.info(`Installing update ${tag}...`)
|
||||||
|
|
||||||
// downloads new package.json and adds new public dir files from the downloaded zip
|
// downloads new package.json and adds new public dir files from the downloaded zip
|
||||||
fs.createReadStream(path.join(__dirname, `youtubedl-material-latest-release-${tag}.zip`)).pipe(unzipper.Parse())
|
fs.createReadStream(path.join(__dirname, `youtubedl-material-release-${tag}.zip`)).pipe(unzipper.Parse())
|
||||||
.on('entry', function (entry) {
|
.on('entry', function (entry) {
|
||||||
var fileName = entry.path;
|
var fileName = entry.path;
|
||||||
var type = entry.type; // 'Directory' or 'File'
|
var type = entry.type; // 'Directory' or 'File'
|
||||||
@@ -276,7 +331,7 @@ async function downloadReleaseFiles(tag) {
|
|||||||
} else if (!is_dir && !replace_ignore_list.includes(fileName)) {
|
} else if (!is_dir && !replace_ignore_list.includes(fileName)) {
|
||||||
// get package.json
|
// get package.json
|
||||||
var actualFileName = fileName.replace('youtubedl-material/', '');
|
var actualFileName = fileName.replace('youtubedl-material/', '');
|
||||||
if (debugMode) logger.verbose('Downloading file ' + actualFileName);
|
logger.verbose('Downloading file ' + actualFileName);
|
||||||
entry.pipe(fs.createWriteStream(path.join(__dirname, actualFileName)));
|
entry.pipe(fs.createWriteStream(path.join(__dirname, actualFileName)));
|
||||||
} else {
|
} else {
|
||||||
entry.autodrain();
|
entry.autodrain();
|
||||||
@@ -343,7 +398,8 @@ async function installDependencies() {
|
|||||||
|
|
||||||
async function backupServerLite() {
|
async function backupServerLite() {
|
||||||
return new Promise(async resolve => {
|
return new Promise(async resolve => {
|
||||||
let output_path = `backup-${Date.now()}.zip`;
|
fs.ensureDirSync(path.join(__dirname, 'appdata', 'backups'));
|
||||||
|
let output_path = path.join('appdata', 'backups', `backup-${Date.now()}.zip`);
|
||||||
logger.info(`Backing up your non-video/audio files to ${output_path}. This may take up to a few seconds/minutes.`);
|
logger.info(`Backing up your non-video/audio files to ${output_path}. This may take up to a few seconds/minutes.`);
|
||||||
let output = fs.createWriteStream(path.join(__dirname, output_path));
|
let output = fs.createWriteStream(path.join(__dirname, output_path));
|
||||||
var archive = archiver('zip', {
|
var archive = archiver('zip', {
|
||||||
@@ -363,7 +419,7 @@ async function backupServerLite() {
|
|||||||
const files_to_ignore = [path.join(config_api.getConfigItem('ytdl_subscriptions_base_path'), '**'),
|
const files_to_ignore = [path.join(config_api.getConfigItem('ytdl_subscriptions_base_path'), '**'),
|
||||||
path.join(config_api.getConfigItem('ytdl_audio_folder_path'), '**'),
|
path.join(config_api.getConfigItem('ytdl_audio_folder_path'), '**'),
|
||||||
path.join(config_api.getConfigItem('ytdl_video_folder_path'), '**'),
|
path.join(config_api.getConfigItem('ytdl_video_folder_path'), '**'),
|
||||||
'backup-*.zip'];
|
'appdata/backups/backup-*.zip'];
|
||||||
|
|
||||||
archive.glob('**/*', {
|
archive.glob('**/*', {
|
||||||
ignore: files_to_ignore
|
ignore: files_to_ignore
|
||||||
@@ -435,7 +491,7 @@ async function setConfigFromEnv() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function loadConfig() {
|
async function loadConfig() {
|
||||||
return new Promise(resolve => {
|
return new Promise(async resolve => {
|
||||||
url = !debugMode ? config_api.getConfigItem('ytdl_url') : 'http://localhost:4200';
|
url = !debugMode ? config_api.getConfigItem('ytdl_url') : 'http://localhost:4200';
|
||||||
backendPort = config_api.getConfigItem('ytdl_port');
|
backendPort = config_api.getConfigItem('ytdl_port');
|
||||||
usingEncryption = config_api.getConfigItem('ytdl_use_encryption');
|
usingEncryption = config_api.getConfigItem('ytdl_use_encryption');
|
||||||
@@ -483,6 +539,9 @@ async function loadConfig() {
|
|||||||
}, subscriptionsCheckInterval * 1000);
|
}, subscriptionsCheckInterval * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check migrations
|
||||||
|
await checkMigrations();
|
||||||
|
|
||||||
// start the server here
|
// start the server here
|
||||||
startServer();
|
startServer();
|
||||||
|
|
||||||
@@ -510,7 +569,7 @@ function watchSubscriptions() {
|
|||||||
let current_delay = 0;
|
let current_delay = 0;
|
||||||
for (let i = 0; i < subscriptions.length; i++) {
|
for (let i = 0; i < subscriptions.length; i++) {
|
||||||
let sub = subscriptions[i];
|
let sub = subscriptions[i];
|
||||||
logger.debug('watching ' + sub.name + ' with delay interval of ' + delay_interval);
|
logger.verbose('Watching ' + sub.name + ' with delay interval of ' + delay_interval);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
subscriptions_api.getVideosForSub(sub);
|
subscriptions_api.getVideosForSub(sub);
|
||||||
}, current_delay);
|
}, current_delay);
|
||||||
@@ -544,6 +603,64 @@ function generateEnvVarConfigItem(key) {
|
|||||||
return {key: key, value: process['env'][key]};
|
return {key: key, value: process['env'][key]};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getMp3s() {
|
||||||
|
let mp3s = [];
|
||||||
|
var files = recFindByExt(audioFolderPath, 'mp3'); // fs.readdirSync(audioFolderPath);
|
||||||
|
for (let i = 0; i < files.length; i++) {
|
||||||
|
let file = files[i];
|
||||||
|
var file_path = file.substring(audioFolderPath.length, file.length);
|
||||||
|
|
||||||
|
var stats = fs.statSync(file);
|
||||||
|
|
||||||
|
var id = file_path.substring(0, file_path.length-4);
|
||||||
|
var jsonobj = getJSONMp3(id);
|
||||||
|
if (!jsonobj) continue;
|
||||||
|
var title = jsonobj.title;
|
||||||
|
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 thumbnail = jsonobj.thumbnail;
|
||||||
|
var duration = jsonobj.duration;
|
||||||
|
var isaudio = true;
|
||||||
|
var file_obj = new File(id, title, thumbnail, isaudio, duration, url, uploader, size, file, upload_date);
|
||||||
|
mp3s.push(file_obj);
|
||||||
|
}
|
||||||
|
return mp3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMp4s(relative_path = true) {
|
||||||
|
let mp4s = [];
|
||||||
|
var files = recFindByExt(videoFolderPath, 'mp4');
|
||||||
|
for (let i = 0; i < files.length; i++) {
|
||||||
|
let file = files[i];
|
||||||
|
var file_path = file.substring(videoFolderPath.length, file.length);
|
||||||
|
|
||||||
|
var stats = fs.statSync(file);
|
||||||
|
|
||||||
|
var id = file_path.substring(0, file_path.length-4);
|
||||||
|
var jsonobj = getJSONMp4(id);
|
||||||
|
if (!jsonobj) continue;
|
||||||
|
var title = jsonobj.title;
|
||||||
|
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 thumbnail = jsonobj.thumbnail;
|
||||||
|
var duration = jsonobj.duration;
|
||||||
|
|
||||||
|
var size = stats.size;
|
||||||
|
|
||||||
|
var isaudio = false;
|
||||||
|
var file_obj = new File(id, title, thumbnail, isaudio, duration, url, uploader, size, file, upload_date);
|
||||||
|
mp4s.push(file_obj);
|
||||||
|
}
|
||||||
|
return mp4s;
|
||||||
|
}
|
||||||
|
|
||||||
function getThumbnailMp3(name)
|
function getThumbnailMp3(name)
|
||||||
{
|
{
|
||||||
var obj = getJSONMp3(name);
|
var obj = getJSONMp3(name);
|
||||||
@@ -856,6 +973,63 @@ function recFindByExt(base,ext,files,result)
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function registerFileDB(full_file_path, type) {
|
||||||
|
const file_id = full_file_path.substring(0, full_file_path.length-4);
|
||||||
|
const file_object = generateFileObject(file_id, type);
|
||||||
|
if (!file_object) {
|
||||||
|
logger.error(`Could not find associated JSON file for ${type} file ${file_id}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_object['uid'] = uuid();
|
||||||
|
path_object = path.parse(file_object['path']);
|
||||||
|
file_object['path'] = path.format(path_object);
|
||||||
|
db.get(`files.${type}`)
|
||||||
|
.push(file_object)
|
||||||
|
.write();
|
||||||
|
return file_object['uid'];
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateFileObject(id, type) {
|
||||||
|
var jsonobj = (type === 'audio') ? getJSONMp3(id) : getJSONMp4(id);
|
||||||
|
if (!jsonobj) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const ext = (type === 'audio') ? '.mp3' : '.mp4'
|
||||||
|
const file_path = getTrueFileName(jsonobj['_filename'], type); // path.join(type === 'audio' ? audioFolderPath : videoFolderPath, id + ext);
|
||||||
|
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.substring(0, 4)}-${upload_date.substring(4, 6)}-${upload_date.substring(6, 8)}`;
|
||||||
|
|
||||||
|
var size = stats.size;
|
||||||
|
|
||||||
|
var thumbnail = jsonobj.thumbnail;
|
||||||
|
var duration = jsonobj.duration;
|
||||||
|
var isaudio = type === 'audio';
|
||||||
|
var file_obj = new File(id, title, thumbnail, isaudio, duration, url, uploader, size, file_path, upload_date);
|
||||||
|
return file_obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
// replaces .webm with appropriate extension
|
||||||
|
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 getAudioInfos(fileNames) {
|
function getAudioInfos(fileNames) {
|
||||||
let result = [];
|
let result = [];
|
||||||
for (let i = 0; i < fileNames.length; i++) {
|
for (let i = 0; i < fileNames.length; i++) {
|
||||||
@@ -1156,6 +1330,7 @@ app.post('/api/tomp3', async function(req, res) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
youtubedl.exec(url, downloadConfig, {}, function(err, output) {
|
youtubedl.exec(url, downloadConfig, {}, function(err, output) {
|
||||||
|
var uid = null;
|
||||||
let new_date = Date.now();
|
let new_date = Date.now();
|
||||||
let difference = (new_date - date)/1000;
|
let difference = (new_date - date)/1000;
|
||||||
logger.debug(`Audio download delay: ${difference} seconds.`);
|
logger.debug(`Audio download delay: ${difference} seconds.`);
|
||||||
@@ -1182,9 +1357,10 @@ app.post('/api/tomp3', async function(req, res) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const filename_no_extension = removeFileExtension(output_json['_filename']);
|
const filepath_no_extension = removeFileExtension(output_json['_filename']);
|
||||||
|
|
||||||
var full_file_path = filename_no_extension + '.mp3';
|
var full_file_path = filepath_no_extension + '.mp3';
|
||||||
|
var file_name = filepath_no_extension.substring(audioFolderPath.length, filepath_no_extension.length);
|
||||||
if (fs.existsSync(full_file_path)) {
|
if (fs.existsSync(full_file_path)) {
|
||||||
let tags = {
|
let tags = {
|
||||||
title: output_json['title'],
|
title: output_json['title'],
|
||||||
@@ -1193,12 +1369,14 @@ app.post('/api/tomp3', async function(req, res) {
|
|||||||
// NodeID3.create(tags, function(frame) { })
|
// NodeID3.create(tags, function(frame) { })
|
||||||
let success = NodeID3.write(tags, full_file_path);
|
let success = NodeID3.write(tags, full_file_path);
|
||||||
if (!success) logger.error('Failed to apply ID3 tag to audio file ' + full_file_path);
|
if (!success) logger.error('Failed to apply ID3 tag to audio file ' + full_file_path);
|
||||||
|
|
||||||
|
// registers file in DB
|
||||||
|
uid = registerFileDB(full_file_path.substring(audioFolderPath.length, full_file_path.length), 'audio');
|
||||||
} else {
|
} else {
|
||||||
logger.info('Output mp3 does not exist');
|
logger.error('Download failed: Output mp3 does not exist');
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_path = filename_no_extension.substring(audioFolderPath.length, filename_no_extension.length);
|
if (file_name) file_names.push(file_name);
|
||||||
if (file_path) file_names.push(file_path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let is_playlist = file_names.length > 1;
|
let is_playlist = file_names.length > 1;
|
||||||
@@ -1214,7 +1392,8 @@ app.post('/api/tomp3', async function(req, res) {
|
|||||||
var audiopathEncoded = encodeURIComponent(file_names[0]);
|
var audiopathEncoded = encodeURIComponent(file_names[0]);
|
||||||
res.send({
|
res.send({
|
||||||
audiopathEncoded: audiopathEncoded,
|
audiopathEncoded: audiopathEncoded,
|
||||||
file_names: is_playlist ? file_names : null
|
file_names: is_playlist ? file_names : null,
|
||||||
|
uid: uid
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1293,6 +1472,7 @@ app.post('/api/tomp4', async function(req, res) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
youtubedl.exec(url, downloadConfig, {}, function(err, output) {
|
youtubedl.exec(url, downloadConfig, {}, function(err, output) {
|
||||||
|
var uid = null;
|
||||||
let new_date = Date.now();
|
let new_date = Date.now();
|
||||||
let difference = (new_date - date)/1000;
|
let difference = (new_date - date)/1000;
|
||||||
logger.debug(`Video download delay: ${difference} seconds.`);
|
logger.debug(`Video download delay: ${difference} seconds.`);
|
||||||
@@ -1318,7 +1498,12 @@ app.post('/api/tomp4', async function(req, res) {
|
|||||||
if (!output_json) {
|
if (!output_json) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var file_name = output_json['_filename'].replace(/^.*[\\\/]/, '');
|
|
||||||
|
// get filepath with no extension
|
||||||
|
const filepath_no_extension = removeFileExtension(output_json['_filename']);
|
||||||
|
|
||||||
|
var full_file_path = filepath_no_extension + '.mp4';
|
||||||
|
var file_name = filepath_no_extension.substring(audioFolderPath.length, filepath_no_extension.length);
|
||||||
|
|
||||||
// renames file if necessary due to bug
|
// renames file if necessary due to bug
|
||||||
if (!fs.existsSync(output_json['_filename'] && fs.existsSync(output_json['_filename'] + '.webm'))) {
|
if (!fs.existsSync(output_json['_filename'] && fs.existsSync(output_json['_filename'] + '.webm'))) {
|
||||||
@@ -1328,11 +1513,11 @@ app.post('/api/tomp4', async function(req, res) {
|
|||||||
} catch(e) {
|
} catch(e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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);
|
// registers file in DB
|
||||||
// remove extension from file path
|
uid = registerFileDB(full_file_path.substring(videoFolderPath.length, full_file_path.length), 'video');
|
||||||
var alternate_file_path = file_path.replace(/\.[^/.]+$/, "")
|
|
||||||
if (alternate_file_name) file_names.push(alternate_file_path);
|
if (file_name) file_names.push(file_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
let is_playlist = file_names.length > 1;
|
let is_playlist = file_names.length > 1;
|
||||||
@@ -1348,7 +1533,8 @@ app.post('/api/tomp4', async function(req, res) {
|
|||||||
var videopathEncoded = encodeURIComponent(file_names[0]);
|
var videopathEncoded = encodeURIComponent(file_names[0]);
|
||||||
res.send({
|
res.send({
|
||||||
videopathEncoded: videopathEncoded,
|
videopathEncoded: videopathEncoded,
|
||||||
file_names: is_playlist ? file_names : null
|
file_names: is_playlist ? file_names : null,
|
||||||
|
uid: uid
|
||||||
});
|
});
|
||||||
res.end("yes");
|
res.end("yes");
|
||||||
}
|
}
|
||||||
@@ -1399,32 +1585,8 @@ app.post('/api/fileStatusMp4', function(req, res) {
|
|||||||
|
|
||||||
// gets all download mp3s
|
// gets all download mp3s
|
||||||
app.post('/api/getMp3s', function(req, res) {
|
app.post('/api/getMp3s', function(req, res) {
|
||||||
var mp3s = [];
|
var mp3s = db.get('files.audio').value(); // getMp3s();
|
||||||
var playlists = db.get('playlists.audio').value();
|
var playlists = db.get('playlists.audio').value();
|
||||||
var files = recFindByExt(audioFolderPath, 'mp3'); // fs.readdirSync(audioFolderPath);
|
|
||||||
for (let i = 0; i < files.length; i++) {
|
|
||||||
let file = files[i];
|
|
||||||
var file_path = file.substring(audioFolderPath.length, file.length);
|
|
||||||
|
|
||||||
var stats = fs.statSync(file);
|
|
||||||
|
|
||||||
var id = file_path.substring(0, file_path.length-4);
|
|
||||||
var jsonobj = getJSONMp3(id);
|
|
||||||
if (!jsonobj) continue;
|
|
||||||
var title = jsonobj.title;
|
|
||||||
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 thumbnail = jsonobj.thumbnail;
|
|
||||||
var duration = jsonobj.duration;
|
|
||||||
var isaudio = true;
|
|
||||||
var file_obj = new File(id, title, thumbnail, isaudio, duration, url, uploader, size, file, upload_date);
|
|
||||||
mp3s.push(file_obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.send({
|
res.send({
|
||||||
mp3s: mp3s,
|
mp3s: mp3s,
|
||||||
@@ -1435,33 +1597,8 @@ app.post('/api/getMp3s', function(req, res) {
|
|||||||
|
|
||||||
// gets all download mp4s
|
// gets all download mp4s
|
||||||
app.post('/api/getMp4s', function(req, res) {
|
app.post('/api/getMp4s', function(req, res) {
|
||||||
var mp4s = [];
|
var mp4s = db.get('files.video').value(); // getMp4s();
|
||||||
var playlists = db.get('playlists.video').value();
|
var playlists = db.get('playlists.video').value();
|
||||||
var fullpath = videoFolderPath;
|
|
||||||
var files = recFindByExt(videoFolderPath, 'mp4');
|
|
||||||
for (let i = 0; i < files.length; i++) {
|
|
||||||
let file = files[i];
|
|
||||||
var file_path = file.substring(videoFolderPath.length, file.length);
|
|
||||||
|
|
||||||
var stats = fs.statSync(file);
|
|
||||||
|
|
||||||
var id = file_path.substring(0, file_path.length-4);
|
|
||||||
var jsonobj = getJSONMp4(id);
|
|
||||||
if (!jsonobj) continue;
|
|
||||||
var title = jsonobj.title;
|
|
||||||
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 thumbnail = jsonobj.thumbnail;
|
|
||||||
var duration = jsonobj.duration;
|
|
||||||
|
|
||||||
var size = stats.size;
|
|
||||||
|
|
||||||
var isaudio = false;
|
|
||||||
var file_obj = new File(id, title, thumbnail, isaudio, duration, url, uploader, size, file, upload_date);
|
|
||||||
mp4s.push(file_obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.send({
|
res.send({
|
||||||
mp4s: mp4s,
|
mp4s: mp4s,
|
||||||
@@ -1470,6 +1607,103 @@ app.post('/api/getMp4s', function(req, res) {
|
|||||||
res.end("yes");
|
res.end("yes");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.post('/api/getFile', function (req, res) {
|
||||||
|
var uid = req.body.uid;
|
||||||
|
var type = req.body.type;
|
||||||
|
|
||||||
|
var file = null;
|
||||||
|
|
||||||
|
if (!type) {
|
||||||
|
file = db.get('files.audio').find({uid: uid}).value();
|
||||||
|
if (!file) {
|
||||||
|
file = db.get('files.video').find({uid: uid}).value();
|
||||||
|
if (file) type = 'video';
|
||||||
|
} else {
|
||||||
|
type = 'audio';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file && type) db.get(`files.${type}`).find({uid: uid}).value();
|
||||||
|
|
||||||
|
if (file) {
|
||||||
|
res.send({
|
||||||
|
success: true,
|
||||||
|
file: file
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.send({
|
||||||
|
success: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// video sharing
|
||||||
|
app.post('/api/enableSharing', function(req, res) {
|
||||||
|
var type = req.body.type;
|
||||||
|
var uid = req.body.uid;
|
||||||
|
var is_playlist = req.body.is_playlist;
|
||||||
|
try {
|
||||||
|
success = true;
|
||||||
|
if (!is_playlist && type !== 'subscription') {
|
||||||
|
db.get(`files.${type}`)
|
||||||
|
.find({uid: uid})
|
||||||
|
.assign({sharingEnabled: true})
|
||||||
|
.write();
|
||||||
|
} else if (is_playlist) {
|
||||||
|
db.get(`playlists.${type}`)
|
||||||
|
.find({id: uid})
|
||||||
|
.assign({sharingEnabled: true})
|
||||||
|
.write();
|
||||||
|
} else if (type === 'subscription') {
|
||||||
|
// TODO: Implement. Main blocker right now is subscription videos are not stored in the DB, they are searched for every
|
||||||
|
// time they are requested from the subscription directory.
|
||||||
|
} else {
|
||||||
|
// error
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch(err) {
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send({
|
||||||
|
success: success
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post('/api/disableSharing', function(req, res) {
|
||||||
|
var type = req.body.type;
|
||||||
|
var uid = req.body.uid;
|
||||||
|
var is_playlist = req.body.is_playlist;
|
||||||
|
try {
|
||||||
|
success = true;
|
||||||
|
if (!is_playlist && type !== 'subscription') {
|
||||||
|
db.get(`files.${type}`)
|
||||||
|
.find({uid: uid})
|
||||||
|
.assign({sharingEnabled: false})
|
||||||
|
.write();
|
||||||
|
} else if (is_playlist) {
|
||||||
|
db.get(`playlists.${type}`)
|
||||||
|
.find({id: uid})
|
||||||
|
.assign({sharingEnabled: false})
|
||||||
|
.write();
|
||||||
|
} else if (type === 'subscription') {
|
||||||
|
// TODO: Implement. Main blocker right now is subscription videos are not stored in the DB, they are searched for every
|
||||||
|
// time they are requested from the subscription directory.
|
||||||
|
} else {
|
||||||
|
// error
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch(err) {
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send({
|
||||||
|
success: success
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
app.post('/api/subscribe', async (req, res) => {
|
app.post('/api/subscribe', async (req, res) => {
|
||||||
let name = req.body.name;
|
let name = req.body.name;
|
||||||
let url = req.body.url;
|
let url = req.body.url;
|
||||||
@@ -1620,7 +1854,8 @@ app.post('/api/createPlaylist', async (req, res) => {
|
|||||||
'name': playlistName,
|
'name': playlistName,
|
||||||
fileNames: fileNames,
|
fileNames: fileNames,
|
||||||
id: shortid.generate(),
|
id: shortid.generate(),
|
||||||
thumbnailURL: thumbnailURL
|
thumbnailURL: thumbnailURL,
|
||||||
|
type: type
|
||||||
};
|
};
|
||||||
|
|
||||||
db.get(`playlists.${type}`)
|
db.get(`playlists.${type}`)
|
||||||
@@ -1633,6 +1868,31 @@ app.post('/api/createPlaylist', async (req, res) => {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.post('/api/getPlaylist', async (req, res) => {
|
||||||
|
let playlistID = req.body.playlistID;
|
||||||
|
let type = req.body.type;
|
||||||
|
|
||||||
|
let playlist = null;
|
||||||
|
|
||||||
|
if (!type) {
|
||||||
|
playlist = db.get('playlists.audio').find({id: playlistID}).value();
|
||||||
|
if (!playlist) {
|
||||||
|
playlist = db.get('playlists.video').find({id: playlistID}).value();
|
||||||
|
if (playlist) type = 'video';
|
||||||
|
} else {
|
||||||
|
type = 'audio';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!playlist) playlist = db.get(`playlists.${type}`).find({id: playlistID}).value();
|
||||||
|
|
||||||
|
res.send({
|
||||||
|
playlist: playlist,
|
||||||
|
type: type,
|
||||||
|
success: !!playlist
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
app.post('/api/updatePlaylist', async (req, res) => {
|
app.post('/api/updatePlaylist', async (req, res) => {
|
||||||
let playlistID = req.body.playlistID;
|
let playlistID = req.body.playlistID;
|
||||||
let fileNames = req.body.fileNames;
|
let fileNames = req.body.fileNames;
|
||||||
@@ -1682,40 +1942,50 @@ 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 uid = req.body.uid;
|
||||||
|
var audio_obj = db.get('files.audio').find({uid: uid}).value();
|
||||||
|
var name = audio_obj.id;
|
||||||
var blacklistMode = req.body.blacklistMode;
|
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, blacklistMode);
|
deleteAudioFile(name, blacklistMode);
|
||||||
|
db.get('files.audio').remove({uid: uid}).write();
|
||||||
wasDeleted = true;
|
wasDeleted = true;
|
||||||
res.send(wasDeleted);
|
res.send(wasDeleted);
|
||||||
res.end("yes");
|
res.end("yes");
|
||||||
}
|
} else if (audio_obj) {
|
||||||
else
|
db.get('files.audio').remove({uid: uid}).write();
|
||||||
{
|
wasDeleted = true;
|
||||||
|
res.send(wasDeleted);
|
||||||
|
} else {
|
||||||
wasDeleted = false;
|
wasDeleted = false;
|
||||||
res.send(wasDeleted);
|
res.send(wasDeleted);
|
||||||
res.end("yes");
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 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 uid = req.body.uid;
|
||||||
|
var video_obj = db.get('files.video').find({uid: uid}).value();
|
||||||
|
var name = video_obj.id;
|
||||||
var blacklistMode = req.body.blacklistMode;
|
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, null, blacklistMode);
|
wasDeleted = await deleteVideoFile(name, null, blacklistMode);
|
||||||
|
db.get('files.video').remove({uid: uid}).write();
|
||||||
// wasDeleted = true;
|
// wasDeleted = true;
|
||||||
res.send(wasDeleted);
|
res.send(wasDeleted);
|
||||||
res.end("yes");
|
res.end("yes");
|
||||||
}
|
} else if (video_obj) {
|
||||||
else
|
db.get('files.video').remove({uid: uid}).write();
|
||||||
{
|
wasDeleted = true;
|
||||||
|
res.send(wasDeleted);
|
||||||
|
} else {
|
||||||
wasDeleted = false;
|
wasDeleted = false;
|
||||||
res.send(wasDeleted);
|
res.send(wasDeleted);
|
||||||
res.end("yes");
|
res.end("yes");
|
||||||
@@ -1728,13 +1998,26 @@ app.post('/api/downloadFile', async (req, res) => {
|
|||||||
let type = req.body.type;
|
let type = req.body.type;
|
||||||
let outputName = req.body.outputName;
|
let outputName = req.body.outputName;
|
||||||
let fullPathProvided = req.body.fullPathProvided;
|
let fullPathProvided = req.body.fullPathProvided;
|
||||||
|
let subscriptionName = req.body.subscriptionName;
|
||||||
|
let subscriptionPlaylist = req.body.subscriptionPlaylist;
|
||||||
let file = null;
|
let file = null;
|
||||||
if (!zip_mode) {
|
if (!zip_mode) {
|
||||||
fileNames = decodeURIComponent(fileNames);
|
fileNames = decodeURIComponent(fileNames);
|
||||||
if (type === 'audio') {
|
if (type === 'audio') {
|
||||||
file = __dirname + '/' + audioFolderPath + fileNames + '.mp3';
|
if (!subscriptionName) {
|
||||||
} else if (type === 'video') {
|
file = path.join(__dirname, audioFolderPath, fileNames + '.mp3');
|
||||||
file = __dirname + '/' + videoFolderPath + fileNames + '.mp4';
|
} else {
|
||||||
|
let basePath = config_api.getConfigItem('ytdl_subscriptions_base_path');
|
||||||
|
file = path.join(__dirname, basePath, (subscriptionPlaylist ? 'playlists' : 'channels'), subscriptionName, fileNames + '.mp3')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if type is 'subscription' or 'video', it's a video
|
||||||
|
if (!subscriptionName) {
|
||||||
|
file = path.join(__dirname, videoFolderPath, fileNames + '.mp4');
|
||||||
|
} else {
|
||||||
|
let basePath = config_api.getConfigItem('ytdl_subscriptions_base_path');
|
||||||
|
file = path.join(__dirname, basePath, (subscriptionPlaylist ? 'playlists' : 'channels'), subscriptionName, fileNames + '.mp4')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (let i = 0; i < fileNames.length; i++) {
|
for (let i = 0; i < fileNames.length; i++) {
|
||||||
@@ -1742,10 +2025,9 @@ app.post('/api/downloadFile', async (req, res) => {
|
|||||||
}
|
}
|
||||||
file = await createPlaylistZipFile(fileNames, type, outputName, fullPathProvided);
|
file = await createPlaylistZipFile(fileNames, type, outputName, fullPathProvided);
|
||||||
}
|
}
|
||||||
|
|
||||||
res.sendFile(file, function (err) {
|
res.sendFile(file, function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
next(err);
|
logger.error(err);
|
||||||
} else if (fullPathProvided) {
|
} else if (fullPathProvided) {
|
||||||
try {
|
try {
|
||||||
fs.unlinkSync(file);
|
fs.unlinkSync(file);
|
||||||
|
|||||||
@@ -215,14 +215,14 @@ async function getVideosForSub(sub) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get videos
|
// get videos
|
||||||
|
logger.verbose('Subscribe: getting videos for subscription ' + sub.name);
|
||||||
youtubedl.exec(sub.url, downloadConfig, {}, function(err, output) {
|
youtubedl.exec(sub.url, downloadConfig, {}, function(err, output) {
|
||||||
logger.verbose('Subscribe: got videos for subscription ' + sub.name);
|
|
||||||
if (err) {
|
if (err) {
|
||||||
logger.error(err.stderr);
|
logger.error(err.stderr);
|
||||||
resolve(false);
|
resolve(false);
|
||||||
} else if (output) {
|
} else if (output) {
|
||||||
if (output.length === 0 || (output.length === 1 && output[0] === '')) {
|
if (output.length === 0 || (output.length === 1 && output[0] === '')) {
|
||||||
logger.debug('No additional videos to download for ' + sub.name);
|
logger.verbose('No additional videos to download for ' + sub.name);
|
||||||
resolve(true);
|
resolve(true);
|
||||||
}
|
}
|
||||||
for (let i = 0; i < output.length; i++) {
|
for (let i = 0; i < output.length; i++) {
|
||||||
|
|||||||
@@ -25,8 +25,9 @@ import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
|||||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||||
import { MatTabsModule } from '@angular/material/tabs';
|
import { MatTabsModule } from '@angular/material/tabs';
|
||||||
import {DragDropModule} from '@angular/cdk/drag-drop';
|
import {DragDropModule} from '@angular/cdk/drag-drop';
|
||||||
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
import {ClipboardModule} from '@angular/cdk/clipboard';
|
||||||
|
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { HttpClientModule, HttpClient } from '@angular/common/http';
|
import { HttpClientModule, HttpClient } from '@angular/common/http';
|
||||||
import { PostsService } from 'app/posts.services';
|
import { PostsService } from 'app/posts.services';
|
||||||
@@ -55,6 +56,7 @@ import { VideoInfoDialogComponent } from './dialogs/video-info-dialog/video-info
|
|||||||
import { ArgModifierDialogComponent, HighlightPipe } from './dialogs/arg-modifier-dialog/arg-modifier-dialog.component';
|
import { ArgModifierDialogComponent, HighlightPipe } from './dialogs/arg-modifier-dialog/arg-modifier-dialog.component';
|
||||||
import { UpdaterComponent } from './updater/updater.component';
|
import { UpdaterComponent } from './updater/updater.component';
|
||||||
import { UpdateProgressDialogComponent } from './dialogs/update-progress-dialog/update-progress-dialog.component';
|
import { UpdateProgressDialogComponent } from './dialogs/update-progress-dialog/update-progress-dialog.component';
|
||||||
|
import { ShareMediaDialogComponent } from './dialogs/share-media-dialog/share-media-dialog.component';
|
||||||
registerLocaleData(es, 'es');
|
registerLocaleData(es, 'es');
|
||||||
|
|
||||||
export function isVisible({ event, element, scrollContainer, offset }: IsVisibleProps<any>) {
|
export function isVisible({ event, element, scrollContainer, offset }: IsVisibleProps<any>) {
|
||||||
@@ -82,7 +84,8 @@ export function isVisible({ event, element, scrollContainer, offset }: IsVisible
|
|||||||
ArgModifierDialogComponent,
|
ArgModifierDialogComponent,
|
||||||
HighlightPipe,
|
HighlightPipe,
|
||||||
UpdaterComponent,
|
UpdaterComponent,
|
||||||
UpdateProgressDialogComponent
|
UpdateProgressDialogComponent,
|
||||||
|
ShareMediaDialogComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
@@ -117,6 +120,7 @@ export function isVisible({ event, element, scrollContainer, offset }: IsVisible
|
|||||||
MatTabsModule,
|
MatTabsModule,
|
||||||
MatTooltipModule,
|
MatTooltipModule,
|
||||||
DragDropModule,
|
DragDropModule,
|
||||||
|
ClipboardModule,
|
||||||
VgCoreModule,
|
VgCoreModule,
|
||||||
VgControlsModule,
|
VgControlsModule,
|
||||||
VgOverlayPlayModule,
|
VgOverlayPlayModule,
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<h4 mat-dialog-title>
|
||||||
|
<ng-container *ngIf="is_playlist" i18n="Share playlist dialog title">Share playlist</ng-container>
|
||||||
|
<ng-container *ngIf="!is_playlist && type === 'video'" i18n="Share video dialog title">Share video</ng-container>
|
||||||
|
<ng-container *ngIf="!is_playlist && type === 'audio'" i18n="Share audio dialog title">Share audio</ng-container>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
<mat-dialog-content>
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<mat-checkbox [checked]="sharing_enabled" (change)="sharingChanged($event)"><ng-container i18n="Enable sharing checkbox">Enable sharing</ng-container></mat-checkbox>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<mat-form-field style="width: 100%">
|
||||||
|
<input matInput [disabled]="!sharing_enabled" [readonly]="true" [value]="share_url">
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div style="margin-bottom: 10px;">
|
||||||
|
<button color="accent" (click)="copiedToClipboard()" [disabled]="!sharing_enabled" [cdkCopyToClipboard]="share_url" mat-raised-button><ng-container i18n="Copy to clipboard button">Copy to clipboard</ng-container></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-dialog-content>
|
||||||
|
|
||||||
|
<mat-dialog-actions>
|
||||||
|
<button mat-button mat-dialog-close><ng-container i18n="Close button">Close</ng-container></button>
|
||||||
|
</mat-dialog-actions>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ShareMediaDialogComponent } from './share-media-dialog.component';
|
||||||
|
|
||||||
|
describe('ShareMediaDialogComponent', () => {
|
||||||
|
let component: ShareMediaDialogComponent;
|
||||||
|
let fixture: ComponentFixture<ShareMediaDialogComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ ShareMediaDialogComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ShareMediaDialogComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import { Component, OnInit, Inject } from '@angular/core';
|
||||||
|
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||||
|
import { PostsService } from 'app/posts.services';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-share-media-dialog',
|
||||||
|
templateUrl: './share-media-dialog.component.html',
|
||||||
|
styleUrls: ['./share-media-dialog.component.scss']
|
||||||
|
})
|
||||||
|
export class ShareMediaDialogComponent implements OnInit {
|
||||||
|
|
||||||
|
type = null;
|
||||||
|
uid = null;
|
||||||
|
share_url = null;
|
||||||
|
sharing_enabled = null;
|
||||||
|
is_playlist = null;
|
||||||
|
|
||||||
|
constructor(@Inject(MAT_DIALOG_DATA) public data: any, public router: Router, private snackBar: MatSnackBar,
|
||||||
|
private postsService: PostsService) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
if (this.data) {
|
||||||
|
this.type = this.data.type;
|
||||||
|
this.uid = this.data.uid;
|
||||||
|
this.sharing_enabled = this.data.sharing_enabled;
|
||||||
|
this.is_playlist = this.data.is_playlist;
|
||||||
|
|
||||||
|
const arg = (this.is_playlist ? ';id=' : ';uid=');
|
||||||
|
this.share_url = window.location.href.split(';')[0] + arg + this.uid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
copiedToClipboard() {
|
||||||
|
this.openSnackBar('Copied to clipboard!');
|
||||||
|
}
|
||||||
|
|
||||||
|
sharingChanged(event) {
|
||||||
|
if (event.checked) {
|
||||||
|
this.postsService.enableSharing(this.uid, this.type, this.is_playlist).subscribe(res => {
|
||||||
|
if (res['success']) {
|
||||||
|
this.openSnackBar('Sharing enabled.');
|
||||||
|
this.sharing_enabled = true;
|
||||||
|
} else {
|
||||||
|
this.openSnackBar('Failed to enable sharing.');
|
||||||
|
}
|
||||||
|
}, err => {
|
||||||
|
this.openSnackBar('Failed to enable sharing - server error.');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.postsService.disableSharing(this.uid, this.type, this.is_playlist).subscribe(res => {
|
||||||
|
if (res['success']) {
|
||||||
|
this.openSnackBar('Sharing disabled.');
|
||||||
|
this.sharing_enabled = false;
|
||||||
|
} else {
|
||||||
|
this.openSnackBar('Failed to disable sharing.');
|
||||||
|
}
|
||||||
|
}, err => {
|
||||||
|
this.openSnackBar('Failed to disable sharing - server error.');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public openSnackBar(message: string, action: string = '') {
|
||||||
|
this.snackBar.open(message, action, {
|
||||||
|
duration: 2000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -18,19 +18,17 @@ export class UpdateProgressDialogComponent implements OnInit {
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.getUpdateProgress();
|
this.getUpdateProgress();
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
this.getUpdateProgress();
|
if (this.updateStatus['updating']) { this.getUpdateProgress(); }
|
||||||
}, 250);
|
}, 250);
|
||||||
}
|
}
|
||||||
|
|
||||||
getUpdateProgress() {
|
getUpdateProgress() {
|
||||||
this.postsService.getUpdaterStatus().subscribe(res => {
|
this.postsService.getUpdaterStatus().subscribe(res => {
|
||||||
this.updateStatus = res;
|
if (res) {
|
||||||
if (!this.updateStatus) {
|
this.updateStatus = res;
|
||||||
// update complete?
|
if (this.updateStatus && this.updateStatus['error']) {
|
||||||
console.log('Update complete? or not started');
|
this.openSnackBar('Update failed. Check logs for more details.');
|
||||||
}
|
}
|
||||||
if (this.updateStatus && this.updateStatus['error']) {
|
|
||||||
this.openSnackBar('Update failed. Check logs for more details.');
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,3 +16,7 @@
|
|||||||
width: 30%;
|
width: 30%;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.a-wrap {
|
||||||
|
word-wrap: break-word
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
<div style="padding:5px">
|
<div style="padding:5px">
|
||||||
<div style="height: 52px;">
|
<div style="height: 52px;">
|
||||||
<div>
|
<div>
|
||||||
<b><a class="file-link" href="javascript:void(0)" (click)="!isPlaylist ? mainComponent.goToFile(name, isAudio) : mainComponent.goToPlaylist(name, type)">{{title}}</a></b>
|
<b><a class="file-link" href="javascript:void(0)" (click)="!isPlaylist ? mainComponent.goToFile(name, isAudio, uid) : mainComponent.goToPlaylist(name, type)">{{title}}</a></b>
|
||||||
</div>
|
</div>
|
||||||
<span class="max-two-lines"><ng-container i18n="File or playlist ID">ID:</ng-container> {{name}}</span>
|
<span class="max-two-lines"><ng-container i18n="File or playlist ID">ID:</ng-container> {{name}}</span>
|
||||||
<div *ngIf="isPlaylist"><ng-container i18n="Playlist video count">Count:</ng-container> {{count}}</div>
|
<div *ngIf="isPlaylist"><ng-container i18n="Playlist video count">Count:</ng-container> {{count}}</div>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ export class FileCardComponent implements OnInit {
|
|||||||
@Input() title: string;
|
@Input() title: string;
|
||||||
@Input() length: string;
|
@Input() length: string;
|
||||||
@Input() name: string;
|
@Input() name: string;
|
||||||
|
@Input() uid: string;
|
||||||
@Input() thumbnailURL: string;
|
@Input() thumbnailURL: string;
|
||||||
@Input() isAudio = true;
|
@Input() isAudio = true;
|
||||||
@Output() removeFile: EventEmitter<string> = new EventEmitter<string>();
|
@Output() removeFile: EventEmitter<string> = new EventEmitter<string>();
|
||||||
@@ -47,7 +48,7 @@ export class FileCardComponent implements OnInit {
|
|||||||
|
|
||||||
deleteFile(blacklistMode = false) {
|
deleteFile(blacklistMode = false) {
|
||||||
if (!this.isPlaylist) {
|
if (!this.isPlaylist) {
|
||||||
this.postsService.deleteFile(this.name, this.isAudio, blacklistMode).subscribe(result => {
|
this.postsService.deleteFile(this.uid, 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 @@
|
|||||||
<div *ngIf="mp3s.length > 0;else nomp3s">
|
<div *ngIf="mp3s.length > 0;else nomp3s">
|
||||||
<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)" [file]="file" [title]="file.title" [name]="file.id" [thumbnailURL]="file.thumbnailURL"
|
<app-file-card #audiofilecard (removeFile)="removeFromMp3($event)" [file]="file" [title]="file.title" [name]="file.id" [uid]="file.uid" [thumbnailURL]="file.thumbnailURL"
|
||||||
[length]="file.duration" [isAudio]="true" [use_youtubedl_archive]="use_youtubedl_archive"></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>
|
||||||
@@ -245,7 +245,7 @@
|
|||||||
<div *ngIf="mp4s.length > 0;else nomp4s">
|
<div *ngIf="mp4s.length > 0;else nomp4s">
|
||||||
<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)" [file]="file" [title]="file.title" [name]="file.id" [thumbnailURL]="file.thumbnailURL"
|
<app-file-card #videofilecard (removeFile)="removeFromMp4($event)" [file]="file" [title]="file.title" [name]="file.id" [uid]="file.uid" [thumbnailURL]="file.thumbnailURL"
|
||||||
[length]="file.duration" [isAudio]="false" [use_youtubedl_archive]="use_youtubedl_archive"></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>
|
||||||
|
|||||||
@@ -390,11 +390,11 @@ export class MainComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public goToFile(name, isAudio) {
|
public goToFile(name, isAudio, uid) {
|
||||||
if (isAudio) {
|
if (isAudio) {
|
||||||
this.downloadHelperMp3(name, false, false);
|
this.downloadHelperMp3(name, uid, false, false);
|
||||||
} else {
|
} else {
|
||||||
this.downloadHelperMp4(name, false, false);
|
this.downloadHelperMp4(name, uid, false, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -407,7 +407,7 @@ export class MainComponent implements OnInit {
|
|||||||
} else {
|
} else {
|
||||||
localStorage.setItem('player_navigator', this.router.url);
|
localStorage.setItem('player_navigator', this.router.url);
|
||||||
const fileNames = playlist.fileNames;
|
const fileNames = playlist.fileNames;
|
||||||
this.router.navigate(['/player', {fileNames: fileNames.join('|nvr|'), type: type, id: playlistID}]);
|
this.router.navigate(['/player', {fileNames: fileNames.join('|nvr|'), type: type, id: playlistID, uid: playlistID}]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// playlist not found
|
// playlist not found
|
||||||
@@ -463,7 +463,7 @@ export class MainComponent implements OnInit {
|
|||||||
|
|
||||||
// download helpers
|
// download helpers
|
||||||
|
|
||||||
downloadHelperMp3(name, is_playlist = false, forceView = false, new_download = null) {
|
downloadHelperMp3(name, uid, is_playlist = false, forceView = false, new_download = null) {
|
||||||
this.downloadingfile = false;
|
this.downloadingfile = false;
|
||||||
|
|
||||||
if (this.multiDownloadMode && !this.downloadOnlyMode) {
|
if (this.multiDownloadMode && !this.downloadOnlyMode) {
|
||||||
@@ -482,7 +482,7 @@ export class MainComponent implements OnInit {
|
|||||||
if (is_playlist) {
|
if (is_playlist) {
|
||||||
this.router.navigate(['/player', {fileNames: name.join('|nvr|'), type: 'audio'}]);
|
this.router.navigate(['/player', {fileNames: name.join('|nvr|'), type: 'audio'}]);
|
||||||
} else {
|
} else {
|
||||||
this.router.navigate(['/player', {fileNames: name, type: 'audio'}]);
|
this.router.navigate(['/player', {fileNames: name, type: 'audio', uid: uid}]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -501,7 +501,7 @@ export class MainComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadHelperMp4(name, is_playlist = false, forceView = false, new_download = null) {
|
downloadHelperMp4(name, uid, is_playlist = false, forceView = false, new_download = null) {
|
||||||
this.downloadingfile = false;
|
this.downloadingfile = false;
|
||||||
if (this.multiDownloadMode && !this.downloadOnlyMode) {
|
if (this.multiDownloadMode && !this.downloadOnlyMode) {
|
||||||
// do nothing
|
// do nothing
|
||||||
@@ -519,7 +519,7 @@ export class MainComponent implements OnInit {
|
|||||||
if (is_playlist) {
|
if (is_playlist) {
|
||||||
this.router.navigate(['/player', {fileNames: name.join('|nvr|'), type: 'video'}]);
|
this.router.navigate(['/player', {fileNames: name.join('|nvr|'), type: 'video'}]);
|
||||||
} else {
|
} else {
|
||||||
this.router.navigate(['/player', {fileNames: name, type: 'video'}]);
|
this.router.navigate(['/player', {fileNames: name, type: 'video', uid: uid}]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -592,7 +592,7 @@ export class MainComponent implements OnInit {
|
|||||||
this.path = is_playlist ? posts['file_names'] : posts['audiopathEncoded'];
|
this.path = is_playlist ? posts['file_names'] : posts['audiopathEncoded'];
|
||||||
|
|
||||||
if (this.path !== '-1') {
|
if (this.path !== '-1') {
|
||||||
this.downloadHelperMp3(this.path, is_playlist, false, new_download);
|
this.downloadHelperMp3(this.path, posts['uid'], is_playlist, false, new_download);
|
||||||
}
|
}
|
||||||
}, error => { // can't access server or failed to download for other reasons
|
}, error => { // can't access server or failed to download for other reasons
|
||||||
this.downloadingfile = false;
|
this.downloadingfile = false;
|
||||||
@@ -631,7 +631,7 @@ export class MainComponent implements OnInit {
|
|||||||
this.path = is_playlist ? posts['file_names'] : posts['videopathEncoded'];
|
this.path = is_playlist ? posts['file_names'] : posts['videopathEncoded'];
|
||||||
|
|
||||||
if (this.path !== '-1') {
|
if (this.path !== '-1') {
|
||||||
this.downloadHelperMp4(this.path, is_playlist, false, new_download);
|
this.downloadHelperMp4(this.path, posts['uid'], is_playlist, false, new_download);
|
||||||
}
|
}
|
||||||
}, error => { // can't access server
|
}, error => { // can't access server
|
||||||
this.downloadingfile = false;
|
this.downloadingfile = false;
|
||||||
|
|||||||
@@ -45,13 +45,19 @@
|
|||||||
|
|
||||||
.save-button {
|
.save-button {
|
||||||
right: 25px;
|
right: 25px;
|
||||||
position: absolute;
|
position: fixed;
|
||||||
|
bottom: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.share-button {
|
||||||
|
left: 25px;
|
||||||
|
position: fixed;
|
||||||
bottom: 25px;
|
bottom: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.favorite-button {
|
.favorite-button {
|
||||||
left: 25px;
|
left: 25px;
|
||||||
position: absolute;
|
position: fixed;
|
||||||
bottom: 25px;
|
bottom: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<div *ngIf="playlist.length > 0">
|
<div *ngIf="playlist.length > 0 && show_player">
|
||||||
<div [ngClass]="(type === 'audio') ? null : 'container-video'" class="container">
|
<div [ngClass]="(type === 'audio') ? null : 'container-video'" class="container">
|
||||||
<div style="max-width: 100%; margin-left: 0px;" class="row">
|
<div style="max-width: 100%; margin-left: 0px;" class="row">
|
||||||
<div [ngClass]="(type === 'audio') ? 'my-2 px-1' : 'video-col'" class="col">
|
<div [ngClass]="(type === 'audio') ? 'my-2 px-1' : 'video-col'" class="col">
|
||||||
@@ -26,8 +26,10 @@
|
|||||||
<div *ngIf="playlist.length > 1">
|
<div *ngIf="playlist.length > 1">
|
||||||
<button class="save-button" color="primary" (click)="downloadContent()" [disabled]="downloading" mat-fab><mat-icon class="save-icon">save</mat-icon><mat-spinner *ngIf="downloading" class="spinner" [diameter]="50"></mat-spinner></button>
|
<button class="save-button" color="primary" (click)="downloadContent()" [disabled]="downloading" mat-fab><mat-icon class="save-icon">save</mat-icon><mat-spinner *ngIf="downloading" class="spinner" [diameter]="50"></mat-spinner></button>
|
||||||
<button *ngIf="!id" color="accent" class="favorite-button" color="primary" (click)="namePlaylistDialog()" mat-fab><mat-icon class="save-icon">favorite</mat-icon></button>
|
<button *ngIf="!id" color="accent" class="favorite-button" color="primary" (click)="namePlaylistDialog()" mat-fab><mat-icon class="save-icon">favorite</mat-icon></button>
|
||||||
|
<button *ngIf="!is_shared && id" class="share-button" color="primary" (click)="openShareDialog()" mat-fab><mat-icon class="save-icon">share</mat-icon></button>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="playlist.length === 1">
|
<div *ngIf="playlist.length === 1">
|
||||||
<button class="save-button" color="primary" (click)="downloadFile()" [disabled]="downloading" mat-fab><mat-icon class="save-icon">save</mat-icon><mat-spinner *ngIf="downloading" class="spinner" [diameter]="50"></mat-spinner></button>
|
<button class="save-button" color="primary" (click)="downloadFile()" [disabled]="downloading" mat-fab><mat-icon class="save-icon">save</mat-icon><mat-spinner *ngIf="downloading" class="spinner" [diameter]="50"></mat-spinner></button>
|
||||||
|
<button *ngIf="!is_shared && type !== 'subscription'" class="share-button" color="primary" (click)="openShareDialog()" mat-fab><mat-icon class="save-icon">share</mat-icon></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -6,6 +6,7 @@ import { MatDialog } from '@angular/material/dialog';
|
|||||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||||
import { InputDialogComponent } from 'app/input-dialog/input-dialog.component';
|
import { InputDialogComponent } from 'app/input-dialog/input-dialog.component';
|
||||||
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
|
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
|
||||||
|
import { ShareMediaDialogComponent } from '../dialogs/share-media-dialog/share-media-dialog.component';
|
||||||
|
|
||||||
export interface IMedia {
|
export interface IMedia {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -25,6 +26,8 @@ export class PlayerComponent implements OnInit {
|
|||||||
original_playlist: string = null;
|
original_playlist: string = null;
|
||||||
playlist_updating = false;
|
playlist_updating = false;
|
||||||
|
|
||||||
|
show_player = false;
|
||||||
|
|
||||||
currentIndex = 0;
|
currentIndex = 0;
|
||||||
currentItem: IMedia = null;
|
currentItem: IMedia = null;
|
||||||
api: VgAPI;
|
api: VgAPI;
|
||||||
@@ -33,14 +36,24 @@ export class PlayerComponent implements OnInit {
|
|||||||
fileNames: string[];
|
fileNames: string[];
|
||||||
type: string;
|
type: string;
|
||||||
id = null; // used for playlists (not subscription)
|
id = null; // used for playlists (not subscription)
|
||||||
|
uid = null; // used for non-subscription files (audio, video, playlist)
|
||||||
subscriptionName = null;
|
subscriptionName = null;
|
||||||
subPlaylist = null;
|
subPlaylist = null;
|
||||||
|
|
||||||
|
is_shared = false;
|
||||||
|
|
||||||
|
db_playlist = null;
|
||||||
|
db_file = null;
|
||||||
|
|
||||||
baseStreamPath = null;
|
baseStreamPath = null;
|
||||||
audioFolderPath = null;
|
audioFolderPath = null;
|
||||||
videoFolderPath = null;
|
videoFolderPath = null;
|
||||||
subscriptionFolderPath = null;
|
subscriptionFolderPath = null;
|
||||||
|
|
||||||
|
// url-mode params
|
||||||
|
url = null;
|
||||||
|
name = null;
|
||||||
|
|
||||||
innerWidth: number;
|
innerWidth: number;
|
||||||
|
|
||||||
downloading = false;
|
downloading = false;
|
||||||
@@ -53,11 +66,13 @@ export class PlayerComponent implements OnInit {
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.innerWidth = window.innerWidth;
|
this.innerWidth = window.innerWidth;
|
||||||
|
|
||||||
this.fileNames = this.route.snapshot.paramMap.get('fileNames').split('|nvr|');
|
|
||||||
this.type = this.route.snapshot.paramMap.get('type');
|
this.type = this.route.snapshot.paramMap.get('type');
|
||||||
this.id = this.route.snapshot.paramMap.get('id');
|
this.id = this.route.snapshot.paramMap.get('id');
|
||||||
|
this.uid = this.route.snapshot.paramMap.get('uid');
|
||||||
this.subscriptionName = this.route.snapshot.paramMap.get('subscriptionName');
|
this.subscriptionName = this.route.snapshot.paramMap.get('subscriptionName');
|
||||||
this.subPlaylist = this.route.snapshot.paramMap.get('subPlaylist');
|
this.subPlaylist = this.route.snapshot.paramMap.get('subPlaylist');
|
||||||
|
this.url = this.route.snapshot.paramMap.get('url');
|
||||||
|
this.name = this.route.snapshot.paramMap.get('name');
|
||||||
|
|
||||||
// loading config
|
// loading config
|
||||||
this.postsService.loadNavItems().subscribe(res => { // loads settings
|
this.postsService.loadNavItems().subscribe(res => { // loads settings
|
||||||
@@ -66,53 +81,35 @@ export class PlayerComponent implements OnInit {
|
|||||||
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.subscriptionFolderPath = result['YoutubeDLMaterial']['Subscriptions']['subscriptions_base_path'];
|
this.subscriptionFolderPath = result['YoutubeDLMaterial']['Subscriptions']['subscriptions_base_path'];
|
||||||
|
this.fileNames = this.route.snapshot.paramMap.get('fileNames') ? this.route.snapshot.paramMap.get('fileNames').split('|nvr|') : null;
|
||||||
|
|
||||||
|
if (!this.fileNames) {
|
||||||
let fileType = null;
|
this.is_shared = true;
|
||||||
if (this.type === 'audio') {
|
|
||||||
fileType = 'audio/mp3';
|
|
||||||
} else if (this.type === 'video') {
|
|
||||||
fileType = 'video/mp4';
|
|
||||||
} else if (this.type === 'subscription') {
|
|
||||||
// only supports mp4 for now
|
|
||||||
fileType = 'video/mp4';
|
|
||||||
} else {
|
|
||||||
// error
|
|
||||||
console.error('Must have valid file type! Use \'audio\', \'video\', or \'subscription\'.');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < this.fileNames.length; i++) {
|
if (this.uid && !this.id) {
|
||||||
const fileName = this.fileNames[i];
|
this.getFile();
|
||||||
let baseLocation = null;
|
} else if (this.id) {
|
||||||
let fullLocation = null;
|
this.getPlaylistFiles();
|
||||||
if (!this.subscriptionName) {
|
}
|
||||||
baseLocation = this.type + '/';
|
|
||||||
fullLocation = this.baseStreamPath + baseLocation + encodeURIComponent(fileName);
|
if (this.url) {
|
||||||
} else {
|
// if a url is given, just stream the URL
|
||||||
// default to video but include subscription name param
|
this.playlist = [];
|
||||||
baseLocation = 'video/';
|
const imedia: IMedia = {
|
||||||
fullLocation = this.baseStreamPath + baseLocation + encodeURIComponent(fileName) + '?subName=' + this.subscriptionName +
|
title: this.name,
|
||||||
'&subPlaylist=' + this.subPlaylist;
|
label: this.name,
|
||||||
}
|
src: this.url,
|
||||||
// if it has a slash (meaning it's in a directory), only get the file name for the label
|
type: 'video/mp4'
|
||||||
let label = null;
|
}
|
||||||
const decodedName = decodeURIComponent(fileName);
|
this.playlist.push(imedia);
|
||||||
const hasSlash = decodedName.includes('/') || decodedName.includes('\\');
|
this.currentItem = this.playlist[0];
|
||||||
if (hasSlash) {
|
this.currentIndex = 0;
|
||||||
label = decodedName.replace(/^.*[\\\/]/, '');
|
this.show_player = true;
|
||||||
} else {
|
} else if (this.type === 'subscription' || this.fileNames) {
|
||||||
label = decodedName;
|
this.show_player = true;
|
||||||
}
|
this.parseFileNames();
|
||||||
const mediaObject: IMedia = {
|
|
||||||
title: fileName,
|
|
||||||
src: fullLocation,
|
|
||||||
type: fileType,
|
|
||||||
label: label
|
|
||||||
}
|
|
||||||
this.playlist.push(mediaObject);
|
|
||||||
}
|
}
|
||||||
this.currentItem = this.playlist[this.currentIndex];
|
|
||||||
this.original_playlist = JSON.stringify(this.playlist);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// this.getFileInfos();
|
// this.getFileInfos();
|
||||||
@@ -124,6 +121,85 @@ export class PlayerComponent implements OnInit {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getFile() {
|
||||||
|
const already_has_filenames = !!this.fileNames;
|
||||||
|
this.postsService.getFile(this.uid, null).subscribe(res => {
|
||||||
|
this.db_file = res['file'];
|
||||||
|
if (!this.fileNames) {
|
||||||
|
// means it's a shared video
|
||||||
|
if (!this.id) {
|
||||||
|
// regular video/audio file (not playlist)
|
||||||
|
this.fileNames = [this.db_file['id']];
|
||||||
|
this.type = this.db_file['isAudio'] ? 'audio' : 'video';
|
||||||
|
if (!already_has_filenames) { this.parseFileNames(); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.db_file['sharingEnabled']) {
|
||||||
|
this.show_player = true;
|
||||||
|
} else if (!already_has_filenames) {
|
||||||
|
this.openSnackBar('Error: Sharing has been disabled for this video!', 'Dismiss');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getPlaylistFiles() {
|
||||||
|
this.postsService.getPlaylist(this.id, null).subscribe(res => {
|
||||||
|
this.db_playlist = res['playlist'];
|
||||||
|
this.fileNames = this.db_playlist['fileNames'];
|
||||||
|
this.type = res['type'];
|
||||||
|
this.show_player = true;
|
||||||
|
this.parseFileNames();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
parseFileNames() {
|
||||||
|
let fileType = null;
|
||||||
|
if (this.type === 'audio') {
|
||||||
|
fileType = 'audio/mp3';
|
||||||
|
} else if (this.type === 'video') {
|
||||||
|
fileType = 'video/mp4';
|
||||||
|
} else if (this.type === 'subscription') {
|
||||||
|
// only supports mp4 for now
|
||||||
|
fileType = 'video/mp4';
|
||||||
|
} else {
|
||||||
|
// error
|
||||||
|
console.error('Must have valid file type! Use \'audio\', \'video\', or \'subscription\'.');
|
||||||
|
}
|
||||||
|
this.playlist = [];
|
||||||
|
for (let i = 0; i < this.fileNames.length; i++) {
|
||||||
|
const fileName = this.fileNames[i];
|
||||||
|
let baseLocation = null;
|
||||||
|
let fullLocation = null;
|
||||||
|
if (!this.subscriptionName) {
|
||||||
|
baseLocation = this.type + '/';
|
||||||
|
fullLocation = this.baseStreamPath + baseLocation + encodeURIComponent(fileName);
|
||||||
|
} else {
|
||||||
|
// default to video but include subscription name param
|
||||||
|
baseLocation = 'video/';
|
||||||
|
fullLocation = this.baseStreamPath + baseLocation + encodeURIComponent(fileName) + '?subName=' + this.subscriptionName +
|
||||||
|
'&subPlaylist=' + this.subPlaylist;
|
||||||
|
}
|
||||||
|
// if it has a slash (meaning it's in a directory), only get the file name for the label
|
||||||
|
let label = null;
|
||||||
|
const decodedName = decodeURIComponent(fileName);
|
||||||
|
const hasSlash = decodedName.includes('/') || decodedName.includes('\\');
|
||||||
|
if (hasSlash) {
|
||||||
|
label = decodedName.replace(/^.*[\\\/]/, '');
|
||||||
|
} else {
|
||||||
|
label = decodedName;
|
||||||
|
}
|
||||||
|
const mediaObject: IMedia = {
|
||||||
|
title: fileName,
|
||||||
|
src: fullLocation,
|
||||||
|
type: fileType,
|
||||||
|
label: label
|
||||||
|
}
|
||||||
|
this.playlist.push(mediaObject);
|
||||||
|
}
|
||||||
|
this.currentItem = this.playlist[this.currentIndex];
|
||||||
|
this.original_playlist = JSON.stringify(this.playlist);
|
||||||
|
}
|
||||||
|
|
||||||
onPlayerReady(api: VgAPI) {
|
onPlayerReady(api: VgAPI) {
|
||||||
this.api = api;
|
this.api = api;
|
||||||
|
|
||||||
@@ -193,7 +269,7 @@ export class PlayerComponent implements OnInit {
|
|||||||
const ext = (this.type === 'audio') ? '.mp3' : '.mp4';
|
const ext = (this.type === 'audio') ? '.mp3' : '.mp4';
|
||||||
const filename = this.playlist[0].title;
|
const filename = this.playlist[0].title;
|
||||||
this.downloading = true;
|
this.downloading = true;
|
||||||
this.postsService.downloadFileFromServer(filename, this.type).subscribe(res => {
|
this.postsService.downloadFileFromServer(filename, this.type, null, null, this.subscriptionName, this.subPlaylist).subscribe(res => {
|
||||||
this.downloading = false;
|
this.downloading = false;
|
||||||
const blob: Blob = res;
|
const blob: Blob = res;
|
||||||
saveAs(blob, filename + ext);
|
saveAs(blob, filename + ext);
|
||||||
@@ -224,6 +300,7 @@ export class PlayerComponent implements OnInit {
|
|||||||
if (res['success']) {
|
if (res['success']) {
|
||||||
dialogRef.close();
|
dialogRef.close();
|
||||||
const new_playlist = res['new_playlist'];
|
const new_playlist = res['new_playlist'];
|
||||||
|
this.db_playlist = new_playlist;
|
||||||
this.openSnackBar('Playlist \'' + name + '\' successfully created!', '')
|
this.openSnackBar('Playlist \'' + name + '\' successfully created!', '')
|
||||||
this.playlistPostCreationHandler(new_playlist.id);
|
this.playlistPostCreationHandler(new_playlist.id);
|
||||||
}
|
}
|
||||||
@@ -273,6 +350,26 @@ export class PlayerComponent implements OnInit {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openShareDialog() {
|
||||||
|
const dialogRef = this.dialog.open(ShareMediaDialogComponent, {
|
||||||
|
data: {
|
||||||
|
uid: this.id ? this.id : this.uid,
|
||||||
|
type: this.type,
|
||||||
|
sharing_enabled: this.id ? this.db_playlist.sharingEnabled : this.db_file.sharingEnabled,
|
||||||
|
is_playlist: !!this.id
|
||||||
|
},
|
||||||
|
width: '60vw'
|
||||||
|
});
|
||||||
|
|
||||||
|
dialogRef.afterClosed().subscribe(res => {
|
||||||
|
if (!this.id) {
|
||||||
|
this.getFile();
|
||||||
|
} else {
|
||||||
|
this.getPlaylistFiles();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// snackbar helper
|
// snackbar helper
|
||||||
public openSnackBar(message: string, action: string) {
|
public openSnackBar(message: string, action: string) {
|
||||||
this.snackBar.open(message, action, {
|
this.snackBar.open(message, action, {
|
||||||
|
|||||||
@@ -102,11 +102,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, blacklistMode = false) {
|
deleteFile(uid: string, isAudio: boolean, blacklistMode = false) {
|
||||||
if (isAudio) {
|
if (isAudio) {
|
||||||
return this.http.post(this.path + 'deleteMp3', {name: name, blacklistMode: blacklistMode});
|
return this.http.post(this.path + 'deleteMp3', {uid: uid, blacklistMode: blacklistMode});
|
||||||
} else {
|
} else {
|
||||||
return this.http.post(this.path + 'deleteMp4', {name: name, blacklistMode: blacklistMode});
|
return this.http.post(this.path + 'deleteMp4', {uid: uid, blacklistMode: blacklistMode});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,12 +118,19 @@ export class PostsService {
|
|||||||
return this.http.post(this.path + 'getMp4s', {});
|
return this.http.post(this.path + 'getMp4s', {});
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadFileFromServer(fileName, type, outputName = null, fullPathProvided = null) {
|
getFile(uid, type) {
|
||||||
|
return this.http.post(this.path + 'getFile', {uid: uid, type: type});
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadFileFromServer(fileName, type, outputName = null, fullPathProvided = null, subscriptionName = null, subPlaylist = null) {
|
||||||
return this.http.post(this.path + 'downloadFile', {fileNames: fileName,
|
return this.http.post(this.path + 'downloadFile', {fileNames: fileName,
|
||||||
type: type,
|
type: type,
|
||||||
zip_mode: Array.isArray(fileName),
|
zip_mode: Array.isArray(fileName),
|
||||||
outputName: outputName,
|
outputName: outputName,
|
||||||
fullPathProvided: fullPathProvided},
|
fullPathProvided: fullPathProvided,
|
||||||
|
subscriptionName: subscriptionName,
|
||||||
|
subPlaylist: subPlaylist
|
||||||
|
},
|
||||||
{responseType: 'blob'});
|
{responseType: 'blob'});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,6 +154,14 @@ export class PostsService {
|
|||||||
return this.http.post(this.path + 'checkPin', {input_pin: unhashed_pin});
|
return this.http.post(this.path + 'checkPin', {input_pin: unhashed_pin});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enableSharing(uid, type, is_playlist) {
|
||||||
|
return this.http.post(this.path + 'enableSharing', {uid: uid, type: type, is_playlist: is_playlist});
|
||||||
|
}
|
||||||
|
|
||||||
|
disableSharing(uid, type, is_playlist) {
|
||||||
|
return this.http.post(this.path + 'disableSharing', {uid: uid, type: type, is_playlist: is_playlist});
|
||||||
|
}
|
||||||
|
|
||||||
createPlaylist(playlistName, fileNames, type, thumbnailURL) {
|
createPlaylist(playlistName, fileNames, type, thumbnailURL) {
|
||||||
return this.http.post(this.path + 'createPlaylist', {playlistName: playlistName,
|
return this.http.post(this.path + 'createPlaylist', {playlistName: playlistName,
|
||||||
fileNames: fileNames,
|
fileNames: fileNames,
|
||||||
@@ -154,6 +169,11 @@ export class PostsService {
|
|||||||
thumbnailURL: thumbnailURL});
|
thumbnailURL: thumbnailURL});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPlaylist(playlistID, type) {
|
||||||
|
return this.http.post(this.path + 'getPlaylist', {playlistID: playlistID,
|
||||||
|
type: type});
|
||||||
|
}
|
||||||
|
|
||||||
updatePlaylist(playlistID, fileNames, type) {
|
updatePlaylist(playlistID, fileNames, type) {
|
||||||
return this.http.post(this.path + 'updatePlaylist', {playlistID: playlistID,
|
return this.http.post(this.path + 'updatePlaylist', {playlistID: playlistID,
|
||||||
fileNames: fileNames,
|
fileNames: fileNames,
|
||||||
|
|||||||
Reference in New Issue
Block a user