immich-app / immich

High performance self-hosted photo and video management solution.
https://immich.app
GNU Affero General Public License v3.0
44.63k stars 2.17k forks source link

bug(mobile): Immich iOS App on iOS 18 Beta incorrectly treats Live Photos as Motion Photos on restore and saves only the image file #11519

Open ankit5902 opened 1 month ago

ankit5902 commented 1 month ago

The bug

I uploaded/backed up a bunch of live photos using the Immich iOS App v1.111.0 on iOS 18. When downloading or restoring the live photo (on any device), only the still image is downloaded or restored, not the video part, thereby it loses the Live Photo functionality. It is treating as a motion photo, even though it is a live photo and saves only the still image file. (shown in the Immich iOS App Logs)

IMG_7189

I tried creating a fresh Immich instance and uploading the live pictures from the iOS app, but it seems the issue still persists. The image is being treated as a motion photo. It could be that this might be due to changes in the Photos app on iOS 18 Beta 4. Images captured and uploaded from iOS 18 Beta version are being treated as motion photos in Immich App when restoring, while images captured on a stable version of iOS are working fine. Even if I airdrop an image from the iOS 18 Beta version device to a device running the stable version of iOS and then upload it, the issue still remains. It seems that something has changed in the Photos app in the unreleased iOS 18.

The OS that Immich Server is running on

Debian 12 (Bookworm)

Version of Immich Server

v1.111.0

Version of Immich Mobile App

v1.111.0 on iOS 18 Beta

Platform with the issue

Your docker-compose.yml content

name: immich

services:
  immich-server:
    container_name: immich-server
    image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
    ports:
      - 2283:3001
    volumes:
      - ${UPLOAD_LOCATION}:/usr/src/app/upload
    env_file:
      - .env
    security_opt:
      - no-new-privileges:true
    networks:
      - traefik-proxy
    environment:
      - TZ=Asia/Kolkata
    restart: always
    depends_on:
       - redis
       - postgres

  immich-machine-learning:
    container_name: immich-machine-learning
    image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
    volumes:
      - './model-cache:/cache'
    env_file:
      - .env
    networks:
      - traefik-proxy
    security_opt:
      - no-new-privileges:true
    environment:
      - TZ=Asia/Kolkata
    restart: always

  redis:
    image: redis:latest
    container_name: redis
    volumes:
      - './config:/data'
    environment:
      - TZ=Asia/Kolkata
    restart: always
    healthcheck:
      test: redis-cli ping || exit 1
    security_opt:
      - no-new-privileges:true
    networks:
      - traefik-proxy

  postgres:
    image: tensorchord/pgvecto-rs:pg16-v0.2.1
    container_name: postgres
    volumes:
      - './postgres/data:/var/lib/postgresql/data'
      - './backups:/backups'
    environment:
      - TZ=Asia/Kolkata
      - POSTGRES_HOST=${DB_HOSTNAME}
      - POSTGRES_USER=${DB_USERNAME}
      - POSTGRES_PASSWORD=${DB_PASSWORD}
      - POSTGRES_DB=${DB_DATABASE_NAME}
      - POSTGRES_INITDB_ARGS='--data-checksums'
    restart: always
    security_opt:
      - no-new-privileges:true
    networks:
      - traefik-proxy
    healthcheck:
      test: pg_isready --dbname='${DB_DATABASE_NAME}' --username='${DB_USERNAME}' || exit 1; Chksum="$$(psql --dbname='${DB_DATABASE_NAME}' --username='${DB_USERNAME}' --tuples-only --no-align --command='SELECT COALESCE(SUM(checksum_failures), 0) FROM pg_stat_database')"; echo "checksum failure count is $$Chksum"; [ "$$Chksum" = '0' ] || exit 1
      interval: 5m
      start_interval: 30s
      start_period: 5m
    command: ["postgres", "-c" ,"shared_preload_libraries=vectors.so", "-c", 'search_path="$$user", public, vectors', "-c", "logging_collector=on", "-c", "max_wal_size=2GB", "-c", "shared_buffers=512MB", "-c", "wal_compression=on"]

networks:
  traefik-proxy:
    external: true

Your .env content

DB_HOSTNAME=postgres
DB_USERNAME=postgres
DB_PASSWORD=<redacted>
DB_DATABASE_NAME=immich

UPLOAD_LOCATION=./library

Reproduction steps

1. Upload or back up the live photos using the Immich iOS app on iOS 18.
2. When downloading or restoring the live photo (on any device), only the still image is downloaded or restored, not the video part.
3. It is treated as a motion photo, even though it is a live photo. (shown in the Immich iOS App Logs)

Relevant log output

IMG_7189

Additional information

No response

alextran1502 commented 1 month ago

Can you please share the full logs? I cannot reproduce it on iOS 18. I took a LivePhotos, upload it and then download it and can use the LivePhotos functionality from the native Photos app just fine

When you say restore on any device, what are those devices?

ankit5902 commented 1 month ago

I have included the logs from the iOS app with the log level set to "Finest". I carried out the following tasks: I uploaded a few live photos, deleted them from the Photos app, and then restored them via the Immich app by clicking on the download icon in the upper right-hand corner.

Immich_log_2024-08-06T01:03:15.400427.log

When you say restore on any device, what are those devices?

For restoring, I tried on an iPhone 13 Pro (the same device from which the image was uploaded to the server) and an iPhone 8 (another device) but I got the following WARNING in the logs:

2024-08-06 01:02:33.435444 | WARNING | ImageViewerService | Asset cannot be saved as a live photo. This is most likely a motion photo. Saving only the image file |

alextran1502 commented 1 month ago

For iPhone 13 Pro, is it successfully restored? You don't need to set the log level to Finest

ankit5902 commented 1 month ago

It only restores the still image, i.e. only the image file is restored. In the Photos app, it shows as a still image with no live photo functions or features. I tried this on both devices, but had no luck.

You don't need to set the log level to Finest

I usually have it set to INFO, but for sending the logs I had it set to FINEST.

alextran1502 commented 1 month ago

Do you mind record the screen capture the whole process? I cannot reproduce it on my end

ankit5902 commented 1 month ago

Below is the screen capture of the entire process:

https://github.com/user-attachments/assets/7e7898a9-fc29-4dff-9c97-03ce1174dd3a

Here are the steps I performed:

  1. First I captured a live photo using the native iOS camera app.
  2. Confirmed in the Photos app that it is a live photo.
  3. Backed up the live photo to the Immich Server via the Immich iOS app.
  4. Deleted the live photo image from the Photos app.
  5. Restored the live photo by clicking the download button in the upper right-hand corner of the Immich app.
  6. Once the download finished, went back to the Photos app to check the image.
  7. Observed that only the still image is restored, and the live photo functionality was lost.
  8. Checked the logs and saw the warning: "Asset cannot be saved as a live photo. This is most likely a motion photo. Saving only the image file."
alextran1502 commented 1 month ago

This is odd; I performed the same thing and was able to restore the LivePhotos functionality just fine.

One thing I noticed about your instance is that the app is still calculating the assets' hash, spinning icon on the bottom app bar. Maybe let that finish and try again

ankit5902 commented 1 month ago

That could be the issue. I'll wait for the hash calculation to finish and then try again to see if that works. Thank you, Alex, for your help! 👍🏻

ankit5902 commented 1 month ago

I tried, but it seems that didn’t help. Additionally, I noticed that now not all pictures or videos from my device are appearing in the Photos tab when I select "Recents" folder for backup. I see some errors in the logs, which I’ve attached below:

Immich_log_2024-08-08T18:48:52.538688.log

ankit5902 commented 3 weeks ago
2024-08-17 19:23:47.578583 | SEVERE   | ImmichErrorLogger    | PlatformDispatcher - Catch all | PlatformException(Error Domain=PHPhotosErrorDomain Code=-1 "(null)", null, null, null) |
#0      StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:648)
#1      MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:334)
<asynchronous suspension>
#2      AssetEntity._getFile (package:photo_manager/src/types/entity.dart:735)
<asynchronous suspension>
#3      HashService._hashAssets (package:immich_mobile/services/hash.service.dart:57)
<asynchronous suspension>
#4      SyncService._addAlbumFromDevice (package:immich_mobile/services/sync.service.dart:687)
<asynchronous suspension>
#5      diffSortedLists (package:immich_mobile/utils/diff.dart:21)
<asynchronous suspension>
#6      SyncService._syncLocalAlbumAssetsToDb (package:immich_mobile/services/sync.service.dart:504)
<asynchronous suspension>
#7      AlbumService.refreshDeviceAlbums (package:immich_mobile/services/album.service.dart:118)
<asynchronous suspension>
#8      AssetNotifier.getAllAsset (package:immich_mobile/providers/asset.provider.dart:55)
<asynchronous suspension>
#9      PhotosPage.build.refreshAssets (package:immich_mobile/pages/photos/photos.page.dart:86)
<asynchronous suspension>
#10     MultiselectGrid.build.wrapLongRunningFun.<anonymous closure> (package:immich_mobile/widgets/asset_grid/multiselect_grid.dart:396)
<asynchronous suspension>
#11     RefreshIndicatorState._show.<anonymous closure>.<anonymous closure> (package:flutter/src/material/refresh_indicator.dart:503)
<asynchronous suspension>

2024-08-17 19:23:47.112148 | INFO     | SyncService          | Syncing a new local album to DB: Recents |
2024-08-17 19:23:47.103740 | INFO     | AlbumService         | Found 36 device albums |
2024-08-17 19:23:46.715999 | SEVERE   | ImmichErrorLogger    | PlatformDispatcher - Catch all | PlatformException(Error Domain=PHPhotosErrorDomain Code=-1 "(null)", null, null, null) |
#0      StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:648)
#1      MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:334)
<asynchronous suspension>
#2      AssetEntity._getFile (package:photo_manager/src/types/entity.dart:735)
<asynchronous suspension>
#3      HashService._hashAssets (package:immich_mobile/services/hash.service.dart:57)
<asynchronous suspension>
#4      SyncService._addAlbumFromDevice (package:immich_mobile/services/sync.service.dart:687)
<asynchronous suspension>
#5      diffSortedLists (package:immich_mobile/utils/diff.dart:21)
<asynchronous suspension>
#6      SyncService._syncLocalAlbumAssetsToDb (package:immich_mobile/services/sync.service.dart:504)
<asynchronous suspension>
#7      AlbumService.refreshDeviceAlbums (package:immich_mobile/services/album.service.dart:118)
<asynchronous suspension>
#8      Future.wait.<anonymous closure> (dart:async/future.dart:524)
<asynchronous suspension>

2024-08-17 19:23:43.189975 | INFO     | BackupNotifier       | _getBackupAlbumsInfo: Found 51 available albums |
2024-08-17 19:23:43.189398 | INFO     | BackupNotifier       | Found 51 local albums |
2024-08-17 19:23:41.931205 | INFO     | AssetNotifier        | Load assets: 1010ms |
2024-08-17 19:23:41.931162 | SEVERE   | ImmichErrorLogger    | PlatformDispatcher - Catch all | PlatformException(Error Domain=PHPhotosErrorDomain Code=-1 "(null)", null, null, null) |
#0      StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:648)
#1      MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:334)
<asynchronous suspension>
#2      AssetEntity._getFile (package:photo_manager/src/types/entity.dart:735)
<asynchronous suspension>
#3      HashService._hashAssets (package:immich_mobile/services/hash.service.dart:57)
<asynchronous suspension>
#4      SyncService._addAlbumFromDevice (package:immich_mobile/services/sync.service.dart:687)
<asynchronous suspension>
#5      diffSortedLists (package:immich_mobile/utils/diff.dart:21)
<asynchronous suspension>
#6      SyncService._syncLocalAlbumAssetsToDb (package:immich_mobile/services/sync.service.dart:504)
<asynchronous suspension>
#7      AlbumService.refreshDeviceAlbums (package:immich_mobile/services/album.service.dart:118)
<asynchronous suspension>
#8      Future.wait.<anonymous closure> (dart:async/future.dart:524)
<asynchronous suspension>

2024-08-17 19:23:41.451397 | INFO     | SyncService          | Syncing a new local album to DB: Recents |
2024-08-17 19:23:41.443105 | INFO     | AlbumService         | Found 36 device albums |
2024-08-17 19:23:41.390119 | INFO     | AlbumService         | refreshDeviceAlbums is already in progress |