{"id":37094806,"url":"https://github.com/derfenix/photocatalog","last_synced_at":"2026-01-14T11:40:18.427Z","repository":{"id":139430983,"uuid":"200540073","full_name":"derfenix/photocatalog","owner":"derfenix","description":"Organize photos within date-aware directory structure","archived":false,"fork":false,"pushed_at":"2025-01-03T22:59:57.000Z","size":20,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-03T23:28:04.234Z","etag":null,"topics":["catalog","golang","organizer","synchronization","tool"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/derfenix.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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}},"created_at":"2019-08-04T20:46:50.000Z","updated_at":"2025-01-03T22:52:21.000Z","dependencies_parsed_at":"2024-06-20T14:24:37.765Z","dependency_job_id":"cbb05c82-48e0-42a4-83eb-8aa45cf59c60","html_url":"https://github.com/derfenix/photocatalog","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/derfenix/photocatalog","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/derfenix%2Fphotocatalog","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/derfenix%2Fphotocatalog/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/derfenix%2Fphotocatalog/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/derfenix%2Fphotocatalog/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/derfenix","download_url":"https://codeload.github.com/derfenix/photocatalog/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/derfenix%2Fphotocatalog/sbom","scorecard":{"id":335354,"data":{"date":"2025-08-11","repo":{"name":"github.com/derfenix/photocatalog","commit":"e66070383f71a0d62aa143b10cf9c8ddded79607"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.4,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/go.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/derfenix/photocatalog/go.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go.yml:20: update your workflow using https://app.stepsecurity.io/secureworkflow/derfenix/photocatalog/go.yml/master?enable=pin","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-18T04:36:09.276Z","repository_id":139430983,"created_at":"2025-08-18T04:36:09.276Z","updated_at":"2025-08-18T04:36:09.276Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28419242,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T10:47:48.104Z","status":"ssl_error","status_checked_at":"2026-01-14T10:46:19.031Z","response_time":107,"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":["catalog","golang","organizer","synchronization","tool"],"created_at":"2026-01-14T11:40:17.808Z","updated_at":"2026-01-14T11:40:18.415Z","avatar_url":"https://github.com/derfenix.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Effortless Photo Organizer\n\n[![Go](https://github.com/derfenix/photocatalog/actions/workflows/go.yml/badge.svg)](https://github.com/derfenix/photocatalog/actions/workflows/go.yml)\n\nA simple tool to organize your photos, videos, or other files by copying or hardlinking them into a date-based directory structure like `$ROOT/year/month/day/`.\n\n## TL;DR\n\nI use a smartphone and Syncthing to automatically sync my photos to my PC. However, if I clean up my phone's memory, the synced photos on my PC are deleted as well.\nDumping everything into one folder wasn't an option — finding anything later would be a nightmare. \n\nTo avoid this, I needed a solution to back up and organize my photos without manual effort. So, I built this tool in one evening to solve the problem. It has worked flawlessly for me and might help you too. If you encounter any issues, feel free to open a ticket — I'll do my best to assist.\n\n## Installation\n\nInstall the tool via `go`:\n\n```bash\ngo install github.com/derfenix/photocatalog/v2@latest\n```\n\nOptionally, copy the binary to a directory in your system or user's `$PATH` (e.g., `/usr/local/bin`):\n\n```bash\nsudo cp ${GOPATH}/bin/photocatalog /usr/local/bin/photocatalog\n```\n\n## Organization Modes\n\nThe tool supports the following organization modes:\n\n- **copy** — Copies files to the target directory. If the filesystem supports it, uses Copy-on-Write (COW) for efficiency (via FICLONE ioctl call).\n- **hardlink** — Creates hardlinks to the source files, saving disk space. Ideal (and usable only) if the source and target are on the same partition, \nthough file permissions remain linked to the original. Fallback to copy on fail.\n- **move** — Moves files from the source to the target directory.\n- **symlink** — Creates symbolic links at the target pointing to the source files.\n\n## Supported Formats\n\n- **JPEG and TIFF files** with valid EXIF metadata.\n- Files named in the format `yyyymmdd_HHMMSS.ext` (optionally with suffixes after the timestamp) (e.g., `20230101_123456.jpg`). This format is common in Android cameras and other devices.\n\nIf a file lacks EXIF data, the tool falls back to parsing the filename.\n\nCurrently, the timestamp format is not customizable. Let me know if support for additional formats is required.\n\n## Usage\n\nArguments\n```\n  -dir-mode string\n        Mode bits for directories can be created while syncing (default \"0777\")\n  -file-mode string\n        Mode bits for files created while syncing (not applicable for hardlink mode) (default \"0644\")\n  -mode string\n        Organazing mode (default \"hardlink\")\n  -overwrite\n        Overwrite existing files\n  -skip-full-sync\n        Skip full sync at startup\n  -source string\n        Source directory\n  -target string\n        Target directory\n  -watch\n        Watch for changes in the source directory (default true)\n\n```\n\n`-skip-full-sync` and `-watch` are not compatible.\n\n`-source` and `-target` are required.\n\n\n## Examples\n\n### One-Time Run\n\n#### Copy Files\n```shell\nphotocatalog -mode copy -target ./photos/ -source ./sync/photos/\n```\n\n#### Create Hardlinks\n```shell\nphotocatalog -mode hardlink -target ./photos/ -source ./sync/photos/\n```\n\n### Watch Mode\n\nEnable continuous monitoring of a source directory:\n\n#### Copy Files\n```shell\nphotocatalog -mode copy -target ./photos -watch -source ./sync/photos/\n```\n\n#### Create Hardlinks\n```shell\nphotocatalog -mode hardlink -target ./photos/ -watch -source ./sync/photos/\n```\n\n## Running as a Service\n\n### Systemd Setup\n\nInstall and configure the service:\n```shell\nsh ./init/install_service.sh systemd\n```\n\nThis will:\n\n1. Install a systemd unit file.\n2. Create a configuration stub at `$HOME/.config/photocatalog`.\n3. Open the config file for editing.\n\nEnable and start the service:\n```shell\nsystemctl --user enable --now photocatalog\n```\n\nNow, files added to the monitored directory (`MONITOR` in the config) will automatically be organized into the target directory under the corresponding subdirectories.\n\n## FAQ\n\n### Why did you create this tool when awesome tool XXX already exists?\nTwo reasons:\n1. I wanted to.\n2. I could.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fderfenix%2Fphotocatalog","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fderfenix%2Fphotocatalog","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fderfenix%2Fphotocatalog/lists"}