gphotosmobile: add Google Photos mobile API backend#9135
Open
xob0t wants to merge 31 commits intorclone:masterfrom
Open
gphotosmobile: add Google Photos mobile API backend#9135xob0t wants to merge 31 commits intorclone:masterfrom
xob0t wants to merge 31 commits intorclone:masterfrom
Conversation
… and quota support Implement a full rclone backend using the reverse-engineered Google Photos mobile API (same as Android app), bypassing official REST API restrictions. Features: library sync with SQLite cache, original quality download/upload, SHA1 hash verification, shared download cache with seeking for FUSE mount, trash support, and About/quota reporting for mounted drive space display.
…l/device_make config options
Replace gms_auth references with step-by-step ReVanced method that doesn't require root. Update help text, comparison table, and troubleshooting to match.
…ding - Rename backend/gphotos_mobile -> backend/gphotosmobile (Go package naming) - Use fshttp.NewClient(ctx) instead of custom http.Client, remove proxy option - Fix all 38 golangci-lint issues (errcheck, var-naming, unused, unconvert, staticcheck) - Add autogenerated options markers to docs - Add backend YAML, test file, doc entries, and test_all config
…add to README - Use config.GetCacheDir()/gphotosmobile/<remote>.db instead of ~/.gpmc/<email>/ - Add lib/encoder support for path encoding (Base, EncodeCrLf, EncodeInvalidUtf8) - Rename doc/yaml files from gphotos_mobile to gphotosmobile to match package dir - Add backend entry to README.md storage providers list - Update all doc references to use consistent gphotosmobile naming
Remove GetFixed32, GetRepeatedStrings, AddSignedVarint, AddFixed64, AddRepeatedVarint from protowire_utils.go and GetByMediaKey, Count from cache.go. None of these were called anywhere in the codebase.
…-ops Mkdir now returns nil only for existing virtual directories (root, media) and fs.ErrorDirNotFound for anything else. Rmdir returns an error for virtual directories (can't remove them) and fs.ErrorDirNotFound for unknown paths.
When multiple items share the same filename, append media_key suffix to ALL of them (not just the 2nd+). This ensures filenames are stable regardless of database ordering between syncs.
…e filenames Media keys share a long common prefix (AF1QipN...) making short prefixes useless for disambiguation. Dedup keys are short (~27 chars) and unique per item.
…to memory Put() now streams the input through a SHA1 hasher into a temp file, then uploads from the temp file. This avoids loading the entire file into memory, making multi-GB video uploads feasible. UploadFile() now accepts io.Reader + size instead of []byte.
…own to YAML features
…, populate Put metadata from cache, use sentinel errors
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What is the purpose of this change?
Add new
gphotosmobilebackend that interfaces with Google Photos using the reverse-engineered mobile API (the same API the Android Google Photos app uses). This bypasses all restrictions of the official REST API — download any photo/video at original quality, upload, trash items, full metadata with SHA1 hashes, and read/write descriptions (captions).Was the change discussed in an issue or in the forum before?
No
Checklist
Summary
gphotosmobilebackend that interfaces with Google Photos using the reverse-engineered mobile API (the same API the Android Google Photos app uses)set-descriptionfor standalone caption editingmake backenddocs), backend YAML, integration test scaffoldingWhat it does
Architecture
android.googleapis.com/authdownload_cache = true)fs.Pacerwithpacer.NewGoogleDrivefor all API calls; standardshouldRetry(ctx, err)pattern withretryErrorCodes(429, 500, 502, 503, 509)api/types.gosubpackage withMediaItemandErrortypes (standard rclone convention used by 25+ backends)lib/rest: Justified — all API calls use raw protobuf over HTTPS, not JSON/XML RESTMetadata support
The backend implements
fs.Metadataerandfs.SetMetadataer:descriptionmedia-typephotoorvideowidthheightdurationlatitudelongitudelocation-namecamera-makecamera-modelapertureshutter-speedisofocal-lengthtaken-atuploaded-atis-favoriteis-archivedoriginself,partner, orsharedBackend commands
The backend implements
fs.Commanderwith:set-description: Set or clear the caption of a media itemConfiguration options
Standard options
auth_dataRCLONE_GPHOTOSMOBILE_AUTH_DATAandroidId=. Obtained via Google Photos ReVanced + ADB logcat.Advanced options
cache_db_pathRCLONE_GPHOTOSMOBILE_CACHE_DB_PATH<cache-dir>/gphotosmobile/<remote>.dbdevice_modelRCLONE_GPHOTOSMOBILE_DEVICE_MODELPixel 9adevice_makeRCLONE_GPHOTOSMOBILE_DEVICE_MAKEGoogleapk_versionRCLONE_GPHOTOSMOBILE_APK_VERSION49029607android_apiRCLONE_GPHOTOSMOBILE_ANDROID_API35download_cacheRCLONE_GPHOTOSMOBILE_DOWNLOAD_CACHEfalserclone mountif you want backend-level seeking in addition to VFS caching.encodingRCLONE_GPHOTOSMOBILE_ENCODINGSlash,CrLf,InvalidUtf8,DotFiles added
gphotosmobile.gofs.Fs,fs.Object,fs.Shutdowner,fs.Commander,fs.Metadataer,fs.SetMetadataer, registration, cache sync orchestrationapi.goMobileAPI: auth, all API methods (including SetCaption),fs.Pacerwithpacer.NewGoogleDrive, standardshouldRetry/retryErrorCodesapi/types.goapi.MediaItemstruct (38+ fields withdb:tags) andapi.Errortype (standard rcloneapi/subpackage convention)request_builders.goprotowire_utils.goparser.gocache.godownload_cache.gogphotosmobile_test.gofstests.Run()gphotosmobile_internal_test.goProtocol documentation (in-code)
Every source file has a detailed package-level or file-level doc comment explaining the inner workings for maintainers:
api.go-- Full protocol overview: authentication flow (device tokens to bearer tokens), list of all API endpoints with their numeric path IDs (including SetCaption), transport details (raw protobuf over HTTPS), justification for not usinglib/restgphotosmobile.go-- Library sync protocol: initial sync vs incremental sync state machine, state_token/page_token semantics, sync throttling, virtual filesystem layout, duplicate filename handling, metadata field mappingparser.go-- Complete protobuf field map for sync responses: every field path for media items (metadata, type info, location, EXIF), deletion entries, and pagination tokensrequest_builders.go-- Request structure: field mask pattern (how the client tells the server which fields to return), top-level request layout shared across the three sync request types, upload commit, trash, and caption request formatsdownload_cache.go-- Download cache architecture: why it exists (no HTTP Range support), how shared downloads work, reference counting, known limitationsprotowire_utils.go-- Why raw wire format instead of compiled protos, encoding/decoding approach, relationship to Python's blackboxprotobufcache.go-- SQLite schema, WAL mode, cache location, crash recovery via persisted page_tokenapi/types.go-- Key identifier types (media_key vs dedup_key vs SHA1Hash) and their usesContributing guide compliance
fshttp.NewClient(ctx)for HTTP transport (respects--proxy,--timeout,--dump,--tpslimit)context.Contextpropagated to all HTTP requests (supports cancellation)fs.Shutdownerimplemented -- closes SQLite cache and cleans up temp filesfs.Commanderimplemented --set-descriptionbackend command for standalone caption editingfs.Pacerwithpacer.NewGoogleDrivefor all API retry logic (standard rclone pattern)shouldRetry(ctx, err) (bool, error)withretryErrorCodes-- matches standard naming used across all backendsapi/types.gosubpackage withMediaItemandErrortypes (matches 25+ other backends)GetUploadToken,DownloadFile,getAuthToken) wrapped inpacer.Call()withshouldRetry*api.Errorfor consistent retry logicNewObjectproperly distinguishes "not found" (sql.ErrNoRows) from real database errors, handles dedup-suffixed filenamesSetModTimereturnsfs.ErrorCantSetModTime(API does not support modtime changes)Precisionreturnsfs.ModTimeNotSupportedPutpopulates full metadata from cache after uploadlib/encodersupport for path encodinggolangci-lintv2 passes with 0 issuesdocs/data/backends/gphotosmobile.yaml) with correct features and precisionbin/make_backend_docs.py)README.md,docs.md,_index.md,navbar.html,make_manual.py-- all in correct alphabetical order by full namefstest/test_all/config.yamlentry--cache-dir(not custom location)Helptexts follow single-sentence-with-period formatgphotosmobile(no underscores), matches directory name andFileName()outputdownload_cacheconfig option, defaultfalse)apk_version,android_api)googlephotospattern)fs.Metadataer+fs.SetMetadataerimplemented -- rich read metadata, writable descriptionMetadataInforegistered withsystemMetadataInfomap listing all 19 metadata keysReadMetadata+WriteMetadatafeature flags setlib/restnot used -- justified (protobuf binary protocol, not JSON REST), documented onMobileAPIstructTesting done
rclone about mygphotos:-- quota display worksrclone ls mygphotos:media/-- lists all ~35K itemsrclone copy mygphotos:media/video.mp4 /local/-- downloads at original qualityrclone copy /local/photo.jpg mygphotos:media/-- uploads with streaming SHA1 + deduprclone delete mygphotos:media/file.mp4-- moves to trashrclone mount mygphotos: G: --vfs-cache-mode full-- mount works, file browsing and sequential reads work. Seeking requires--gphotosmobile-download-cache=truerclone lsjson mygphotos:media/photo.jpg --metadata-- returns all metadata fieldsrclone backend set-description mygphotos: media/photo.jpg -o description="test"-- sets captiongo test -v -run "Test[^I]" ./backend/gphotosmobile/...-- 26 unit tests passgolangci-lintv2 run./backend/gphotosmobile/...-- 0 issuesgo vet ./backend/gphotosmobile/...-- passes-tags cmount-- passesbin/make_backend_docs.py gphotosmobile-- autogenerated options populated successfully