Infuse Artwork WebDAV Server
infuse-artwork.andrewe.dev
This public WebDAV endpoint serves custom artwork for Infuse, allowing you to personalize the artwork for categories and favorites. Infuse is a popular media player for Apple TV, iOS, and macOS that supports fetching custom artwork via WebDAV.
Using custom artwork with Infuse:
- Settings → Shares → Add Share → WebDAV
- Server:
https://infuse-artwork.andrewe.dev - Advanced: Auto Scan: Off
- Save
- Long-press and choose Select Artwork.
- Select a WebDAV share.
- Select an image file.
Note
Infuse only discovers artwork at the WebDAV root. This project keeps a hierarchical folder structure in the repository and Cloudflare R2, but presents a flattened root view where nested paths are exposed as folder.filename-details.ext
- built on r2-webdav a WebDAV server implementation for Cloudflare Workers and R2
- Wrangler CLI installed
- rclone installed:
brew install rclone - Cloudflare account with Workers and R2 enabled
- Node.js 18+ and npm
git clone https://github.com/andesco/infuse-artwork-webdav-worker.git
cd infuse-artwork-webdav-worker
npm install
wrangler r2 bucket create infuse-artworkUpdate wrangler.toml:
name = "infuse-artwork-webdav"
main = "src/index.ts"
compatibility_date = "2025-12-22"
compatibility_flags = ["nodejs_compat"]
workers_dev = true
# Custom domain (auto-creates DNS)
routes = [
{ pattern = "infuse-artwork.andrewe.dev", custom_domain = true }
]
[[r2_buckets]]
binding = "bucket"
bucket_name = "infuse-artwork"
[observability]
enabled = true
head_sampling_rate = 1wrangler deployUse rclone for true synchronization (handles adds, updates, deletes, and renames):
-
Get your Cloudflare Account ID:
wrangler whoami
-
Create R2 API credentials:
- Cloudflare Dashboard → R2 → Overview → Manage R2 API Tokens → Create API Token
- Permissions: Admin Read & Write
- Copy: Access Key ID and Secret Access Key
-
Configure
rclone:rclone config
Follow the prompts:
nfor new remote- Name:
r2 - Storage: Amazon S3 compatible
- Provider:
Cloudflare - Access Key ID: [paste from step 2]
- Secret Access Key: [paste from step 2]
- Region:
auto - Endpoint:
https://{ACCOUNT_ID}.r2.cloudflarestorage.com - ACL:
private - accept defaults for remaining options
rclone sync infuse-artwork/ r2:infuse-artwork -vNote
rclone sync makes the R2 bucket identical to your local folder and will delete remote files that do not exist locally. Use the --dry-run flag to see what will change when syncing:
rclone sync infuse-artwork/ r2:infuse-artwork --dry-run -v
Important
Always use the --remote flag to upload to the production R2 bucket (not to local development storage).
upload a single image:
wrangler r2 object put infuse-artwork/filename.png \
--file=./filename.png \
--content-type=image/png \
--remoteupload multiple images:
for file in *.png; do
echo "Uploading $file..."
wrangler r2 object put "infuse-artwork/$file" \
--file="$file" \
--content-type=image/png \
--remote
donedelete images:
wrangler r2 object delete infuse-artwork/filename.png --remotenpm run devtest HTML directory listing:
curl https://infuse-artwork-webdav.andrewe.workers.dev/test specific image:
curl -I https://infuse-artwork-webdav.andrewe.workers.dev/cast.pngtest WebDAV PROPFIND:
curl -X PROPFIND https://infuse-artwork-webdav.andrewe.workers.dev/ \
-H "Depth: 1" \
-H "Content-Type: text/xml" \
--data '<?xml version="1.0"?><propfind xmlns="DAV:"><prop><resourcetype/><getcontentlength/><getlastmodified/></prop></propfind>'verify write protection (401):
curl -X PUT https://infuse-artwork-webdav.andrewe.workers.dev/test.txt \
-H "Content-Type: text/plain" \
--data "test"publicly accessible without authentication: GET HEAD PROPFIND OPTIONS
require authentication: PUT DELETE MKCOL COPY
MOVE PROPPATCH
Note
Since no credentials are configured, all write operations will return 401 Unauthorized .
- protocol: WebDAV Class 1, 3
- hosting: Cloudflare Workers
- storage: Cloudflare R2
- framework: r2-webdav
This deployment includes a modification to r2-webdav to enable public read-only access:
File: src/index.ts
// Allow public read-only access (GET, HEAD, PROPFIND)
// Require authentication for write operations
const readOnlyMethods = ['OPTIONS', 'GET', 'HEAD', 'PROPFIND'];
const requiresAuth = !readOnlyMethods.includes(request.method);
if (
requiresAuth &&
!is_authorized(request.headers.get('Authorization') ?? '', env.USERNAME, env.PASSWORD)
) {
return new Response('Unauthorized', {
status: 401,
headers: {
'WWW-Authenticate': 'Basic realm="webdav"',
},
});
}The project is based on abersheeran/r2-webdav.