Merge remote-tracking branch 'proxy-upstream/master' into 15-feature-proxy-direct-merged

Directly merged, not reviewed code yet ! Use it in your own caution !
This commit is contained in:
Aliberk Sandıkçı 2025-01-10 18:16:00 +03:00
commit 66eb459cb9
11 changed files with 402 additions and 9 deletions

View file

@ -77,7 +77,7 @@ graph TD
<!-- <details><summary>Tubular Readme</summary>
<h1 align="center"><b>Tubular</b></h2>
<h4 align="center">A fork of <a href="https://newpipe.net/">NewPipe</a> (<a href="https://github.com/TeamNewPipe/NewPipe/">Github</a>) that implements <a href="https://sponsor.ajay.app/">SponsorBlock</a> (<a href="https://github.com/ajayyy/SponsorBlock/">Github</a>) and <a href="https://www.returnyoutubedislike.com/">ReturnYouTubeDislike</a> (<a href="https://github.com/Anarios/return-youtube-dislike/">Github</a>).</h4>
<p align="center">Download the APK <a href="https://github.com/polymorphicshade/Tubular/releases/latest">here</a>.</p>
<p align="center">Download the APK <a href="https://github.com/grisha765/Tubular-Proxy/releases/latest">here</a>.</p>
<p align="center"><img src="doc/gif/preview_01.gif" width="400"></p>
## To Do

View file

@ -1,6 +1,8 @@
package org.schabi.newpipe;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@ -22,6 +24,8 @@ import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.net.Proxy;
import java.net.InetSocketAddress;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
@ -40,11 +44,45 @@ public final class DownloaderImpl extends Downloader {
private final OkHttpClient client;
private DownloaderImpl(final OkHttpClient.Builder builder) {
final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(
App.getApp()
);
final boolean isProxyEnabled = sharedPreferences.getBoolean(
App.getApp().getString(R.string.proxy_enabled_key), false);
Log.d("DownloaderImpl_ProxySettings", "Read proxy_enabled_key: " + isProxyEnabled);
if (isProxyEnabled) {
Log.d("DownloaderImpl_ProxySettings",
"Update called. proxy_enabled_key on: " + isProxyEnabled);
final String proxyAddress = sharedPreferences.getString(
App.getApp().getString(R.string.proxy_address_key), "192.168.1.1");
final String proxyPortStr = sharedPreferences.getString(
App.getApp().getString(R.string.proxy_port_key), "1080");
final String proxyTypeStr = sharedPreferences.getString(
App.getApp().getString(R.string.proxy_type_key), "socks");
final Proxy.Type proxyType = "http".equalsIgnoreCase(proxyTypeStr)
? Proxy.Type.HTTP
: Proxy.Type.SOCKS;
final int proxyPort = Integer.parseInt(proxyPortStr);
Log.d("DownloaderImpl_ProxySettings",
"Proxy enabled with address: " + proxyAddress
+ " and port: " + proxyPort + ", type: " + proxyType);
final Proxy proxy = new Proxy(proxyType,
new InetSocketAddress(proxyAddress, proxyPort));
this.client = builder
.proxy(proxy)
.readTimeout(30, TimeUnit.SECONDS)
// .cache(new Cache(new File(context.getExternalCacheDir(), "okhttp"),
// 16 * 1024 * 1024))
.build();
} else {
Log.d("DownloaderImpl_ProxySettings",
"Update called. proxy_enabled_key off: " + isProxyEnabled);
this.client = builder
.readTimeout(30, TimeUnit.SECONDS)
// .cache(new Cache(new File(context.getExternalCacheDir(), "okhttp"),
// 16 * 1024 * 1024))
.build();
}
this.mCookies = new HashMap<>();
}

View file

@ -25,6 +25,10 @@ import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.content.SharedPreferences;
import androidx.preference.PreferenceManager;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.PlaybackException;
import com.google.android.exoplayer2.upstream.BaseDataSource;
@ -44,6 +48,8 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import com.google.common.net.HttpHeaders;
import org.schabi.newpipe.App;
import org.schabi.newpipe.R;
import org.schabi.newpipe.DownloaderImpl;
import java.io.IOException;
@ -61,6 +67,9 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.zip.GZIPInputStream;
import java.net.Proxy;
import java.net.InetSocketAddress;
/**
* An {@link HttpDataSource} that uses Android's {@link HttpURLConnection}, based on
@ -713,7 +722,38 @@ public final class YoutubeHttpDataSource extends BaseDataSource implements HttpD
* @return an {@link HttpURLConnection} created with the {@code url}
*/
private HttpURLConnection openConnection(@NonNull final URL url) throws IOException {
return (HttpURLConnection) url.openConnection();
final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(
App.getApp()
);
final boolean isProxyEnabled = sharedPreferences.getBoolean(
App.getApp().getString(R.string.proxy_enabled_key), false);
android.util.Log.d("YoutubeHttpDataSource_ProxySettings",
"Read proxy_enabled_key: " + isProxyEnabled);
if (isProxyEnabled) {
android.util.Log.d("YoutubeHttpDataSource_ProxySettings",
"Update called. proxy_enabled_key on: " + isProxyEnabled);
final String proxyAddress = sharedPreferences.getString(
App.getApp().getString(R.string.proxy_address_key), "192.168.1.1");
final String proxyPortStr = sharedPreferences.getString(
App.getApp().getString(R.string.proxy_port_key), "1080");
final String proxyTypeStr = sharedPreferences.getString(
App.getApp().getString(R.string.proxy_type_key), "socks");
final Proxy.Type proxyType = "http".equalsIgnoreCase(proxyTypeStr)
? Proxy.Type.HTTP
: Proxy.Type.SOCKS;
final int proxyPort = Integer.parseInt(proxyPortStr);
android.util.Log.d("YoutubeHttpDataSource_ProxySettings",
"Proxy enabled with address: "
+ proxyAddress + " and port: " + proxyPort + ", type: " + proxyType);
final Proxy proxy = new Proxy(proxyType,
new InetSocketAddress(proxyAddress, proxyPort));
connection = (HttpURLConnection) url.openConnection(proxy);
} else {
android.util.Log.d("YoutubeHttpDataSource_ProxySettings",
"Update called. proxy_enabled_key off: " + isProxyEnabled);
connection = (HttpURLConnection) url.openConnection();
}
return connection;
}
/**

View file

@ -0,0 +1,163 @@
package org.schabi.newpipe.settings;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.util.Patterns;
import android.widget.Toast;
import androidx.preference.Preference;
import androidx.preference.ListPreference;
import androidx.preference.SwitchPreferenceCompat;
import androidx.preference.PreferenceManager;
import org.schabi.newpipe.App;
import org.schabi.newpipe.R;
public class ProxySettingsFragment extends BasePreferenceFragment {
@Override
public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) {
// Подключаем preferences файл для Proxy
addPreferencesFromResource(R.xml.proxy_settings);
// Получаем SharedPreferences
final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(
App.getApp()
);
// Настройка включения прокси
final SwitchPreferenceCompat proxyEnablePref = findPreference(
App.getApp().getString(R.string.proxy_enabled_key)
);
assert proxyEnablePref != null;
proxyEnablePref.setOnPreferenceChangeListener((preference, newValue) -> {
final boolean isEnabled = (Boolean) newValue;
Log.d("ProxySettings", "Read proxy_enabled_key: " + isEnabled);
// Сохраняем новое значение proxy_enabled_key
sharedPreferences.edit().putBoolean(
App.getApp().getString(R.string.proxy_enabled_key),
isEnabled).apply();
Log.d("ProxySettings",
"Saved proxy_enabled_key: " + sharedPreferences.getBoolean(
App.getApp().getString(R.string.proxy_enabled_key), false));
// Сообщаем пользователю, что требуется перезапуск
Toast.makeText(getContext(),
getString(R.string.proxy_restart_required), Toast.LENGTH_LONG).show();
return true;
});
// Настройка адреса прокси
final Preference proxyAddressPref = findPreference(
App.getApp().getString(R.string.proxy_address_key)
);
assert proxyAddressPref != null;
proxyAddressPref.setOnPreferenceChangeListener((preference, newValue) -> {
// Валидация IP-адреса
if (!isValidIpAddress(newValue.toString())) {
Toast.makeText(getContext(),
getString(R.string.invalid_ip_address), Toast.LENGTH_SHORT).show();
return false; // Не сохраняем изменение
}
Log.d("ProxySettings", "Read proxy_address_key: " + newValue);
// Сохраняем новое значение IP-адреса
sharedPreferences.edit().putString(
App.getApp().getString(R.string.proxy_address_key),
newValue.toString()).apply();
// Обновляем summary с новым адресом
proxyAddressPref.setSummary(
getString(R.string.proxy_address_summary, newValue)
);
// Сообщаем пользователю, что требуется перезапуск
Toast.makeText(getContext(),
getString(R.string.proxy_restart_required), Toast.LENGTH_LONG).show();
return true;
});
// Устанавливаем текущее значение в summary при загрузке настроек
final String currentAddress = sharedPreferences.getString(
App.getApp().getString(R.string.proxy_address_key), "192.168.1.1"
);
proxyAddressPref.setSummary(
getString(R.string.proxy_address_summary, currentAddress)
);
// Настройка порта прокси
final Preference proxyPortPref = findPreference(
App.getApp().getString(R.string.proxy_port_key)
);
assert proxyPortPref != null;
proxyPortPref.setOnPreferenceChangeListener((preference, newValue) -> {
// Валидация порта
if (!isValidPort(newValue.toString())) {
Toast.makeText(getContext(),
getString(R.string.invalid_port), Toast.LENGTH_SHORT).show();
return false; // Не сохраняем изменение
}
Log.d("ProxySettings", "Read proxy_port_key: " + newValue);
// Сохраняем новое значение порта
sharedPreferences.edit().putString(
App.getApp().getString(R.string.proxy_port_key),
newValue.toString()).apply();
// Обновляем summary с новым портом
proxyPortPref.setSummary(
getString(R.string.proxy_port_summary, newValue)
);
// Сообщаем пользователю, что требуется перезапуск
Toast.makeText(getContext(),
getString(R.string.proxy_restart_required), Toast.LENGTH_LONG).show();
return true;
});
// Устанавливаем текущее значение в summary при загрузке настроек
final String currentPort = sharedPreferences.getString(
App.getApp().getString(R.string.proxy_port_key), "1080"
);
proxyPortPref.setSummary(
getString(R.string.proxy_port_summary, currentPort)
);
// Настройка типа прокси
final ListPreference proxyTypePref = findPreference(
App.getApp().getString(R.string.proxy_type_key)
);
assert proxyTypePref != null;
proxyTypePref.setOnPreferenceChangeListener((preference, newValue) -> {
final String proxyType = newValue.toString();
Log.d("ProxySettings", "Read proxy_type_key: " + proxyType);
// Сохраняем новое значение типа прокси
sharedPreferences.edit().putString(
App.getApp().getString(R.string.proxy_type_key),
proxyType).apply();
// Обновляем summary
proxyTypePref.setSummary(
getString(R.string.proxy_type_summary) + ": " + proxyType.toUpperCase()
);
// Сообщаем пользователю, что требуется перезапуск
Toast.makeText(getContext(),
getString(R.string.proxy_restart_required), Toast.LENGTH_LONG).show();
return true;
});
// Устанавливаем текущее значение в summary при загрузке настроек
final String currentProxyType = sharedPreferences.getString(
App.getApp().getString(R.string.proxy_type_key), "socks"
);
proxyTypePref.setSummary(
getString(R.string.proxy_type_summary) + ": " + currentProxyType.toUpperCase()
);
}
// Метод для валидации IP-адреса
public boolean isValidIpAddress(final String ipAddress) {
return Patterns.IP_ADDRESS.matcher(ipAddress).matches();
}
// Метод для валидации порта
public boolean isValidPort(final String port) {
try {
final int portNumber = Integer.parseInt(port);
return portNumber >= 1 && portNumber <= 65535;
} catch (final NumberFormatException e) {
return false;
}
}
}

View file

@ -6,9 +6,12 @@ import static org.schabi.newpipe.util.image.ImageStrategy.choosePreferredImage;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.util.Log;
import androidx.preference.PreferenceManager;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@ -28,6 +31,8 @@ import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.net.Proxy;
import java.net.InetSocketAddress;
import okhttp3.OkHttpClient;
@ -45,17 +50,45 @@ public final class PicassoHelper {
// suppress because terminate() is called in App.onTerminate(), preventing leaks
@SuppressLint("StaticFieldLeak")
private static Picasso picassoInstance;
private static Proxy proxySet;
public static void init(final Context context) {
picassoCache = new LruCache(10 * 1024 * 1024);
final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(
context
);
final boolean isProxyEnabled = sharedPreferences.getBoolean(
context.getString(R.string.proxy_enabled_key), false);
Log.d("PicassoHelper_ProxySettings", "Read proxy_enabled_key: " + isProxyEnabled);
if (isProxyEnabled) {
Log.d("PicassoHelper_ProxySettings",
"Update called. proxy_enabled_key on: " + isProxyEnabled);
final String proxyAddress = sharedPreferences.getString(
context.getString(R.string.proxy_address_key), "192.168.1.1");
final String proxyPortStr = sharedPreferences.getString(
context.getString(R.string.proxy_port_key), "1080");
final String proxyTypeStr = sharedPreferences.getString(
context.getString(R.string.proxy_type_key), "socks");
final Proxy.Type proxyType = "http".equalsIgnoreCase(proxyTypeStr)
? Proxy.Type.HTTP
: Proxy.Type.SOCKS;
final int proxyPort = Integer.parseInt(proxyPortStr);
Log.d("PicassoHelper_ProxySettings",
"Proxy enabled with address: "
+ proxyAddress + " and port: " + proxyPort + ", type: " + proxyType);
proxySet = new Proxy(proxyType, new InetSocketAddress(proxyAddress, proxyPort));
} else {
Log.d("PicassoHelper_ProxySettings",
"Update called. proxy_enabled_key off: " + isProxyEnabled);
}
picassoDownloaderClient = new OkHttpClient.Builder()
.proxy(proxySet)
.cache(new okhttp3.Cache(new File(context.getExternalCacheDir(), "picasso"),
50L * 1024L * 1024L))
// this should already be the default timeout in OkHttp3, but just to be sure...
.callTimeout(15, TimeUnit.SECONDS)
.build();
picassoInstance = new Picasso.Builder(context)
.memoryCache(picassoCache) // memory cache
.downloader(new OkHttp3Downloader(picassoDownloaderClient)) // disk cache

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="@color/defaultIconTint"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M7 14C5.9 14 5 13.1 5 12S5.9 10 7 10 9 10.9 9 12 8.1 14 7 14M12.6 10C11.8 7.7 9.6 6 7 6C3.7 6 1 8.7 1 12S3.7 18 7 18C9.6 18 11.8 16.3 12.6 14H16V18H20V14H23V10H12.6Z" />
</vector>

View file

@ -844,4 +844,28 @@
\n
\nВы уверены, что хотите продолжить?</string>
<string name="import_settings_vulnerable_format">Настройки в импортируемом экспорте используют уязвимый формат, который устарел с версии NewPipe 0.27.0. Убедитесь, что импортируемый экспорт получен из надёжного источника, и в будущем предпочтительнее использовать только экспорт, полученный из NewPipe 0.27.0 или новее. Поддержка импорта настроек в этом уязвимом формате скоро будет полностью удалена, и тогда старые версии NewPipe больше не смогут импортировать настройки из экспорта из новых версий.</string>
<!-- Proxy Settings -->
<string name="proxy_settings_title">Настройки прокси</string>
<string name="proxy_enabled_title">Включить\\Отключить прокси</string>
<string name="proxy_enabled_summary">Включите или отключите использование прокси-сервера.</string>
<string name="proxy_enabled_key">proxy_enabled</string>
<string name="proxy_type_title">Выбрать тип прокси</string>
<string name="proxy_type_summary">Тип прокси</string>
<string name="proxy_type_key">proxy_type</string>
<string name="proxy_address_title">IP Адрес</string>
<string name="proxy_address_summary">Текущий адрес: %1$s</string>
<string name="proxy_address_key">proxy_address</string>
<string name="invalid_ip_address">Неверный IPv4 адрес</string>
<string name="proxy_port_title">Порт</string>
<string name="proxy_port_summary">Текущий порт: %1$s</string>
<string name="proxy_port_key">proxy_port</string>
<string name="invalid_port">Неверный порт. Порт должен быть от 1 до 65535</string>
<string name="proxy_restart_required">Перезапустите приложение для применения настроек прокси.</string>
</resources>

View file

@ -1526,4 +1526,16 @@
<string name="return_youtube_dislike_override_like_count_key" translatable="false">return_youtube_dislike_override_like_count</string>
<string name="return_youtube_dislike_override_view_count_key" translatable="false">return_youtube_dislike_override_view_count</string>
<string name="return_youtube_dislike_show_dislikes_as_percentage_key" translatable="false">return_youtube_dislike_show_dislikes_as_percentage</string>
<!-- proxy_types -->
<string-array name="proxy_type_entries">
<item>SOCKS Proxy</item>
<item>HTTP Proxy</item>
</string-array>
<string-array name="proxy_type_values">
<item>socks</item>
<item>http</item>
</string-array>
</resources>

View file

@ -860,6 +860,28 @@
<string name="show_more">Show more</string>
<string name="show_less">Show less</string>
<string name="import_settings_vulnerable_format">The settings in the export being imported use a vulnerable format that was deprecated since NewPipe 0.27.0. Make sure the export being imported is from a trusted source, and prefer using only exports obtained from NewPipe 0.27.0 or newer in the future. Support for importing settings in this vulnerable format will soon be removed completely, and then old versions of NewPipe will not be able to import settings of exports from new versions anymore.</string>
<!-- Proxy Settings -->
<string name="proxy_settings_title">Proxy Settings</string>
<string name="proxy_enabled_title">Enable\\Disable Proxy</string>
<string name="proxy_enabled_summary">Turn on or off the use of a proxy server for connections.</string>
<string name="proxy_enabled_key">proxy_enabled</string>
<string name="proxy_type_title">Select proxy type</string>
<string name="proxy_type_summary">Proxy type</string>
<string name="proxy_type_key">proxy_type</string>
<string name="proxy_address_title">IP Address</string>
<string name="proxy_address_summary">Current address: %1$s</string>
<string name="proxy_address_key">proxy_address</string>
<string name="invalid_ip_address">Invalid IPv4 address</string>
<string name="proxy_port_title">Port</string>
<string name="proxy_port_summary">Current port: %1$s</string>
<string name="proxy_port_key">proxy_port</string>
<string name="invalid_port">Invalid port. Port must be between 1 and 65535</string>
<string name="proxy_restart_required">Please restart the app to apply the proxy settings.</string>
<!-- SponsorBlock -->
<string name="sponsor_block">SponsorBlock</string>
<string name="sponsor_block_category_sponsor">Sponsor</string>

View file

@ -65,10 +65,17 @@
android:title="ReturnYouTubeDislike"
app:iconSpaceReserved="false" />
<PreferenceScreen
android:fragment="org.schabi.newpipe.settings.ProxySettingsFragment"
android:icon="@drawable/ic_key"
android:title="@string/proxy_settings_title"
app:iconSpaceReserved="false" />
<PreferenceScreen
android:fragment="org.schabi.newpipe.settings.DebugSettingsFragment"
android:icon="@drawable/ic_bug_report"
android:key="@string/debug_pref_screen_key"
android:title="@string/settings_category_debug_title"
app:iconSpaceReserved="false" />
</PreferenceScreen>

View file

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:title="@string/proxy_settings_title">
<SwitchPreferenceCompat
app:iconSpaceReserved="false"
android:key="proxy_enabled"
android:title="@string/proxy_enabled_title"
android:summary="@string/proxy_enabled_summary"
android:defaultValue="false" />
<ListPreference
app:iconSpaceReserved="false"
android:key="@string/proxy_type_key"
android:title="@string/proxy_type_title"
android:summary="@string/proxy_type_summary"
android:entries="@array/proxy_type_entries"
android:entryValues="@array/proxy_type_values"
android:defaultValue="socks"
android:dependency="proxy_enabled" />
<EditTextPreference
app:iconSpaceReserved="false"
android:key="proxy_address"
android:title="@string/proxy_address_title"
android:summary="@string/proxy_address_summary"
android:dependency="proxy_enabled"
android:defaultValue="192.168.1.1"
android:inputType="text" />
<EditTextPreference
app:iconSpaceReserved="false"
android:key="proxy_port"
android:title="@string/proxy_port_title"
android:summary="@string/proxy_port_summary"
android:dependency="proxy_enabled"
android:defaultValue="1080"
android:inputType="number" />
</PreferenceScreen>