mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-10-03 02:40:52 +02:00
Compare commits
51 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c6e1721884 | ||
![]() |
94684fe380 | ||
![]() |
398a2f55ce | ||
![]() |
1f7b3b5b06 | ||
![]() |
909ed616c4 | ||
![]() |
dd223af28d | ||
![]() |
dbee8d8128 | ||
![]() |
b62a09b5b3 | ||
![]() |
87317c6faf | ||
![]() |
53b599b042 | ||
![]() |
21df24abfd | ||
![]() |
ca4592a935 | ||
![]() |
3fc487310b | ||
![]() |
056809cb0d | ||
![]() |
a60bb3e7af | ||
![]() |
ecd3f6c2ee | ||
![]() |
70ff47b810 | ||
![]() |
b8e050f6c4 | ||
![]() |
46d0bc1004 | ||
![]() |
e7fe84f2c7 | ||
![]() |
2b183a0576 | ||
![]() |
f856bd9306 | ||
![]() |
0066b322e1 | ||
![]() |
3bdae81c0a | ||
![]() |
6010c4ea7f | ||
![]() |
690b3410e9 | ||
![]() |
ba86ce137b | ||
![]() |
410c01547c | ||
![]() |
47263f5254 | ||
![]() |
01bf855015 | ||
![]() |
ebf3008729 | ||
![]() |
33ecfb757e | ||
![]() |
ffe26d882b | ||
![]() |
83f8141fe7 | ||
![]() |
9253640fae | ||
![]() |
8b5aa5cd9b | ||
![]() |
58393ad4ef | ||
![]() |
977f7e28b5 | ||
![]() |
99e77249de | ||
![]() |
a955408053 | ||
![]() |
86203d6800 | ||
![]() |
edd19641ac | ||
![]() |
65749cbac0 | ||
![]() |
658ddfc921 | ||
![]() |
f7d0fd545d | ||
![]() |
27e6be792f | ||
![]() |
3fc0147f47 | ||
![]() |
c6b05c6094 | ||
![]() |
240a2fe36b | ||
![]() |
de46e3abb3 | ||
![]() |
70748fa0bc |
2
.github/workflows/build-release-apk.yml
vendored
2
.github/workflows/build-release-apk.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '17'
|
||||
java-version: '21'
|
||||
cache: 'gradle'
|
||||
|
||||
- name: "Build release APK"
|
||||
|
4
.github/workflows/image-minimizer.js
vendored
4
.github/workflows/image-minimizer.js
vendored
@@ -32,8 +32,8 @@ module.exports = async ({github, context}) => {
|
||||
}
|
||||
|
||||
// Regex for finding images (simple variant) 
|
||||
const REGEX_USER_CONTENT_IMAGE_LOOKUP = /\!\[(.*)\]\((https:\/\/[-a-z0-9]+\.githubusercontent\.com\/\d+\/[-0-9a-f]{32,512}\.(jpg|gif|png))\)/gm;
|
||||
const REGEX_ASSETS_IMAGE_LOCKUP = /\!\[(.*)\]\((https:\/\/github\.com\/[-\w\d]+\/[-\w\d]+\/assets\/\d+\/[\-0-9a-f]{32,512})\)/gm;
|
||||
const REGEX_USER_CONTENT_IMAGE_LOOKUP = /\!\[([^\]]*)\]\((https:\/\/[-a-z0-9]+\.githubusercontent\.com\/\d+\/[-0-9a-f]{32,512}\.(jpg|gif|png))\)/gm;
|
||||
const REGEX_ASSETS_IMAGE_LOCKUP = /\!\[([^\]]*)\]\((https:\/\/github\.com\/[-\w\d]+\/[-\w\d]+\/assets\/\d+\/[\-0-9a-f]{32,512})\)/gm;
|
||||
|
||||
// Check if we found something
|
||||
let foundSimpleImages = REGEX_USER_CONTENT_IMAGE_LOOKUP.test(initialBody)
|
||||
|
@@ -23,9 +23,9 @@ android {
|
||||
if (System.properties.containsKey('versionCodeOverride')) {
|
||||
versionCode System.getProperty('versionCodeOverride') as Integer
|
||||
} else {
|
||||
versionCode 1001
|
||||
versionCode 1003
|
||||
}
|
||||
versionName "0.27.4"
|
||||
versionName "0.27.6"
|
||||
if (System.properties.containsKey('versionNameSuffix')) {
|
||||
versionNameSuffix System.getProperty('versionNameSuffix')
|
||||
}
|
||||
@@ -208,7 +208,7 @@ dependencies {
|
||||
implementation 'com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751'
|
||||
// WORKAROUND: if you get errors with the NewPipeExtractor dependency, replace `v0.24.3` with
|
||||
// the corresponding commit hash, since JitPack is sometimes buggy
|
||||
implementation 'com.github.TeamNewPipe:NewPipeExtractor:v0.24.3'
|
||||
implementation 'com.github.TeamNewPipe:NewPipeExtractor:9f83b385a'
|
||||
implementation 'com.github.TeamNewPipe:NoNonsense-FilePicker:5.0.0'
|
||||
|
||||
/** Checkstyle **/
|
||||
@@ -241,6 +241,7 @@ dependencies {
|
||||
implementation "androidx.work:work-runtime-ktx:${androidxWorkVersion}"
|
||||
implementation "androidx.work:work-rxjava3:${androidxWorkVersion}"
|
||||
implementation 'com.google.android.material:material:1.11.0'
|
||||
implementation "androidx.webkit:webkit:1.9.0"
|
||||
|
||||
/** Third-party libraries **/
|
||||
// Instance state boilerplate elimination
|
||||
|
127
app/src/main/assets/po_token.html
Normal file
127
app/src/main/assets/po_token.html
Normal file
@@ -0,0 +1,127 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en"><head><title></title><script>
|
||||
/**
|
||||
* Factory method to create and load a BotGuardClient instance.
|
||||
* @param options - Configuration options for the BotGuardClient.
|
||||
* @returns A promise that resolves to a loaded BotGuardClient instance.
|
||||
*/
|
||||
function loadBotGuard(challengeData) {
|
||||
this.vm = this[challengeData.globalName];
|
||||
this.program = challengeData.program;
|
||||
this.vmFunctions = {};
|
||||
this.syncSnapshotFunction = null;
|
||||
|
||||
if (!this.vm)
|
||||
throw new Error('[BotGuardClient]: VM not found in the global object');
|
||||
|
||||
if (!this.vm.a)
|
||||
throw new Error('[BotGuardClient]: Could not load program');
|
||||
|
||||
const vmFunctionsCallback = function (
|
||||
asyncSnapshotFunction,
|
||||
shutdownFunction,
|
||||
passEventFunction,
|
||||
checkCameraFunction
|
||||
) {
|
||||
this.vmFunctions = {
|
||||
asyncSnapshotFunction: asyncSnapshotFunction,
|
||||
shutdownFunction: shutdownFunction,
|
||||
passEventFunction: passEventFunction,
|
||||
checkCameraFunction: checkCameraFunction
|
||||
};
|
||||
};
|
||||
|
||||
this.syncSnapshotFunction = this.vm.a(this.program, vmFunctionsCallback, true, this.userInteractionElement, function () {/** no-op */ }, [ [], [] ])[0]
|
||||
|
||||
// an asynchronous function runs in the background and it will eventually call
|
||||
// `vmFunctionsCallback`, however we need to manually tell JavaScript to pass
|
||||
// control to the things running in the background by interrupting this async
|
||||
// function in any way, e.g. with a delay of 1ms. The loop is most probably not
|
||||
// needed but is there just because.
|
||||
return new Promise(function (resolve, reject) {
|
||||
i = 0
|
||||
refreshIntervalId = setInterval(function () {
|
||||
if (!!this.vmFunctions.asyncSnapshotFunction) {
|
||||
resolve(this)
|
||||
clearInterval(refreshIntervalId);
|
||||
}
|
||||
if (i >= 10000) {
|
||||
reject("asyncSnapshotFunction is null even after 10 seconds")
|
||||
clearInterval(refreshIntervalId);
|
||||
}
|
||||
i += 1;
|
||||
}, 1);
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a snapshot asynchronously.
|
||||
* @returns The snapshot result.
|
||||
* @example
|
||||
* ```ts
|
||||
* const result = await botguard.snapshot({
|
||||
* contentBinding: {
|
||||
* c: "a=6&a2=10&b=SZWDwKVIuixOp7Y4euGTgwckbJA&c=1729143849&d=1&t=7200&c1a=1&c6a=1&c6b=1&hh=HrMb5mRWTyxGJphDr0nW2Oxonh0_wl2BDqWuLHyeKLo",
|
||||
* e: "ENGAGEMENT_TYPE_VIDEO_LIKE",
|
||||
* encryptedVideoId: "P-vC09ZJcnM"
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* console.log(result);
|
||||
* ```
|
||||
*/
|
||||
function snapshot(args) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
if (!this.vmFunctions.asyncSnapshotFunction)
|
||||
return reject(new Error('[BotGuardClient]: Async snapshot function not found'));
|
||||
|
||||
this.vmFunctions.asyncSnapshotFunction(function (response) { resolve(response) }, [
|
||||
args.contentBinding,
|
||||
args.signedTimestamp,
|
||||
args.webPoSignalOutput,
|
||||
args.skipPrivacyBuffer
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
function runBotGuard(challengeData) {
|
||||
const interpreterJavascript = challengeData.interpreterJavascript.privateDoNotAccessOrElseSafeScriptWrappedValue;
|
||||
|
||||
if (interpreterJavascript) {
|
||||
new Function(interpreterJavascript)();
|
||||
} else throw new Error('Could not load VM');
|
||||
|
||||
const webPoSignalOutput = [];
|
||||
return loadBotGuard({
|
||||
globalName: challengeData.globalName,
|
||||
globalObj: this,
|
||||
program: challengeData.program
|
||||
}).then(function (botguard) {
|
||||
return botguard.snapshot({ webPoSignalOutput: webPoSignalOutput })
|
||||
}).then(function (botguardResponse) {
|
||||
return { webPoSignalOutput: webPoSignalOutput, botguardResponse: botguardResponse }
|
||||
})
|
||||
}
|
||||
|
||||
function obtainPoToken(webPoSignalOutput, integrityToken, identifier) {
|
||||
const getMinter = webPoSignalOutput[0];
|
||||
|
||||
if (!getMinter)
|
||||
throw new Error('PMD:Undefined');
|
||||
|
||||
const mintCallback = getMinter(integrityToken);
|
||||
|
||||
if (!(mintCallback instanceof Function))
|
||||
throw new Error('APF:Failed');
|
||||
|
||||
const result = mintCallback(identifier);
|
||||
|
||||
if (!result)
|
||||
throw new Error('YNJ:Undefined');
|
||||
|
||||
if (!(result instanceof Uint8Array))
|
||||
throw new Error('ODM:Invalid');
|
||||
|
||||
return result;
|
||||
}
|
||||
</script></head><body></body></html>
|
@@ -17,6 +17,7 @@ import org.acra.config.CoreConfigurationBuilder;
|
||||
import org.schabi.newpipe.error.ReCaptchaActivity;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.downloader.Downloader;
|
||||
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor;
|
||||
import org.schabi.newpipe.ktx.ExceptionUtils;
|
||||
import org.schabi.newpipe.settings.NewPipeSettings;
|
||||
import org.schabi.newpipe.util.BridgeStateSaverInitializer;
|
||||
@@ -26,6 +27,7 @@ import org.schabi.newpipe.util.StateSaver;
|
||||
import org.schabi.newpipe.util.image.ImageStrategy;
|
||||
import org.schabi.newpipe.util.image.PicassoHelper;
|
||||
import org.schabi.newpipe.util.image.PreferredImageQuality;
|
||||
import org.schabi.newpipe.util.potoken.PoTokenProviderImpl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
@@ -118,6 +120,8 @@ public class App extends Application {
|
||||
&& prefs.getBoolean(getString(R.string.show_image_indicators_key), false));
|
||||
|
||||
configureRxJavaErrorHandler();
|
||||
|
||||
YoutubeStreamExtractor.setPoTokenProvider(PoTokenProviderImpl.INSTANCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -137,7 +137,8 @@ public final class DownloaderImpl extends Downloader {
|
||||
}
|
||||
|
||||
final okhttp3.Request.Builder requestBuilder = new okhttp3.Request.Builder()
|
||||
.method(httpMethod, requestBody).url(url)
|
||||
.method(httpMethod, requestBody)
|
||||
.url(url)
|
||||
.addHeader("User-Agent", USER_AGENT);
|
||||
|
||||
final String cookies = getCookies(url);
|
||||
@@ -145,38 +146,33 @@ public final class DownloaderImpl extends Downloader {
|
||||
requestBuilder.addHeader("Cookie", cookies);
|
||||
}
|
||||
|
||||
for (final Map.Entry<String, List<String>> pair : headers.entrySet()) {
|
||||
final String headerName = pair.getKey();
|
||||
final List<String> headerValueList = pair.getValue();
|
||||
|
||||
if (headerValueList.size() > 1) {
|
||||
headers.forEach((headerName, headerValueList) -> {
|
||||
requestBuilder.removeHeader(headerName);
|
||||
for (final 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();
|
||||
headerValueList.forEach(headerValue ->
|
||||
requestBuilder.addHeader(headerName, headerValue));
|
||||
});
|
||||
|
||||
try (
|
||||
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;
|
||||
|
||||
try (ResponseBody body = response.body()) {
|
||||
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);
|
||||
return new Response(
|
||||
response.code(),
|
||||
response.message(),
|
||||
response.headers().toMultimap(),
|
||||
responseBodyToReturn,
|
||||
latestUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -92,6 +92,7 @@ import org.schabi.newpipe.util.SerializedCache;
|
||||
import org.schabi.newpipe.util.ServiceHelper;
|
||||
import org.schabi.newpipe.util.StateSaver;
|
||||
import org.schabi.newpipe.util.ThemeHelper;
|
||||
import org.schabi.newpipe.util.external_communication.ShareUtils;
|
||||
import org.schabi.newpipe.views.FocusOverlayView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -120,7 +121,8 @@ public class MainActivity extends AppCompatActivity {
|
||||
private static final int ITEM_ID_DOWNLOADS = -4;
|
||||
private static final int ITEM_ID_HISTORY = -5;
|
||||
private static final int ITEM_ID_SETTINGS = 0;
|
||||
private static final int ITEM_ID_ABOUT = 1;
|
||||
private static final int ITEM_ID_DONATION = 1;
|
||||
private static final int ITEM_ID_ABOUT = 2;
|
||||
|
||||
private static final int ORDER = 0;
|
||||
|
||||
@@ -262,6 +264,10 @@ public class MainActivity extends AppCompatActivity {
|
||||
drawerLayoutBinding.navigation.getMenu()
|
||||
.add(R.id.menu_options_about_group, ITEM_ID_SETTINGS, ORDER, R.string.settings)
|
||||
.setIcon(R.drawable.ic_settings);
|
||||
drawerLayoutBinding.navigation.getMenu()
|
||||
.add(R.id.menu_options_about_group, ITEM_ID_DONATION, ORDER,
|
||||
R.string.donation_title)
|
||||
.setIcon(R.drawable.volunteer_activism_ic);
|
||||
drawerLayoutBinding.navigation.getMenu()
|
||||
.add(R.id.menu_options_about_group, ITEM_ID_ABOUT, ORDER, R.string.tab_about)
|
||||
.setIcon(R.drawable.ic_info_outline);
|
||||
@@ -337,6 +343,9 @@ public class MainActivity extends AppCompatActivity {
|
||||
case ITEM_ID_SETTINGS:
|
||||
NavigationHelper.openSettings(this);
|
||||
break;
|
||||
case ITEM_ID_DONATION:
|
||||
ShareUtils.openUrlInBrowser(this, getString(R.string.donation_url));
|
||||
break;
|
||||
case ITEM_ID_ABOUT:
|
||||
NavigationHelper.openAbout(this);
|
||||
break;
|
||||
@@ -924,4 +933,5 @@ public class MainActivity extends AppCompatActivity {
|
||||
return sheetState == BottomSheetBehavior.STATE_HIDDEN
|
||||
|| sheetState == BottomSheetBehavior.STATE_COLLAPSED;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -26,7 +26,7 @@ import org.schabi.newpipe.util.Localization;
|
||||
import org.schabi.newpipe.util.ThemeHelper;
|
||||
import org.schabi.newpipe.util.external_communication.ShareUtils;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -67,10 +67,6 @@ public class ErrorActivity extends AppCompatActivity {
|
||||
public static final String ERROR_GITHUB_ISSUE_URL =
|
||||
"https://github.com/TeamNewPipe/NewPipe/issues";
|
||||
|
||||
public static final DateTimeFormatter CURRENT_TIMESTAMP_FORMATTER =
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
|
||||
|
||||
|
||||
private ErrorInfo errorInfo;
|
||||
private String currentTimeStamp;
|
||||
|
||||
@@ -107,7 +103,9 @@ public class ErrorActivity extends AppCompatActivity {
|
||||
|
||||
// important add guru meditation
|
||||
addGuruMeditation();
|
||||
currentTimeStamp = CURRENT_TIMESTAMP_FORMATTER.format(LocalDateTime.now());
|
||||
// print current time, as zoned ISO8601 timestamp
|
||||
final ZonedDateTime now = ZonedDateTime.now();
|
||||
currentTimeStamp = now.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
|
||||
|
||||
activityErrorBinding.errorReportEmailButton.setOnClickListener(v ->
|
||||
openPrivacyPolicyDialog(this, "EMAIL"));
|
||||
@@ -250,6 +248,9 @@ public class ErrorActivity extends AppCompatActivity {
|
||||
.append("\n* __Content Language:__ ").append(getContentLanguageString())
|
||||
.append("\n* __App Language:__ ").append(getAppLanguage())
|
||||
.append("\n* __Service:__ ").append(errorInfo.getServiceName())
|
||||
.append("\n* __Timestamp:__ ").append(currentTimeStamp)
|
||||
.append("\n* __Package:__ ").append(getPackageName())
|
||||
.append("\n* __Service:__ ").append(errorInfo.getServiceName())
|
||||
.append("\n* __Version:__ ").append(BuildConfig.VERSION_NAME)
|
||||
.append("\n* __OS:__ ").append(getOsString()).append("\n");
|
||||
|
||||
|
@@ -283,11 +283,11 @@ public final class VideoDetailFragment
|
||||
/*////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public static VideoDetailFragment getInstance(final int serviceId,
|
||||
@Nullable final String videoUrl,
|
||||
@Nullable final String url,
|
||||
@NonNull final String name,
|
||||
@Nullable final PlayQueue queue) {
|
||||
final VideoDetailFragment instance = new VideoDetailFragment();
|
||||
instance.setInitialData(serviceId, videoUrl, name, queue);
|
||||
instance.setInitialData(serviceId, url, name, queue);
|
||||
return instance;
|
||||
}
|
||||
|
||||
@@ -1736,7 +1736,7 @@ public final class VideoDetailFragment
|
||||
playQueue = queue;
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onQueueUpdate() called with: serviceId = ["
|
||||
+ serviceId + "], videoUrl = [" + url + "], name = ["
|
||||
+ serviceId + "], url = [" + url + "], name = ["
|
||||
+ title + "], playQueue = [" + playQueue + "]");
|
||||
}
|
||||
|
||||
|
@@ -14,10 +14,12 @@ import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
|
||||
import static com.google.android.exoplayer2.util.Util.castNonNull;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getAndroidUserAgent;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getIosUserAgent;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getTvHtml5UserAgent;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.isAndroidStreamingUrl;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.isIosStreamingUrl;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.isTvHtml5StreamingUrl;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.isWebStreamingUrl;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.isTvHtml5SimplyEmbeddedPlayerStreamingUrl;
|
||||
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.isWebEmbeddedPlayerStreamingUrl;
|
||||
import static java.lang.Math.min;
|
||||
|
||||
import android.net.Uri;
|
||||
@@ -270,6 +272,7 @@ public final class YoutubeHttpDataSource extends BaseDataSource implements HttpD
|
||||
|
||||
private static final String RN_PARAMETER = "&rn=";
|
||||
private static final String YOUTUBE_BASE_URL = "https://www.youtube.com";
|
||||
private static final byte[] POST_BODY = new byte[] {0x78, 0};
|
||||
|
||||
private final boolean allowCrossProtocolRedirects;
|
||||
private final boolean rangeParameterEnabled;
|
||||
@@ -658,8 +661,11 @@ public final class YoutubeHttpDataSource extends BaseDataSource implements HttpD
|
||||
}
|
||||
}
|
||||
|
||||
final boolean isTvHtml5StreamingUrl = isTvHtml5StreamingUrl(requestUrl);
|
||||
|
||||
if (isWebStreamingUrl(requestUrl)
|
||||
|| isTvHtml5SimplyEmbeddedPlayerStreamingUrl(requestUrl)) {
|
||||
|| isTvHtml5StreamingUrl
|
||||
|| isWebEmbeddedPlayerStreamingUrl(requestUrl)) {
|
||||
httpURLConnection.setRequestProperty(HttpHeaders.ORIGIN, YOUTUBE_BASE_URL);
|
||||
httpURLConnection.setRequestProperty(HttpHeaders.REFERER, YOUTUBE_BASE_URL);
|
||||
httpURLConnection.setRequestProperty(HttpHeaders.SEC_FETCH_DEST, "empty");
|
||||
@@ -679,6 +685,9 @@ public final class YoutubeHttpDataSource extends BaseDataSource implements HttpD
|
||||
} else if (isIosStreamingUrl) {
|
||||
httpURLConnection.setRequestProperty(HttpHeaders.USER_AGENT,
|
||||
getIosUserAgent(null));
|
||||
} else if (isTvHtml5StreamingUrl) {
|
||||
httpURLConnection.setRequestProperty(HttpHeaders.USER_AGENT,
|
||||
getTvHtml5UserAgent());
|
||||
} else {
|
||||
// non-mobile user agent
|
||||
httpURLConnection.setRequestProperty(HttpHeaders.USER_AGENT, DownloaderImpl.USER_AGENT);
|
||||
@@ -687,22 +696,16 @@ public final class YoutubeHttpDataSource extends BaseDataSource implements HttpD
|
||||
httpURLConnection.setRequestProperty(HttpHeaders.ACCEPT_ENCODING,
|
||||
allowGzip ? "gzip" : "identity");
|
||||
httpURLConnection.setInstanceFollowRedirects(followRedirects);
|
||||
httpURLConnection.setDoOutput(httpBody != null);
|
||||
|
||||
// Mobile clients uses POST requests to fetch contents
|
||||
httpURLConnection.setRequestMethod(isAndroidStreamingUrl || isIosStreamingUrl
|
||||
? "POST"
|
||||
: DataSpec.getStringForHttpMethod(httpMethod));
|
||||
|
||||
if (httpBody != null) {
|
||||
httpURLConnection.setFixedLengthStreamingMode(httpBody.length);
|
||||
// Most clients use POST requests to fetch contents
|
||||
httpURLConnection.setRequestMethod("POST");
|
||||
httpURLConnection.setDoOutput(true);
|
||||
httpURLConnection.setFixedLengthStreamingMode(POST_BODY.length);
|
||||
httpURLConnection.connect();
|
||||
|
||||
final OutputStream os = httpURLConnection.getOutputStream();
|
||||
os.write(httpBody);
|
||||
os.write(POST_BODY);
|
||||
os.close();
|
||||
} else {
|
||||
httpURLConnection.connect();
|
||||
}
|
||||
|
||||
return httpURLConnection;
|
||||
}
|
||||
|
||||
|
@@ -25,9 +25,7 @@ import android.graphics.Color;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.provider.Settings;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -44,6 +42,7 @@ import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.exoplayer2.ui.SubtitleView;
|
||||
import com.google.android.exoplayer2.video.VideoSize;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
@@ -522,11 +521,8 @@ public final class MainPlayerUi extends VideoPlayerUi implements View.OnLayoutCh
|
||||
|
||||
@Override
|
||||
protected void setupSubtitleView(final float captionScale) {
|
||||
final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
|
||||
final int minimumLength = Math.min(metrics.heightPixels, metrics.widthPixels);
|
||||
final float captionRatioInverse = 20f + 4f * (1.0f - captionScale);
|
||||
binding.subtitleView.setFixedTextSize(
|
||||
TypedValue.COMPLEX_UNIT_PX, minimumLength / captionRatioInverse);
|
||||
binding.subtitleView.setFractionalTextSize(
|
||||
SubtitleView.DEFAULT_TEXT_SIZE_FRACTION * captionScale);
|
||||
}
|
||||
//endregion
|
||||
|
||||
|
@@ -424,9 +424,8 @@ public final class PopupPlayerUi extends VideoPlayerUi {
|
||||
|
||||
@Override
|
||||
protected void setupSubtitleView(final float captionScale) {
|
||||
final float captionRatio = (captionScale - 1.0f) / 5.0f + 1.0f;
|
||||
binding.subtitleView.setFractionalTextSize(
|
||||
SubtitleView.DEFAULT_TEXT_SIZE_FRACTION * captionRatio);
|
||||
SubtitleView.DEFAULT_TEXT_SIZE_FRACTION * captionScale);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -1414,6 +1414,10 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa
|
||||
binding.subtitleView.setStyle(captionStyle);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param captionScale Value returned by {@link PlayerHelper#getCaptionScale}.
|
||||
*/
|
||||
protected abstract void setupSubtitleView(float captionScale);
|
||||
//endregion
|
||||
|
||||
|
@@ -17,6 +17,7 @@ import android.view.InputDevice;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowManager;
|
||||
import android.webkit.CookieManager;
|
||||
|
||||
import androidx.annotation.Dimension;
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -335,4 +336,17 @@ public final class DeviceUtils {
|
||||
&& !TX_50JXW834
|
||||
&& !HMB9213NW;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the device has support for WebView, see
|
||||
* <a href="https://stackoverflow.com/a/69626735">https://stackoverflow.com/a/69626735</a>
|
||||
*/
|
||||
public static boolean supportsWebView() {
|
||||
try {
|
||||
CookieManager.getInstance();
|
||||
return true;
|
||||
} catch (final Throwable ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -452,8 +452,12 @@ public final class NavigationHelper {
|
||||
if (fragment instanceof VideoDetailFragment && fragment.isVisible()) {
|
||||
onVideoDetailFragmentReady.run((VideoDetailFragment) fragment);
|
||||
} else {
|
||||
// Specify no url here, otherwise the VideoDetailFragment will start loading the
|
||||
// stream automatically if it's the first time it is being opened, but then
|
||||
// onVideoDetailFragmentReady will kick in and start another loading process.
|
||||
// See VideoDetailFragment.wasCleared() and its usage in doInitialLoadLogic().
|
||||
final VideoDetailFragment instance = VideoDetailFragment
|
||||
.getInstance(serviceId, url, title, playQueue);
|
||||
.getInstance(serviceId, null, title, playQueue);
|
||||
instance.setAutoPlay(autoPlay);
|
||||
|
||||
defaultTransaction(fragmentManager)
|
||||
|
@@ -0,0 +1,113 @@
|
||||
package org.schabi.newpipe.util.potoken
|
||||
|
||||
import com.grack.nanojson.JsonObject
|
||||
import com.grack.nanojson.JsonParser
|
||||
import com.grack.nanojson.JsonWriter
|
||||
import okio.ByteString.Companion.decodeBase64
|
||||
import okio.ByteString.Companion.toByteString
|
||||
|
||||
/**
|
||||
* Parses the raw challenge data obtained from the Create endpoint and returns an object that can be
|
||||
* embedded in a JavaScript snippet.
|
||||
*/
|
||||
fun parseChallengeData(rawChallengeData: String): String {
|
||||
val scrambled = JsonParser.array().from(rawChallengeData)
|
||||
|
||||
val challengeData = if (scrambled.size > 1 && scrambled.isString(1)) {
|
||||
val descrambled = descramble(scrambled.getString(1))
|
||||
JsonParser.array().from(descrambled)
|
||||
} else {
|
||||
scrambled.getArray(1)
|
||||
}
|
||||
|
||||
val messageId = challengeData.getString(0)
|
||||
val interpreterHash = challengeData.getString(3)
|
||||
val program = challengeData.getString(4)
|
||||
val globalName = challengeData.getString(5)
|
||||
val clientExperimentsStateBlob = challengeData.getString(7)
|
||||
|
||||
val privateDoNotAccessOrElseSafeScriptWrappedValue = challengeData.getArray(1, null)?.find { it is String }
|
||||
val privateDoNotAccessOrElseTrustedResourceUrlWrappedValue = challengeData.getArray(2, null)?.find { it is String }
|
||||
|
||||
return JsonWriter.string(
|
||||
JsonObject.builder()
|
||||
.value("messageId", messageId)
|
||||
.`object`("interpreterJavascript")
|
||||
.value("privateDoNotAccessOrElseSafeScriptWrappedValue", privateDoNotAccessOrElseSafeScriptWrappedValue)
|
||||
.value("privateDoNotAccessOrElseTrustedResourceUrlWrappedValue", privateDoNotAccessOrElseTrustedResourceUrlWrappedValue)
|
||||
.end()
|
||||
.value("interpreterHash", interpreterHash)
|
||||
.value("program", program)
|
||||
.value("globalName", globalName)
|
||||
.value("clientExperimentsStateBlob", clientExperimentsStateBlob)
|
||||
.done()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the raw integrity token data obtained from the GenerateIT endpoint to a JavaScript
|
||||
* `Uint8Array` that can be embedded directly in JavaScript code, and an [Int] representing the
|
||||
* duration of this token in seconds.
|
||||
*/
|
||||
fun parseIntegrityTokenData(rawIntegrityTokenData: String): Pair<String, Long> {
|
||||
val integrityTokenData = JsonParser.array().from(rawIntegrityTokenData)
|
||||
return base64ToU8(integrityTokenData.getString(0)) to integrityTokenData.getLong(1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a string (usually the identifier used as input to `obtainPoToken`) to a JavaScript
|
||||
* `Uint8Array` that can be embedded directly in JavaScript code.
|
||||
*/
|
||||
fun stringToU8(identifier: String): String {
|
||||
return newUint8Array(identifier.toByteArray())
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a poToken encoded as a sequence of bytes represented as integers separated by commas
|
||||
* (e.g. "97,98,99" would be "abc"), which is the output of `Uint8Array::toString()` in JavaScript,
|
||||
* and converts it to the specific base64 representation for poTokens.
|
||||
*/
|
||||
fun u8ToBase64(poToken: String): String {
|
||||
return poToken.split(",")
|
||||
.map { it.toUByte().toByte() }
|
||||
.toByteArray()
|
||||
.toByteString()
|
||||
.base64()
|
||||
.replace("+", "-")
|
||||
.replace("/", "_")
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the scrambled challenge, decodes it from base64, adds 97 to each byte.
|
||||
*/
|
||||
private fun descramble(scrambledChallenge: String): String {
|
||||
return base64ToByteString(scrambledChallenge)
|
||||
.map { (it + 97).toByte() }
|
||||
.toByteArray()
|
||||
.decodeToString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a base64 string encoded in the specific base64 representation used by YouTube, and
|
||||
* returns a JavaScript `Uint8Array` that can be embedded directly in JavaScript code.
|
||||
*/
|
||||
private fun base64ToU8(base64: String): String {
|
||||
return newUint8Array(base64ToByteString(base64))
|
||||
}
|
||||
|
||||
private fun newUint8Array(contents: ByteArray): String {
|
||||
return "new Uint8Array([" + contents.joinToString(separator = ",") { it.toUByte().toString() } + "])"
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a base64 string encoded in the specific base64 representation used by YouTube.
|
||||
*/
|
||||
private fun base64ToByteString(base64: String): ByteArray {
|
||||
val base64Mod = base64
|
||||
.replace('-', '+')
|
||||
.replace('_', '/')
|
||||
.replace('.', '=')
|
||||
|
||||
return (base64Mod.decodeBase64() ?: throw PoTokenException("Cannot base64 decode"))
|
||||
.toByteArray()
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
package org.schabi.newpipe.util.potoken
|
||||
|
||||
class PoTokenException(message: String) : Exception(message)
|
||||
|
||||
// to be thrown if the WebView provided by the system is broken
|
||||
class BadWebViewException(message: String) : Exception(message)
|
||||
|
||||
fun buildExceptionForJsError(error: String): Exception {
|
||||
return if (error.contains("SyntaxError"))
|
||||
BadWebViewException(error)
|
||||
else
|
||||
PoTokenException(error)
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
package org.schabi.newpipe.util.potoken
|
||||
|
||||
import android.content.Context
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import java.io.Closeable
|
||||
|
||||
/**
|
||||
* This interface was created to allow for multiple methods to generate poTokens in the future (e.g.
|
||||
* via WebView and via a local DOM implementation)
|
||||
*/
|
||||
interface PoTokenGenerator : Closeable {
|
||||
/**
|
||||
* Generates a poToken for the provided identifier, using the `integrityToken` and
|
||||
* `webPoSignalOutput` previously obtained in the initialization of [PoTokenWebView]. Can be
|
||||
* called multiple times.
|
||||
*/
|
||||
fun generatePoToken(identifier: String): Single<String>
|
||||
|
||||
/**
|
||||
* @return whether the `integrityToken` is expired, in which case all tokens generated by
|
||||
* [generatePoToken] will be invalid
|
||||
*/
|
||||
fun isExpired(): Boolean
|
||||
|
||||
interface Factory {
|
||||
/**
|
||||
* Initializes a [PoTokenGenerator] by loading the BotGuard VM, running it, and obtaining
|
||||
* an `integrityToken`. Can then be used multiple times to generate multiple poTokens with
|
||||
* [generatePoToken].
|
||||
*
|
||||
* @param context used e.g. to load the HTML asset or to instantiate a WebView
|
||||
*/
|
||||
fun newPoTokenGenerator(context: Context): Single<PoTokenGenerator>
|
||||
}
|
||||
}
|
@@ -0,0 +1,131 @@
|
||||
package org.schabi.newpipe.util.potoken
|
||||
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import org.schabi.newpipe.App
|
||||
import org.schabi.newpipe.BuildConfig
|
||||
import org.schabi.newpipe.extractor.NewPipe
|
||||
import org.schabi.newpipe.extractor.services.youtube.InnertubeClientRequestInfo
|
||||
import org.schabi.newpipe.extractor.services.youtube.PoTokenProvider
|
||||
import org.schabi.newpipe.extractor.services.youtube.PoTokenResult
|
||||
import org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper
|
||||
import org.schabi.newpipe.util.DeviceUtils
|
||||
|
||||
object PoTokenProviderImpl : PoTokenProvider {
|
||||
val TAG = PoTokenProviderImpl::class.simpleName
|
||||
private val webViewSupported by lazy { DeviceUtils.supportsWebView() }
|
||||
private var webViewBadImpl = false // whether the system has a bad WebView implementation
|
||||
|
||||
private object WebPoTokenGenLock
|
||||
private var webPoTokenVisitorData: String? = null
|
||||
private var webPoTokenStreamingPot: String? = null
|
||||
private var webPoTokenGenerator: PoTokenGenerator? = null
|
||||
|
||||
override fun getWebClientPoToken(videoId: String): PoTokenResult? {
|
||||
if (!webViewSupported || webViewBadImpl) {
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
return getWebClientPoToken(videoId = videoId, forceRecreate = false)
|
||||
} catch (e: RuntimeException) {
|
||||
// RxJava's Single wraps exceptions into RuntimeErrors, so we need to unwrap them here
|
||||
when (val cause = e.cause) {
|
||||
is BadWebViewException -> {
|
||||
Log.e(TAG, "Could not obtain poToken because WebView is broken", e)
|
||||
webViewBadImpl = true
|
||||
return null
|
||||
}
|
||||
null -> throw e
|
||||
else -> throw cause // includes PoTokenException
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param forceRecreate whether to force the recreation of [webPoTokenGenerator], to be used in
|
||||
* case the current [webPoTokenGenerator] threw an error last time
|
||||
* [PoTokenGenerator.generatePoToken] was called
|
||||
*/
|
||||
private fun getWebClientPoToken(videoId: String, forceRecreate: Boolean): PoTokenResult {
|
||||
// just a helper class since Kotlin does not have builtin support for 4-tuples
|
||||
data class Quadruple<T1, T2, T3, T4>(val t1: T1, val t2: T2, val t3: T3, val t4: T4)
|
||||
|
||||
val (poTokenGenerator, visitorData, streamingPot, hasBeenRecreated) =
|
||||
synchronized(WebPoTokenGenLock) {
|
||||
val shouldRecreate = webPoTokenGenerator == null || forceRecreate ||
|
||||
webPoTokenGenerator!!.isExpired()
|
||||
|
||||
if (shouldRecreate) {
|
||||
|
||||
val innertubeClientRequestInfo = InnertubeClientRequestInfo.ofWebClient()
|
||||
innertubeClientRequestInfo.clientInfo.clientVersion =
|
||||
YoutubeParsingHelper.getClientVersion()
|
||||
|
||||
webPoTokenVisitorData = YoutubeParsingHelper.getVisitorDataFromInnertube(
|
||||
innertubeClientRequestInfo,
|
||||
NewPipe.getPreferredLocalization(),
|
||||
NewPipe.getPreferredContentCountry(),
|
||||
YoutubeParsingHelper.getYouTubeHeaders(),
|
||||
YoutubeParsingHelper.YOUTUBEI_V1_URL,
|
||||
null,
|
||||
false
|
||||
)
|
||||
// close the current webPoTokenGenerator on the main thread
|
||||
webPoTokenGenerator?.let { Handler(Looper.getMainLooper()).post { it.close() } }
|
||||
|
||||
// create a new webPoTokenGenerator
|
||||
webPoTokenGenerator = PoTokenWebView
|
||||
.newPoTokenGenerator(App.getApp()).blockingGet()
|
||||
|
||||
// The streaming poToken needs to be generated exactly once before generating
|
||||
// any other (player) tokens.
|
||||
webPoTokenStreamingPot = webPoTokenGenerator!!
|
||||
.generatePoToken(webPoTokenVisitorData!!).blockingGet()
|
||||
}
|
||||
|
||||
return@synchronized Quadruple(
|
||||
webPoTokenGenerator!!,
|
||||
webPoTokenVisitorData!!,
|
||||
webPoTokenStreamingPot!!,
|
||||
shouldRecreate
|
||||
)
|
||||
}
|
||||
|
||||
val playerPot = try {
|
||||
// Not using synchronized here, since poTokenGenerator would be able to generate
|
||||
// multiple poTokens in parallel if needed. The only important thing is for exactly one
|
||||
// visitorData/streaming poToken to be generated before anything else.
|
||||
poTokenGenerator.generatePoToken(videoId).blockingGet()
|
||||
} catch (throwable: Throwable) {
|
||||
if (hasBeenRecreated) {
|
||||
// the poTokenGenerator has just been recreated (and possibly this is already the
|
||||
// second time we try), so there is likely nothing we can do
|
||||
throw throwable
|
||||
} else {
|
||||
// retry, this time recreating the [webPoTokenGenerator] from scratch;
|
||||
// this might happen for example if NewPipe goes in the background and the WebView
|
||||
// content is lost
|
||||
Log.e(TAG, "Failed to obtain poToken, retrying", throwable)
|
||||
return getWebClientPoToken(videoId = videoId, forceRecreate = true)
|
||||
}
|
||||
}
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.d(
|
||||
TAG,
|
||||
"poToken for $videoId: playerPot=$playerPot, " +
|
||||
"streamingPot=$streamingPot, visitor_data=$visitorData"
|
||||
)
|
||||
}
|
||||
|
||||
return PoTokenResult(visitorData, playerPot, streamingPot)
|
||||
}
|
||||
|
||||
override fun getWebEmbedClientPoToken(videoId: String): PoTokenResult? = null
|
||||
|
||||
override fun getAndroidClientPoToken(videoId: String): PoTokenResult? = null
|
||||
|
||||
override fun getIosClientPoToken(videoId: String): PoTokenResult? = null
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -664,6 +664,13 @@ public class MissionAdapter extends Adapter<ViewHolder> implements Handler.Callb
|
||||
return true;
|
||||
case R.id.md5:
|
||||
case R.id.sha1:
|
||||
final StoredFileHelper storage = h.item.mission.storage;
|
||||
if (!storage.existsAsFile()) {
|
||||
Toast.makeText(mContext, R.string.missing_file, Toast.LENGTH_SHORT).show();
|
||||
mDeleter.append(h.item.mission);
|
||||
applyChanges();
|
||||
return true;
|
||||
}
|
||||
final NotificationManager notificationManager
|
||||
= ContextCompat.getSystemService(mContext, NotificationManager.class);
|
||||
final NotificationCompat.Builder progressNotificationBuilder
|
||||
@@ -678,7 +685,6 @@ public class MissionAdapter extends Adapter<ViewHolder> implements Handler.Callb
|
||||
|
||||
notificationManager.notify(HASH_NOTIFICATION_ID, progressNotificationBuilder
|
||||
.build());
|
||||
final StoredFileHelper storage = h.item.mission.storage;
|
||||
compositeDisposable.add(
|
||||
Observable.fromCallable(() -> Utility.checksum(storage, id))
|
||||
.subscribeOn(Schedulers.computation())
|
||||
|
9
app/src/main/res/drawable-mdpi/volunteer_activism_ic.xml
Normal file
9
app/src/main/res/drawable-mdpi/volunteer_activism_ic.xml
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -669,7 +669,7 @@
|
||||
<string name="feed_use_dedicated_fetch_method_summary">Bəzi xidmətlərdə mövcuddur, adətən daha sürətli olur, lakin məhdud sayda elementləri və çox vaxt natamam məlumatı qaytara bilər (məsələn, müddət, element növü, canlı status yoxdur)</string>
|
||||
<string name="no_appropriate_file_manager_message">Bu əməliyyat üçün uyğun fayl meneceri tapılmadı. Zəhmət olmasa, fayl menecerini quraşdır və ya endirmə tənzimləmələrində \'%s\'-i qeyri-aktiv etməyə çalış</string>
|
||||
<string name="feed_load_error_account_info">\'%s\' üçün axın yükləmək mümkün olmadı.</string>
|
||||
<string name="no_appropriate_file_manager_message_android_10">Bu fəaliyyət üçün uyğun fayl meneceri tapılmadı.\nXahiş olunur, Yaddaş Giriş Quruluşuna uyğun fayl meneceri quraşdırın.</string>
|
||||
<string name="no_appropriate_file_manager_message_android_10">Bu fəaliyyət üçün uyğun fayl meneceri tapılmadı.\nXahiş olunur, Yaddaş Giriş Quruluşuna uyğun fayl meneceri quraşdırın</string>
|
||||
<string name="youtube_music_premium_content">Bu video yalnız YouTube Music Premium üzvləri üçün əlçatandır, ona görə də NewPipe tərəfindən yayımlamaq və ya endirmək mümkün deyil.</string>
|
||||
<string name="description_select_note">İndi açıqlamadakı mətni seçə bilərsiniz. Nəzərə alın ki, seçim rejimində səhifə titrəyə və linklər kliklənməyə bilər.</string>
|
||||
<string name="notification_scale_to_square_image_summary">Bildirişdə göstərilən video miniatürünü 16:9-dan 1:1 görünüş nisbətinə qədər kəs</string>
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -60,7 +60,7 @@
|
||||
<string name="show_next_and_similar_title">Покажи „следващ“ и „подобни“</string>
|
||||
<string name="show_hold_to_append_title">Покажи съвет „Задръжте за поставяне в опашка“</string>
|
||||
<string name="unsupported_url">Непознат URL</string>
|
||||
<string name="content_language_title">Език на съдържанието по подразбиране</string>
|
||||
<string name="content_language_title">Език на съдържание по подразбиране</string>
|
||||
<string name="settings_category_player_title">Плейър</string>
|
||||
<string name="settings_category_player_behavior_title">Поведение</string>
|
||||
<string name="settings_category_video_audio_title">Видео и аудио</string>
|
||||
@@ -583,9 +583,7 @@
|
||||
<string name="remove_duplicates_title">Премахни повторения?</string>
|
||||
<string name="feed_groups_header_title">Група от канали</string>
|
||||
<string name="feed_load_error_account_info">Неуспешно зареждане на емисия за \'%s\'.</string>
|
||||
<string name="feed_load_error_terminated">Акаунтът на автора е бил отстранен.
|
||||
\nNewPipe няма да може да зареди тази емисия вече.
|
||||
\nИскате ли да махнете абонамента от този канал?</string>
|
||||
<string name="feed_load_error_terminated">Профилът на автора е бил отстранен. \nNewPipe няма да може да зареди тази емисия вече. \nИскате ли да махнете абонамента от този канал?</string>
|
||||
<string name="feed_update_threshold_option_always_update">Винаги опреснявай</string>
|
||||
<string name="feed_load_error">Грешка при зареждане на емисия</string>
|
||||
<string name="feed_show_hide_streams">Покажи/Скрий потоци</string>
|
||||
@@ -694,7 +692,7 @@
|
||||
<string name="card">Карта</string>
|
||||
<string name="delete_downloaded_files_confirm">Изтрий всички изтеглени файлове от диска?</string>
|
||||
<string name="enable_queue_limit_desc">Едно изтегляне ще се изпълнява едновременно</string>
|
||||
<string name="downloads_storage_ask_title">Попитайте къде да изтеглите</string>
|
||||
<string name="downloads_storage_ask_title">Подкана за папка за изтегляне</string>
|
||||
<string name="systems_language">Система по подразбиране</string>
|
||||
<string name="remove_duplicates">Премахване на дубликати</string>
|
||||
<string name="remove_duplicates_message">Искате ли да премахнете всички дублиращи се потоци в този плейлист?</string>
|
||||
@@ -753,7 +751,7 @@
|
||||
<string name="enable_streams_notifications_title">Известия за нови потоци</string>
|
||||
<string name="any_network">Всяка мрежа</string>
|
||||
<string name="updates_setting_description">Покажи известие за актуализация на приложението, когато е налична нова версия</string>
|
||||
<string name="account_terminated">Акаунтът е прекратен</string>
|
||||
<string name="account_terminated">Профилът е прекратен</string>
|
||||
<string name="detail_pinned_comment_view_description">Фиксиран коментар</string>
|
||||
<string name="streams_not_yet_supported_removed">Потоци, които все още не се поддържат от програмата за изтегляне, не се показват</string>
|
||||
<string name="soundcloud_go_plus_content">Това е песен на SoundCloud Go+, поне във вашата страна, така че не може да бъде предавана поточно или изтеглена от NewPipe.</string>
|
||||
|
File diff suppressed because one or more lines are too long
@@ -135,7 +135,7 @@
|
||||
<string name="action_history">Verlauf</string>
|
||||
<string name="subscription_change_failed">Abonnement konnte nicht geändert werden</string>
|
||||
<string name="subscription_update_failed">Abonnement konnte nicht aktualisiert werden</string>
|
||||
<string name="resume_on_audio_focus_gain_summary">Nach Unterbrechungen (z. B. Telefonaten) Wiedergabe fortsetzen</string>
|
||||
<string name="resume_on_audio_focus_gain_summary">Nach Unterbrechungen (z. B. Telefonaten) Wiedergabe fortsetzen</string>
|
||||
<string name="notification_channel_name">NewPipe-Benachrichtigung</string>
|
||||
<string name="notification_channel_description">Benachrichtigungen für den NewPipe-Player</string>
|
||||
<string name="settings_category_player_behavior_title">Verhalten</string>
|
||||
@@ -319,7 +319,7 @@
|
||||
\nDu musst den Datenschutzrichtlinien zustimmen, um den Fehlerbericht an uns zu senden.</string>
|
||||
<string name="limit_data_usage_none_description">Unbegrenzt</string>
|
||||
<string name="limit_mobile_data_usage_title">Auflösung bei Verwendung mobiler Daten begrenzen</string>
|
||||
<string name="minimize_on_exit_title">Beim App-wechsel minimieren</string>
|
||||
<string name="minimize_on_exit_title">Beim App-Wechsel minimieren</string>
|
||||
<string name="minimize_on_exit_summary">Aktion beim Umschalten auf eine andere App vom Haupt-Videoplayer — %s</string>
|
||||
<string name="minimize_on_exit_none_description">Keine</string>
|
||||
<string name="minimize_on_exit_background_description">Für die Wiedergabe im Hintergrund minimieren</string>
|
||||
@@ -409,7 +409,7 @@
|
||||
<string name="error_progress_lost">Vorgang abgebrochen, da die Datei gelöscht wurde</string>
|
||||
<string name="confirm_prompt">Möchtest du deinen Downloadverlauf oder alle heruntergeladenen Dateien löschen\?</string>
|
||||
<string name="enable_queue_limit">Downloadwarteschlange begrenzen</string>
|
||||
<string name="enable_queue_limit_desc">Ein Download wird zur gleichen Zeit ausgeführt</string>
|
||||
<string name="enable_queue_limit_desc">Ein Download wird gleichzeitig ausgeführt</string>
|
||||
<string name="start_downloads">Downloads starten</string>
|
||||
<string name="pause_downloads">Downloads anhalten</string>
|
||||
<string name="downloads_storage_ask_title">Downloadziel abfragen</string>
|
||||
@@ -497,7 +497,7 @@
|
||||
<string name="feed_oldest_subscription_update">Feed zuletzt aktualisiert: %s</string>
|
||||
<string name="feed_update_threshold_title">Grenzwert für Feed-Aktualisierung</string>
|
||||
<string name="feed_use_dedicated_fetch_method_title">Aus fest zugeordnetem Feed abrufen wenn verfügbar</string>
|
||||
<string name="feed_use_dedicated_fetch_method_summary">Steht in manchen Diensten zur Verfügung, ist meist viel schneller, liefert aber eventuell eine eingeschränkte Anzahl an Elementen und oft unvollständige Informationen (z. B. keine Videolänge, keinen Elementtyp, keinen Live-Status)</string>
|
||||
<string name="feed_use_dedicated_fetch_method_summary">Steht in manchen Diensten zur Verfügung, ist meist viel schneller, liefert aber eventuell eine eingeschränkte Anzahl an Elementen und oft unvollständige Informationen (z. B. keine Videolänge, keinen Elementtyp, keinen Live-Status)</string>
|
||||
<string name="feed_use_dedicated_fetch_method_help_text">Glaubst du, dass das Laden von Feeds zu langsam ist? Wenn ja, versuche den Schnelllademodus einzuschalten (du kannst ihn in den Einstellungen oder über die Schaltfläche unten ändern).
|
||||
\n
|
||||
\nNewPipe bietet zwei Feed-Ladestrategien:
|
||||
@@ -527,7 +527,7 @@
|
||||
<string name="remove_watched_popup_title">Gesehene Videos entfernen\?</string>
|
||||
<string name="show_original_time_ago_title">Originalzeit vor Elementen anzeigen</string>
|
||||
<string name="show_original_time_ago_summary">Originaltexte von Diensten werden in Stream-Elementen sichtbar sein</string>
|
||||
<string name="youtube_restricted_mode_enabled_title">Aktivieren des „Eingeschränkten Modus“ von YouTube</string>
|
||||
<string name="youtube_restricted_mode_enabled_title">YouTubes „Eingeschränkten Modus“ aktivieren</string>
|
||||
<string name="detail_sub_channel_thumbnail_view_description">Profilbild des Kanals</string>
|
||||
<string name="channel_created_by">Erstellt von %s</string>
|
||||
<string name="video_detail_by">Von %s</string>
|
||||
@@ -659,7 +659,7 @@
|
||||
<string name="checking_updates_toast">Suche nach Aktualisierungen …</string>
|
||||
<string name="manual_update_description">Manuelle Prüfung auf neue Versionen</string>
|
||||
<string name="feed_new_items">Neue Feed-Elemente</string>
|
||||
<string name="show_crash_the_player_title">\"Player abstürzen lassen\" anzeigen</string>
|
||||
<string name="show_crash_the_player_title">„Player abstürzen lassen“ anzeigen</string>
|
||||
<string name="crash_the_player">Player abstürzen lassen</string>
|
||||
<string name="show_crash_the_player_summary">Zeigt eine Absturzoption an, wenn der Player verwendet wird</string>
|
||||
<string name="error_report_channel_name">Fehlerbericht-Benachrichtigung</string>
|
||||
@@ -719,7 +719,7 @@
|
||||
<string name="card">Karte</string>
|
||||
<string name="playlist_add_stream_success_duplicate">Duplikat %d mal hinzugefügt</string>
|
||||
<string name="duplicate_in_playlist">Die ausgegrauten Wiedergabelisten enthalten dieses Element bereits.</string>
|
||||
<string name="ignore_hardware_media_buttons_summary">Nützlich, wenn z. B. ein Headset mit defekten physischen Tasten verwendet wird</string>
|
||||
<string name="ignore_hardware_media_buttons_summary">Nützlich, wenn z. B. ein Headset mit defekten physischen Tasten verwendet wird</string>
|
||||
<string name="ignore_hardware_media_buttons_title">Ereignisse der Hardware-Medientasten ignorieren</string>
|
||||
<string name="remove_duplicates">Duplikate entfernen</string>
|
||||
<string name="remove_duplicates_title">Duplikate entfernen\?</string>
|
||||
|
@@ -9,7 +9,7 @@
|
||||
<string name="download">Λήψη</string>
|
||||
<string name="search">Αναζήτηση</string>
|
||||
<string name="settings">Ρυθμίσεις</string>
|
||||
<string name="did_you_mean">Μήπως εννοείτε «%1$s»;</string>
|
||||
<string name="did_you_mean">Μήπως εννοείτε \"%1$s\";</string>
|
||||
<string name="share_dialog_title">Κοινοποίηση με</string>
|
||||
<string name="use_external_video_player_title">Χρήση εξωτερικής εφαρμογής αναπαραγωγής βίντεο</string>
|
||||
<string name="use_external_audio_player_title">Χρήση εξωτερικής συσκευής αναπαραγωγής ήχου</string>
|
||||
@@ -103,7 +103,7 @@
|
||||
<string name="resume_on_audio_focus_gain_title">Ανάκτηση αναπαραγωγής</string>
|
||||
<string name="resume_on_audio_focus_gain_summary">Συνέχιση της αναπαραγωγής έπειτα από διακοπές (π.χ. κλήσεις)</string>
|
||||
<string name="show_hold_to_append_title">Εμφάνιση επεξήγησης του «Πιέστε παρατεταμένα για προσθήκη στην ουρά»</string>
|
||||
<string name="show_hold_to_append_summary">Εμφάνιση υπόδειξης όταν πατηθεί το κουμπί παρασκηνίου ή αναδυόμενου παραθύρου στη σελίδα λεπτομερειών του βίντεο</string>
|
||||
<string name="show_hold_to_append_summary">Εμφάνιση υπόδειξης όταν πατηθεί το κουμπί παρασκηνίου ή αναδυόμενου παραθύρου στις \"Λεπτομέρειες:\\ στο βίντεο</string>
|
||||
<string name="default_content_country_title">Προεπιλεγμένη χώρα περιεχομένου</string>
|
||||
<string name="settings_category_player_title">Αναπαραγωγός</string>
|
||||
<string name="settings_category_player_behavior_title">Συμπεριφορά</string>
|
||||
@@ -472,7 +472,7 @@
|
||||
<string name="restricted_video">Αυτό το βίντεο έχει περιορισμό ηλικίας.
|
||||
\n
|
||||
\nΕνεργοποιήστε το «%1$s» στις ρυθμίσεις εάν θέλετε να το δείτε.</string>
|
||||
<string name="youtube_restricted_mode_enabled_title">Λειτουργία περιορισμένης πρόσβασης του YouTube</string>
|
||||
<string name="youtube_restricted_mode_enabled_title">Ενεργοποίηση \"Περιορισμένη Λειτουργία\\ του YouTube</string>
|
||||
<string name="unsupported_url_dialog_message">Δεν ήταν δυνατή η αναγνώριση της διεύθυνσης URL. Άνοιγμα με άλλη εφαρμογή;</string>
|
||||
<string name="auto_queue_toggle">Αυτόματη προσθήκη στην ουρά</string>
|
||||
<string name="clear_queue_confirmation_description">Η ουρά του ενεργού αναπαραγωγού θα αντικατασταθεί</string>
|
||||
@@ -656,7 +656,7 @@
|
||||
<string name="manual_update_description">Χειροκίνητος έλεγχος για νέα έκδοση</string>
|
||||
<string name="check_for_updates">Έλεγχος αναβάθμισης</string>
|
||||
<string name="feed_new_items">Νέα αντικείμενα τροφοδοσίας</string>
|
||||
<string name="show_crash_the_player_title">Εμφάνιση «Κατάρρευσης αναπαραγωγέα»</string>
|
||||
<string name="show_crash_the_player_title">Εμφάνιση «Κατάρρευση αναπαραγωγέα\\</string>
|
||||
<string name="show_crash_the_player_summary">Εμφανίζει μια επιλογή κατάρρευσης κατά τη χρήση του αναπαραγωγέα</string>
|
||||
<string name="crash_the_player">Κατάρρευση αναπαραγωγέα</string>
|
||||
<string name="error_report_channel_name">Ειδοποίηση αναφοράς σφάλματος</string>
|
||||
|
@@ -12,11 +12,11 @@
|
||||
<string name="share_dialog_title">Konigi kun</string>
|
||||
<string name="use_external_video_player_title">Uzi eksteran filmetoludilon</string>
|
||||
<string name="use_external_audio_player_title">Uzi eksteran sonludilon</string>
|
||||
<string name="default_resolution_title">Defaŭlta rezolucio</string>
|
||||
<string name="default_resolution_title">Preferata distingumo</string>
|
||||
<string name="play_with_kodi_title">Ludi per Kodi</string>
|
||||
<string name="show_play_with_kodi_title">Montri \"Ludi per Kodi\"-opcion</string>
|
||||
<string name="play_audio">Sono</string>
|
||||
<string name="default_audio_format_title">Defaŭlta sondosierformo</string>
|
||||
<string name="default_audio_format_title">Preferata sona dosierformo</string>
|
||||
<string name="theme_title">Etoso</string>
|
||||
<string name="dark_theme_title">Malhela</string>
|
||||
<string name="light_theme_title">Hela</string>
|
||||
@@ -24,7 +24,7 @@
|
||||
<string name="download_dialog_title">Elŝuti</string>
|
||||
<string name="unsupported_url">Ligilo ne subtenita</string>
|
||||
<string name="content_language_title">Preferata enhavlingvo</string>
|
||||
<string name="settings_category_video_audio_title">Filmeto kaj sono</string>
|
||||
<string name="settings_category_video_audio_title">Filmo kaj sono</string>
|
||||
<string name="settings_category_appearance_title">Apero</string>
|
||||
<string name="background_player_playing_toast">Ludanta fone</string>
|
||||
<string name="general_error">Eraro</string>
|
||||
@@ -32,17 +32,17 @@
|
||||
<string name="content_not_available">Enhavo malhavebla</string>
|
||||
<string name="detail_likes_img_view_description">Ŝatoj</string>
|
||||
<string name="detail_dislikes_img_view_description">Malŝatoj</string>
|
||||
<string name="no_player_found">Neniu elsendlflua ludilo trovita. Ĉu instali la aplikaĵon VLC\?</string>
|
||||
<string name="kore_not_found">Ĉu instali la mankan aplikaĵon Kore\?</string>
|
||||
<string name="show_next_and_similar_title">Montri \'Sekvajn\' kaj \'Similajn\' videojn</string>
|
||||
<string name="no_player_found">Neniu ludilo de elsendoj trovita. Ĉu instalu la programon VLC?</string>
|
||||
<string name="kore_not_found">Ĉu instalu la mankatan programon Kore?</string>
|
||||
<string name="show_next_and_similar_title">Montri ‹Sekvajn› kaj ‹Similajn› filmetojn</string>
|
||||
<string name="could_not_load_thumbnails">Ĉiuj bildetoj ne ŝargeblas</string>
|
||||
<string name="parsing_error">La retejo ne analizeblas</string>
|
||||
<string name="detail_thumbnail_view_description">Ludi filmeton, daŭro:</string>
|
||||
<string name="detail_uploader_thumbnail_view_description">Bildeto de la alŝutinto</string>
|
||||
<string name="download_path_title">Elŝutujo por filmetoj</string>
|
||||
<string name="download_path_audio_title">Elŝutujo por aŭdio</string>
|
||||
<string name="show_play_with_kodi_summary">Montri opcion por ludi filmeton per la aplikaĵo Kodi</string>
|
||||
<string name="download_path_summary">Elŝutitaj filmetoj estas konservitaj tie</string>
|
||||
<string name="show_play_with_kodi_summary">Montri opcion por ludi filmeton per la programo Kodi</string>
|
||||
<string name="download_path_summary">Elŝutitaj filmetoj konserviĝas ĉi tie</string>
|
||||
<string name="download_path_audio_summary">Dosierujo por konservi elŝutitajn muzikojn</string>
|
||||
<string name="download_path_dialog_title">Elektu lokon por konservi elŝutitajn filmetojn</string>
|
||||
<string name="download_path_audio_dialog_title">Elektu lokon por konservi elŝutitajn muzikojn</string>
|
||||
@@ -57,7 +57,7 @@
|
||||
<string name="main_bg_subtitle">Premi \"Serĉi\" por komenci.</string>
|
||||
<string name="no_player_found_toast">Neniu elsendlflua ludilo trovita (instalu VLC por ludi ĝin).</string>
|
||||
<string name="open_in_popup_mode">Malfermi en ŝprucfenestran modon</string>
|
||||
<string name="use_external_video_player_summary">Forigas aŭdon ĉe kelkaj rezolucioj</string>
|
||||
<string name="use_external_video_player_summary">Forigas aŭdon ĉe kelkaj distingumoj</string>
|
||||
<string name="subscribe_button_title">Aboni</string>
|
||||
<string name="subscribed_button_title">Abonita</string>
|
||||
<string name="channel_unsubscribed">Kanalo malabonita</string>
|
||||
@@ -71,10 +71,10 @@
|
||||
<string name="controls_background_title">Fono</string>
|
||||
<string name="controls_popup_title">Ŝprucfenestro</string>
|
||||
<string name="controls_add_to_playlist_title">Aldonu al</string>
|
||||
<string name="default_popup_resolution_title">Defaŭlta rezolucio de la ŝprucfenestra ludilo</string>
|
||||
<string name="show_higher_resolutions_title">Montri pli altajn rezoluciojn</string>
|
||||
<string name="show_higher_resolutions_summary">Nur kelkaj aparatoj povas ludi 2K / 4K filmetojn</string>
|
||||
<string name="default_video_format_title">Defaŭlta filmetdosierformo</string>
|
||||
<string name="default_popup_resolution_title">Komenca distingumo de la ŝprucfenestra ludilo</string>
|
||||
<string name="show_higher_resolutions_title">Montri pli altajn distingumojn</string>
|
||||
<string name="show_higher_resolutions_summary">Nur kelkaj aparatoj povas ludi filmetojn je distingumoj 2K / 4K</string>
|
||||
<string name="default_video_format_title">Preferata filma dosierformo</string>
|
||||
<string name="popup_remember_size_pos_title">Memori ecojn de ŝprucfenestro</string>
|
||||
<string name="popup_remember_size_pos_summary">Memori lastan grandon kaj pozicion de ŝprucfenestro</string>
|
||||
<string name="use_inexact_seek_title">Uzi rapidan malekzaktan serĉon</string>
|
||||
@@ -100,7 +100,7 @@
|
||||
<string name="clear">Forviŝi</string>
|
||||
<string name="show_search_suggestions_title">Serĉi sugestojn</string>
|
||||
<string name="show_search_suggestions_summary">Montri sugestojn kiam serĉanto</string>
|
||||
<string name="best_resolution">Plej bona rezolucio</string>
|
||||
<string name="best_resolution">Plej bona distingumo</string>
|
||||
<string name="app_description">Libera malpeza torentado ĉe Android.</string>
|
||||
<string name="settings_category_downloads_title">Elŝuti</string>
|
||||
<string name="charset_letters_and_digits">Leteroj kaj ciferoj</string>
|
||||
@@ -110,7 +110,7 @@
|
||||
<string name="enable_search_history_title">Serĉa historio</string>
|
||||
<string name="enable_search_history_summary">Konservi la historio de serĉo lokale</string>
|
||||
<string name="enable_watch_history_title">Rigardu historion</string>
|
||||
<string name="enable_watch_history_summary">Spuri la viditajn filmetojn</string>
|
||||
<string name="enable_watch_history_summary">Protokoli spektitajn filmetojn</string>
|
||||
<string name="notification_channel_name">Sciigo de NewPipe</string>
|
||||
<string name="notification_channel_description">Sciigoj por ludilo de NewPipe</string>
|
||||
<string name="settings_category_player_title">Ludilo</string>
|
||||
@@ -121,7 +121,7 @@
|
||||
<string name="top_50">Supro 50</string>
|
||||
<string name="new_and_hot">Nova kaj varma</string>
|
||||
<string name="show_hold_to_append_title">Montri la indiko « Tenu por aldoni »</string>
|
||||
<string name="show_hold_to_append_summary">Montri indikon premante la fona aŭ la ŝprucfenestra butono en filmeta \"Detaloj:\"</string>
|
||||
<string name="show_hold_to_append_summary">Montri indikon premante la fonon aŭ la ŝprucfenestran butonon en filmeta «Detaloj:»</string>
|
||||
<string name="play_all">Ludi ĉiujn</string>
|
||||
<string name="player_stream_failure">Ne povis ludi tion torenton</string>
|
||||
<string name="player_unrecoverable_failure">Neatendebla eraro de ludilo okazis</string>
|
||||
@@ -138,7 +138,7 @@
|
||||
<string name="switch_to_main">Ŝangi al Ĉefa</string>
|
||||
<string name="always">Ĉiam</string>
|
||||
<string name="just_once">Nur unufoje</string>
|
||||
<string name="video_streams_empty">Neniuj filmeta torentoj trovitaj</string>
|
||||
<string name="video_streams_empty">Neniu filmofluo trovita</string>
|
||||
<string name="audio_streams_empty">Neniuj sonaj torentoj trovis</string>
|
||||
<string name="popup_player">Ŝprucfenestra ludilo</string>
|
||||
<string name="import_data_title">Importi la datumbazon</string>
|
||||
@@ -191,7 +191,7 @@
|
||||
<string name="clear_search_history_summary">Forviŝi la serĉajn ŝlosilvortojn</string>
|
||||
<string name="delete_search_history_alert">Ĉu vi volas forviŝi la totalon de la historio de serĉo \?</string>
|
||||
<string name="search_history_deleted">Historio de serĉo forviŝita</string>
|
||||
<string name="limit_mobile_data_usage_title">Limigi rezolucio kiam uzanta moveblan datumon</string>
|
||||
<string name="limit_mobile_data_usage_title">Limigi distingumon uzante telefonan retkonekton</string>
|
||||
<string name="minimize_on_exit_popup_description">Minimumigi al ŝprucfenestra ludilo</string>
|
||||
<string name="channels">Kanaloj</string>
|
||||
<string name="playlists">Ludlistoj</string>
|
||||
@@ -258,8 +258,8 @@
|
||||
<string name="msg_copied">Enpoŝigita</string>
|
||||
<string name="no_available_dir">Bonvolu difini elŝutan dosierujon poste en agordoj</string>
|
||||
<plurals name="views">
|
||||
<item quantity="one">%s spektaĵo</item>
|
||||
<item quantity="other">%s spektaĵoj</item>
|
||||
<item quantity="one">%s spekto</item>
|
||||
<item quantity="other">%s spektoj</item>
|
||||
</plurals>
|
||||
<string name="short_thousand">k</string>
|
||||
<string name="short_million">M</string>
|
||||
@@ -286,7 +286,7 @@
|
||||
<item quantity="one">%s abonanto</item>
|
||||
<item quantity="other">%s abonantoj</item>
|
||||
</plurals>
|
||||
<string name="no_views">Neniuj spektaĵoj</string>
|
||||
<string name="no_views">Neniuj spektoj</string>
|
||||
<string name="no_videos">Neniu filmeto</string>
|
||||
<string name="delete_item_search_history">Ĉu vi volas forviŝi tion eron el la historio de serĉo \?</string>
|
||||
<string name="main_page_content">Enhavo de la ĉefpaĝo</string>
|
||||
@@ -305,7 +305,7 @@
|
||||
<string name="drawer_open">Malfermi la tirkeston</string>
|
||||
<string name="drawer_close">Fermi la tirtekston</string>
|
||||
<string name="external_player_unsupported_link_type">Ekstaraj ludantoj ne suportas tiajn ligilojn</string>
|
||||
<string name="video_player">Filmetoludilo</string>
|
||||
<string name="video_player">Filmludilo</string>
|
||||
<string name="background_player">Fona ludilo</string>
|
||||
<string name="preferred_player_fetcher_notification_title">Akiranta informoj…</string>
|
||||
<string name="preferred_player_fetcher_notification_message">Ŝarĝante petita enhavo</string>
|
||||
@@ -347,7 +347,7 @@
|
||||
<string name="playback_pitch">Ludkampo</string>
|
||||
<string name="unhook_checkbox">Malligi (povas kaŭzi distordon)</string>
|
||||
<string name="preferred_open_action_settings_title">Preferita \'malfermi\' ago</string>
|
||||
<string name="preferred_open_action_settings_summary">Defaŭlta ago malfermante enhavo — %s</string>
|
||||
<string name="preferred_open_action_settings_summary">Implicita ago malfermante vidaŭdaĵon — %s</string>
|
||||
<string name="caption_setting_title">Subtitoloj</string>
|
||||
<string name="caption_setting_description">Modifi la dimension de la teksto kaj la fonajn stilojn de la subtitoloj de la ludilo. Ĝi bezonas restarto de la apo por efektiviĝi.</string>
|
||||
<string name="one_item_deleted">1 ero forviŝita.</string>
|
||||
@@ -369,9 +369,9 @@
|
||||
<string name="skip_silence_checkbox">Plirapidigi dum silentoj</string>
|
||||
<string name="playback_step">Paŝo</string>
|
||||
<string name="playback_reset">Restarigi</string>
|
||||
<string name="saved_tabs_invalid_json">Ne povis legi konservitajn ongletoj, tial uzante la defaŭltajn</string>
|
||||
<string name="restore_defaults">Restaŭri la defaŭltojn</string>
|
||||
<string name="restore_defaults_confirmation">Ĉu vi volas restaŭri la defaŭltojn valorojn\?</string>
|
||||
<string name="saved_tabs_invalid_json">Ne povis legi agorditajn langetojn, do uzos la implicitajn</string>
|
||||
<string name="restore_defaults">Restarigi implicitajn valorojn</string>
|
||||
<string name="restore_defaults_confirmation">Ĉu vi volas restarigi la implicitajn valorojn?</string>
|
||||
<string name="subscribers_count_not_available">Abonantoj kalkulo malhaveblas</string>
|
||||
<string name="main_page_content_summary">Kioj ongletoj estas montritaj en la ĉefpaĝo</string>
|
||||
<string name="updates_setting_title">Ĝisdatigoj</string>
|
||||
@@ -410,7 +410,7 @@
|
||||
<string name="error_progress_lost">Progreso perdita, ĉar la dosiero estis forviŝita</string>
|
||||
<string name="error_timeout">Eltempiĝo de Konekto</string>
|
||||
<string name="drawer_header_description">Ŝangi la servon, nuntempe elektita:</string>
|
||||
<string name="default_kiosk_page_summary">Defaŭlta Kiosko</string>
|
||||
<string name="default_kiosk_page_summary">Implicita Kiosko</string>
|
||||
<string name="no_one_watching">Neniu spektas</string>
|
||||
<plurals name="watching">
|
||||
<item quantity="one">%s spektanto</item>
|
||||
@@ -443,7 +443,7 @@
|
||||
<string name="videos_string">Filmetoj</string>
|
||||
<string name="permission_display_over_apps">Doni la permeson por afiŝiĝi supre aliaj apoj</string>
|
||||
<string name="app_language_title">Preferata aplingvo</string>
|
||||
<string name="systems_language">Sistemnormo</string>
|
||||
<string name="systems_language">Sistema</string>
|
||||
<string name="subtitle_activity_recaptcha">Premu “Finita” kiam solvita</string>
|
||||
<string name="done">Finita</string>
|
||||
<plurals name="seconds">
|
||||
@@ -499,9 +499,7 @@
|
||||
<string name="notification_action_repeat">Ripeti</string>
|
||||
<string name="search_showing_result_for">Montrante rezultojn pri: %s</string>
|
||||
<string name="open_with">Malfermi per</string>
|
||||
<string name="restricted_video">Tiu ĉi filmeto havas aĝlimon.
|
||||
\n
|
||||
\nŜalti \"%1$s\" en la agordoj, se vi volas vidi ĝin.</string>
|
||||
<string name="restricted_video">Tiu ĉi filmeto havas aĝminimumon. \n \nŜaltu «%1$s» en la agordoj, se vi volus vidi ĝin.</string>
|
||||
<string name="night_theme_title">Malhela etoso</string>
|
||||
<string name="notification_colorize_title">farbi sciigon</string>
|
||||
<string name="notification_action_buffering">Alŝuto</string>
|
||||
@@ -530,11 +528,11 @@
|
||||
<string name="description_tab_description">Priskribo</string>
|
||||
<string name="comments_tab_description">Komentoj</string>
|
||||
<string name="show_description_title">Montri priskribon</string>
|
||||
<string name="notification_scale_to_square_image_summary">Stuci la bildeton de la video en la sciigo de 16:9 ĝis 1:1 propocio</string>
|
||||
<string name="notification_scale_to_square_image_summary">Stuci la antaŭvidan bildeton de la filmo en la sciigo de proporcio 16:9 al 1:1</string>
|
||||
<string name="show_meta_info_title">Montri metadatumojn</string>
|
||||
<string name="mark_as_watched">Marki spektita</string>
|
||||
<string name="show_meta_info_summary">Malŝatu por kaŝi metadatumujojn kio havas aldonajn informojn pri la elsendfluisto, enhavo de la fluo, aŭ serĉpto</string>
|
||||
<string name="show_description_summary">Malŝaltu por kaŝi la videan priskribon kaj aldonan informon</string>
|
||||
<string name="show_description_summary">Malŝaltu por kaŝi priskribojn kaj aldonajn informojn de filmetoj</string>
|
||||
<string name="clear_queue_confirmation_title">Peti por konfirmo antaŭ vakigado atendvico</string>
|
||||
<string name="clear_queue_confirmation_summary">Via atendvico povas anstataŭigi se vi ŝanĝi al malsama ludilo</string>
|
||||
<string name="clear_queue_confirmation_description">La aktiva ludila atendvico anstataŭigos</string>
|
||||
@@ -549,7 +547,7 @@
|
||||
<string name="volume">Laŭteco</string>
|
||||
<string name="none">Neniu</string>
|
||||
<string name="notification_colorize_summary">Permesi al Android agordi koloron de sciigo laŭ la precipa koloro de videaĵminiaturo (noti, ke ĉi tio ne disponeblas en ĉiuj iloj)</string>
|
||||
<string name="auto_queue_toggle">Aŭtomata vicigado</string>
|
||||
<string name="auto_queue_toggle">Memaga vicigado</string>
|
||||
<string name="right_gesture_control_title">Ago de dekstra gesto</string>
|
||||
<string name="notification_actions_summary">Redakti ĉiun agon de sciigo per tuŝi gin. Elekti maksimume tri agon por montri en la kompakta sciigo per markobutonoj dekstre.</string>
|
||||
<string name="prefer_original_audio_summary">Elekti la originalan aŭdiotrakon malgraŭ lingvo</string>
|
||||
@@ -574,20 +572,19 @@
|
||||
<string name="remote_search_suggestions">Foraj serĉsugestoj</string>
|
||||
<string name="error_report_channel_description">Sciigoj por raporti erarojn</string>
|
||||
<string name="loading_metadata_title">Ŝargante metadatumoj…</string>
|
||||
<string name="hash_channel_description">Sciigo por kreado de haketaĵoj de videoj</string>
|
||||
<string name="hash_channel_description">Sciigo por kreado de haketaĵoj de filmetoj</string>
|
||||
<string name="youtube_restricted_mode_enabled_summary">YouTube provizas \"Limigitan Reĝimon\", kiu kaŝas enhavon, kiu potence maltaŭgas por infanoj</string>
|
||||
<string name="restricted_video_no_stream">Ĉi tiu video estas aĝo-limigita.
|
||||
\nPro novaj reguloj de YouTube, kiuj aplikas al aĝo-limigitaj videoj, NewPipe ne povas atingi iun ajn video-fluoj de ĉi tiu video kaj konsekvence ne povas ludi ĝin.</string>
|
||||
<string name="restricted_video_no_stream">Ĉi tiu filmeto havas aĝminimumon. \nPro novaj reguloj de YouTube, kiuj rilatas filmojn kun minimuma aĝo, NewPipe ne povas atingi ajnan fluon de ĉi tiu filmo kaj tial ne povas ludi ĝin.</string>
|
||||
<string name="error_report_channel_name">Sciigo por erar-raportoj</string>
|
||||
<string name="notifications">Sciigoj</string>
|
||||
<string name="settings_category_player_notification_title">Ludila sciigo</string>
|
||||
<string name="hash_channel_name">Sciigo por haketado de videoj</string>
|
||||
<string name="hash_channel_name">Sciigo por haketado de filmetoj</string>
|
||||
<string name="local_search_suggestions">Lokaj serĉsugestoj</string>
|
||||
<string name="start_main_player_fullscreen_title">Ŝalti ĉefan ludilon plenekrane</string>
|
||||
<string name="streams_notification_channel_description">Sciigo por novaj fluoj de abonoj</string>
|
||||
<string name="clear_cookie_title">Forigi kuketojn de reCAPTCHA</string>
|
||||
<string name="settings_category_player_notification_summary">Agordi la sciigon por ĉi-momente ludantaj datumtorentoj</string>
|
||||
<string name="start_main_player_fullscreen_summary">Ne komenci ludi videojn en la mini-ludilo, sed ŝalti plenekranan reĝimon rekte, se aŭtomata rotacio ŝlositas. Vi ankoraŭ povus atingi mini-ludilon, se vi elirus plenekranan reĝimon.</string>
|
||||
<string name="start_main_player_fullscreen_summary">Ne ekludu filmojn en la etludilo, sed ŝalti plenekranan reĝimon rekte, se memaga turniĝo estas ŝaltita. Vi ankoraŭ povus aliri la etludilon elirinte la plenekranan reĝimon.</string>
|
||||
<string name="clear_cookie_summary">Forigi kuketojn, kiujn NewPipe konservas, kiam vi solvas reCAPTCHA-taskojn</string>
|
||||
<string name="error_report_notification_title">NewPipe renkontis eraron, tuŝi por raporti</string>
|
||||
<string name="main_tabs_position_title">Pozicio de la ĉefaj langetoj</string>
|
||||
@@ -605,7 +602,7 @@
|
||||
<string name="import_subscriptions_hint">Importi aŭ eksporti abonojn per la tri-punkta menuo</string>
|
||||
<string name="msg_calculating_hash">Kalkulado de haketaĵo</string>
|
||||
<string name="faq_title">Oftaj demandoj</string>
|
||||
<string name="no_dir_yet">Neniu dosierujo por elŝutoj agordita, bonvolu elekti la defaŭltan elŝuto-dosierujon nun</string>
|
||||
<string name="no_dir_yet">Neniu dosierujo por elŝutoj agordita, bonvolu elekti la preferatan elŝuto-dosierujon nun</string>
|
||||
<string name="error_report_open_github_notice">Bonvolu certigi, ĉu erarraporto, kiu diskutas pri via eraro, jam ekzistas. Kreado de duoblaĵaj erarraportoj forprenas tempon el ni, kiun ni povus uzi por ripari la veran eraron.</string>
|
||||
<string name="related_items_tab_description">Rilatajn erojn</string>
|
||||
<string name="recaptcha_solve">Solvi</string>
|
||||
@@ -613,4 +610,13 @@
|
||||
<string name="downloads_storage_ask_summary_no_saf_notice">Oni petos al vi kien salvi ĉiujn elŝutojn</string>
|
||||
<string name="yes">Jes</string>
|
||||
<string name="no">Ne</string>
|
||||
<string name="remove_watched_popup_title">Ĉu forigu spektitajn filmetojn?</string>
|
||||
<string name="youtube_music_premium_content">Ĉu tiu filmeto sole haveblas al abonantoj de YouTube Music Premium, do ĝi ne spekteblas nek elŝuteblas de NewPipe.</string>
|
||||
<string name="audio_track_present_in_video">Sono devus jam esti en ĉi tiu fluo</string>
|
||||
<string name="no_video_streams_available_for_external_players">Neniu filmofluo ludeblas por ekstera ludilo</string>
|
||||
<string name="channel_tab_videos">Filmetoj</string>
|
||||
<string name="remove_watched_popup_warning">Filmetoj kiuj spektiĝis antaŭ aŭ post sia aldoniĝo al la ludlisto foriĝus.. \nĈu vi certas? Ĉi tio nemalfareblus!</string>
|
||||
<string name="share_playlist_with_titles_message">Kunhavigus ludliston inkluzivante informojn kiel la nomoj de listeroj, aŭ kiel simpla listo de ligiloj</string>
|
||||
<string name="reset_settings_summary">Restarigi implicitajn agordojn</string>
|
||||
<string name="remove_watched_popup_yes_and_partially_watched_videos">Jes, kaj ankaŭ parte spektitajn filmetojn</string>
|
||||
</resources>
|
@@ -47,7 +47,7 @@
|
||||
<string name="detail_uploader_thumbnail_view_description">Miniatura del avatar del usuario</string>
|
||||
<string name="content">Contenido</string>
|
||||
<string name="show_age_restricted_content_title">Mostrar contenido con restricción de edad</string>
|
||||
<string name="main_bg_subtitle">Toca la lupa para empezar.</string>
|
||||
<string name="main_bg_subtitle">Toca la lupa para comenzar..</string>
|
||||
<string name="duration_live">En directo</string>
|
||||
<string name="downloads">Descargas</string>
|
||||
<string name="downloads_title">Descargas</string>
|
||||
|
@@ -89,7 +89,7 @@
|
||||
<string name="duration_live">Otse</string>
|
||||
<string name="downloads">Allalaadimised</string>
|
||||
<string name="downloads_title">Allalaadimised</string>
|
||||
<string name="error_report_title">Vea teatamine</string>
|
||||
<string name="error_report_title">Veateade</string>
|
||||
<string name="all">Kõik</string>
|
||||
<string name="disabled">Keelatud</string>
|
||||
<string name="clear">Kustuta</string>
|
||||
@@ -369,7 +369,7 @@
|
||||
<string name="error_file_creation">Faili ei saa luua</string>
|
||||
<string name="error_ssl_exception">Turvalist ühendust ei suudetud luua</string>
|
||||
<string name="error_unknown_host">Serverit ei leitud</string>
|
||||
<string name="error_connect_host">Serveriga ei saadud ühendust</string>
|
||||
<string name="error_connect_host">Serveriga ei saa ühendust</string>
|
||||
<string name="error_http_no_content">Server ei saada andmeid</string>
|
||||
<string name="error_http_unsupported_range">Server ei toeta mitmelõimelisi allalaadimisi. Proovi uuesti kasutades @string/msg_threads = 1</string>
|
||||
<string name="error_http_not_found">Ei leitud</string>
|
||||
@@ -453,7 +453,7 @@
|
||||
<item quantity="other">%s kuulajat</item>
|
||||
</plurals>
|
||||
<string name="hash_channel_description">Teavitused video räsimise edenemise kohta</string>
|
||||
<string name="youtube_restricted_mode_enabled_title">Võta kasutusele YouTube\'i „Piiratud režiim“</string>
|
||||
<string name="youtube_restricted_mode_enabled_title">Võta kasutusele YouTube\'i „Piiratud režiim“\\</string>
|
||||
<string name="missing_file">Faili asukoht on muutunud või on ta kustutatud</string>
|
||||
<string name="watch_history_states_deleted">Taasesituste asukohad on kustutatud</string>
|
||||
<string name="delete_playback_states_alert">Kas kustutame kõik taasesituste asukohad\?</string>
|
||||
@@ -656,7 +656,7 @@
|
||||
<string name="check_for_updates">Kontrolli uuendusi</string>
|
||||
<string name="manual_update_description">Kontrolli uuendusi käsitsi</string>
|
||||
<string name="feed_new_items">Uued andmevoo kirjed</string>
|
||||
<string name="show_crash_the_player_title">Näita „Jooksuta meediamängija kokku“ nupukest</string>
|
||||
<string name="show_crash_the_player_title">Näita „Jooksuta meediamängija kokku“ nupukest\\</string>
|
||||
<string name="show_crash_the_player_summary">Näitab valikut meediamängija kokkujooksutamiseks</string>
|
||||
<string name="error_report_notification_title">NewPipe töös tekkis viga, sellest teavitamiseks toksa</string>
|
||||
<string name="crash_the_player">Jooksuta meediamängija kokku</string>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user