A music app for iOS, Android and Web — inspired by Spotify, but no premium plans needed.
Listen to whatever you want, whenever you want.
Below you'll find every screen of the app, both on mobile and on the web, with a quick rundown of what each one does.
Sign in with your Google account so your data sticks around between sessions. No forms, no passwords to remember, no "verify your email" hoops — one tap and you're in. The next time you open the app, your favorites, history and queue are still there waiting for you, even if you switched devices.
Under the hood: Google OAuth handles the auth flow, and the session token is stored locally so the app can rehydrate your profile on startup. All user-specific state (favorites, history, queue, top tracks) is keyed by the account so you can sign out and back in without losing anything.
The idea of the home screen is that you open the app and instantly find something to play — no scrolling forever, no "what should I listen to today?" paralysis. Everything you see here is built around your own taste. You'll find:
- The last 8 songs you played, so you can pick up right where you left off
- The artists you've saved, each one with fresh song recommendations pulled from their channel
- Your most-played tracks, in case you want to keep the catchy stuff going
- All your favorite artists in one spot for quick access
Under the hood: Recently played and most-played counters are persisted in
AsyncStorage(@last_music_played,@most_played_songs) and updated by the playback hook every time a song finishes. The artist recommendation rows hitGET /api/youtube/search/channel/videoson the backend, which uses Innertube to pull the latest uploads from each saved channel.
Every song you mark as a favorite lands here, and this is the screen that turns DiegoMusic into a real on-the-go player. You can:
- Shuffle your favorites so the next song is a fun surprise
- Filter to find a specific track quickly when the list gets long
- Listen offline — super useful when you've got no signal, no data, or you're on a plane
- Check the download icon: if it's blue, the song is already saved on your device. If it's white, just hit the button to download it
Under the hood: Favorites live under the
@favorites_songskey inAsyncStorage. When you trigger a download, the backend streams MP3 audio throughGET /api/youtube/audio/download(yt-dlp + ffmpeg), and the file is written to the Expo cache directory. Thumbnails are cached locally too, so the offline experience stays visually intact. TheNetworkProviderwatches connectivity in the background and silently falls back to the local file when the network drops.
The player is the heart of the app. Expand it and you get a bunch of options:
- Play / pause
- Skip to the next or previous song
- Watch the song's video
- Share it
- Save it to favorites
- View the play queue
- Set a sleep timer
- Toggle shuffle mode
- Save the artist to your favorites
- Open the original video on YouTube
Under the hood: Playback is split across four hooks composed by
PlayerContext:useAudioPlayer(the engine, built onexpo-audio),usePlayerQueue(queue, shuffle, repeat),usePreloader(preloads the next 2 tracks into separateexpo-audioinstances to make skips feel instant), andusePlayerStorage(hydrates and persists everything toAsyncStorage). Skipping doesn't wait for the network — it swaps to the already-buffered next instance.
The options panel keeps every player action one tap away — share, favorite, save the artist, jump to YouTube — without crowding the main view. The queue lets you see what's coming next, reorder it, or kick the whole list out if you've changed your mind.
Under the hood: The queue lives in
usePlayerQueueand is mirrored toAsyncStorage(@player_*_queue) so the list survives app restarts. Shuffle keeps a separate "original order" array so toggling it off restores the queue exactly as it was instead of leaving you in a random state.
The sleep timer is great for putting on music before bed without having it play all night — pick a duration and the player fades out when the time is up. Video mode swaps the album art for the actual music video, so you can switch between "audio in your pocket" and "full music-video experience" without leaving the player.
Under the hood: The sleep timer is a simple
setTimeouttracked inside the player context so it survives navigation and gets cleared automatically if you start a new track or skip it manually. Video mode mounts anexpo-videoview backed byGET /api/youtube/video/stream, which proxies the YouTube video stream through the backend — that means no extra YouTube SDK on the client and the same cookie-protected pipeline used for audio.
You can also see the lyrics of the song right inside the player. And if the algorithm can't find them automatically, you can search for them manually — and once you do, they get saved for next time, so nobody else (or future you) has to do the work again.
Under the hood: Lyrics are fetched on-demand and cached locally per song id. Manual lookups use the same cache key, so a manual fix becomes the canonical version for that track on your device.
Discover new music or look up something specific:
- Browse by genre when you don't know what you want
- Search songs or artists directly when you do
Under the hood: Search calls
GET /api/youtube/search/videoon the backend, which uses Innertube as the primary source and falls back to@distube/ytdl-core/yt-searchif Innertube fails. The client keeps a 50-entry LRU cache of recent queries (services/api.ts) so repeat searches are instant and don't hammer the backend. Requests time out at 8 seconds so a slow YouTube response doesn't block the UI.
Check your profile and dig into your listening stats — see what you've been playing the most, which artists own your week, and how your taste has been moving over time. It's the closest thing to a personal "Wrapped" you get whenever you want, not just once a year.
Under the hood: Stats are computed client-side from the same play counters that feed the home screen (
@most_played_songs, listening history). No analytics service is involved — the data never leaves your device, which keeps things fast and private.
In settings you can switch the language between English, Spanish and Japanese, and also pick the video quality you want for the song videos. Heads up: the higher the quality, the slower the video loads, so if you're on mobile data you'll probably want to drop a notch.
Under the hood: Language and video-quality preferences are persisted in
AsyncStorageand applied at app start. The video quality preference is forwarded to the backend on eachGET /api/youtube/video/streamcall so yt-dlp picks the matching format on the server side instead of downloading the highest one and downscaling.
The player works straight from the lock screen too, so you can:
- Pause or play without unlocking your phone
- Skip songs while your phone is in your pocket
- See what's currently playing with the cover art front and center
Under the hood: Lock-screen controls are wired through
expo-media-controlfromcontext/player/mediaControls.ts, and iOS background audio is enabled viaUIBackgroundModes: ["audio"]inapp.json. That same setup feeds the now-playing metadata to AirPods controls, Android Auto and CarPlay-style surfaces.
- Expo (SDK 54, New Architecture)
- React Native + TypeScript
- Node.js + Express
- Docker
The app pulls music and videos from YouTube using a scraping system. From there it grabs:
- Song info
- Music videos
Heads up: to avoid getting blocked by YouTube, you need to set up the cookies properly so it looks like a real browser session.
Want to see it in action?













