-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 1946ff8
Showing
17 changed files
with
2,326 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
name: Create Release | ||
|
||
on: | ||
workflow_dispatch: | ||
inputs: | ||
tag: | ||
description: 'The next release tag to use' | ||
required: false | ||
type: string | ||
push: | ||
branches: | ||
- 'main' | ||
tags: | ||
- 'v*' | ||
|
||
# Ensures that only one deploy task per branch/environment will run at a time. | ||
concurrency: | ||
group: environment-${{ github.ref }} | ||
cancel-in-progress: true | ||
|
||
jobs: | ||
build: | ||
runs-on: ubuntu-latest | ||
permissions: | ||
contents: write | ||
packages: write | ||
steps: | ||
|
||
- name: Expose GitHub Runtime | ||
uses: crazy-max/ghaction-github-runtime@v3 | ||
# - name: Env | ||
# run: | | ||
# # ACTIONS_RUNTIME_TOKEN, ACTIONS_RUNTIME_URL should be exposed | ||
# env|sort | ||
- name: Check out the repository to the runner | ||
uses: actions/checkout@v4 | ||
- name: Bump version and push tag | ||
id: tag_version | ||
uses: mathieudutour/[email protected] | ||
with: | ||
github_token: ${{ secrets.GITHUB_TOKEN }} | ||
custom_tag: ${{ inputs.tag }} | ||
- name: Zip it up | ||
run: zip -r 'mi.Immich.Publisher.lrplugin.zip' . -x '*.git*' | ||
- name: Create a GitHub release | ||
uses: ncipollo/release-action@v1 | ||
with: | ||
tag: ${{ steps.tag_version.outputs.new_tag }} | ||
name: Release ${{ steps.tag_version.outputs.new_tag }} | ||
body: ${{ steps.tag_version.outputs.changelog }} | ||
removeArtifacts: true | ||
artifacts: "mi.Immich.Publisher.lrplugin.zip" | ||
allowUpdates: true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
-- | ||
-- Copyright (c) 2024 Min Idzelis | ||
-- | ||
-- This file is part of LR-Immich. | ||
-- | ||
-- Foobar is free software: you can redistribute it and/or modify it under the terms | ||
-- of the GNU General Public License as published by the Free Software Foundation, | ||
-- either version 3 of the License, or (at your option) any later version. | ||
-- | ||
-- LR-Immich is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; | ||
-- without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
-- See the GNU General Public License for more details. | ||
-- | ||
-- You should have received a copy of the GNU General Public License along with Foobar. | ||
-- If not, see <https://www.gnu.org/licenses/>. | ||
-- | ||
|
||
return { | ||
LrSdkVersion = 3.0, | ||
LrSdkMinimumVersion = 1.3, | ||
LrToolkitIdentifier = 'midzelis-LR-Immich', | ||
LrPluginName = "[mi] Immich Publisher", | ||
LrPluginInfoProvider = 'plugininfo.lua', | ||
LrPluginInfoUrl = "https://github.com/midzelis/immich-publisher", | ||
LrExportServiceProvider = { | ||
title = "Immich", | ||
file = "publisher.lua", | ||
}, | ||
LrInitPlugin = "init.lua", | ||
VERSION = { major = 1, minor = 0, revision = 0, build = "0", }, | ||
} |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# Immich Publisher [im] | ||
|
||
A plugin for Lightroom Classic to publish collections from Lightroom into Immich Albums. | ||
|
||
This plugin never deletes images! | ||
|
||
This plugin creates a relationship between local photos and collections and immich assets and albums. If the same photo is present in multiple collections, that relationship is maintained in immich - there are no additional copies created, just memberships. | ||
|
||
Exports will upload photos to immich without adding them to an alubm. However, if you later create a published collection with those previously exported pictures, the photos will not be re-uploaded, only the memberships will be added. | ||
|
||
Export and Publish functions work. | ||
|
||
### Known issues | ||
* Immich doesn't support overwriting an image for the time being. So, if you make edits to an image in Lightroom, and try to republish, it will not show up on immich | ||
* I'll be enhancing Immich in the future to support this. | ||
* If you remove a photo from a Lightroom collection, I haven't been able to figure out how to get notified. | ||
* Workaround | ||
* Mark a file for republishing. This will cause the plugin to run, and it will check that all the local files are synced to the remote. | ||
|
||
### Advanced | ||
Turn on Logging if you want to see more information about what is being send to the server. Verbose logging is very versbose, and normal logging is probably the most you want. | ||
|
||
### Future | ||
I plan on adding keywords, comments, likes, and additional enhancements in the future. | ||
|
||
PRs Welcome! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
-- | ||
-- Copyright (c) 2024 Min Idzelis | ||
-- | ||
-- This file is part of LR-Immich. | ||
-- | ||
-- Foobar is free software: you can redistribute it and/or modify it under the terms | ||
-- of the GNU General Public License as published by the Free Software Foundation, | ||
-- either version 3 of the License, or (at your option) any later version. | ||
-- | ||
-- LR-Immich is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY | ||
-- without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
-- See the GNU General Public License for more details. | ||
-- | ||
-- You should have received a copy of the GNU General Public License along with Foobar. | ||
-- If not, see <https://www.gnu.org/licenses/>. | ||
-- | ||
|
||
Immich = {} | ||
|
||
function Immich:new(server, api_key) | ||
local immich = {} | ||
setmetatable(immich, { | ||
__index = Immich, | ||
}) | ||
if "/" == server:sub(-1) then | ||
immich.server = server:sub(1, -2) | ||
else | ||
immich.server = server | ||
end | ||
immich.api_key = api_key | ||
immich.deviceId = 'lightroom.immich' | ||
return immich | ||
end | ||
|
||
function Immich:_authHeaders(additional) | ||
local headers = { | ||
{ field = 'x-api-key', value = self.api_key }, | ||
{ field = 'Accept', value = 'application/json' } | ||
} | ||
for key, val in pairs(additional or {}) do | ||
table.insert(headers, { field = key, value = val }) | ||
end | ||
return headers | ||
end | ||
|
||
function _invoke(func) | ||
local result = func() | ||
if not result then | ||
LrErrors.throwUserError("error") | ||
end | ||
log:trace("HTTP Response: ", result) | ||
return json.decode(result) | ||
end | ||
|
||
function Immich:_get(url) | ||
log:trace('GET ' .. url) | ||
return _invoke(function() return LrHttp.get(url, self:_authHeaders()) end) | ||
end | ||
|
||
function Immich:_send(url, body, method) | ||
local headers = self:_authHeaders({ ['Content-Type'] = 'application/json' }) | ||
local encoded = json.encode(body) | ||
log:trace((method or 'POST') .. ' ' .. url, encoded) | ||
return _invoke(function() return LrHttp.post(url, encoded, headers, method) end) | ||
end | ||
|
||
function Immich:_postMultipart(url, formdata) | ||
log:trace('POST (multipart) ' .. url) | ||
return _invoke(function() return LrHttp.postMultipart(url, formdata, self:_authHeaders()) end) | ||
end | ||
|
||
function Immich:_url(path) | ||
return self.server .. path | ||
end | ||
|
||
function Immich:asset_exists(identifiers) | ||
local url = self:_url('/api/asset/exist') | ||
local body = { | ||
deviceAssetIds = identifiers, | ||
deviceId = self.deviceId, | ||
} | ||
local response = self:_send(url, body) | ||
local set = Set:new(response.existingIds) | ||
return set.items | ||
end | ||
|
||
function Immich:asset_upload(photo, rendered) | ||
local url = self:_url('/api/asset/upload') | ||
local name = LrPathUtils.leafName(rendered) | ||
local date = photo:getRawMetadata("dateTimeOriginalISO8601") | ||
|
||
local formdata = { | ||
{ name = 'assetData', filePath = rendered, fileName = name }, | ||
{ name = 'deviceAssetId', value = photo.localIdentifier }, | ||
{ name = 'deviceId', value = self.deviceId }, | ||
{ name = 'fileCreatedAt', value = date }, | ||
{ name = 'fileModifiedAt', value = date }, | ||
} | ||
|
||
return self:_postMultipart(url, formdata) | ||
end | ||
|
||
function Immich:album_info(id) | ||
local url = self:_url('/api/album/' .. id) | ||
return self:_get(url) | ||
end | ||
|
||
function Immich:create_album(albumName, description, assetIds) | ||
local url = self:_url('/api/album') | ||
local body = { | ||
albumName = albumName, | ||
description = description, | ||
assetIds = assetIds, | ||
} | ||
return self:_send(url, body) | ||
end | ||
|
||
function Immich:add_album_assets(albumId, assetIds) | ||
log:trace('add_album_assets', albumId, assetIds) | ||
local url = self:_url('/api/album/' .. albumId .. '/assets') | ||
local body = { | ||
ids = assetIds, | ||
} | ||
return self:_send(url, body, 'PUT') | ||
end | ||
|
||
function Immich:remove_album_assets(albumId, assetIds) | ||
log:trace('remove_album_assets', albumId, assetIds) | ||
local url = self:_url('/api/album/' .. albumId .. '/assets') | ||
local body = { | ||
ids = assetIds, | ||
} | ||
return self:_send(url, body, 'DELETE') | ||
end | ||
|
||
function Immich:search_metadata(body) | ||
local url = self:_url('/api/search/metadata') | ||
return self:_send(url, body) | ||
end | ||
|
||
function Immich:albumWebUrl(albumId) | ||
return self:_url('/albums/' .. albumId) | ||
end | ||
|
||
return Immich |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
-- | ||
-- Copyright (c) 2024 Min Idzelis | ||
-- | ||
-- This file is part of LR-Immich. | ||
-- | ||
-- Foobar is free software: you can redistribute it and/or modify it under the terms | ||
-- of the GNU General Public License as published by the Free Software Foundation, | ||
-- either version 3 of the License, or (at your option) any later version. | ||
-- | ||
-- LR-Immich is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; | ||
-- without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
-- See the GNU General Public License for more details. | ||
-- | ||
-- You should have received a copy of the GNU General Public License along with Foobar. | ||
-- If not, see <https://www.gnu.org/licenses/>. | ||
-- | ||
|
||
local Log = require "log" | ||
|
||
_G.log = Log:new() | ||
|
||
_G.LrHttp = import 'LrHttp' | ||
_G.LrDate = import 'LrDate' | ||
_G.LrPathUtils = import 'LrPathUtils' | ||
_G.LrErrors = import "LrErrors" | ||
_G.LrTasks = import "LrTasks" | ||
|
||
_G.json = require "json" | ||
_G.serialize = require "serialize" | ||
_G.Set = require "set" | ||
_G.utils = require "utils" | ||
|
||
_G.LrView = import 'LrView' | ||
_G.bind = LrView.bind | ||
_G.LrBinding = import 'LrBinding' | ||
_G.LrDialogs = import 'LrDialogs' | ||
|
||
_G.LrPrefs = import 'LrPrefs' | ||
_G.prefs = _G.LrPrefs.prefsForPlugin() | ||
|
||
_G.LrErrors = import "LrErrors" |
Oops, something went wrong.