{"id":29639654,"url":"https://github.com/thrillfall/journeys","last_synced_at":"2026-05-07T10:01:32.818Z","repository":{"id":304251467,"uuid":"1018244945","full_name":"thrillfall/journeys","owner":"thrillfall","description":"Clusters your images into journeys (vacations/trips) and creates albums and video reels for each journey. Integrates with the Memories app to access image metadata.","archived":false,"fork":false,"pushed_at":"2026-04-25T21:11:55.000Z","size":37712,"stargazers_count":13,"open_issues_count":3,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-25T23:32:13.100Z","etag":null,"topics":["clustering","memories","nextcloud","video"],"latest_commit_sha":null,"homepage":"https://apps.nextcloud.com/apps/journeys","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thrillfall.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-07-11T21:39:32.000Z","updated_at":"2026-04-25T21:11:59.000Z","dependencies_parsed_at":"2025-07-11T23:34:38.569Z","dependency_job_id":"a956f7aa-9aa4-4d7d-8a06-b1e0079ba2bc","html_url":"https://github.com/thrillfall/journeys","commit_stats":null,"previous_names":["thrillfall/journeys"],"tags_count":77,"template":false,"template_full_name":null,"purl":"pkg:github/thrillfall/journeys","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thrillfall%2Fjourneys","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thrillfall%2Fjourneys/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thrillfall%2Fjourneys/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thrillfall%2Fjourneys/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thrillfall","download_url":"https://codeload.github.com/thrillfall/journeys/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thrillfall%2Fjourneys/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32732349,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-07T02:14:30.463Z","status":"ssl_error","status_checked_at":"2026-05-07T02:14:29.405Z","response_time":62,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clustering","memories","nextcloud","video"],"created_at":"2025-07-21T20:38:14.022Z","updated_at":"2026-05-07T10:01:32.803Z","avatar_url":"https://github.com/thrillfall.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Journeys: Automatic Photo Album Creation for Nextcloud\n\nAutomatically cluster your images into journeys (vacations/trips) and create albums for each journey.\n\n**Requires the [Memories](https://github.com/pulsejet/memories) app.**\n\n## ✨ Features\n- **🗺️ Location \u0026 Time Clustering:** Group images by when and where they were taken\n- **🗂️ Album Creation:** Albums are created automatically for each journey\n- **⚙️ Customizable:** Control minimum cluster size, time gap, and distance thresholds in a modern card-based settings UI with inline auto-saving toggles\n- **🎥 Video Rendering:** Render any Journey album—or a manually created Photos album—to an MP4 from the personal settings page or via OCC. Settings-page renders are queued as background jobs so the page returns instantly and the tab can be closed; the resulting file shows up in `Documents/Journeys Movies/`. Portrait videos use Ken Burns transitions and may include occasional 3‑wide landscape stacks with a short center pause. Per-location subtitles fade in/out as the journey moves between places (toggle in personal settings, default on). Background music is sourced at 128kbps and gently fades out at the end of the video.\n- **📚 Mobile-friendly journey browser:** Compact card list with year, month and location filters, plus a `▶ Watch` shortcut on cards whose video already exists, and a separate \"Rendered videos\" section listing every file in `Documents/Journeys Movies/`. Built for instances where journeys (and renders) accumulate over time.\n - **⚡ Auto-generation (cron-only, \u003e= 0.8.0):** New away-from-home albums found by the daily cron job can automatically trigger video rendering. Enable this in personal settings and choose the orientation (portrait/landscape). Rendering runs as a separate Nextcloud background job so clustering is not blocked.\n - **🧹 Auto-cleanup of empty albums (\u003e= 0.23.6):** Journey albums whose photos you've all deleted from the Photos app are removed automatically on the next clustering run (daily cron, OCC, or settings UI). Manual albums are never touched.\n - **✏️ User-editable journey names (\u003e= 0.24.0):** Set a custom name (e.g. *Christmas 2024*, *Family reunion*, *Sabbatical*) for any journey from the personal settings page. The custom name shows as the card heading, renames the Photos album, and is used as the video title overlay; the auto-derived location/date name stays visible as a smaller secondary line. Custom names survive `--from-scratch` reclustering — they re-attach automatically to whichever new cluster has the highest file-ID overlap (Jaccard ≥ 0.5), so retroactive photo additions and algorithm/place-resolver improvements don't lose your hand-picked names.\n\n## Requirements\n- The Memories app must be installed and enabled.\n- The Places setup in the Memories app must be completed (see Memories app documentation for details).\n- The Nextcloud server must have `ffmpeg` available in `PATH` for video rendering.\n\n## 📌 Note on which images are clustered\n\nJourneys uses the Memories index (`oc_memories`) to determine which images are available for clustering.\n\nThis means the set of images Journeys can cluster depends on the **Memories admin settings**.\n\nIf you don’t want unexpected images from outside your configured Memories timeline folders to be included in Journeys clustering, configure:\n\n- **Settings → Administration → Memories → Media Indexing → Index per-user timeline folders**\n\n## 🚀 OCC Command Usage\n\n```sh\nphp occ journeys:cluster-create-albums \u003cuser\u003e [maxTimeGap] [maxDistanceKm] [--from-scratch] [--include-group-folders] [--include-shared-images] [--debug-splits] [--from \u003cdate\u003e] [--to \u003cdate\u003e] [--last-years \u003cN\u003e]\n```\n\n**Arguments:**\n- `user` — The user to cluster images for (**required**)\n- `maxTimeGap` — Max allowed time gap in hours (optional; if omitted, uses your Personal Settings value)\n- `maxDistanceKm` — Maximum allowed distance in kilometers between consecutive images in a cluster (optional; if omitted, uses your Personal Settings value)\n- `--min-cluster-size` — Minimum images per cluster (optional; if omitted, uses your Personal Settings value)\n  - `--include-group-folders` — Include photos from Group Folders and other mounts in clustering (optional, default: off)\n  - `--include-shared-images` — Include images available via user shares (optional, default: off). Inclusion is scoped to the shared mount root subtree (prevents pulling unrelated files from other users' storages).\n  - `--from` — Only cluster images taken on/after this date/time (ISO-8601 or `YYYY-MM-DD`)\n  - `--to` — Only cluster images taken on/before this date/time (ISO-8601 or `YYYY-MM-DD`)\n  - `--last-years` — Only cluster images from the last `N` years (alternative to `--from/--to`)\n  - `--debug-splits` — Print why clustering starts a new cluster (time/distance exceeded amounts and home-aware boundaries).\n\nNote: Time thresholds are specified in hours and support decimals (e.g., 0.5 = 30 minutes).\n\n**Note:** When arguments/options are omitted, the command falls back to the user's saved values from Personal Settings. The command prints the effective settings at the start of the run.\n\n**How time gap influences clustering:**  \nThe `maxTimeGap` defines the largest allowed time (in hours) between two consecutive images for them to be grouped into the same journey. If the gap between two images exceeds this value, a new journey (album) is started. Smaller values create more, shorter journeys; larger values group more images together.\n\n**Example:**\n```sh\nphp occ journeys:cluster-create-albums admin 24 100 --min-cluster-size=5\n```\n\n### Large libraries: safe, scoped backfills\n\nIf your photo timeline goes back many years, you can limit clustering to a specific time window.\n\nCreate journeys only for the last 2 years:\n\n```sh\nphp occ journeys:cluster-create-albums admin --last-years=2\n```\n\nCreate journeys only for a specific time period:\n\n```sh\nphp occ journeys:cluster-create-albums admin --from=2018-01-01 --to=2019-12-31\n```\n\nNotes:\n\n- If you omit `--from/--to/--last-years`, the command keeps the current behavior (it will use incremental clustering by default).\n- With incremental clustering, the effective start is the later of:\n  - the latest previously created journey end\n  - `--from` (if provided)\n\nWhen arguments/options are omitted, the command falls back to the user's saved values from Personal Settings. The command prints the effective settings at the start of the run.\n\n## 🎥 Video rendering (\u003e= 0.7.2)\n\n- Render any Journey album to an MP4 via personal settings: open **Settings → Journeys**, find the album in the list, and click **Render Video**.\n- Render any manual Photos album by entering its album ID (or selecting it from the \"Manual Photos albums\" table) in the same settings page.\n- Or use the OCC command:\n\n  ```sh\n  php occ journeys:render-cluster-video \u003cuser\u003e \u003calbumId\u003e\n  ```\n\n- The rendered file is saved to `Documents/Journeys Movies/` in the user’s storage (or to a custom path when `--output` is provided).\n- Each render stitches in background music selected from the bundled list (128kbps sources) and applies a short fade‑out at the end of the video.\n\n### Automatic rendering (cron-only, \u003e= 0.8.0)\n\n- When enabled in personal settings, the daily cron job will enqueue a render for each newly created away-from-home album.\n- Orientation honors the user preference (portrait/landscape).\n- Rendering is performed by a separate Nextcloud background job.\n\n### Landscape stacks in portrait videos (\u003e= 0.7.4)\n\n- When enough landscape photos are available, the renderer inserts an occasional 3‑row landscape stack into portrait videos.\n- Each row slides in, pauses at the center for ~2 seconds, then slides out. Slides are a bit faster for a dynamic feel.\n- Stacks require at least 3 landscape images in the selected set. By default, a stack is inserted after ~4 portrait clips (heuristic).\n- Tip: use `--duration-seconds \u003e= 2.8` to allow a full ~2s center pause plus visible in/out slides; shorter durations still work but the pause will be shorter.\n\n### Current limitations\n\n- Portrait mode is the default.\n- Portrait and landscape rendering chunk automatically when any source image exceeds 13 megapixels; otherwise they run as a single pass.\n\n### Roadmap\n\n- Background music selection.\n- Scene transitions and pacing controls.\n\n\n## 🧵 Merging adjacent journeys (default)\n\nAfter the raw clusterer runs, a post-processing pass stitches adjacent clusters that look like the same journey — specifically, clusters in the same country (via OSM admin_level=2) within 7 days of each other. This fixes two common over-splits:\n\n- **Multi-city road trips** (e.g. Paris → Lyon → Nice) where the distance threshold trips on each inter-city leg.\n- **Long vacations with rest days** where time gaps of 2–3 days without photos trip the time threshold.\n\nOnly away-from-home clusters are merged; near-home clusters never merge across gaps, because returning to the home radius is treated as ending a journey. Disable with:\n\n- CLI: `--no-merge` flag on `journeys:cluster-create-albums`\n- UI: untick \"Merge adjacent journeys in the same country\" in Personal Settings\n\n## 🏠 Home-aware clustering (default)\n\nHome-aware mode adapts clustering based on whether photos are taken near your home or away:\n\n- Near home: uses the global time threshold and a capped distance (defaults: 24h, up to 25km)\n- Away from home: uses separate, typically looser thresholds (defaults: 36h, 50km)\n- The timeline is segmented into contiguous near/away blocks, and each block is clustered independently. This supports long, multi-week away trips without per-edge switching.\n\n\n```sh\nphp occ journeys:cluster-create-albums \u003cuser\u003e [--home-lat \u003clat\u003e --home-lon \u003clon\u003e --home-radius \u003ckm\u003e] \\\n  [--near-time-gap \u003chours\u003e] [--near-distance-km \u003ckm\u003e] \\\n  [--away-time-gap \u003chours\u003e] [--away-distance-km \u003ckm\u003e]\n```\n\nFlags:\n\n- `--home-lat`, `--home-lon` Provide home coordinates (optional; otherwise auto-detected)\n- `--home-radius` Home radius in km (default: 50)\n- `--near-time-gap` Near-home max time gap in hours (optional; if omitted, uses your Personal Settings value)\n- `--near-distance-km` Near-home max distance in km (optional; if omitted, uses your Personal Settings value)\n- `--away-time-gap` Away-from-home max time gap in hours (optional; if omitted, uses your Personal Settings value)\n- `--away-distance-km` Away-from-home max distance in km (optional; if omitted, uses your Personal Settings value)\n\nNote: Time thresholds use hours and accept decimals (e.g., 1.5 = 1 hour 30 minutes).\n\nNotes:\n\n- If near-home thresholds are left at their defaults, the time gap aligns to the global `maxTimeGap`, and the distance aligns to the global `maxDistanceKm` but is capped at 25km for finer local clustering.\n- For long away trips with multi-day gaps in the same place, consider increasing `--away-time-gap` (e.g., 72–168 hours).\n- In home-aware mode, there is no post-processing merge; clusters are used as produced per segment for predictability.\n\n### Incremental clustering (default)\n\n- By default, clustering runs incrementally: it only considers images taken after the latest previously created cluster. This keeps runtime low and avoids recreating existing albums.\n- Use `--from-scratch` to purge previously created cluster albums and recluster all images from a clean slate.\n- To avoid creating incomplete trips, clusters whose last image is within the past 48 hours are skipped (configurable via `--recent-cutoff-days`); they will be picked up in a future run once the trip is likely complete.\n\n## 🔔 Notifications (\u003e= 0.5.4)\n\n- After each run that creates one or more albums, the app sends a single aggregated notification to the user with a short summary of the created albums.\n- The notification contains an \"Open Photos\" action that links to the Photos app so you can review the albums quickly.\n- Notifications may appear with a short delay due to client polling.\n\n\n## 🧭 Clustering robustness (\u003e= 4.0.3)\n\n- The clusterer now prevents time-only tails (images without coordinates) from bridging over large spatial jumps.\n- Distance checks are anchored to the last-known geolocated photo within the current cluster, so a run of no-location images won’t “stitch” a far-away next geolocated point into the same cluster.\n- This improves results for long trips where some photos are missing GPS data, especially in home-aware “away” segments.\n\n\n\n## ⬆️ Upgrade notes\n\n- Update the app to apply DB migrations:\n  ```sh\n  php occ app:update journeys\n  # if prompted that an upgrade is required, run:\n  php occ upgrade\n  ```\n- After updating, re-run the cluster command. To reset old albums once, you can run:\n  ```sh\n  php occ journeys:remove-all-albums \u003cuser\u003e\n  ```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthrillfall%2Fjourneys","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthrillfall%2Fjourneys","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthrillfall%2Fjourneys/lists"}