A lightweight Flask-based web server intended for a Raspberry Pi with a touch display. It exposes HTTP endpoints for controlling the display power state, launching a Google Photos Picker session, and a minimal in-memory publish/subscribe message queue.
- Display control:
GET /display?cmd=on|off
usesxset
to toggle the HDMI display. - Google Photos Picker integration:
POST /selectPhotos
creates a Picker session, returning apickerUri
the user can open in the Google Photos app or web UI. The backend polls the session for up to 15 minutes, following the Picker API guidance, and caches the selected media.GET /selectPhotos?sessionId=<id>
returns the cached status, including any picked media items once available.- When a selection completes the server downloads every asset into
screensaver/photos/
and rotates a random image on-screen every two minutes usingfeh
, mirroring the original screensaver behavior.
- Publish/Subscribe:
POST /publish
accepts a JSON payload and enqueues it.GET /subscribe
returns and removes the oldest queued message.
- Serves
static/index.html
when visiting/
.
- Python 3
- Flask
- requests
- google-auth
- google-auth-oauthlib
Install the Python dependencies with:
pip install flask requests google-auth google-auth-oauthlib
- Create an OAuth 2.0 Client ID (type Desktop) in Google Cloud Console.
- Download the client configuration JSON and save it as
screensaver/credentials.json
. - Ensure the
screensaver/
directory is writable so the app can persistpicker_token.json
with refreshed tokens. - On first run you will be prompted to complete the OAuth flow. The application requests
the
https://www.googleapis.com/auth/photospicker.mediaitems.readonly
scope.
python server.py
The server listens on port 8080 on all interfaces.
The following curl
commands demonstrate an end-to-end session using the Picker API.
-
Create a session (optionally constrain the selection with
maxItemCount
):curl -sS -X POST http://<host>:8080/selectPhotos \ -H 'Content-Type: application/json' \ -d '{"maxItemCount": 25}'
A successful response resembles:
{ "sessionId": "41b6f2fd-bf22-4242-94e9-1b6f640a2501", "pickerUri": "https://photos.google.com/picker/...", "status": "PENDING", "statusEndpoint": "http://<host>:8080/selectPhotos?sessionId=41b6f2fd-bf22-4242-94e9-1b6f640a2501" }
-
Share the
pickerUri
with the user so they can finish the media selection in the Google Photos app or web UI. -
Poll for completion (repeat until
state
becomesCOMPLETE
orERROR
):curl -sS "http://<host>:8080/selectPhotos?sessionId=41b6f2fd-bf22-4242-94e9-1b6f640a2501"
While pending, the response will include metadata and the recommended polling cadence:
{ "sessionId": "41b6f2fd-bf22-4242-94e9-1b6f640a2501", "state": "PENDING", "pollIntervalSeconds": 5.0, "pollingDeadline": "2024-02-21T17:05:42.123456+00:00" }
-
Retrieve the selected media once the state becomes
COMPLETE
:curl -sS "http://<host>:8080/selectPhotos?sessionId=41b6f2fd-bf22-4242-94e9-1b6f640a2501" | jq '.mediaItems'
The array includes every chosen item with its metadata and download URLs returned by Google Photos Picker. The response also lists the files saved within
screensaver/photos/
, which feed the two-minute slideshow cycle. -
(Optional) Abort a session if needed by calling:
curl -sS -X DELETE "https://photospicker.googleapis.com/v1/sessions/41b6f2fd-bf22-4242-94e9-1b6f640a2501"
The
PhotosPickerService
automatically stops polling when the session completes or times out, but explicit cleanup can be useful while testing.
This project is provided as-is without warranty.