mirror of
https://github.com/Tzahi12345/YoutubeDL-Material.git
synced 2026-03-29 16:10:56 +03:00
Refactored player component to utilize uids instead of fileNames to improve maintainability, consistency, and reliability
Playlists now use uids instead of fileNames Added generic getPlaylist and updatePlaylist functions
This commit is contained in:
164
backend/app.js
164
backend/app.js
@@ -139,6 +139,8 @@ var updaterStatus = null;
|
||||
|
||||
var timestamp_server_start = Date.now();
|
||||
|
||||
const concurrentStreams = {};
|
||||
|
||||
if (debugMode) logger.info('YTDL-Material in debug mode!');
|
||||
|
||||
// check if just updated
|
||||
@@ -1849,14 +1851,14 @@ const optionalJwt = function (req, res, next) {
|
||||
const multiUserMode = config_api.getConfigItem('ytdl_multi_user_mode');
|
||||
if (multiUserMode && ((req.body && req.body.uuid) || (req.query && req.query.uuid)) && (req.path.includes('/api/getFile') ||
|
||||
req.path.includes('/api/stream') ||
|
||||
req.path.includes('/api/getPlaylist') ||
|
||||
req.path.includes('/api/downloadFile'))) {
|
||||
// check if shared video
|
||||
const using_body = req.body && req.body.uuid;
|
||||
const uuid = using_body ? req.body.uuid : req.query.uuid;
|
||||
const uid = using_body ? req.body.uid : req.query.uid;
|
||||
const type = using_body ? req.body.type : req.query.type;
|
||||
const playlist_id = using_body ? req.body.id : req.query.id;
|
||||
const file = !playlist_id ? auth_api.getUserVideo(uuid, uid, type, true, req.body) : auth_api.getUserPlaylist(uuid, playlist_id, null, false);
|
||||
const playlist_id = using_body ? req.body.playlist_id : req.query.playlist_id;
|
||||
const file = !playlist_id ? auth_api.getUserVideo(uuid, uid, true) : db_api.getPlaylist(playlist_id, uuid, true);
|
||||
if (file) {
|
||||
req.can_watch = true;
|
||||
return next();
|
||||
@@ -2118,6 +2120,34 @@ app.post('/api/getAllFiles', optionalJwt, async function (req, res) {
|
||||
});
|
||||
});
|
||||
|
||||
app.post('/api/checkConcurrentStream', async (req, res) => {
|
||||
const uid = req.body.uid;
|
||||
|
||||
const DEAD_SERVER_THRESHOLD = 10;
|
||||
|
||||
if (concurrentStreams[uid] && Date.now()/1000 - concurrentStreams[uid]['unix_timestamp'] > DEAD_SERVER_THRESHOLD) {
|
||||
logger.verbose( `Killing dead stream on ${uid}`);
|
||||
delete concurrentStreams[uid];
|
||||
}
|
||||
|
||||
res.send({stream: concurrentStreams[uid]})
|
||||
});
|
||||
|
||||
app.post('/api/updateConcurrentStream', optionalJwt, async (req, res) => {
|
||||
const uid = req.body.uid;
|
||||
const playback_timestamp = req.body.playback_timestamp;
|
||||
const unix_timestamp = req.body.unix_timestamp;
|
||||
const playing = req.body.playing;
|
||||
|
||||
concurrentStreams[uid] = {
|
||||
playback_timestamp: playback_timestamp,
|
||||
unix_timestamp: unix_timestamp,
|
||||
playing: playing
|
||||
}
|
||||
|
||||
res.send({stream: concurrentStreams[uid]})
|
||||
});
|
||||
|
||||
app.post('/api/getFullTwitchChat', optionalJwt, async (req, res) => {
|
||||
var id = req.body.id;
|
||||
var type = req.body.type;
|
||||
@@ -2174,7 +2204,7 @@ app.post('/api/enableSharing', optionalJwt, function(req, res) {
|
||||
// single-user mode
|
||||
try {
|
||||
success = true;
|
||||
if (!is_playlist && type !== 'subscription') {
|
||||
if (!is_playlist) {
|
||||
db.get(`files`)
|
||||
.find({uid: uid})
|
||||
.assign({sharingEnabled: true})
|
||||
@@ -2184,7 +2214,7 @@ app.post('/api/enableSharing', optionalJwt, function(req, res) {
|
||||
.find({id: uid})
|
||||
.assign({sharingEnabled: true})
|
||||
.write();
|
||||
} else if (type === 'subscription') {
|
||||
} else if (false) {
|
||||
// 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 {
|
||||
@@ -2193,6 +2223,7 @@ app.post('/api/enableSharing', optionalJwt, function(req, res) {
|
||||
}
|
||||
|
||||
} catch(err) {
|
||||
logger.error(err);
|
||||
success = false;
|
||||
}
|
||||
|
||||
@@ -2525,14 +2556,14 @@ app.post('/api/getSubscriptions', optionalJwt, async (req, res) => {
|
||||
|
||||
app.post('/api/createPlaylist', optionalJwt, async (req, res) => {
|
||||
let playlistName = req.body.playlistName;
|
||||
let fileNames = req.body.fileNames;
|
||||
let uids = req.body.uids;
|
||||
let type = req.body.type;
|
||||
let thumbnailURL = req.body.thumbnailURL;
|
||||
let duration = req.body.duration;
|
||||
|
||||
let new_playlist = {
|
||||
name: playlistName,
|
||||
fileNames: fileNames,
|
||||
uids: uids,
|
||||
id: shortid.generate(),
|
||||
thumbnailURL: thumbnailURL,
|
||||
type: type,
|
||||
@@ -2556,15 +2587,19 @@ app.post('/api/createPlaylist', optionalJwt, async (req, res) => {
|
||||
});
|
||||
|
||||
app.post('/api/getPlaylist', optionalJwt, async (req, res) => {
|
||||
let playlistID = req.body.playlistID;
|
||||
let uuid = req.body.uuid;
|
||||
let playlist_id = req.body.playlist_id;
|
||||
let uuid = req.body.uuid ? req.body.uuid : (req.user && req.user.uid ? req.user.uid : null);
|
||||
let include_file_metadata = req.body.include_file_metadata;
|
||||
|
||||
let playlist = null;
|
||||
const playlist = await db_api.getPlaylist(playlist_id, uuid);
|
||||
|
||||
if (req.isAuthenticated()) {
|
||||
playlist = auth_api.getUserPlaylist(uuid ? uuid : req.user.uid, playlistID);
|
||||
} else {
|
||||
playlist = db.get(`playlists`).find({id: playlistID}).value();
|
||||
if (playlist && include_file_metadata) {
|
||||
playlist['file_objs'] = [];
|
||||
for (let i = 0; i < playlist['uids'].length; i++) {
|
||||
const uid = playlist['uids'][i];
|
||||
const file_obj = await db_api.getVideo(uid, uuid);
|
||||
playlist['file_objs'].push(file_obj);
|
||||
}
|
||||
}
|
||||
|
||||
res.send({
|
||||
@@ -2576,16 +2611,16 @@ app.post('/api/getPlaylist', optionalJwt, async (req, res) => {
|
||||
|
||||
app.post('/api/updatePlaylistFiles', optionalJwt, async (req, res) => {
|
||||
let playlistID = req.body.playlistID;
|
||||
let fileNames = req.body.fileNames;
|
||||
let uids = req.body.uids;
|
||||
|
||||
let success = false;
|
||||
try {
|
||||
if (req.isAuthenticated()) {
|
||||
auth_api.updatePlaylistFiles(req.user.uid, playlistID, fileNames);
|
||||
auth_api.updatePlaylistFiles(req.user.uid, playlistID, uids);
|
||||
} else {
|
||||
db.get(`playlists`)
|
||||
.find({id: playlistID})
|
||||
.assign({fileNames: fileNames})
|
||||
.assign({uids: uids})
|
||||
.write();
|
||||
}
|
||||
|
||||
@@ -2664,51 +2699,36 @@ app.post('/api/deleteFile', optionalJwt, async (req, res) => {
|
||||
});
|
||||
|
||||
app.post('/api/downloadFile', optionalJwt, async (req, res) => {
|
||||
let fileNames = req.body.fileNames;
|
||||
let zip_mode = req.body.zip_mode;
|
||||
let type = req.body.type;
|
||||
let outputName = req.body.outputName;
|
||||
let fullPathProvided = req.body.fullPathProvided;
|
||||
let subscriptionName = req.body.subscriptionName;
|
||||
let subscriptionPlaylist = req.body.subPlaylist;
|
||||
let file = null;
|
||||
if (!zip_mode) {
|
||||
fileNames = decodeURIComponent(fileNames);
|
||||
const is_audio = type === 'audio';
|
||||
const fileFolderPath = is_audio ? audioFolderPath : videoFolderPath;
|
||||
const ext = is_audio ? '.mp3' : '.mp4';
|
||||
let uid = req.body.uid;
|
||||
let is_playlist = req.body.is_playlist;
|
||||
let uuid = req.body.uuid;
|
||||
|
||||
let base_path = fileFolderPath;
|
||||
let usersFileFolder = null;
|
||||
const multiUserMode = config_api.getConfigItem('ytdl_multi_user_mode');
|
||||
if (multiUserMode && (req.body.uuid || req.user.uid)) {
|
||||
usersFileFolder = config_api.getConfigItem('ytdl_users_base_path');
|
||||
base_path = path.join(usersFileFolder, req.body.uuid ? req.body.uuid : req.user.uid, type);
|
||||
}
|
||||
if (!subscriptionName) {
|
||||
file = path.join(__dirname, base_path, fileNames + ext);
|
||||
} else {
|
||||
let basePath = null;
|
||||
if (usersFileFolder)
|
||||
basePath = path.join(usersFileFolder, req.user.uid, 'subscriptions');
|
||||
else
|
||||
basePath = config_api.getConfigItem('ytdl_subscriptions_base_path');
|
||||
let file_path_to_download = null;
|
||||
|
||||
file = path.join(__dirname, basePath, (subscriptionPlaylist === true || subscriptionPlaylist === 'true' ? 'playlists' : 'channels'), subscriptionName, fileNames + ext);
|
||||
if (!uuid && req.user) uuid = req.user.uid;
|
||||
if (is_playlist) {
|
||||
const playlist_files_to_download = [];
|
||||
const playlist = db_api.getPlaylist(uid, uuid);
|
||||
for (let i = 0; i < playlist['uids'].length; i++) {
|
||||
const uid = playlist['uids'][i];
|
||||
const file_obj = await db_api.getVideo(uid, uuid);
|
||||
playlist_files_to_download.push(file_obj.path);
|
||||
}
|
||||
|
||||
// generate zip
|
||||
file_path_to_download = await createPlaylistZipFile(playlist_files_to_download, playlist.type, playlist.name);
|
||||
} else {
|
||||
for (let i = 0; i < fileNames.length; i++) {
|
||||
fileNames[i] = decodeURIComponent(fileNames[i]);
|
||||
}
|
||||
file = await createPlaylistZipFile(fileNames, type, outputName, fullPathProvided, req.body.uuid || req.user.uid);
|
||||
if (!path.isAbsolute(file)) file = path.join(__dirname, file);
|
||||
const file_obj = await db_api.getVideo(uid, uuid)
|
||||
file_path_to_download = file_obj.path;
|
||||
}
|
||||
res.sendFile(file, function (err) {
|
||||
if (!path.isAbsolute(file_path_to_download)) file_path_to_download = path.join(__dirname, file_path_to_download);
|
||||
res.sendFile(file_path_to_download, function (err) {
|
||||
if (err) {
|
||||
logger.error(err);
|
||||
} else if (fullPathProvided) {
|
||||
} else if (is_playlist) {
|
||||
try {
|
||||
fs.unlinkSync(file);
|
||||
// delete generated zip file
|
||||
fs.unlinkSync(file_path_to_download);
|
||||
} catch(e) {
|
||||
logger.error("Failed to remove file", file);
|
||||
}
|
||||
@@ -2783,31 +2803,21 @@ app.post('/api/generateNewAPIKey', function (req, res) {
|
||||
|
||||
// Streaming API calls
|
||||
|
||||
app.get('/api/stream/:id', optionalJwt, (req, res) => {
|
||||
app.get('/api/stream', optionalJwt, async (req, res) => {
|
||||
const type = req.query.type;
|
||||
const ext = type === 'audio' ? '.mp3' : '.mp4';
|
||||
const mimetype = type === 'audio' ? 'audio/mp3' : 'video/mp4';
|
||||
var head;
|
||||
let optionalParams = url_api.parse(req.url,true).query;
|
||||
let id = decodeURIComponent(req.params.id);
|
||||
let file_path = req.query.file_path ? decodeURIComponent(req.query.file_path.split('?')[0]) : null;
|
||||
if (!file_path && (req.isAuthenticated() || req.can_watch)) {
|
||||
let usersFileFolder = config_api.getConfigItem('ytdl_users_base_path');
|
||||
if (optionalParams['subName']) {
|
||||
const isPlaylist = optionalParams['subPlaylist'];
|
||||
file_path = path.join(usersFileFolder, req.user.uid, 'subscriptions', (isPlaylist === 'true' ? 'playlists/' : 'channels/'),optionalParams['subName'], id + ext)
|
||||
} else {
|
||||
file_path = path.join(usersFileFolder, req.query.uuid ? req.query.uuid : req.user.uid, type, id + ext);
|
||||
}
|
||||
} else if (!file_path && 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 + ext;
|
||||
}
|
||||
let uid = decodeURIComponent(req.query.uid);
|
||||
|
||||
if (!file_path) {
|
||||
file_path = path.join(type === 'audio' ? audioFolderPath : videoFolderPath, id + ext);
|
||||
let file_path = null;
|
||||
|
||||
const multiUserMode = config_api.getConfigItem('ytdl_multi_user_mode');
|
||||
if (!multiUserMode || req.isAuthenticated() || req.can_watch) {
|
||||
const file_obj = await db_api.getVideo(uid, req.query.uuid ? req.query.uuid : (req.user ? req.user.uid : null), req.query.sub_id);
|
||||
if (file_obj) file_path = file_obj['path'];
|
||||
else file_path = null;
|
||||
}
|
||||
|
||||
const stat = fs.statSync(file_path)
|
||||
@@ -2821,11 +2831,11 @@ app.get('/api/stream/:id', optionalJwt, (req, res) => {
|
||||
: fileSize-1
|
||||
const chunksize = (end-start)+1
|
||||
const file = fs.createReadStream(file_path, {start, end})
|
||||
if (config_api.descriptors[id]) config_api.descriptors[id].push(file);
|
||||
else config_api.descriptors[id] = [file];
|
||||
if (config_api.descriptors[uid]) config_api.descriptors[uid].push(file);
|
||||
else config_api.descriptors[uid] = [file];
|
||||
file.on('close', function() {
|
||||
let index = config_api.descriptors[id].indexOf(file);
|
||||
config_api.descriptors[id].splice(index, 1);
|
||||
let index = config_api.descriptors[uid].indexOf(file);
|
||||
config_api.descriptors[uid].splice(index, 1);
|
||||
logger.debug('Successfully closed stream and removed file reference.');
|
||||
});
|
||||
head = {
|
||||
|
||||
@@ -10,12 +10,12 @@ 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) {
|
||||
exports.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, customPath = null, category = null, cropFileSettings = null) {
|
||||
exports.registerFileDB = (file_path, type, multiUserMode = null, sub = null, customPath = null, category = null, cropFileSettings = null) => {
|
||||
let db_path = null;
|
||||
const file_id = utils.removeFileExtension(file_path);
|
||||
const file_object = generateFileObject(file_id, type, customPath || multiUserMode && multiUserMode.file_path, sub);
|
||||
@@ -107,23 +107,11 @@ function generateFileObject(id, type, customPath = null, sub = null) {
|
||||
return file_obj;
|
||||
}
|
||||
|
||||
function updatePlaylist(playlist, user_uid) {
|
||||
let playlistID = playlist.id;
|
||||
let db_loc = null;
|
||||
if (user_uid) {
|
||||
db_loc = users_db.get('users').find({uid: user_uid}).get(`playlists`).find({id: playlistID});
|
||||
} else {
|
||||
db_loc = db.get(`playlists`).find({id: playlistID});
|
||||
}
|
||||
db_loc.assign(playlist).write();
|
||||
return true;
|
||||
}
|
||||
|
||||
function getAppendedBasePathSub(sub, base_path) {
|
||||
return path.join(base_path, (sub.isPlaylist ? 'playlists/' : 'channels/'), sub.name);
|
||||
}
|
||||
|
||||
function getFileDirectoriesAndDBs() {
|
||||
exports.getFileDirectoriesAndDBs = () => {
|
||||
let dirs_to_check = [];
|
||||
let subscriptions_to_check = [];
|
||||
const subscriptions_base_path = config_api.getConfigItem('ytdl_subscriptions_base_path'); // only for single-user mode
|
||||
@@ -192,8 +180,8 @@ function getFileDirectoriesAndDBs() {
|
||||
return dirs_to_check;
|
||||
}
|
||||
|
||||
async function importUnregisteredFiles() {
|
||||
const dirs_to_check = getFileDirectoriesAndDBs();
|
||||
exports.importUnregisteredFiles = async () => {
|
||||
const dirs_to_check = exports.getFileDirectoriesAndDBs();
|
||||
|
||||
// run through check list and check each file to see if it's missing from the db
|
||||
for (const dir_to_check of dirs_to_check) {
|
||||
@@ -213,7 +201,7 @@ async function importUnregisteredFiles() {
|
||||
|
||||
}
|
||||
|
||||
async function preimportUnregisteredSubscriptionFile(sub, appendedBasePath) {
|
||||
exports.preimportUnregisteredSubscriptionFile = async (sub, appendedBasePath) => {
|
||||
const preimported_file_paths = [];
|
||||
|
||||
let dbPath = null;
|
||||
@@ -236,13 +224,60 @@ async function preimportUnregisteredSubscriptionFile(sub, appendedBasePath) {
|
||||
return preimported_file_paths;
|
||||
}
|
||||
|
||||
async function getVideo(file_uid, uuid, sub_id) {
|
||||
exports.getPlaylist = async (playlist_id, user_uid = null, require_sharing = false) => {
|
||||
let playlist = null
|
||||
if (user_uid) {
|
||||
playlist = users_db.get('users').find({uid: user_uid}).get(`playlists`).find({id: playlist_id}).value();
|
||||
|
||||
// prevent unauthorized users from accessing the file info
|
||||
if (require_sharing && !playlist['sharingEnabled']) return null;
|
||||
} else {
|
||||
playlist = db.get(`playlists`).find({id: playlist_id}).value();
|
||||
}
|
||||
|
||||
// converts playlists to new UID-based schema
|
||||
if (playlist && playlist['fileNames'] && !playlist['uids']) {
|
||||
playlist['uids'] = [];
|
||||
logger.verbose(`Converting playlist ${playlist['name']} to new UID-based schema.`);
|
||||
for (let i = 0; i < playlist['fileNames'].length; i++) {
|
||||
const fileName = playlist['fileNames'][i];
|
||||
const uid = exports.getVideoUIDByID(fileName, user_uid);
|
||||
if (uid) playlist['uids'].push(uid);
|
||||
else logger.warn(`Failed to convert file with name ${fileName} to its UID while converting playlist ${playlist['name']} to the new UID-based schema. The original file is likely missing/deleted and it will be skipped.`);
|
||||
}
|
||||
delete playlist['fileNames'];
|
||||
exports.updatePlaylist(playlist, user_uid);
|
||||
}
|
||||
|
||||
return playlist;
|
||||
}
|
||||
|
||||
exports.updatePlaylist = (playlist, user_uid = null) => {
|
||||
let playlistID = playlist.id;
|
||||
let db_loc = null;
|
||||
if (user_uid) {
|
||||
db_loc = users_db.get('users').find({uid: user_uid}).get(`playlists`).find({id: playlistID});
|
||||
} else {
|
||||
db_loc = db.get(`playlists`).find({id: playlistID});
|
||||
}
|
||||
db_loc.assign(playlist).write();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Video ID is basically just the file name without the base path and file extension - this method helps us get away from that
|
||||
exports.getVideoUIDByID = (file_id, uuid = null) => {
|
||||
const base_db_path = uuid ? users_db.get('users').find({uid: uuid}) : db;
|
||||
const file_obj = base_db_path.get('files').find({id: file_id}).value();
|
||||
return file_obj ? file_obj['uid'] : null;
|
||||
}
|
||||
|
||||
exports.getVideo = async (file_uid, uuid, sub_id) => {
|
||||
const base_db_path = uuid ? users_db.get('users').find({uid: uuid}) : db;
|
||||
const sub_db_path = sub_id ? base_db_path.get('subscriptions').find({id: sub_id}).get('videos') : base_db_path.get('files');
|
||||
return sub_db_path.find({uid: file_uid}).value();
|
||||
}
|
||||
|
||||
async function setVideoProperty(file_uid, assignment_obj, uuid, sub_id) {
|
||||
exports.setVideoProperty = async (file_uid, assignment_obj, uuid, sub_id) => {
|
||||
const base_db_path = uuid ? users_db.get('users').find({uid: uuid}) : db;
|
||||
const sub_db_path = sub_id ? base_db_path.get('subscriptions').find({id: sub_id}).get('videos') : base_db_path.get('files');
|
||||
const file_db_path = sub_db_path.find({uid: file_uid});
|
||||
@@ -251,14 +286,3 @@ async function setVideoProperty(file_uid, assignment_obj, uuid, sub_id) {
|
||||
}
|
||||
sub_db_path.find({uid: file_uid}).assign(assignment_obj).write();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
initialize: initialize,
|
||||
registerFileDB: registerFileDB,
|
||||
updatePlaylist: updatePlaylist,
|
||||
getFileDirectoriesAndDBs: getFileDirectoriesAndDBs,
|
||||
importUnregisteredFiles: importUnregisteredFiles,
|
||||
preimportUnregisteredSubscriptionFile: preimportUnregisteredSubscriptionFile,
|
||||
getVideo: getVideo,
|
||||
setVideoProperty: setVideoProperty
|
||||
}
|
||||
|
||||
@@ -57,12 +57,11 @@ export class CustomPlaylistsComponent implements OnInit {
|
||||
|
||||
if (playlist) {
|
||||
if (this.postsService.config['Extra']['download_only_mode']) {
|
||||
this.downloading_content[type][playlistID] = true;
|
||||
this.downloadPlaylist(playlist.fileNames, type, playlist.name, playlistID);
|
||||
this.downloadPlaylist(playlist.id, playlist.name);
|
||||
} else {
|
||||
localStorage.setItem('player_navigator', this.router.url);
|
||||
const fileNames = playlist.fileNames;
|
||||
this.router.navigate(['/player', {fileNames: fileNames.join('|nvr|'), type: type, id: playlistID, uid: playlistID, auto: playlist.auto}]);
|
||||
this.router.navigate(['/player', {playlist_id: playlistID, auto: playlist.auto}]);
|
||||
}
|
||||
} else {
|
||||
// playlist not found
|
||||
@@ -70,11 +69,12 @@ export class CustomPlaylistsComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
downloadPlaylist(fileNames, type, zipName = null, playlistID = null) {
|
||||
this.postsService.downloadFileFromServer(fileNames, type, zipName).subscribe(res => {
|
||||
if (playlistID) { this.downloading_content[type][playlistID] = false };
|
||||
const blob: Blob = res;
|
||||
saveAs(blob, zipName + '.zip');
|
||||
downloadPlaylist(playlist_id, playlist_name) {
|
||||
this.downloading_content[playlist_id] = true;
|
||||
this.postsService.downloadPlaylistFromServer(playlist_id).subscribe(res => {
|
||||
this.downloading_content[playlist_id] = false;
|
||||
const blob: any = res;
|
||||
saveAs(blob, playlist_name + '.zip');
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@@ -201,8 +201,7 @@ export class RecentVideosComponent implements OnInit {
|
||||
const type = file.isAudio ? 'audio' : 'video';
|
||||
const ext = type === 'audio' ? '.mp3' : '.mp4'
|
||||
const sub = this.postsService.getSubscriptionByID(file.sub_id);
|
||||
this.postsService.downloadFileFromServer(file.id, type, null, null, sub.name, sub.isPlaylist,
|
||||
this.postsService.user ? this.postsService.user.uid : null, null).subscribe(res => {
|
||||
this.postsService.downloadFileFromServer(file.uid).subscribe(res => {
|
||||
const blob: Blob = res;
|
||||
saveAs(blob, file.id + ext);
|
||||
}, err => {
|
||||
@@ -215,7 +214,7 @@ export class RecentVideosComponent implements OnInit {
|
||||
const ext = type === 'audio' ? '.mp3' : '.mp4'
|
||||
const name = file.id;
|
||||
this.downloading_content[type][name] = true;
|
||||
this.postsService.downloadFileFromServer(name, type).subscribe(res => {
|
||||
this.postsService.downloadFileFromServer(file.uid).subscribe(res => {
|
||||
this.downloading_content[type][name] = false;
|
||||
const blob: Blob = res;
|
||||
saveAs(blob, decodeURIComponent(name) + ext);
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<mat-label *ngIf="type === 'audio'"><ng-container i18n="Audio files title">Audio files</ng-container></mat-label>
|
||||
<mat-label *ngIf="type === 'video'"><ng-container i18n="Videos title">Videos</ng-container></mat-label>
|
||||
<mat-select [formControl]="filesSelect" multiple required aria-required>
|
||||
<ng-container *ngIf="filesToSelectFrom"><mat-option *ngFor="let file of filesToSelectFrom" [value]="file.id">{{file.id}}</mat-option></ng-container>
|
||||
<ng-container *ngIf="filesToSelectFrom"><mat-option *ngFor="let file of filesToSelectFrom" [value]="file.uid">{{file.id}}</mat-option></ng-container>
|
||||
<ng-container *ngIf="audiosToSelectFrom && type === 'audio'"><mat-option *ngFor="let file of audiosToSelectFrom" [value]="file.id">{{file.id}}</mat-option></ng-container>
|
||||
<ng-container *ngIf="videosToSelectFrom && type === 'video'"><mat-option *ngFor="let file of videosToSelectFrom" [value]="file.id">{{file.id}}</mat-option></ng-container>
|
||||
</mat-select>
|
||||
|
||||
@@ -33,7 +33,7 @@ export class ShareMediaDialogComponent implements OnInit {
|
||||
this.is_playlist = this.data.is_playlist;
|
||||
this.current_timestamp = (this.data.current_timestamp / 1000).toFixed(2);
|
||||
|
||||
const arg = (this.is_playlist ? ';id=' : ';uid=');
|
||||
const arg = (this.is_playlist ? ';playlist_id=' : ';uid=');
|
||||
this.default_share_url = window.location.href.split(';')[0] + arg + this.uid;
|
||||
if (this.uuid) {
|
||||
this.default_share_url += ';uuid=' + this.uuid;
|
||||
|
||||
@@ -355,7 +355,7 @@ export class MainComponent implements OnInit {
|
||||
if (playlist) {
|
||||
if (this.downloadOnlyMode) {
|
||||
this.downloading_content[type][playlistID] = true;
|
||||
this.downloadPlaylist(playlist.fileNames, type, playlist.name, playlistID);
|
||||
this.downloadPlaylist(playlist);
|
||||
} else {
|
||||
localStorage.setItem('player_navigator', this.router.url);
|
||||
const fileNames = playlist.fileNames;
|
||||
@@ -626,41 +626,41 @@ export class MainComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
downloadAudioFile(name) {
|
||||
this.downloading_content['audio'][name] = true;
|
||||
this.postsService.downloadFileFromServer(name, 'audio').subscribe(res => {
|
||||
this.downloading_content['audio'][name] = false;
|
||||
downloadAudioFile(file) {
|
||||
this.downloading_content['audio'][file.id] = true;
|
||||
this.postsService.downloadFileFromServer(file.uid).subscribe(res => {
|
||||
this.downloading_content['audio'][file.id] = false;
|
||||
const blob: Blob = res;
|
||||
saveAs(blob, decodeURIComponent(name) + '.mp3');
|
||||
saveAs(blob, decodeURIComponent(file.id) + '.mp3');
|
||||
|
||||
if (!this.fileManagerEnabled) {
|
||||
// tell server to delete the file once downloaded
|
||||
this.postsService.deleteFile(name, 'video').subscribe(delRes => {
|
||||
this.postsService.deleteFile(file.uid).subscribe(delRes => {
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
downloadVideoFile(name) {
|
||||
this.downloading_content['video'][name] = true;
|
||||
this.postsService.downloadFileFromServer(name, 'video').subscribe(res => {
|
||||
this.downloading_content['video'][name] = false;
|
||||
downloadVideoFile(file) {
|
||||
this.downloading_content['video'][file.id] = true;
|
||||
this.postsService.downloadFileFromServer(file.uid).subscribe(res => {
|
||||
this.downloading_content['video'][file.id] = false;
|
||||
const blob: Blob = res;
|
||||
saveAs(blob, decodeURIComponent(name) + '.mp4');
|
||||
saveAs(blob, decodeURIComponent(file.id) + '.mp4');
|
||||
|
||||
if (!this.fileManagerEnabled) {
|
||||
// tell server to delete the file once downloaded
|
||||
this.postsService.deleteFile(name, 'audio').subscribe(delRes => {
|
||||
this.postsService.deleteFile(file.uid).subscribe(delRes => {
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
downloadPlaylist(fileNames, type, zipName = null, playlistID = null) {
|
||||
this.postsService.downloadFileFromServer(fileNames, type, zipName).subscribe(res => {
|
||||
if (playlistID) { this.downloading_content[type][playlistID] = false };
|
||||
downloadPlaylist(playlist) {
|
||||
this.postsService.downloadFileFromServer(playlist.id, null, true).subscribe(res => {
|
||||
if (playlist.id) { this.downloading_content[playlist.type][playlist.id] = false };
|
||||
const blob: Blob = res;
|
||||
saveAs(blob, zipName + '.zip');
|
||||
saveAs(blob, playlist.name + '.zip');
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@@ -29,12 +29,11 @@
|
||||
<div class="col-2">
|
||||
<ng-container *ngIf="playlist.length > 1">
|
||||
<button (click)="downloadContent()" [disabled]="downloading" mat-icon-button><mat-icon>save</mat-icon><mat-spinner *ngIf="downloading" class="spinner" [diameter]="35"></mat-spinner></button>
|
||||
<button *ngIf="!id" (click)="namePlaylistDialog()" mat-icon-button><mat-icon>favorite</mat-icon></button>
|
||||
<button *ngIf="!is_shared && id && (!postsService.isLoggedIn || postsService.permissions.includes('sharing'))" (click)="openShareDialog()" mat-icon-button><mat-icon>share</mat-icon></button>
|
||||
<button *ngIf="!postsService.isLoggedIn || postsService.permissions.includes('sharing')" (click)="openShareDialog()" mat-icon-button><mat-icon>share</mat-icon></button>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="playlist.length === 1">
|
||||
<button (click)="downloadFile()" [disabled]="downloading" mat-icon-button><mat-icon>save</mat-icon><mat-spinner *ngIf="downloading" class="spinner" [diameter]="35"></mat-spinner></button>
|
||||
<button *ngIf="!is_shared && uid && uid !== 'false' && type !== 'subscription' && (!postsService.isLoggedIn || postsService.permissions.includes('sharing'))" (click)="openShareDialog()" mat-icon-button><mat-icon>share</mat-icon></button>
|
||||
<button *ngIf="type !== 'subscription' && (!postsService.isLoggedIn || postsService.permissions.includes('sharing'))" (click)="openShareDialog()" mat-icon-button><mat-icon>share</mat-icon></button>
|
||||
<button (click)="openFileInfoDialog()" *ngIf="db_file" mat-icon-button><mat-icon>info</mat-icon></button>
|
||||
</ng-container>
|
||||
<button *ngIf="db_file && db_file.url.includes('twitch.tv/videos/') && postsService['config']['API']['use_twitch_API']" (click)="drawer.toggle()" mat-icon-button><mat-icon>chat</mat-icon></button>
|
||||
@@ -47,6 +46,9 @@
|
||||
<mat-button-toggle cdkDrag *ngFor="let playlist_item of playlist; let i = index" [checked]="currentItem.title === playlist_item.title" (click)="onClickPlaylistItem(playlist_item, i)" class="toggle-button" [value]="playlist_item.title">{{playlist_item.label}}</mat-button-toggle>
|
||||
</mat-button-toggle-group>
|
||||
</div>
|
||||
|
||||
<app-concurrent-stream *ngIf="db_file && api && postsService.config" (setPlaybackRate)="setPlaybackRate($event)" (togglePlayback)="togglePlayback($event)" (setPlaybackTimestamp)="setPlaybackTimestamp($event)" [playing]="api.state === 'playing'" [uid]="uid" [playback_timestamp]="api.time.current/1000" [server_mode]="!postsService.config.Advanced.multi_user_mode || postsService.isLoggedIn"></app-concurrent-stream>
|
||||
|
||||
<mat-drawer #drawer class="example-sidenav" mode="side" position="end" [opened]="db_file && db_file['chat_exists'] && postsService['config']['API']['use_twitch_API']">
|
||||
<ng-container *ngIf="api_ready && db_file && db_file.url.includes('twitch.tv/videos/')">
|
||||
<app-twitch-chat #twitchchat [db_file]="db_file" [current_timestamp]="api.currentTime" [sub]="subscription"></app-twitch-chat>
|
||||
|
||||
@@ -36,18 +36,16 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
api_ready = false;
|
||||
|
||||
// params
|
||||
fileNames: string[];
|
||||
uids: string[];
|
||||
type: string;
|
||||
id = null; // used for playlists (not subscription)
|
||||
playlist_id = null; // used for playlists (not subscription)
|
||||
uid = null; // used for non-subscription files (audio, video, playlist)
|
||||
subscription = null;
|
||||
subscriptionName = null;
|
||||
sub_id = null;
|
||||
subPlaylist = null;
|
||||
uuid = null; // used for sharing in multi-user mode, uuid is the user that downloaded the video
|
||||
timestamp = null;
|
||||
|
||||
is_shared = false;
|
||||
|
||||
db_playlist = null;
|
||||
db_file = null;
|
||||
|
||||
@@ -56,8 +54,6 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
videoFolderPath = null;
|
||||
subscriptionFolderPath = null;
|
||||
|
||||
sharingEnabled = null;
|
||||
|
||||
// url-mode params
|
||||
url = null;
|
||||
name = null;
|
||||
@@ -79,11 +75,9 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
ngOnInit(): void {
|
||||
this.innerWidth = window.innerWidth;
|
||||
|
||||
this.type = this.route.snapshot.paramMap.get('type');
|
||||
this.id = this.route.snapshot.paramMap.get('id');
|
||||
this.playlist_id = this.route.snapshot.paramMap.get('playlist_id');
|
||||
this.uid = this.route.snapshot.paramMap.get('uid');
|
||||
this.subscriptionName = this.route.snapshot.paramMap.get('subscriptionName');
|
||||
this.subPlaylist = this.route.snapshot.paramMap.get('subPlaylist');
|
||||
this.sub_id = this.route.snapshot.paramMap.get('sub_id');
|
||||
this.url = this.route.snapshot.paramMap.get('url');
|
||||
this.name = this.route.snapshot.paramMap.get('name');
|
||||
this.uuid = this.route.snapshot.paramMap.get('uuid');
|
||||
@@ -120,19 +114,14 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.audioFolderPath = this.postsService.config['Downloader']['path-audio'];
|
||||
this.videoFolderPath = this.postsService.config['Downloader']['path-video'];
|
||||
this.subscriptionFolderPath = this.postsService.config['Subscriptions']['subscriptions_base_path'];
|
||||
this.fileNames = this.route.snapshot.paramMap.get('fileNames') ? this.route.snapshot.paramMap.get('fileNames').split('|nvr|') : null;
|
||||
|
||||
if (!this.fileNames && !this.type) {
|
||||
this.is_shared = true;
|
||||
}
|
||||
|
||||
if (this.uid && !this.id) {
|
||||
this.getFile();
|
||||
} else if (this.id) {
|
||||
this.getPlaylistFiles();
|
||||
} else if (this.subscriptionName) {
|
||||
if (this.sub_id) {
|
||||
this.getSubscription();
|
||||
}
|
||||
} else if (this.playlist_id) {
|
||||
this.getPlaylistFiles();
|
||||
} else if (this.uid) {
|
||||
this.getFile();
|
||||
}
|
||||
|
||||
if (this.url) {
|
||||
// if a url is given, just stream the URL
|
||||
@@ -147,14 +136,10 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.currentItem = this.playlist[0];
|
||||
this.currentIndex = 0;
|
||||
this.show_player = true;
|
||||
} else if (this.fileNames && !this.subscriptionName) {
|
||||
this.show_player = true;
|
||||
this.parseFileNames();
|
||||
}
|
||||
}
|
||||
|
||||
getFile() {
|
||||
const already_has_filenames = !!this.fileNames;
|
||||
this.postsService.getFile(this.uid, null, this.uuid).subscribe(res => {
|
||||
this.db_file = res['file'];
|
||||
if (!this.db_file) {
|
||||
@@ -165,45 +150,32 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
console.error('Failed to increment view count');
|
||||
console.error(err);
|
||||
});
|
||||
this.sharingEnabled = this.db_file.sharingEnabled;
|
||||
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.uuid) {
|
||||
this.show_player = true;
|
||||
} else if (!already_has_filenames) {
|
||||
this.openSnackBar('Error: Sharing has been disabled for this video!', 'Dismiss');
|
||||
}
|
||||
// regular video/audio file (not playlist)
|
||||
this.uids = [this.db_file['uid']];
|
||||
this.type = this.db_file['isAudio'] ? 'audio' : 'video';
|
||||
this.parseFileNames();
|
||||
});
|
||||
}
|
||||
|
||||
getSubscription() {
|
||||
this.postsService.getSubscription(null, this.subscriptionName).subscribe(res => {
|
||||
this.postsService.getSubscription(this.sub_id).subscribe(res => {
|
||||
const subscription = res['subscription'];
|
||||
this.subscription = subscription;
|
||||
if (this.fileNames) {
|
||||
subscription.videos.forEach(video => {
|
||||
if (video['id'] === this.fileNames[0]) {
|
||||
this.db_file = video;
|
||||
this.postsService.incrementViewCount(this.db_file['uid'], this.subscription['id'], this.uuid).subscribe(res => {}, err => {
|
||||
console.error('Failed to increment view count');
|
||||
console.error(err);
|
||||
});
|
||||
this.show_player = true;
|
||||
this.parseFileNames();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log('no file name specified');
|
||||
}
|
||||
this.type === this.subscription.type;
|
||||
subscription.videos.forEach(video => {
|
||||
if (video['uid'] === this.uid) {
|
||||
this.db_file = video;
|
||||
this.postsService.incrementViewCount(this.db_file['uid'], this.sub_id, this.uuid).subscribe(res => {}, err => {
|
||||
console.error('Failed to increment view count');
|
||||
console.error(err);
|
||||
});
|
||||
this.uids = this.db_file['uid'];
|
||||
this.show_player = true;
|
||||
this.parseFileNames();
|
||||
}
|
||||
});
|
||||
}, err => {
|
||||
this.openSnackBar(`Failed to find subscription ${this.subscriptionName}`, 'Dismiss');
|
||||
this.openSnackBar(`Failed to find subscription ${this.sub_id}`, 'Dismiss');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -212,10 +184,10 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.show_player = true;
|
||||
return;
|
||||
}
|
||||
this.postsService.getPlaylist(this.id, null, this.uuid).subscribe(res => {
|
||||
this.postsService.getPlaylist(this.playlist_id, this.uuid, true).subscribe(res => {
|
||||
if (res['playlist']) {
|
||||
this.db_playlist = res['playlist'];
|
||||
this.fileNames = this.db_playlist['fileNames'];
|
||||
this.uids = this.db_playlist.uids;
|
||||
this.type = res['type'];
|
||||
this.show_player = true;
|
||||
this.parseFileNames();
|
||||
@@ -231,60 +203,43 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
let fileType = null;
|
||||
if (this.type === 'audio') {
|
||||
fileType = 'audio/mp3';
|
||||
} else if (this.type === 'video') {
|
||||
fileType = 'video/mp4';
|
||||
} else {
|
||||
// error
|
||||
console.error('Must have valid file type! Use \'audio\', \'video\', or \'subscription\'.');
|
||||
fileType = 'video/mp4';
|
||||
}
|
||||
this.playlist = [];
|
||||
for (let i = 0; i < this.fileNames.length; i++) {
|
||||
const fileName = this.fileNames[i];
|
||||
let baseLocation = null;
|
||||
let fullLocation = null;
|
||||
for (let i = 0; i < this.uids.length; i++) {
|
||||
const uid = this.uids[i];
|
||||
|
||||
// adds user token if in multi-user-mode
|
||||
const uuid_str = this.uuid ? `&uuid=${this.uuid}` : '';
|
||||
const uid_str = (this.id || !this.db_file) ? '' : `&uid=${this.db_file.uid}`;
|
||||
const type_str = (this.type || !this.db_file) ? `&type=${this.type}` : `&type=${this.db_file.type}`
|
||||
const id_str = this.id ? `&id=${this.id}` : '';
|
||||
const file_path_str = (!this.db_file) ? '' : `&file_path=${encodeURIComponent(this.db_file.path)}`;
|
||||
const file_obj = this.playlist_id ? this.db_playlist['file_objs'][i] : this.db_file;
|
||||
|
||||
if (!this.subscriptionName) {
|
||||
baseLocation = 'stream/';
|
||||
fullLocation = this.baseStreamPath + baseLocation + encodeURIComponent(fileName) + `?test=test${type_str}${file_path_str}`;
|
||||
} else {
|
||||
// default to video but include subscription name param
|
||||
baseLocation = 'stream/';
|
||||
fullLocation = this.baseStreamPath + baseLocation + encodeURIComponent(fileName) + '?subName=' + this.subscriptionName +
|
||||
'&subPlaylist=' + this.subPlaylist + `${file_path_str}${type_str}`;
|
||||
}
|
||||
let baseLocation = 'stream/';
|
||||
let fullLocation = this.baseStreamPath + baseLocation + `?test=test&uid=${file_obj['uid']}`;
|
||||
|
||||
if (this.postsService.isLoggedIn) {
|
||||
fullLocation += (this.subscriptionName ? '&' : '&') + `jwt=${this.postsService.token}`;
|
||||
if (this.is_shared) { fullLocation += `${uuid_str}${uid_str}${type_str}${id_str}`; }
|
||||
} else if (this.is_shared) {
|
||||
fullLocation += (this.subscriptionName ? '&' : '?') + `test=test${uuid_str}${uid_str}${type_str}${id_str}`;
|
||||
fullLocation += `&jwt=${this.postsService.token}`;
|
||||
}
|
||||
// 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;
|
||||
|
||||
if (this.uuid) {
|
||||
fullLocation += `&uuid=${this.uuid}`;
|
||||
}
|
||||
|
||||
if (this.sub_id) {
|
||||
fullLocation += `&sub_id=${this.sub_id}`;
|
||||
} else if (this.playlist_id) {
|
||||
fullLocation += `&playlist_id=${this.playlist_id}`;
|
||||
}
|
||||
|
||||
const mediaObject: IMedia = {
|
||||
title: fileName,
|
||||
title: file_obj['title'],
|
||||
src: fullLocation,
|
||||
type: fileType,
|
||||
label: label
|
||||
label: file_obj['title']
|
||||
}
|
||||
this.playlist.push(mediaObject);
|
||||
}
|
||||
this.currentItem = this.playlist[this.currentIndex];
|
||||
this.original_playlist = JSON.stringify(this.playlist);
|
||||
this.show_player = true;
|
||||
}
|
||||
|
||||
onPlayerReady(api: VgApiService) {
|
||||
@@ -361,8 +316,7 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
|
||||
const zipName = fileNames[0].split(' ')[0] + fileNames[1].split(' ')[0];
|
||||
this.downloading = true;
|
||||
this.postsService.downloadFileFromServer(fileNames, this.type, zipName, null, null, null, null,
|
||||
!this.uuid ? this.postsService.user.uid : this.uuid, this.id).subscribe(res => {
|
||||
this.postsService.downloadFileFromServer(this.playlist_id, this.uuid, true).subscribe(res => {
|
||||
this.downloading = false;
|
||||
const blob: Blob = res;
|
||||
saveAs(blob, zipName + '.zip');
|
||||
@@ -376,8 +330,7 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
const ext = (this.type === 'audio') ? '.mp3' : '.mp4';
|
||||
const filename = this.playlist[0].title;
|
||||
this.downloading = true;
|
||||
this.postsService.downloadFileFromServer(filename, this.type, null, null, this.subscriptionName, this.subPlaylist,
|
||||
this.is_shared ? this.db_file['uid'] : null, this.uuid).subscribe(res => {
|
||||
this.postsService.downloadFileFromServer(this.uid, this.uuid, false).subscribe(res => {
|
||||
this.downloading = false;
|
||||
const blob: Blob = res;
|
||||
saveAs(blob, filename + ext);
|
||||
@@ -387,50 +340,10 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
namePlaylistDialog() {
|
||||
const done = new EventEmitter<any>();
|
||||
const dialogRef = this.dialog.open(InputDialogComponent, {
|
||||
width: '300px',
|
||||
data: {
|
||||
inputTitle: 'Name the playlist',
|
||||
inputPlaceholder: 'Name',
|
||||
submitText: 'Favorite',
|
||||
doneEmitter: done
|
||||
}
|
||||
});
|
||||
|
||||
done.subscribe(name => {
|
||||
|
||||
// Eventually do additional checks on name
|
||||
if (name) {
|
||||
const fileNames = this.getFileNames();
|
||||
this.postsService.createPlaylist(name, fileNames, this.type, null).subscribe(res => {
|
||||
if (res['success']) {
|
||||
dialogRef.close();
|
||||
const new_playlist = res['new_playlist'];
|
||||
this.db_playlist = new_playlist;
|
||||
this.openSnackBar('Playlist \'' + name + '\' successfully created!', '')
|
||||
this.playlistPostCreationHandler(new_playlist.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
createPlaylist(name) {
|
||||
this.postsService.createPlaylist(name, this.fileNames, this.type, null).subscribe(res => {
|
||||
if (res['success']) {
|
||||
console.log('Success!');
|
||||
}
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
playlistPostCreationHandler(playlistID) {
|
||||
// changes the route without moving from the current view or
|
||||
// triggering a navigation event
|
||||
this.id = playlistID;
|
||||
this.playlist_id = playlistID;
|
||||
this.router.navigateByUrl(this.router.url + ';id=' + playlistID);
|
||||
}
|
||||
|
||||
@@ -445,11 +358,11 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
updatePlaylist() {
|
||||
const fileNames = this.getFileNames();
|
||||
this.playlist_updating = true;
|
||||
this.postsService.updatePlaylistFiles(this.id, fileNames, this.type).subscribe(res => {
|
||||
this.postsService.updatePlaylistFiles(this.playlist_id, fileNames, this.type).subscribe(res => {
|
||||
this.playlist_updating = false;
|
||||
if (res['success']) {
|
||||
const fileNamesEncoded = fileNames.join('|nvr|');
|
||||
this.router.navigate(['/player', {fileNames: fileNamesEncoded, type: this.type, id: this.id}]);
|
||||
this.router.navigate(['/player', {fileNames: fileNamesEncoded, type: this.type, id: this.playlist_id}]);
|
||||
this.openSnackBar('Successfully updated playlist.', '');
|
||||
this.original_playlist = JSON.stringify(this.playlist);
|
||||
} else {
|
||||
@@ -461,10 +374,10 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
openShareDialog() {
|
||||
const dialogRef = this.dialog.open(ShareMediaDialogComponent, {
|
||||
data: {
|
||||
uid: this.id ? this.id : this.uid,
|
||||
uid: this.playlist_id ? this.playlist_id : this.uid,
|
||||
type: this.type,
|
||||
sharing_enabled: this.id ? this.db_playlist.sharingEnabled : this.db_file.sharingEnabled,
|
||||
is_playlist: !!this.id,
|
||||
sharing_enabled: this.playlist_id ? this.db_playlist.sharingEnabled : this.db_file.sharingEnabled,
|
||||
is_playlist: !!this.playlist_id,
|
||||
uuid: this.postsService.isLoggedIn ? this.postsService.user.uid : null,
|
||||
current_timestamp: this.api.time.current
|
||||
},
|
||||
@@ -472,7 +385,7 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(res => {
|
||||
if (!this.id) {
|
||||
if (!this.playlist_id) {
|
||||
this.getFile();
|
||||
} else {
|
||||
this.getPlaylistFiles();
|
||||
@@ -489,6 +402,22 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
})
|
||||
}
|
||||
|
||||
setPlaybackTimestamp(time) {
|
||||
this.api.seekTime(time);
|
||||
}
|
||||
|
||||
togglePlayback(to_play) {
|
||||
if (to_play) {
|
||||
this.api.play();
|
||||
} else {
|
||||
this.api.pause();
|
||||
}
|
||||
}
|
||||
|
||||
setPlaybackRate(speed) {
|
||||
this.api.playbackRate = speed;
|
||||
}
|
||||
|
||||
// snackbar helper
|
||||
public openSnackBar(message: string, action: string) {
|
||||
this.snackBar.open(message, action, {
|
||||
|
||||
@@ -219,8 +219,8 @@ export class PostsService implements CanActivate {
|
||||
return this.http.post(this.path + 'setConfig', {new_config_file: config}, this.httpOptions);
|
||||
}
|
||||
|
||||
deleteFile(uid: string, type: string, blacklistMode = false) {
|
||||
return this.http.post(this.path + 'deleteFile', {uid: uid, type: type, blacklistMode: blacklistMode}, this.httpOptions);
|
||||
deleteFile(uid: string, blacklistMode = false) {
|
||||
return this.http.post(this.path + 'deleteFile', {uid: uid, blacklistMode: blacklistMode}, this.httpOptions);
|
||||
}
|
||||
|
||||
getMp3s() {
|
||||
@@ -247,22 +247,30 @@ export class PostsService implements CanActivate {
|
||||
return this.http.post(this.path + 'downloadTwitchChatByVODID', {id: id, type: type, vodId: vodId, uuid: uuid, sub: sub}, this.httpOptions);
|
||||
}
|
||||
|
||||
downloadFileFromServer(fileName, type, outputName = null, fullPathProvided = null, subscriptionName = null, subPlaylist = null,
|
||||
uid = null, uuid = null, id = null) {
|
||||
return this.http.post(this.path + 'downloadFile', {fileNames: fileName,
|
||||
type: type,
|
||||
zip_mode: Array.isArray(fileName),
|
||||
outputName: outputName,
|
||||
fullPathProvided: fullPathProvided,
|
||||
subscriptionName: subscriptionName,
|
||||
subPlaylist: subPlaylist,
|
||||
uuid: uuid,
|
||||
downloadFileFromServer(uid, uuid = null, is_playlist = false) {
|
||||
return this.http.post(this.path + 'downloadFile', {
|
||||
uid: uid,
|
||||
id: id
|
||||
uuid: uuid,
|
||||
is_playlist: is_playlist
|
||||
},
|
||||
{responseType: 'blob', params: this.httpOptions.params});
|
||||
}
|
||||
|
||||
downloadPlaylistFromServer(playlist_id, uuid = null) {
|
||||
return this.http.post(this.path + 'downloadPlaylist', {playlist_id: playlist_id, uuid: uuid});
|
||||
}
|
||||
|
||||
checkConcurrentStream(uid) {
|
||||
return this.http.post(this.path + 'checkConcurrentStream', {uid: uid}, this.httpOptions);
|
||||
}
|
||||
|
||||
updateConcurrentStream(uid, playback_timestamp, unix_timestamp, playing) {
|
||||
return this.http.post(this.path + 'updateConcurrentStream', {uid: uid,
|
||||
playback_timestamp: playback_timestamp,
|
||||
unix_timestamp: unix_timestamp,
|
||||
playing: playing}, this.httpOptions);
|
||||
}
|
||||
|
||||
uploadCookiesFile(fileFormData) {
|
||||
return this.http.post(this.path + 'uploadCookies', fileFormData, this.httpOptions);
|
||||
}
|
||||
@@ -299,17 +307,18 @@ export class PostsService implements CanActivate {
|
||||
return this.http.post(this.path + 'disableSharing', {uid: uid, type: type, is_playlist: is_playlist}, this.httpOptions);
|
||||
}
|
||||
|
||||
createPlaylist(playlistName, fileNames, type, thumbnailURL, duration = null) {
|
||||
createPlaylist(playlistName, uids, type, thumbnailURL, duration = null) {
|
||||
return this.http.post(this.path + 'createPlaylist', {playlistName: playlistName,
|
||||
fileNames: fileNames,
|
||||
uids: uids,
|
||||
type: type,
|
||||
thumbnailURL: thumbnailURL,
|
||||
duration: duration}, this.httpOptions);
|
||||
}
|
||||
|
||||
getPlaylist(playlistID, type, uuid = null) {
|
||||
return this.http.post(this.path + 'getPlaylist', {playlistID: playlistID,
|
||||
type: type, uuid: uuid}, this.httpOptions);
|
||||
getPlaylist(playlist_id, uuid = null, include_file_metadata = false) {
|
||||
return this.http.post(this.path + 'getPlaylist', {playlist_id: playlist_id,
|
||||
uuid: uuid,
|
||||
include_file_metadata: include_file_metadata}, this.httpOptions);
|
||||
}
|
||||
|
||||
updatePlaylist(playlist) {
|
||||
|
||||
@@ -42,7 +42,7 @@ export class SubscriptionFileCardComponent implements OnInit {
|
||||
|
||||
goToFile() {
|
||||
const emit_obj = {
|
||||
name: this.file.id,
|
||||
uid: this.file.uid,
|
||||
url: this.file.requested_formats ? this.file.requested_formats[0].url : this.file.url
|
||||
}
|
||||
this.goToFileEmit.emit(emit_obj);
|
||||
|
||||
@@ -103,15 +103,14 @@ export class SubscriptionComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
goToFile(emit_obj) {
|
||||
const name = emit_obj['name'];
|
||||
const uid = emit_obj['uid'];
|
||||
const url = emit_obj['url'];
|
||||
localStorage.setItem('player_navigator', this.router.url);
|
||||
if (this.subscription.streamingOnly) {
|
||||
this.router.navigate(['/player', {name: name, url: url}]);
|
||||
this.router.navigate(['/player', {uid: uid, url: url}]);
|
||||
} else {
|
||||
this.router.navigate(['/player', {fileNames: name,
|
||||
type: this.subscription.type ? this.subscription.type : 'video', subscriptionName: this.subscription.name,
|
||||
subPlaylist: this.subscription.isPlaylist}]);
|
||||
this.router.navigate(['/player', {uid: uid,
|
||||
sub_id: this.subscription.id}]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,14 +153,15 @@ export class SubscriptionComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
this.downloading = true;
|
||||
this.postsService.downloadFileFromServer(fileNames, 'video', this.subscription.name, true).subscribe(res => {
|
||||
// TODO: add download subscription route
|
||||
/*this.postsService.downloadFileFromServer(fileNames, 'video', this.subscription.name, true).subscribe(res => {
|
||||
this.downloading = false;
|
||||
const blob: Blob = res;
|
||||
saveAs(blob, this.subscription.name + '.zip');
|
||||
}, err => {
|
||||
console.log(err);
|
||||
this.downloading = false;
|
||||
});
|
||||
});*/
|
||||
}
|
||||
|
||||
editSubscription() {
|
||||
|
||||
Reference in New Issue
Block a user