@ -1,8 +1,9 @@
package eu.kanade.tachiyomi.animeextension.tr.turkanime
import android.app.Application
import android.content.SharedPreferences
import android.util.Base64
import android.widget.Toast
import androidx.preference.EditTextPreference
import androidx.preference.ListPreference
import androidx.preference.MultiSelectListPreference
import androidx.preference.PreferenceScreen
@ -10,12 +11,9 @@ import eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors.AlucardExtract
import eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors.EmbedgramExtractor
import eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors.MVidooExtractor
import eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors.MailRuExtractor
import eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors.MytvExtractor
import eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors.SibnetExtractor
import eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors.StreamVidExtractor
import eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors.UqloadExtractor
import eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors.VTubeExtractor
import eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors.VkExtractor
import eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors.VudeoExtractor
import eu.kanade.tachiyomi.animeextension.tr.turkanime.extractors.WolfstreamExtractor
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
@ -30,9 +28,12 @@ import eu.kanade.tachiyomi.lib.doodextractor.DoodExtractor
import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor
import eu.kanade.tachiyomi.lib.googledriveextractor.GoogleDriveExtractor
import eu.kanade.tachiyomi.lib.mp4uploadextractor.Mp4uploadExtractor
import eu.kanade.tachiyomi.lib.mytvextractor.MytvExtractor
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
import eu.kanade.tachiyomi.lib.sendvidextractor.SendvidExtractor
import eu.kanade.tachiyomi.lib.sibnetextractor.SibnetExtractor
import eu.kanade.tachiyomi.lib.synchrony.Deobfuscator
import eu.kanade.tachiyomi.lib.vkextractor.VkExtractor
import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
@ -54,6 +55,7 @@ import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
class TurkAnime : ConfigurableAnimeSource , ParsedAnimeHttpSource ( ) {
@ -67,16 +69,13 @@ class TurkAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val client = network . cloudflareClient
private val json = Json {
ignoreUnknownKeys = true
}
private val json : Json by injectLazy ( )
private val preferences : SharedPreferences by lazy {
private val preferences by lazy {
Injekt . get < Application > ( ) . getSharedPreferences ( " source_ $id " , 0x0000 )
}
// ============================== Popular ===============================
override fun popularAnimeRequest ( page : Int ) = GET ( " $baseUrl /ajax/rankagore?sayfa= $page " , xmlHeader )
override fun popularAnimeSelector ( ) = " div.panel-visible "
@ -84,11 +83,11 @@ class TurkAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun popularAnimeNextPageSelector ( ) = " button.btn-default[data-loading-text*=Sonraki] "
override fun popularAnimeFromElement ( element : Element ) : SAnime {
val animeTitle = element . select ( " div.panel-title > a " ) . first ( ) !!
val animeTitle = element . selectFirst ( " div.panel-title > a " ) !!
val name = animeTitle . attr ( " title " )
. substringBefore ( " izle " )
val img = element . select ( " img.media-object " )
val animeId = element . select ( " a.reactions " ) . first ( ) !! . attr ( " data-unique-id " )
val img = element . selectFirst ( " img.media-object " )
val animeId = element . selectFirst ( " a.reactions " ) !! . attr ( " data-unique-id " )
val animeUrl = animeTitle . attr ( " abs:href " ) . toHttpUrl ( )
. newBuilder ( )
. addQueryParameter ( " animeId " , animeId )
@ -96,12 +95,11 @@ class TurkAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
return SAnime . create ( ) . apply {
setUrlWithoutDomain ( animeUrl )
title = name
thumbnail _url = " https: " + img .attr ( " data-src " )
thumbnail _url = img ? .attr ( " abs: data-src" )
}
}
// =============================== Latest ===============================
override fun latestUpdatesRequest ( page : Int ) = GET ( " $baseUrl /ajax/yenieklenenseriler?sayfa= $page " , xmlHeader )
override fun latestUpdatesSelector ( ) = popularAnimeSelector ( )
@ -111,11 +109,10 @@ class TurkAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun latestUpdatesNextPageSelector ( ) = popularAnimeNextPageSelector ( )
// =============================== Search ===============================
override fun searchAnimeRequest ( page : Int , query : String , filters : AnimeFilterList ) =
POST (
" $baseUrl /arama?sayfa= $page " ,
Headers . headersOf ( " content-type " , " application/x-www-form-urlencoded " ) ,
headers ,
FormBody . Builder ( ) . add ( " arama " , query ) . build ( ) ,
)
@ -152,11 +149,10 @@ class TurkAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun searchAnimeNextPageSelector ( ) = popularAnimeNextPageSelector ( )
// =========================== Anime Details ============================
override fun animeDetailsParse ( document : Document ) : SAnime {
val img = document . select ( " div.imaj > img.media-object " ) . ifEmpty { null }
val studio = document . select ( " div#animedetay > table tr:contains(Stüdyo) > td:last-child a " ) . ifEmpty { null }
val desc = document . select ( " div#animedetay p.ozet " ) . ifEmpty { null }
val img = document . selectFirst ( " div.imaj > img.media-object " )
val studio = document . selectFirst ( " div#animedetay > table tr:contains(Stüdyo) > td:last-child a " )
val desc = document . selectFirst ( " div#animedetay p.ozet " )
val genres = document . select ( " div#animedetay > table tr:contains(Anime Türü) > td:last-child a " )
. ifEmpty { null }
return SAnime . create ( ) . apply {
@ -169,24 +165,23 @@ class TurkAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
}
// ============================== Episodes ==============================
override fun episodeListRequest ( anime : SAnime ) : Request {
val animeId = ( baseUrl + anime . url ) . toHttpUrl ( ) . queryParameter ( " animeId " )
?: client . newCall ( GET ( baseUrl + anime . url ) ) . execute ( ) . asJsoup ( )
. selectFirst ( " a[data-unique-id] " ) !! . attr ( " data-unique-id " )
return GET ( " https://www.turkanime.co /ajax/bolumler?animeId=$animeId " , xmlHeader )
return GET ( " $baseUrl /ajax/bolumler?animeId=$animeId " , xmlHeader )
}
override fun episodeListSelector ( ) = " ul.menum li "
override fun episodeFromElement ( element : Element ) : SEpisode {
val a = element . select ( " a:has(span.bolumAdi) " )
val a = element . selectFirst ( " a:has(span.bolumAdi) " ) !!
val title = a . attr ( " title " )
val substring = title . substringBefore ( " . Bölüm " )
val numIdx = substring . indexOfLast { ! it . isDigit ( ) } + 1
val numbers = substring . slice ( numIdx .. substring . lastIndex )
return SEpisode . create ( ) . apply {
setUrlWithoutDomain ( " https: " + a . attr ( " href " ) )
setUrlWithoutDomain ( a . attr ( " abs: href" ) )
name = title
episode _number = numbers . toFloatOrNull ( ) ?: 1F
}
@ -196,20 +191,26 @@ class TurkAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
super . episodeListParse ( response ) . reversed ( )
// ============================ Video Links =============================
override fun videoListParse ( response : Response ) : List < Video > {
val document = response . asJsoup ( )
val fansubbers = document . select ( " div#videodetay div.pull-right button " )
val videoList = if ( fansubbers . size == 1 ) {
getVideosFromHosters ( document , fansubbers . first ( ) !! . text ( ) . trim ( ) )
} else {
val videos = mutableListOf < Video > ( )
fansubbers . parallelMap {
val allFansubs = PREF _FANSUB _SELECTION _ENTRIES
val chosenFansubs = preferences . getStringSet ( PREF _FANSUB _SELECTION _KEY , allFansubs . toSet ( ) ) !!
val filteredSubs = fansubbers . toList ( ) . filter {
val subName = it . text ( ) . substringBeforeLast ( " BD " ) . trim ( )
chosenFansubs . any ( subName :: contains ) || allFansubs . none ( subName :: contains )
}
filteredSubs . parallelMap {
val url = it . attr ( " onclick " ) . trimOnClick ( )
val subDoc = client . newCall ( GET ( url , xmlHeader ) ) . execute ( ) . asJsoup ( )
videos . addAll ( getVideosFromHosters( subDoc , it . text ( ) . trim ( ) ) )
}
videos
getVideosFromHosters ( subDoc , it . text ( ) . trim ( ) )
} . flatten ( )
}
require ( videoList . isNotEmpty ( ) ) { " Failed to extract videos " }
@ -223,26 +224,31 @@ class TurkAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val hosterSelection = preferences . getStringSet ( PREF _HOSTER _KEY , PREF _HOSTER _DEFAULT ) !!
val videoList = mutableListOf < Video > ( )
val videoList = buildList {
val selectedHosterName = selectedHoster . text ( ) . trim ( )
if ( selectedHosterName in SUPPORTED _HOSTERS && selectedHosterName in hosterSelection ) {
val src = document . select ( " iframe " ) .attr ( " src " )
videoList . addAll ( getVideosFromSource ( src , selectedHosterName , subber ) )
document . selectFirst ( " iframe " ) ? .attr ( " src " ) ?. also { src ->
addAll ( getVideosFromSource ( src , selectedHosterName , subber ) )
}
}
hosters . parallelMap {
val hosterName = it . text ( ) . trim ( )
if ( hosterName !in SUPPORTED _HOSTERS ) return @parallelMap
if ( hosterName !in hosterSelection ) return @parallelMap
val url = it . attr ( " onclick " ) . trimOnClick ( )
val videoDoc = client . newCall ( GET ( url , xmlHeader ) ) . execute ( ) . asJsoup ( )
val src = videoDoc . select ( " iframe " ) .attr ( " src " ) . replace ( " ^// " . toRegex ( ) , " https:// " )
videoList . addAll ( getVideosFromSource ( src , hosterName , subber ) )
val src = videoDoc . selectFirst ( " iframe " ) ? .attr ( " src " )
?. replace ( " ^// " . toRegex ( ) , " https:// " )
?: return @parallelMap
addAll ( getVideosFromSource ( src , hosterName , subber ) )
}
}
return videoList
}
private fun getVideosFromSource ( src : String , hosterName : String , subber : String ) : List < Video > {
val videoList = mutableListOf < Video > ( )
val cipherParamsEncoded = src
. substringAfter ( " /embed/#/url/ " )
. substringBefore ( " ?status " )
@ -255,72 +261,72 @@ class TurkAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val hosterLink = " https: " + decryptParams ( cipherParams )
runCatching {
val videoList = runCatching {
when ( hosterName ) {
" ALUCARD(BETA) " -> {
videoList . addAll ( AlucardExtractor( client , json , baseUrl ) . extractVideos ( hosterLink , subber ) )
AlucardExtractor ( client , json , baseUrl ) . extractVideos ( hosterLink , subber )
}
" DOODSTREAM " -> {
videoList . addAll ( DoodExtractor( client ) . videosFromUrl ( hosterLink , " $subber : DOODSTREAM " , redirect = false ) )
DoodExtractor ( client ) . videosFromUrl ( hosterLink , " $subber : DOODSTREAM " , redirect = false )
}
" EMBEDGRAM " -> {
videoList . addAll ( EmbedgramExtractor( client , headers ) . videosFromUrl ( hosterLink , prefix = " $subber : " ) )
EmbedgramExtractor ( client , headers ) . videosFromUrl ( hosterLink , prefix = " $subber : " )
}
" FILEMOON " -> {
videoList . addAll ( FilemoonExtractor( client ) . videosFromUrl ( hosterLink , prefix = " $subber : " , headers = headers ) )
FilemoonExtractor ( client ) . videosFromUrl ( hosterLink , prefix = " $subber : " , headers = headers )
}
" GDRIVE " -> {
Regex ( """ [\w-]{28,} """ ) . find ( hosterLink ) ?. groupValues ?. get ( 0 ) ?. let {
videoList . addAll ( GoogleDriveExtractor( client , headers ) . videosFromUrl ( " https://drive.google.com/uc?id= $it " , " $subber : Gdrive " ) )
GoogleDriveExtractor ( client , headers ) . videosFromUrl ( " https://drive.google.com/uc?id= $it " , " $subber : Gdrive " )
}
}
" MAIL " -> {
videoList . addAll ( MailRuExtractor( client , headers ) . videosFromUrl ( hosterLink , prefix = " $subber : " ) )
MailRuExtractor ( client , headers ) . videosFromUrl ( hosterLink , prefix = " $subber : " )
}
" MP4UPLOAD " -> {
videoList . addAll ( Mp4uploadExtractor( client ) . videosFromUrl ( hosterLink , headers , prefix = " $subber : " ) )
Mp4uploadExtractor ( client ) . videosFromUrl ( hosterLink , headers , prefix = " $subber : " )
}
" MYVI " -> {
videoList . addAll ( MytvExtractor( client ) . videosFromUrl ( hosterLink , prefix = " $subber : " ) )
MytvExtractor ( client ) . videosFromUrl ( hosterLink , prefix = " $subber : " )
}
" MVIDOO " -> {
videoList . addAll ( MVidooExtractor( client ) . videosFromUrl ( hosterLink , prefix = " $subber : " ) )
MVidooExtractor ( client ) . videosFromUrl ( hosterLink , prefix = " $subber : " )
}
" ODNOKLASSNIKI " -> {
videoList . addAll ( OkruExtractor( client ) . videosFromUrl ( hosterLink , prefix = " $subber : " ) )
OkruExtractor ( client ) . videosFromUrl ( hosterLink , prefix = " $subber : " )
}
" SENDVID " -> {
videoList . addAll ( SendvidExtractor( client , headers ) . videosFromUrl ( hosterLink , prefix = " $subber : " ) )
SendvidExtractor ( client , headers ) . videosFromUrl ( hosterLink , prefix = " $subber : " )
}
" SIBNET " -> {
videoList . addAll ( SibnetExtractor( client ) . getV ideosFromUrl( hosterLink , prefix = " $subber : " ) )
SibnetExtractor ( client ) . v ideosFromUrl( hosterLink , prefix = " $subber : " )
}
" STREAMVID " -> {
videoList . addAll ( StreamVidExtractor( client ) . videosFromUrl ( hosterLink , headers , prefix = " $subber : " ) )
StreamVidExtractor ( client ) . videosFromUrl ( hosterLink , headers , prefix = " $subber : " )
}
" UQLOAD " -> {
videoList . addAll ( UqloadExtractor( client ) . videosFromUrl ( hosterLink , headers , " $subber : Uqload " ) )
UqloadExtractor ( client ) . videosFromUrl ( hosterLink , headers , " $subber : Uqload " )
}
" VK " -> {
val vkUrl = " https://vk.com " + hosterLink . substringAfter ( " vk.com " )
videoList . addAll ( VkExtractor( client ) . getV ideosFromUrl( vkUrl , prefix = " $subber : " ) )
VkExtractor ( client , headers ). v ideosFromUrl( vkUrl , prefix = " $subber : " )
}
" VOE " -> {
VoeExtractor ( client ) . videoFromUrl ( hosterLink , " $subber : VOE " ) ?. let { video -> videoList . add ( video ) }
VoeExtractor ( client ) . videoFromUrl ( hosterLink , " $subber : VOE " ) ?. let ( :: listOf )
}
" VTUBE " -> {
videoList . addAll ( VTubeExtractor( client , headers ) . videosFromUrl ( hosterLink , baseUrl , prefix = " $subber : " ) )
VTubeExtractor ( client , headers ) . videosFromUrl ( hosterLink , baseUrl , prefix = " $subber : " )
}
" VUDEA " -> {
videoList . addAll ( VudeoExtractor( client ) . videosFromUrl ( hosterLink , prefix = " $subber : " ) )
VudeoExtractor ( client ) . videosFromUrl ( hosterLink , prefix = " $subber : " )
}
" WOLFSTREAM " -> {
videoList . addAll ( WolfstreamExtractor( client ) . videosFromUrl ( hosterLink , prefix = " $subber : " ) )
}
else -> { }
WolfstreamExtractor ( client ) . videosFromUrl ( hosterLink , prefix = " $subber : " )
}
else -> null
}
} . getOrNull ( ) ?: emptyList ( )
return videoList
}
@ -332,13 +338,14 @@ class TurkAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun videoUrlParse ( document : Document ) : String = throw Exception ( " not used " )
// ============================= Utilities ==============================
override fun List < Video > . sort ( ) : List < Video > {
val quality = preferences . getString ( PREF _QUALITY _KEY , PREF _QUALITY _DEFAULT ) !!
return this . sortedWith (
return sortedWith (
compareBy (
{ it . quality . contains ( quality ) } ,
{ it . quality . contains ( quality ) } , // preferred quality first
{ it . quality . substringBefore ( " : " ) } , // then group by fansub
// then group by quality
{ Regex ( """ (\d+)p """ ) . find ( it . quality ) ?. groupValues ?. get ( 1 ) ?. toIntOrNull ( ) ?: 0 } ,
) ,
) . reversed ( )
@ -346,11 +353,7 @@ class TurkAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
@Serializable
private data class CipherParams (
@Serializable
val ct : String ,
@Serializable
val iv : String ,
@Serializable
val s : String ,
)
@ -437,25 +440,91 @@ class TurkAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
" WOLFSTREAM " ,
)
private val DEFAULT _SUBS by lazy {
setOf (
" Adonis " ,
" Aitr " ,
" Akatsuki " ,
" AkiraSubs " ,
" AniKeyf " ,
" ANS " ,
" AnimeMangaTR " ,
" AnimeOU " ,
" AniSekai " ,
" AniTürk " ,
" AoiSubs " ,
" ARE-YOU-SURE " ,
" AnimeWho " ,
" Benihime " ,
" Chevirman " ,
" Fatality " ,
" Hikigaya " ,
" HolySubs " ,
" Kirigana Fairies " ,
" Lawsonia Sub " ,
" LowSubs " ,
" Magnus357 " ,
" Momo & Berhann " ,
" NoaSubs " ,
" OrigamiSubs " ,
" Pijamalı Koi " ,
" Puzzlesubs " ,
" RaionSubs " ,
" ShimazuSubs " ,
" SoutenSubs " ,
" TAÇE " ,
" TRanimeizle " ,
" TR Altyazı lı " ,
" Uragiri " ,
" Varsayı lan " ,
" YukiSubs " ,
)
}
private const val PREF _KEY _KEY = " key "
private const val DEFAULT _KEY = " 710^8A@3@>T2}#zN5xK?kR7KNKb@-A!LzYL5~M1qU0UfdWsZoBm4UUat%}ueUv6E--*hDPPbH7K2bp9^3o41hw,khL:}Kx8080@M "
private const val PREF _QUALITY _KEY = " preferred_quality "
private const val PREF _QUALITY _TITLE = " Preferred quality "
private const val PREF _QUALITY _DEFAULT = " 1080 "
private val PREF _QUALITY _ENTRIES = arrayOf ( " 1080p " , " 720p " , " 480p " , " 360p " )
private val PREF _QUALITY _VALUES = arrayOf ( " 1080 " , " 720 " , " 480 " , " 360 " )
private const val PREF _HOSTER _KEY = " hoster_selection "
private const val PREF _HOSTER _TITLE = " Enable/Disable Hosts "
private val PREF _HOSTER _DEFAULT = setOf ( " GDRIVE " , " VOE " )
// Copypasted from tr/tranimeizle.
private const val PREF _FANSUB _SELECTION _KEY = " pref_fansub_selection "
private const val PREF _FANSUB _SELECTION _TITLE = " Enable/Disable Fansubs "
private const val PREF _ADDITIONAL _FANSUBS _KEY = " pref_additional_fansubs_key "
private const val PREF _ADDITIONAL _FANSUBS _TITLE = " Add custom fansubs to the selection preference "
private const val PREF _ADDITIONAL _FANSUBS _DEFAULT = " "
private const val PREF _ADDITIONAL _FANSUBS _DIALOG _TITLE = " Enter a list of additional fansubs, separated by a comma. "
private const val PREF _ADDITIONAL _FANSUBS _DIALOG _MESSAGE = " Example: AntichristHaters Fansub, 2cm erect subs "
private const val PREF _ADDITIONAL _FANSUBS _SUMMARY = " You can add more fansubs to the previous preference from here. "
private const val PREF _ADDITIONAL _FANSUBS _TOAST = " Reopen the extension's preferences for it to take effect. "
}
// =============================== Preferences ===============================
private val PREF _FANSUB _SELECTION _ENTRIES : Array < String > get ( ) {
val additional = preferences . getString ( PREF _ADDITIONAL _FANSUBS _KEY , " " ) !!
. split ( " , " )
. map ( String :: trim )
. filter ( String :: isNotBlank )
. toSet ( )
return ( DEFAULT _SUBS + additional ) . sorted ( ) . toTypedArray ( )
}
// =============================== Preferences ==============================
@Suppress ( " UNCHECKED_CAST " )
override fun setupPreferenceScreen ( screen : PreferenceScreen ) {
ListPreference ( screen . context ) . apply {
key = PREF _QUALITY _KEY
title = " Preferred quality "
entries = arrayOf ( " 1080p " , " 720p " , " 480p " , " 360p " )
entryValues = arrayOf ( " 1080 " , " 720 " , " 480 " , " 360 " )
title = PREF _QUALITY _TITLE
entries = PREF _QUALITY _ENTRIES
entryValues = PREF _QUALITY _VALUES
setDefaultValue ( PREF _QUALITY _DEFAULT )
summary = " %s "
@ -469,7 +538,7 @@ class TurkAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
MultiSelectListPreference ( screen . context ) . apply {
key = PREF _HOSTER _KEY
title = " Enable/Disable Hosts "
title = PREF _HOSTER _TITLE
entries = SUPPORTED_HOSTERS . toTypedArray ( )
entryValues = SUPPORTED_HOSTERS . toTypedArray ( )
setDefaultValue ( PREF _HOSTER _DEFAULT )
@ -478,5 +547,37 @@ class TurkAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
preferences . edit ( ) . putStringSet ( key , newValue as Set < String > ) . commit ( )
}
} . also ( screen :: addPreference )
MultiSelectListPreference ( screen . context ) . apply {
key = PREF _FANSUB _SELECTION _KEY
title = PREF _FANSUB _SELECTION _TITLE
PREF_FANSUB_SELECTION_ENTRIES . let {
entries = it
entryValues = it
setDefaultValue ( it . toSet ( ) )
}
setOnPreferenceChangeListener { _ , newValue ->
@Suppress ( " UNCHECKED_CAST " )
preferences . edit ( ) . putStringSet ( key , newValue as Set < String > ) . commit ( )
}
} . also ( screen :: addPreference )
EditTextPreference ( screen . context ) . apply {
key = PREF _ADDITIONAL _FANSUBS _KEY
title = PREF _ADDITIONAL _FANSUBS _TITLE
dialogTitle = PREF _ADDITIONAL _FANSUBS _DIALOG _TITLE
dialogMessage = PREF _ADDITIONAL _FANSUBS _DIALOG _MESSAGE
setDefaultValue ( PREF _ADDITIONAL _FANSUBS _DEFAULT )
summary = PREF _ADDITIONAL _FANSUBS _SUMMARY
setOnPreferenceChangeListener { _ , newValue ->
runCatching {
val value = newValue as String
Toast . makeText ( screen . context , PREF _ADDITIONAL _FANSUBS _TOAST , Toast . LENGTH _LONG ) . show ( )
preferences . edit ( ) . putString ( key , value ) . commit ( )
} . getOrDefault ( false )
}
} . also ( screen :: addPreference )
}
}