diff --git a/owncloud/README.md b/owncloud/README.md new file mode 100644 index 0000000..a198541 --- /dev/null +++ b/owncloud/README.md @@ -0,0 +1,76 @@ +# ownCloud Photo Organizer + +## Overview + +ownCloud's Android app does not support automatic year/month subfolder creation +during instant upload (unlike Nextcloud). This script compensates by running +nightly and sorting all uploaded photos and videos into a `YEAR/YEAR-MM/` +folder structure. + +## Script + +**Location on server:** `/usr/local/bin/organize_photos.sh` + +**Source folder watched:** +`/mnt/INTEL-SSD/ownCloud - Joe Gitta@cloud.jgitta.com/Personal/InstantUpload` + +**Subfolders processed:** `Camera`, `BlueIris`, `Facebook`, `scans`, `EufyVideoDir` + +**Supported file types:** jpg, jpeg, png, gif, heic, webp, bmp, tiff, mp4, mov, pdf + +## How It Works + +The script reads the date from each filename using these patterns (in order): + +1. `YYYYMMDD` embedded in the filename (e.g. `20231022_082107.jpg`) +2. `YYYY-MM-DD` or `YYYY_MM_DD` format +3. 13-digit Unix timestamp in milliseconds (e.g. `FB_IMG_1572815868839.jpg`) +4. `MM_DD_YYYY` prefix (scans folder format, e.g. `06_11_2026_7_28_16_AM.pdf`) + +Files that don't match any pattern are moved to `_unsorted/` for manual review. + +## Output Structure + +``` +InstantUpload/ +└── Camera/ + ├── 2023/ + │ ├── 2023-06/ + │ └── 2023-12/ + ├── 2024/ + │ └── 2024-03/ + └── _unsorted/ +``` + +## Cron Job + +Runs nightly at 2:00 AM as root: + +``` +0 2 * * * /usr/local/bin/organize_photos.sh +``` + +To view or edit: `sudo crontab -e` + +## Log File + +Each run appends to `/var/log/organize_photos.log`: + +``` +=== 2026-06-14 02:00:01 Starting === + Moved: 12 Unsorted: 0 Skipped: 0 Errors: 0 +=== Done === +``` + +Check the log anytime with: +```bash +cat /var/log/organize_photos.log +``` + +## Installation + +```bash +sudo cp organize_photos.sh /usr/local/bin/organize_photos.sh +sudo chmod +x /usr/local/bin/organize_photos.sh +(sudo crontab -l; echo "0 2 * * * /usr/local/bin/organize_photos.sh") | sudo crontab - +``` diff --git a/owncloud/organize_photos.sh b/owncloud/organize_photos.sh new file mode 100755 index 0000000..81e094a --- /dev/null +++ b/owncloud/organize_photos.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash + +BASE="/mnt/INTEL-SSD/ownCloud - Joe Gitta@cloud.jgitta.com/Personal/InstantUpload" +UNSORTED="$BASE/_unsorted" +LOG="/var/log/organize_photos.log" +moved=0; skipped=0; unsorted=0; errors=0 + +echo "=== $(date +%Y-%m-%d\ %H:%M:%S) Starting ===" >> "$LOG" + +ts_to_ym() { + local ts_ms="$1" + local ts_s=$(( ts_ms / 1000 )) + date -d "@$ts_s" "+%Y %m" 2>/dev/null +} + +get_ym() { + local f="$1" + local b + b=$(basename "$f") + if [[ "$b" =~ (^|[^0-9])([0-9]{4})(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01]) ]]; then + echo "${BASH_REMATCH[2]} ${BASH_REMATCH[3]}"; return + fi + if [[ "$b" =~ ([0-9]{4})[-_](0[1-9]|1[0-2])[-_](0[1-9]|[12][0-9]|3[01]) ]]; then + echo "${BASH_REMATCH[1]} ${BASH_REMATCH[2]}"; return + fi + if [[ "$b" =~ (^|[^0-9])([0-9]{13})([^0-9]|$) ]]; then + ts_to_ym "${BASH_REMATCH[2]}"; return + fi + if [[ "$b" =~ ^([0-9]{2})_([0-9]{2})_([0-9]{4})_ ]]; then + echo "${BASH_REMATCH[3]} ${BASH_REMATCH[1]}"; return + fi + echo "" +} + +move_file() { + local src="$1" + local root="$2" + local fname + fname=$(basename "$src") + local ym + ym=$(get_ym "$src") + if [[ -z "$ym" ]]; then + mkdir -p "$UNSORTED" + if [[ ! -e "$UNSORTED/$fname" ]]; then + mv "$src" "$UNSORTED/$fname" && ((unsorted++)) || ((errors++)) + else + ((skipped++)) + fi + return + fi + local year month dest_dir dest + year=$(echo "$ym" | cut -d' ' -f1) + month=$(echo "$ym" | cut -d' ' -f2) + dest_dir="$root/$year/$year-$month" + dest="$dest_dir/$fname" + mkdir -p "$dest_dir" + if [[ -e "$dest" ]]; then + ((skipped++)); return + fi + mv "$src" "$dest" && ((moved++)) || { echo " ERROR: $fname" >> "$LOG"; ((errors++)); } +} + +for SUBDIR in Camera BlueIris Facebook scans EufyVideoDir; do + SUBPATH="$BASE/$SUBDIR" + [[ -d "$SUBPATH" ]] || continue + while IFS= read -r -d '' f; do + move_file "$f" "$SUBPATH" + done < <(find "$SUBPATH" -maxdepth 1 -type f -print0) +done + +echo " Moved: $moved Unsorted: $unsorted Skipped: $skipped Errors: $errors" >> "$LOG" +echo "=== Done ===" >> "$LOG"