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"]
path = app/src/unstable/assets/sources/spotify
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.FileOutputStream
import java.io.IOException
import java.lang.Thread.sleep
import java.time.OffsetDateTime
import java.util.UUID
import java.util.concurrent.Executors
@ -168,6 +169,7 @@ class VideoDownload {
this.targetBitrate = targetBitrate;
this.hasVideoRequestExecutor = video is JSSource && video.hasRequestExecutor;
this.requiresLiveVideoSource = false;
this.requiresLiveAudioSource = false;
this.targetVideoName = videoSource?.name;
this.requireVideoSource = targetPixelCount != null
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"))
{
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();
onProgress(sourceLength, 0, 0);
downloadSource_Ranges(name, client, fileStream, videoUrl, sourceLength, 1024*512, concurrency, onProgress);
@ -781,6 +783,76 @@ class VideoDownload {
onProgress(sourceLength, totalRead, 0);
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) {
val progressRate: Int = 4096 * 5;
var lastProgressCount: Int = 0;
@ -853,18 +925,42 @@ class VideoDownload {
return tasks.map { it.get() };
}
private fun requestByteRange(client: ManagedHttpClient, url: String, rangeStart: Long, rangeEnd: Long): Triple<ByteArray, Long, Long> {
val toRead = rangeEnd - rangeStart;
val req = client.get(url, mutableMapOf(Pair("Range", "bytes=${rangeStart}-${rangeEnd}")));
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();
var retryCount = 0
var lastException: Throwable? = null
if(read < toRead)
throw IllegalStateException("Byte-Range request attempted to provide less (${read} < ${toRead})");
while (retryCount <= 3) {
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() {

View file

@ -28,7 +28,12 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
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.time.Duration
import java.time.OffsetDateTime
class DownloadService : Service() {
@ -44,7 +49,12 @@ class DownloadService : Service() {
private var _notificationManager: NotificationManager? = 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;

View file

@ -33,6 +33,11 @@
<data android:host="bilibili.com" />
<data android:host="bilibili.tv" />
<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="/" />
</intent-filter>
<intent-filter android:autoVerify="true">
@ -57,6 +62,11 @@
<data android:host="bilibili.com" />
<data android:host="bilibili.tv" />
<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>
</activity>
</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",
"9d703ff5-c556-4962-a990-4f000829cb87": "sources/nebula/NebulaConfig.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": [
"35ae969a-a7db-11ed-afa1-0242ac120002"

View file

@ -34,6 +34,11 @@
<data android:host="bilibili.com" />
<data android:host="bilibili.tv" />
<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="/" />
</intent-filter>
<intent-filter android:autoVerify="true">
@ -58,6 +63,11 @@
<data android:host="bilibili.com" />
<data android:host="bilibili.tv" />
<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>
</activity>
</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",
"9d703ff5-c556-4962-a990-4f000829cb87": "sources/nebula/NebulaConfig.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": [
"35ae969a-a7db-11ed-afa1-0242ac120002"