A web application for syncing YouTube content to Yoto cards. Download audio from YouTube videos or playlists and upload them directly to your Yoto cards. Deployed on Cloudflare Workers with Containers for YouTube downloading.
Features
- Browse and manage your Yoto cards
- Import audio from YouTube videos and playlists
- Drag-and-drop track reordering
- Track deletion and card management
- Search and sort cards
Prerequisites
- Node.js 20+
- Docker Desktop (for local development)
Installation
git clone https://github.com/bradgarropy/yoto.git
cd yoto
npm installUsage
Start the development server:
npm run dev
Open http://localhost:3000 in your browser.
First Time Setup
- Click "Login" to authenticate with your Yoto account
- Confirm that the code shown in Yoto Sync matches the device confirmation screen on Yoto's website
- Click "Confirm" on Yoto's website
- Once logged in, you'll see all your Yoto cards
Syncing YouTube Content
- Navigate to a card's detail page
- Click "Add Tracks"
- Paste a YouTube video or playlist URL
- Click "Import" and wait for the sync to complete
Development
npm run dev # Start development server npm run build # Build for production npm run deploy # Build and deploy to Cloudflare npm run test # Run tests npm run lint # Check for lint errors npm run typecheck # Check TypeScript types npm run format # Check code formatting
Observability
The application uses structured diagnostic logs and typed product telemetry. See Observability for event conventions, log levels, privacy rules, testing guidance, and instructions for viewing logs.
Local Development with Containers
This app uses Cloudflare Containers for YouTube downloading via yt-dlp. For local development:
- Docker must be installed and running - The Vite dev server uses Docker to run containers locally
- First run builds the container - Takes 2-3 minutes; subsequent runs use cached layers
- Rebuild containers - Press
r+ Enter in the terminal to rebuild after Dockerfile changes
The local development environment runs your Worker code in workerd (the same runtime as production) and containers in Docker, providing near-production behavior.
Project Structure
yoto/
├── app/
│ ├── routes/ # React Router pages
│ │ ├── home.tsx # Card grid dashboard
│ │ ├── login.tsx # Device code auth flow
│ │ └── cards.$id.tsx # Card detail with tracks
│ ├── components/ui/ # shadcn/ui components
│ └── lib/ # Server-side utilities
│ ├── auth.server.ts
│ ├── youtube.server.ts
│ └── sync.server.ts
├── public/ # Static assets
└── images/ # Image assets
Tech Stack
- Cloudflare Workers - Edge runtime
- Cloudflare Containers - Serverless containers for yt-dlp
- React Router v7 - Full-stack React framework
- Tailwind CSS - Styling
- shadcn/ui - UI components
- yt-dlp - YouTube downloading (runs in container)
- @yotoplay/yoto-sdk - Yoto API client