diff --git a/backend/app.js b/backend/app.js index 50c6bb9..a508702 100644 --- a/backend/app.js +++ b/backend/app.js @@ -933,20 +933,13 @@ app.post('/api/getAllFiles', optionalJwt, async function (req, res) { else if (file_type_filter === 'video_only') filter_obj['isAudio'] = false; files = await db_api.getRecords('files', filter_obj, false, sort, range, text_search); - let file_count = await db_api.getRecords('files', filter_obj, true); - playlists = await db_api.getRecords('playlists', {user_uid: uuid}); - - const categories = await categories_api.getCategoriesAsPlaylists(files); - if (categories) { - playlists = playlists.concat(categories); - } + const file_count = await db_api.getRecords('files', filter_obj, true); files = JSON.parse(JSON.stringify(files)); res.send({ files: files, file_count: file_count, - playlists: playlists }); }); @@ -1383,7 +1376,7 @@ app.post('/api/getPlaylists', optionalJwt, async (req, res) => { let playlists = await db_api.getRecords('playlists', {user_uid: uuid}); if (include_categories) { - const categories = await categories_api.getCategoriesAsPlaylists(files); + const categories = await categories_api.getCategoriesAsPlaylists(); if (categories) { playlists = playlists.concat(categories); } diff --git a/backend/categories.js b/backend/categories.js index 9b599cc..2faebfb 100644 --- a/backend/categories.js +++ b/backend/categories.js @@ -55,12 +55,12 @@ async function getCategories() { return categories ? categories : null; } -async function getCategoriesAsPlaylists(files = null) { +async function getCategoriesAsPlaylists() { const categories_as_playlists = []; const available_categories = await getCategories(); - if (available_categories && files) { + if (available_categories) { for (let category of available_categories) { - const files_that_match = utils.addUIDsToCategory(category, files); + const files_that_match = await db_api.getRecords('files', {'category.uid': category['uid']}); if (files_that_match && files_that_match.length > 0) { category['thumbnailURL'] = files_that_match[0].thumbnailURL; category['thumbnailPath'] = files_that_match[0].thumbnailPath; diff --git a/backend/config.js b/backend/config.js index 95e364c..4e208fb 100644 --- a/backend/config.js +++ b/backend/config.js @@ -127,7 +127,7 @@ function setConfigItem(key, value) { success = setConfigFile(config_json); return success; -}; +} function setConfigItems(items) { let success = false; diff --git a/backend/db.js b/backend/db.js index d8a7b9f..73f56f1 100644 --- a/backend/db.js +++ b/backend/db.js @@ -387,9 +387,9 @@ exports.getPlaylist = async (playlist_id, user_uid = null, require_sharing = fal if (!playlist) { playlist = await exports.getRecord('categories', {uid: playlist_id}); if (playlist) { - // category found - const files = await exports.getFiles(user_uid); - utils.addUIDsToCategory(playlist, files); + const uids = (await exports.getRecords('files', {'category.uid': playlist_id})).map(file => file.uid); + playlist['uids'] = uids; + playlist['auto'] = true; } } @@ -1090,7 +1090,7 @@ exports.applyFilterLocalDB = (db_path, filter_obj, operation) => { const filter_prop = filter_props[i]; const filter_prop_value = filter_obj[filter_prop]; if (filter_prop_value === undefined || filter_prop_value === null) { - filtered &= record[filter_prop] === undefined || record[filter_prop] === null + filtered &= record[filter_prop] === undefined || record[filter_prop] === null; } else { if (typeof filter_prop_value === 'object') { if ('$regex' in filter_prop_value) { @@ -1099,7 +1099,11 @@ exports.applyFilterLocalDB = (db_path, filter_obj, operation) => { filtered &= filter_prop in record && record[filter_prop] !== filter_prop_value['$ne']; } } else { - filtered &= record[filter_prop] === filter_prop_value; + // handle case of nested property check + if (filter_prop.includes('.')) + filtered &= utils.searchObjectByString(record, filter_prop) === filter_prop_value; + else + filtered &= record[filter_prop] === filter_prop_value; } } } diff --git a/backend/test/tests.js b/backend/test/tests.js index 2accc0f..448db7b 100644 --- a/backend/test/tests.js +++ b/backend/test/tests.js @@ -272,6 +272,11 @@ describe('Database', async function() { const result = db_api.applyFilterLocalDB([{test: 'test'}, {test: 'test1'}], {test: filter}, 'find'); assert(result && result['test'] === 'test1'); }); + + it('Nested', async function() { + const result = db_api.applyFilterLocalDB([{test1: {test2: 'test3'}}, {test4: 'test5'}], {'test1.test2': 'test3'}, 'find'); + assert(result && result['test1']['test2'] === 'test3'); + }); }) }); diff --git a/backend/utils.js b/backend/utils.js index 56f12bb..805d591 100644 --- a/backend/utils.js +++ b/backend/utils.js @@ -456,6 +456,21 @@ function injectArgs(original_args, new_args) { return updated_args; } +const searchObjectByString = function(o, s) { + s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties + s = s.replace(/^\./, ''); // strip a leading dot + var a = s.split('.'); + for (var i = 0, n = a.length; i < n; ++i) { + var k = a[i]; + if (k in o) { + o = o[k]; + } else { + return; + } + } + return o; +} + // objects function File(id, title, thumbnailURL, isAudio, duration, url, uploader, size, path, upload_date, description, view_count, height, abr) { @@ -489,7 +504,6 @@ module.exports = { createContainerZipFile: createContainerZipFile, durationStringToNumber: durationStringToNumber, getMatchingCategoryFiles: getMatchingCategoryFiles, - addUIDsToCategory: addUIDsToCategory, getCurrentDownloader: getCurrentDownloader, recFindByExt: recFindByExt, removeFileExtension: removeFileExtension, @@ -501,5 +515,6 @@ module.exports = { fetchFile: fetchFile, restartServer: restartServer, injectArgs: injectArgs, + searchObjectByString: searchObjectByString, File: File } diff --git a/src/app/components/custom-playlists/custom-playlists.component.ts b/src/app/components/custom-playlists/custom-playlists.component.ts index 9654013..e659724 100644 --- a/src/app/components/custom-playlists/custom-playlists.component.ts +++ b/src/app/components/custom-playlists/custom-playlists.component.ts @@ -35,7 +35,7 @@ export class CustomPlaylistsComponent implements OnInit { getAllPlaylists() { this.playlists_received = false; // must call getAllFiles as we need to get category playlists as well - this.postsService.getPlaylists().subscribe(res => { + this.postsService.getPlaylists(true).subscribe(res => { this.playlists = res['playlists']; this.playlists_received = true; }); diff --git a/src/app/dialogs/video-info-dialog/video-info-dialog.component.html b/src/app/dialogs/video-info-dialog/video-info-dialog.component.html index c9c0cc9..8f99be9 100644 --- a/src/app/dialogs/video-info-dialog/video-info-dialog.component.html +++ b/src/app/dialogs/video-info-dialog/video-info-dialog.component.html @@ -28,12 +28,12 @@ - - + + N/A - + {{available_category.value.name}} diff --git a/src/app/dialogs/video-info-dialog/video-info-dialog.component.ts b/src/app/dialogs/video-info-dialog/video-info-dialog.component.ts index 8524bca..4d9a7b6 100644 --- a/src/app/dialogs/video-info-dialog/video-info-dialog.component.ts +++ b/src/app/dialogs/video-info-dialog/video-info-dialog.component.ts @@ -18,6 +18,7 @@ export class VideoInfoDialogComponent implements OnInit { upload_date: Date; category: Category; editing = false; + initialized = false; constructor(@Inject(MAT_DIALOG_DATA) public data: any, public postsService: PostsService, private datePipe: DatePipe) { } @@ -37,15 +38,16 @@ export class VideoInfoDialogComponent implements OnInit { this.upload_date = new Date(this.new_file.upload_date); this.upload_date.setMinutes( this.upload_date.getMinutes() + this.upload_date.getTimezoneOffset() ); - this.category = this.file.category ? this.category : {}; + this.category = this.file.category ? this.file.category : {}; // we need to align whether missing category is null or undefined. this line helps with that. if (!this.file.category) { this.new_file.category = null; this.file.category = null; } + this.initialized = true; } saveChanges(): void { const change_obj = {}; - const keys = Object.keys(this.file); + const keys = Object.keys(this.new_file); keys.forEach(key => { if (this.file[key] !== this.new_file[key]) change_obj[key] = this.new_file[key]; }); @@ -67,7 +69,8 @@ export class VideoInfoDialogComponent implements OnInit { } categoryChanged(event): void { - this.new_file.category = Object.keys(event).length ? {uid: event.uid, name: event.name} : null; + const new_category = event.value; + this.new_file.category = Object.keys(new_category).length ? {uid: new_category.uid, name: new_category.name} : null; } categoryComparisonFunction(option: Category, value: Category): boolean { diff --git a/src/app/posts.services.ts b/src/app/posts.services.ts index c384f33..186418a 100644 --- a/src/app/posts.services.ts +++ b/src/app/posts.services.ts @@ -457,15 +457,15 @@ export class PostsService implements CanActivate { return this.http.post(this.path + 'getPlaylist', body, this.httpOptions); } + getPlaylists(include_categories = false) { + return this.http.post(this.path + 'getPlaylists', {include_categories: include_categories}, this.httpOptions); + } + incrementViewCount(file_uid, sub_id, uuid) { const body: IncrementViewCountRequest = {file_uid: file_uid, sub_id: sub_id, uuid: uuid}; return this.http.post(this.path + 'incrementViewCount', body, this.httpOptions); } - getPlaylists() { - return this.http.post(this.path + 'getPlaylists', {}, this.httpOptions); - } - updatePlaylist(playlist: Playlist) { const body: UpdatePlaylistRequest = {playlist: playlist}; return this.http.post(this.path + 'updatePlaylist', body, this.httpOptions);