2
0
mirror of https://github.com/TeamNewPipe/NewPipe synced 2025-08-22 01:58:16 +00:00

Compare commits

...

11 Commits

Author SHA1 Message Date
Stypox
e9922fe162
Merge pull request #12470 from litetex/cleanup-PlayerHelper-localization 2025-07-28 15:30:07 +02:00
Stypox
eea2b7417e
Fix player formatters resetting too early on language change
formatters() is called again by the player before the user has a chance to click on the language in the language chooser.

So the correct solution would probably be to attach to https://developer.android.com/reference/android/content/Intent#ACTION_LOCALE_CHANGED, but let's keep it simple. I added `PlayerHelper.resetFormat();` in `ContentSettingsFragment.onDestroy()` and it works. It will mean the player formatters will be reset every time the user exits content settings, but whatever.
2025-07-28 15:29:06 +02:00
litetex
893a1cb699
Encapsulate Formatters in PlayerHelper
and reset them when the language is changed/changing.
This way they will be re-initialized on the next call.

Also Remove a bunch of outdated/non-thread safe code (STRING_FORMATTER)
2025-07-28 15:11:27 +02:00
litetex
ebd5e1a318
Remove unused method 2025-07-28 15:11:27 +02:00
litetex
70841db92f
Cleanup `Localization` formatting 2025-07-28 15:11:27 +02:00
litetex
859555e129
Use regions 2025-07-28 15:11:27 +02:00
Stypox
c1cef19b33
Merge pull request #12455 from TobiGr/nextPage-nullable 2025-07-28 14:52:08 +02:00
Stypox
9ba30887f9
Improve null checking further in SearchFragment.handleNextItems 2025-07-28 14:43:46 +02:00
Stypox
0ef38e3a4d
Merge pull request #12472 from TeamNewPipe/user-agent-140 2025-07-28 14:03:42 +02:00
TobiGr
a9ce2e9605 Update USER_AGENT to Firefox 140 ESR 2025-07-27 09:39:53 +02:00
TobiGr
30e33d59e8 Use correct fix for nextPage being null while creating error report in SearchFragment.handleNextItems() 2025-07-22 16:12:02 +02:00
5 changed files with 86 additions and 53 deletions

View File

@ -29,7 +29,7 @@ import okhttp3.ResponseBody;
public final class DownloaderImpl extends Downloader { public final class DownloaderImpl extends Downloader {
public static final String USER_AGENT = public static final String USER_AGENT =
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0"; "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0) Gecko/20100101 Firefox/140.0";
public static final String YOUTUBE_RESTRICTED_MODE_COOKIE_KEY = public static final String YOUTUBE_RESTRICTED_MODE_COOKIE_KEY =
"youtube_restricted_mode_key"; "youtube_restricted_mode_key";
public static final String YOUTUBE_RESTRICTED_MODE_COOKIE = "PREF=f2=8000000"; public static final String YOUTUBE_RESTRICTED_MODE_COOKIE = "PREF=f2=8000000";

View File

@ -1089,15 +1089,25 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
public void handleNextItems(final ListExtractor.InfoItemsPage<?> result) { public void handleNextItems(final ListExtractor.InfoItemsPage<?> result) {
showListFooter(false); showListFooter(false);
infoListAdapter.addInfoItemList(result.getItems()); infoListAdapter.addInfoItemList(result.getItems());
nextPage = result.getNextPage();
if (!result.getErrors().isEmpty() && nextPage != null) { if (!result.getErrors().isEmpty()) {
showSnackBarError(new ErrorInfo(result.getErrors(), UserAction.SEARCHED, // nextPage should be non-null at this point, because it refers to the page
"\"" + searchString + "\" → pageUrl: " + nextPage.getUrl() + ", " // whose results are handled here, but let's check it anyway
+ "pageIds: " + nextPage.getIds() + ", " if (nextPage == null) {
+ "pageCookies: " + nextPage.getCookies(), showSnackBarError(new ErrorInfo(result.getErrors(), UserAction.SEARCHED,
serviceId)); "\"" + searchString + "\" → nextPage == null", serviceId));
} else {
showSnackBarError(new ErrorInfo(result.getErrors(), UserAction.SEARCHED,
"\"" + searchString + "\" → pageUrl: " + nextPage.getUrl() + ", "
+ "pageIds: " + nextPage.getIds() + ", "
+ "pageCookies: " + nextPage.getCookies(),
serviceId));
}
} }
// keep the reassignment of nextPage after the error handling to ensure that nextPage
// still holds the correct value during the error handling
nextPage = result.getNextPage();
super.handleNextItems(result); super.handleNextItems(result);
} }

View File

@ -33,11 +33,9 @@ import com.google.android.exoplayer2.trackselection.ExoTrackSelection;
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout.ResizeMode; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout.ResizeMode;
import com.google.android.exoplayer2.ui.CaptionStyleCompat; import com.google.android.exoplayer2.ui.CaptionStyleCompat;
import com.google.android.exoplayer2.util.MimeTypes;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.stream.SubtitlesStream; import org.schabi.newpipe.extractor.stream.SubtitlesStream;
@ -47,13 +45,14 @@ import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.player.playqueue.PlayQueueItem; import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import org.schabi.newpipe.player.playqueue.SinglePlayQueue; import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
import org.schabi.newpipe.util.ListHelper; import org.schabi.newpipe.util.ListHelper;
import org.schabi.newpipe.util.Localization;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat; import java.text.NumberFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Formatter;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -62,11 +61,7 @@ import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public final class PlayerHelper { public final class PlayerHelper {
private static final StringBuilder STRING_BUILDER = new StringBuilder(); private static final FormattersProvider FORMATTERS_PROVIDER = new FormattersProvider();
private static final Formatter STRING_FORMATTER =
new Formatter(STRING_BUILDER, Locale.getDefault());
private static final NumberFormat SPEED_FORMATTER = new DecimalFormat("0.##x");
private static final NumberFormat PITCH_FORMATTER = new DecimalFormat("##%");
@Retention(SOURCE) @Retention(SOURCE)
@IntDef({AUTOPLAY_TYPE_ALWAYS, AUTOPLAY_TYPE_WIFI, @IntDef({AUTOPLAY_TYPE_ALWAYS, AUTOPLAY_TYPE_WIFI,
@ -89,9 +84,11 @@ public final class PlayerHelper {
private PlayerHelper() { private PlayerHelper() {
} }
//////////////////////////////////////////////////////////////////////////// // region Exposed helpers
// Exposed helpers
//////////////////////////////////////////////////////////////////////////// public static void resetFormat() {
FORMATTERS_PROVIDER.reset();
}
@NonNull @NonNull
public static String getTimeString(final int milliSeconds) { public static String getTimeString(final int milliSeconds) {
@ -100,35 +97,24 @@ public final class PlayerHelper {
final int hours = (milliSeconds % 86400000) / 3600000; final int hours = (milliSeconds % 86400000) / 3600000;
final int days = (milliSeconds % (86400000 * 7)) / 86400000; final int days = (milliSeconds % (86400000 * 7)) / 86400000;
STRING_BUILDER.setLength(0); final Formatters formatters = FORMATTERS_PROVIDER.formatters();
return (days > 0 if (days > 0) {
? STRING_FORMATTER.format("%d:%02d:%02d:%02d", days, hours, minutes, seconds) return formatters.stringFormat("%d:%02d:%02d:%02d", days, hours, minutes, seconds);
: hours > 0 }
? STRING_FORMATTER.format("%d:%02d:%02d", hours, minutes, seconds)
: STRING_FORMATTER.format("%02d:%02d", minutes, seconds) return hours > 0
).toString(); ? formatters.stringFormat("%d:%02d:%02d", hours, minutes, seconds)
: formatters.stringFormat("%02d:%02d", minutes, seconds);
} }
@NonNull @NonNull
public static String formatSpeed(final double speed) { public static String formatSpeed(final double speed) {
return SPEED_FORMATTER.format(speed); return FORMATTERS_PROVIDER.formatters().speed().format(speed);
} }
@NonNull @NonNull
public static String formatPitch(final double pitch) { public static String formatPitch(final double pitch) {
return PITCH_FORMATTER.format(pitch); return FORMATTERS_PROVIDER.formatters().pitch().format(pitch);
}
@NonNull
public static String subtitleMimeTypesOf(@NonNull final MediaFormat format) {
switch (format) {
case VTT:
return MimeTypes.TEXT_VTT;
case TTML:
return MimeTypes.APPLICATION_TTML;
default:
throw new IllegalArgumentException("Unrecognized mime type: " + format.name());
}
} }
@NonNull @NonNull
@ -219,9 +205,8 @@ public final class PlayerHelper {
? null : getAutoQueuedSinglePlayQueue(autoQueueItems.get(0)); ? null : getAutoQueuedSinglePlayQueue(autoQueueItems.get(0));
} }
//////////////////////////////////////////////////////////////////////////// // endregion
// Settings Resolution // region Resolution
////////////////////////////////////////////////////////////////////////////
public static boolean isResumeAfterAudioFocusGain(@NonNull final Context context) { public static boolean isResumeAfterAudioFocusGain(@NonNull final Context context) {
return getPreferences(context) return getPreferences(context)
@ -405,9 +390,8 @@ public final class PlayerHelper {
return Integer.parseInt(preferredIntervalBytes) * 1024; return Integer.parseInt(preferredIntervalBytes) * 1024;
} }
//////////////////////////////////////////////////////////////////////////// // endregion
// Private helpers // region Private helpers
////////////////////////////////////////////////////////////////////////////
@NonNull @NonNull
private static SharedPreferences getPreferences(@NonNull final Context context) { private static SharedPreferences getPreferences(@NonNull final Context context) {
@ -427,9 +411,8 @@ public final class PlayerHelper {
} }
//////////////////////////////////////////////////////////////////////////// // endregion
// Utils used by player // region Utils used by player
////////////////////////////////////////////////////////////////////////////
@RepeatMode @RepeatMode
public static int nextRepeatMode(@RepeatMode final int repeatMode) { public static int nextRepeatMode(@RepeatMode final int repeatMode) {
@ -503,4 +486,43 @@ public final class PlayerHelper {
player.getContext().getString(R.string.seek_duration_key), player.getContext().getString(R.string.seek_duration_key),
player.getContext().getString(R.string.seek_duration_default_value)))); player.getContext().getString(R.string.seek_duration_default_value))));
} }
// endregion
// region Format
static class FormattersProvider {
private Formatters formatters;
public Formatters formatters() {
if (formatters == null) {
formatters = Formatters.create();
}
return formatters;
}
public void reset() {
formatters = null;
}
}
record Formatters(
Locale locale,
NumberFormat speed,
NumberFormat pitch) {
static Formatters create() {
final Locale locale = Localization.getAppLocale();
final DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(locale);
return new Formatters(
locale,
new DecimalFormat("0.##x", dfs),
new DecimalFormat("##%", dfs));
}
String stringFormat(final String format, final Object... args) {
return String.format(locale, format, args);
}
}
// endregion
} }

View File

@ -16,6 +16,7 @@ import androidx.preference.Preference;
import org.schabi.newpipe.DownloaderImpl; import org.schabi.newpipe.DownloaderImpl;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.player.helper.PlayerHelper;
import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.image.ImageStrategy; import org.schabi.newpipe.util.image.ImageStrategy;
import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.util.image.PicassoHelper;
@ -107,5 +108,6 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
NewPipe.setupLocalization( NewPipe.setupLocalization(
Localization.getPreferredLocalization(context), Localization.getPreferredLocalization(context),
Localization.getPreferredContentCountry(context)); Localization.getPreferredContentCountry(context));
PlayerHelper.resetFormat();
} }
} }

View File

@ -127,14 +127,13 @@ public final class Localization {
} }
public static String localizeNumber(final double number) { public static String localizeNumber(final double number) {
final NumberFormat nf = NumberFormat.getInstance(getAppLocale()); return NumberFormat.getInstance(getAppLocale()).format(number);
return nf.format(number);
} }
public static String formatDate(@NonNull final OffsetDateTime offsetDateTime) { public static String formatDate(@NonNull final OffsetDateTime offsetDateTime) {
return DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM) return DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)
.withLocale(getAppLocale()).format(offsetDateTime .withLocale(getAppLocale())
.atZoneSameInstant(ZoneId.systemDefault())); .format(offsetDateTime.atZoneSameInstant(ZoneId.systemDefault()));
} }
@SuppressLint("StringFormatInvalid") @SuppressLint("StringFormatInvalid")