Files
ytplayer/frontend/manifest.webmanifest
Jonathan Sykes 91627dc0a9 Share target: shared YouTube links play, shared audio/video uploads to the user's library; page shortcuts
- manifest share_target (POST multipart: title/text/url + audio/video files)
  and eight shortcuts (Search, Queue, Saved, Downloads, History, Library,
  Notes, Settings). /?view=<page> now actually opens that page — the old
  shortcuts pointed at it but nothing read it.
- sw.js receives the share: files are parked in the ytp-share-inbox cache (not
  a versioned shell cache, so deploys never evict it) and the app is opened;
  links/text go to /?shared=. A server POST /share-target fallback covers the
  first visit before a worker is in control (links only).
- The app plays a YouTube link found anywhere in the shared text (watch,
  youtu.be, shorts, live, embed, music; keeps t=), otherwise searches the text.
  /?shared=<link> also works from an iOS Shortcut (iOS has no share target).
- Shared files upload with progress to PUT /api/uploads/shared and join a
  "Shared uploads" playlist; network failures stay in the inbox for the next
  open, refusals are dropped with the reason.
- The public route is bounded: audio/video only (ffprobe-validated, error text
  without server paths), 500 MB per file, 2 GB and 20 a day per device, 50 GB
  for all shared uploads, one at a time per device (all env-tunable). Shared
  uploads are unlisted: reachable by id, never in anyone else's search.
  uploads gains owner + listed columns (idempotent ALTER).
2026-10-03 01:33:51 +08:00

167 lines
3.5 KiB
JSON

{
"name": "YT Player",
"short_name": "YTPlayer",
"description": "Ad-free YouTube player — search, play, and keep playlists offline. No login, no tracking.",
"display": "standalone",
"orientation": "any",
"start_url": "/",
"scope": "/",
"theme_color": "#1a1311",
"background_color": "#1a1311",
"categories": [
"music",
"entertainment"
],
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "/icons/icon-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
}
],
"screenshots": [],
"shortcuts": [
{
"name": "Search",
"short_name": "Search",
"url": "/?view=search",
"description": "Search YouTube",
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png"
}
]
},
{
"name": "Queue",
"short_name": "Queue",
"url": "/?view=queue",
"description": "What plays next",
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png"
}
]
},
{
"name": "Saved",
"short_name": "Saved",
"url": "/?view=saved",
"description": "Videos saved for offline",
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png"
}
]
},
{
"name": "Downloads",
"short_name": "Downloads",
"url": "/?view=downloads",
"description": "Saves in progress and paused",
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png"
}
]
},
{
"name": "History",
"short_name": "History",
"url": "/?view=history",
"description": "Recently watched videos",
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png"
}
]
},
{
"name": "Library",
"short_name": "Library",
"url": "/?view=library",
"description": "Your playlists",
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png"
}
]
},
{
"name": "Lyrics & notes",
"short_name": "Notes",
"url": "/?view=notes",
"description": "Lyrics, chapters and notes",
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png"
}
]
},
{
"name": "Settings",
"short_name": "Settings",
"url": "/?view=settings",
"description": "App settings",
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png"
}
]
}
],
"share_target": {
"action": "/share-target",
"method": "POST",
"enctype": "multipart/form-data",
"params": {
"title": "title",
"text": "text",
"url": "url",
"files": [
{
"name": "media",
"accept": [
"audio/*",
"video/*",
".mp3",
".m4a",
".aac",
".wav",
".flac",
".ogg",
".opus",
".mp4",
".mov",
".webm",
".mkv"
]
}
]
}
}
}