Open bpatrik opened 1 year ago
I like this idea a lot, and I think it has a lot of potential to serve as a bridge between what you're developing pigallery2 for (as you lay out in #738) and what other users are wanting.
As far as I can tell, the biggest philosophical difference I have is with this concept in #738:
Source of truth is the disk: DB is only a cache
In my particular case, there is an additional source of truth: the DigiKam DB. This is where I mark individual directories to be "Public" (i.e. displayed on pigallery2), and where I manually select directory covers/previews/thumbnails/highlights.
Allowing me to code up a custom extension that intervenes in the "Directory Scanning" and "Preview Filling" jobs would be pretty much a perfect solution.
Added backend extension support (in alpha version). So far the app looks for extension subfolders in the
<pigallery src root>/extensions/myextension/package.js <- this is optional. You can add extra npm packages here
<pigallery src root>/extensions/myextension/server.js <- this is needed
sample server js:
/* eslint-disable @typescript-eslint/no-inferrable-types */
import {IExtensionObject} from '../../src/backend/model/extension/IExtension';
import {PhotoMetadata} from '../../src/common/entities/PhotoDTO';
import {Column, Entity, Index, PrimaryGeneratedColumn} from 'typeorm';
import {SubConfigClass} from 'typeconfig/src/decorators/class/SubConfigClass';
import {ConfigProperty} from 'typeconfig/src/decorators/property/ConfigPropoerty';
@Entity()
class TestLoggerEntity {
@Index()
@PrimaryGeneratedColumn({unsigned: true})
id: number;
@Column()
text: string;
}
@SubConfigClass({softReadonly: true})
export class TestConfig {
@ConfigProperty({
tags: {
name: `Test config value`,
},
description: `This is just a description`,
})
cleanUpUnusedTables: boolean = true;
}
export const init = async (extension: IExtensionObject<TestConfig>): Promise<void> => {
extension.Logger.debug('Setting up');
// override metadata loader example
extension.events.gallery.MetadataLoader
.loadPhotoMetadata.before(async (input, event) => {
extension.Logger.debug('skipping');
event.stopPropagation = true;
return {
size: {width: 1, height: 1},
fileSize: 1,
creationDate: 0
} as PhotoMetadata;
});
extension.config.setTemplate(TestConfig);
console.log(JSON.stringify(extension._app.config.Extensions));
// add new res API example
extension.RESTApi.get.jsonResponse(['/hw'], null, async () => {
const conn = await extension.db.getSQLConnection();
conn.getRepository(TestLoggerEntity).save({text: 'logger entry' + Date.now()});
return await conn.getRepository(TestLoggerEntity).find();
});
// set own tables
await extension.db.setExtensionTables([TestLoggerEntity]);
// add messenger example
extension.messengers.addMessenger('loggerMsg',
[{
id: 'text',
type: 'string',
name: 'just a text',
description: 'nothing to mention here',
defaultValue: 'I hand picked these photos just for you:',
}],
{
sendMedia: async (c, m) => {
console.log(m);
}
});
};
export const cleanUp = async (extension: IExtensionObject<TestConfig>): Promise<void> => {
extension.Logger.debug('Cleaning up');
};
See https://github.com/bpatrik/pigallery2/blob/master/src/backend/model/extension/IExtension.ts for full extension API
a more in depth documentation and easier usage comes later
Kudos @bpatrik for this extensive new feature, I am loving it. Thank you for staying innovative!
Documentation for extensions is available here: https://github.com/bpatrik/pigallery2/tree/master/extension
Added sample extension here: https://github.com/bpatrik/pigallery2-sample-extension
This is great, thanks so much!
I've written a 'DigiKam Connector' plugin that (mostly) suits my needs: https://github.com/mblythe86/pigallery2-extension-digikam-connector
I'm relatively inexperienced at JavaScript/TypeScript development (my day job mostly uses shell/ruby/perl). If you would be willing, I'd really appreciate it if you could take a look over my code and give me some feedback. Am I following good TypeScript conventions? Do you think I'm using the extension API in a sane way? etc...
While creating this extension, I did hit a few speedbumps. I'll file separate issues (or pull requests) for the places where I think pigallery2 extension support could be improved, and with a question I have.
Thanks again!
Had a quick look, this is very impressive @mblythe86! When I was creating the extension, I was not thinking about calling an external DB and doing decisions in the app :)
I think the code looks good. Probably I would have had the same approach. (In my work I also do not user Typescript, so 🤷♂️)
To answer you readme, the plan is to have a nice config surface (also unified one with the jobs/messenger), but that I run into some issues.
I'm curious hear about you dev exp. here or in an other bug.
Thanks for taking a look!
the plan is to have a nice config surface (also unified one with the jobs/messenger), but that I run into some issues.
Yeah, implementing that sounds tedious. It's definitely something that I'd put off.
An quick-and-dirty improvement might be to just have the JSON pretty-printed in the text box, and have the text box auto-scale to the contents.
I'm curious hear about you dev exp
Overall, it was good!
I really like the before/after hooks that you have in the extension API. It makes it very convenient to either replace the implementation of the wrapped function (as I did with getCoverForDirectory), or just tweak the return value of the function a little without re-implementing the whole thing (as I did with scanDirectory).
There are a couple ways that the dev experience could improve, but none are showstoppers:
NODE_ENV=debug
mode to avoid a performance impact)at Array.<anonymous> (/app/data/config/extensions/sample-extension/server.js:324:14)
I think that the *.js.map
file is supposed to allow for debuggers to map back to the original TypeScript code location. It would be nice if the backtrace mechanism could use that.Backtraces
Source file support is a good idea. Added js.map files to the release. Using them is disabled by default as random google searches suggests that it would slow down the app in prod: https://github.com/nodejs/node/issues/41541, https://nodejs.org/docs/latest-v18.x/api/cli.html#--enable-source-maps
It can be enabled with env NODE_OPTIONS=--enable-source-maps
Yeah, implementing that sounds tedious. It's definitely something that I'd put off.
The app config is one of the most complex part of the app. I might have overcomplicated it. But now it supports declarative config creation and UI generataion which is awesome, but it has some blind spots.
I'm long thinkg about rewriting some part of it, but I can't find the will poiwer to do so as its not the fun part of the app :)
I really like the before/after hooks
Note: after hooks parameter list slightly changed yesterday in a beaking way. see aa4c8a2
Extension re-loading
I see waht you mean. changing any config through the UI reloads the extensions. That would be a short term workaround. In longer term, I was not thinking about a reload button, but an enable disable button that would basically also do a relaod (because of the config change aspect of it)
Hi! Is the status of this feature up-to-date? Looking around, it looks like most of it is now implemented. Or are there still remaining blocking points before issuing a new release?
Repository and frontend extension support is still not ready.
On Sun, Jun 23, 2024 at 11:57 AM Tofe @.***> wrote:
Hi! Is the status of this feature up-to-date? Looking around, it looks like most of it is now implemented. Or are there still remaining blocking points before issuing a new release?
— Reply to this email directly, view it on GitHub https://github.com/bpatrik/pigallery2/issues/743#issuecomment-2184927014, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABZKA5UGWMN6OCP6PA5O4I3ZI2LYNAVCNFSM6AAAAABJYHDPPGVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDCOBUHEZDOMBRGQ . You are receiving this because you were mentioned.Message ID: @.***>
This a tracking bug and "design doc" for the next mayor update that will be about extension support.
Requirement:
Design ideas
Extension will live in their own folder, next to the config.json file. config folder is by default writable from the docker setup, so it should be easy to add there more files. It will be written in Javascript (I hope its not a question :) ).
server side extension
Creating a server side extension is the easy part. The app will assume a presence of an
server.js
that has afunction init(app)
function. App will call the init function of all extensions on startup. If the folder has package.json, the app will run annpm isntall
so that the extension developers can use external libraries (like if someone wants to develop an extension to support bmp files). Theapp
object in theinit
function will represent an interface to the app, where extension developers can subscribe to certain events of the app. These events will be, like:before directory scanning
orafter directory scanning
. With this developers can override the input and the output of the directory scanning event. (So things like also indexing doc files or skipping files that start with_
can be added). Furthermoreapp
object will also export low level api. Low level API will be less stable than the exported events, but with that developers can override any aspect of the app without restriction.app
object will also export the currently used config.client side extensions
This is the tricky one as the client side is built and minified and localized AOT with webpack. Therefore there is one build app for all language. Exposing the full client app to the extension is not an option as its is not possible to know in extension development time how webapck will minify the function names. I also could not find a way to add new angular component to the app after compile. Eg.: So I do not see a way to add UI for supporting docx files on UI through extensions.
I was thinking about exposing callback for the client extension, where developers can add icons or buttons in a very restrictive way to the app. And also here could the developer inject some business logic to the client.
repository
I do not want to host a custom repository. I think I will just "hack" it into a readme file. Developers will maintain extensions on github and add a link to their repo to to a readme file in the repository. This way the app will be able to auto discover the list of extensions.
Future work
Security can be implemented in the future where the app makes it explicit what resources an extension uses. Like it changes the config or touches low-level APIs.
Open questions
I don't really know how to solve the client side extension support properly
Sub tasks