Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
Fork of [Allusion](https://github.com/allusion-app/Allusion/) with:
- Video support.
- Video and GIF playback options.
- Bulk tagging.
- Implied tag relationships.
- Automatic inheritance of implied tags.
- Fully compatible with advanced search.
Expand All @@ -23,7 +24,7 @@ Fork of [Allusion](https://github.com/allusion-app/Allusion/) with:
- Find tags using the contextual menu in the tag editor.
- Adjustable padding for thumbnails in the gallery.
- Multiple optimizations and bug fixes.
- And more see the changelogs in the releases page for more details.
- And more, see the changelogs in the releases page for more details.

Thanks to the developers and the community for your hard work! ❤️

Expand Down
6 changes: 3 additions & 3 deletions resources/style/inspector.scss
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
padding-right: 0.2rem;
}

#inspector-extra-porperties-header {
.inspector-extra-porperties-header {
.toolbar-button {
opacity: 0;

Expand All @@ -100,11 +100,11 @@
}
}

#inspector-extra-porperties-header > div[tabindex='-1'] > .toolbar-button {
.inspector-extra-porperties-header > div[tabindex='-1'] > .toolbar-button {
justify-content: flex-end;
}

&:hover #inspector-extra-porperties-header .toolbar-button {
&:hover .inspector-extra-porperties-header .toolbar-button {
opacity: 1;
}
}
Expand Down
2 changes: 2 additions & 0 deletions resources/style/outliner.scss
Original file line number Diff line number Diff line change
Expand Up @@ -239,10 +239,12 @@
margin-top: 0.25rem;
}

.location-properties-dialog,
.tag-properties-dialog {
display: flex;
flex-direction: column;

#location-properties-dialog-content,
#tag-properties-dialog-content {
width: min(90ch, 80vw);
max-height: 80vh;
Expand Down
1 change: 1 addition & 0 deletions src/api/data-storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ export interface DataStorage {
): Promise<Array<[missingFileId: ID, dbMatch: FileDTO]>>;
clear(): Promise<void>;
setSeed(seed?: number): Promise<void>;
optimizeDatabase(): Promise<void>;
}

export function makeFileBatchFetcher(
Expand Down
1 change: 1 addition & 0 deletions src/api/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export const IMG_EXTENSIONS = [
'jpeg',
'jfif',
'webp',
'avif',
'tif',
'tiff',
'bmp',
Expand Down
2 changes: 1 addition & 1 deletion src/api/location.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export type LocationDTO = {

export type SubLocationDTO = {
id: ID;
name: string;
path: string;
isExcluded: boolean;
subLocations: SubLocationDTO[];
tags: ID[];
Expand Down
1 change: 1 addition & 0 deletions src/api/tag.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ID } from './id';

export const ROOT_TAG_ID = 'root';
export const ROOT_LOCATIONS_TAG_ID = 'locations_root';

export type TagDTO = {
id: ID;
Expand Down
81 changes: 70 additions & 11 deletions src/backend/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ import { FileDTO, FileStats } from 'src/api/file';
import { FileSearchDTO, SearchGroupDTO } from 'src/api/file-search';
import { generateId, ID } from 'src/api/id';
import { LocationDTO, SubLocationDTO } from 'src/api/location';
import { ROOT_TAG_ID, TagDTO } from 'src/api/tag';
import { ROOT_LOCATIONS_TAG_ID, ROOT_TAG_ID, TagDTO } from 'src/api/tag';
import { jsonArrayFrom } from 'kysely/helpers/sqlite';
import { IS_DEV } from 'common/process';
import { UpdateObject } from 'kysely/dist/cjs/parser/update-set-parser';
Expand Down Expand Up @@ -136,7 +136,7 @@ export default class Backend implements DataStorage {
await sql`PRAGMA cache_size = -64000;`.execute(db);
await sql`PRAGMA OPTIMIZE;`.execute(db);

// Create Root Tag if not exists.
// Create Root Tags if not exists.
const rootTag = await db
.selectFrom('tags')
.selectAll()
Expand All @@ -159,6 +159,29 @@ export default class Backend implements DataStorage {
})
.execute();
}
const rootLocationTag = await db
.selectFrom('tags')
.selectAll()
.where('id', '=', ROOT_LOCATIONS_TAG_ID)
.executeTakeFirst();
if (!rootLocationTag) {
await db
.insertInto('tags')
.values({
id: ROOT_LOCATIONS_TAG_ID,
name: 'Root Locations Tag',
dateAdded: serializeDate(new Date()),
color: '',
isHidden: serializeBoolean(false),
isVisibleInherited: serializeBoolean(false),
description: '',
isHeader: serializeBoolean(false),
fileCount: 0,
isFileCountDirty: serializeBoolean(true),
})
.execute();
}

await this.preAggregateJSON();
}

Expand Down Expand Up @@ -230,7 +253,17 @@ export default class Backend implements DataStorage {
SELECT
file_id,
json_group_array(tag_id) AS tags
FROM file_tags
FROM (
-- Source A: Explicit tags
SELECT file_id, tag_id FROM file_tags
UNION ALL
-- Source B: Virtual location tags driven by the directory_path index
SELECT
f.id AS file_id,
l.id AS tag_id
FROM files f
INNER JOIN location_nodes l ON l.path = f.directory_path
)
GROUP BY file_id;
`.execute(this.#db);
await sql`
Expand Down Expand Up @@ -409,7 +442,7 @@ export default class Backend implements DataStorage {
// convert data into SubLocationDTO format
const slc: SubLocationDTO = {
id: dbLoc.id,
name: dbLoc.path,
path: dbLoc.path,
subLocations: [],
tags: dbLoc.tags.map((t) => t.tagId),
isExcluded: deserializeBoolean(dbLoc.isExcluded),
Expand Down Expand Up @@ -632,9 +665,11 @@ export default class Backend implements DataStorage {

try {
// Create temp tables form a copy of the actual tables.
await sql`CREATE TEMP TABLE ${sql.id(tempFiles)} AS SELECT * FROM files WHERE 0`.execute(
trx,
);
const fileKeys = Object.keys(files[0]);
const fileColumns = sql.join(fileKeys.map((key) => sql.ref(key as string)));
await sql`CREATE TEMP TABLE ${sql.id(
tempFiles,
)} AS SELECT ${fileColumns} FROM files WHERE 0`.execute(trx);
await sql`CREATE TEMP TABLE ${sql.id(
tempFileTags,
)} AS SELECT * FROM file_tags WHERE 0`.execute(trx);
Expand Down Expand Up @@ -847,6 +882,13 @@ export default class Backend implements DataStorage {
this.#notifyChange();
}

async optimizeDatabase(): Promise<void> {
console.info('SQLite: Optimize database and free space...');
await sql`PRAGMA optimize;`.execute(this.#db);
await sql`VACUUM;`.execute(this.#db);
this.#notifyChange();
}

async removeSearch(search: ID): Promise<void> {
console.info('SQLite: Removing search...', search);
// Cascade delte in other tables deleting from savedSearches table.
Expand Down Expand Up @@ -1657,11 +1699,24 @@ function applyTagArrayCondition(
.select('fileId')
.where('tagId', 'in', values)
.distinct();
const matchingFilesByLocationTag = eb
.selectFrom('files')
.innerJoin('locationNodes', 'locationNodes.path', 'files.directoryPath')
.select('files.id as fileId')
.where('locationNodes.id', 'in', values) // Location Tag Ids are the same as the locationNode id
.distinct();

if (operator === 'contains') {
return eb('files.id', 'in', matchingFiles);
return eb.or([
eb('files.id', 'in', matchingFiles),
eb('files.id', 'in', matchingFilesByLocationTag),
]);
} else {
// notContains: ensure NOT EXISTS any tag in the list for that file
return eb.not(eb('files.id', 'in', matchingFiles));
return eb.and([
eb.not(eb('files.id', 'in', matchingFiles)),
eb.not(eb('files.id', 'in', matchingFilesByLocationTag)),
]);
}
}
}
Expand Down Expand Up @@ -1791,6 +1846,7 @@ async function upsertTable<
`sampleObject is required when using SQL expressions for table ${String(table)}`,
);
}
const fileKeys = Object.keys(referenceRow);
const columnsToUpdate = Object.keys(referenceRow).filter(
(key) =>
!conflictColumns.includes(key as any) &&
Expand All @@ -1803,7 +1859,10 @@ async function upsertTable<

let query;
if (isExpression) {
query = db.insertInto(table as keyof AllusionDB_SQL & string).expression(values as any);
query = db
.insertInto(table as keyof AllusionDB_SQL & string)
.columns(fileKeys as any)
.expression(values as any);
} else {
query = db.insertInto(table as keyof AllusionDB_SQL & string);
}
Expand Down Expand Up @@ -1883,7 +1942,7 @@ function normalizeLocations(sourcelocations: LocationDTO[]) {
isRoot: boolean,
) {
const parentIdvalue = isRoot ? null : parentId;
const pathValue = 'path' in node ? node.path : node.name;
const pathValue = node.path;
nodeIds.push(node.id);
locationNodes.push({
id: node.id,
Expand Down
1 change: 1 addition & 0 deletions src/backend/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class InlineMigrationProvider implements MigrationProvider {
return {
'000_initial': await import('./migrations/000_initial'),
'001_migrateJSON': (await import('./migrations/001_migrateJSON')).default(context),
'002_files_add_generated_directory_path': await import('./migrations/002_files_add_generated_directory_path'), // eslint-disable-line prettier/prettier
};
}
}
Expand Down
23 changes: 23 additions & 0 deletions src/backend/migrations/002_files_add_generated_directory_path.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* eslint-disable prettier/prettier */
import { Kysely, sql } from 'kysely';

/**
* Migration to add a generated virtual directory column and its index
* to the files table, optimizing location tag aggregation.
*/
export async function up(db: Kysely<any>): Promise<void> {
await db.schema
.alterTable('files')
.addColumn('directory_path', 'text', (col) =>
col.generatedAlwaysAs(sql`SUBSTR(absolute_path, 1, LENGTH(absolute_path) - LENGTH(name) - 1)`)
)
.execute();

// create index
await db.schema.createIndex('idx_files_directory_path').on('files').column('directory_path').execute();
}

export async function down(db: Kysely<any>): Promise<void> {
await db.schema.dropIndex('idx_files_directory_path').execute();
await db.schema.alterTable('files').dropColumn('directory_path').execute();
}
3 changes: 3 additions & 0 deletions src/backend/schemaTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ export type Files = {
width: number;
height: number;
dateCreated: DateAsNumber;

/** Auto generated by SQLite from absolute_path. Read-only field backed by idx_files_directory_path. */
directoryPath: ColumnType<string, never, never>;
};

export type FileTags = {
Expand Down
8 changes: 0 additions & 8 deletions src/frontend/components/FileExtraPropertiesEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,6 @@ export const FileExtraPropertiesEditor = observer(
editableNode: undefined,
});

/*useEffect(() => {
runInAction(() => {
if (file && uiStore.fileSelection.size < 1) {
uiStore.selectFile(file);
}
});
}, [file, uiStore]);*/

const counter: ExtraPropertiesCounter = useComputed(() => {
//Map of Clientstores: and a tuple of count, value
const counter = new Map<ClientExtraProperty, [number, ExtraPropertyValue | undefined]>();
Expand Down
Loading
Loading