Refactored retrieval of categories and improved runtime search of files in category

Fixed issue with editing/saving categories

Database queries can now handle nested object paths

Code cleanup
improved-downloads-management
Isaac Abadi 3 years ago
parent c810d4d878
commit b56c66f705

@ -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);
}

@ -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;

@ -127,7 +127,7 @@ function setConfigItem(key, value) {
success = setConfigFile(config_json);
return success;
};
}
function setConfigItems(items) {
let success = false;

@ -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;
}
}
}

@ -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');
});
})
});

@ -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
}

@ -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;
});

@ -28,12 +28,12 @@
<mat-form-field class="info-field">
<input [(ngModel)]="new_file.thumbnailURL" matInput placeholder="Thumbnail URL" i18n-placeholder="Thumbnail URL" [disabled]="!editing || new_file.thumbnailPath">
</mat-form-field>
<mat-form-field class="info-field">
<mat-select placeholder="Category" i18n-placeholder="Category" [value]="category" (valueChange)="categoryChanged($event)" [compareWith]="categoryComparisonFunction" [disabled]="!editing">
<mat-form-field *ngIf="initialized && postsService.categories" class="info-field">
<mat-select placeholder="Category" i18n-placeholder="Category" [value]="category" (selectionChange)="categoryChanged($event)" [compareWith]="categoryComparisonFunction" [disabled]="!editing">
<mat-option [value]="{}">
N/A
</mat-option>
<mat-option *ngFor="let available_category of postsService.categories | keyvalue" [value]="available_category">
<mat-option *ngFor="let available_category of postsService.categories | keyvalue" [value]="available_category.value">
{{available_category.value.name}}
</mat-option>
</mat-select>

@ -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 {

@ -457,15 +457,15 @@ export class PostsService implements CanActivate {
return this.http.post<GetPlaylistResponse>(this.path + 'getPlaylist', body, this.httpOptions);
}
getPlaylists(include_categories = false) {
return this.http.post<GetPlaylistsRequest>(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<SuccessObject>(this.path + 'incrementViewCount', body, this.httpOptions);
}
getPlaylists() {
return this.http.post<GetPlaylistsRequest>(this.path + 'getPlaylists', {}, this.httpOptions);
}
updatePlaylist(playlist: Playlist) {
const body: UpdatePlaylistRequest = {playlist: playlist};
return this.http.post<SuccessObject>(this.path + 'updatePlaylist', body, this.httpOptions);

Loading…
Cancel
Save