pman07 / Immich_Notify

2 stars 1 forks source link

Add ability to authenticate to user #2

Closed feerlessleadr closed 1 year ago

feerlessleadr commented 1 year ago

Hi - I have ntfy setup in a separate docker container, however I have authentication setup. I mapped the main.py file outside of the container and tried to add in the basic auth to the main.py file:

def ntfy_notification(ntfyurl, ntfytitle, ntfymessage, ntfylink):
    requests.post(ntfyurl,
                  data=ntfymessage.encode('utf-8'),
                  headers={
                      "Title": ntfytitle,
                      "Click": ntfylink,
                      "Icon": NTFY_ICON
                      "Authorization": "Basic XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
                  })

However when I run the above, I get an error in the container that says:

immich_notify  | 09/12/2023 22:15:01 - Immich Notification Check Triggered
immich_notify  |   File "//./main.py", line 77
immich_notify  |     "Authorization": "Basic XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX "
immich_notify  |     ^
immich_notify  | SyntaxError: invalid syntax

Any idea how I can add in the auth to your script? Thanks

feerlessleadr commented 1 year ago

So turns out, I was just missing a comma, however I'm still getting an error:

immich_notify  | 09/15/2023 15:30:02 - Immich Notification Check Triggered
immich_notify  | Traceback (most recent call last):
immich_notify  |   File "/usr/local/lib/python3.9/site-packages/requests/models.py", line 971, in json
immich_notify  |     return complexjson.loads(self.text, **kwargs)
immich_notify  |   File "/usr/local/lib/python3.9/json/__init__.py", line 346, in loads
immich_notify  |     return _default_decoder.decode(s)
immich_notify  |   File "/usr/local/lib/python3.9/json/decoder.py", line 337, in decode
immich_notify  |     obj, end = self.raw_decode(s, idx=_w(s, 0).end())
immich_notify  |   File "/usr/local/lib/python3.9/json/decoder.py", line 355, in raw_decode
immich_notify  |     raise JSONDecodeError("Expecting value", s, err.value) from None
immich_notify  | json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
immich_notify  |
immich_notify  | During handling of the above exception, another exception occurred:
immich_notify  |
immich_notify  | Traceback (most recent call last):
immich_notify  |   File "//./main.py", line 141, in <module>
immich_notify  |     tmp_title, tmp_total = get_album_contents(album, IMMICH_KEY)
immich_notify  |   File "//./main.py", line 62, in get_album_contents
immich_notify  |     a = response.json()
immich_notify  |   File "/usr/local/lib/python3.9/site-packages/requests/models.py", line 975, in json
immich_notify  |     raise RequestsJSONDecodeError(e.msg, e.doc, e.pos)
immich_notify  | requests.exceptions.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
pman07 commented 1 year ago

So turns out, I was just missing a comma, however I'm still getting an error:

immich_notify  | 09/15/2023 15:30:02 - Immich Notification Check Triggered
immich_notify  | Traceback (most recent call last):
immich_notify  |   File "/usr/local/lib/python3.9/site-packages/requests/models.py", line 971, in json
immich_notify  |     return complexjson.loads(self.text, **kwargs)
immich_notify  |   File "/usr/local/lib/python3.9/json/__init__.py", line 346, in loads
immich_notify  |     return _default_decoder.decode(s)
immich_notify  |   File "/usr/local/lib/python3.9/json/decoder.py", line 337, in decode
immich_notify  |     obj, end = self.raw_decode(s, idx=_w(s, 0).end())
immich_notify  |   File "/usr/local/lib/python3.9/json/decoder.py", line 355, in raw_decode
immich_notify  |     raise JSONDecodeError("Expecting value", s, err.value) from None
immich_notify  | json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
immich_notify  |
immich_notify  | During handling of the above exception, another exception occurred:
immich_notify  |
immich_notify  | Traceback (most recent call last):
immich_notify  |   File "//./main.py", line 141, in <module>
immich_notify  |     tmp_title, tmp_total = get_album_contents(album, IMMICH_KEY)
immich_notify  |   File "//./main.py", line 62, in get_album_contents
immich_notify  |     a = response.json()
immich_notify  |   File "/usr/local/lib/python3.9/site-packages/requests/models.py", line 975, in json
immich_notify  |     raise RequestsJSONDecodeError(e.msg, e.doc, e.pos)
immich_notify  | requests.exceptions.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

Could you post your docker ENV variables setup?

feerlessleadr commented 1 year ago

sure

# Base url to access API
# example: http://192.168.1.100:
BASEURL=http://192.168.1.18:

# External url to access albums via notification link
# example: https://immich.fakeurl.com
EXTERNALURL=https://pics.mydomain.com

# Immich API Key
IMMICHKEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXX

# File to store album item counts
FILEPATH=./data/data.txt

# Dictionary with album id(s) and desired notification topic(s)
# Can all be the same topic or divide up as desired
ALBUMS={'79bed5d2-2c71-4dd8-91f7-d06afe496569': 'My-Topic'}

# ntfy URL to send notifications to
# ntfy can be self hosted or use free or paid teirs of https://ntfy.sh
NTFYURL=https://ntfy.mydomain.com

# Icon to use for ntfy notification
NTFYICON=https://raw.githubusercontent.com/immich-app/immich/main/design/immich-logo-no-outline.png
pman07 commented 1 year ago

Does your album have pictures in it? Are you sure it's the right album ID?

It looks like it's not getting any album contents back.

feerlessleadr commented 1 year ago

The album definitely has pictures in it (1k+ pictures). I'm not 100% sure I have the right ID though. I grabbed the ID by navigating to the album and then copying the numbers at the end of the address bar in my browser:

https://pics.mydomain.com/albums/79bed5d2-2c71-4dd8-91f7-d06afe496569

Is that the right way to do it?

feerlessleadr commented 1 year ago

Any thoughts on whether I'm grabbing the right album ID? @pman07

pman07 commented 1 year ago

Well, that should be it. Where did you get your Immich API Key? Is it still active?

feerlessleadr commented 1 year ago

I created 2 new keys (tested both, both give the same error as above) by going to my username on the top right, then 'Account Settings', then 'API Keys', then 'New API Key'.

I even created a test album with only a few pictures in it (vs my main album with has a few thousand and is shared with a few people just in case).

Here is my compose file in case something is wonky there:

version: "3.8"

services:
  immich_notify:
    image: pierson07/immich_notify:latest
    container_name: immich_notify
    env_file:
      - stack.env
    volumes:
      - ./data:/data # optional, use to retain data through redeployment
      - ${PWD}/data/main.py:/main.py # to persist the main.py file to add authentication
    restart: unless-stopped

And here is the main.py file, which I have mapped outside the container to allow me to add auth to my ntfy instance. I only made 2 changes to this file, which I bolded below:

import os
import ast
import socket
import requests

IMMICH_KEY = os.environ.get('IMMICHKEY')
BASE_URL = os.environ.get('BASEURL')
EXT_URL = os.environ.get('EXTERNALURL')
FILE_PATH = os.environ.get('FILEPATH')
ALBUMS = ast.literal_eval(os.environ['ALBUMS'])
NTFY_URL = os.environ.get('NTFYURL')
NTFY_ICON = os.environ.get('NTFYICON')
DEBUG = (os.getenv('DEBUG', 'False') == 'True')

def check(host, port, timeout=1):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.settimeout(timeout)
    try:
        sock.connect((host, port))
    except:
        return False
    else:
        sock.close()
        return True

def save_data(file_path, dictionary):
    try:
        with open(file_path, 'w') as file:
            for key in dictionary:
                file.write(str(dictionary[key]['total items']) + '\n')
        if DEBUG:
            print("Data stored successfully!")
    except IOError:
        print("An error occurred while saving the file.")

def read_data(file_path):
    tmp = []
    try:
        with open(file_path, 'r') as file:
            for string in file:
                tmp.append(int(string.strip()))
        return tmp
    except IOError:
        print("An error occurred while loading the file.")

def get_album_contents(uuid, imkey):
    url = BASE_URL + "/api/album/" + uuid

    payload = {}
    headers = {
        'Accept': 'application/json',
        'x-api-key': imkey
    }

    response = requests.request("GET", url, headers=headers, data=payload)

    a = response.json()

    album_name = a['albumName']
    count = a['assetCount']

    return album_name, count

def ntfy_notification(ntfyurl, ntfytitle, ntfymessage, ntfylink):
    requests.post(ntfyurl,
                  data=ntfymessage.encode('utf-8'),
                  headers={
                      "Title": ntfytitle,
                      "Click": ntfylink,
                      "Icon": NTFY_ICON,
                      "Authorization": "Basic XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
                  })

if __name__ == '__main__':

    total_items_stored = []
    albums = {}

    if check('192.168.1.18', 2283):
        if os.path.exists(FILE_PATH):
            if DEBUG:
                print('File Exists')
            total_items_stored = read_data(FILE_PATH)
            if DEBUG:
                for item in total_items_stored:
                    print('Items:', item)

            index = 0
            for key in ALBUMS:
                album = key
                topic = ALBUMS[key]
                if DEBUG:
                    print("Topic: ", topic)
                    print("Album ID: ", album)
                tmp_title, tmp_total = get_album_contents(album, IMMICH_KEY)
                albums[album] = {'topic': topic, 'title': tmp_title, 'total items': tmp_total,
                                 'stored items': total_items_stored[index]}
                index += 1

            if DEBUG:
                for album in albums:
                    print('Album Name: ', albums[album]['title'])
                    print('Total Items Stored: ', albums[album]['stored items'])
                    print('Total Items Now: ', albums[album]['total items'])

            index = 0
            for album in albums:
                albums[album]['new items'] = albums[album]['total items'] - albums[album]['stored items']
                index += 1

            if DEBUG:
                for album in albums:
                    print('Album Name:', albums[album]['title'])
                    print("Items Added:", albums[album]['new items'])

            for album in albums:
                if albums[album]['new items'] > 0:
                    topic = albums[album]['topic']
                    url = NTFY_URL + '/' + topic
                    title = 'Immich'
                    link = EXT_URL + '/albums/' + album

                    if albums[album]['new items'] > 1:
                        message = str(albums[album]['new items']) + ' photos added to ' + albums[album]['title'] + '!'
                    else:
                        message = 'Photo added to ' + albums[album]['title'] + '!'

                    ntfy_notification(url, title, message, link)

        else:
            for key in ALBUMS:
                album = key
                topic = ALBUMS[key]
                tmp_title, tmp_total = get_album_contents(album, IMMICH_KEY)
                albums[album] = {'topic': topic, 'title': tmp_title, 'total items': tmp_total}

            if DEBUG:
                for album in albums:
                    print('Album Name:', albums[album]['title'])
                    print('Total Items Now: ', albums[album]['total items'])

        save_data(FILE_PATH, albums)

I made 2 changes to the above file:

I added in the authorization for my ntfy instance:

def ntfy_notification(ntfyurl, ntfytitle, ntfymessage, ntfylink):
    requests.post(ntfyurl,
                  data=ntfymessage.encode('utf-8'),
                  headers={
                      "Title": ntfytitle,
                      "Click": ntfylink,
                      "Icon": NTFY_ICON,
                      "Authorization": "Basic XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
                  })

And I changed a hard coded local IP from 192.168.1.80 to 192.168.1.18, which I assumed was a mistake?

if __name__ == '__main__':

    total_items_stored = []
    albums = {}

    if check('192.168.1.18', 2283):
        if os.path.exists(FILE_PATH):
            if DEBUG:
                print('File Exists')
            total_items_stored = read_data(FILE_PATH)
            if DEBUG:
                for item in total_items_stored:
                    print('Items:', item)

If I leave the above portion of main.py as it is in the repo:

if __name__ == '__main__':

    total_items_stored = []
    albums = {}

    if check('192.168.1.80', 2283):
        if os.path.exists(FILE_PATH):
            if DEBUG:
                print('File Exists')
            total_items_stored = read_data(FILE_PATH)
            if DEBUG:
                for item in total_items_stored:
                    print('Items:', item)

the following shows up, but nothing happens (no notification, etc.):

immich_notify | 09/18/2023 22:00:01 - Immich Notification Check Triggered

pman07 commented 1 year ago

I don't see anything glaring that's wrong. If I make the same changes to my copy it still works, but I don't have NTFY authentication setup currently.

Good catch on the hardcoded local IP, that's embarrassing. I'm working on a fix for that as well as adding optional authentication. Would you be able to test the following out and see if it works for you?

Add AUTHORIZATION_KEY to your Docker stack.env variables

import os
import ast
import socket
import requests
from urllib.parse import urlparse

IMMICH_KEY = os.environ.get('IMMICHKEY')
BASE_URL = os.environ.get('BASEURL')
EXT_URL = os.environ.get('EXTERNALURL')
FILE_PATH = os.environ.get('FILEPATH')
ALBUMS = ast.literal_eval(os.environ['ALBUMS'])
NTFY_URL = os.environ.get('NTFYURL')
NTFY_ICON = os.environ.get('NTFYICON')
DEBUG = (os.getenv('DEBUG', 'False') == 'True')

if 'AUTHORIZATION_KEY' in os.environ:
    AUTHORIZATION_KEY = os.environ.get('AUTHORIZATION_KEY')
else:
    AUTHORIZATION_KEY = ''

def check(host, port, timeout=1):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.settimeout(timeout)
    try:
        sock.connect((host, port))
    except:
        return False
    else:
        sock.close()
        return True

def save_data(file_path, dictionary):
    try:
        with open(file_path, 'w') as file:
            for key in dictionary:
                file.write(str(dictionary[key]['total items']) + '\n')
        if DEBUG:
            print("Data stored successfully!")
    except IOError:
        print("An error occurred while saving the file.")

def read_data(file_path):
    tmp = []
    try:
        with open(file_path, 'r') as file:
            for string in file:
                tmp.append(int(string.strip()))
        return tmp
    except IOError:
        print("An error occurred while loading the file.")

def get_album_contents(uuid, imkey):
    url = BASE_URL + "/api/album/" + uuid

    payload = {}
    headers = {
        'Accept': 'application/json',
        'x-api-key': imkey
    }

    response = requests.request("GET", url, headers=headers, data=payload)

    a = response.json()

    album_name = a['albumName']
    count = a['assetCount']

    return album_name, count

def ntfy_notification(ntfyurl, ntfytitle, ntfymessage, ntfylink, authorization=''):
    if authorization != '':
        requests.post(ntfyurl,
                  data=ntfymessage.encode('utf-8'),
                  headers={
                      "Title": ntfytitle,
                      "Click": ntfylink,
                      "Icon": NTFY_ICON,
                      "Authorization": "Basic " + authorization
                  })
    else:
        requests.post(ntfyurl,
                  data=ntfymessage.encode('utf-8'),
                  headers={
                      "Title": ntfytitle,
                      "Click": ntfylink,
                      "Icon": NTFY_ICON
                  })

if __name__ == '__main__':

    total_items_stored = []
    albums = {}

    url = urlparse(BASE_URL)

    if check(url.hostname, url.port):
        if os.path.exists(FILE_PATH):
            if DEBUG:
                print('File Exists')
            total_items_stored = read_data(FILE_PATH)
            if DEBUG:
                for item in total_items_stored:
                    print('Items:', item)

            index = 0
            for key in ALBUMS:
                album = key
                topic = ALBUMS[key]
                if DEBUG:
                    print("Topic: ", topic)
                    print("Album ID: ", album)
                tmp_title, tmp_total = get_album_contents(album, IMMICH_KEY)
                albums[album] = {'topic': topic, 'title': tmp_title, 'total items': tmp_total,
                                 'stored items': total_items_stored[index]}
                index += 1

            if DEBUG:
                for album in albums:
                    print('Album Name: ', albums[album]['title'])
                    print('Total Items Stored: ', albums[album]['stored items'])
                    print('Total Items Now: ', albums[album]['total items'])

            index = 0
            for album in albums:
                albums[album]['new items'] = albums[album]['total items'] - albums[album]['stored items']
                index += 1

            if DEBUG:
                for album in albums:
                    print('Album Name:', albums[album]['title'])
                    print("Items Added:", albums[album]['new items'])

            for album in albums:
                if albums[album]['new items'] > 0:
                    topic = albums[album]['topic']
                    url = NTFY_URL + '/' + topic
                    title = 'Immich'
                    link = EXT_URL + '/albums/' + album

                    if albums[album]['new items'] > 1:
                        message = str(albums[album]['new items']) + ' photos added to ' + albums[album]['title'] + '!'
                    else:
                        message = 'Photo added to ' + albums[album]['title'] + '!'

                    ntfy_notification(url, title, message, link, AUTHORIZATION_KEY)

        else:
            for key in ALBUMS:
                album = key
                topic = ALBUMS[key]
                tmp_title, tmp_total = get_album_contents(album, IMMICH_KEY)
                albums[album] = {'topic': topic, 'title': tmp_title, 'total items': tmp_total}

            if DEBUG:
                for album in albums:
                    print('Album Name:', albums[album]['title'])
                    print('Total Items Now: ', albums[album]['total items'])

        save_data(FILE_PATH, albums)
pman07 commented 1 year ago

@feerlessleadr I've updated the container to fix the hardcoded IP and hopefully add support for Basic Auth.

If you get a chance to try it out, let me know how it goes!

feerlessleadr commented 1 year ago

Thanks - I updated my compose to remove mapping the main.py outside of the container:

version: "3.8"

services:
  immich_notify:
    image: pierson07/immich_notify:latest
    container_name: immich_notify
    env_file:
      - stack.env
    volumes:
      - ./data:/data # optional, use to retain data through redeployment
#      - ${PWD}/data/main.py:/main.py # to persist the main.py file to add authentication
    restart: unless-stopped

I also updated my stack.env for the auth:

# Base url to access API
# example: http://192.168.1.100:
BASEURL=http://192.168.1.18:

# External url to access albums via notification link
# example: https://immich.fakeurl.com
EXTERNALURL=https://pics.mydomain.com

# Immich API Key
IMMICHKEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

# File to store album item counts
FILEPATH=./data.txt

# Dictionary with album id(s) and desired notification topic(s)
# Can all be the same topic or divide up as desired
ALBUMS={'1009fc79-72ea-4875-b6a4-a493ad205632': 'immich'}

# ntfy URL to send notifications to
# ntfy can be self hosted or use free or paid teirs of https://ntfy.sh
NTFYURL=https://ntfy.mydomain.com

# Icon to use for ntfy notification
NTFYICON=https://raw.githubusercontent.com/immich-app/immich/main/design/immich-logo-no-outline.png

# OPTIONAL Basic Authorization Token
AUTHORIZATION_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

I re-pulled the contaner and let this run and all I get in the docker log is:

immich_notify  | 09/20/2023 17:22:01 - Immich Notification Check Triggered
immich_notify  | 09/20/2023 17:23:01 - Immich Notification Check Triggered
immich_notify  | 09/20/2023 17:24:01 - Immich Notification Check Triggered

It seems like the script is running every minute now? However I do not get a notification nor is the data.txt created.

I know that my ntfy instance is working, as I'm using it for other scripts/services, so not sure where I'm going wrong.

@pman07

pman07 commented 1 year ago

@feerlessleadr Ahh I see a documentation error. There needs to be your Immich port number in the stack.env variable BASEURL=http://192.168.1.18:2283

I'll fix that in the ReadMe. Apologies.

feerlessleadr commented 1 year ago

No worries. I'm definitely getting somewhere. I have a new error:

immich_notify  | 09/20/2023 18:31:01 - Immich Notification Check Triggered
immich_notify  | Traceback (most recent call last):
immich_notify  |   File "//./main.py", line 158, in <module>
immich_notify  |     tmp_title, tmp_total = get_album_contents(album, IMMICH_KEY)
immich_notify  |   File "//./main.py", line 70, in get_album_contents
immich_notify  |     album_name = a['albumName']
immich_notify  | KeyError: 'albumName'
pman07 commented 1 year ago

Hmm. Are you familiar with the program Postman?

If so, could you try sending a "GET" to https://pics.mydomain.com/api/album/1009fc79-72ea-4875-b6a4-a493ad205632

with Authorization set to Bearer Token and paste your Immich API token XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

I'm curious if your API access isn't working with your key. What the error means is that when calling the album API to check the album contents it's not seeing the albumName in the response.

feerlessleadr commented 1 year ago

I'm not familiar, but I was able to sign up and download the program. It looks like the issue is an invalid key. I'm not sure how that's possible though, as I just created a new key, copied it into the postman and re-ran the request, and I'm still getting the below error in postman:

{
    "message": "Invalid user token",
    "error": "Unauthorized",
    "statusCode": 401
}

Am I creating the key correctly? I'm going to my username on the top right, then 'Account Settings', then 'API Keys', then 'New API Key', then copying the key, then clicking done.

::EDIT:: Ok, so things are getting stranger and stranger. I ran across this issue, thinking I was having the same problem.

I created another new api key and ran the following:

curl -L -X GET 'pics.mydomain.com/api/server-info' -H 'Accept: application/json' -H 'x-api-key: XXXXXXXXXXXXXXXXXX'

and the api key works:

{"diskAvailable":"26.2 TiB","diskSize":"117.6 TiB","diskUse":"91.3 TiB","diskAvailableRaw":28833714106368,"diskSizeRaw":129270715641856,"diskUseRaw":100424953786368,"diskUsagePercentage":77.69}

However, when I use the exact same API key in postman (which I know works), I get the invalid user token message from above.

pman07 commented 1 year ago

Hmmm. Do you have '' around your API Key in the stack.env?

Kinda grasping at straws, that's very odd.

feerlessleadr commented 1 year ago

I tried it both ways (with and without ") and get the same error.

pman07 commented 1 year ago

Where are you running the curl command vs postman? Same computer?

feerlessleadr commented 1 year ago

So turns out I'm a little dumb and forgot to update my env with the new API key I used in the curl command. Now that I did that and encased the key inside double quotes, the error goes away and it just says that a notification check was triggered. Unfortunately I still don't get a notification and a data.txt file isn't created still. No other errors are thrown though, so I'm not sure what exactly is failing.

I did run the curl command from the same machine that immich and immich-notify docker containers are located. Postman is located on my windows machine though. Despite me not updating the env, postman still comes back with invalid token, but that could be a Windows firewall issue.

pman07 commented 1 year ago

I'm assuming it still didn't create the data.txt file? The first time it runs successfully it won't send a notification.

feerlessleadr commented 1 year ago

Yeah, no data.txt file. I'm not sure I have the env and the compose file setup correctly for the data.txt file though (I posted both above).

In my /immich-notify/ folder, I have a data folder and that is where I'd like the data.txt file to be created.

In the compose file, should I just map the ./data/ folder to inside the container (as I have now), or should I include the specific data.txt file as well?

Also in the .env, is that path for inside the container or outside? And should I have it as a relative path or full path, and should I include data.txt in that path or no?

pman07 commented 1 year ago

The volume should just be the folder, the env file should include the path and the file name.

Here is how I have my Docker Compose setup

services:
  immich_notify:
    image: pierson07/immich_notify:latest
    container_name: immich_notify
    env_file:
      - stack.env
    volumes:
      - /mnt/data/appdata/immich_notification:/mnt/data/appdata/immich_notification
    restart: unless-stopped

And my stack.env FILEPATH

BASEURL=http://192.168.1.80:2283
EXTERNALURL=https://pics.url
IMMICHKEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
FILEPATH=/mnt/data/appdata/immich_notification/data.txt
ALBUMS={'18e2df3e-e44d-48a4-ac78-7d08dc826a7e': 'immich','3a5ef3de-e0f1-4759-be59-bcc56c2f2104': 'immich'}
NTFYURL=http://192.168.1.80:8080
NTFYICON=https://raw.githubusercontent.com/immich-app/immich/main/design/immich-logo-no-outline.png

In my case, the /mnt/data/appdata/immich_notification is a folder on my TrueNas server. You could probably simplify the internal folder structure but I keep it the same because I'm lazy and it works.

feerlessleadr commented 1 year ago

well im thoroughly confused now. all i did was change the file path in the env and im back to the same error with the album:

immich_notify  | 09/20/2023 22:20:01 - Immich Notification Check Triggered
immich_notify  | Traceback (most recent call last):
immich_notify  |   File "/main.py", line 11, in <module>
immich_notify  |     ALBUMS = ast.literal_eval(os.environ['ALBUMS'])
immich_notify  |   File "/usr/local/lib/python3.9/os.py", line 679, in __getitem__
immich_notify  |     raise KeyError(key) from None
immich_notify  | KeyError: 'ALBUMS'

one more time in case i messed up, heres my compose:

version: "3.8"

services:
  immich_notify:
    image: pierson07/immich_notify:latest
    container_name: immich_notify
    env_file:
      - stack.env
    volumes:
      - /home/kevin/docker-apps/immich-notify/data:/data # optional, use to retain data through redeployment
#      - ${PWD}/data/main.py:/main.py # to persist the main.py file to add authentication
    restart: unless-stopped

here is my env:

# Base url to access API
# example: http://192.168.1.100:
BASEURL=http://192.168.1.18:2283

# External url to access albums via notification link
# example: https://immich.fakeurl.com
EXTERNALURL=https://pics.mydomain.com

# Immich API Key
IMMICHKEY=XXXX

# File to store album item counts
FILEPATH=/home/kevin/docker-apps/immich-notify/data/data.txt

# Dictionary with album id(s) and desired notification topic(s)
# Can all be the same topic or divide up as desired
ALBUMS={'1009fc79-72ea-4875-b6a4-a493ad205632': 'immich'}

# ntfy URL to send notifications to
# ntfy can be self hosted or use free or paid teirs of https://ntfy.sh
NTFYURL=https://ntfy.mydomain.com

# Icon to use for ntfy notification
NTFYICON=https://raw.githubusercontent.com/immich-app/immich/main/design/immich-logo-no-outline.png

# OPTIONAL Basic Authorization Token
AUTHORIZATION_KEY=XXXXX
pman07 commented 1 year ago

Well, in this case we're in the same boat. I updated it to run every 15 minutes and the docker container broke. I'm trying to figure out why its 50/50 when I build the docker container. I'll message when I get it fixed. Sorry for the inconvenience.

EDIT This is why I shouldn't try using my desktop. Back on my laptop and testing before I push the fixes.

feerlessleadr commented 1 year ago

All good, hopefully I can help you fix!

pman07 commented 1 year ago

Ok, latest container works for me. Should be back on track.

-EDIT- Also, you can add

DEBUG=True

to stack.env to see more information in logs.

feerlessleadr commented 1 year ago

Ok - making more progress. The container can see the album and can see the number of assets, but I'm getting an error about writing the file:

immich_notify  | 09/21/2023 14:00:01 - Immich Notification Check Triggered
immich_notify  | Album Name: Test
immich_notify  | Total Items Now:  3
immich_notify  | An error occurred while saving the file.

I then added another asset to the 'Test' album, and received the same error, and did not get a notification through ntfy. I'm assuming that's because of the file error.

immich_notify  | 09/21/2023 14:30:01 - Immich Notification Check Triggered
immich_notify  | Album Name: Test
immich_notify  | Total Items Now:  4
immich_notify  | An error occurred while saving the file.

When I log into the container, I can access the /data/ folder (mapped to /home/kevin/docker-apps/immich-notify/data/ on my docker host), where I have the data.txt file set to save. I can also create/edit/delete a test file within the container in the mapped directory above.

pman07 commented 1 year ago

That's interesting, I'll add some logging for the actual error with saving the file later today.

In the meantime, can you try running

chmod 777 /home/kevin/docker-apps/immich-notify/data/

and see if that changes anything?

feerlessleadr commented 1 year ago

thanks, happy to keep testing.

i ran the chmod command and same issue:

kevin@ubuntu-server:~/docker-apps/immich-notify$ sudo chmod 777 /home/kevin/docker-apps/immich-notify/data/
[sudo] password for kevin:
kevin@ubuntu-server:~/docker-apps/immich-notify$ ls -la
total 32
drwxrwxr-x  3 kevin kevin 4096 Sep 21 10:41 .
drwxrwxr-x 30 kevin kevin 4096 Sep 11 15:15 ..
drwxrwxrwx  2 root  root  4096 Sep 21 10:56 data
-rw-rw-r--  1 kevin kevin  391 Sep 21 10:40 docker-compose.yml
-rw-r--r--  1 root  root  4744 Sep 18 17:15 main.py.bak
-rw-rw-r--  1 kevin kevin 1027 Sep 21 10:41 stack.env
-rwxrwxr-x  1 kevin kevin  232 Sep 12 18:25 test.py
immich_notify  | 09/21/2023 23:00:02 - Immich Notification Check Triggered
immich_notify  | Album Name: Test
immich_notify  | Total Items Now:  4
immich_notify  | An error occurred while saving the file.
pman07 commented 1 year ago

@feerlessleadr dang, hoped that'd get it. Ok I added some logging for the IO errors. Let me know if that helps at all.

feerlessleadr commented 1 year ago

Thanks - here is the error with the updated logging:

immich_notify  | 09/22/2023 05:00:01 - Immich Notification Check Triggered
immich_notify  | Album Name: Test
immich_notify  | Total Items Now:  4
immich_notify  | <class 'OSError'>
immich_notify  | An error occurred while saving the file.
pman07 commented 1 year ago

Hmm ok that didn't work like I thought it would. Updated again, please try again when you get a chance.

feerlessleadr commented 1 year ago

thanks - just updated and got the following:

immich_notify  | 09/22/2023 15:00:01 - Immich Notification Check Triggered
immich_notify  | Album Name: Test
immich_notify  | Total Items Now:  4
immich_notify  | [Errno 2] No such file or directory: '/home/kevin/docker-apps/immich-notify/data/data.txt'
immich_notify  | An error occurred while saving the file.

I then created the 'data.txt' file in the folder just in case the script doesn't create that file automatically:

kevin@ubuntu-server:~/docker-apps/immich-notify/data$ nano data.txt
kevin@ubuntu-server:~/docker-apps/immich-notify/data$ ls
data.txt
kevin@ubuntu-server:~/docker-apps/immich-notify/data$ readlink -f data.txt
/home/kevin/docker-apps/immich-notify/data/data.txt

I think made sure it is world writable:

kevin@ubuntu-server:~/docker-apps/immich-notify/data$ chmod 777 /home/kevin/docker-apps/immich-notify/data/data.txt
kevin@ubuntu-server:~/docker-apps/immich-notify/data$ ls -la
total 12
drwxrwxrwx 2 root  root  4096 Sep 22 10:32 .
drwxrwxr-x 3 kevin kevin 4096 Sep 22 09:20 ..
-rwxrwxrwx 1 kevin kevin   13 Sep 22 10:32 data.txt

However I'm still getting the same error that there is no such file or directory:

immich_notify  | 09/22/2023 15:15:01 - Immich Notification Check Triggered
immich_notify  | Album Name: Test
immich_notify  | Total Items Now:  4
immich_notify  | [Errno 2] No such file or directory: '/home/kevin/docker-apps/immich-notify/data/data.txt'
immich_notify  | An error occurred while saving the file.
pman07 commented 1 year ago

Could you try using the following as your docker-compose file? I think either the volume mapping order needs swapped or the FILEPATH variable needs to be /data/data.txt for your current setup, but the below should work either way.

version: "3.8"

services:
  immich_notify:
    image: pierson07/immich_notify:latest
    container_name: immich_notify
    env_file:
      - stack.env
    volumes:
      - /home/kevin/docker-apps/immich-notify/data:/home/kevin/docker-apps/immich-notify/data # optional, use to retain data through redeployment
#      - ${PWD}/data/main.py:/main.py # to persist the main.py file to add authentication
    restart: unless-stopped
feerlessleadr commented 1 year ago

I'm extremely happy to say that it's finally working for me. Thank you for your help and patience getting this to work, it's awesome that it opens up the website too when you click on the notification!

pman07 commented 1 year ago

Phew! Man I'm glad it's working. Thanks for your patience, obviously you're the first one to use it other than myself lol. Sorry for how long it took to get here. Hopefully made it more usable for anyone else now! Please let me know if you have any other issues or requests!

Enjoy!