Skip to main content

Photo Gallery Part 4: Lychee

Today, I am trying out Lychee. Chris Horn suggested that I give it a try, noting that it can use SQLite and keep resource usage down.

Note that this software uses the term “album” to refer to a gallery. I will use the two words interchangeably.

Impressions

Installation

Lychee is implemented using PHP, which I do not trust at all, so I will run it within a container. A lycheeorg/lychee image is available on Docker Hub, with the source in the LycheeOrg/Lychee-Docker repository. I noticed that it uses Debian bookworm, the current testing distribution. There is some documentation, but I still have a number of questions.

I like how the PUID and PGID environment variables can be used to set the UID and GID of the lychee user within the container. I was curious how it is implemented and found that it just uses usermod, groupmod, and chown in the entrypoint.sh script. (It does not use fixuid.)

The configuration exposes two time zone variables: PHP_TZ and TIMEZONE. Both default to UTC, and a comment suggests changing the PHP_TZ setting to a local time zone. Should TIMEZONE not be changed? Unfortunately, this is not documented.

The container has three volume mount points. I can guess what conf and uploads are for, but I have no idea what sym is for. I will have to check it out after installation. I plan on using SQLite and wonder where that database will be stored. Reading entrypoint.sh, I found that it is stored in the conf directory, so no additional volumes are required.

There is a STARTUP_DELAY setting but no documentation about why it exists. The entrypoint.sh script simply sleeps for the specified amount of seconds. My guess is that this is used to delay the start of the web application to ensure that the database comes online first, in cases when both are started at the same time (using docker-compose for example). If this is correct, then no delay is needed when using SQLite. There is no delay when the setting is not set, so it appears that there is no need to explicitly set it to 0.

I created a docker-compose.yaml file along with some empty directories in a test directory.

$ ls -d *
conf  docker-compose.yaml  sym  uploads

The docker-compose.yaml file is configured as follows. I set my 1331 UID and GID, set PHP_TZ to my time zone, and exposed the service on port 8080 on my host network.

version: "3.9"
services:
  lychee:
    image: lycheeorg/lychee
    container_name: lychee
    environment:
      - PUID=1331
      - PGID=1331
      - PHP_TZ=Asia/Tokyo
    volumes:
      - "./conf:/conf"
      - "./uploads:/uploads"
      - "./sym:/sym"
    ports:
      - "8080:80"

I started the service using docker-compose up and checked out the mounted directories. The sym directory just contains an empty file named empty_file. The uploads directory is initialized with subdirectories for different sizes of images, each with an empty index.html file. The conf directory is initialized with an empty user.css file, a .env shell script that sets environment variables, and the SQLite database. Inspecting the database, I see that it has eleven tables and is mostly straightforward. There is a sym_links table that I do not yet understand.

$ find . -type f | sort
./conf/.env
./conf/database.sqlite
./conf/user.css
./docker-compose.yaml
./sym/empty_file
./uploads/big/index.html
./uploads/import/index.html
./uploads/medium/index.html
./uploads/raw/index.html
./uploads/small/index.html
./uploads/thumb/index.html

Accessing the service in my browser, a modal dialog prompts for an initial username and password. After entering this information, I opened the users page and found that it reports “User list is empty!” Perhaps the initial user is not displayed by design? I took a look in the database and found something very strange: the username column is hashed!

sqlite> SELECT * FROM users;
0|$2y$10$q...|$2y$10$/...|0|0||2022-02-07 05:08:03|2022-02-07 05:18:39|

The users table is designed as follows. There is a unique index on the username column, and I really doubt it is supposed to be hashed.

CREATE TABLE users (
  id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
  username VARCHAR(255) NOT NULL COLLATE BINARY,
  password VARCHAR(255) NOT NULL COLLATE BINARY,
  upload BOOLEAN DEFAULT '0' NOT NULL,
  lock BOOLEAN DEFAULT '0' NOT NULL,
  remember_token VARCHAR(255) DEFAULT NULL COLLATE BINARY,
  created_at DATETIME NOT NULL,
  updated_at DATETIME NOT NULL,
  "email" varchar
);
CREATE UNIQUE INDEX users_username_unique ON users (username);

I added a new user, and that new user is displayed in the users list. Checking the database, the username for the new user is not hashed. I logged out and then logged in using my initial user without issue. I guess that the initial user (WHERE id = 0?) is handled specially as the administrative user and the username is hashed so that even that information is not leaked in event that the database is compromised. I have never seen such a design before and am skeptical that it is worth it. This is PHP, though, so perhaps this feature was added after problems with compromised databases.

I cleared my browser cache and reloaded. 9 requests are made, transferring a total of 1.04MB. I consider that to be pretty heavy, but not a deal breaker. On a connection with 31.75KB/s download throughput, the overhead of loading the site requires just over 32 seconds of download time. Within my LAN, it took 740ms to load. Two cookies are set: an XSRF token and session information.

Upon logging in, the initial albums page took 1.6 seconds to load. That is pretty slow.

Configuration

Only the administrative user can administer users. Users can be added or deleted, passwords can be reset, and the following flags can be set.

  • Allow uploads
  • Restricted account

The first flag is straightforward, but there is no documentation about what is restricted when the second flag is set. Through experimentation, I found that restricted accounts are unable to change the password. My guess is that this is used when an account is shared among multiple people.

The “Add” menu is displayed even when the user is not allowed to upload. Attempting to upload a photo results in the “Uploading” dialog box to be stuck in the “Processing” state, and the “Cancel” button is not clickable! The only way to continue was to reload the page. Attempting to create a new album results in an error. The “Add” user should not be displayed in this case; it is a bug.

The settings page design is a bit odd. Buttons are styled with a background color that is similar to the page background, making them look like headings. Such usability issues do not matter, however, because I am the only person who would use the settings page.

Lychee supports a number of different languages, but it unfortunately does not support Japanese. This is not a big problem if the user interface is intuitive enough for common usage. Translations are written in PHP source files, but it looks straightforward to add a new one if necessary.

There is a setting for the layout of photos with the following options. Nice!

  • Square thumbnails
  • With aspect, justified
  • With aspect, unjustified

The user interface for boolean settings is terrible! I guess a circle on the left over a dark background means false and a circle on the right over a light background means true. The checkboxes used on the user administration page are much better!

There is an awkwardly-styled button titled More that opens advanced settings in about:config key-value style but without any documentation. Searching the database, I found that these settings are stored in the configs table. Rows contain some “documentation” (type_range and description) that may be useful to help understand what kinds of values are expected.

CREATE TABLE IF NOT EXISTS "configs" (
  "id" integer not null primary key autoincrement,
  "key" varchar not null,
  "value" varchar,
  "cat" varchar not null default 'Config',
  "confidentiality" integer not null default '0',
  "type_range" varchar not null default '0|1',
  "description" varchar not null default ''
);

The albums page includes the following copyright information in the page footer.

ALL IMAGES ON THIS WEBSITE ARE SUBJECT TO COPYRIGHT BY JOHN SMITH © 2019

I was able to customize this by changing the following settings:

  • landing_owner
  • landing_title
  • site_copyright_begin
  • site_copyright_end

The message can be disabled altogether by setting site_copyright_enable to 0.

I do not know if there is a way to disable the “smart albums.” I did not spot a setting to do so.

I do not see any settings relevant to hosting the application with a path prefix.

I logged in as a non-administrative user and created a new album. I do not like the login user interface. The root page is used to display public albums, but this list is always empty when there are no public albums. The tiny (and IMHO ugly) login icon is displayed at the top left of the page.

Photos and videos can be uploaded using a dialog box or via drag-and-drop. My wife uses drag-and-drop, so that is what I tried first. It works really well! A dialog box shows the progress as the images and videos are uploaded. They are processed quickly, and even .mov files are handled without issue.

Control icons at the top right of the album page allow the owner to configure the album settings. The users that an album is shared with must be explicitly selected for each album.

I immediately notice that the times for the uploaded photos and videos are wrong. Some are set to the time that I uploaded them to the site, and others are set to January 1, 1970 (the zero date of Unix time). This is really annoying because it affects the sorting. On the settings page, I confirmed that it is configured to sort by time. It looks like it is sorting by time and then filename.

I also notice that it is using the UTC time zone, so I guess PHP_TZ setting is insufficient. I am not going to try to fix this issue at this time.

My wife would probably not need Japanese translations to administer galleries.

I logged in as a different non-administrative user that I shared the test album with. The “Albums” page looks fine. In particular, the smart albums are not displayed for users who do not have upload permissions. The page lists (shared) albums by user. The album name and incorrect dates are displayed over each album thumbnail. It looks like the first thumbnail in the album is used as the thumbnail for the album. Clicking on the thumbnail loads that album page. It loads quickly on my laptop.

With my settings, the layout shows thumbnail images in rows, maintaining the aspect ratio of each image yet fully justifying each row by adjusting the height of the images in each row. It looks quite similar to the layout of Google Photos.

When you put the mouse cursor over a thumbnail image, the filename and incorrect time is displayed. Videos have a large play icon in the middle of the thumbnail. This makes it easy to distinguish videos, but it is ugly compared to the PiGallery 2 UI and does not indicate the video length.

Clicking on a thumbnail image opens the image in a “lightbox” with a semi-transparent dark background. The control bar is displayed at the top of the page for some time after the mouse is moved, and it transitions to a full-page layout after a timeout has been reached. The filename (without extension) and incorrect date are displayed on the bottom left of the page, partially overlapping images/videos with a wide aspect ratio.

When viewing a photo, the control bar at the top of the page has controls on the left, center, and right. The left has a single control icon, used to close the photo and go back to the album. The center shows the name of the current file (without extension). Clicking on this control allows you to select other photos/videos in the album by file name.

The right has many control icons. Two icons allow you to rotate the image counter-clockwise or clockwise. We do not need this functionality since we process photos before uploading them to a photo gallery. Attempts to try it out results in an error, probably because the user viewing the photo does not own it. These controls should not be displayed! An “i” control icon shows information about the image, which looks fine. A full screen control icon puts the browser in full screen mode. An ellipsis icon has a single option, to open the original image.

When you move the mouse to the left or right edge of the page, arrows are displayed for navigating to the previous or next photo/video. Tiny thumbnails are displayed, which I guess is a nice feature, but I would rather not show the thumbnails. Navigating to the previous file from the first file takes you to the last file, and navigating to the next file from the last file takes you to the first file.

Unfortunately, there is no way to zoom. When you view the original photo, it is loaded directly in the browser, so that does not help much. There is no icon for downloading a photo/video, but you can view the original and use the browser to save it. There is no slideshow functionality.

The controls are intuitive, so I do not think I would need Japanese translations for family members. I would probably need to create a manual, however, to help family members download images correctly.

When administering an album, the owner is able to set descriptions for albums as well as photos/videos. Album descriptions seem to only be displayed in the album information. Photos and video descriptions are displayed over the thumbnail, instead of the (incorrect) time. Nice!

Unhappy with the user interface, I tried making a test album public. After logging out, the album is displayed on the root page and can be viewed without logging in. With downloading enabled, a download icon is displayed in the album toolbar. When viewing an image, the troublesome rotate controls are gone, and the ellipsis control icon provides options to download or show the original. Selecting the download options opens a dialog box that allows the viewer to download the original or a specific resolution. Nice!

Next, I tried out the user interface on a tablet. It looks good in both portrait and landscape orientations, and navigating by touch has no issues, aside from zooming. Attempting to pinch to zoom causes the browser to zoom the page. This sort of works, but it does not look nice and may confuse users because they have to zoom back out to access the controls on the page. Navigation by swiping works fine, and full screen mode works fine.

Next, I tried out the user interface on a phone. It looks fine on the especially small screen, and it behaves the same as it does on my tablet. I noticed that loading of album thumbnails is visible slow, however. I am not sure why, but it is not a big issue.

Like PiGallery 2, Lychee creates a new entry in the browser history for each photo/video view.

URLs use anchors with large integer identifiers to determine the page to display. In my opinion, this is really poor design.

Evaluation

How does Lychee measure up to my requirements?

The installation and configuration of the software is nowhere near as clean as I had hoped. User management is pretty good, but I am not satisfied with the user interface that family members would be presented with. Making galleries public fixes many issues! If I decide to use this software, I will make all galleries public and use basic authentication to control access. Logging in will then only be required for administration.

Gallery administration is done via the web user interface. Uploading photos and videos works great. (It is by far my favorite aspect of this software.) Thumbnail images are created automatically, with no issues.

The gallery user interface is pretty good, and the controls are intuitive. The lack of zoom and slideshow functionality is unfortunate. Aside from zoom, the user interface works fine on my tablet and phone.

The software is pretty heavy, but not so much that I would not consider it. I can probably get it to run with a prefix using rewrite rules in the proxy server, and I should be able to run the container without internet access, for improved security.

Image and video format support is excellent. The comment (description) functionality is convenient. Resource usage is low, so I suspect that it would run fine even on a server with modest specs.

Unfortunately, image/video times are lost when uploading. I will not consider using this software if I cannot resolve this issue. I really hope that I will find better options, however, and I will only try to resolve this issue if my search is not fruitful.