I've had this problem for like half a year and I've expected someone else to have it too and report it, really no one? I'm using android 4.4.4 and (before that update half a year ago) the background player worked just fine. I can use VLC for external player for background and that works, it's just really crusty and I really liked the default background player so I wondered if anyone knew something on this issue. Did it start taking up more ram at that point or something? My device (i9070) only has about 640 megs.
0.19.2
Push 'Background' on any video
The background play to work as intended
The onscreen message "Playing in background" appears, and never goes away until the app has crashed.
The background player notification appears, but with the video duration and elapsed time both showing 00:00, the audio plays for about 15 seconds during which the app slowly grinds to a halt (can't scroll anything or do anything at all). After being fully frozen for a few seconds it crashes with just "Newpipe has stopped" no error report thing.
Which version were you using before?
Is this your device? I tested with an emulator with 768MB of RAM, but couldn't reproduce this issue
Yes it is my device, but strange. If I remember correctly it was one of the 0.17 updates that broke the background player for me, so I was probably using 0.16.2 when it was working perfectly. Is there something in the operating system the function relies upon that could be missing? I'm using lineageOS 11
Thanks. Could you try to check if the app or the system prints something in the logcat using this app? https://www.f-droid.org/en/packages/com.dp.logcatapp/
Yeah, alot.
https://pastebin.com/9pMAqKtM
It crashes at 13:05:14
And I discovered that if I just let the app hang without touching anything when it's frozen, it can recover but it takes about a minute.
Thank you
Mmmh, the logcat keeps saying that the application may be doing too much work on the main thread, and that it could not dispatch events right from 13:04:56.364. Another message to note is "Launch timeout has expired, giving up wake lock!", but I don't know what this all means.
Yeah and there's alot of mention of "Binder" and "libbinder.so" and other modules in /system/lib/
Could this issue just be from having too old of an android version?
That shouldn't be a problem, I fully tested (for a pr) the app on Android 4.4 two days ago and it worked flawlessly...
What happens with a fresh install?
Fresh install of the system or the app? The app has done the same since that ~0.16.2 update, I've tried fresh installs. I haven't reinstalled the system for a few months, but I have multiple times during this issue and it has never made a difference. Although I haven't used any other rom than the 10/10/2017 lineageos
No, I'm not asking you to reinstall the system ;-)
So this should not be an issue with database or such, since it happens even after a fresh app install. But there is something wrong indeed. @TobiGr do you know a way this could be debugged? Maybe by providing an APK where DEBUG == true, so that more things are printed out to logcat?
Well just for experimentations sake I installed android 6.0.1 on my phone and right now can't make the background play bug out, so it must be some incompatibility with android 4.4.4 or atleast the rom I've always used (lineageos)
Hello, I'm having a similar issue.
I use SM-G360H.
It was a few months ago maybe around 23rd of January, 2020, NewPipe couldn't play YouTube because of some change in YouTube API and it was fixed with NewPipeExtractor I think.
Since after that update whenever I tap on Background, it starts playing the audio but freezes my phone. And soon enough my phone does a quick reboot. It's been happening for months.
Btw, Android version: 4.4.4.
Please help.
I did some more investigation on 4.4.4, In developer settings I changed runtime from dalvik to ART and yeah everything is kind of crashy and laggy but newpipe works solid now, cannot make it crash on the background play like on dalvik. @Stypox do you have any ideas what could be done?
This is really strange, why would switching Java VM completely change the performace?!? I'm not sure I can help here, maybe it is a problem with the audio device access, but I don't know
But it was working fine before that update.
I want to try to figure this out. Honestly I've done many hours of googling to get this far: This is the point where I can't figure it out. How can I read the debug messages? I got the newpipe debug app compiled and installed on my phone, and I can through ADB shell start it with "am start -D" and I can get it to the the "Waiting for debugger" screen, I can also attach to it with JDB from my laptop.
@Stypox How do I read something useful from this?
@skil3z: What's the latest version that you know works and what's the earliest version that you know doesn't work? And do you have Git, JDK 8, and Android SDK 28 installed on your laptop? (Or could you install that?)
@wb9688 I have all of those installed, and I can't remember which version is the first to cause this crash. Pretty sure 0.16 worked. Is my idea about this completely wrong: If I could get the app to print out debug messages on what it's doing when I push the background button, the last thing printed before the hang would be a clue on what's causing it?
@skil3z: If you're going that route, why not just use a proper debugger like the one that's built-in for Android Studio? Then you could see what calls it makes exactly and stuff when that happens.
Anyway, I would suggest to do a git bisect instead to find the commit that caused it.
@wb9688 Well I don't have android studio installed but I can install it and try that.
@skil3z: But if you know what version contains the bug and which doesn't yet, just use git bisect to find what commit caused the regression. You don't neccessarily need Android Studio for that, just git to do the bisection, and JDK 8 and Android SDK 28 to compile the app.
@wb9688 But if I compile such an old version I would be unable to test it anyway? Youtube has "broken" the functionality atleast twice since that bug appeared if I recall correctly.
@skil3z: You could test services other than YouTube ;)
@wb9688 Well "framatube"? Doesn't have background play, "MediaCCC" background play works just fine, no freezes and soundcloud doesn't load anything, "SSL_ERROR_ZERO_RETURN" so I don't really know how to test other than with youtube
So, this is reallly strange, why would MediaCCC cause no issue at all? Since the conditions for the player are the same (it gets fed a link to play), I think the issue lies in youtube-specific parts of the app?
I verified this, I can background play atleast 10 videos in MediaCCC when the app is running and everything is smooth, but on youtube it hangs on the first or second video. Are you sure the background player is absolutely equal for both?
Does #3513 solve the issue, by chance?
@Stypox Well it's progress, now when hitting background, the app doesn't crash but only slows down to a 1 frame/second slideshow for about 30-40 seconds. (34, 42 & 46 seconds, I timed it three times)
Also I now got the idea to look at the cpu usage, and the app that actually cranks the cpu is "system_server" instantly after pushing background.. So newpipe itself doesn't cause the lag, but it triggers it

Now the background player doesn't crash but yes, it slows down.
Any fix on that?
Thanks.
Do I sound demanding and ungrateful?
Well what should I have said? The question was whether the patch fixed it or not, which it didn't
Atleast I gave an answer with more info on what happens than just "No"
Thanks for the analysis, this is really strange. Maybe google/ExoPlayer#759 is related? Maybe this is an issue with exoplayer, but I don't know.
I don't know enough of programming these massive programs to really say... Would it be difficult to somehow put the current parsing/extraction code (so youtube would load pages) for example into 0.14.2 ? Keeping everything else intact?
@skil3z: It would require some changes, but I don't think it would be too hard.
@skil3z: Here is v0.14.2 with the following patches:
Diff
diff --git a/app/build.gradle b/app/build.gradle
index b507fd860..58912e002 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -54,7 +54,7 @@ dependencies {
exclude module: 'support-annotations'
})
- implementation 'com.github.TeamNewPipe:NewPipeExtractor:32d316330c26'
+ implementation 'com.github.TeamNewPipe:NewPipeExtractor:3cae32b6db'
testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-core:2.23.0'
diff --git a/app/src/debug/java/org/schabi/newpipe/DebugApp.java b/app/src/debug/java/org/schabi/newpipe/DebugApp.java
index aff354a69..0b96a85fd 100644
--- a/app/src/debug/java/org/schabi/newpipe/DebugApp.java
+++ b/app/src/debug/java/org/schabi/newpipe/DebugApp.java
@@ -15,8 +15,6 @@ import com.squareup.leakcanary.LeakCanary;
import com.squareup.leakcanary.LeakDirectoryProvider;
import com.squareup.leakcanary.RefWatcher;
-import org.schabi.newpipe.extractor.Downloader;
-
import java.io.File;
import java.util.concurrent.TimeUnit;
@@ -38,8 +36,8 @@ public class DebugApp extends App {
}
@Override
- protected Downloader getDownloader() {
- return org.schabi.newpipe.Downloader.init(new OkHttpClient.Builder()
+ protected DownloaderImpl getDownloader() {
+ return DownloaderImpl.init(new OkHttpClient.Builder()
.addNetworkInterceptor(new StethoInterceptor()));
}
diff --git a/app/src/main/java/org/schabi/newpipe/App.java b/app/src/main/java/org/schabi/newpipe/App.java
index 314c95c8d..27500fbef 100644
--- a/app/src/main/java/org/schabi/newpipe/App.java
+++ b/app/src/main/java/org/schabi/newpipe/App.java
@@ -5,7 +5,6 @@ import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Build;
-import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
import android.util.Log;
@@ -20,9 +19,7 @@ import org.acra.config.ACRAConfiguration;
import org.acra.config.ACRAConfigurationException;
import org.acra.config.ConfigurationBuilder;
import org.acra.sender.ReportSenderFactory;
-import org.schabi.newpipe.extractor.Downloader;
import org.schabi.newpipe.extractor.NewPipe;
-import org.schabi.newpipe.extractor.utils.Localization;
import org.schabi.newpipe.report.AcraReportSenderFactory;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
@@ -102,8 +99,8 @@ public class App extends Application {
configureRxJavaErrorHandler();
}
- protected Downloader getDownloader() {
- return org.schabi.newpipe.Downloader.init(null);
+ protected DownloaderImpl getDownloader() {
+ return DownloaderImpl.init(null);
}
private void configureRxJavaErrorHandler() {
diff --git a/app/src/main/java/org/schabi/newpipe/Downloader.java b/app/src/main/java/org/schabi/newpipe/Downloader.java
deleted file mode 100644
index 62c7d1671..000000000
--- a/app/src/main/java/org/schabi/newpipe/Downloader.java
+++ /dev/null
@@ -1,179 +0,0 @@
-package org.schabi.newpipe;
-
-import android.support.annotation.Nullable;
-import android.text.TextUtils;
-
-import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
-import org.schabi.newpipe.extractor.utils.Localization;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-import okhttp3.OkHttpClient;
-import okhttp3.Request;
-import okhttp3.Response;
-import okhttp3.ResponseBody;
-
-
-/*
- * Created by Christian Schabesberger on 28.01.16.
- *
- * Copyright (C) Christian Schabesberger 2016 <[email protected]>
- * Downloader.java is part of NewPipe.
- *
- * NewPipe is free software: you can redistribute it 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.
- *
- * NewPipe is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
- */
-
-public class Downloader implements org.schabi.newpipe.extractor.Downloader {
- public static final String USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0";
-
- private static Downloader instance;
- private String mCookies;
- private final OkHttpClient client;
-
- private Downloader(OkHttpClient.Builder builder) {
- this.client = builder
- .readTimeout(30, TimeUnit.SECONDS)
- //.cache(new Cache(new File(context.getExternalCacheDir(), "okhttp"), 16 * 1024 * 1024))
- .build();
- }
-
- /**
- * It's recommended to call exactly once in the entire lifetime of the application.
- *
- * @param builder if null, default builder will be used
- */
- public static Downloader init(@Nullable OkHttpClient.Builder builder) {
- return instance = new Downloader(builder != null ? builder : new OkHttpClient.Builder());
- }
-
- public static Downloader getInstance() {
- return instance;
- }
-
- public String getCookies() {
- return mCookies;
- }
-
- public void setCookies(String cookies) {
- mCookies = cookies;
- }
-
- /**
- * Get the size of the content that the url is pointing by firing a HEAD request.
- *
- * @param url an url pointing to the content
- * @return the size of the content, in bytes
- */
- public long getContentLength(String url) throws IOException {
- Response response = null;
- try {
- final Request request = new Request.Builder()
- .head().url(url)
- .addHeader("User-Agent", USER_AGENT)
- .build();
- response = client.newCall(request).execute();
-
- return Long.parseLong(response.header("Content-Length"));
- } catch (NumberFormatException e) {
- throw new IOException("Invalid content length", e);
- } finally {
- if (response != null) {
- response.close();
- }
- }
- }
-
- /**
- * Download the text file at the supplied URL as in download(String),
- * but set the HTTP header field "Accept-Language" to the supplied string.
- *
- * @param siteUrl the URL of the text file to return the contents of
- * @param localization the language and country (usually a 2-character code) to set
- * @return the contents of the specified text file
- */
- @Override
- public String download(String siteUrl, Localization localization) throws IOException, ReCaptchaException {
- Map<String, String> requestProperties = new HashMap<>();
- requestProperties.put("Accept-Language", localization.getLanguage());
- return download(siteUrl, requestProperties);
- }
-
- /**
- * Download the text file at the supplied URL as in download(String),
- * but set the HTTP headers included in the customProperties map.
- *
- * @param siteUrl the URL of the text file to return the contents of
- * @param customProperties set request header properties
- * @return the contents of the specified text file
- * @throws IOException
- */
- @Override
- public String download(String siteUrl, Map<String, String> customProperties) throws IOException, ReCaptchaException {
- return getBody(siteUrl, customProperties).string();
- }
-
- public InputStream stream(String siteUrl) throws IOException {
- try {
- return getBody(siteUrl, Collections.emptyMap()).byteStream();
- } catch (ReCaptchaException e) {
- throw new IOException(e.getMessage(), e.getCause());
- }
- }
-
- private ResponseBody getBody(String siteUrl, Map<String, String> customProperties) throws IOException, ReCaptchaException {
- final Request.Builder requestBuilder = new Request.Builder()
- .method("GET", null).url(siteUrl)
- .addHeader("User-Agent", USER_AGENT);
-
- for (Map.Entry<String, String> header : customProperties.entrySet()) {
- requestBuilder.addHeader(header.getKey(), header.getValue());
- }
-
- if (!TextUtils.isEmpty(mCookies)) {
- requestBuilder.addHeader("Cookie", mCookies);
- }
-
- final Request request = requestBuilder.build();
- final Response response = client.newCall(request).execute();
- final ResponseBody body = response.body();
-
- if (response.code() == 429) {
- throw new ReCaptchaException("reCaptcha Challenge requested");
- }
-
- if (body == null) {
- response.close();
- return null;
- }
-
- return body;
- }
-
- /**
- * Download (via HTTP) the text file located at the supplied URL, and return its contents.
- * Primarily intended for downloading web pages.
- *
- * @param siteUrl the URL of the text file to download
- * @return the contents of the specified text file
- */
- @Override
- public String download(String siteUrl) throws IOException, ReCaptchaException {
- return download(siteUrl, Collections.emptyMap());
- }
-}
diff --git a/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java b/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java
new file mode 100644
index 000000000..32309cd7d
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java
@@ -0,0 +1,232 @@
+package org.schabi.newpipe;
+
+import android.os.Build;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+
+import org.schabi.newpipe.extractor.downloader.Downloader;
+import org.schabi.newpipe.extractor.downloader.Request;
+import org.schabi.newpipe.extractor.downloader.Response;
+import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
+import org.schabi.newpipe.util.TLSSocketFactoryCompat;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
+import okhttp3.CipherSuite;
+import okhttp3.ConnectionSpec;
+import okhttp3.OkHttpClient;
+import okhttp3.RequestBody;
+import okhttp3.ResponseBody;
+
+import static org.schabi.newpipe.MainActivity.DEBUG;
+
+public final class DownloaderImpl extends Downloader {
+ public static final String USER_AGENT
+ = "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:68.0) Gecko/20100101 Firefox/68.0";
+
+ private static DownloaderImpl instance;
+ private String mCookies;
+ private OkHttpClient client;
+
+ private DownloaderImpl(final OkHttpClient.Builder builder) {
+ if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
+ enableModernTLS(builder);
+ }
+ this.client = builder
+ .readTimeout(30, TimeUnit.SECONDS)
+// .cache(new Cache(new File(context.getExternalCacheDir(), "okhttp"),
+// 16 * 1024 * 1024))
+ .build();
+ }
+
+ /**
+ * It's recommended to call exactly once in the entire lifetime of the application.
+ *
+ * @param builder if null, default builder will be used
+ * @return a new instance of {@link DownloaderImpl}
+ */
+ public static DownloaderImpl init(@Nullable final OkHttpClient.Builder builder) {
+ instance = new DownloaderImpl(
+ builder != null ? builder : new OkHttpClient.Builder());
+ return instance;
+ }
+
+ public static DownloaderImpl getInstance() {
+ return instance;
+ }
+
+ /**
+ * Enable TLS 1.2 and 1.1 on Android Kitkat. This function is mostly taken
+ * from the documentation of OkHttpClient.Builder.sslSocketFactory(_,_).
+ * <p>
+ * If there is an error, the function will safely fall back to doing nothing
+ * and printing the error to the console.
+ * </p>
+ *
+ * @param builder The HTTPClient Builder on which TLS is enabled on (will be modified in-place)
+ */
+ private static void enableModernTLS(final OkHttpClient.Builder builder) {
+ try {
+ // get the default TrustManager
+ TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
+ TrustManagerFactory.getDefaultAlgorithm());
+ trustManagerFactory.init((KeyStore) null);
+ TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
+ if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
+ throw new IllegalStateException("Unexpected default trust managers:"
+ + Arrays.toString(trustManagers));
+ }
+ X509TrustManager trustManager = (X509TrustManager) trustManagers[0];
+
+ // insert our own TLSSocketFactory
+ SSLSocketFactory sslSocketFactory = TLSSocketFactoryCompat.getInstance();
+
+ builder.sslSocketFactory(sslSocketFactory, trustManager);
+
+ // This will try to enable all modern CipherSuites(+2 more)
+ // that are supported on the device.
+ // Necessary because some servers (e.g. Framatube.org)
+ // don't support the old cipher suites.
+ // https://github.com/square/okhttp/issues/4053#issuecomment-402579554
+ List<CipherSuite> cipherSuites = new ArrayList<>();
+ cipherSuites.addAll(ConnectionSpec.MODERN_TLS.cipherSuites());
+ cipherSuites.add(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA);
+ cipherSuites.add(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA);
+ ConnectionSpec legacyTLS = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
+ .cipherSuites(cipherSuites.toArray(new CipherSuite[0]))
+ .build();
+
+ builder.connectionSpecs(Arrays.asList(legacyTLS, ConnectionSpec.CLEARTEXT));
+ } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
+ if (DEBUG) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public String getCookies() {
+ return mCookies;
+ }
+
+ public void setCookies(final String cookies) {
+ mCookies = cookies;
+ }
+
+ /**
+ * Get the size of the content that the url is pointing by firing a HEAD request.
+ *
+ * @param url an url pointing to the content
+ * @return the size of the content, in bytes
+ */
+ public long getContentLength(final String url) throws IOException {
+ try {
+ final Response response = head(url);
+ return Long.parseLong(response.getHeader("Content-Length"));
+ } catch (NumberFormatException e) {
+ throw new IOException("Invalid content length", e);
+ } catch (ReCaptchaException e) {
+ throw new IOException(e);
+ }
+ }
+
+ public InputStream stream(final String siteUrl) throws IOException {
+ try {
+ final okhttp3.Request.Builder requestBuilder = new okhttp3.Request.Builder()
+ .method("GET", null).url(siteUrl)
+ .addHeader("User-Agent", USER_AGENT);
+
+ if (!TextUtils.isEmpty(mCookies)) {
+ requestBuilder.addHeader("Cookie", mCookies);
+ }
+
+ final okhttp3.Request request = requestBuilder.build();
+ final okhttp3.Response response = client.newCall(request).execute();
+ final ResponseBody body = response.body();
+
+ if (response.code() == 429) {
+ throw new ReCaptchaException("reCaptcha Challenge requested", siteUrl);
+ }
+
+ if (body == null) {
+ response.close();
+ return null;
+ }
+
+ return body.byteStream();
+ } catch (ReCaptchaException e) {
+ throw new IOException(e.getMessage(), e.getCause());
+ }
+ }
+
+ @Override
+ public Response execute(@NonNull final Request request)
+ throws IOException, ReCaptchaException {
+ final String httpMethod = request.httpMethod();
+ final String url = request.url();
+ final Map<String, List<String>> headers = request.headers();
+ final byte[] dataToSend = request.dataToSend();
+
+ RequestBody requestBody = null;
+ if (dataToSend != null) {
+ requestBody = RequestBody.create(null, dataToSend);
+ }
+
+ final okhttp3.Request.Builder requestBuilder = new okhttp3.Request.Builder()
+ .method(httpMethod, requestBody).url(url)
+ .addHeader("User-Agent", USER_AGENT);
+
+ if (!TextUtils.isEmpty(mCookies)) {
+ requestBuilder.addHeader("Cookie", mCookies);
+ }
+
+ for (Map.Entry<String, List<String>> pair : headers.entrySet()) {
+ final String headerName = pair.getKey();
+ final List<String> headerValueList = pair.getValue();
+
+ if (headerValueList.size() > 1) {
+ requestBuilder.removeHeader(headerName);
+ for (String headerValue : headerValueList) {
+ requestBuilder.addHeader(headerName, headerValue);
+ }
+ } else if (headerValueList.size() == 1) {
+ requestBuilder.header(headerName, headerValueList.get(0));
+ }
+
+ }
+
+ final okhttp3.Response response = client.newCall(requestBuilder.build()).execute();
+
+ if (response.code() == 429) {
+ response.close();
+
+ throw new ReCaptchaException("reCaptcha Challenge requested", url);
+ }
+
+ final ResponseBody body = response.body();
+ String responseBodyToReturn = null;
+
+ if (body != null) {
+ responseBodyToReturn = body.string();
+ }
+
+ final String latestUrl = response.request().url().toString();
+ return new Response(response.code(), response.message(), response.headers().toMultimap(),
+ responseBodyToReturn, latestUrl);
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/ImageDownloader.java b/app/src/main/java/org/schabi/newpipe/ImageDownloader.java
index eb5e92e88..dfb7d3276 100644
--- a/app/src/main/java/org/schabi/newpipe/ImageDownloader.java
+++ b/app/src/main/java/org/schabi/newpipe/ImageDownloader.java
@@ -40,7 +40,7 @@ public class ImageDownloader extends BaseImageDownloader {
}
protected InputStream getStreamFromNetwork(String imageUri, Object extra) throws IOException {
- final Downloader downloader = (Downloader) NewPipe.getDownloader();
+ final DownloaderImpl downloader = (DownloaderImpl) NewPipe.getDownloader();
return downloader.stream(imageUri);
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/ReCaptchaActivity.java b/app/src/main/java/org/schabi/newpipe/ReCaptchaActivity.java
index 74c818bf9..3ee5e733c 100644
--- a/app/src/main/java/org/schabi/newpipe/ReCaptchaActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/ReCaptchaActivity.java
@@ -107,7 +107,7 @@ public class ReCaptchaActivity extends AppCompatActivity {
// find cookies : s_gl & goojf and Add cookies to Downloader
if (find_access_cookies(cookies)) {
// Give cookies to Downloader class
- Downloader.getInstance().setCookies(mCookies);
+ DownloaderImpl.getInstance().setCookies(mCookies);
// Closing activity and return to parent
setResult(RESULT_OK);
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
index 9ab40e81c..eab62d94a 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
@@ -1215,10 +1215,10 @@ public class VideoDetailFragment
} else {
videoDescriptionRootLayout.setVisibility(View.VISIBLE);
}
- if (!TextUtils.isEmpty(info.getUploadDate())) {
- videoUploadDateView.setText(Localization.localizeDate(activity, info.getUploadDate()));
+ if (!TextUtils.isEmpty(info.getTextualUploadDate())) {
+ videoUploadDateView.setText(Localization.localizeDate(activity, info.getTextualUploadDate()));
}
- prepareDescription(info.getDescription());
+ prepareDescription(info.getDescription().getContent());
animateView(spinnerToolbar, true, 500);
setupActionBar(info);
@@ -1294,9 +1294,7 @@ public class VideoDetailFragment
protected boolean onError(Throwable exception) {
if (super.onError(exception)) return true;
- if (exception instanceof YoutubeStreamExtractor.GemaException) {
- onBlockedByGemaError();
- } else if (exception instanceof ContentNotAvailableException) {
+ if (exception instanceof ContentNotAvailableException) {
showError(getString(R.string.content_not_available), false);
} else {
int errorId = exception instanceof YoutubeStreamExtractor.DecryptException
@@ -1333,4 +1331,4 @@ public class VideoDetailFragment
relatedStreamRootLayout.setVisibility(visibility);
}
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamInfoItemHolder.java
index 0a7705427..4b9000553 100644
--- a/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamInfoItemHolder.java
+++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamInfoItemHolder.java
@@ -54,11 +54,11 @@ public class StreamInfoItemHolder extends StreamMiniInfoItemHolder {
if (infoItem.getViewCount() >= 0) {
viewsAndDate = Localization.shortViewCount(itemBuilder.getContext(), infoItem.getViewCount());
}
- if (!TextUtils.isEmpty(infoItem.getUploadDate())) {
+ if (!TextUtils.isEmpty(infoItem.getTextualUploadDate())) {
if (viewsAndDate.isEmpty()) {
- viewsAndDate = infoItem.getUploadDate();
+ viewsAndDate = infoItem.getTextualUploadDate();
} else {
- viewsAndDate += " • " + infoItem.getUploadDate();
+ viewsAndDate += " • " + infoItem.getTextualUploadDate();
}
}
return viewsAndDate;
diff --git a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
index 0e4d07179..9074550f8 100644
--- a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java
@@ -52,7 +52,7 @@ import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
import org.schabi.newpipe.BuildConfig;
-import org.schabi.newpipe.Downloader;
+import org.schabi.newpipe.DownloaderImpl;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.local.history.HistoryRecordManager;
@@ -179,7 +179,7 @@ public abstract class BasePlayer implements
this.progressUpdateReactor = new SerialDisposable();
this.databaseUpdateReactor = new CompositeDisposable();
- final String userAgent = Downloader.USER_AGENT;
+ final String userAgent = DownloaderImpl.USER_AGENT;
final DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
this.dataSource = new PlayerDataSource(context, userAgent, bandwidthMeter);
diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java
index 16dffc3de..bbb76c881 100644
--- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java
@@ -15,15 +15,14 @@ import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
import com.google.android.exoplayer2.upstream.BandwidthMeter;
-import com.google.android.exoplayer2.util.MimeTypes;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.InfoItem;
-import org.schabi.newpipe.extractor.Subtitles;
+import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
-import org.schabi.newpipe.extractor.stream.SubtitlesFormat;
+import org.schabi.newpipe.extractor.stream.SubtitlesStream;
import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
@@ -45,7 +44,9 @@ import static com.google.android.exoplayer2.ui.AspectRatioFrameLayout.RESIZE_MOD
import static com.google.android.exoplayer2.ui.AspectRatioFrameLayout.RESIZE_MODE_FIT;
import static com.google.android.exoplayer2.ui.AspectRatioFrameLayout.RESIZE_MODE_ZOOM;
import static java.lang.annotation.RetentionPolicy.SOURCE;
-import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.*;
+import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_BACKGROUND;
+import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_NONE;
+import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_POPUP;
public class PlayerHelper {
private PlayerHelper() {}
@@ -87,17 +88,13 @@ public class PlayerHelper {
return pitchFormatter.format(pitch);
}
- public static String mimeTypesOf(final SubtitlesFormat format) {
- switch (format) {
- case VTT: return MimeTypes.TEXT_VTT;
- case TTML: return MimeTypes.APPLICATION_TTML;
- default: throw new IllegalArgumentException("Unrecognized mime type: " + format.name());
- }
+ public static String mimeTypesOf(final MediaFormat format) {
+ return format.getMimeType();
}
@NonNull
public static String captionLanguageOf(@NonNull final Context context,
- @NonNull final Subtitles subtitles) {
+ @NonNull final SubtitlesStream subtitles) {
final String displayName = subtitles.getLocale().getDisplayName(subtitles.getLocale());
return displayName + (subtitles.isAutoGenerated() ? " (" + context.getString(R.string.caption_auto_generated)+ ")" : "");
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/resolver/VideoPlaybackResolver.java b/app/src/main/java/org/schabi/newpipe/player/resolver/VideoPlaybackResolver.java
index 8f91f4886..ed0bf5ae5 100644
--- a/app/src/main/java/org/schabi/newpipe/player/resolver/VideoPlaybackResolver.java
+++ b/app/src/main/java/org/schabi/newpipe/player/resolver/VideoPlaybackResolver.java
@@ -10,9 +10,9 @@ import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MergingMediaSource;
import org.schabi.newpipe.extractor.MediaFormat;
-import org.schabi.newpipe.extractor.Subtitles;
import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.StreamInfo;
+import org.schabi.newpipe.extractor.stream.SubtitlesStream;
import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.player.helper.PlayerDataSource;
import org.schabi.newpipe.player.helper.PlayerHelper;
@@ -93,8 +93,8 @@ public class VideoPlaybackResolver implements PlaybackResolver {
// Below are auxiliary media sources
// Create subtitle sources
- for (final Subtitles subtitle : info.getSubtitles()) {
- final String mimeType = PlayerHelper.mimeTypesOf(subtitle.getFileType());
+ for (final SubtitlesStream subtitle : info.getSubtitles()) {
+ final String mimeType = PlayerHelper.mimeTypesOf(subtitle.getFormat());
if (mimeType == null) continue;
final Format textFormat = Format.createTextSampleFormat(null, mimeType,
diff --git a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java
index 82604f7da..5c3d162ed 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java
@@ -18,7 +18,7 @@ import com.nostra13.universalimageloader.core.ImageLoader;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.NewPipe;
-import org.schabi.newpipe.extractor.utils.Localization;
+import org.schabi.newpipe.extractor.localization.Localization;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.FilePickerActivityHelper;
@@ -112,14 +112,14 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
Preference setPreferredLanguage = findPreference(getString(R.string.content_language_key));
setPreferredLanguage.setOnPreferenceChangeListener((Preference p, Object newLanguage) -> {
Localization oldLocal = org.schabi.newpipe.util.Localization.getPreferredExtractorLocal(getActivity());
- NewPipe.setLocalization(new Localization(oldLocal.getCountry(), (String) newLanguage));
+ NewPipe.setupLocalization(new Localization(oldLocal.getCountryCode(), (String) newLanguage));
return true;
});
Preference setPreferredCountry = findPreference(getString(R.string.content_country_key));
setPreferredCountry.setOnPreferenceChangeListener((Preference p, Object newCountry) -> {
Localization oldLocal = org.schabi.newpipe.util.Localization.getPreferredExtractorLocal(getActivity());
- NewPipe.setLocalization(new Localization((String) newCountry, oldLocal.getLanguage()));
+ NewPipe.setupLocalization(new Localization((String) newCountry, oldLocal.getLanguageCode()));
return true;
});
}
diff --git a/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java b/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java
index e04c1e8d0..5328164e9 100644
--- a/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java
@@ -32,7 +32,6 @@ import org.schabi.newpipe.extractor.Info;
import org.schabi.newpipe.extractor.ListExtractor.InfoItemsPage;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.channel.ChannelInfo;
-import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
@@ -220,8 +219,6 @@ public final class ExtractorHelper {
context.startActivity(intent);
} else if (exception instanceof IOException) {
Toast.makeText(context, R.string.network_error, Toast.LENGTH_LONG).show();
- } else if (exception instanceof YoutubeStreamExtractor.GemaException) {
- Toast.makeText(context, R.string.blocked_by_gema, Toast.LENGTH_LONG).show();
} else if (exception instanceof ContentNotAvailableException) {
Toast.makeText(context, R.string.content_not_available, Toast.LENGTH_LONG).show();
} else {
diff --git a/app/src/main/java/org/schabi/newpipe/util/Localization.java b/app/src/main/java/org/schabi/newpipe/util/Localization.java
index eed1a8ae2..5e56096e4 100644
--- a/app/src/main/java/org/schabi/newpipe/util/Localization.java
+++ b/app/src/main/java/org/schabi/newpipe/util/Localization.java
@@ -69,7 +69,7 @@ public class Localization {
return stringBuilder.toString();
}
- public static org.schabi.newpipe.extractor.utils.Localization getPreferredExtractorLocal(Context context) {
+ public static org.schabi.newpipe.extractor.localization.Localization getPreferredExtractorLocal(Context context) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
String languageCode = sp.getString(context.getString(R.string.content_language_key),
@@ -78,7 +78,7 @@ public class Localization {
String countryCode = sp.getString(context.getString(R.string.content_country_key),
context.getString(R.string.default_country_value));
- return new org.schabi.newpipe.extractor.utils.Localization(countryCode, languageCode);
+ return new org.schabi.newpipe.extractor.localization.Localization(countryCode, languageCode);
}
public static Locale getPreferredLocale(Context context) {
diff --git a/app/src/main/java/org/schabi/newpipe/util/StreamItemAdapter.java b/app/src/main/java/org/schabi/newpipe/util/StreamItemAdapter.java
index e100a447b..ec88b5758 100644
--- a/app/src/main/java/org/schabi/newpipe/util/StreamItemAdapter.java
+++ b/app/src/main/java/org/schabi/newpipe/util/StreamItemAdapter.java
@@ -9,7 +9,7 @@ import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.TextView;
-import org.schabi.newpipe.Downloader;
+import org.schabi.newpipe.DownloaderImpl;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.Stream;
@@ -147,7 +147,7 @@ public class StreamItemAdapter<T extends Stream> extends BaseAdapter {
continue;
}
- final long contentLength = Downloader.getInstance().getContentLength(stream.getUrl());
+ final long contentLength = DownloaderImpl.getInstance().getContentLength(stream.getUrl());
streamsWrapper.setSize(stream, contentLength);
hasChanged = true;
}
@@ -193,4 +193,4 @@ public class StreamItemAdapter<T extends Stream> extends BaseAdapter {
return (StreamSizeWrapper<X>) EMPTY;
}
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/org/schabi/newpipe/util/TLSSocketFactoryCompat.java b/app/src/main/java/org/schabi/newpipe/util/TLSSocketFactoryCompat.java
new file mode 100644
index 000000000..105af5086
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/util/TLSSocketFactoryCompat.java
@@ -0,0 +1,114 @@
+package org.schabi.newpipe.util;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+
+import static org.schabi.newpipe.MainActivity.DEBUG;
+
+
+/**
+ * This is an extension of the SSLSocketFactory which enables TLS 1.2 and 1.1.
+ * Created for usage on Android 4.1-4.4 devices, which haven't enabled those by default.
+ */
+public class TLSSocketFactoryCompat extends SSLSocketFactory {
+
+
+ private static TLSSocketFactoryCompat instance = null;
+
+ private SSLSocketFactory internalSSLSocketFactory;
+
+ public TLSSocketFactoryCompat() throws KeyManagementException, NoSuchAlgorithmException {
+ SSLContext context = SSLContext.getInstance("TLS");
+ context.init(null, null, null);
+ internalSSLSocketFactory = context.getSocketFactory();
+ }
+
+
+ public TLSSocketFactoryCompat(final TrustManager[] tm)
+ throws KeyManagementException, NoSuchAlgorithmException {
+ SSLContext context = SSLContext.getInstance("TLS");
+ context.init(null, tm, new java.security.SecureRandom());
+ internalSSLSocketFactory = context.getSocketFactory();
+ }
+
+ public static TLSSocketFactoryCompat getInstance()
+ throws NoSuchAlgorithmException, KeyManagementException {
+ if (instance != null) {
+ return instance;
+ }
+ instance = new TLSSocketFactoryCompat();
+ return instance;
+ }
+
+ public static void setAsDefault() {
+ try {
+ HttpsURLConnection.setDefaultSSLSocketFactory(getInstance());
+ } catch (NoSuchAlgorithmException | KeyManagementException e) {
+ if (DEBUG) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ @Override
+ public String[] getDefaultCipherSuites() {
+ return internalSSLSocketFactory.getDefaultCipherSuites();
+ }
+
+ @Override
+ public String[] getSupportedCipherSuites() {
+ return internalSSLSocketFactory.getSupportedCipherSuites();
+ }
+
+ @Override
+ public Socket createSocket() throws IOException {
+ return enableTLSOnSocket(internalSSLSocketFactory.createSocket());
+ }
+
+ @Override
+ public Socket createSocket(final Socket s, final String host, final int port,
+ final boolean autoClose) throws IOException {
+ return enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose));
+ }
+
+ @Override
+ public Socket createSocket(final String host, final int port) throws IOException {
+ return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
+ }
+
+ @Override
+ public Socket createSocket(final String host, final int port, final InetAddress localHost,
+ final int localPort) throws IOException {
+ return enableTLSOnSocket(internalSSLSocketFactory.createSocket(
+ host, port, localHost, localPort));
+ }
+
+ @Override
+ public Socket createSocket(final InetAddress host, final int port) throws IOException {
+ return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
+ }
+
+ @Override
+ public Socket createSocket(final InetAddress address, final int port,
+ final InetAddress localAddress, final int localPort)
+ throws IOException {
+ return enableTLSOnSocket(internalSSLSocketFactory.createSocket(
+ address, port, localAddress, localPort));
+ }
+
+ private Socket enableTLSOnSocket(final Socket socket) {
+ if (socket != null && (socket instanceof SSLSocket)) {
+ ((SSLSocket) socket).setEnabledProtocols(new String[]{"TLSv1.1", "TLSv1.2"});
+ }
+ return socket;
+ }
+}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 5084c008b..f2715b1a2 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -4,5 +4,4 @@ distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
-distributionSha256Sum=9af7345c199f1731c187c96d3fe3d31f5405192a42046bafa71d846c3d9adacb
#distributionSha256Sum must be updated along with the version of gradle in distributionUrl
@wb9688 Wow! Thank you so much, and yes this works perfectly, no cpu usage spike, lag or crash, everything smooth
I think that this could be closed.
@wb9688 Wow! Thank you so much, and yes this works perfectly, no cpu usage spike, lag or crash, everything smooth
I think that this could be closed.
@wb9688 Wow! Thank you so much, and yes this works perfectly, no cpu usage spike, lag or crash, everything smooth
Probably? I don't think many people are affected by this exact problem. Also I don't know how to help more with this... I don't currently have time IRL nor the android & java know-how to solve what exactly causes my specific build of android 4.4.4 to be allergic to the newer versions NewPipe (or exoplayer used for the playback to be more exact?).
@skil3z Does this apk, containing the features that will be in 0.20.0, improve the situation? At least the notification part should be improved, I'm not sure about the player
@Stypox Yes, it's alot better with background play, doesn't crash or freeze. Although with this version, it's more frequent videos won't load (infinite buffering on the info page, nothing else shows but the title) and to fix it the app needs to be force quit. Before it was 1/10 now it's like 1/4
But yeah the original problem is pretty much gone, thank you! Do I close the issue now or when the new version gets released officially?
Great! It's strange when issues are fixed without even knowing about them. :-D
Let's close this, as everything in that apk will be in the next version. Thank you for testing ;-)
Although with this version, it's more frequent videos won't load (infinite buffering on the info page, nothing else shows but the title) and to fix it the app needs to be force quit. Before it was 1/10 now it's like 1/4
For this, you should open a new issue. Although you should check for duplicates first.
Most helpful comment
@skil3z: Here is v0.14.2 with the following patches:
Diff