[Android] Fix opensubtitles crash when trying to download a file

Nicolas Pomepuy git at videolan.org
Thu Mar 26 12:20:15 UTC 2026


vlc-android | branch: master | Nicolas Pomepuy <nicolas at videolabs.io> | Wed Mar 25 10:46:13 2026 +0100| [49d5418bb4744b3f05680ded92f3bbfd3ce2a699] | committer: Nicolas Pomepuy

Fix opensubtitles crash when trying to download a file

> https://code.videolan.org/videolan/vlc-android/commit/49d5418bb4744b3f05680ded92f3bbfd3ce2a699
---

 .../resources/src/main/res/values/strings.xml      |  1 +
 .../res/layout/subtitle_download_item.xml          | 32 +++++++++++++------
 .../dialogs/SubtitleDownloaderDialogFragment.kt    | 36 +++++++++++++++-------
 .../org/videolan/vlc/gui/dialogs/SubtitleItem.kt   |  3 +-
 .../videolan/vlc/gui/dialogs/SubtitlesAdapter.kt   |  2 +-
 5 files changed, 52 insertions(+), 22 deletions(-)

diff --git a/application/resources/src/main/res/values/strings.xml b/application/resources/src/main/res/values/strings.xml
index 2c5f0401e9..8da536fa0d 100644
--- a/application/resources/src/main/res/values/strings.xml
+++ b/application/resources/src/main/res/values/strings.xml
@@ -653,6 +653,7 @@
     <string name="no_internet_connection">Check your internet connection</string>
     <string name="retry">Retry</string>
     <string name="open_subs_download_error">OpenSubtitles is currently unavailable. Please try again later</string>
+    <string name="open_subs_download_link_error">There is an issue with OpenSubtitles. Please try again later</string>
 
     <string name="network_browsing">Local Network</string>
     <string name="browsing">Browsing</string>
diff --git a/application/vlc-android/res/layout/subtitle_download_item.xml b/application/vlc-android/res/layout/subtitle_download_item.xml
index 0703016895..3ff5cd6296 100644
--- a/application/vlc-android/res/layout/subtitle_download_item.xml
+++ b/application/vlc-android/res/layout/subtitle_download_item.xml
@@ -106,6 +106,16 @@
                 app:barrierDirection="start"
                 app:constraint_referenced_ids="loading,download_sub" />
 
+        <View
+                android:id="@+id/view3"
+                android:layout_width="0dp"
+                android:layout_height="1dp"
+                android:layout_marginStart="16dp"
+                android:background="?attr/default_divider"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent" />
+
         <TextView
                 android:id="@+id/textView39"
                 android:layout_width="wrap_content"
@@ -114,9 +124,9 @@
                 android:layout_marginBottom="8dp"
                 android:gravity="center_vertical"
                 android:includeFontPadding="false"
-                android:textColor="?attr/font_default"
                 android:text="@{subtitleItem.getReadableDownloadNumber()}"
-                android:visibility="@{subtitleItem.downloadNumber > 0 ? View.VISIBLE : View.GONE}"
+                android:textColor="?attr/font_default"
+                android:visibility="@{subtitleItem.downloadNumber > 0 && !subtitleItem.downloadError ? View.VISIBLE : View.INVISIBLE}"
                 app:drawableStartCompat="@drawable/ic_download"
                 app:layout_constraintBottom_toBottomOf="parent"
                 app:layout_constraintStart_toStartOf="@+id/sub_title"
@@ -129,7 +139,7 @@
                 android:layout_height="wrap_content"
                 android:layout_marginStart="16dp"
                 android:orientation="horizontal"
-                android:visibility="@{subtitleItem.rating > 0 ? View.VISIBLE : View.GONE}"
+                android:visibility="@{subtitleItem.rating > 0 && !subtitleItem.downloadError ? View.VISIBLE : View.GONE}"
                 app:layout_constraintBottom_toBottomOf="@+id/textView39"
                 app:layout_constraintStart_toEndOf="@+id/textView39"
                 app:layout_constraintTop_toTopOf="@+id/textView39"
@@ -172,15 +182,19 @@
                     tools:srcCompat="@drawable/ic_star_border" />
         </LinearLayout>
 
-        <View
-                android:id="@+id/view3"
+        <TextView
+                android:id="@+id/textView10"
                 android:layout_width="0dp"
-                android:layout_height="1dp"
+                android:layout_height="wrap_content"
                 android:layout_marginStart="16dp"
-                android:background="?attr/default_divider"
-                app:layout_constraintBottom_toBottomOf="parent"
+                android:layout_marginEnd="16dp"
+                android:text="@string/open_subs_download_link_error"
+                android:textColor="@color/red500transparent"
+                android:visibility="@{subtitleItem.downloadError ? View.VISIBLE : View.GONE}"
+                app:layout_constraintBottom_toBottomOf="@+id/textView39"
                 app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintStart_toStartOf="parent" />
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toTopOf="@+id/textView39" />
 
     </androidx.constraintlayout.widget.ConstraintLayout>
 </layout>
diff --git a/application/vlc-android/src/org/videolan/vlc/gui/dialogs/SubtitleDownloaderDialogFragment.kt b/application/vlc-android/src/org/videolan/vlc/gui/dialogs/SubtitleDownloaderDialogFragment.kt
index c75df0eb7b..0f53a361da 100644
--- a/application/vlc-android/src/org/videolan/vlc/gui/dialogs/SubtitleDownloaderDialogFragment.kt
+++ b/application/vlc-android/src/org/videolan/vlc/gui/dialogs/SubtitleDownloaderDialogFragment.kt
@@ -5,6 +5,7 @@ import android.content.SharedPreferences
 import android.content.res.Configuration
 import android.net.Uri
 import android.os.Bundle
+import android.util.Log
 import android.view.Gravity
 import android.view.KeyEvent
 import android.view.LayoutInflater
@@ -70,20 +71,33 @@ class SubtitleDownloaderDialogFragment : VLCBottomSheetDialogFragment() {
         for (subtitleEvent in channel) if (isActive) when (subtitleEvent) {
             is SubtitleClick -> when (subtitleEvent.item.state) {
                 State.NotDownloaded -> {
-                     withContext(Dispatchers.IO) {
+                    var downloadAvailable = true
+                    withContext(Dispatchers.IO) {
+                        Log.i("SubtitleDownload", "Launching download for ${subtitleEvent.item.idSubtitle}")
                         val downloadLink = OpenSubtitleRepository.getInstance()
                             .getDownloadLink(subtitleEvent.item.fileId)
-                         val openSubtitlesLimit = OpenSubtitlesLimit(
-                             downloadLink.requests,
-                             downloadLink.requests + downloadLink.remaining,
-                             downloadLink.resetTimeUtc
-                         )
-                         OpenSubtitlesUtils.saveLimit(settings, openSubtitlesLimit)
-                         viewModel.observableLimit.set(openSubtitlesLimit)
-                         subtitleEvent.item.zipDownloadLink = downloadLink.link
-                         subtitleEvent.item.fileName = downloadLink.fileName
+                        val openSubtitlesLimit = OpenSubtitlesLimit(
+                            downloadLink.requests,
+                            downloadLink.requests + downloadLink.remaining,
+                            downloadLink.resetTimeUtc
+                        )
+                        Log.i("SubtitleDownload", "Subtitle download retrieved: ${downloadLink.link} - ${downloadLink.fileName}")
+                        try {
+                            OpenSubtitlesUtils.saveLimit(settings, openSubtitlesLimit)
+                            viewModel.observableLimit.set(openSubtitlesLimit)
+                            subtitleEvent.item.zipDownloadLink = downloadLink.link
+                            subtitleEvent.item.fileName = downloadLink.fileName
+                            subtitleEvent.item.downloadError = false
+                        } catch (e: Exception) {
+                            Log.w("SubtitleDownload", e.message, e)
+                            downloadAvailable = false
+                            subtitleEvent.item.downloadError = true
+                            withContext(Dispatchers.Main) {
+                                downloadAdapter.notifyItemChanged(downloadAdapter.dataset?.indexOf(subtitleEvent.item) ?: 0)
+                            }
+                        }
                     }
-                    VLCDownloadManager.download(requireActivity(), subtitleEvent.item, true)
+                    if (downloadAvailable) VLCDownloadManager.download(requireActivity(), subtitleEvent.item, true)
                 }
                 State.Downloaded -> deleteSubtitleDialog(requireActivity(), DialogInterface.OnClickListener { _, _ ->
                     subtitleEvent.item.mediaUri.path?.let { viewModel.deleteSubtitle(it, subtitleEvent.item.idSubtitle) }
diff --git a/application/vlc-android/src/org/videolan/vlc/gui/dialogs/SubtitleItem.kt b/application/vlc-android/src/org/videolan/vlc/gui/dialogs/SubtitleItem.kt
index c2465ca217..f8c5f0d527 100644
--- a/application/vlc-android/src/org/videolan/vlc/gui/dialogs/SubtitleItem.kt
+++ b/application/vlc-android/src/org/videolan/vlc/gui/dialogs/SubtitleItem.kt
@@ -14,7 +14,8 @@ data class SubtitleItem(
     val hearingImpaired: Boolean,
     val rating: Float,
     val downloadNumber: Long,
-    var fileName: String = ""
+    var fileName: String = "",
+    var downloadError: Boolean = false
 ) {
     fun getReadableDownloadNumber() = downloadNumber.readableNumber()
 }
diff --git a/application/vlc-android/src/org/videolan/vlc/gui/dialogs/SubtitlesAdapter.kt b/application/vlc-android/src/org/videolan/vlc/gui/dialogs/SubtitlesAdapter.kt
index 6abc9a24bd..b8b2315665 100644
--- a/application/vlc-android/src/org/videolan/vlc/gui/dialogs/SubtitlesAdapter.kt
+++ b/application/vlc-android/src/org/videolan/vlc/gui/dialogs/SubtitlesAdapter.kt
@@ -10,7 +10,7 @@ import org.videolan.vlc.databinding.SubtitleDownloadItemBinding
 import java.util.Locale
 
 internal class SubtitlesAdapter(private val eventActor: SendChannel<SubtitleEvent>) : RecyclerView.Adapter<SubtitlesAdapter.ViewHolder>() {
-    private var dataset: List<SubtitleItem>? = null
+    var dataset: List<SubtitleItem>? = null
 
     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
         val inflater = LayoutInflater.from(parent.context)



More information about the Android mailing list