TubeCast turns YouTube channels into a personal, audio-first listening library. Follow channels, browse new uploads as episodes, save audio for offline listening, and organize it into playlists.
TubeCast is an independent Expo / React Native client for iOS and Android. It is not affiliated with YouTube.
| Feed | Home | Convert |
|---|---|---|
![]() |
![]() |
![]() |
| Player | Playlist | Settings |
![]() |
![]() |
![]() |
- Convert YouTube videos into audio from a pasted URL, with clear queued, downloading, transcoding, saving, playable, failed, and expired states.
- Browse recent and popular converted videos on the Home screen, then play or cache them without leaving the app.
- Follow YouTube channels by URL or handle, manage subscriptions, and browse new uploads in a podcast-style feed.
- Open publisher previews from subscribed feeds or the player, see recent videos, subscribe or unsubscribe, and convert playable episodes.
- Cache completed audio locally for offline playback, retry failed cache jobs, and inspect local storage usage.
- Play audio with a full-screen player, draggable progress bar, previous/next controls, source links, publisher metadata, and cache status.
- Keep listening in the background with iOS lock-screen metadata and a persistent mini player above the tab bar.
- Maintain a local playlist/library with playback progress, listened state, reorder support, swipe-to-delete, bulk edit/delete, and an unplayed-only filter.
- Use light, dark, or system appearance, and switch between English, Simplified Chinese, or system language.
TubeCast is available via TestFlight for iOS:
Android is not distributed publicly at this time. See Local builds to build from source.
- Node.js 20 or later
- pnpm 10
- Expo Go for quick development runs, or Android Studio / Xcode for native builds
pnpm install
pnpm startOpen the Expo development server in Expo Go, or start a native development build:
pnpm android
pnpm iosUse screenshot demo mode when preparing App Store screenshots. It keeps the real UI but replaces network/storage data with fixed, English demo content and local illustration covers:
pnpm start:screenshots
pnpm ios:screenshots
pnpm ios:screenshots:release
pnpm ios:screenshots:ipadThe mode is enabled only when EXPO_PUBLIC_SCREENSHOT_DEMO_MODE=1 is present.
Normal development, TestFlight, and App Store builds keep using real user data.
Use ios:screenshots:release for 6.5-inch App Store screenshots. It targets
the iPhone 13 Pro Max simulator and avoids development-only overlays such as
the floating Tools button.
Use ios:screenshots:ipad for the 13-inch iPad App Store screenshots. It
targets the iPad Pro 13-inch (M5) simulator.
TubeCast builds locally; it does not require EAS.
Install Xcode, connect an unlocked iPhone, and select a signing team in the generated Xcode project. Then build and install a Release build locally:
pnpm release:iosThis runs expo run:ios --device --configuration Release. To create an archive for TestFlight or App Store Connect, use Xcode's Product → Archive and Organizer.
npx expo prebuild --platform android
cd android
./gradlew assembleReleaseThe release APK is written to android/app/build/outputs/apk/release/.
If you distribute a fork, replace expo.ios.bundleIdentifier and expo.android.package in app.json with identifiers you own. Do not publish a fork under TubeCast's identifiers.
Releasing to TestFlight is CI-driven. The only manual step is cutting the version locally; everything from building the IPA to distributing it to testers happens in GitHub Actions.
pnpm release:version— bumps the marketing version +ios.buildNumber, updatesCHANGELOG.md, tagsvX.Y.Z, pushes the tag, and opens a draft GitHub Release.- Pushing the
vX.Y.Ztag triggers.github/workflows/release-testflight.yml, which: builds the IPA with EAS Build (cloud macOS, EAS-managed signing), uploads it to TestFlight viaeas submit(fastlane'supload_to_testflightshells out to Apple's Transporter tool, which has known unresolved bugs on Linux —eas submituploads through EAS's own infra instead and works fine on the Ubuntu runner), generates bilingual (EN/中文) "What to Test" notes with an LLM call, distributes the build via fastlane to thePublic Beta Testersgroup (this part is a pure App Store Connect API call, no binary upload, so it's unaffected), and flips the draft Release to published — no manual clicks. - The private root repo's
mobilesubmodule pointer is intentionally not bumped by CI (the two repos stay independent; no cross-repo write token is granted). Runpnpm release:publishlocally whenever you want to sync it, same as before.
For a same-version hotfix rebuild (buildNumber bump only, no new marketing version/tag):
pnpm release:testflight-bump # buildNumber+1, commit + push, then tags + pushes testflight/<version>-<build>The pushed tag triggers the workflow automatically — no manual step needed. It runs the same build/upload/distribute steps, tags a prerelease GitHub changelog (the tag itself already exists by the time CI runs, since it's tag-first — CI just fills in the release notes), and skips promoting a Release (that only applies to an actual version-tag release). workflow_dispatch (GitHub UI or gh workflow run release-testflight.yml) still exists as a manual fallback for re-running without pushing a new tag.
Versioning follows conventional commits via commit-and-tag-version (feat: → minor, fix: → patch, BREAKING CHANGE → major). The first release bootstraps a baseline v1.0.0 tag from existing history. See plans/007-mobile-release-flow.md for the original local-only design and plans/009-mobile-eas-ci-release.md for the CI/EAS automation that superseded its build/upload/distribution steps.
Before the workflow can run, someone with the right account access needs to do this once:
eas login/eas init(writesexpo.extra.eas.projectIdintoapp.json) andeas credentialsfor iOS → Build Credentials, so EAS manages the Apple signing certificate/provisioning profile itself — nofastlane matchneeded. Done — seeexpo.extra.eas.build.experimental.ios.appExtensionsinapp.json, which is required foreas credentialsto also provision theTubeCastShareExtensiontarget (EAS doesn't discover hand-added extension targets on its own).eas credentialsfor iOS → App Store Connect: Manage your API Key, soeas submitcan upload builds non-interactively. This is a separate credential from the build one above; stored server-side by EAS, not a GitHub secret. Done.- Mint an Expo robot/service-account token and store it as the
EXPO_TOKENGitHub Actions secret. - Store the existing App Store Connect API key as
APP_STORE_CONNECT_API_KEY_KEY_ID,APP_STORE_CONNECT_API_KEY_ISSUER_ID, and the.p8file's contents asAPP_STORE_CONNECT_API_KEY_P8— used only by the fastlane distribute step now (see below), not by the upload step. ANTHROPIC_API_KEYis reused from the existing OpenWiki workflow secret.ascAppIdineas.json'ssubmit.production.ios— the app's numeric App Store Connect ID, required byeas submit. Done.- The full chain has been validated by hand end-to-end (not yet by CI running start to finish in one go):
eas build→eas submit --id <buildId>→ fastlanetestflight_distribute, resulting in a real build live on TestFlight. Getting the build working required: adding a standalonemobile/pnpm-lock.yaml(this repo never had its own — the lockfile only ever existed in the private monorepo's workspace), pinningreact-native-reanimatedto the exact version already in use (an unpinned range resolved to a newer, incompatiblereact-native-workletsrequirement without lockfile history), adding@expo/config-pluginsas an explicit devDependency (withShareExtension.cjsneeds it directly, previously only available via workspace hoisting), and fixingwithShareExtension.cjsto hardcode the Apple Team ID on the extension target instead of reading it back from the main target (EAS's ownexpo prebuildnever runsrelease.mjs'ssyncNativeIosVersion(), so neither target had it yet at plugin-run time). Getting the upload working required switching from fastlane'supload_to_testflight(broken on Linux, see above) toeas submit.
The local fastlane testflight_build/release:archive (Xcode Archive) path still works as a manual fallback if EAS is ever unavailable.
App Store Connect metadata, screenshots, and TestFlight helper lanes are managed with fastlane. The local Xcode Archive / Transporter flow remains available as a fallback.
First-time setup:
cd mobile
mise install
mise exec -- bundle installDownload the current App Store Connect metadata before making broad edits:
pnpm store:download-metadata
pnpm store:download-screenshotsEdit files under fastlane/metadata/<locale>/, then upload metadata only:
pnpm store:metadataUpload screenshots only:
pnpm store:screenshotsUpload both metadata and screenshots:
pnpm store:assetsThe configured locales are Simplified Chinese (zh-Hans), English (en-US),
and Traditional Chinese (zh-Hant). Add more locales only when you intend to
maintain their keywords, description, and screenshots.
pnpm testThe app is deliberately separated from the backend implementation. It communicates only through the HTTP API for job submission and status, channel feeds, library records, and downloadable audio. Keep changes compatible with the existing API contract, or make the server endpoint configurable when adding new capabilities.
TubeCast is licensed under the GNU Affero General Public License v3.0 or later. See LICENSE.





