Tsacdop is a Flutter podcast player with two search backends and F-Droid builds
Enjoy podcasts with Tsacdop! A podcast player built with flutter.
At a glance
- What is it?
- Tsacdop is an open source podcast player written in Flutter, with podcast group management, playlists, OPML import and export, offline downloads, a sleep timer, skip silence and boost volume, and distribution through F-Droid plus signed APKs on GitHub releases since Google Play support was dropped.
- Who is it for?
- Tsacdop fits a listener who wants an open source podcast player on Android without a store account, and who wants control over subscriptions and downloads rather than a fixed catalogue. It is the wrong tool if you are on iOS and want an App Store build, since the iOS target exists in the source but the distribution story described in the README is Android only, and it is the wrong tool if you rely on gpodder.net account synchronisation, which is no longer supported.
- Can I use it commercially?
- Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository last received commits 7 days ago.
- What is it written in?
- Mainly Dart, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Two search engines, and one of them needs a key
Tsacdop is a podcast player developed with Flutter, described as a clean, simply beautiful and friendly app, and it is free and open source. The search side is where the first real design decision sits: the podcast search engine is powered by ListenNotes and by PodcastIndex, two different services.
ListenNotes needs a credential, and the build instructions explain it. You can optionally supply a Listen Notes API key at build time, passed as a Dart define, and the instruction is not to commit the key to the repository. Without it, the app uses Podcast Index search only. The key itself comes from the Listen Notes API page.
That makes the search story configurable by deployment rather than by code, and it has a practical effect on what a user can find. Two backends means two different indices, and a podcast that one of them does not have may still be in the other. A user running a build with only the Podcast Index backend is therefore working with a smaller catalogue than one running with both.
The build define is the mechanism, and it appears three times in the build commands, once for the run command and once for each of the two release builds.
F-Droid and GitHub APKs, and why Google Play went away
The distribution section is the part that changed recently, and it is worth reading in full because it affects how you install and update the app.
There are two routes. You can install Tsacdop from F-Droid and receive updates through the F-Droid client, or you can download an APK from the latest GitHub release. For the APK you choose the architecture: arm64-v8a for most modern Android phones, armeabi-v7a for older 32-bit ARM devices, or x86_64 for compatible Intel and AMD devices and for emulators.
The two routes update differently, and this is the part that trips people up. F-Droid updates through its client. GitHub APK installations need to be updated manually. There is also a signing consequence, because F-Droid signs its own builds, so switching between F-Droid and another distribution may require uninstalling the existing app first, and the app warns to export subscriptions and back up settings before uninstalling, since uninstalling removes local app data.
Google Play distribution is no longer supported, which is stated plainly. The site is tsacdop.app, and the project is also on F-Droid, with a Localizely badge for the localisation tooling.
The feature list is a subscription manager as much as a player
The features list is long enough to show what the app is for, and it clusters into three groups.
The subscription and organisation group is the largest: podcast group management, playlists support, OPML file export and import, listening and subscription history records, and auto-download of new episodes with auto-delete of outdated downloads. Auto-syncing in the background and settings backup are in there too, and there is a sleep timer with a speed setting, skip silence and boost volume, dark mode with an accent colour, and downloads for offline play with sorting of downloaded episodes by download time, episode title or podcast name. There is also a detail that tells you the app is used on a daily basis, which is the ability to continue the last playback session from the home screen after reopening the app.
The second group is the subscription transfer story, and it is a change rather than a feature. gpodder.net account synchronisation is no longer supported, and the README says to use OPML export and import to transfer podcast subscriptions between devices. That is a simpler and more portable mechanism, and it is the one the project now relies on.
The third is a known issue, listed on its own: the playlist is unstable. It is stated without a fix or a timeline, which is useful information in itself for anyone deciding whether the playlist feature is safe to rely on.
The build, and the toolchain it is pinned to
The build section names an exact toolchain, which is what you need for a reproducible release build. The current versions are Flutter 3.47.2, Dart 3.13.2, Android Gradle Plugin 9.3.2, Gradle 9.5 and JDK 21, and the Android release lint requires JDK 21. The iOS deployment target is 15.0.
The steps are the standard Flutter ones with a couple of specifics. You install Flutter and JDK 21, fetch the source, and point Flutter at the JDK:
git clone https://github.com/tsacdop/tsacdop.git
cd tsacdop
flutter config --jdk-dir /path/to/jdk-21Then the dependencies are resolved with:
flutter pub getThe run command shows the flavour and the define together, and the flags in the README are the fdroid flavour and a Listen Notes key:
flutter run --flavor fdroid \
--dart-define=LISTEN_NOTES_API_KEY=your_keyFor iOS the steps are pod install in the ios directory on macOS, then a build with Xcode or Flutter, and the iOS build is passed --no-codesign so it can be produced without a signing identity.
Two build flavours, and why the F-Droid one is unsigned
There are two flavours, and the reason for the split is signing, which is the kind of detail that saves an afternoon of failed builds.
The fdroid release is intentionally unsigned, so that F-Droid can sign it itself. That is a requirement of the F-Droid build model, and it is why a local fdroid build cannot be the artefact you install directly on a phone.
The github flavour produces signed APKs for GitHub Releases, using the keystore variables KEYSTORE, KEYSTORE_PASSWORD, KEY_ALIAS and KEY_PASSWORD. The README adds the constraint that matters over time: keep using the same release key so that existing GitHub installations can update. A new key means a new signature, and a new signature means existing installs cannot be upgraded in place.
The Android release commands reflect the same split. A debug fdroid build is produced with flutter build apk, and the release variant is assembled through the Gradle wrapper with a JAVA_HOME pointing at JDK 21:
JAVA_HOME=/path/to/jdk-21 \
./android/gradlew -p android assembleFdroidReleaseand the GitHub flavour is a release build split per ABI, so each architecture gets its own artefact, with the Listen Notes key passed again at build time.
Vendored plugin forks, and the state management underneath
The plugins that need compatibility patches are vendored in a third_party directory, and each fork includes an UPSTREAM.md file recording its original repository, the pinned commit and the local changes. That is a practice worth calling out, because vendoring a fork without recording where it came from is how a project ends up unable to update a dependency.
The plugin list in the architecture section is compact and tells you what the app is built on. Local storage is sqflite and shared_preferences. Audio is just_audio and audio_service. State management is the provider package. Downloads use flutter_downloader, and background work uses workmanager.
The directory structure in the README separates three layers. The UI layer is a set of pages, home with the homepage, search and playlist, podcasts with the group and detail pages, episodes with the detail page, and settings. The state layer holds audio state, download state, podcast groups, episode refresh and settings. The service layer holds the API service for podcast search and the OPML builder.
That split is the standard Flutter arrangement with the provider package, and the notable thing about it is that the service layer is thin. Search goes out to ListenNotes and PodcastIndex, and the OPML export is its own module, which is consistent with the app being a client of other services rather than a server of its own.
Localisation, the community, and where the project is heading
The README invites translations by email, and the localisation section credits Localizely for support for open source projects. The languages listed with badges are English, Simplified Chinese, French, Spanish and Portuguese.
Contact is by email, and there is also a Telegram group linked for the community. Bug reports go to GitHub issues and pull requests are welcome.
On the state of the project, the release history is recent and steady: v1.0.3 on 2026-09-26, v1.0.2 on 2026-09-04 and v1.0.1 on 2026-09-01, with the last push to the repository on 2026-10-16 and the repository not archived. A version 1.0 with three releases inside a month suggests the project has just reached a stable public version after the distribution change, which is the moment when a project like this either finds an audience or settles into maintenance.
The repository layout supports a real project rather than a sample: android and ios directories for the platform shells, lib for the Dart source, assets, test, a fastlane directory for release automation, a tool directory, a preview directory for the store images, and pubspec files. The licence is GPL-3.0.
Where Tsacdop is the wrong choice
The first limit is the platform distribution, and it is the one the README is most candid about. Google Play distribution is no longer supported, so the Android experience depends on F-Droid or on manual APK installs that you have to update yourself. For a listener who wants the app to update itself the way every other app does, that is a real cost, and the F-Droid route adds the signing friction described earlier.
The second is gpodder.net. Account synchronisation is gone, and OPML export and import is the replacement. For someone with years of subscriptions in a gpodder account, the transition is manual, and the app is explicit that the data should be exported and the settings backed up before any uninstall.
The third is the iOS story. The source has an ios directory, a pod install step and a deployment target of 15.0, so the app can be built for iOS. What the README describes is an Android distribution story, so an iOS user is on their own for installation and updates, and the source repository is where the app points to reach the author.
The fourth is the feature stability. The known issue list contains one entry, that the playlist is unstable, and playlists are in the feature list as a supported capability. If your workflow depends on playlists, that is a gap to test before you commit to the app. The same applies to the Listen Notes search depending on a key you supply at build time, which means a build you make yourself is not the same app as the one a user installs from F-Droid.
Editorial conclusion
Tsacdop fits a listener who wants an open source podcast player on Android without a store account, and who wants control over subscriptions and downloads rather than a fixed catalogue. It is the wrong tool if you are on iOS and want an App Store build, since the iOS target exists in the source but the distribution story described in the README is Android only, and it is the wrong tool if you rely on gpodder.net account synchronisation, which is no longer supported. Before you build it, check three things: that you have JDK 21 and the pinned Flutter toolchain, since the release lint requires it, that you have a Listen Notes API key if you want that search backend rather than Podcast Index only, and that the vendored plugin forks in third_party/ are acceptable to you, since they are the mechanism the project uses to carry compatibility patches. The licence is GPL-3.0, the current release is v1.0.3 from 2026-09-26, and the last push was on 2026-10-16.
Frequently asked questions
How do I install Tsacdop on Android?
Install it from F-Droid to get updates through the F-Droid client, or download an APK from the latest GitHub release and pick the architecture: arm64-v8a for most modern phones, armeabi-v7a for older 32-bit ARM devices, or x86_64 for Intel and AMD devices and emulators. GitHub APK installs have to be updated manually.
What does Tsacdop need to build a release?
The pinned toolchain is Flutter 3.47.2, Dart 3.13.2, Android Gradle Plugin 9.3.2, Gradle 9.5 and JDK 21, and the Android release lint requires JDK 21. You clone the repository, point Flutter at the JDK with `flutter config --jdk-dir /path/to/jdk-21`, and run `flutter pub get`.
Why are there two build flavours in Tsacdop?
The fdroid flavour is intentionally unsigned so that F-Droid can sign it, and the github flavour produces signed APKs for GitHub Releases using the KEYSTORE, KEYSTORE_PASSWORD, KEY_ALIAS and KEY_PASSWORD variables. The README says to keep using the same release key so existing installs can update.
Which podcast search engines does Tsacdop use?
ListenNotes and PodcastIndex. A Listen Notes API key can be supplied at build time with a Dart define, and without it the app uses Podcast Index search only.
How do I move podcast subscriptions between devices in Tsacdop?
Through OPML export and import. gpodder.net account synchronisation is no longer supported, and the README asks you to export subscriptions and back up settings before uninstalling, since uninstalling removes local app data.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/tsacdop-tsacdop)Was this useful?