Added description to player component and simplified the database by un-splitting videos and playlists by type

updated-player
Isaac Abadi 5 years ago
parent c6fc5352c5
commit 4f693d4eda

@ -13,7 +13,7 @@ var express = require("express");
var bodyParser = require("body-parser"); var bodyParser = require("body-parser");
var archiver = require('archiver'); var archiver = require('archiver');
var unzipper = require('unzipper'); var unzipper = require('unzipper');
var db_api = require('./db') var db_api = require('./db');
var utils = require('./utils') var utils = require('./utils')
var mergeFiles = require('merge-files'); var mergeFiles = require('merge-files');
const low = require('lowdb') const low = require('lowdb')
@ -87,14 +87,8 @@ categories_api.initialize(db, users_db, logger, db_api);
// Set some defaults // Set some defaults
db.defaults( db.defaults(
{ {
playlists: { playlists: [],
audio: [], files: [],
video: []
},
files: {
audio: [],
video: []
},
configWriteFlag: false, configWriteFlag: false,
downloads: {}, downloads: {},
subscriptions: [], subscriptions: [],
@ -218,10 +212,12 @@ async function checkMigrations() {
// 4.1->4.2 migration // 4.1->4.2 migration
const add_description_migration_complete = false; // db.get('add_description_migration_complete').value(); const add_description_migration_complete = db.get('add_description_migration_complete').value();
if (!add_description_migration_complete) { if (!add_description_migration_complete) {
logger.info('Beginning migration: 4.1->4.2+') logger.info('Beginning migration: 4.1->4.2+')
const success = await addMetadataPropertyToDB('description'); let success = await simplifyDBFileStructure();
success = success && await addMetadataPropertyToDB('view_count');
success = success && await addMetadataPropertyToDB('description');
if (success) { logger.info('4.1->4.2+ migration complete!'); } if (success) { logger.info('4.1->4.2+ migration complete!'); }
else { logger.error('Migration failed: 4.1->4.2+'); } else { logger.error('Migration failed: 4.1->4.2+'); }
} }
@ -229,6 +225,7 @@ async function checkMigrations() {
return true; return true;
} }
/*
async function runFilesToDBMigration() { async function runFilesToDBMigration() {
try { try {
let mp3s = await getMp3s(); let mp3s = await getMp3s();
@ -260,6 +257,37 @@ async function runFilesToDBMigration() {
return false; return false;
} }
} }
*/
async function simplifyDBFileStructure() {
let users = users_db.get('users').value();
for (let i = 0; i < users.length; i++) {
const user = users[i];
if (user['files']['video'] !== undefined && user['files']['audio'] !== undefined) {
const user_files = user['files']['video'].concat(user['files']['audio']);
const user_db_path = users_db.get('users').find({uid: user['uid']});
user_db_path.assign({files: user_files}).write();
}
if (user['playlists']['video'] !== undefined && user['playlists']['audio'] !== undefined) {
const user_playlists = user['playlists']['video'].concat(user['playlists']['audio']);
const user_db_path = users_db.get('users').find({uid: user['uid']});
user_db_path.assign({playlists: user_playlists}).write();
}
}
if (db.get('files.video').value() !== undefined && db.get('files.audio').value() !== undefined) {
const files = db.get('files.video').value().concat(db.get('files.audio'));
db.assign({files: files}).write();
}
if (db.get('playlists.video').value() !== undefined && db.get('playlists.audio').value() !== undefined) {
const playlists = db.get('playlists.video').value().concat(db.get('playlists.audio'));
db.assign({playlists: playlists}).write();
}
return true;
}
async function addMetadataPropertyToDB(property_key) { async function addMetadataPropertyToDB(property_key) {
try { try {
@ -592,6 +620,9 @@ async function loadConfig() {
// creates archive path if missing // creates archive path if missing
await fs.ensureDir(archivePath); await fs.ensureDir(archivePath);
// check migrations
await checkMigrations();
// now this is done here due to youtube-dl's repo takedown // now this is done here due to youtube-dl's repo takedown
await startYoutubeDL(); await startYoutubeDL();
@ -606,9 +637,6 @@ async function loadConfig() {
db_api.importUnregisteredFiles(); db_api.importUnregisteredFiles();
// check migrations
await checkMigrations();
// load in previous downloads // load in previous downloads
downloads = db.get('downloads').value(); downloads = db.get('downloads').value();
@ -1994,7 +2022,7 @@ async function addThumbnails(files) {
// gets all download mp3s // gets all download mp3s
app.get('/api/getMp3s', optionalJwt, async function(req, res) { app.get('/api/getMp3s', optionalJwt, async function(req, res) {
var mp3s = db.get('files.audio').value(); // getMp3s(); var mp3s = db.get('files').chain().find({isAudio: true}).value(); // getMp3s();
var playlists = db.get('playlists.audio').value(); var playlists = db.get('playlists.audio').value();
const is_authenticated = req.isAuthenticated(); const is_authenticated = req.isAuthenticated();
if (is_authenticated) { if (is_authenticated) {
@ -2020,8 +2048,8 @@ app.get('/api/getMp3s', optionalJwt, async function(req, res) {
// gets all download mp4s // gets all download mp4s
app.get('/api/getMp4s', optionalJwt, async function(req, res) { app.get('/api/getMp4s', optionalJwt, async function(req, res) {
var mp4s = db.get('files.video').value(); // getMp4s(); var mp4s = db.get('files').chain().find({isAudio: false}).value(); // getMp4s();
var playlists = db.get('playlists.video').value(); var playlists = db.get('playlists').value();
const is_authenticated = req.isAuthenticated(); const is_authenticated = req.isAuthenticated();
if (is_authenticated) { if (is_authenticated) {
@ -2052,21 +2080,11 @@ app.post('/api/getFile', optionalJwt, function (req, res) {
var file = null; var file = null;
if (req.isAuthenticated()) { if (req.isAuthenticated()) {
file = auth_api.getUserVideo(req.user.uid, uid, type); file = auth_api.getUserVideo(req.user.uid, uid);
} else if (uuid) { } else if (uuid) {
file = auth_api.getUserVideo(uuid, uid, type, true); file = auth_api.getUserVideo(uuid, uid, true);
} else { } else {
if (!type) { file = db.get('files').find({uid: uid}).value();
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) file = db.get(`files.${type}`).find({uid: uid}).value();
} }
// check if chat exists for twitch videos // check if chat exists for twitch videos
@ -2086,32 +2104,20 @@ app.post('/api/getFile', optionalJwt, function (req, res) {
app.post('/api/getAllFiles', optionalJwt, async function (req, res) { app.post('/api/getAllFiles', optionalJwt, async function (req, res) {
// these are returned // these are returned
let files = []; let files = null;
let playlists = []; let playlists = null;
let subscription_files = [];
let videos = null;
let audios = null;
let audio_playlists = null;
let video_playlists = null;
let subscriptions = config_api.getConfigItem('ytdl_allow_subscriptions') ? (subscriptions_api.getAllSubscriptions(req.isAuthenticated() ? req.user.uid : null)) : []; let subscriptions = config_api.getConfigItem('ytdl_allow_subscriptions') ? (subscriptions_api.getAllSubscriptions(req.isAuthenticated() ? req.user.uid : null)) : [];
// get basic info depending on multi-user mode being enabled // get basic info depending on multi-user mode being enabled
if (req.isAuthenticated()) { if (req.isAuthenticated()) {
videos = auth_api.getUserVideos(req.user.uid, 'video'); files = auth_api.getUserVideos(req.user.uid);
audios = auth_api.getUserVideos(req.user.uid, 'audio'); playlists = auth_api.getUserPlaylists(req.user.uid);
audio_playlists = auth_api.getUserPlaylists(req.user.uid, 'audio');
video_playlists = auth_api.getUserPlaylists(req.user.uid, 'video');
} else { } else {
videos = db.get('files.audio').value(); files = db.get('files').value();
audios = db.get('files.video').value(); playlists = db.get('playlists').value();
audio_playlists = db.get('playlists.audio').value();
video_playlists = db.get('playlists.video').value();
} }
files = videos.concat(audios);
playlists = video_playlists.concat(audio_playlists);
// loop through subscriptions and add videos // loop through subscriptions and add videos
for (let i = 0; i < subscriptions.length; i++) { for (let i = 0; i < subscriptions.length; i++) {
sub = subscriptions[i]; sub = subscriptions[i];
@ -2187,14 +2193,13 @@ app.post('/api/downloadTwitchChatByVODID', optionalJwt, async (req, res) => {
// video sharing // video sharing
app.post('/api/enableSharing', optionalJwt, function(req, res) { app.post('/api/enableSharing', optionalJwt, function(req, res) {
var type = req.body.type;
var uid = req.body.uid; var uid = req.body.uid;
var is_playlist = req.body.is_playlist; var is_playlist = req.body.is_playlist;
let success = false; let success = false;
// multi-user mode // multi-user mode
if (req.isAuthenticated()) { if (req.isAuthenticated()) {
// if multi user mode, use this method instead // if multi user mode, use this method instead
success = auth_api.changeSharingMode(req.user.uid, uid, type, is_playlist, true); success = auth_api.changeSharingMode(req.user.uid, uid, is_playlist, true);
res.send({success: success}); res.send({success: success});
return; return;
} }
@ -2203,12 +2208,12 @@ app.post('/api/enableSharing', optionalJwt, function(req, res) {
try { try {
success = true; success = true;
if (!is_playlist && type !== 'subscription') { if (!is_playlist && type !== 'subscription') {
db.get(`files.${type}`) db.get(`files`)
.find({uid: uid}) .find({uid: uid})
.assign({sharingEnabled: true}) .assign({sharingEnabled: true})
.write(); .write();
} else if (is_playlist) { } else if (is_playlist) {
db.get(`playlists.${type}`) db.get(`playlists`)
.find({id: uid}) .find({id: uid})
.assign({sharingEnabled: true}) .assign({sharingEnabled: true})
.write(); .write();
@ -2237,7 +2242,7 @@ app.post('/api/disableSharing', optionalJwt, function(req, res) {
// multi-user mode // multi-user mode
if (req.isAuthenticated()) { if (req.isAuthenticated()) {
// if multi user mode, use this method instead // if multi user mode, use this method instead
success = auth_api.changeSharingMode(req.user.uid, uid, type, is_playlist, false); success = auth_api.changeSharingMode(req.user.uid, uid, is_playlist, false);
res.send({success: success}); res.send({success: success});
return; return;
} }
@ -2246,12 +2251,12 @@ app.post('/api/disableSharing', optionalJwt, function(req, res) {
try { try {
success = true; success = true;
if (!is_playlist && type !== 'subscription') { if (!is_playlist && type !== 'subscription') {
db.get(`files.${type}`) db.get(`files`)
.find({uid: uid}) .find({uid: uid})
.assign({sharingEnabled: false}) .assign({sharingEnabled: false})
.write(); .write();
} else if (is_playlist) { } else if (is_playlist) {
db.get(`playlists.${type}`) db.get(`playlists`)
.find({id: uid}) .find({id: uid})
.assign({sharingEnabled: false}) .assign({sharingEnabled: false})
.write(); .write();
@ -2544,7 +2549,7 @@ app.post('/api/createPlaylist', optionalJwt, async (req, res) => {
if (req.isAuthenticated()) { if (req.isAuthenticated()) {
auth_api.addPlaylist(req.user.uid, new_playlist, type); auth_api.addPlaylist(req.user.uid, new_playlist, type);
} else { } else {
db.get(`playlists.${type}`) db.get(`playlists`)
.push(new_playlist) .push(new_playlist)
.write(); .write();
} }
@ -2558,26 +2563,15 @@ app.post('/api/createPlaylist', optionalJwt, async (req, res) => {
app.post('/api/getPlaylist', optionalJwt, async (req, res) => { app.post('/api/getPlaylist', optionalJwt, async (req, res) => {
let playlistID = req.body.playlistID; let playlistID = req.body.playlistID;
let type = req.body.type;
let uuid = req.body.uuid; let uuid = req.body.uuid;
let playlist = null; let playlist = null;
if (req.isAuthenticated()) { if (req.isAuthenticated()) {
playlist = auth_api.getUserPlaylist(uuid ? uuid : req.user.uid, playlistID, type); playlist = auth_api.getUserPlaylist(uuid ? uuid : req.user.uid, playlistID);
type = playlist.type; type = playlist.type;
} else { } else {
if (!type) { playlist = db.get(`playlists`).find({id: playlistID}).value();
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({ res.send({
@ -2590,14 +2584,13 @@ app.post('/api/getPlaylist', optionalJwt, async (req, res) => {
app.post('/api/updatePlaylistFiles', optionalJwt, async (req, res) => { app.post('/api/updatePlaylistFiles', optionalJwt, async (req, res) => {
let playlistID = req.body.playlistID; let playlistID = req.body.playlistID;
let fileNames = req.body.fileNames; let fileNames = req.body.fileNames;
let type = req.body.type;
let success = false; let success = false;
try { try {
if (req.isAuthenticated()) { if (req.isAuthenticated()) {
auth_api.updatePlaylistFiles(req.user.uid, playlistID, fileNames, type); auth_api.updatePlaylistFiles(req.user.uid, playlistID, fileNames);
} else { } else {
db.get(`playlists.${type}`) db.get(`playlists`)
.find({id: playlistID}) .find({id: playlistID})
.assign({fileNames: fileNames}) .assign({fileNames: fileNames})
.write(); .write();
@ -2623,15 +2616,14 @@ app.post('/api/updatePlaylist', optionalJwt, async (req, res) => {
app.post('/api/deletePlaylist', optionalJwt, async (req, res) => { app.post('/api/deletePlaylist', optionalJwt, async (req, res) => {
let playlistID = req.body.playlistID; let playlistID = req.body.playlistID;
let type = req.body.type;
let success = null; let success = null;
try { try {
if (req.isAuthenticated()) { if (req.isAuthenticated()) {
auth_api.removePlaylist(req.user.uid, playlistID, type); auth_api.removePlaylist(req.user.uid, playlistID);
} else { } else {
// removes playlist from playlists // removes playlist from playlists
db.get(`playlists.${type}`) db.get(`playlists`)
.remove({id: playlistID}) .remove({id: playlistID})
.write(); .write();
} }
@ -2653,23 +2645,23 @@ app.post('/api/deleteFile', optionalJwt, async (req, res) => {
var blacklistMode = req.body.blacklistMode; var blacklistMode = req.body.blacklistMode;
if (req.isAuthenticated()) { if (req.isAuthenticated()) {
let success = auth_api.deleteUserFile(req.user.uid, uid, type, blacklistMode); let success = auth_api.deleteUserFile(req.user.uid, uid, blacklistMode);
res.send(success); res.send(success);
return; return;
} }
var file_obj = db.get(`files.${type}`).find({uid: uid}).value(); var file_obj = db.get(`files`).find({uid: uid}).value();
var name = file_obj.id; var name = file_obj.id;
var fullpath = file_obj ? file_obj.path : null; var fullpath = file_obj ? file_obj.path : null;
var wasDeleted = false; var wasDeleted = false;
if (await fs.pathExists(fullpath)) if (await fs.pathExists(fullpath))
{ {
wasDeleted = type === 'audio' ? await deleteAudioFile(name, path.basename(fullpath), blacklistMode) : await deleteVideoFile(name, path.basename(fullpath), blacklistMode); wasDeleted = type === 'audio' ? await deleteAudioFile(name, path.basename(fullpath), blacklistMode) : await deleteVideoFile(name, path.basename(fullpath), blacklistMode);
db.get('files.video').remove({uid: uid}).write(); db.get('files').remove({uid: uid}).write();
// wasDeleted = true; // wasDeleted = true;
res.send(wasDeleted); res.send(wasDeleted);
} else if (video_obj) { } else if (video_obj) {
db.get('files.video').remove({uid: uid}).write(); db.get('files').remove({uid: uid}).write();
wasDeleted = true; wasDeleted = true;
res.send(wasDeleted); res.send(wasDeleted);
} else { } else {

@ -281,24 +281,13 @@ exports.adminExists = function() {
// video stuff // video stuff
exports.getUserVideos = function(user_uid, type) { exports.getUserVideos = function(user_uid) {
const user = users_db.get('users').find({uid: user_uid}).value(); const user = users_db.get('users').find({uid: user_uid}).value();
return user['files'][type]; return user['files'];
} }
exports.getUserVideo = function(user_uid, file_uid, type, requireSharing = false) { exports.getUserVideo = function(user_uid, file_uid, requireSharing = false) {
let file = null; let file = users_db.get('users').find({uid: user_uid}).get(`files`).find({uid: file_uid}).value();
if (!type) {
file = users_db.get('users').find({uid: user_uid}).get(`files.audio`).find({uid: file_uid}).value();
if (!file) {
file = users_db.get('users').find({uid: user_uid}).get(`files.video`).find({uid: file_uid}).value();
if (file) type = 'video';
} else {
type = 'audio';
}
}
if (!file && type) file = users_db.get('users').find({uid: user_uid}).get(`files.${type}`).find({uid: file_uid}).value();
// prevent unauthorized users from accessing the file info // prevent unauthorized users from accessing the file info
if (file && !file['sharingEnabled'] && requireSharing) file = null; if (file && !file['sharingEnabled'] && requireSharing) file = null;
@ -306,38 +295,28 @@ exports.getUserVideo = function(user_uid, file_uid, type, requireSharing = false
return file; return file;
} }
exports.addPlaylist = function(user_uid, new_playlist, type) { exports.addPlaylist = function(user_uid, new_playlist) {
users_db.get('users').find({uid: user_uid}).get(`playlists.${type}`).push(new_playlist).write(); users_db.get('users').find({uid: user_uid}).get(`playlists`).push(new_playlist).write();
return true; return true;
} }
exports.updatePlaylistFiles = function(user_uid, playlistID, new_filenames, type) { exports.updatePlaylistFiles = function(user_uid, playlistID, new_filenames) {
users_db.get('users').find({uid: user_uid}).get(`playlists.${type}`).find({id: playlistID}).assign({fileNames: new_filenames}); users_db.get('users').find({uid: user_uid}).get(`playlists`).find({id: playlistID}).assign({fileNames: new_filenames});
return true; return true;
} }
exports.removePlaylist = function(user_uid, playlistID, type) { exports.removePlaylist = function(user_uid, playlistID) {
users_db.get('users').find({uid: user_uid}).get(`playlists.${type}`).remove({id: playlistID}).write(); users_db.get('users').find({uid: user_uid}).get(`playlists`).remove({id: playlistID}).write();
return true; return true;
} }
exports.getUserPlaylists = function(user_uid, type) { exports.getUserPlaylists = function(user_uid) {
const user = users_db.get('users').find({uid: user_uid}).value(); const user = users_db.get('users').find({uid: user_uid}).value();
return user['playlists'][type]; return user['playlists'];
} }
exports.getUserPlaylist = function(user_uid, playlistID, type, requireSharing = false) { exports.getUserPlaylist = function(user_uid, playlistID, requireSharing = false) {
let playlist = null; let playlist = users_db.get('users').find({uid: user_uid}).get(`playlists`).find({id: playlistID}).value();
if (!type) {
playlist = users_db.get('users').find({uid: user_uid}).get(`playlists.audio`).find({id: playlistID}).value();
if (!playlist) {
playlist = users_db.get('users').find({uid: user_uid}).get(`playlists.video`).find({id: playlistID}).value();
if (playlist) type = 'video';
} else {
type = 'audio';
}
}
if (!playlist) playlist = users_db.get('users').find({uid: user_uid}).get(`playlists.${type}`).find({id: playlistID}).value();
// prevent unauthorized users from accessing the file info // prevent unauthorized users from accessing the file info
if (requireSharing && !playlist['sharingEnabled']) playlist = null; if (requireSharing && !playlist['sharingEnabled']) playlist = null;
@ -345,21 +324,22 @@ exports.getUserPlaylist = function(user_uid, playlistID, type, requireSharing =
return playlist; return playlist;
} }
exports.registerUserFile = function(user_uid, file_object, type) { exports.registerUserFile = function(user_uid, file_object) {
users_db.get('users').find({uid: user_uid}).get(`files.${type}`) users_db.get('users').find({uid: user_uid}).get(`files`)
.remove({ .remove({
path: file_object['path'] path: file_object['path']
}).write(); }).write();
users_db.get('users').find({uid: user_uid}).get(`files.${type}`) users_db.get('users').find({uid: user_uid}).get(`files`)
.push(file_object) .push(file_object)
.write(); .write();
} }
exports.deleteUserFile = async function(user_uid, file_uid, type, blacklistMode = false) { exports.deleteUserFile = async function(user_uid, file_uid, blacklistMode = false) {
let success = false; let success = false;
const file_obj = users_db.get('users').find({uid: user_uid}).get(`files.${type}`).find({uid: file_uid}).value(); const file_obj = users_db.get('users').find({uid: user_uid}).get(`files`).find({uid: file_uid}).value();
if (file_obj) { if (file_obj) {
const type = file_obj.isAudio ? 'audio' : 'video';
const usersFileFolder = config_api.getConfigItem('ytdl_users_base_path'); const usersFileFolder = config_api.getConfigItem('ytdl_users_base_path');
const ext = type === 'audio' ? '.mp3' : '.mp4'; const ext = type === 'audio' ? '.mp3' : '.mp4';
@ -375,7 +355,7 @@ exports.deleteUserFile = async function(user_uid, file_uid, type, blacklistMode
} }
const full_path = path.join(usersFileFolder, user_uid, type, file_obj.id + ext); const full_path = path.join(usersFileFolder, user_uid, type, file_obj.id + ext);
users_db.get('users').find({uid: user_uid}).get(`files.${type}`) users_db.get('users').find({uid: user_uid}).get(`files`)
.remove({ .remove({
uid: file_uid uid: file_uid
}).write(); }).write();
@ -424,11 +404,11 @@ exports.deleteUserFile = async function(user_uid, file_uid, type, blacklistMode
return success; return success;
} }
exports.changeSharingMode = function(user_uid, file_uid, type, is_playlist, enabled) { exports.changeSharingMode = function(user_uid, file_uid, is_playlist, enabled) {
let success = false; let success = false;
const user_db_obj = users_db.get('users').find({uid: user_uid}); const user_db_obj = users_db.get('users').find({uid: user_uid});
if (user_db_obj.value()) { if (user_db_obj.value()) {
const file_db_obj = is_playlist ? user_db_obj.get(`playlists.${type}`).find({id: file_uid}) : user_db_obj.get(`files.${type}`).find({uid: file_uid}); const file_db_obj = is_playlist ? user_db_obj.get(`playlists`).find({id: file_uid}) : user_db_obj.get(`files`).find({uid: file_uid});
if (file_db_obj.value()) { if (file_db_obj.value()) {
success = true; success = true;
file_db_obj.assign({sharingEnabled: enabled}).write(); file_db_obj.assign({sharingEnabled: enabled}).write();

@ -32,9 +32,9 @@ function registerFileDB(file_path, type, multiUserMode = null, sub = null, custo
if (!sub) { if (!sub) {
if (multiUserMode) { if (multiUserMode) {
const user_uid = multiUserMode.user; const user_uid = multiUserMode.user;
db_path = users_db.get('users').find({uid: user_uid}).get(`files.${type}`); db_path = users_db.get('users').find({uid: user_uid}).get(`files`);
} else { } else {
db_path = db.get(`files.${type}`) db_path = db.get(`files`);
} }
} else { } else {
if (multiUserMode) { if (multiUserMode) {
@ -94,18 +94,18 @@ function generateFileObject(id, type, customPath = null, sub = null) {
var thumbnail = jsonobj.thumbnail; var thumbnail = jsonobj.thumbnail;
var duration = jsonobj.duration; var duration = jsonobj.duration;
var isaudio = type === 'audio'; var isaudio = type === 'audio';
var file_obj = new utils.File(id, title, thumbnail, isaudio, duration, url, uploader, size, file_path, upload_date); var description = jsonobj.description;
var file_obj = new utils.File(id, title, thumbnail, isaudio, duration, url, uploader, size, file_path, upload_date, description);
return file_obj; return file_obj;
} }
function updatePlaylist(playlist, user_uid) { function updatePlaylist(playlist, user_uid) {
let playlistID = playlist.id; let playlistID = playlist.id;
let type = playlist.type;
let db_loc = null; let db_loc = null;
if (user_uid) { if (user_uid) {
db_loc = users_db.get('users').find({uid: user_uid}).get(`playlists.${type}`).find({id: playlistID}); db_loc = users_db.get('users').find({uid: user_uid}).get(`playlists`).find({id: playlistID});
} else { } else {
db_loc = db.get(`playlists.${type}`).find({id: playlistID}); db_loc = db.get(`playlists`).find({id: playlistID});
} }
db_loc.assign(playlist).write(); db_loc.assign(playlist).write();
return true; return true;
@ -132,14 +132,14 @@ function getFileDirectoriesAndDBs() {
// add user's audio dir to check list // add user's audio dir to check list
dirs_to_check.push({ dirs_to_check.push({
basePath: path.join(usersFileFolder, user.uid, 'audio'), basePath: path.join(usersFileFolder, user.uid, 'audio'),
dbPath: users_db.get('users').find({uid: user.uid}).get('files.audio'), dbPath: users_db.get('users').find({uid: user.uid}).get('files'),
type: 'audio' type: 'audio'
}); });
// add user's video dir to check list // add user's video dir to check list
dirs_to_check.push({ dirs_to_check.push({
basePath: path.join(usersFileFolder, user.uid, 'video'), basePath: path.join(usersFileFolder, user.uid, 'video'),
dbPath: users_db.get('users').find({uid: user.uid}).get('files.video'), dbPath: users_db.get('users').find({uid: user.uid}).get('files'),
type: 'video' type: 'video'
}); });
} }
@ -153,14 +153,14 @@ function getFileDirectoriesAndDBs() {
// add audio dir to check list // add audio dir to check list
dirs_to_check.push({ dirs_to_check.push({
basePath: audioFolderPath, basePath: audioFolderPath,
dbPath: db.get('files.audio'), dbPath: db.get('files'),
type: 'audio' type: 'audio'
}); });
// add video dir to check list // add video dir to check list
dirs_to_check.push({ dirs_to_check.push({
basePath: videoFolderPath, basePath: videoFolderPath,
dbPath: db.get('files.video'), dbPath: db.get('files'),
type: 'video' type: 'video'
}); });
} }

@ -189,7 +189,7 @@ async function recFindByExt(base,ext,files,result)
// objects // objects
function File(id, title, thumbnailURL, isAudio, duration, url, uploader, size, path, upload_date) { function File(id, title, thumbnailURL, isAudio, duration, url, uploader, size, path, upload_date, description) {
this.id = id; this.id = id;
this.title = title; this.title = title;
this.thumbnailURL = thumbnailURL; this.thumbnailURL = thumbnailURL;
@ -200,6 +200,7 @@ function File(id, title, thumbnailURL, isAudio, duration, url, uploader, size, p
this.size = size; this.size = size;
this.path = path; this.path = path;
this.upload_date = upload_date; this.upload_date = upload_date;
this.description = description;
} }
module.exports = { module.exports = {

@ -81,6 +81,7 @@ import { EditSubscriptionDialogComponent } from './dialogs/edit-subscription-dia
import { CustomPlaylistsComponent } from './components/custom-playlists/custom-playlists.component'; import { CustomPlaylistsComponent } from './components/custom-playlists/custom-playlists.component';
import { EditCategoryDialogComponent } from './dialogs/edit-category-dialog/edit-category-dialog.component'; import { EditCategoryDialogComponent } from './dialogs/edit-category-dialog/edit-category-dialog.component';
import { TwitchChatComponent } from './components/twitch-chat/twitch-chat.component'; import { TwitchChatComponent } from './components/twitch-chat/twitch-chat.component';
import { LinkifyPipe, SeeMoreComponent } from './components/see-more/see-more.component';
registerLocaleData(es, 'es'); registerLocaleData(es, 'es');
@ -107,6 +108,7 @@ export function isVisible({ event, element, scrollContainer, offset }: IsVisible
VideoInfoDialogComponent, VideoInfoDialogComponent,
ArgModifierDialogComponent, ArgModifierDialogComponent,
HighlightPipe, HighlightPipe,
LinkifyPipe,
UpdaterComponent, UpdaterComponent,
UpdateProgressDialogComponent, UpdateProgressDialogComponent,
ShareMediaDialogComponent, ShareMediaDialogComponent,
@ -127,7 +129,8 @@ export function isVisible({ event, element, scrollContainer, offset }: IsVisible
EditSubscriptionDialogComponent, EditSubscriptionDialogComponent,
CustomPlaylistsComponent, CustomPlaylistsComponent,
EditCategoryDialogComponent, EditCategoryDialogComponent,
TwitchChatComponent TwitchChatComponent,
SeeMoreComponent
], ],
imports: [ imports: [
CommonModule, CommonModule,
@ -188,7 +191,8 @@ export function isVisible({ event, element, scrollContainer, offset }: IsVisible
PostsService PostsService
], ],
exports: [ exports: [
HighlightPipe HighlightPipe,
LinkifyPipe
], ],
bootstrap: [AppComponent] bootstrap: [AppComponent]
}) })

@ -0,0 +1,11 @@
<span class="text" [ngStyle]="{'-webkit-line-clamp': !see_more_active ? line_limit : null}" [innerHTML]="text | linkify"></span>
<span>
<a [routerLink]="" (click)="toggleSeeMore()">
<ng-container *ngIf="!see_more_active" i18n="See more">
See more.
</ng-container>
<ng-container *ngIf="see_more_active" i18n="See less">
See less.
</ng-container>
</a>
</span>

@ -0,0 +1,7 @@
.text {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
word-wrap: break-word;
}

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SeeMoreComponent } from './see-more.component';
describe('SeeMoreComponent', () => {
let component: SeeMoreComponent;
let fixture: ComponentFixture<SeeMoreComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ SeeMoreComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SeeMoreComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,60 @@
import { Component, Input, OnInit, Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
@Pipe({ name: 'linkify' })
export class LinkifyPipe implements PipeTransform {
constructor(private _domSanitizer: DomSanitizer) {}
transform(value: any, args?: any): any {
return this._domSanitizer.bypassSecurityTrustHtml(this.stylize(value));
}
// Modify this method according to your custom logic
private stylize(text: string): string {
let stylizedText: string = '';
if (text && text.length > 0) {
for (let line of text.split("\n")) {
for (let t of line.split(" ")) {
if (t.startsWith("http") && t.length>7) {
stylizedText += `<a target="_blank" href="${t}">${t}</a> `;
}
else
stylizedText += t + " ";
}
stylizedText += '<br>';
}
return stylizedText;
}
else return text;
}
}
@Component({
selector: 'app-see-more',
templateUrl: './see-more.component.html',
providers: [LinkifyPipe],
styleUrls: ['./see-more.component.scss']
})
export class SeeMoreComponent implements OnInit {
see_more_active = false;
@Input() text = '';
@Input() line_limit = 2;
constructor() { }
ngOnInit(): void {
}
toggleSeeMore() {
this.see_more_active = !this.see_more_active;
}
parseText() {
return this.text.replace(/(http.*?\s)/, "<a href=\"$1\">$1</a>")
}
}

@ -8,14 +8,28 @@
</video> </video>
</vg-player> </vg-player>
</div> </div>
<div *ngIf="file_obj" style="height: fit-content; width: 100%; margin-top: 10px;"> <div *ngIf="db_file" style="height: fit-content; width: 100%; margin-top: 10px;">
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col"> <div class="col-2 col-lg-1">
<ng-container *ngIf="file_obj">{{file_obj['local_play_count'] ? file_obj['local_play_count'] : 0}}&nbsp;<ng-container i18n="View count label">views</ng-container></ng-container> <ng-container *ngIf="db_file">{{db_file['local_view_count'] ? db_file['local_view_count'] : 0}}&nbsp;<ng-container i18n="View count label">views</ng-container></ng-container>
</div> </div>
<div class="col"> <div style="white-space: pre-line;" class="col-9 col-lg-10">
<ng-container *ngIf="db_file && db_file['description']">
<p>
<app-see-more [text]="db_file['description']"></app-see-more>
</p>
</ng-container>
<ng-container *ngIf="!db_file || !db_file['description']">
<p style="text-align: center;">
No description available.
</p>
</ng-container>
</div>
<div class="col-1">
<div *ngIf="db_file && db_file.url.includes('twitch.tv/videos/') && postsService['config']['API']['use_twitch_API']">
<button (click)="drawer.toggle()" mat-icon-button><mat-icon>chat</mat-icon></button>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -25,15 +39,11 @@
<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 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> </mat-button-toggle-group>
</div> </div>
<mat-drawer #drawer class="example-sidenav" mode="side" position="end" [opened]="db_file && db_file['chat_exists']"> <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/')"> <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"></app-twitch-chat> <app-twitch-chat #twitchchat [db_file]="db_file" [current_timestamp]="api.currentTime"></app-twitch-chat>
</ng-container> </ng-container>
</mat-drawer> </mat-drawer>
<div *ngIf="db_file && db_file.url.includes('twitch.tv/videos/') && postsService['config']['API']['use_twitch_API']" style="position: absolute; right: 0px">
<button style="right: 0px; top: -46px;" (click)="drawer.toggle()" mat-icon-button><mat-icon>chat</mat-icon></button>
</div>
<div class="update-playlist-button-div" *ngIf="id && playlistChanged()"> <div class="update-playlist-button-div" *ngIf="id && playlistChanged()">
<div class="spinner-div"> <div class="spinner-div">

Loading…
Cancel
Save