# O-PHONE A Python command-line tool for organising and deduplicating media imported from phones and other devices. The program is designed primarily for creating a long-term media backup on an external drive. It uses SHA-256 file hashes and a SQLite database to identify files that have already been backed up. This allows the same source folder to be imported repeatedly without creating duplicate copies. ### WHAT IT DOES Given a source folder and a destination folder, o-phone.py: 1. Searches the source folder recursively. 2. Identifies supported image, video and other files. 3. Determines the best available date for each file. 4. Organises media into a date-based directory structure. 5. Renames imported media using a consistent filename format. 6. Calculates a SHA-256 hash for every imported file. 7. Stores the hash and file information in a SQLite database located in the destination folder. 8. Checks the database before importing a file. 9. Skips files whose hash is already present in the database. 10. Verifies files after they have been copied. 11. Records the imported file in the database only after the copy and verification have succeeded. This makes it safe to run the program again later when new photos and videos have been copied from the phone. ### DIRECTORY STRUCTURE The normal destination structure looks like this: phone-media-backup/ | +-- .phone-backup.sqlite +-- 2024/ | +-- January/ | | +-- 03-01-2024/ | | | +-- Phone-03-01-2024-482193.jpg | | | +-- Phone-03-01-2024-817204.mp4 | | | | | +-- February/ | | | +-- December/ | +-- 2025/ | +-- January/ | | | +-- ... | +-- 2026/ | +-- screenshots/ | +-- u-mp3/ | +-- u-xxx/ ### MEDIA ORGANISATION Images and videos are organised by: YEAR MONTH DAY-MONTH-YEAR For example: 2026/ August/ 14-08-2026/ The month is written as its full English name rather than a numeric month. ### FILENAMES Imported files are renamed to: Phone-DD-MM-YYYY-RANDOM.ext For example: Phone-14-08-2026-483921.jpg Phone-14-08-2026-182735.mp4 The six-digit number is randomly generated. The number is not used as the duplicate identifier. The SHA-256 hash of the actual file contents is the authoritative duplicate identifier. ### SCREENSHOTS PNG files are treated as screenshots. They are placed into: screenshots/ The original date information is still retained in the database. Other supported image formats remain in the normal date-based directory structure. ### UNKNOWN FILE TYPES Files with formats that are not recognised as normal photo or video media are placed into a directory based on their extension. For example: .mp3 becomes: u-mp3/ A file with extension: .xyz would become: u-xyz/ ### FILES WITHOUT AN EXTENSION Files without a usable extension are handled separately rather than being silently discarded. The exact handling depends on the current version of o-phone.py. ### DUPLICATE DETECTION Duplicate detection is based on SHA-256. For each file being imported, o-phone.py calculates: SHA-256(file contents) The resulting hash is compared against hashes stored in: .phone-backup.sqlite If the hash already exists, the file is considered already backed up and is skipped. This means duplicate detection is based on the contents of the file rather than: - filename - folder name - file creation date - file modification date - device name This is important because phones can rename files or give different metadata to the same media. ### WHY USE A DATABASE? The SQLite database acts as an index of the backup. Instead of repeatedly scanning every existing file on the external drive, o-phone.py can check the database for the SHA-256 hash. Conceptually: Source file | v Calculate SHA-256 | v Search SQLite database | +---- hash exists ----> SKIP | +---- hash missing ---> COPY | v Verify copy | v Add to database SQLite is built into Python, so no external database server is required. ### IMPORTANT: DATABASE UPDATE ORDER The database should only be updated after a successful copy. The intended sequence is: 1. Calculate source hash 2. Check database 3. If already present, skip 4. Copy file 5. Verify destination file 6. Calculate/verify destination hash 7. Confirm hashes match 8. Add file to database This prevents a failed or incomplete copy from being recorded as successfully backed up. ### RUNNING THE PROGRAM The basic command is: python3 o-phone.py SOURCE DESTINATION Example: python3 o-phone.py \ "/Users/yourname/Desktop/phone-backup-moto-g13" \ "/Volumes/all/phone-media-backup" ### SOURCE The first argument is the folder containing the files imported from your phone. The source folder can contain subdirectories. o-phone.py searches recursively. Example: /Users/yourname/Desktop/phone-backup-moto-g13/ ### DESTINATION The second argument is the root folder of the backup. For example: /Volumes/all/phone-media-backup/ The destination can be located on an external hard drive or SSD. ### RECOMMENDED WORKFLOW When new photos are available on the phone: 1. Copy the new phone files onto your Mac. 2. Put them into a source folder. 3. Run o-phone.py using the same destination folder. For example: python3 o-phone.py \ "/Users/yourname/Desktop/phone-backup-moto-g13" \ "/Volumes/all/phone-media-backup" The program will calculate hashes for the source files. Files already present in the database will be skipped. New files will be copied into the appropriate destination directories. Therefore it is safe to use the same destination repeatedly. ### DRY RUN The program supports a dry-run mode when available in the current version. Dry-run mode allows the proposed changes to be inspected without actually copying files. Example: python3 o-phone.py SOURCE DESTINATION --dry-run Always check the output before performing a large import. ### DATABASE The SQLite database is stored inside the destination directory. Default database: .phone-backup.sqlite It should be treated as part of the backup. If you move the entire backup to another drive, move the database with it. Do not delete or manually modify the database unless you understand the consequences. ### DATABASE INTEGRITY The database is an index of the files in the backup. Over time it is possible for the database and the actual files to become different. For example, this could happen if somebody manually deletes a file from the backup drive. A database verification/check function is therefore intended to compare: SQLite records against actual files on disk The check should be used periodically, especially after manually changing files on the backup drive. ### DO NOT MANUALLY ORGANISE THE BACKUP Once files have been imported, it is recommended not to manually rename or move them. The database records their paths. Moving files outside of o-phone.py can therefore cause the database and the actual backup to become inconsistent. If files need to be reorganised, future versions of the program may provide dedicated maintenance commands. ### SOURCE FILES ARE NOT DELETED o-phone.py is intended to be a backup/import tool. It should not delete the original files from the source folder as part of a normal import. The source files should remain available until you have independently confirmed that the backup is complete and healthy. ### FILE DATES The program attempts to determine the most useful date associated with each media file. Depending on the file type, this may come from embedded metadata or filesystem information. The exact date source can vary between: - JPEG photos - HEIC photos - PNG screenshots - videos - RAW camera files - files copied from different phones - files whose metadata has been removed When metadata is unavailable, the program uses an appropriate fallback. ### AW FILES RAW camera files are preserved rather than converted. They are treated as source files and copied to the backup. The original RAW data is not modified. ### SUPPORTED MEDIA The exact list of supported extensions is defined in o-phone.py. Typical photo formats include: .jpg .jpeg .heic .heif .png .webp .tif .tiff RAW formats Typical video formats include: .mp4 .mov .m4v .avi .mkv .3gp The program may recognise additional formats. Unknown formats are not automatically discarded. ### REQUIREMENTS macOS or another operating system with Python 3. Python 3.9 or newer is recommended. The standard Python libraries used by the program do not require a separate database server. Some optional functionality may require additional Python packages. ### INSTALLATION Check Python: python3 --version Run the program directly: python3 o-phone.py SOURCE DESTINATION ### SAFETY Before using o-phone.py with an important archive: 1. Test it with a small source folder. 2. Use --dry-run where available. 3. Confirm the destination path carefully. 4. Check the generated directory structure. 5. Open several copied files. 6. Confirm the SQLite database exists. 7. Run the database/filesystem integrity check if available. 8. Keep another independent backup of important media. ### IMPORTANT PRINCIPLE The SHA-256 hash is the identity of the file. The filename and directory are organisational information. For example, these two files: IMG_1234.JPG vacation-photo.jpg may represent exactly the same file contents. If their SHA-256 hashes match, o-phone.py considers them duplicates. Conversely, two files with the same filename are not considered duplicates if their contents differ. ### PROJECT PHILOSOPHY The goal of o-phone.py is to provide a simple, dependable and repeatable way to build a personal media archive. The important principles are: - Do not delete source files. - Do not overwrite existing backup files unnecessarily. - Use file contents rather than filenames for duplicate detection. - Verify files before recording them as successfully backed up. - Keep a local SQLite index. - Keep the archive organised by date. - Make repeated imports safe. - Prefer simple, inspectable storage over proprietary formats. ### EXAMPLE Suppose the phone contains: DCIM/Camera/IMG_1234.jpg DCIM/Camera/IMG_1235.jpg DCIM/Camera/VID_001.mp4 Screenshots/screenshot.png Run: python3 o-phone.py \ "/Users/yourname/Desktop/phone-import" \ "/Volumes/all/phone-media-backup" The destination might become: phone-media-backup/ | +-- .phone-backup.sqlite | +-- 2026/ | | | +-- August/ | | | +-- 14-08-2026/ | | | +-- Phone-14-08-2026-483921.jpg | +-- Phone-14-08-2026-193827.mp4 | +-- screenshots/ | +-- Phone-14-08-2026-928374.png If the same source files are imported again, their hashes will already exist in the database and they will be skipped. # catalogue.py Read-only HTML catalogue generator for backups created by `o-phone.py`. Produces a single offline `index.html` you can open directly from the backup folder. No web server, no external dependencies, no changes to your media or database. ## Requirements - Python 3.9+ - An existing o-phone backup that contains `.photo_organiser.db` ## Usage ```bash # Generate the catalogue python3 catalogue.py "/path/to/phone-media-backup" # Generate and open in your default browser python3 catalogue.py "/path/to/phone-media-backup" --open # Check database ↔ filesystem consistency (no HTML written) python3 catalogue.py "/path/to/phone-media-backup" --check # Extra diagnostic detail python3 catalogue.py "/path/to/phone-media-backup" --check --verbose ``` Output is always written to: ``` /path/to/phone-media-backup/index.html ``` ## What it shows - Year → Month → Day layout (newest first) - Filter buttons: All / Photos / Videos / Screenshots - Client-side search (filename, date, device, type, etc.) - Click any item for a lightbox viewer (← → Esc) - Videos and screenshots are marked; RAW files show a placeholder - Lazy-loaded thumbnails; original files are never modified ## Safety - Opens the SQLite database **read-only** - Never moves, renames, deletes, or copies media - Never writes to the database - Missing files are reported as warnings; the catalogue is still generated for files that exist