{"id":34192886,"url":"https://github.com/tommyblue/smugmug-backup","last_synced_at":"2026-03-09T16:43:22.265Z","repository":{"id":36537937,"uuid":"177876633","full_name":"tommyblue/smugmug-backup","owner":"tommyblue","description":"Makes a full backup of a SmugMug account","archived":false,"fork":false,"pushed_at":"2026-02-25T17:07:16.000Z","size":21644,"stargazers_count":100,"open_issues_count":3,"forks_count":19,"subscribers_count":4,"default_branch":"main","last_synced_at":"2026-02-25T20:08:25.036Z","etag":null,"topics":["hacktoberfest"],"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/tommyblue.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"code_of_conduct.md","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":"2019-03-26T22:14:45.000Z","updated_at":"2026-02-25T17:07:19.000Z","dependencies_parsed_at":"2023-11-21T15:29:46.755Z","dependency_job_id":"9ccc8ee2-c0a3-4f13-b2de-a8416d28b912","html_url":"https://github.com/tommyblue/smugmug-backup","commit_stats":null,"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"purl":"pkg:github/tommyblue/smugmug-backup","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tommyblue%2Fsmugmug-backup","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tommyblue%2Fsmugmug-backup/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tommyblue%2Fsmugmug-backup/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tommyblue%2Fsmugmug-backup/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tommyblue","download_url":"https://codeload.github.com/tommyblue/smugmug-backup/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tommyblue%2Fsmugmug-backup/sbom","scorecard":{"id":892993,"data":{"date":"2025-08-11","repo":{"name":"github.com/tommyblue/smugmug-backup","commit":"6ea883fa373cd9042d4b26b0dc48cb8d3d9ecf77"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.9,"checks":[{"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":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/go.yml:1","Warn: no topLevel permission defined: .github/workflows/stale.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":"Code-Review","score":0,"reason":"Found 0/20 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":"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":"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":"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":"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":"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":"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":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"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":"Signed-Releases","score":0,"reason":"Project has not signed or included provenance with any releases.","details":["Warn: release artifact v1.6.0 not signed: https://api.github.com/repos/tommyblue/smugmug-backup/releases/190726031","Warn: release artifact v1.5.5 not signed: https://api.github.com/repos/tommyblue/smugmug-backup/releases/172178566","Warn: release artifact v1.5.4 not signed: https://api.github.com/repos/tommyblue/smugmug-backup/releases/157687788","Warn: release artifact v1.5.3 not signed: https://api.github.com/repos/tommyblue/smugmug-backup/releases/139830689","Warn: release artifact v1.5.2 not signed: https://api.github.com/repos/tommyblue/smugmug-backup/releases/137461408","Warn: release artifact v1.6.0 does not have provenance: https://api.github.com/repos/tommyblue/smugmug-backup/releases/190726031","Warn: release artifact v1.5.5 does not have provenance: https://api.github.com/repos/tommyblue/smugmug-backup/releases/172178566","Warn: release artifact v1.5.4 does not have provenance: https://api.github.com/repos/tommyblue/smugmug-backup/releases/157687788","Warn: release artifact v1.5.3 does not have provenance: https://api.github.com/repos/tommyblue/smugmug-backup/releases/139830689","Warn: release artifact v1.5.2 does not have provenance: https://api.github.com/repos/tommyblue/smugmug-backup/releases/137461408"],"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"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:9: update your workflow using https://app.stepsecurity.io/secureworkflow/tommyblue/smugmug-backup/go.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go.yml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/tommyblue/smugmug-backup/go.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go.yml:20: update your workflow using https://app.stepsecurity.io/secureworkflow/tommyblue/smugmug-backup/go.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/go.yml:58: update your workflow using https://app.stepsecurity.io/secureworkflow/tommyblue/smugmug-backup/go.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/stale.yml:10: update your workflow using https://app.stepsecurity.io/secureworkflow/tommyblue/smugmug-backup/stale.yml/main?enable=pin","Warn: containerImage not pinned by hash: Dockerfile:1: pin your Docker image by updating ubuntu:22.04 to ubuntu:22.04@sha256:1aa979d85661c488ce030ac292876cf6ed04535d3a237e49f61542d8e5de5ae0","Warn: pipCommand not pinned by hash: get_tokens/init.sh:6","Warn: goCommand not pinned by hash: script/test:9","Warn: goCommand not pinned by hash: .github/workflows/go.yml:40","Warn: goCommand not pinned by hash: .github/workflows/go.yml:45","Warn: goCommand not pinned by hash: .github/workflows/go.yml:50","Info:   0 out of   4 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction dependencies pinned","Info:   0 out of   4 goCommand dependencies pinned","Info:   0 out of   1 containerImage dependencies pinned","Info:   0 out of   1 pipCommand 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":"Packaging","score":10,"reason":"packaging workflow detected","details":["Info: Project packages its releases by way of GitHub Actions.: .github/workflows/go.yml:4"],"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":"SAST","score":3,"reason":"SAST tool is not run on all commits -- score normalized to 3","details":["Warn: 4 commits out of 13 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":9,"reason":"1 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GO-2025-3770 / GHSA-vrw8-fxc6-2r93"],"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-24T12:34:47.287Z","repository_id":36537937,"created_at":"2025-08-24T12:34:47.287Z","updated_at":"2025-08-24T12:34:47.287Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30302712,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-09T14:33:48.460Z","status":"ssl_error","status_checked_at":"2026-03-09T14:33:48.027Z","response_time":61,"last_error":"SSL_read: 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":["hacktoberfest"],"created_at":"2025-12-15T16:25:51.418Z","updated_at":"2026-03-09T16:43:22.255Z","avatar_url":"https://github.com/tommyblue.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SmugMug backup\n\n[![PkgGoDev](https://pkg.go.dev/badge/github.com/tommyblue/smugmug-backup)](https://pkg.go.dev/github.com/tommyblue/smugmug-backup) ![Go](https://github.com/tommyblue/smugmug-backup/workflows/Go/badge.svg)\n\nMakes a full backup of a [SmugMug](https://www.smugmug.com/) account (images and videos\nare supported).\n\nThe program loops over the images and videos of the user's albums and saves them in the destination\nfolder, replicating the SmugMug paths.\n\nYou can run the app multiple times, all exising files will be skipped if their sizes match.\n\nWith a good internet connection, a full backup of ~200GB can be completed in around 90 minutes using 10 analyzers and 10 downloaders (see #configuration for these options tuning) and few minutes for a daily incremental backup.\n\n- [SmugMug backup](#smugmug-backup)\n  - [Releases](#releases)\n  - [Automated backups](#automated-backups)\n  - [Configuration](#configuration)\n  - [Run](#run)\n  - [Credentials](#credentials)\n    - [Obtain API keys](#obtain-api-keys)\n    - [Obtain Tokens](#obtain-tokens)\n  - [Build and install](#build-and-install)\n  - [Debug for errors](#debug-for-errors)\n  - [Visualize software stats](#visualize-software-stats)\n  - [Credits](#credits)\n  - [Code of conduct](#code-of-conduct)\n  - [Bugs and contributing](#bugs-and-contributing)\n\n## Releases\n\nReleases for multiple systems (including ARM) can be found in the\n[project releases page](https://github.com/tommyblue/smugmug-backup/releases)\n\n## Automated backups\n\nAs mentioned above, the app can be executed multiple times with the same configuration to perform incremental backups.  \nMy suggestion is to use a linux home server (like a RaspberryPI with an attached NAS or external disk) and use `cron` to execute the backup daily.\n\nA simple setup includes the configuration file into `$HOME/.smgmg/config.toml`, the binary file into `$HOME/bin/smugmug-backup` and this simple line into user's crontab (run `crontab -e` to edit the crontab configuration):\n\n```\n3 4 * * * $HOME/bin/smugmug-backup\n```\n\nIf your system is configured to send emails then you'll also receive a daily recap of the backup job in your inbox :tada:\n\n## Configuration\n\nThe app expects to find the [TOML](https://github.com/toml-lang/toml) configuration file in\n`./config.toml` or `$HOME/.smgmg/config.toml`.\n\nFor the configuration structure, check the [config.example.toml](./config.example.toml) file.\n\nSome values can be overridden by environment variables, that have the following names:\n\n```sh\nSMGMG_BK_API_KEY = \"\u003cAPI Key\u003e\"\nSMGMG_BK_API_SECRET = \"\u003cAPI Secret\u003e\"\nSMGMG_BK_USER_TOKEN = \"\u003cUser Token\u003e\"\nSMGMG_BK_USER_SECRET = \"\u003cUser Secret\u003e\"\nSMGMG_BK_DESTINATION = \"\u003cBackup destination folder\u003e\"\nSMGMG_BK_FILE_NAMES = \"\u003cFilename with template replacements\u003e\"\n```\n\n| Configuration identifier   | Required | Default Value   | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |\n| -------------------------- | -------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| authentication.\\*          | Yes      |                 | See [credentials](#credentials) below for details about how to obtain them.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |\n| store.destination          | Yes      |                 | Local path to save SmugMug pictures and videos into. If the folder is not empty, then only new or changed files will be downloaded. **Windows users** The value of `destination` must use slash `/` or double backslash `\\\\` Examples: `toml  destination = \"C:/folder/subfolder\"  destination = \"C:\\\\folder\\\\subfolder\"  destination = \"/folder/subfolder\" # This writes to the primary partition C: `                                                                                                                                                                                                                                                                                                                                        |\n| store.file_names           | No       | `{{.FileName}}` | Is a string including template replacements that will be used to build the file names for the files on disk. Accepted keys are `FileName`, `FileNameNoExt`, `Extension`, `ImageKey`, `ArchivedMD5`, `UploadKey`, `Date` and `Time`. Their values comes from the AlbumImage API response. If an invalid replacement is used, an error is returned. Date and Time formats are `2006-01-02` and `15_04_05` respectively (`Time` uses underscores instead of colon to be valid on every platform). `Extension` contains the dot (e.g. `.jpg`).                                                                                                                                                                                                     |\n| store.use_metadata_times   | No       | false           | When true, the last modification timestamp of the objects will be set based on SmugMug metadata for newly downloaded files. If also **force_metadata_times** is true, then the timestamp is applied to all existing files. This configuration can be required if you notice that the images creation datetime is wrong by ~7h. This is a bug in the SmugMug Uploader: \"Our uploader process currently isn't time zone aware and takes the DateTimeOriginal field without time zone information\". The solution is to use the Metadata API endpoint to retrieve the EXIF informations, but it requires an additional API call for each image/video. In my case, a full backup that requires ~10 minutes, increases to 2+ hours with this option. |\n| store.force_metadata_times | No       | false           |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |\n| store.write_csv            | No       | false           | When true, a `metadata.csv` file is created (or overwritten) storing some information about the user files (both downloaded or skipped).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |\n| store.force_video_download | No       | false           | When true, videos are downloaded also if marked as \"under processing\"                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |\n| store.concurrent_albums    | No       | 1               | Number of concurrently analyzed albums. It multiplies API calls, don't stress this number or you'll be rate limited. 5 is a good start.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |\n| store.concurrent_downloads | No       | 1               | Number of concurrently downloaded images and videos.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |\n\n## Run\n\nOnce the configuration file and/or the environment variables are set,\nyou can perform the account backup with:\n\n```sh\n./smugmug-backup\n```\n\nRunning the backup can take a lot of time, depending on the size of your account and the\nconnection speed. Check the command line logs to see what's going on.\n\n## Credentials\n\nSmugMug requires _OAuth1 authentication_. OAuth1 requires 4 values: an API key and secret that\nidentify the app with SmugMug and that you can get from SmugMug registering the app and\nuser credentials (token and secret) that each user must obtain (those credentials authorize\nthe app to access the user's data).\n\n### Obtain API keys\n\nApply for an API key from\n[https://api.smugmug.com/api/developer/apply](https://api.smugmug.com/api/developer/apply)\nand wait the app to be authorized.\n\nOnce you have obtained the API key and secret, save them in the `authentication` section\nof the configuration file:\n\n```toml\n[authentication]\napi_key = \"\u003cAPI Key\u003e\"\napi_secret = \"\u003cAPI Secret\u003e\"\n```\n\n### Obtain Tokens\n\nOnce your app has been accepted by SmugMug and you got the API key and secret, then go to your\n[Account Settings \u003e Privacy page](https://www.smugmug.com/app/account/settings/?#section=privacy)\nand scroll down to \"Authorized Services\", where you'll find the app and a link to see the tokens.\n\nAdd them to the `authentication` section of the configuration file:\n\n```toml\n[authentication]\nuser_token = \"\u003cUser Token\u003e\"\nuser_secret = \"\u003cUser Secret\u003e\"\n```\n\n## Build and install\n\nTo build and install the program from source:\n\n```sh\ngit clone https://github.com/tommyblue/smugmug-backup.git\ncd smugmug-backup\nmake build\n```\n\nThis will produce the `./smugmug-backup` binary.  \nMore `make` commands are available, run `make help` to get help\n\n## Debug for errors\n\nTo increase the logging, export a `DEBUG=1` environment variable:\n\n```sh\nDEBUG=1 ./smugmug-backup\n```\n\n## Visualize software stats\n\nIn case you want to monitor the software performances you can use the `-stats` flag to enable\n[statsviz](https://github.com/arl/statsviz) debug page (available at http://localhost:6060/debug/statsviz/)\n\n## Credits\n\nOAuth1 signature has been heavily inspired by\n[https://github.com/gomodule/oauth1](https://github.com/gomodule/oauth1)\n\nThe code in the `get_tokens` folder is a copy of\n[https://gist.github.com/smugkarl/10046914](https://gist.github.com/smugkarl/10046914)\n\n## Code of conduct\n\nBefore interacting with the community around this project, be sure to read the\n[code of conduct](./code_of_conduct.md).\n\n## Bugs and contributing\n\nSee [CONTRIBUTING.md](./CONTRIBUTING.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftommyblue%2Fsmugmug-backup","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftommyblue%2Fsmugmug-backup","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftommyblue%2Fsmugmug-backup/lists"}