Three Opus max-effort audits (CVE/security, code-health, function-
correctness) on the vc=34 surface returned a consolidated punch list
of 5 CRIT + 14 HIGH + ~10 MED. This commit lands all CRIT + HIGH +
the cheap MEDs in one cohesive pass.
CRIT — privacy + main-thread blocks
S1 IosSafeHttpDataSource was logging full pre-signed googlevideo
URLs (signature/sig/pot/expire/cpn) via raw android.util.Log.i
(no DEBUG gate). Then LogDump.capture would scrape its own PID's
logcat and ship it via the share sheet — a "report a bug to
Telegram" silently exfiltrated session credentials. Fixed by
switching all log calls to strawLogD/strawLogW (gated on
BuildConfig.DEBUG), dropping the full-URL log entirely, and
adding a regex scrub pass in LogDump for googlevideo URLs +
signed-param keys before the file hits disk.
S2 Downloader.enqueue handed signed googlevideo URLs to the system
DownloadManager, where they leak into DM's SQLite, logcat, the
system notification, and apps holding ACCESS_DOWNLOAD_MANAGER.
Set VISIBILITY_HIDDEN + setVisibleInDownloadsUi(false) so the
URL never surfaces in any external surface. Added the
DOWNLOAD_WITHOUT_NOTIFICATION permission DM requires.
S3 SettingsImport extracted the user's full newpipe.db (every sub,
watch, search) into cacheDir, deleted on finally. A force-kill
mid-import left the DB on disk indefinitely. Wrapped cleanup in
withContext(NonCancellable), switched workDir to createTempFile
(unguessable name), and added StrawApp.onCreate sweep of stale
newpipe-import-* dirs on every cold start.
C1 SearchViewModel.reactiveFilter ran a fresh SharedPreferences.
getString + Json.decodeFromString on FeedCache (~225 KB) AND
SearchCache (~150 KB) on EVERY keystroke. Hoisted into a
MutableStateFlow<List<StreamItem>> pool, built once on
Dispatchers.IO at VM init and refreshed after each successful
submit. Reactive filter now walks an in-memory list.
C2 SubscriptionFeedViewModel.init did the same FeedCache.load()
synchronously on the main thread at construction (first compose
pass blocked on the JSON decode). Moved into
viewModelScope.launch + withContext(Dispatchers.IO).
HIGH — function correctness + defense in depth
B1+B2 SearchScreen when-branch order: loading + error short-
circuited before the results branch, hiding the cached
preview the VM explicitly kept visible on cache-hit + on
network failure. Refactored to render the cached list under
a thin progress bar / error banner instead.
B3 Downloads "tap completed row" silently failed since minSdk 24
(FileUriExposedException on the file:// URI). Route through
FileProvider with new file_paths.xml entries for
Movies/audio + Movies/video.
B6 Manifest VIEW intent-filter was missing music.youtube.com and
youtube-nocookie.com hosts even though YT_HOSTS allowed them
— added both.
B7 SponsorBlockSkipLoop fired one Toast per skip with no rate
limit; sponsor-dense videos painted 20+ Toasts over 40s after
the seeks completed. 3s rate limit per cur.streamUrl.
Q8 resolvePlayback.pickVideo fallback used maxByOrNull when the
comment said "lowest available" — a 480p-capped user on a
1080p-only upload got 1080p (their data cap blown). Switched
to minByOrNull { height } when nothing fits the cap.
S1 SettingsImport extractZip had no size or entry-count caps —
zip-bomb could fill cacheDir. Added MAX_DB_BYTES (256 MB),
MAX_PREFS_BYTES (1 MB), MAX_ZIP_ENTRIES (64). copyBounded /
readBoundedBytes helpers replace the unbounded copyTo /
readBytes.
S2 Manifest: android:allowBackup=false +
android:dataExtractionRules=@xml/data_extraction_rules.xml +
android:fullBackupContent=false. Excludes root/file/database/
sharedpref/external from both cloud-backup and device-transfer
so the user's full search + watch history doesn't ride to
Google Drive.
S3 Dropped isLenient = true from every Json {} instance (7 sites
across data/ and net/). Lenient parser was buying nothing on
data we wrote ourselves and was a hardening gap on the third-
party SponsorBlock + RYD endpoints (community-run; malformed
payload could feed bad timestamps into the skip loop).
S4 SubscriptionsStore.addAll + HistoryStore.recordAllWatches bulk
methods, used by SettingsImport. Per-row toggle was O(N²) +
N SP writes; bulk path is O(N) + 1 write.
C3 SubsPane infinite-scroll LaunchedEffect keyed on
(displayed.size, hasMore) — both mutated BY the effect. The
collector cancelled itself mid-stream and dropped emissions,
producing "scroll to bottom, nothing more loads". Re-keyed on
listState + filteredCount; the collect lambda reads state
through the snapshotFlow producer to avoid stale captures.
C7 liveDrag + playbackSpeed: mutableFloatStateOf instead of
mutableStateOf — no Float boxing on the 100Hz drag callback.
C8 LogDump.capture is now suspend on Dispatchers.IO. The Settings
click handler launches into scope; button shows "Exporting…"
while in flight.
MED — cheap wins picked up in passing
Q9 reactiveFilter clears the cached preview when current query no
longer has any matches (was leaving stale results visible).
Q10 Hide-watched filter excludes blank video IDs from watchedIds
— a blank in the set used to match every malformed-URL feed
item and silently hide them.
Q13 PiP button in VideoDetail bails with a Toast on null
controller OR null resolved playback (was falling through to
enterPictureInPictureMode with no stream).
C17 SpeedPickerDialog row used fillMaxSize inside an AlertDialog;
only the first row got non-zero height. Fixed to fillMaxWidth.
Deferred to vc=36 follow-up (touch surface area we don't want to
churn in the same ship):
- C6 atomic setPlayingFrom guard in StrawMediaController
- S3 (full) — direct-streaming download replacing DownloadManager
- MED-C16 LazyColumn refactor in VideoDetailScreen
- MED-Q12 loadedUrl assignment ordering hardening
|
||
|---|---|---|
| .github | ||
| .idea | ||
| app | ||
| assets | ||
| buildSrc | ||
| checkstyle | ||
| desktopApp | ||
| doc | ||
| docs/sulkta | ||
| fastlane/metadata/android | ||
| gradle | ||
| iosApp | ||
| rust | ||
| shared | ||
| strawApp | ||
| .editorconfig | ||
| .gitignore | ||
| build.gradle.kts | ||
| gradle.properties | ||
| gradlew | ||
| gradlew.bat | ||
| LICENSE | ||
| README.md | ||
| settings.gradle.kts | ||
We are rewriting large chunks of the codebase, to bring about a modern and stable NewPipe! You can download nightly builds here.
Please work on the refactor branch if you want to contribute new features. The current codebase is in maintenance mode and will only receive bugfixes.
NewPipe
A libre lightweight streaming front-end for Android.
Screenshots • Supported Services • Description • Features • Installation and updates • Contribution • Donate • License
Read this document in other languages: Deutsch, English, Español, Français, हिन्दी, Italiano, 한국어, Português Brasil, Polski, ਪੰਜਾਬੀ , 日本語, Română, Soomaali, Türkçe, 正體中文, অসমীয়া, Српски, العربية
Warning
THIS APP IS IN BETA, SO YOU MAY ENCOUNTER BUGS. IF YOU DO, OPEN AN ISSUE IN OUR GITHUB REPOSITORY BY FILLING OUT THE ISSUE TEMPLATE.
PUTTING NEWPIPE, OR ANY FORK OF IT, INTO THE GOOGLE PLAY STORE VIOLATES THEIR TERMS AND CONDITIONS.
Screenshots
Supported Services
NewPipe currently supports these services:
- YouTube (website) and YouTube Music (website) (wiki)
- PeerTube (website) and all its instances (open the website to know what that means!) (wiki)
- Bandcamp (website) (wiki)
- SoundCloud (website) (wiki)
- media.ccc.de (website) (wiki)
As you can see, NewPipe supports multiple video and audio services. Though it started off with YouTube, other people have added more services over the years, making NewPipe more and more versatile!
Partially due to circumstance, and partially due to its popularity, YouTube is the best supported out of these services. If you use or are familiar with any of these other services, please help us improve support for them! We're looking for maintainers for SoundCloud and PeerTube.
If you intend to add a new service, please get in touch with us first! Our docs provide more information on how a new service can be added to the app and to the NewPipe Extractor.
Description
NewPipe works by fetching the required data from the official API (e.g. PeerTube) of the service you're using. If the official API is restricted (e.g. YouTube) for our purposes, or is proprietary, the app parses the website or uses an internal API instead. This means that you don't need an account on any service to use NewPipe.
Also, since they are free and open source software, neither the app nor the Extractor use any proprietary libraries or frameworks, such as Google Play Services. This means you can use NewPipe on devices or custom ROMs that do not have Google apps installed.
Features
- Watch videos at resolutions up to 4K
- Listen to audio in the background, only loading the audio stream to save data
- Popup mode (floating player, aka Picture-in-Picture)
- Watch live streams
- Show/hide subtitles/closed captions
- Search videos and audios (on YouTube, you can specify the content language as well)
- Enqueue videos (and optionally save them as local playlists)
- Show/hide general information about videos (such as description and tags)
- Show/hide next/related videos
- Show/hide comments
- Search videos, audios, channels, playlists and albums
- Browse videos and audios within a channel
- Subscribe to channels (yes, without logging into any account!)
- Get notifications about new videos from channels you're subscribed to
- Create and edit channel groups (for easier browsing and management)
- Browse video feeds generated from your channel groups
- View and search your watch history
- Search and watch playlists (these are remote playlists, which means they're fetched from the service you're browsing)
- Create and edit local playlists (these are created and saved within the app, and have nothing to do with any service)
- Download videos/audios/subtitles (closed captions)
- Open in Kodi
- Watch/Block age-restricted material
Installation and updates
You can install NewPipe using one of the following methods:
- Add our custom repo to F-Droid and install it from there. The instructions are here: https://newpipe.net/FAQ/tutorials/install-add-fdroid-repo/
- Download the APK from GitHub Releases, compare the signing key and install it.
- Update via F-Droid. This is the slowest method of getting updates, as F-Droid must recognize changes, build the APK itself, sign it, and then push the update to users.
- Build a debug APK yourself. This is the fastest way to get new features on your device, but is much more complicated, so we recommend using one of the other methods.
- If you're interested in a specific feature or bugfix provided in a Pull Request in this repo, you can also download its APK from within the PR. Read the PR description for instructions. The great thing about PR-specific APKs is that they're installed side-by-side the official app, so you don't have to worry about losing your data or messing anything up.
We recommend method 1 for most users. APKs installed using method 1 or 2 are compatible with each other (meaning that if you installed NewPipe using either method 1 or 2, you can also update NewPipe using the other), but not with those installed using method 3. This is due to the same signing key (ours) being used for 1 and 2, but a different signing key (F-Droid's) being used for 3. Building a debug APK using method 4 excludes a key entirely. Signing keys help ensure that a user isn't tricked into installing a malicious update to an app. When using method 5, each APK is signed with a different random key supplied by GitHub Actions, so you cannot even update it. You will have to backup and restore the app data each time you wish to use a new APK.
In the meanwhile, if you want to switch sources for some reason (e.g. NewPipe's core functionality breaks and F-Droid doesn't have the latest update yet), we recommend following this procedure:
- Back up your data via Settings > Backup and Restore > Export Database so you keep your history, subscriptions, and playlists
- Uninstall NewPipe
- Download the APK from the new source and install it
- Import the data from step 1 via Settings > Backup and Restore > Import Database
Note
When you're importing a database into the official app, always make sure that it is the one you exported from the official app. If you import a database exported from an APK other than the official app, it may break things. Such an action is unsupported, and you should only do so when you're absolutely certain you know what you're doing.
APK Info
This is the SHA fingerprint of NewPipe's signing key to verify downloaded APKs which are signed by us. The fingerprint is also available on NewPipe's website. This is relevant for method 2.
CB:84:06:9B:D6:81:16:BA:FA:E5:EE:4E:E5:B0:8A:56:7A:A6:D8:98:40:4E:7C:B1:2F:9E:75:6D:F5:CF:5C:AB
Contribution
Whether you have ideas, translations, design changes, code cleaning, or even major code changes, help is always welcome. The app gets better and better with each contribution, no matter how big or small! If you'd like to get involved, check our contribution notes.
Donate
If you like NewPipe, you're welcome to send a donation. We prefer Liberapay, as it is both open-source and non-profit. For further info on donating to NewPipe, please visit our website.
![]() |
Privacy Policy
The NewPipe project aims to provide a private, anonymous experience for using web-based media services. Therefore, the app does not collect any data without your consent. NewPipe's privacy policy explains in detail what data is sent and stored when you send a crash report, or leave a comment in our blog. You can find the document here.
License
NewPipe is Free Software: You can use, study, share, and improve it at will. Specifically you can redistribute and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.












