Add docs
This commit is contained in:
parent
85b1d8de09
commit
9ddfbcec67
43
dist/immich.js
vendored
43
dist/immich.js
vendored
File diff suppressed because one or more lines are too long
57
dist/index.js
vendored
57
dist/index.js
vendored
File diff suppressed because one or more lines are too long
12
dist/render.js
vendored
12
dist/render.js
vendored
@ -20,17 +20,17 @@ class Render {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
gallery(res, assets, openItem) {
|
gallery(res, share, openItem) {
|
||||||
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
||||||
const items = [];
|
const items = [];
|
||||||
for (const asset of assets) {
|
for (const asset of share.assets) {
|
||||||
let video;
|
let video;
|
||||||
if (asset.type === types_1.AssetType.video) {
|
if (asset.type === types_1.AssetType.video) {
|
||||||
// Populate the data-video property
|
// Populate the data-video property
|
||||||
video = JSON.stringify({
|
video = JSON.stringify({
|
||||||
source: [
|
source: [
|
||||||
{
|
{
|
||||||
src: immich_1.default.videoUrl(asset.id),
|
src: immich_1.default.videoUrl(share.key, asset.id),
|
||||||
type: yield immich_1.default.getContentType(asset)
|
type: yield immich_1.default.getContentType(asset)
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -41,8 +41,8 @@ class Render {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
items.push({
|
items.push({
|
||||||
originalUrl: immich_1.default.photoUrl(asset.id),
|
originalUrl: immich_1.default.photoUrl(share.key, asset.id),
|
||||||
thumbnailUrl: immich_1.default.photoUrl(asset.id, types_1.ImageSize.thumbnail),
|
thumbnailUrl: immich_1.default.photoUrl(share.key, asset.id, types_1.ImageSize.thumbnail),
|
||||||
video
|
video
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -55,4 +55,4 @@ class Render {
|
|||||||
}
|
}
|
||||||
const render = new Render();
|
const render = new Render();
|
||||||
exports.default = render;
|
exports.default = render;
|
||||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVuZGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3JlbmRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw4REFBNkI7QUFFN0IsbUNBQXFEO0FBQ3JELDBEQUF5QjtBQUV6QixNQUFNLE1BQU07SUFDSixXQUFXLENBQUUsR0FBYSxFQUFFLEtBQVksRUFBRSxJQUFnQjs7WUFDOUQsTUFBTSxJQUFJLEdBQUcsTUFBTSxnQkFBTSxDQUFDLGNBQWMsQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLENBQUE7WUFDckQsSUFBSSxJQUFJLEVBQUUsQ0FBQztnQkFDVCxLQUFLLE1BQU0sTUFBTSxJQUFJLENBQUMsY0FBYyxFQUFFLGdCQUFnQixDQUFDLEVBQUUsQ0FBQztvQkFDeEQsR0FBRyxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFBO2dCQUN2QyxDQUFDO2dCQUNELE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxJQUFBLGVBQUssR0FBRSxDQUFDLE1BQU0sRUFBRSxrQkFBa0IsS0FBSyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUE7Z0JBQzVELEdBQUcsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQyxDQUFDLENBQUE7WUFDakQsQ0FBQztpQkFBTSxDQUFDO2dCQUNOLEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUE7WUFDeEIsQ0FBQztRQUNILENBQUM7S0FBQTtJQUVLLE9BQU8sQ0FBRSxHQUFhLEVBQUUsTUFBZSxFQUFFLFFBQWlCOztZQUM5RCxNQUFNLEtBQUssR0FBRyxFQUFFLENBQUE7WUFDaEIsS0FBSyxNQUFNLEtBQUssSUFBSSxNQUFNLEVBQUUsQ0FBQztnQkFDM0IsSUFBSSxLQUFLLENBQUE7Z0JBQ1QsSUFBSSxLQUFLLENBQUMsSUFBSSxLQUFLLGlCQUFTLENBQUMsS0FBSyxFQUFFLENBQUM7b0JBQ25DLG1DQUFtQztvQkFDbkMsS0FBSyxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUM7d0JBQ3JCLE1BQU0sRUFBRTs0QkFDTjtnQ0FDRSxHQUFHLEVBQUUsZ0JBQU0sQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztnQ0FDOUIsSUFBSSxFQUFFLE1BQU0sZ0JBQU0sQ0FBQyxjQUFjLENBQUMsS0FBSyxDQUFDOzZCQUN6Qzt5QkFDRjt3QkFDRCxVQUFVLEVBQUU7NEJBQ1YsT0FBTyxFQUFFLEtBQUs7NEJBQ2QsUUFBUSxFQUFFLElBQUk7eUJBQ2Y7cUJBQ0YsQ0FBQyxDQUFBO2dCQUNKLENBQUM7Z0JBQ0QsS0FBSyxDQUFDLElBQUksQ0FBQztvQkFDVCxXQUFXLEVBQUUsZ0JBQU0sQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztvQkFDdEMsWUFBWSxFQUFFLGdCQUFNLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxFQUFFLEVBQUUsaUJBQVMsQ0FBQyxTQUFTLENBQUM7b0JBQzVELEtBQUs7aUJBQ04sQ0FBQyxDQUFBO1lBQ0osQ0FBQztZQUNELEdBQUcsQ0FBQyxNQUFNLENBQUMsU0FBUyxFQUFFO2dCQUNwQixLQUFLO2dCQUNMLFFBQVE7YUFDVCxDQUFDLENBQUE7UUFDSixDQUFDO0tBQUE7Q0FDRjtBQUVELE1BQU0sTUFBTSxHQUFHLElBQUksTUFBTSxFQUFFLENBQUE7QUFFM0Isa0JBQWUsTUFBTSxDQUFBIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGltbWljaCBmcm9tICcuL2ltbWljaCdcbmltcG9ydCB7IFJlc3BvbnNlIH0gZnJvbSAnZXhwcmVzcy1zZXJ2ZS1zdGF0aWMtY29yZSdcbmltcG9ydCB7IEFzc2V0LCBBc3NldFR5cGUsIEltYWdlU2l6ZSB9IGZyb20gJy4vdHlwZXMnXG5pbXBvcnQgZGF5anMgZnJvbSAnZGF5anMnXG5cbmNsYXNzIFJlbmRlciB7XG4gIGFzeW5jIGFzc2V0QnVmZmVyIChyZXM6IFJlc3BvbnNlLCBhc3NldDogQXNzZXQsIHNpemU/OiBJbWFnZVNpemUpIHtcbiAgICBjb25zdCBkYXRhID0gYXdhaXQgaW1taWNoLmdldEFzc2V0QnVmZmVyKGFzc2V0LCBzaXplKVxuICAgIGlmIChkYXRhKSB7XG4gICAgICBmb3IgKGNvbnN0IGhlYWRlciBvZiBbJ2NvbnRlbnQtdHlwZScsICdjb250ZW50LWxlbmd0aCddKSB7XG4gICAgICAgIHJlcy5zZXQoaGVhZGVyLCBkYXRhLmhlYWRlcnNbaGVhZGVyXSlcbiAgICAgIH1cbiAgICAgIGNvbnNvbGUubG9nKGAke2RheWpzKCkuZm9ybWF0KCl9IFNlcnZpbmcgYXNzZXQgJHthc3NldC5pZH1gKVxuICAgICAgcmVzLnNlbmQoQnVmZmVyLmZyb20oYXdhaXQgZGF0YS5hcnJheUJ1ZmZlcigpKSlcbiAgICB9IGVsc2Uge1xuICAgICAgcmVzLnN0YXR1cyg0MDQpLnNlbmQoKVxuICAgIH1cbiAgfVxuXG4gIGFzeW5jIGdhbGxlcnkgKHJlczogUmVzcG9uc2UsIGFzc2V0czogQXNzZXRbXSwgb3Blbkl0ZW0/OiBudW1iZXIpIHtcbiAgICBjb25zdCBpdGVtcyA9IFtdXG4gICAgZm9yIChjb25zdCBhc3NldCBvZiBhc3NldHMpIHtcbiAgICAgIGxldCB2aWRlb1xuICAgICAgaWYgKGFzc2V0LnR5cGUgPT09IEFzc2V0VHlwZS52aWRlbykge1xuICAgICAgICAvLyBQb3B1bGF0ZSB0aGUgZGF0YS12aWRlbyBwcm9wZXJ0eVxuICAgICAgICB2aWRlbyA9IEpTT04uc3RyaW5naWZ5KHtcbiAgICAgICAgICBzb3VyY2U6IFtcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgc3JjOiBpbW1pY2gudmlkZW9VcmwoYXNzZXQuaWQpLFxuICAgICAgICAgICAgICB0eXBlOiBhd2FpdCBpbW1pY2guZ2V0Q29udGVudFR5cGUoYXNzZXQpXG4gICAgICAgICAgICB9XG4gICAgICAgICAgXSxcbiAgICAgICAgICBhdHRyaWJ1dGVzOiB7XG4gICAgICAgICAgICBwcmVsb2FkOiBmYWxzZSxcbiAgICAgICAgICAgIGNvbnRyb2xzOiB0cnVlXG4gICAgICAgICAgfVxuICAgICAgICB9KVxuICAgICAgfVxuICAgICAgaXRlbXMucHVzaCh7XG4gICAgICAgIG9yaWdpbmFsVXJsOiBpbW1pY2gucGhvdG9VcmwoYXNzZXQuaWQpLFxuICAgICAgICB0aHVtYm5haWxVcmw6IGltbWljaC5waG90b1VybChhc3NldC5pZCwgSW1hZ2VTaXplLnRodW1ibmFpbCksXG4gICAgICAgIHZpZGVvXG4gICAgICB9KVxuICAgIH1cbiAgICByZXMucmVuZGVyKCdnYWxsZXJ5Jywge1xuICAgICAgaXRlbXMsXG4gICAgICBvcGVuSXRlbVxuICAgIH0pXG4gIH1cbn1cblxuY29uc3QgcmVuZGVyID0gbmV3IFJlbmRlcigpXG5cbmV4cG9ydCBkZWZhdWx0IHJlbmRlclxuIl19
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVuZGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3JlbmRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw4REFBNkI7QUFFN0IsbUNBQWlFO0FBQ2pFLDBEQUF5QjtBQUV6QixNQUFNLE1BQU07SUFDSixXQUFXLENBQUUsR0FBYSxFQUFFLEtBQVksRUFBRSxJQUFnQjs7WUFDOUQsTUFBTSxJQUFJLEdBQUcsTUFBTSxnQkFBTSxDQUFDLGNBQWMsQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLENBQUE7WUFDckQsSUFBSSxJQUFJLEVBQUUsQ0FBQztnQkFDVCxLQUFLLE1BQU0sTUFBTSxJQUFJLENBQUMsY0FBYyxFQUFFLGdCQUFnQixDQUFDLEVBQUUsQ0FBQztvQkFDeEQsR0FBRyxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFBO2dCQUN2QyxDQUFDO2dCQUNELE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxJQUFBLGVBQUssR0FBRSxDQUFDLE1BQU0sRUFBRSxrQkFBa0IsS0FBSyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUE7Z0JBQzVELEdBQUcsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQyxDQUFDLENBQUE7WUFDakQsQ0FBQztpQkFBTSxDQUFDO2dCQUNOLEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUE7WUFDeEIsQ0FBQztRQUNILENBQUM7S0FBQTtJQUVLLE9BQU8sQ0FBRSxHQUFhLEVBQUUsS0FBaUIsRUFBRSxRQUFpQjs7WUFDaEUsTUFBTSxLQUFLLEdBQUcsRUFBRSxDQUFBO1lBQ2hCLEtBQUssTUFBTSxLQUFLLElBQUksS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDO2dCQUNqQyxJQUFJLEtBQUssQ0FBQTtnQkFDVCxJQUFJLEtBQUssQ0FBQyxJQUFJLEtBQUssaUJBQVMsQ0FBQyxLQUFLLEVBQUUsQ0FBQztvQkFDbkMsbUNBQW1DO29CQUNuQyxLQUFLLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQzt3QkFDckIsTUFBTSxFQUFFOzRCQUNOO2dDQUNFLEdBQUcsRUFBRSxnQkFBTSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxFQUFFLENBQUM7Z0NBQ3pDLElBQUksRUFBRSxNQUFNLGdCQUFNLENBQUMsY0FBYyxDQUFDLEtBQUssQ0FBQzs2QkFDekM7eUJBQ0Y7d0JBQ0QsVUFBVSxFQUFFOzRCQUNWLE9BQU8sRUFBRSxLQUFLOzRCQUNkLFFBQVEsRUFBRSxJQUFJO3lCQUNmO3FCQUNGLENBQUMsQ0FBQTtnQkFDSixDQUFDO2dCQUNELEtBQUssQ0FBQyxJQUFJLENBQUM7b0JBQ1QsV0FBVyxFQUFFLGdCQUFNLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLEVBQUUsQ0FBQztvQkFDakQsWUFBWSxFQUFFLGdCQUFNLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLEVBQUUsRUFBRSxpQkFBUyxDQUFDLFNBQVMsQ0FBQztvQkFDdkUsS0FBSztpQkFDTixDQUFDLENBQUE7WUFDSixDQUFDO1lBQ0QsR0FBRyxDQUFDLE1BQU0sQ0FBQyxTQUFTLEVBQUU7Z0JBQ3BCLEtBQUs7Z0JBQ0wsUUFBUTthQUNULENBQUMsQ0FBQTtRQUNKLENBQUM7S0FBQTtDQUNGO0FBRUQsTUFBTSxNQUFNLEdBQUcsSUFBSSxNQUFNLEVBQUUsQ0FBQTtBQUUzQixrQkFBZSxNQUFNLENBQUEiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgaW1taWNoIGZyb20gJy4vaW1taWNoJ1xuaW1wb3J0IHsgUmVzcG9uc2UgfSBmcm9tICdleHByZXNzLXNlcnZlLXN0YXRpYy1jb3JlJ1xuaW1wb3J0IHsgQXNzZXQsIEFzc2V0VHlwZSwgSW1hZ2VTaXplLCBTaGFyZWRMaW5rIH0gZnJvbSAnLi90eXBlcydcbmltcG9ydCBkYXlqcyBmcm9tICdkYXlqcydcblxuY2xhc3MgUmVuZGVyIHtcbiAgYXN5bmMgYXNzZXRCdWZmZXIgKHJlczogUmVzcG9uc2UsIGFzc2V0OiBBc3NldCwgc2l6ZT86IEltYWdlU2l6ZSkge1xuICAgIGNvbnN0IGRhdGEgPSBhd2FpdCBpbW1pY2guZ2V0QXNzZXRCdWZmZXIoYXNzZXQsIHNpemUpXG4gICAgaWYgKGRhdGEpIHtcbiAgICAgIGZvciAoY29uc3QgaGVhZGVyIG9mIFsnY29udGVudC10eXBlJywgJ2NvbnRlbnQtbGVuZ3RoJ10pIHtcbiAgICAgICAgcmVzLnNldChoZWFkZXIsIGRhdGEuaGVhZGVyc1toZWFkZXJdKVxuICAgICAgfVxuICAgICAgY29uc29sZS5sb2coYCR7ZGF5anMoKS5mb3JtYXQoKX0gU2VydmluZyBhc3NldCAke2Fzc2V0LmlkfWApXG4gICAgICByZXMuc2VuZChCdWZmZXIuZnJvbShhd2FpdCBkYXRhLmFycmF5QnVmZmVyKCkpKVxuICAgIH0gZWxzZSB7XG4gICAgICByZXMuc3RhdHVzKDQwNCkuc2VuZCgpXG4gICAgfVxuICB9XG5cbiAgYXN5bmMgZ2FsbGVyeSAocmVzOiBSZXNwb25zZSwgc2hhcmU6IFNoYXJlZExpbmssIG9wZW5JdGVtPzogbnVtYmVyKSB7XG4gICAgY29uc3QgaXRlbXMgPSBbXVxuICAgIGZvciAoY29uc3QgYXNzZXQgb2Ygc2hhcmUuYXNzZXRzKSB7XG4gICAgICBsZXQgdmlkZW9cbiAgICAgIGlmIChhc3NldC50eXBlID09PSBBc3NldFR5cGUudmlkZW8pIHtcbiAgICAgICAgLy8gUG9wdWxhdGUgdGhlIGRhdGEtdmlkZW8gcHJvcGVydHlcbiAgICAgICAgdmlkZW8gPSBKU09OLnN0cmluZ2lmeSh7XG4gICAgICAgICAgc291cmNlOiBbXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIHNyYzogaW1taWNoLnZpZGVvVXJsKHNoYXJlLmtleSwgYXNzZXQuaWQpLFxuICAgICAgICAgICAgICB0eXBlOiBhd2FpdCBpbW1pY2guZ2V0Q29udGVudFR5cGUoYXNzZXQpXG4gICAgICAgICAgICB9XG4gICAgICAgICAgXSxcbiAgICAgICAgICBhdHRyaWJ1dGVzOiB7XG4gICAgICAgICAgICBwcmVsb2FkOiBmYWxzZSxcbiAgICAgICAgICAgIGNvbnRyb2xzOiB0cnVlXG4gICAgICAgICAgfVxuICAgICAgICB9KVxuICAgICAgfVxuICAgICAgaXRlbXMucHVzaCh7XG4gICAgICAgIG9yaWdpbmFsVXJsOiBpbW1pY2gucGhvdG9Vcmwoc2hhcmUua2V5LCBhc3NldC5pZCksXG4gICAgICAgIHRodW1ibmFpbFVybDogaW1taWNoLnBob3RvVXJsKHNoYXJlLmtleSwgYXNzZXQuaWQsIEltYWdlU2l6ZS50aHVtYm5haWwpLFxuICAgICAgICB2aWRlb1xuICAgICAgfSlcbiAgICB9XG4gICAgcmVzLnJlbmRlcignZ2FsbGVyeScsIHtcbiAgICAgIGl0ZW1zLFxuICAgICAgb3Blbkl0ZW1cbiAgICB9KVxuICB9XG59XG5cbmNvbnN0IHJlbmRlciA9IG5ldyBSZW5kZXIoKVxuXG5leHBvcnQgZGVmYXVsdCByZW5kZXJcbiJdfQ==
|
@ -1,6 +1,10 @@
|
|||||||
import { Asset, AssetType, ImageSize, SharedLink } from './types'
|
import { Asset, AssetType, ImageSize, SharedLink } from './types'
|
||||||
|
|
||||||
class Immich {
|
class Immich {
|
||||||
|
/**
|
||||||
|
* Make a request to Immich API. We're not using the SDK to limit
|
||||||
|
* the possible attack surface of this app.
|
||||||
|
*/
|
||||||
async request (endpoint: string) {
|
async request (endpoint: string) {
|
||||||
const res = await fetch(process.env.IMMICH_URL + '/api' + endpoint, {
|
const res = await fetch(process.env.IMMICH_URL + '/api' + endpoint, {
|
||||||
headers: {
|
headers: {
|
||||||
@ -17,11 +21,22 @@ class Immich {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query Immich for the SharedLink metadata for a given key.
|
||||||
|
* The key is what is returned in the URL when you create a share in Immich.
|
||||||
|
* Immich doesn't have a method to query by key, so this method gets all
|
||||||
|
* known shared links, and returns the link which matches the provided key.
|
||||||
|
*/
|
||||||
async getShareByKey (key: string) {
|
async getShareByKey (key: string) {
|
||||||
const res = (await this.request('/shared-links') || []) as SharedLink[]
|
const res = (await this.request('/shared-links') || []) as SharedLink[]
|
||||||
return res?.find(x => x.key === key)
|
return res?.find(x => x.key === key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stream asset buffer data from Immich.
|
||||||
|
* For photos, you can request 'thumbnail' or 'original' size.
|
||||||
|
* For videos, it is Immich's streaming quality, not the original quality.
|
||||||
|
*/
|
||||||
async getAssetBuffer (asset: Asset, size?: ImageSize) {
|
async getAssetBuffer (asset: Asset, size?: ImageSize) {
|
||||||
switch (asset.type) {
|
switch (asset.type) {
|
||||||
case AssetType.image:
|
case AssetType.image:
|
||||||
@ -32,22 +47,41 @@ class Immich {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the content-type of an Immich asset
|
||||||
|
*/
|
||||||
async getContentType (asset: Asset) {
|
async getContentType (asset: Asset) {
|
||||||
const assetBuffer = await this.getAssetBuffer(asset)
|
const assetBuffer = await this.getAssetBuffer(asset)
|
||||||
return assetBuffer.headers.get('Content-Type')
|
return assetBuffer.headers.get('Content-Type')
|
||||||
}
|
}
|
||||||
|
|
||||||
photoUrl (id: string, size?: ImageSize) {
|
/**
|
||||||
return `${process.env.SERVER_URL}/photo/${id}` + (size ? `?size=${size}` : '')
|
* Return the image data URL for a photo
|
||||||
|
*/
|
||||||
|
photoUrl (key: string, id: string, size?: ImageSize) {
|
||||||
|
return `${process.env.SERVER_URL}/photo/${key}/${id}` + (size ? `?size=${size}` : '')
|
||||||
}
|
}
|
||||||
|
|
||||||
videoUrl (id: string) {
|
/**
|
||||||
return `${process.env.SERVER_URL}/video/${id}`
|
* Return the video data URL for a video
|
||||||
|
*/
|
||||||
|
videoUrl (key: string, id: string) {
|
||||||
|
return `${process.env.SERVER_URL}/video/${key}/${id}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a provided ID matches the Immich ID format
|
||||||
|
*/
|
||||||
isId (id: string) {
|
isId (id: string) {
|
||||||
return !!id.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/)
|
return !!id.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a provided key matches the Immich shared-link key format
|
||||||
|
*/
|
||||||
|
isKey (key: string) {
|
||||||
|
return !!key.match(/^[\w-]+$/)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const immich = new Immich()
|
const immich = new Immich()
|
||||||
|
42
src/index.ts
42
src/index.ts
@ -16,42 +16,46 @@ const getSize = (req: Request) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
app.get('/share/:key', async (req, res) => {
|
app.get('/share/:key', async (req, res) => {
|
||||||
if (req.params.key.match(/[^\w-]/)) {
|
if (!immich.isKey(req.params.key)) {
|
||||||
// Invalid characters in the incoming URL
|
|
||||||
res.status(404).send()
|
res.status(404).send()
|
||||||
} else {
|
} else {
|
||||||
const share = await immich.getShareByKey(req.params.key)
|
const sharedLink = await immich.getShareByKey(req.params.key)
|
||||||
if (!share || !share.assets.length) {
|
if (!sharedLink || !sharedLink.assets.length) {
|
||||||
res.status(404).send()
|
res.status(404).send()
|
||||||
} else if (share.assets.length === 1) {
|
} else if (sharedLink.assets.length === 1) {
|
||||||
// This is an individual item (not a gallery)
|
// This is an individual item (not a gallery)
|
||||||
const asset = share.assets[0]
|
const asset = sharedLink.assets[0]
|
||||||
if (asset.type === AssetType.image) {
|
if (asset.type === AssetType.image) {
|
||||||
// For photos, output the image directly
|
// For photos, output the image directly
|
||||||
await render.assetBuffer(res, share.assets[0], getSize(req))
|
await render.assetBuffer(res, sharedLink.assets[0], getSize(req))
|
||||||
} else if (asset.type === AssetType.video) {
|
} else if (asset.type === AssetType.video) {
|
||||||
// For videos, show the video as a web player
|
// For videos, show the video as a web player
|
||||||
await render.gallery(res, share.assets, 1)
|
await render.gallery(res, sharedLink, 1)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Multiple images - render as a gallery
|
// Multiple images - render as a gallery
|
||||||
await render.gallery(res, share.assets)
|
await render.gallery(res, sharedLink)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Output the buffer data for an photo or video
|
// Output the buffer data for an photo or video
|
||||||
app.get('/:type(photo|video)/:id', (req, res) => {
|
app.get('/:type(photo|video)/:key/:id', async (req, res) => {
|
||||||
if (!immich.isId(req.params.id) || !['photo', 'video'].includes(req.params.type)) {
|
// Check for valid key and ID
|
||||||
// Invalid characters in the incoming URL
|
if (immich.isKey(req.params.key) && immich.isId(req.params.id)) {
|
||||||
res.status(404).send()
|
// Check if the key is a valid share link
|
||||||
return
|
const sharedLink = await immich.getShareByKey(req.params.key)
|
||||||
|
if (sharedLink?.assets.length) {
|
||||||
|
// Check that the requested asset exists in this share
|
||||||
|
const asset = sharedLink.assets.find(x => x.id === req.params.id)
|
||||||
|
if (asset) {
|
||||||
|
asset.type = req.params.type === 'video' ? AssetType.video : AssetType.image
|
||||||
|
render.assetBuffer(res, asset, getSize(req)).then()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const asset = {
|
res.status(404).send()
|
||||||
id: req.params.id,
|
|
||||||
type: req.params.type === 'video' ? AssetType.video : AssetType.image
|
|
||||||
}
|
|
||||||
render.assetBuffer(res, asset, getSize(req)).then()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// Send a 404 for all other unmatched routes
|
// Send a 404 for all other unmatched routes
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import immich from './immich'
|
import immich from './immich'
|
||||||
import { Response } from 'express-serve-static-core'
|
import { Response } from 'express-serve-static-core'
|
||||||
import { Asset, AssetType, ImageSize } from './types'
|
import { Asset, AssetType, ImageSize, SharedLink } from './types'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
class Render {
|
class Render {
|
||||||
@ -17,16 +17,16 @@ class Render {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async gallery (res: Response, assets: Asset[], openItem?: number) {
|
async gallery (res: Response, share: SharedLink, openItem?: number) {
|
||||||
const items = []
|
const items = []
|
||||||
for (const asset of assets) {
|
for (const asset of share.assets) {
|
||||||
let video
|
let video
|
||||||
if (asset.type === AssetType.video) {
|
if (asset.type === AssetType.video) {
|
||||||
// Populate the data-video property
|
// Populate the data-video property
|
||||||
video = JSON.stringify({
|
video = JSON.stringify({
|
||||||
source: [
|
source: [
|
||||||
{
|
{
|
||||||
src: immich.videoUrl(asset.id),
|
src: immich.videoUrl(share.key, asset.id),
|
||||||
type: await immich.getContentType(asset)
|
type: await immich.getContentType(asset)
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -37,8 +37,8 @@ class Render {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
items.push({
|
items.push({
|
||||||
originalUrl: immich.photoUrl(asset.id),
|
originalUrl: immich.photoUrl(share.key, asset.id),
|
||||||
thumbnailUrl: immich.photoUrl(asset.id, ImageSize.thumbnail),
|
thumbnailUrl: immich.photoUrl(share.key, asset.id, ImageSize.thumbnail),
|
||||||
video
|
video
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user