mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-10-02 15:00:51 +02:00
Compare commits
163 Commits
master
...
frankenpip
Author | SHA1 | Date | |
---|---|---|---|
![]() |
6c3f31a721 | ||
![]() |
10163e1082 | ||
![]() |
0911d1ce7d | ||
![]() |
df3b56ed63 | ||
![]() |
cf351c28b0 | ||
![]() |
4409a990de | ||
![]() |
16b372dece | ||
![]() |
c02fb89359 | ||
![]() |
fbafdeb2ca | ||
![]() |
781040efaa | ||
![]() |
3f7ef49979 | ||
![]() |
dab0148a78 | ||
![]() |
c0c08a4f63 | ||
![]() |
aaf337421d | ||
![]() |
79a0edacd7 | ||
![]() |
d56eef6ece | ||
![]() |
72f054a4fa | ||
![]() |
172c3c92ac | ||
![]() |
137ef3fee4 | ||
![]() |
e49156fb11 | ||
![]() |
de5d45849f | ||
![]() |
a25034b898 | ||
![]() |
ae9e82b2c1 | ||
![]() |
a70b38a8e5 | ||
![]() |
0cff3a6ecd | ||
![]() |
9b78e49e45 | ||
![]() |
4e55f1bee6 | ||
![]() |
cff3834fde | ||
![]() |
c8b01a06b0 | ||
![]() |
414b1a8344 | ||
![]() |
404d9f3fac | ||
![]() |
55e4014036 | ||
![]() |
1cd5563b27 | ||
![]() |
1abced992b | ||
![]() |
46b9243661 | ||
![]() |
ad72b2cb31 | ||
![]() |
8b79d0ee29 | ||
![]() |
295f719b77 | ||
![]() |
b584353f4d | ||
![]() |
d73314b4dd | ||
![]() |
9f4a33c7a8 | ||
![]() |
3a9540b042 | ||
![]() |
ca855cbca0 | ||
![]() |
6a98b1dac7 | ||
![]() |
7d4a2836fc | ||
![]() |
226b6de34f | ||
![]() |
13585ca0be | ||
![]() |
62ab9bd740 | ||
![]() |
fdf36cbad6 | ||
![]() |
aea2b7c7f3 | ||
![]() |
37d1c784fa | ||
![]() |
cea149f852 | ||
![]() |
a92a28517e | ||
![]() |
800961c3d7 | ||
![]() |
9d8a79b0bd | ||
![]() |
ef56dea817 | ||
![]() |
23b3835af0 | ||
![]() |
412e1d602a | ||
![]() |
802a094154 | ||
![]() |
e6b1341246 | ||
![]() |
36ede243e3 | ||
![]() |
bac9f7eebf | ||
![]() |
8ada566bf1 | ||
![]() |
5bd4ed77df | ||
![]() |
97652ac015 | ||
![]() |
6dd24033a4 | ||
![]() |
4de3ef20be | ||
![]() |
702f74291d | ||
![]() |
d8759993a9 | ||
![]() |
7787eafd3a | ||
![]() |
4f4136c6e9 | ||
![]() |
b399030e19 | ||
![]() |
0991461d04 | ||
![]() |
49bcf2c41b | ||
![]() |
c00c6c460c | ||
![]() |
4c4fe3f511 | ||
![]() |
db485c3d77 | ||
![]() |
6471b64ab6 | ||
![]() |
b9fcf0dff8 | ||
![]() |
3177ca6e8a | ||
![]() |
5017f4f05a | ||
![]() |
823d4a041f | ||
![]() |
62d4044d6c | ||
![]() |
3785404618 | ||
![]() |
4cac111b66 | ||
![]() |
941b8eb194 | ||
![]() |
b1add13bfd | ||
![]() |
5fffee2c7d | ||
![]() |
f9340ae604 | ||
![]() |
d3a6991fd4 | ||
![]() |
b0bfd4a807 | ||
![]() |
3641698379 | ||
![]() |
2836191fb3 | ||
![]() |
0df6c7fc2c | ||
![]() |
b1ebd3ecd9 | ||
![]() |
4758244cf5 | ||
![]() |
294b9cf347 | ||
![]() |
6d05af484e | ||
![]() |
e082bca5e0 | ||
![]() |
f9dae9078e | ||
![]() |
e955beeef1 | ||
![]() |
eaac7f3f85 | ||
![]() |
ea414f57d4 | ||
![]() |
f984b26626 | ||
![]() |
edab9a6a1f | ||
![]() |
4740e3be86 | ||
![]() |
e639b02fed | ||
![]() |
ac1ca1412d | ||
![]() |
d131d3399a | ||
![]() |
1009dc4d4e | ||
![]() |
42cb914616 | ||
![]() |
e72da94eb1 | ||
![]() |
c5d94a5b60 | ||
![]() |
02c5f2607a | ||
![]() |
369a46f8fe | ||
![]() |
909d214002 | ||
![]() |
5e7e14ee4d | ||
![]() |
b092fe2c76 | ||
![]() |
b9dd7078ad | ||
![]() |
93310955f2 | ||
![]() |
9c52e039ee | ||
![]() |
be037e0756 | ||
![]() |
5bfb0449cf | ||
![]() |
0ec81c9e52 | ||
![]() |
5841eaa6d7 | ||
![]() |
e92ba8f5d1 | ||
![]() |
1908e18dc4 | ||
![]() |
e30d5e4305 | ||
![]() |
11bb2495ba | ||
![]() |
341cc37ce7 | ||
![]() |
1620668966 | ||
![]() |
56c80ce6dd | ||
![]() |
8ce9a7e43c | ||
![]() |
e05d97732e | ||
![]() |
644a345b55 | ||
![]() |
bda961a04c | ||
![]() |
ba2efded76 | ||
![]() |
b05b98ca61 | ||
![]() |
7a7f81ac7f | ||
![]() |
6e6c171dd7 | ||
![]() |
8a41c8cf66 | ||
![]() |
05271d95a9 | ||
![]() |
9d04a73c85 | ||
![]() |
d336f4cef2 | ||
![]() |
4ec7532126 | ||
![]() |
da83646303 | ||
![]() |
5062d38b65 | ||
![]() |
82b492c050 | ||
![]() |
73e3a69aaf | ||
![]() |
348a79f91d | ||
![]() |
c4ada7ff6e | ||
![]() |
39d0691c7e | ||
![]() |
71361de8ee | ||
![]() |
8aa2590fd3 | ||
![]() |
e3b7bf467e | ||
![]() |
f74402bc94 | ||
![]() |
4d3b4a7b20 | ||
![]() |
e6302cc868 | ||
![]() |
844b4edf48 | ||
![]() |
92a7f22d3c | ||
![]() |
03167a1e9c | ||
![]() |
d479f29e9b | ||
![]() |
1af798b04b |
2
.github/CONTRIBUTING.md
vendored
2
.github/CONTRIBUTING.md
vendored
@@ -6,7 +6,7 @@ NewPipe contribution guidelines
|
||||
## Crash reporting
|
||||
|
||||
Report crashes through the **automated crash report system** of NewPipe.
|
||||
This way all the data needed for debugging is included in your bug report for GitHub.
|
||||
This way all the data needed for debugging is included in your bugreport for GitHub.
|
||||
You'll see *exactly* what is sent, be able to add **your comments**, and then send it.
|
||||
|
||||
## Issue reporting/feature requests
|
||||
|
38
.github/workflows/build-release-apk.yml
vendored
38
.github/workflows/build-release-apk.yml
vendored
@@ -1,38 +0,0 @@
|
||||
name: "Build unsigned release APK on master"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: 'master'
|
||||
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '21'
|
||||
cache: 'gradle'
|
||||
|
||||
- name: "Build release APK"
|
||||
run: ./gradlew assembleRelease --stacktrace
|
||||
|
||||
- name: "Rename APK"
|
||||
run: |
|
||||
VERSION_NAME="$(jq -r ".elements[0].versionName" "app/build/outputs/apk/release/output-metadata.json")"
|
||||
echo "Version name: $VERSION_NAME" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo '```json' >> "$GITHUB_STEP_SUMMARY"
|
||||
cat "app/build/outputs/apk/release/output-metadata.json" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo >> "$GITHUB_STEP_SUMMARY"
|
||||
echo '```' >> "$GITHUB_STEP_SUMMARY"
|
||||
# assume there is only one APK in that folder
|
||||
mv app/build/outputs/apk/release/*.apk "app/build/outputs/apk/release/NewPipe_v$VERSION_NAME.apk"
|
||||
|
||||
- name: "Upload APK"
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: app
|
||||
path: app/build/outputs/apk/release/*.apk
|
1
.github/workflows/ci.yml
vendored
1
.github/workflows/ci.yml
vendored
@@ -111,7 +111,6 @@ jobs:
|
||||
path: app/build/reports/androidTests/connected/**
|
||||
|
||||
sonar:
|
||||
if: ${{ false }} # the key has expired and needs to be regenerated by the sonar admins
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
|
8
.github/workflows/image-minimizer.js
vendored
8
.github/workflows/image-minimizer.js
vendored
@@ -32,12 +32,12 @@ 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_LOOKUP = /\!\[([^\]]*)\]\((https:\/\/github\.com\/(?:user-attachments\/assets|[-\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)
|
||||
|| REGEX_ASSETS_IMAGE_LOOKUP.test(initialBody);
|
||||
|| REGEX_ASSETS_IMAGE_LOCKUP.test(initialBody);
|
||||
if (!foundSimpleImages) {
|
||||
console.log('Found no simple images to process');
|
||||
return;
|
||||
@@ -52,7 +52,7 @@ module.exports = async ({github, context}) => {
|
||||
|
||||
// Try to find and replace the images with minimized ones
|
||||
let newBody = await replaceAsync(initialBody, REGEX_USER_CONTENT_IMAGE_LOOKUP, minimizeAsync);
|
||||
newBody = await replaceAsync(newBody, REGEX_ASSETS_IMAGE_LOOKUP, minimizeAsync);
|
||||
newBody = await replaceAsync(newBody, REGEX_ASSETS_IMAGE_LOCKUP, minimizeAsync);
|
||||
|
||||
if (!wasMatchModified) {
|
||||
console.log('Nothing was modified. Skipping update');
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -10,6 +10,7 @@ captures/
|
||||
*.class
|
||||
app/debug/
|
||||
app/release/
|
||||
.kotlin/
|
||||
|
||||
# vscode / eclipse files
|
||||
*.classpath
|
||||
|
249
app/build.gradle
249
app/build.gradle
File diff suppressed because it is too large
Load Diff
48
app/check-dependencies.gradle
Normal file
48
app/check-dependencies.gradle
Normal file
@@ -0,0 +1,48 @@
|
||||
tasks.register('checkDependenciesOrder') {
|
||||
group = 'verification'
|
||||
description = 'Checks that each section in libs.versions.toml is sorted alphabetically'
|
||||
|
||||
def tomlFile = file('../gradle/libs.versions.toml')
|
||||
|
||||
doLast {
|
||||
if (!tomlFile.exists()) {
|
||||
throw new GradleException('TOML file not found')
|
||||
}
|
||||
|
||||
def lines = tomlFile.readLines()
|
||||
def nonSortedBlocks = []
|
||||
def currentBlock = []
|
||||
def prevLine = ''
|
||||
def prevIndex = 0
|
||||
|
||||
lines.eachWithIndex { line, lineIndex ->
|
||||
if (line.trim() && !line.startsWith('#')) {
|
||||
if (line.startsWith('[')) {
|
||||
prevLine = ''
|
||||
} else {
|
||||
def currIndex = lineIndex + 1
|
||||
if (prevLine > line) {
|
||||
if (currentBlock && currentBlock[-1] == "${prevIndex}: ${prevLine}") {
|
||||
currentBlock.add("${currIndex}: ${line}")
|
||||
} else {
|
||||
if (!currentBlock.isEmpty()) {
|
||||
nonSortedBlocks.add(currentBlock)
|
||||
currentBlock = []
|
||||
}
|
||||
currentBlock.add("${prevIndex}: ${prevLine}")
|
||||
currentBlock.add("${currIndex}: ${line}")
|
||||
}
|
||||
}
|
||||
prevLine = line
|
||||
prevIndex = lineIndex + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!currentBlock.isEmpty()) {
|
||||
nonSortedBlocks.add(currentBlock)
|
||||
throw new GradleException("The following lines were not sorted:\n" +
|
||||
nonSortedBlocks.collect { it.join("\n") }.join("\n\n"))
|
||||
}
|
||||
}
|
||||
}
|
7
app/proguard-rules.pro
vendored
7
app/proguard-rules.pro
vendored
@@ -5,17 +5,10 @@
|
||||
|
||||
## Rules for NewPipeExtractor
|
||||
-keep class org.schabi.newpipe.extractor.timeago.patterns.** { *; }
|
||||
## Rules for Rhino and Rhino Engine
|
||||
-keep class org.mozilla.javascript.* { *; }
|
||||
-keep class org.mozilla.javascript.** { *; }
|
||||
-keep class org.mozilla.javascript.engine.** { *; }
|
||||
-keep class org.mozilla.classfile.ClassFileWriter
|
||||
-dontwarn org.mozilla.javascript.JavaToJSONConverters
|
||||
-dontwarn org.mozilla.javascript.tools.**
|
||||
-keep class javax.script.** { *; }
|
||||
-dontwarn javax.script.**
|
||||
-keep class jdk.dynalink.** { *; }
|
||||
-dontwarn jdk.dynalink.**
|
||||
|
||||
## Rules for ExoPlayer
|
||||
-keep class com.google.android.exoplayer2.** { *; }
|
||||
|
@@ -57,15 +57,6 @@
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<service
|
||||
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
|
||||
android:enabled="false"
|
||||
android:exported="false">
|
||||
<meta-data
|
||||
android:name="autoStoreLocales"
|
||||
android:value="true" />
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:name=".player.PlayerService"
|
||||
android:exported="true"
|
||||
@@ -73,9 +64,6 @@
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.media.browse.MediaBrowserService"/>
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<activity
|
||||
@@ -89,6 +77,11 @@
|
||||
android:exported="false"
|
||||
android:label="@string/settings" />
|
||||
|
||||
<activity
|
||||
android:name=".settings.SettingsV2Activity"
|
||||
android:exported="true"
|
||||
android:label="@string/settings" />
|
||||
|
||||
<activity
|
||||
android:name=".about.AboutActivity"
|
||||
android:exported="false"
|
||||
@@ -436,10 +429,5 @@
|
||||
<meta-data
|
||||
android:name="com.samsung.android.multidisplay.keep_process_alive"
|
||||
android:value="true" />
|
||||
<!-- Android Auto -->
|
||||
<meta-data android:name="com.google.android.gms.car.application"
|
||||
android:resource="@xml/automotive_app_desc" />
|
||||
<meta-data android:name="com.google.android.gms.car.notification.SmallIcon"
|
||||
android:resource="@mipmap/ic_launcher" />
|
||||
</application>
|
||||
</manifest>
|
||||
|
@@ -1,127 +0,0 @@
|
||||
<!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>
|
File diff suppressed because it is too large
Load Diff
286
app/src/main/java/org/schabi/newpipe/App.kt
Normal file
286
app/src/main/java/org/schabi/newpipe/App.kt
Normal file
File diff suppressed because it is too large
Load Diff
22
app/src/main/java/org/schabi/newpipe/AppModule.kt
Normal file
22
app/src/main/java/org/schabi/newpipe/AppModule.kt
Normal file
@@ -0,0 +1,22 @@
|
||||
package org.schabi.newpipe
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.preference.PreferenceManager
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
class AppModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesSharedPreference(@ApplicationContext context: Context): SharedPreferences {
|
||||
return PreferenceManager.getDefaultSharedPreferences(context)
|
||||
}
|
||||
}
|
@@ -29,7 +29,7 @@ import okhttp3.ResponseBody;
|
||||
|
||||
public final class DownloaderImpl extends Downloader {
|
||||
public static final String USER_AGENT =
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0) Gecko/20100101 Firefox/140.0";
|
||||
"Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0";
|
||||
public static final String YOUTUBE_RESTRICTED_MODE_COOKIE_KEY =
|
||||
"youtube_restricted_mode_key";
|
||||
public static final String YOUTUBE_RESTRICTED_MODE_COOKIE = "PREF=f2=8000000";
|
||||
@@ -48,6 +48,11 @@ public final class DownloaderImpl extends Downloader {
|
||||
this.mCookies = new HashMap<>();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public OkHttpClient getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
/**
|
||||
* It's recommended to call exactly once in the entire lifetime of the application.
|
||||
*
|
||||
@@ -137,8 +142,7 @@ 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);
|
||||
@@ -146,33 +150,38 @@ public final class DownloaderImpl extends Downloader {
|
||||
requestBuilder.addHeader("Cookie", cookies);
|
||||
}
|
||||
|
||||
headers.forEach((headerName, headerValueList) -> {
|
||||
requestBuilder.removeHeader(headerName);
|
||||
headerValueList.forEach(headerValue ->
|
||||
requestBuilder.addHeader(headerName, headerValue));
|
||||
});
|
||||
for (final Map.Entry<String, List<String>> pair : headers.entrySet()) {
|
||||
final String headerName = pair.getKey();
|
||||
final List<String> headerValueList = pair.getValue();
|
||||
|
||||
try (
|
||||
okhttp3.Response response = client.newCall(requestBuilder.build()).execute()
|
||||
) {
|
||||
if (response.code() == 429) {
|
||||
throw new ReCaptchaException("reCaptcha Challenge requested", url);
|
||||
}
|
||||
|
||||
String responseBodyToReturn = null;
|
||||
try (ResponseBody body = response.body()) {
|
||||
if (body != null) {
|
||||
responseBodyToReturn = body.string();
|
||||
if (headerValueList.size() > 1) {
|
||||
requestBuilder.removeHeader(headerName);
|
||||
for (final String headerValue : headerValueList) {
|
||||
requestBuilder.addHeader(headerName, headerValue);
|
||||
}
|
||||
} else if (headerValueList.size() == 1) {
|
||||
requestBuilder.header(headerName, headerValueList.get(0));
|
||||
}
|
||||
|
||||
final String latestUrl = response.request().url().toString();
|
||||
return new Response(
|
||||
response.code(),
|
||||
response.message(),
|
||||
response.headers().toMultimap(),
|
||||
responseBodyToReturn,
|
||||
latestUrl);
|
||||
}
|
||||
|
||||
final okhttp3.Response response = client.newCall(requestBuilder.build()).execute();
|
||||
|
||||
if (response.code() == 429) {
|
||||
response.close();
|
||||
|
||||
throw new ReCaptchaException("reCaptcha Challenge requested", url);
|
||||
}
|
||||
|
||||
final ResponseBody body = response.body();
|
||||
String responseBodyToReturn = null;
|
||||
|
||||
if (body != null) {
|
||||
responseBodyToReturn = body.string();
|
||||
}
|
||||
|
||||
final String latestUrl = response.request().url().toString();
|
||||
return new Response(response.code(), response.message(), response.headers().toMultimap(),
|
||||
responseBodyToReturn, latestUrl);
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
268
app/src/main/java/org/schabi/newpipe/NewPlayerComponent.kt
Normal file
268
app/src/main/java/org/schabi/newpipe/NewPlayerComponent.kt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -84,6 +84,7 @@ import org.schabi.newpipe.util.ChannelTabHelper;
|
||||
import org.schabi.newpipe.util.Constants;
|
||||
import org.schabi.newpipe.util.DeviceUtils;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
import org.schabi.newpipe.util.Localization;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.PermissionHelper;
|
||||
import org.schabi.newpipe.util.ThemeHelper;
|
||||
@@ -131,6 +132,7 @@ public class RouterActivity extends AppCompatActivity {
|
||||
ThemeHelper.setDayNightMode(this);
|
||||
setTheme(ThemeHelper.isLightThemeSelected(this)
|
||||
? R.style.RouterActivityThemeLight : R.style.RouterActivityThemeDark);
|
||||
Localization.assureCorrectAppLanguage(this);
|
||||
|
||||
// Pass-through touch events to background activities
|
||||
// so that our transparent window won't lock UI in the mean time
|
||||
|
@@ -1,201 +1,31 @@
|
||||
package org.schabi.newpipe.about
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import org.schabi.newpipe.BuildConfig
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import org.schabi.newpipe.R
|
||||
import org.schabi.newpipe.databinding.ActivityAboutBinding
|
||||
import org.schabi.newpipe.databinding.FragmentAboutBinding
|
||||
import org.schabi.newpipe.util.ThemeHelper
|
||||
import org.schabi.newpipe.util.external_communication.ShareUtils
|
||||
import org.schabi.newpipe.ui.components.common.ScaffoldWithToolbar
|
||||
import org.schabi.newpipe.ui.screens.AboutScreen
|
||||
import org.schabi.newpipe.ui.theme.AppTheme
|
||||
import org.schabi.newpipe.util.Localization
|
||||
|
||||
class AboutActivity : AppCompatActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
Localization.assureCorrectAppLanguage(this)
|
||||
enableEdgeToEdge()
|
||||
super.onCreate(savedInstanceState)
|
||||
ThemeHelper.setTheme(this)
|
||||
title = getString(R.string.title_activity_about)
|
||||
|
||||
val aboutBinding = ActivityAboutBinding.inflate(layoutInflater)
|
||||
setContentView(aboutBinding.root)
|
||||
setSupportActionBar(aboutBinding.aboutToolbar)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
|
||||
// Create the adapter that will return a fragment for each of the three
|
||||
// primary sections of the activity.
|
||||
val mAboutStateAdapter = AboutStateAdapter(this)
|
||||
// Set up the ViewPager with the sections adapter.
|
||||
aboutBinding.aboutViewPager2.adapter = mAboutStateAdapter
|
||||
TabLayoutMediator(
|
||||
aboutBinding.aboutTabLayout,
|
||||
aboutBinding.aboutViewPager2
|
||||
) { tab, position ->
|
||||
tab.setText(mAboutStateAdapter.getPageTitle(position))
|
||||
}.attach()
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) {
|
||||
finish()
|
||||
return true
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
/**
|
||||
* A placeholder fragment containing a simple view.
|
||||
*/
|
||||
class AboutFragment : Fragment() {
|
||||
private fun Button.openLink(@StringRes url: Int) {
|
||||
setOnClickListener {
|
||||
ShareUtils.openUrlInApp(context, requireContext().getString(url))
|
||||
setContent {
|
||||
AppTheme {
|
||||
ScaffoldWithToolbar(
|
||||
title = stringResource(R.string.title_activity_about),
|
||||
onBackClick = { onBackPressedDispatcher.onBackPressed() }
|
||||
) { padding ->
|
||||
AboutScreen(padding)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
FragmentAboutBinding.inflate(inflater, container, false).apply {
|
||||
aboutAppVersion.text = BuildConfig.VERSION_NAME
|
||||
aboutGithubLink.openLink(R.string.github_url)
|
||||
aboutDonationLink.openLink(R.string.donation_url)
|
||||
aboutWebsiteLink.openLink(R.string.website_url)
|
||||
aboutPrivacyPolicyLink.openLink(R.string.privacy_policy_url)
|
||||
faqLink.openLink(R.string.faq_url)
|
||||
return root
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A [FragmentStateAdapter] that returns a fragment corresponding to
|
||||
* one of the sections/tabs/pages.
|
||||
*/
|
||||
private class AboutStateAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) {
|
||||
private val posAbout = 0
|
||||
private val posLicense = 1
|
||||
private val totalCount = 2
|
||||
|
||||
override fun createFragment(position: Int): Fragment {
|
||||
return when (position) {
|
||||
posAbout -> AboutFragment()
|
||||
posLicense -> LicenseFragment.newInstance(SOFTWARE_COMPONENTS)
|
||||
else -> throw IllegalArgumentException("Unknown position for ViewPager2")
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
// Show 2 total pages.
|
||||
return totalCount
|
||||
}
|
||||
|
||||
fun getPageTitle(position: Int): Int {
|
||||
return when (position) {
|
||||
posAbout -> R.string.tab_about
|
||||
posLicense -> R.string.tab_licenses
|
||||
else -> throw IllegalArgumentException("Unknown position for ViewPager2")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* List of all software components.
|
||||
*/
|
||||
private val SOFTWARE_COMPONENTS = arrayListOf(
|
||||
SoftwareComponent(
|
||||
"ACRA", "2013", "Kevin Gaudin",
|
||||
"https://github.com/ACRA/acra", StandardLicenses.APACHE2
|
||||
),
|
||||
SoftwareComponent(
|
||||
"AndroidX", "2005 - 2011", "The Android Open Source Project",
|
||||
"https://developer.android.com/jetpack", StandardLicenses.APACHE2
|
||||
),
|
||||
SoftwareComponent(
|
||||
"ExoPlayer", "2014 - 2020", "Google, Inc.",
|
||||
"https://github.com/google/ExoPlayer", StandardLicenses.APACHE2
|
||||
),
|
||||
SoftwareComponent(
|
||||
"GigaGet", "2014 - 2015", "Peter Cai",
|
||||
"https://github.com/PaperAirplane-Dev-Team/GigaGet", StandardLicenses.GPL3
|
||||
),
|
||||
SoftwareComponent(
|
||||
"Groupie", "2016", "Lisa Wray",
|
||||
"https://github.com/lisawray/groupie", StandardLicenses.MIT
|
||||
),
|
||||
SoftwareComponent(
|
||||
"Android-State", "2018", "Evernote",
|
||||
"https://github.com/Evernote/android-state", StandardLicenses.EPL1
|
||||
),
|
||||
SoftwareComponent(
|
||||
"Bridge", "2021", "Livefront",
|
||||
"https://github.com/livefront/bridge", StandardLicenses.APACHE2
|
||||
),
|
||||
SoftwareComponent(
|
||||
"Jsoup", "2009 - 2020", "Jonathan Hedley",
|
||||
"https://github.com/jhy/jsoup", StandardLicenses.MIT
|
||||
),
|
||||
SoftwareComponent(
|
||||
"Markwon", "2019", "Dimitry Ivanov",
|
||||
"https://github.com/noties/Markwon", StandardLicenses.APACHE2
|
||||
),
|
||||
SoftwareComponent(
|
||||
"Material Components for Android", "2016 - 2020", "Google, Inc.",
|
||||
"https://github.com/material-components/material-components-android",
|
||||
StandardLicenses.APACHE2
|
||||
),
|
||||
SoftwareComponent(
|
||||
"NewPipe Extractor", "2017 - 2020", "Christian Schabesberger",
|
||||
"https://github.com/TeamNewPipe/NewPipeExtractor", StandardLicenses.GPL3
|
||||
),
|
||||
SoftwareComponent(
|
||||
"NoNonsense-FilePicker", "2016", "Jonas Kalderstam",
|
||||
"https://github.com/spacecowboy/NoNonsense-FilePicker", StandardLicenses.MPL2
|
||||
),
|
||||
SoftwareComponent(
|
||||
"OkHttp", "2019", "Square, Inc.",
|
||||
"https://square.github.io/okhttp/", StandardLicenses.APACHE2
|
||||
),
|
||||
SoftwareComponent(
|
||||
"Picasso", "2013", "Square, Inc.",
|
||||
"https://square.github.io/picasso/", StandardLicenses.APACHE2
|
||||
),
|
||||
SoftwareComponent(
|
||||
"PrettyTime", "2012 - 2020", "Lincoln Baxter, III",
|
||||
"https://github.com/ocpsoft/prettytime", StandardLicenses.APACHE2
|
||||
),
|
||||
SoftwareComponent(
|
||||
"ProcessPhoenix", "2015", "Jake Wharton",
|
||||
"https://github.com/JakeWharton/ProcessPhoenix", StandardLicenses.APACHE2
|
||||
),
|
||||
SoftwareComponent(
|
||||
"RxAndroid", "2015", "The RxAndroid authors",
|
||||
"https://github.com/ReactiveX/RxAndroid", StandardLicenses.APACHE2
|
||||
),
|
||||
SoftwareComponent(
|
||||
"RxBinding", "2015", "Jake Wharton",
|
||||
"https://github.com/JakeWharton/RxBinding", StandardLicenses.APACHE2
|
||||
),
|
||||
SoftwareComponent(
|
||||
"RxJava", "2016 - 2020", "RxJava Contributors",
|
||||
"https://github.com/ReactiveX/RxJava", StandardLicenses.APACHE2
|
||||
),
|
||||
SoftwareComponent(
|
||||
"SearchPreference", "2018", "ByteHamster",
|
||||
"https://github.com/ByteHamster/SearchPreference", StandardLicenses.MIT
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +0,0 @@
|
||||
package org.schabi.newpipe.about
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.io.Serializable
|
||||
|
||||
/**
|
||||
* Class for storing information about a software license.
|
||||
*/
|
||||
@Parcelize
|
||||
class License(val name: String, val abbreviation: String, val filename: String) : Parcelable, Serializable
|
@@ -1,138 +0,0 @@
|
||||
package org.schabi.newpipe.about
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Base64
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.webkit.WebView
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.Fragment
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.Observable
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import io.reactivex.rxjava3.disposables.Disposable
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import org.schabi.newpipe.BuildConfig
|
||||
import org.schabi.newpipe.R
|
||||
import org.schabi.newpipe.databinding.FragmentLicensesBinding
|
||||
import org.schabi.newpipe.databinding.ItemSoftwareComponentBinding
|
||||
import org.schabi.newpipe.ktx.parcelableArrayList
|
||||
import org.schabi.newpipe.util.external_communication.ShareUtils
|
||||
|
||||
/**
|
||||
* Fragment containing the software licenses.
|
||||
*/
|
||||
class LicenseFragment : Fragment() {
|
||||
private lateinit var softwareComponents: List<SoftwareComponent>
|
||||
private var activeSoftwareComponent: SoftwareComponent? = null
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
softwareComponents = arguments?.parcelableArrayList<SoftwareComponent>(ARG_COMPONENTS)!!
|
||||
.sortedBy { it.name } // Sort components by name
|
||||
activeSoftwareComponent = savedInstanceState?.getSerializable(SOFTWARE_COMPONENT_KEY) as? SoftwareComponent
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
compositeDisposable.dispose()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
val binding = FragmentLicensesBinding.inflate(inflater, container, false)
|
||||
binding.licensesAppReadLicense.setOnClickListener {
|
||||
compositeDisposable.add(
|
||||
showLicense(NEWPIPE_SOFTWARE_COMPONENT)
|
||||
)
|
||||
}
|
||||
for (component in softwareComponents) {
|
||||
val componentBinding = ItemSoftwareComponentBinding
|
||||
.inflate(inflater, container, false)
|
||||
componentBinding.name.text = component.name
|
||||
componentBinding.copyright.text = getString(
|
||||
R.string.copyright,
|
||||
component.years,
|
||||
component.copyrightOwner,
|
||||
component.license.abbreviation
|
||||
)
|
||||
val root: View = componentBinding.root
|
||||
root.tag = component
|
||||
root.setOnClickListener {
|
||||
compositeDisposable.add(
|
||||
showLicense(component)
|
||||
)
|
||||
}
|
||||
binding.licensesSoftwareComponents.addView(root)
|
||||
registerForContextMenu(root)
|
||||
}
|
||||
activeSoftwareComponent?.let { compositeDisposable.add(showLicense(it)) }
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(savedInstanceState: Bundle) {
|
||||
super.onSaveInstanceState(savedInstanceState)
|
||||
activeSoftwareComponent?.let { savedInstanceState.putSerializable(SOFTWARE_COMPONENT_KEY, it) }
|
||||
}
|
||||
|
||||
private fun showLicense(
|
||||
softwareComponent: SoftwareComponent
|
||||
): Disposable {
|
||||
return if (context == null) {
|
||||
Disposable.empty()
|
||||
} else {
|
||||
val context = requireContext()
|
||||
activeSoftwareComponent = softwareComponent
|
||||
Observable.fromCallable { getFormattedLicense(context, softwareComponent.license) }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { formattedLicense ->
|
||||
val webViewData = Base64.encodeToString(
|
||||
formattedLicense.toByteArray(), Base64.NO_PADDING
|
||||
)
|
||||
val webView = WebView(context)
|
||||
webView.loadData(webViewData, "text/html; charset=UTF-8", "base64")
|
||||
|
||||
val builder = AlertDialog.Builder(requireContext())
|
||||
.setTitle(softwareComponent.name)
|
||||
.setView(webView)
|
||||
.setOnCancelListener { activeSoftwareComponent = null }
|
||||
.setOnDismissListener { activeSoftwareComponent = null }
|
||||
.setPositiveButton(R.string.done) { dialog, _ -> dialog.dismiss() }
|
||||
|
||||
if (softwareComponent != NEWPIPE_SOFTWARE_COMPONENT) {
|
||||
builder.setNeutralButton(R.string.open_website_license) { _, _ ->
|
||||
ShareUtils.openUrlInApp(requireContext(), softwareComponent.link)
|
||||
}
|
||||
}
|
||||
|
||||
builder.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ARG_COMPONENTS = "components"
|
||||
private const val SOFTWARE_COMPONENT_KEY = "ACTIVE_SOFTWARE_COMPONENT"
|
||||
private val NEWPIPE_SOFTWARE_COMPONENT = SoftwareComponent(
|
||||
"NewPipe",
|
||||
"2014-2023",
|
||||
"Team NewPipe",
|
||||
"https://newpipe.net/",
|
||||
StandardLicenses.GPL3,
|
||||
BuildConfig.VERSION_NAME
|
||||
)
|
||||
|
||||
fun newInstance(softwareComponents: ArrayList<SoftwareComponent>): LicenseFragment {
|
||||
val fragment = LicenseFragment()
|
||||
fragment.arguments = bundleOf(ARG_COMPONENTS to softwareComponents)
|
||||
return fragment
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,52 +0,0 @@
|
||||
package org.schabi.newpipe.about
|
||||
|
||||
import android.content.Context
|
||||
import org.schabi.newpipe.R
|
||||
import org.schabi.newpipe.util.ThemeHelper
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* @param context the context to use
|
||||
* @param license the license
|
||||
* @return String which contains a HTML formatted license page
|
||||
* styled according to the context's theme
|
||||
*/
|
||||
fun getFormattedLicense(context: Context, license: License): String {
|
||||
try {
|
||||
return context.assets.open(license.filename).bufferedReader().use { it.readText() }
|
||||
// split the HTML file and insert the stylesheet into the HEAD of the file
|
||||
.replace("</head>", "<style>${getLicenseStylesheet(context)}</style></head>")
|
||||
} catch (e: IOException) {
|
||||
throw IllegalArgumentException("Could not get license file: ${license.filename}", e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context the Android context
|
||||
* @return String which is a CSS stylesheet according to the context's theme
|
||||
*/
|
||||
fun getLicenseStylesheet(context: Context): String {
|
||||
val isLightTheme = ThemeHelper.isLightThemeSelected(context)
|
||||
val licenseBackgroundColor = getHexRGBColor(
|
||||
context, if (isLightTheme) R.color.light_license_background_color else R.color.dark_license_background_color
|
||||
)
|
||||
val licenseTextColor = getHexRGBColor(
|
||||
context, if (isLightTheme) R.color.light_license_text_color else R.color.dark_license_text_color
|
||||
)
|
||||
val youtubePrimaryColor = getHexRGBColor(
|
||||
context, if (isLightTheme) R.color.light_youtube_primary_color else R.color.dark_youtube_primary_color
|
||||
)
|
||||
return "body{padding:12px 15px;margin:0;background:#$licenseBackgroundColor;color:#$licenseTextColor}" +
|
||||
"a[href]{color:#$youtubePrimaryColor}pre{white-space:pre-wrap}"
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast R.color to a hexadecimal color value.
|
||||
*
|
||||
* @param context the context to use
|
||||
* @param color the color number from R.color
|
||||
* @return a six characters long String with hexadecimal RGB values
|
||||
*/
|
||||
fun getHexRGBColor(context: Context, color: Int): String {
|
||||
return context.getString(color).substring(3)
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
package org.schabi.newpipe.about
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.io.Serializable
|
||||
|
||||
@Parcelize
|
||||
class SoftwareComponent
|
||||
@JvmOverloads
|
||||
constructor(
|
||||
val name: String,
|
||||
val years: String,
|
||||
val copyrightOwner: String,
|
||||
val link: String,
|
||||
val license: License,
|
||||
val version: String? = null
|
||||
) : Parcelable, Serializable
|
@@ -1,21 +0,0 @@
|
||||
package org.schabi.newpipe.about
|
||||
|
||||
/**
|
||||
* Class containing information about standard software licenses.
|
||||
*/
|
||||
object StandardLicenses {
|
||||
@JvmField
|
||||
val GPL3 = License("GNU General Public License, Version 3.0", "GPLv3", "gpl_3.html")
|
||||
|
||||
@JvmField
|
||||
val APACHE2 = License("Apache License, Version 2.0", "ALv2", "apache2.html")
|
||||
|
||||
@JvmField
|
||||
val MPL2 = License("Mozilla Public License, Version 2.0", "MPL 2.0", "mpl2.html")
|
||||
|
||||
@JvmField
|
||||
val MIT = License("MIT License", "MIT", "mit.html")
|
||||
|
||||
@JvmField
|
||||
val EPL1 = License("Eclipse Public License, Version 1.0", "EPL 1.0", "epl1.html")
|
||||
}
|
@@ -3,8 +3,6 @@ package org.schabi.newpipe.database.history.model
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Embedded
|
||||
import org.schabi.newpipe.database.stream.model.StreamEntity
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem
|
||||
import org.schabi.newpipe.util.image.ImageStrategy
|
||||
import java.time.OffsetDateTime
|
||||
|
||||
data class StreamHistoryEntry(
|
||||
@@ -29,17 +27,4 @@ data class StreamHistoryEntry(
|
||||
return this.streamEntity.uid == other.streamEntity.uid && streamId == other.streamId &&
|
||||
accessDate.isEqual(other.accessDate)
|
||||
}
|
||||
|
||||
fun toStreamInfoItem(): StreamInfoItem =
|
||||
StreamInfoItem(
|
||||
streamEntity.serviceId,
|
||||
streamEntity.url,
|
||||
streamEntity.title,
|
||||
streamEntity.streamType,
|
||||
).apply {
|
||||
duration = streamEntity.duration
|
||||
uploaderName = streamEntity.uploader
|
||||
uploaderUrl = streamEntity.uploaderUrl
|
||||
thumbnails = ImageStrategy.dbUrlToImageList(streamEntity.thumbnailUrl)
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,5 @@
|
||||
package org.schabi.newpipe.database.playlist;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.schabi.newpipe.database.LocalItem;
|
||||
|
||||
public interface PlaylistLocalItem extends LocalItem {
|
||||
@@ -12,7 +10,4 @@ public interface PlaylistLocalItem extends LocalItem {
|
||||
long getUid();
|
||||
|
||||
void setDisplayIndex(long displayIndex);
|
||||
|
||||
@Nullable
|
||||
String getThumbnailUrl();
|
||||
}
|
||||
|
@@ -9,8 +9,6 @@ import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST
|
||||
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_THUMBNAIL_STREAM_ID;
|
||||
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_THUMBNAIL_URL;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public class PlaylistMetadataEntry implements PlaylistLocalItem {
|
||||
public static final String PLAYLIST_STREAM_COUNT = "streamCount";
|
||||
|
||||
@@ -73,10 +71,4 @@ public class PlaylistMetadataEntry implements PlaylistLocalItem {
|
||||
public void setDisplayIndex(final long displayIndex) {
|
||||
this.displayIndex = displayIndex;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getThumbnailUrl() {
|
||||
return thumbnailUrl;
|
||||
}
|
||||
}
|
||||
|
@@ -34,7 +34,7 @@ public interface PlaylistRemoteDAO extends BasicDAO<PlaylistRemoteEntity> {
|
||||
|
||||
@Query("SELECT * FROM " + REMOTE_PLAYLIST_TABLE + " WHERE "
|
||||
+ REMOTE_PLAYLIST_ID + " = :playlistId")
|
||||
Flowable<PlaylistRemoteEntity> getPlaylist(long playlistId);
|
||||
Flowable<List<PlaylistRemoteEntity>> getPlaylist(long playlistId);
|
||||
|
||||
@Query("SELECT * FROM " + REMOTE_PLAYLIST_TABLE + " WHERE "
|
||||
+ REMOTE_PLAYLIST_URL + " = :url AND " + REMOTE_PLAYLIST_SERVICE_ID + " = :serviceId")
|
||||
|
@@ -154,6 +154,6 @@ public interface PlaylistStreamDAO extends BasicDAO<PlaylistStreamEntity> {
|
||||
+ " AND :streamUrl = :streamUrl"
|
||||
|
||||
+ " GROUP BY " + JOIN_PLAYLIST_ID
|
||||
+ " ORDER BY " + PLAYLIST_DISPLAY_INDEX + ", " + PLAYLIST_NAME)
|
||||
+ " ORDER BY " + PLAYLIST_DISPLAY_INDEX)
|
||||
Flowable<List<PlaylistDuplicatesEntry>> getPlaylistDuplicatesMetadata(String streamUrl);
|
||||
}
|
||||
|
@@ -2,7 +2,6 @@ package org.schabi.newpipe.database.playlist.model;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.Ignore;
|
||||
@@ -135,8 +134,6 @@ public class PlaylistRemoteEntity implements PlaylistLocalItem {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getThumbnailUrl() {
|
||||
return thumbnailUrl;
|
||||
}
|
||||
|
@@ -8,6 +8,7 @@ import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import io.reactivex.rxjava3.core.Completable
|
||||
import io.reactivex.rxjava3.core.Flowable
|
||||
import io.reactivex.rxjava3.core.Maybe
|
||||
import org.schabi.newpipe.database.BasicDAO
|
||||
import org.schabi.newpipe.database.stream.model.StreamEntity
|
||||
import org.schabi.newpipe.database.stream.model.StreamEntity.Companion.STREAM_ID
|
||||
@@ -27,7 +28,7 @@ abstract class StreamDAO : BasicDAO<StreamEntity> {
|
||||
abstract override fun listByService(serviceId: Int): Flowable<List<StreamEntity>>
|
||||
|
||||
@Query("SELECT * FROM streams WHERE url = :url AND service_id = :serviceId")
|
||||
abstract fun getStream(serviceId: Long, url: String): Flowable<List<StreamEntity>>
|
||||
abstract fun getStream(serviceId: Long, url: String): Maybe<StreamEntity>
|
||||
|
||||
@Query("UPDATE streams SET uploader_url = :uploaderUrl WHERE url = :url AND service_id = :serviceId")
|
||||
abstract fun setUploaderUrl(serviceId: Long, url: String, uploaderUrl: String): Completable
|
||||
|
@@ -1,5 +1,8 @@
|
||||
package org.schabi.newpipe.database.stream.dao;
|
||||
|
||||
import static org.schabi.newpipe.database.stream.model.StreamStateEntity.JOIN_STREAM_ID;
|
||||
import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_STATE_TABLE;
|
||||
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
@@ -12,9 +15,7 @@ import org.schabi.newpipe.database.stream.model.StreamStateEntity;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.rxjava3.core.Flowable;
|
||||
|
||||
import static org.schabi.newpipe.database.stream.model.StreamStateEntity.JOIN_STREAM_ID;
|
||||
import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_STATE_TABLE;
|
||||
import io.reactivex.rxjava3.core.Maybe;
|
||||
|
||||
@Dao
|
||||
public interface StreamStateDAO extends BasicDAO<StreamStateEntity> {
|
||||
@@ -32,7 +33,7 @@ public interface StreamStateDAO extends BasicDAO<StreamStateEntity> {
|
||||
}
|
||||
|
||||
@Query("SELECT * FROM " + STREAM_STATE_TABLE + " WHERE " + JOIN_STREAM_ID + " = :streamId")
|
||||
Flowable<List<StreamStateEntity>> getState(long streamId);
|
||||
Maybe<StreamStateEntity> getState(long streamId);
|
||||
|
||||
@Query("DELETE FROM " + STREAM_STATE_TABLE + " WHERE " + JOIN_STREAM_ID + " = :streamId")
|
||||
int deleteState(long streamId);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user