fix downloaded subtitles

This commit is contained in:
Blatzar 2021-12-03 23:48:30 +01:00
parent e6a85855b1
commit 9b639a06fb
3 changed files with 94 additions and 31 deletions

View file

@ -528,7 +528,7 @@ class ResultFragment : Fragment() {
val downloadList = ctx.getDownloadSubsLanguageISO639_1() val downloadList = ctx.getDownloadSubsLanguageISO639_1()
main { main {
subs?.let { subsList -> subs?.let { subsList ->
subsList.filter { downloadList.contains(SubtitleHelper.fromLanguageToTwoLetters(it.lang)) } subsList.filter { downloadList.contains(SubtitleHelper.fromLanguageToTwoLetters(it.lang, true)) }
.map { ExtractorSubtitleLink(it.lang, it.url, "") } .map { ExtractorSubtitleLink(it.lang, it.url, "") }
.forEach { link -> .forEach { link ->
val epName = meta.name ?: "${context?.getString(R.string.episode)} ${meta.episode}" val epName = meta.name ?: "${context?.getString(R.string.episode)} ${meta.episode}"

View file

@ -39,13 +39,25 @@ object SubtitleHelper {
println("ISO CREATED:\n$text") println("ISO CREATED:\n$text")
}*/ }*/
/** lang -> ISO_639_1*/ /** lang -> ISO_639_1
fun fromLanguageToTwoLetters(input: String): String? { * @param looseCheck will use .contains in addition to .equals
for (lang in languages) { * */
if (lang.languageName == input || lang.nativeName == input) { fun fromLanguageToTwoLetters(input: String, looseCheck: Boolean): String? {
return lang.ISO_639_1
languages.forEach {
if (it.languageName.equals(input, ignoreCase = true)
|| it.nativeName.equals(input, ignoreCase = true)
) return it.ISO_639_1
} }
// Runs as a separate loop as to prioritize fully matching languages.
if (looseCheck)
languages.forEach {
if (input.contains(it.languageName, ignoreCase = true)
|| input.contains(it.nativeName, ignoreCase = true)
) return it.ISO_639_1
} }
return null return null
} }

View file

@ -150,7 +150,8 @@ object VideoDownloadManager {
private const val SUCCESS_DOWNLOAD_DONE = 1 private const val SUCCESS_DOWNLOAD_DONE = 1
private const val SUCCESS_STREAM = 3 private const val SUCCESS_STREAM = 3
private const val SUCCESS_STOPPED = 2 private const val SUCCESS_STOPPED = 2
private const val ERROR_DELETING_FILE = 3 // will not download the next one, but is still classified as an error private const val ERROR_DELETING_FILE =
3 // will not download the next one, but is still classified as an error
private const val ERROR_CREATE_FILE = -2 private const val ERROR_CREATE_FILE = -2
private const val ERROR_UNKNOWN = -10 private const val ERROR_UNKNOWN = -10
private const val ERROR_OPEN_FILE = -3 private const val ERROR_OPEN_FILE = -3
@ -422,7 +423,8 @@ object VideoDownloadManager {
c.moveToFirst() c.moveToFirst()
while (true) { while (true) {
val id = c.getLong(c.getColumnIndexOrThrow(MediaStore.MediaColumns._ID)) val id = c.getLong(c.getColumnIndexOrThrow(MediaStore.MediaColumns._ID))
val name = c.getString(c.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME)) val name =
c.getString(c.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME))
val uri = ContentUris.withAppendedId( val uri = ContentUris.withAppendedId(
MediaStore.Downloads.EXTERNAL_CONTENT_URI, id MediaStore.Downloads.EXTERNAL_CONTENT_URI, id
) )
@ -450,7 +452,11 @@ object VideoDownloadManager {
* Used for getting video player subs. * Used for getting video player subs.
* @return List of pairs for the files in this format: <Name, Uri> * @return List of pairs for the files in this format: <Name, Uri>
* */ * */
fun getFolder(context: Context, relativePath: String, basePath: String?): List<Pair<String, Uri>>? { fun getFolder(
context: Context,
relativePath: String,
basePath: String?
): List<Pair<String, Uri>>? {
val base = basePathToFile(context, basePath) val base = basePathToFile(context, basePath)
val folder = base?.gotoDir(relativePath, false) val folder = base?.gotoDir(relativePath, false)
@ -472,7 +478,10 @@ object VideoDownloadManager {
} }
@RequiresApi(Build.VERSION_CODES.Q) @RequiresApi(Build.VERSION_CODES.Q)
private fun ContentResolver.getExistingDownloadUriOrNullQ(relativePath: String, displayName: String): Uri? { private fun ContentResolver.getExistingDownloadUriOrNullQ(
relativePath: String,
displayName: String
): Uri? {
try { try {
val projection = arrayOf( val projection = arrayOf(
MediaStore.MediaColumns._ID, MediaStore.MediaColumns._ID,
@ -558,10 +567,15 @@ object VideoDownloadManager {
val cr = context.contentResolver ?: return StreamData(ERROR_CONTENT_RESOLVER_NOT_FOUND) val cr = context.contentResolver ?: return StreamData(ERROR_CONTENT_RESOLVER_NOT_FOUND)
val currentExistingFile = val currentExistingFile =
cr.getExistingDownloadUriOrNullQ(folder ?: "", displayName) // CURRENT FILE WITH THE SAME PATH cr.getExistingDownloadUriOrNullQ(
folder ?: "",
displayName
) // CURRENT FILE WITH THE SAME PATH
fileLength = fileLength =
if (currentExistingFile == null || !resume) 0 else (cr.getFileLength(currentExistingFile) if (currentExistingFile == null || !resume) 0 else (cr.getFileLength(
currentExistingFile
)
?: 0)// IF NOT RESUME THEN 0, OTHERWISE THE CURRENT FILE SIZE ?: 0)// IF NOT RESUME THEN 0, OTHERWISE THE CURRENT FILE SIZE
if (!resume && currentExistingFile != null) { // DELETE FILE IF FILE EXITS AND NOT RESUME if (!resume && currentExistingFile != null) { // DELETE FILE IF FILE EXITS AND NOT RESUME
@ -580,9 +594,13 @@ object VideoDownloadManager {
MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY) // USE INSTEAD OF MediaStore.Downloads.EXTERNAL_CONTENT_URI MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY) // USE INSTEAD OF MediaStore.Downloads.EXTERNAL_CONTENT_URI
//val currentMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) //val currentMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension)
val currentMimeType = when (extension) { val currentMimeType = when (extension) {
"vtt" -> "text/vtt"
// Absolutely ridiculous, if text/vtt is used as mimetype scoped storage prevents
// downloading to /Downloads yet it works with null
"vtt" -> null // "text/vtt"
"mp4" -> "video/mp4" "mp4" -> "video/mp4"
"srt" -> "application/x-subrip"//"text/plain" "srt" -> null // "application/x-subrip"//"text/plain"
else -> null else -> null
} }
val newFile = ContentValues().apply { val newFile = ContentValues().apply {
@ -616,7 +634,8 @@ object VideoDownloadManager {
if (subDir.createFile(displayName) == null) return StreamData(ERROR_CREATE_FILE) if (subDir.createFile(displayName) == null) return StreamData(ERROR_CREATE_FILE)
} }
} }
fileStream = (subDir.findFile(displayName) ?: subDir.createFile(displayName))!!.openOutputStream() fileStream = (subDir.findFile(displayName)
?: subDir.createFile(displayName))!!.openOutputStream()
// fileStream = FileOutputStream(rFile, false) // fileStream = FileOutputStream(rFile, false)
if (fileLength == 0L) resume = false if (fileLength == 0L) resume = false
} }
@ -640,7 +659,8 @@ object VideoDownloadManager {
val basePath = context.getBasePath() val basePath = context.getBasePath()
val displayName = getDisplayName(name, extension) val displayName = getDisplayName(name, extension)
val relativePath = if (isScopedStorage && basePath.first.isDownloadDir()) getRelativePath(folder) else folder val relativePath =
if (isScopedStorage && basePath.first.isDownloadDir()) getRelativePath(folder) else folder
fun deleteFile(): Int { fun deleteFile(): Int {
return delete(context, name, relativePath, extension, parentId, basePath.first) return delete(context, name, relativePath, extension, parentId, basePath.first)
@ -690,7 +710,8 @@ object VideoDownloadManager {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // fuck android if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // fuck android
connection.contentLengthLong connection.contentLengthLong
} else { } else {
connection.getHeaderField("content-length").toLongOrNull() ?: connection.contentLength.toLong() connection.getHeaderField("content-length").toLongOrNull()
?: connection.contentLength.toLong()
} }
} catch (e: Exception) { } catch (e: Exception) {
logError(e) logError(e)
@ -753,7 +774,13 @@ object VideoDownloadManager {
} }
} }
createNotificationCallback.invoke(CreateNotificationMetadata(type, bytesDownloaded, bytesTotal)) createNotificationCallback.invoke(
CreateNotificationMetadata(
type,
bytesDownloaded,
bytesTotal
)
)
/*createNotification( /*createNotification(
context, context,
source, source,
@ -853,7 +880,15 @@ object VideoDownloadManager {
deleteFile() deleteFile()
} }
else -> { else -> {
parentId?.let { id -> downloadProgressEvent.invoke(Triple(id, bytesDownloaded, bytesTotal)) } parentId?.let { id ->
downloadProgressEvent.invoke(
Triple(
id,
bytesDownloaded,
bytesTotal
)
)
}
isDone = true isDone = true
updateNotification() updateNotification()
SUCCESS_DOWNLOAD_DONE SUCCESS_DOWNLOAD_DONE
@ -871,7 +906,10 @@ object VideoDownloadManager {
* @param directoryName if null will use the current path. * @param directoryName if null will use the current path.
* @return UniFile / null if createMissingDirectories = false and folder is not found. * @return UniFile / null if createMissingDirectories = false and folder is not found.
* */ * */
private fun UniFile.gotoDir(directoryName: String?, createMissingDirectories: Boolean = true): UniFile? { private fun UniFile.gotoDir(
directoryName: String?,
createMissingDirectories: Boolean = true
): UniFile? {
// May give this error on scoped storage. // May give this error on scoped storage.
// W/DocumentsContract: Failed to create document // W/DocumentsContract: Failed to create document
@ -945,7 +983,10 @@ object VideoDownloadManager {
@Deprecated("TODO fix UniFile to work with download directory.") @Deprecated("TODO fix UniFile to work with download directory.")
private fun getRelativePath(folder: String?): String { private fun getRelativePath(folder: String?): String {
return (Environment.DIRECTORY_DOWNLOADS + '/' + folder + '/').replace('/', File.separatorChar) return (Environment.DIRECTORY_DOWNLOADS + '/' + folder + '/').replace(
'/',
File.separatorChar
)
} }
/** /**
@ -988,7 +1029,8 @@ object VideoDownloadManager {
// If scoped storage and using download dir (not accessible with UniFile) // If scoped storage and using download dir (not accessible with UniFile)
if (isScopedStorage && basePath.isDownloadDir()) { if (isScopedStorage && basePath.isDownloadDir()) {
val relativePath = getRelativePath(folder) val relativePath = getRelativePath(folder)
val lastContent = context.contentResolver.getExistingDownloadUriOrNullQ(relativePath, displayName) val lastContent =
context.contentResolver.getExistingDownloadUriOrNullQ(relativePath, displayName)
if (lastContent != null) { if (lastContent != null) {
context.contentResolver.delete(lastContent, null, null) context.contentResolver.delete(lastContent, null, null)
} }
@ -1040,7 +1082,8 @@ object VideoDownloadManager {
var realIndex = startIndex ?: 0 var realIndex = startIndex ?: 0
val basePath = context.getBasePath() val basePath = context.getBasePath()
val relativePath = if (isScopedStorage && basePath.first.isDownloadDir()) getRelativePath(folder) else folder val relativePath =
if (isScopedStorage && basePath.first.isDownloadDir()) getRelativePath(folder) else folder
val stream = setupStream(context, name, relativePath, extension, realIndex > 0) val stream = setupStream(context, name, relativePath, extension, realIndex > 0)
if (stream.errorCode != SUCCESS_STREAM) return stream.errorCode if (stream.errorCode != SUCCESS_STREAM) return stream.errorCode
@ -1258,7 +1301,8 @@ object VideoDownloadManager {
notificationCallback: (Int, Notification) -> Unit, notificationCallback: (Int, Notification) -> Unit,
tryResume: Boolean = false, tryResume: Boolean = false,
): Int { ): Int {
val name = sanitizeFilename(ep.name ?: "${context.getString(R.string.episode)} ${ep.episode}") val name =
sanitizeFilename(ep.name ?: "${context.getString(R.string.episode)} ${ep.episode}")
if (link.isM3u8 || URI(link.url).path.endsWith(".m3u8")) { if (link.isM3u8 || URI(link.url).path.endsWith(".m3u8")) {
val startIndex = if (tryResume) { val startIndex = if (tryResume) {
@ -1323,7 +1367,11 @@ object VideoDownloadManager {
val link = item.links[index] val link = item.links[index]
val resume = pkg.linkIndex == index val resume = pkg.linkIndex == index
context.setKey(KEY_RESUME_PACKAGES, id.toString(), DownloadResumePackage(item, index)) context.setKey(
KEY_RESUME_PACKAGES,
id.toString(),
DownloadResumePackage(item, index)
)
val connectionResult = withContext(Dispatchers.IO) { val connectionResult = withContext(Dispatchers.IO) {
normalSafeApiCall { normalSafeApiCall {
downloadSingleEpisode( downloadSingleEpisode(
@ -1361,7 +1409,8 @@ object VideoDownloadManager {
} }
private fun getDownloadFileInfo(context: Context, id: Int): DownloadedFileInfoResult? { private fun getDownloadFileInfo(context: Context, id: Int): DownloadedFileInfoResult? {
val info = context.getKey<DownloadedFileInfo>(KEY_DOWNLOAD_INFO, id.toString()) ?: return null val info =
context.getKey<DownloadedFileInfo>(KEY_DOWNLOAD_INFO, id.toString()) ?: return null
val base = basePathToFile(context, info.basePath) val base = basePathToFile(context, info.basePath)
if (isScopedStorage && base.isDownloadDir()) { if (isScopedStorage && base.isDownloadDir()) {
@ -1404,7 +1453,8 @@ object VideoDownloadManager {
} }
private fun deleteFile(context: Context, id: Int): Boolean { private fun deleteFile(context: Context, id: Int): Boolean {
val info = context.getKey<DownloadedFileInfo>(KEY_DOWNLOAD_INFO, id.toString()) ?: return false val info =
context.getKey<DownloadedFileInfo>(KEY_DOWNLOAD_INFO, id.toString()) ?: return false
downloadEvent.invoke(Pair(id, DownloadActionType.Stop)) downloadEvent.invoke(Pair(id, DownloadActionType.Stop))
downloadProgressEvent.invoke(Triple(id, 0, 0)) downloadProgressEvent.invoke(Triple(id, 0, 0))
downloadStatusEvent.invoke(Pair(id, DownloadType.IsStopped)) downloadStatusEvent.invoke(Pair(id, DownloadType.IsStopped))
@ -1468,7 +1518,8 @@ object VideoDownloadManager {
private fun saveQueue(context: Context) { private fun saveQueue(context: Context) {
val dQueue = val dQueue =
downloadQueue.toList().mapIndexed { index, any -> DownloadQueueResumePackage(index, any) } downloadQueue.toList()
.mapIndexed { index, any -> DownloadQueueResumePackage(index, any) }
.toTypedArray() .toTypedArray()
context.setKey(KEY_RESUME_QUEUE_PACKAGES, dQueue) context.setKey(KEY_RESUME_QUEUE_PACKAGES, dQueue)
} }