Skip to content

Commit

Permalink
Add SubScene (#923)
Browse files Browse the repository at this point in the history
* Lower targetSdk to get all installed packages

* Update sdk version

* Let's not be too radical

* Many fixes

* Revert targetSdk

* Make account homepage persistent

* Add SubScene and change subtitle API

Co-authored-by: Aymanbest <51868001+aymanbest@users.noreply.github.com>

* Fix file deletion

---------

Co-authored-by: Aymanbest <51868001+aymanbest@users.noreply.github.com>
  • Loading branch information
CranberrySoup and aymanbest committed Feb 6, 2024
1 parent 9ea7674 commit 2b7d102
Show file tree
Hide file tree
Showing 9 changed files with 333 additions and 65 deletions.
32 changes: 30 additions & 2 deletions app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import com.lagradost.cloudstream3.APIHolder.apis
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
import com.lagradost.cloudstream3.APIHolder.initAll
import com.lagradost.cloudstream3.APIHolder.updateHasTrailers
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.CommonActivity.loadThemes
Expand Down Expand Up @@ -287,9 +288,27 @@ var app = Requests(responseParser = object : ResponseParser {
class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
companion object {
const val TAG = "MAINACT"
const val ANIMATED_OUTLINE : Boolean = false
const val ANIMATED_OUTLINE: Boolean = false
var lastError: String? = null

private const val FILE_DELETE_KEY = "FILES_TO_DELETE_KEY"

/**
* Transient files to delete on application exit.
* Deletes files on onDestroy().
*/
private var filesToDelete: Set<String>
// This needs to be persistent because the application may exit without calling onDestroy.
get() = getKey<Set<String>>(FILE_DELETE_KEY) ?: setOf()
private set(value) = setKey(FILE_DELETE_KEY, value)

/**
* Add file to delete on Exit.
*/
fun deleteFileOnExit(file: File) {
filesToDelete = filesToDelete + file.path
}

/**
* Setting this will automatically enter the query in the search
* next time the search fragment is opened.
Expand Down Expand Up @@ -676,6 +695,15 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
}

override fun onDestroy() {
filesToDelete.forEach { path ->
val result = File(path).deleteRecursively()
if (result) {
Log.d(TAG, "Deleted temporary file: $path")
} else {
Log.d(TAG, "Failed to delete temporary file: $path")
}
}
filesToDelete = setOf()
val broadcastIntent = Intent()
broadcastIntent.action = "restart_service"
broadcastIntent.setClass(this, VideoDownloadRestartReceiver::class.java)
Expand Down Expand Up @@ -1654,7 +1682,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
// this ensures that no unnecessary space is taken
loadCache()
File(filesDir, "exoplayer").deleteRecursively() // old cache
File(cacheDir, "exoplayer").deleteOnExit() // current cache
deleteFileOnExit(File(cacheDir, "exoplayer")) // current cache
} catch (e: Exception) {
logError(e)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
package com.lagradost.cloudstream3.subtitles

import androidx.annotation.WorkerThread
import androidx.core.net.toUri
import com.lagradost.cloudstream3.MainActivity.Companion.deleteFileOnExit
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities.SubtitleEntity
import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities.SubtitleSearch
import com.lagradost.cloudstream3.syncproviders.AuthAPI
import com.lagradost.cloudstream3.ui.player.SubtitleOrigin
import okio.BufferedSource
import okio.buffer
import okio.sink
import okio.source
import java.io.File
import java.util.zip.ZipInputStream

interface AbstractSubProvider {
val idPrefix: String

@WorkerThread
suspend fun search(query: SubtitleSearch): List<SubtitleEntity>? {
throw NotImplementedError()
Expand All @@ -15,6 +27,98 @@ interface AbstractSubProvider {
suspend fun load(data: SubtitleEntity): String? {
throw NotImplementedError()
}

@WorkerThread
suspend fun SubtitleResource.getResources(data: SubtitleEntity) {
this.addUrl(load(data))
}

@WorkerThread
suspend fun getResource(data: SubtitleEntity): SubtitleResource {
return SubtitleResource().apply {
this.getResources(data)
}
}
}

/**
* A builder for subtitle files.
* @see addUrl
* @see addFile
*/
class SubtitleResource {
fun downloadFile(source: BufferedSource): File {
val file = File.createTempFile("temp-subtitle", ".tmp").apply {
deleteFileOnExit(this)
}
val sink = file.sink().buffer()
sink.writeAll(source)
sink.close()
source.close()

return file
}

fun unzip(file: File): List<Pair<String, File>> {
val entries = mutableListOf<Pair<String, File>>()

ZipInputStream(file.inputStream()).use { zipInputStream ->
var zipEntry = zipInputStream.nextEntry

while (zipEntry != null) {
val tempFile = File.createTempFile("unzipped-subtitle", ".tmp").apply {
deleteFileOnExit(this)
}
entries.add(zipEntry.name to tempFile)

tempFile.sink().buffer().use { buffer ->
buffer.writeAll(zipInputStream.source())
}

zipEntry = zipInputStream.nextEntry
}
}
return entries
}

data class SingleSubtitleResource(
val name: String?,
val url: String,
val origin: SubtitleOrigin
)

private var resources: MutableList<SingleSubtitleResource> = mutableListOf()

fun getSubtitles(): List<SingleSubtitleResource> {
return resources.toList()
}

fun addUrl(url: String?, name: String? = null) {
if (url == null) return
this.resources.add(
SingleSubtitleResource(name, url, SubtitleOrigin.URL)
)
}

fun addFile(file: File, name: String? = null) {
this.resources.add(
SingleSubtitleResource(name, file.toUri().toString(), SubtitleOrigin.DOWNLOADED_FILE)
)
deleteFileOnExit(file)
}

suspend fun addZipUrl(
url: String,
nameGenerator: (String, File) -> String? = { _, _ -> null }
) {
val source = app.get(url).okhttpResponse.body.source()
val zip = downloadFile(source)
val realFiles = unzip(zip)
zip.deleteRecursively()
realFiles.forEach { (name, subtitleFile) ->
addFile(subtitleFile, nameGenerator(name, subtitleFile))
}
}
}

interface AbstractSubApi : AbstractSubProvider, AuthAPI
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.lagradost.cloudstream3.syncproviders
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKeys
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.syncproviders.providers.SubScene
import com.lagradost.cloudstream3.syncproviders.providers.*
import java.util.concurrent.TimeUnit

Expand All @@ -14,6 +15,7 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI {
val simklApi = SimklApi(0)
val indexSubtitlesApi = IndexSubtitleApi()
val addic7ed = Addic7ed()
val subScene = SubScene()
val localListApi = LocalList()

// used to login via app intent
Expand Down Expand Up @@ -41,7 +43,8 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI {
get() = listOf(
openSubtitlesApi,
indexSubtitlesApi, // they got anti scraping measures in place :(
addic7ed
addic7ed,
subScene
)

const val appString = "cloudstreamapp"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,47 @@ class IndexSubtitleApi : AbstractSubApi {
companion object {
const val host = "https://indexsubtitle.com"
const val TAG = "INDEXSUBS"

fun getOrdinal(num: Int?): String? {
return when (num) {
1 -> "First"
2 -> "Second"
3 -> "Third"
4 -> "Fourth"
5 -> "Fifth"
6 -> "Sixth"
7 -> "Seventh"
8 -> "Eighth"
9 -> "Ninth"
10 -> "Tenth"
11 -> "Eleventh"
12 -> "Twelfth"
13 -> "Thirteenth"
14 -> "Fourteenth"
15 -> "Fifteenth"
16 -> "Sixteenth"
17 -> "Seventeenth"
18 -> "Eighteenth"
19 -> "Nineteenth"
20 -> "Twentieth"
21 -> "Twenty-First"
22 -> "Twenty-Second"
23 -> "Twenty-Third"
24 -> "Twenty-Fourth"
25 -> "Twenty-Fifth"
26 -> "Twenty-Sixth"
27 -> "Twenty-Seventh"
28 -> "Twenty-Eighth"
29 -> "Twenty-Ninth"
30 -> "Thirtieth"
31 -> "Thirty-First"
32 -> "Thirty-Second"
33 -> "Thirty-Third"
34 -> "Thirty-Fourth"
35 -> "Thirty-Fifth"
else -> null
}
}
}

private fun fixUrl(url: String): String {
Expand All @@ -44,47 +85,6 @@ class IndexSubtitleApi : AbstractSubApi {
}
}

private fun getOrdinal(num: Int?): String? {
return when (num) {
1 -> "First"
2 -> "Second"
3 -> "Third"
4 -> "Fourth"
5 -> "Fifth"
6 -> "Sixth"
7 -> "Seventh"
8 -> "Eighth"
9 -> "Ninth"
10 -> "Tenth"
11 -> "Eleventh"
12 -> "Twelfth"
13 -> "Thirteenth"
14 -> "Fourteenth"
15 -> "Fifteenth"
16 -> "Sixteenth"
17 -> "Seventeenth"
18 -> "Eighteenth"
19 -> "Nineteenth"
20 -> "Twentieth"
21 -> "Twenty-First"
22 -> "Twenty-Second"
23 -> "Twenty-Third"
24 -> "Twenty-Fourth"
25 -> "Twenty-Fifth"
26 -> "Twenty-Sixth"
27 -> "Twenty-Seventh"
28 -> "Twenty-Eighth"
29 -> "Twenty-Ninth"
30 -> "Thirtieth"
31 -> "Thirty-First"
32 -> "Thirty-Second"
33 -> "Thirty-Third"
34 -> "Thirty-Fourth"
35 -> "Thirty-Fifth"
else -> null
}
}

private fun isRightEps(text: String, seasonNum: Int?, epNum: Int?): Boolean {
val FILTER_EPS_REGEX =
Regex("(?i)((Chapter\\s?0?${epNum})|((Season)?\\s?0?${seasonNum}?\\s?(Episode)\\s?0?${epNum}[^0-9]))|(?i)((S?0?${seasonNum}?E0?${epNum}[^0-9])|(0?${seasonNum}[a-z]0?${epNum}[^0-9]))")
Expand Down
Loading

0 comments on commit 2b7d102

Please sign in to comment.