Merge branch 'master' of gitlab.futo.org:videostreaming/grayjay

This commit is contained in:
Kelvin 2024-08-29 15:43:44 +02:00
commit ecca3b6793
11 changed files with 160 additions and 14 deletions

12
.gitmodules vendored
View File

@ -70,3 +70,15 @@
[submodule "app/src/unstable/assets/sources/spotify"] [submodule "app/src/unstable/assets/sources/spotify"]
path = app/src/unstable/assets/sources/spotify path = app/src/unstable/assets/sources/spotify
url = ../plugins/spotify.git url = ../plugins/spotify.git
[submodule "app/src/stable/assets/sources/bitchute"]
path = app/src/stable/assets/sources/bitchute
url = ../plugins/bitchute.git
[submodule "app/src/unstable/assets/sources/bitchute"]
path = app/src/unstable/assets/sources/bitchute
url = ../plugins/bitchute.git
[submodule "app/src/unstable/assets/sources/dailymotion"]
path = app/src/unstable/assets/sources/dailymotion
url = ../plugins/dailymotion.git
[submodule "app/src/stable/assets/sources/dailymotion"]
path = app/src/stable/assets/sources/dailymotion
url = ../plugins/dailymotion.git

View File

@ -58,6 +58,7 @@ import kotlinx.serialization.Transient
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.IOException import java.io.IOException
import java.lang.Thread.sleep
import java.time.OffsetDateTime import java.time.OffsetDateTime
import java.util.UUID import java.util.UUID
import java.util.concurrent.Executors import java.util.concurrent.Executors
@ -168,6 +169,7 @@ class VideoDownload {
this.targetBitrate = targetBitrate; this.targetBitrate = targetBitrate;
this.hasVideoRequestExecutor = video is JSSource && video.hasRequestExecutor; this.hasVideoRequestExecutor = video is JSSource && video.hasRequestExecutor;
this.requiresLiveVideoSource = false; this.requiresLiveVideoSource = false;
this.requiresLiveAudioSource = false;
this.targetVideoName = videoSource?.name; this.targetVideoName = videoSource?.name;
this.requireVideoSource = targetPixelCount != null this.requireVideoSource = targetPixelCount != null
this.requireAudioSource = targetBitrate != null; //TODO: May not be a valid check.. can only be determined after live fetch? this.requireAudioSource = targetBitrate != null; //TODO: May not be a valid check.. can only be determined after live fetch?
@ -697,7 +699,7 @@ class VideoDownload {
if(Settings.instance.downloads.byteRangeDownload && head?.containsKey("accept-ranges") == true && head.containsKey("content-length")) if(Settings.instance.downloads.byteRangeDownload && head?.containsKey("accept-ranges") == true && head.containsKey("content-length"))
{ {
val concurrency = Settings.instance.downloads.getByteRangeThreadCount(); val concurrency = Settings.instance.downloads.getByteRangeThreadCount();
Logger.i(TAG, "Download $name ByteRange Parallel (${concurrency})"); Logger.i(TAG, "Download $name ByteRange Parallel (${concurrency}): " + videoUrl);
sourceLength = head["content-length"]!!.toLong(); sourceLength = head["content-length"]!!.toLong();
onProgress(sourceLength, 0, 0); onProgress(sourceLength, 0, 0);
downloadSource_Ranges(name, client, fileStream, videoUrl, sourceLength, 1024*512, concurrency, onProgress); downloadSource_Ranges(name, client, fileStream, videoUrl, sourceLength, 1024*512, concurrency, onProgress);
@ -781,6 +783,76 @@ class VideoDownload {
onProgress(sourceLength, totalRead, 0); onProgress(sourceLength, totalRead, 0);
return sourceLength; return sourceLength;
} }
/*private fun downloadSource_Sequential(client: ManagedHttpClient, fileStream: FileOutputStream, url: String, onProgress: (Long, Long, Long) -> Unit): Long {
val progressRate: Int = 4096 * 25
var lastProgressCount: Int = 0
val speedRate: Int = 4096 * 25
var readSinceLastSpeedTest: Long = 0
var timeSinceLastSpeedTest: Long = System.currentTimeMillis()
var lastSpeed: Long = 0
var totalRead: Long = 0
var sourceLength: Long
val buffer = ByteArray(4096)
var isPartialDownload = false
var result: ManagedHttpClient.Response? = null
do {
result = client.get(url, if (isPartialDownload) hashMapOf("Range" to "bytes=$totalRead-") else hashMapOf())
if (isPartialDownload) {
if (result.code != 206)
throw IllegalStateException("Failed to download source, byte range fallback failed. Web[${result.code}] Error")
} else {
if (!result.isOk)
throw IllegalStateException("Failed to download source. Web[${result.code}] Error")
}
if (result.body == null)
throw IllegalStateException("Failed to download source. Web[${result.code}] No response")
isPartialDownload = true
sourceLength = result.body!!.contentLength()
val sourceStream = result.body!!.byteStream()
try {
while (true) {
val read = sourceStream.read(buffer)
if (read <= 0) {
break
}
fileStream.write(buffer, 0, read)
totalRead += read
readSinceLastSpeedTest += read
if (totalRead / progressRate > lastProgressCount) {
onProgress(sourceLength, totalRead, lastSpeed)
lastProgressCount++
}
if (readSinceLastSpeedTest > speedRate) {
val lastSpeedTime = timeSinceLastSpeedTest
timeSinceLastSpeedTest = System.currentTimeMillis()
val timeSince = timeSinceLastSpeedTest - lastSpeedTime
if (timeSince > 0)
lastSpeed = (readSinceLastSpeedTest / (timeSince / 1000.0)).toLong()
readSinceLastSpeedTest = 0
}
if (isCancelled)
throw CancellationException("Cancelled")
}
} catch (e: Throwable) {
Logger.w(TAG, "Sequential download was interrupted, trying to fallback to byte ranges", e)
} finally {
sourceStream.close()
result.body?.close()
}
} while (totalRead < sourceLength)
onProgress(sourceLength, totalRead, 0)
return sourceLength
}*/
private fun downloadSource_Ranges(name: String, client: ManagedHttpClient, fileStream: FileOutputStream, url: String, sourceLength: Long, rangeSize: Int, concurrency: Int = 1, onProgress: (Long, Long, Long) -> Unit) { private fun downloadSource_Ranges(name: String, client: ManagedHttpClient, fileStream: FileOutputStream, url: String, sourceLength: Long, rangeSize: Int, concurrency: Int = 1, onProgress: (Long, Long, Long) -> Unit) {
val progressRate: Int = 4096 * 5; val progressRate: Int = 4096 * 5;
var lastProgressCount: Int = 0; var lastProgressCount: Int = 0;
@ -853,18 +925,42 @@ class VideoDownload {
return tasks.map { it.get() }; return tasks.map { it.get() };
} }
private fun requestByteRange(client: ManagedHttpClient, url: String, rangeStart: Long, rangeEnd: Long): Triple<ByteArray, Long, Long> { private fun requestByteRange(client: ManagedHttpClient, url: String, rangeStart: Long, rangeEnd: Long): Triple<ByteArray, Long, Long> {
val toRead = rangeEnd - rangeStart; var retryCount = 0
val req = client.get(url, mutableMapOf(Pair("Range", "bytes=${rangeStart}-${rangeEnd}"))); var lastException: Throwable? = null
if(!req.isOk)
throw IllegalStateException("Range request failed Code [${req.code}] due to: ${req.message}");
if(req.body == null)
throw IllegalStateException("Range request failed, No body");
val read = req.body.contentLength();
if(read < toRead) while (retryCount <= 3) {
throw IllegalStateException("Byte-Range request attempted to provide less (${read} < ${toRead})"); try {
val toRead = rangeEnd - rangeStart;
val req = client.get(url, mutableMapOf(Pair("Range", "bytes=${rangeStart}-${rangeEnd}")));
if (!req.isOk) {
val bodyString = req.body?.string()
req.body?.close()
throw IllegalStateException("Range request failed Code [${req.code}] due to: ${req.message}");
}
if (req.body == null)
throw IllegalStateException("Range request failed, No body");
val read = req.body.contentLength();
return Triple(req.body.bytes(), rangeStart, rangeEnd); if (read < toRead)
throw IllegalStateException("Byte-Range request attempted to provide less (${read} < ${toRead})");
return Triple(req.body.bytes(), rangeStart, rangeEnd);
} catch (e: Throwable) {
Logger.w(TAG, "Failed to download range (url=${url} bytes=${rangeStart}-${rangeEnd})", e)
retryCount++
lastException = e
sleep(when (retryCount) {
1 -> 1000 + ((Math.random() * 300.0).toLong() - 150)
2 -> 2000 + ((Math.random() * 300.0).toLong() - 150)
3 -> 4000 + ((Math.random() * 300.0).toLong() - 150)
else -> 1000 + ((Math.random() * 300.0).toLong() - 150)
})
}
}
throw lastException!!
} }
fun validate() { fun validate() {

View File

@ -28,7 +28,12 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import okhttp3.OkHttpClient
import java.net.InetAddress
import java.net.InetSocketAddress
import java.net.Proxy
import java.net.SocketException import java.net.SocketException
import java.time.Duration
import java.time.OffsetDateTime import java.time.OffsetDateTime
class DownloadService : Service() { class DownloadService : Service() {
@ -44,7 +49,12 @@ class DownloadService : Service() {
private var _notificationManager: NotificationManager? = null; private var _notificationManager: NotificationManager? = null;
private var _notificationChannel: NotificationChannel? = null; private var _notificationChannel: NotificationChannel? = null;
private val _client = ManagedHttpClient(); private val _client = ManagedHttpClient(OkHttpClient.Builder()
//.proxy(Proxy(Proxy.Type.HTTP, InetSocketAddress(InetAddress.getByName("192.168.1.175"), 8081)))
.readTimeout(Duration.ofSeconds(30))
.writeTimeout(Duration.ofSeconds(30))
.connectTimeout(Duration.ofSeconds(30))
.callTimeout(Duration.ofMinutes(30)))
private var _started = false; private var _started = false;

View File

@ -33,6 +33,11 @@
<data android:host="bilibili.com" /> <data android:host="bilibili.com" />
<data android:host="bilibili.tv" /> <data android:host="bilibili.tv" />
<data android:host="spotify.com" /> <data android:host="spotify.com" />
<data android:host="dailymotion.com" />
<data android:host="www.dailymotion.com" />
<data android:host="bitchute.com" />
<data android:host="www.bitchute.com" />
<data android:host="old.bitchute.com" />
<data android:pathPrefix="/" /> <data android:pathPrefix="/" />
</intent-filter> </intent-filter>
<intent-filter android:autoVerify="true"> <intent-filter android:autoVerify="true">
@ -57,6 +62,11 @@
<data android:host="bilibili.com" /> <data android:host="bilibili.com" />
<data android:host="bilibili.tv" /> <data android:host="bilibili.tv" />
<data android:host="spotify.com" /> <data android:host="spotify.com" />
<data android:host="dailymotion.com" />
<data android:host="www.dailymotion.com" />
<data android:host="bitchute.com" />
<data android:host="www.bitchute.com" />
<data android:host="old.bitchute.com" />
</intent-filter> </intent-filter>
</activity> </activity>
</application> </application>

@ -0,0 +1 @@
Subproject commit 41fe8f79735176cd8a0ae8c971936cafed00fa16

@ -0,0 +1 @@
Subproject commit 069aa3d31a35559e45c1fe1ea1eb2a94d3b5d120

View File

@ -10,7 +10,9 @@
"aac9e9f0-24b5-11ee-be56-0242ac120002": "sources/patreon/PatreonConfig.json", "aac9e9f0-24b5-11ee-be56-0242ac120002": "sources/patreon/PatreonConfig.json",
"9d703ff5-c556-4962-a990-4f000829cb87": "sources/nebula/NebulaConfig.json", "9d703ff5-c556-4962-a990-4f000829cb87": "sources/nebula/NebulaConfig.json",
"cf8ea74d-ad9b-489e-a083-539b6aa8648c": "sources/bilibili/build/BiliBiliConfig.json", "cf8ea74d-ad9b-489e-a083-539b6aa8648c": "sources/bilibili/build/BiliBiliConfig.json",
"4e365633-6d3f-4267-8941-fdc36631d813": "sources/spotify/build/SpotifyConfig.json" "4e365633-6d3f-4267-8941-fdc36631d813": "sources/spotify/build/SpotifyConfig.json",
"9c87e8db-e75d-48f4-afe5-2d203d4b95c5": "sources/dailymotion/build/DailymotionConfig.json",
"e8b1ad5f-0c6d-497d-a5fa-0a785a16d902": "sources/bitchute/BitchuteConfig.json"
}, },
"SOURCES_EMBEDDED_DEFAULT": [ "SOURCES_EMBEDDED_DEFAULT": [
"35ae969a-a7db-11ed-afa1-0242ac120002" "35ae969a-a7db-11ed-afa1-0242ac120002"

View File

@ -34,6 +34,11 @@
<data android:host="bilibili.com" /> <data android:host="bilibili.com" />
<data android:host="bilibili.tv" /> <data android:host="bilibili.tv" />
<data android:host="spotify.com" /> <data android:host="spotify.com" />
<data android:host="dailymotion.com" />
<data android:host="www.dailymotion.com" />
<data android:host="bitchute.com" />
<data android:host="www.bitchute.com" />
<data android:host="old.bitchute.com" />
<data android:pathPrefix="/" /> <data android:pathPrefix="/" />
</intent-filter> </intent-filter>
<intent-filter android:autoVerify="true"> <intent-filter android:autoVerify="true">
@ -58,6 +63,11 @@
<data android:host="bilibili.com" /> <data android:host="bilibili.com" />
<data android:host="bilibili.tv" /> <data android:host="bilibili.tv" />
<data android:host="spotify.com" /> <data android:host="spotify.com" />
<data android:host="dailymotion.com" />
<data android:host="www.dailymotion.com" />
<data android:host="bitchute.com" />
<data android:host="www.bitchute.com" />
<data android:host="old.bitchute.com" />
</intent-filter> </intent-filter>
</activity> </activity>
</application> </application>

@ -0,0 +1 @@
Subproject commit 41fe8f79735176cd8a0ae8c971936cafed00fa16

@ -0,0 +1 @@
Subproject commit 069aa3d31a35559e45c1fe1ea1eb2a94d3b5d120

View File

@ -10,7 +10,9 @@
"aac9e9f0-24b5-11ee-be56-0242ac120002": "sources/patreon/PatreonConfig.json", "aac9e9f0-24b5-11ee-be56-0242ac120002": "sources/patreon/PatreonConfig.json",
"9d703ff5-c556-4962-a990-4f000829cb87": "sources/nebula/NebulaConfig.json", "9d703ff5-c556-4962-a990-4f000829cb87": "sources/nebula/NebulaConfig.json",
"cf8ea74d-ad9b-489e-a083-539b6aa8648c": "sources/bilibili/build/BiliBiliConfig.json", "cf8ea74d-ad9b-489e-a083-539b6aa8648c": "sources/bilibili/build/BiliBiliConfig.json",
"4e365633-6d3f-4267-8941-fdc36631d813": "sources/spotify/build/SpotifyConfig.json" "4e365633-6d3f-4267-8941-fdc36631d813": "sources/spotify/build/SpotifyConfig.json",
"9c87e8db-e75d-48f4-afe5-2d203d4b95c5": "sources/dailymotion/build/DailymotionConfig.json",
"e8b1ad5f-0c6d-497d-a5fa-0a785a16d902": "sources/bitchute/BitchuteConfig.json"
}, },
"SOURCES_EMBEDDED_DEFAULT": [ "SOURCES_EMBEDDED_DEFAULT": [
"35ae969a-a7db-11ed-afa1-0242ac120002" "35ae969a-a7db-11ed-afa1-0242ac120002"