Add the hearing unpaired flag to opensubtitles queries

This commit is contained in:
Nicolas Pomepuy 2024-10-24 11:07:44 +02:00 committed by Duncan McNamara
parent 48e4cecb6b
commit cba5d3ead4
11 changed files with 91 additions and 39 deletions

View File

@ -28,5 +28,6 @@ data class ExternalSub (
val subtitlePath: String,
val mediaPath: String,
val subLanguageID: String,
val movieReleaseName: String
val movieReleaseName: String,
val hearingImpaired: Boolean
)

View File

@ -18,11 +18,13 @@ interface IOpenSubtitleService {
@GET("subtitles")
suspend fun query( @Query("languages") languageId: String = "",
@Query("movieHash") movieHash: String? = null,
@Query("query") name: String? = null,
@Query("imdb_id") imdbId: String? = null ,
suspend fun query(
@Query("episode_number") episode: Int? = null,
@Query("hearing_impaired") hearingImpaired: String,
@Query("imdb_id") imdbId: String? = null,
@Query("languages") languageId: String = "",
@Query("moviehash") movieHash: String? = null,
@Query("query") name: String? = null,
@Query("season_number") season: Int? = null,
): OpenSubV1

View File

@ -10,15 +10,24 @@ class OpenSubtitleRepository(private val openSubtitleService: IOpenSubtitleServi
*/
suspend fun queryWithHash(movieByteSize: Long, movieHash: String?, languageIds: List<String>?): OpenSubV1 {
val actualLanguageIds = languageIds?.toSet()?.run { if (contains("") || isEmpty()) setOf("") else this } ?: setOf("")
suspend fun queryWithHash(
movieByteSize: Long,
movieHash: String?,
languageIds: List<String>?,
hearingImpaired: Boolean
): OpenSubV1 {
val actualLanguageIds =
languageIds?.toSet()?.run { if (contains("") || isEmpty()) setOf("") else this }
?: setOf("")
return openSubtitleService.query(
// movieByteSize = movieByteSize.toString(),
movieHash = movieHash ?: "",
languageId = actualLanguageIds.joinToString(","))
languageId = actualLanguageIds.sorted().joinToString(","),
hearingImpaired = if (hearingImpaired) "only" else "include"
)
}
suspend fun queryWithName(name: String, episode: Int?, season: Int?, languageIds: List<String>?): OpenSubV1 {
suspend fun queryWithName(name: String, episode: Int?, season: Int?, languageIds: List<String>?, hearingImpaired: Boolean): OpenSubV1 {
val actualEpisode = episode ?: 0
val actualSeason = season ?: 0
val actualLanguageIds = languageIds?.toSet()?.run { if (contains("") || isEmpty()) setOf("") else this } ?: setOf("")
@ -26,7 +35,10 @@ class OpenSubtitleRepository(private val openSubtitleService: IOpenSubtitleServi
name = name,
episode = actualEpisode,
season = actualSeason,
languageId = actualLanguageIds.joinToString(","))
languageId = actualLanguageIds.sorted().joinToString(","),
hearingImpaired = if (hearingImpaired) "only" else "include"
)
}
companion object {

View File

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:width="18dp"
android:height="18dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M6.03,3.2C7.15,2.44 8.51,2 10,2c3.93,0 7,3.07 7,7c0,1.26 -0.38,2.65 -1.07,3.9c-0.02,0.04 -0.05,0.08 -0.08,0.13l-1.48,-1.48C14.77,10.69 15,9.8 15,9c0,-2.8 -2.2,-5 -5,-5C9.08,4 8.24,4.26 7.5,4.67L6.03,3.2zM17.21,14.38l1.43,1.43C20.11,13.93 21,11.57 21,9c0,-3.04 -1.23,-5.79 -3.22,-7.78l-1.42,1.42C17.99,4.26 19,6.51 19,9C19,11.02 18.33,12.88 17.21,14.38zM10,6.5c-0.21,0 -0.4,0.03 -0.59,0.08l3.01,3.01C12.47,9.4 12.5,9.21 12.5,9C12.5,7.62 11.38,6.5 10,6.5zM21.19,21.19L2.81,2.81L1.39,4.22l2.13,2.13C3.19,7.16 3,8.05 3,9h2c0,-0.36 0.05,-0.71 0.12,-1.05l6.61,6.61c-0.88,0.68 -1.78,1.41 -2.27,2.9c-0.5,1.5 -1,2.01 -1.71,2.38C7.56,19.94 7.29,20 7,20c-1.1,0 -2,-0.9 -2,-2H3c0,2.21 1.79,4 4,4c0.57,0 1.13,-0.12 1.64,-0.35c1.36,-0.71 2.13,-1.73 2.73,-3.55c0.32,-0.98 0.9,-1.43 1.71,-2.05c0.03,-0.02 0.05,-0.04 0.08,-0.06l6.62,6.62L21.19,21.19z"
tools:fillColor="@color/white" />
</vector>

View File

@ -976,6 +976,7 @@
<string name="sub_result_by_name">Results for %s</string>
<string name="sub_result_by_name_season">Season %s</string>
<string name="sub_result_by_name_episode">Episode %s</string>
<string name="sub_result_by_name_hearing_impaired">only hearing impaired</string>
<string name="sub_result_by_file">Results for your file</string>
<string name="donate">Help VLC</string>

View File

@ -17,9 +17,9 @@
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="50dp"
android:focusable="true"
android:background="?attr/selectableItemBackground"
android:focusable="true"
android:minHeight="50dp"
android:nextFocusRight="@+id/download_sub"
android:nextFocusForward="@+id/download_sub">
@ -34,16 +34,29 @@
android:text="@{subtitleItem.movieReleaseName.trim()}"
android:textColor="?attr/font_default"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/language"
app:layout_constraintEnd_toStartOf="@+id/imageView20"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Attack.on.Titan.S02E01.DUBBED.HDTV.x264-W4F" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/imageView20"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:visibility="@{subtitleItem.hearingImpaired ? View.VISIBLE : View.GONE}"
app:layout_constraintBottom_toBottomOf="@+id/sub_title"
app:layout_constraintStart_toEndOf="@+id/sub_title"
app:layout_constraintTop_toTopOf="@+id/sub_title"
app:srcCompat="@drawable/ic_hearing_impaired" />
<TextView
android:id="@+id/language"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginStart="4dp"
android:layout_marginEnd="8dp"
android:fontFamily="sans-serif-light"
android:gravity="center_vertical"
@ -52,9 +65,9 @@
android:textColor="?attr/font_default"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/barrier"
app:layout_constraintEnd_toStartOf="@+id/download_sub"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/sub_title"
app:layout_constraintStart_toEndOf="@+id/imageView20"
app:layout_constraintTop_toTopOf="parent"
tools:text="En" />
@ -63,14 +76,16 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:focusable="true"
android:background="?attr/selectableItemBackgroundBorderless"
android:focusable="true"
android:importantForAccessibility="no"
android:padding="4dp"
android:src="@{subtitleItem.state == State.Downloaded ? @drawable/ic_done : @drawable/ic_download_subtitles }"
android:visibility="@{subtitleItem.state == State.Downloading ? View.GONE : View.VISIBLE}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/language"
app:layout_constraintTop_toTopOf="parent"
tools:srcCompat="@drawable/ic_download_subtitles" />

View File

@ -152,6 +152,8 @@ class SubtitleDownloaderDialogFragment : VLCBottomSheetDialogFragment() {
viewModel.observableSearchLanguage.set(selectedLanguages)
}
})
//todo
viewModel.observableSearchHearingImpaired.set(true)
binding.retryButton.setOnClickListener {
viewModel.onRefresh()

View File

@ -8,7 +8,8 @@ data class SubtitleItem (
val subLanguageID: String,
val movieReleaseName: String,
val state: State,
val zipDownloadLink: String
val zipDownloadLink: String,
val hearingImpaired: Boolean
)
enum class State {

View File

@ -44,8 +44,8 @@ class ExternalSubRepository(private val externalSubDao: ExternalSubDao, private
val downloadingSubtitles: LiveData<Map<Long, SubtitleItem>>
get() = _downloadingSubtitles as LiveData<Map<Long, SubtitleItem>>
fun saveDownloadedSubtitle(idSubtitle: String, subtitlePath: String, mediaPath: String, language: String, movieReleaseName: String): Job {
return GlobalScope.launch(coroutineContextProvider.IO) { externalSubDao.insert(org.videolan.vlc.mediadb.models.ExternalSub(idSubtitle, subtitlePath, mediaPath, language, movieReleaseName)) }
fun saveDownloadedSubtitle(idSubtitle: String, subtitlePath: String, mediaPath: String, language: String, movieReleaseName: String, hearingImpaired: Boolean): Job {
return GlobalScope.launch(coroutineContextProvider.IO) { externalSubDao.insert(org.videolan.vlc.mediadb.models.ExternalSub(idSubtitle, subtitlePath, mediaPath, language, movieReleaseName, hearingImpaired)) }
}
fun getDownloadedSubtitles(mediaUri: Uri): LiveData<List<org.videolan.vlc.mediadb.models.ExternalSub>> {

View File

@ -88,7 +88,8 @@ object VLCDownloadManager: BroadcastReceiver(), DefaultLifecycleObserver {
it,
mediaUri.path!!,
subLanguageID,
movieReleaseName
movieReleaseName,
hearingImpaired
)
}
else

View File

@ -29,6 +29,7 @@ import org.videolan.vlc.R
import org.videolan.vlc.gui.dialogs.State
import org.videolan.vlc.gui.dialogs.SubtitleItem
import org.videolan.vlc.repository.ExternalSubRepository
import org.videolan.vlc.util.TextUtils
import java.io.File
import java.util.*
@ -39,6 +40,7 @@ class SubtitlesModel(private val context: Context, private val mediaUri: Uri, pr
val observableSearchEpisode = ObservableField<String>()
val observableSearchSeason = ObservableField<String>()
val observableSearchLanguage = ObservableField<List<String>>()
val observableSearchHearingImpaired = ObservableField<Boolean>()
private var previousSearchLanguage: List<String>? = null
val manualSearchEnabled = ObservableBoolean(false)
@ -49,7 +51,7 @@ class SubtitlesModel(private val context: Context, private val mediaUri: Uri, pr
private val apiResultLiveData: MutableLiveData<List<Data>> = MutableLiveData()
private val downloadedLiveData = ExternalSubRepository.getInstance(context).getDownloadedSubtitles(mediaUri).map { list ->
list.map { SubtitleItem(it.idSubtitle, mediaUri, it.subLanguageID, it.movieReleaseName, State.Downloaded, "") }
list.map { SubtitleItem(it.idSubtitle, mediaUri, it.subLanguageID, it.movieReleaseName, State.Downloaded, "", it.hearingImpaired) }
}
private val downloadingLiveData = ExternalSubRepository.getInstance(context).downloadingSubtitles
@ -109,26 +111,28 @@ class SubtitlesModel(private val context: Context, private val mediaUri: Uri, pr
val exist = history?.find { it.idSubtitle == openSubtitle.attributes.subtitleId }
val state = exist?.state ?: State.NotDownloaded
if (openSubtitle.attributes.files.isNotEmpty())
list.add(SubtitleItem(openSubtitle.attributes.subtitleId, mediaUri, openSubtitle.attributes.language, openSubtitle.attributes.featureDetails.movieName, state, OpenSubtitleClient.getDownloadLink(openSubtitle.attributes.files.first().fileId)))
list.add(SubtitleItem(openSubtitle.attributes.subtitleId, mediaUri, openSubtitle.attributes.language, openSubtitle.attributes.featureDetails.movieName, state, OpenSubtitleClient.getDownloadLink(openSubtitle.attributes.files.first().fileId), openSubtitle.attributes.hearingImpaired))
}
list
}
private suspend fun getSubtitleByName(name: String, episode: Int?, season: Int?, languageIds: List<String>?): OpenSubV1 {
private suspend fun getSubtitleByName(name: String, episode: Int?, season: Int?, languageIds: List<String>?, hearingImpaired: Boolean): OpenSubV1 {
if (BuildConfig.DEBUG) Log.d(this::class.java.simpleName, "Getting subs by name with $name")
val builder = StringBuilder(context.getString(R.string.sub_result_by_name, "<i>$name</i>"))
season?.let { builder.append(" - ").append(context.getString(R.string.sub_result_by_name_season, "<i>$it</i>")) }
episode?.let { builder.append(" - ").append(context.getString(R.string.sub_result_by_name_episode, "<i>$it</i>")) }
season?.let { builder.append(" ${TextUtils.SEPARATOR} ").append(context.getString(R.string.sub_result_by_name_season, "<i>$it</i>")) }
episode?.let { builder.append(" ${TextUtils.SEPARATOR} ").append(context.getString(R.string.sub_result_by_name_episode, "<i>$it</i>")) }
languageIds?.let { if (languageIds.isNotEmpty()) builder.append(" ${TextUtils.SEPARATOR} ").append("<i>${it.joinToString(", ")}</i>") }
if (hearingImpaired) builder.append(" ${TextUtils.SEPARATOR} ").append(context.getString(R.string.sub_result_by_name_hearing_impaired))
observableResultDescription.set(Html.fromHtml(builder.toString()))
manualSearchEnabled.set(true)
return OpenSubtitleRepository.getInstance().queryWithName(name, episode, season, languageIds)
return OpenSubtitleRepository.getInstance().queryWithName(name, episode, season, languageIds, hearingImpaired)
}
private suspend fun getSubtitleByHash(movieByteSize: Long, movieHash: String?, languageIds: List<String>?): OpenSubV1 {
private suspend fun getSubtitleByHash(movieByteSize: Long, movieHash: String?, languageIds: List<String>?, hearingImpaired: Boolean): OpenSubV1 {
if (BuildConfig.DEBUG) Log.d(this::class.java.simpleName, "Getting subs by hash with $movieHash")
manualSearchEnabled.set(false)
observableResultDescription.set(context.getString(R.string.sub_result_by_file).toSpanned())
return OpenSubtitleRepository.getInstance().queryWithHash(movieByteSize, movieHash, languageIds)
return OpenSubtitleRepository.getInstance().queryWithHash(movieByteSize, movieHash, languageIds, hearingImpaired)
}
fun onRefresh() {
@ -155,17 +159,17 @@ class SubtitlesModel(private val context: Context, private val mediaUri: Uri, pr
if (videoFile.exists()) {
val hash = FileUtils.computeHash(videoFile)
val fileLength = videoFile.length()
val hashSubs = getSubtitleByHash(fileLength, hash, observableSearchLanguage.get()).data
val hashSubs = getSubtitleByHash(fileLength, hash, observableSearchLanguage.get(), observableSearchHearingImpaired.get() ?: false).data
// No result for hash. Falling back to name search
if (hashSubs.isEmpty()) getSubtitleByName(videoFile.name, null, null, observableSearchLanguage.get()).data else hashSubs
if (hashSubs.isEmpty()) getSubtitleByName(videoFile.name, null, null, observableSearchLanguage.get(), observableSearchHearingImpaired.get() ?: false).data else hashSubs
} else {
getSubtitleByName(name, null, null, observableSearchLanguage.get()).data
getSubtitleByName(name, null, null, observableSearchLanguage.get(), observableSearchHearingImpaired.get() ?: false).data
}
}
} else {
observableSearchName.get()?.let {
getSubtitleByName(it, observableSearchEpisode.get()?.toInt(), observableSearchSeason.get()?.toInt(), observableSearchLanguage.get()).data
getSubtitleByName(it, observableSearchEpisode.get()?.toInt(), observableSearchSeason.get()?.toInt(), observableSearchLanguage.get(), observableSearchHearingImpaired.get() ?: false).data
} ?: listOf()
}
if (isActive) apiResultLiveData.postValue(subs)