mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
bump
This commit is contained in:
parent
12977e1788
commit
8ca808ce37
23 changed files with 59 additions and 84 deletions
|
@ -34,8 +34,8 @@ android {
|
|||
applicationId "com.lagradost.cloudstream3"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 31
|
||||
versionCode 28
|
||||
versionName "2.0.0"
|
||||
versionCode 29
|
||||
versionName "2.1.0"
|
||||
|
||||
resValue "string", "app_version",
|
||||
"${defaultConfig.versionName}${versionNameSuffix ?: ""}"
|
||||
|
|
|
@ -10,6 +10,7 @@ import com.lagradost.cloudstream3.utils.getQualityFromName
|
|||
import okhttp3.Response
|
||||
import org.jsoup.Jsoup
|
||||
import java.util.*
|
||||
import kotlin.math.pow
|
||||
|
||||
class AnimePaheProvider : MainAPI() {
|
||||
// credit to https://github.com/justfoolingaround/animdl/tree/master/animdl/core/codebase/providers/animepahe
|
||||
|
@ -209,10 +210,8 @@ class AnimePaheProvider : MainAPI() {
|
|||
val episodes = ArrayList<AnimeEpisode>()
|
||||
|
||||
fun getEpisodeTitle(k: AnimeData): String {
|
||||
return if (k.title.isEmpty()) {
|
||||
return k.title.ifEmpty {
|
||||
"Episode ${k.episode}"
|
||||
} else {
|
||||
k.title
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -222,10 +221,8 @@ class AnimePaheProvider : MainAPI() {
|
|||
AnimeEpisode(
|
||||
"$mainUrl/api?m=links&id=${it.animeId}&session=${it.session}&p=kwik!!TRUE!!",
|
||||
getEpisodeTitle(it),
|
||||
if (it.snapshot.length == 0) {
|
||||
it.snapshot.ifEmpty {
|
||||
null
|
||||
} else {
|
||||
it.snapshot
|
||||
},
|
||||
it.createdAt
|
||||
)
|
||||
|
@ -355,7 +352,7 @@ class AnimePaheProvider : MainAPI() {
|
|||
acc += (when (isNumber("$i")) {
|
||||
true -> "$i".toLong()
|
||||
false -> "0".toLong()
|
||||
}) * Math.pow(s1.toDouble(), n.toDouble()).toInt()
|
||||
}) * s1.toDouble().pow(n.toDouble()).toInt()
|
||||
}
|
||||
|
||||
var k = ""
|
||||
|
|
|
@ -150,27 +150,27 @@ class GogoanimeProvider : MainAPI() {
|
|||
var nativeName: String? = null
|
||||
var type: String? = null
|
||||
|
||||
animeBody.select("p.type").forEach {
|
||||
when (it.selectFirst("span").text().trim()) {
|
||||
animeBody.select("p.type").forEach { pType ->
|
||||
when (pType.selectFirst("span").text().trim()) {
|
||||
"Plot Summary:" -> {
|
||||
description = it.text().replace("Plot Summary:", "").trim()
|
||||
description = pType.text().replace("Plot Summary:", "").trim()
|
||||
}
|
||||
"Genre:" -> {
|
||||
genre.addAll(it.select("a").map {
|
||||
genre.addAll(pType.select("a").map {
|
||||
it.attr("title")
|
||||
})
|
||||
}
|
||||
"Released:" -> {
|
||||
year = it.text().replace("Released:", "").trim().toIntOrNull()
|
||||
year = pType.text().replace("Released:", "").trim().toIntOrNull()
|
||||
}
|
||||
"Status:" -> {
|
||||
status = it.text().replace("Status:", "").trim()
|
||||
status = pType.text().replace("Status:", "").trim()
|
||||
}
|
||||
"Other name:" -> {
|
||||
nativeName = it.text().replace("Other name:", "").trim()
|
||||
nativeName = pType.text().replace("Other name:", "").trim()
|
||||
}
|
||||
"Type:" -> {
|
||||
type = it.text().replace("type:", "").trim()
|
||||
type = pType.text().replace("type:", "").trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -187,10 +187,10 @@ class WcoProvider : MainAPI() {
|
|||
val isDubbed = canonicalTitle.contains("Dub")
|
||||
val episodeNodes = document.select(".tab-content .nav-item > a")
|
||||
|
||||
val episodes = ArrayList<AnimeEpisode>(episodeNodes?.map {
|
||||
val episodes = ArrayList(episodeNodes?.map {
|
||||
AnimeEpisode(it.attr("href"))
|
||||
}
|
||||
?: ArrayList<AnimeEpisode>())
|
||||
} ?: ArrayList())
|
||||
|
||||
val statusElem = document.selectFirst("div.elements div.row > div:nth-child(1) > div.row-line:nth-child(2)")
|
||||
val status = when (statusElem?.text()?.replace("Status:", "")?.trim()) {
|
||||
"Ongoing" -> ShowStatus.Ongoing
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package com.lagradost.cloudstream3.animeproviders
|
||||
|
||||
import androidx.core.net.toUri
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import com.lagradost.cloudstream3.*
|
||||
|
@ -10,7 +9,6 @@ import com.lagradost.cloudstream3.movieproviders.SflixProvider.Companion.toSubti
|
|||
import com.lagradost.cloudstream3.network.WebViewResolver
|
||||
import com.lagradost.cloudstream3.network.get
|
||||
import com.lagradost.cloudstream3.network.text
|
||||
import com.lagradost.cloudstream3.network.url
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import org.jsoup.Jsoup
|
||||
import org.jsoup.nodes.Element
|
||||
|
@ -259,7 +257,7 @@ class ZoroProvider : MainAPI() {
|
|||
).text
|
||||
}
|
||||
|
||||
private data class rapidCloudResponse(
|
||||
private data class RapidCloudResponse(
|
||||
@JsonProperty("link") val link: String
|
||||
)
|
||||
|
||||
|
@ -291,7 +289,7 @@ class ZoroProvider : MainAPI() {
|
|||
|
||||
val responses = servers.map {
|
||||
val link = "$mainUrl/ajax/v2/episode/sources?id=${it.second}&_token=$recaptchaToken"
|
||||
Pair(it.first, getM3u8FromRapidCloud(mapper.readValue<rapidCloudResponse>(get(link, res.request.headers.toMap()).text).link))
|
||||
Pair(it.first, getM3u8FromRapidCloud(mapper.readValue<RapidCloudResponse>(get(link, res.request.headers.toMap()).text).link))
|
||||
}
|
||||
|
||||
responses.forEach {
|
||||
|
|
|
@ -65,7 +65,7 @@ class SflixProvider : MainAPI() {
|
|||
}
|
||||
}
|
||||
|
||||
override fun getMainPage(): HomePageResponse? {
|
||||
override fun getMainPage(): HomePageResponse {
|
||||
val html = get("$mainUrl/home").text
|
||||
val document = Jsoup.parse(html)
|
||||
|
||||
|
@ -78,16 +78,16 @@ class SflixProvider : MainAPI() {
|
|||
map.forEach {
|
||||
all.add(HomePageList(
|
||||
it.key,
|
||||
document.select(it.value).select("div.film-poster").map {
|
||||
it.toSearchResult()
|
||||
document.select(it.value).select("div.film-poster").map { element ->
|
||||
element.toSearchResult()
|
||||
}
|
||||
))
|
||||
}
|
||||
|
||||
document.select("section.block_area.block_area_home.section-id-02").forEach {
|
||||
val title = it.select("h2.cat-heading").text().trim()
|
||||
val elements = it.select("div.film-poster").map {
|
||||
it.toSearchResult()
|
||||
val elements = it.select("div.film-poster").map { element ->
|
||||
element.toSearchResult()
|
||||
}
|
||||
all.add(HomePageList(title, elements))
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ class SflixProvider : MainAPI() {
|
|||
}
|
||||
}
|
||||
|
||||
override fun load(url: String): LoadResponse? {
|
||||
override fun load(url: String): LoadResponse {
|
||||
val html = get(url).text
|
||||
val document = Jsoup.parse(html)
|
||||
|
||||
|
@ -195,14 +195,14 @@ class SflixProvider : MainAPI() {
|
|||
val seasonsDocument = Jsoup.parse(seasonsHtml)
|
||||
val episodes = arrayListOf<TvSeriesEpisode>()
|
||||
|
||||
seasonsDocument.select("div.dropdown-menu.dropdown-menu-model > a").forEachIndexed { season, it ->
|
||||
val seasonId = it.attr("data-id")
|
||||
seasonsDocument.select("div.dropdown-menu.dropdown-menu-model > a").forEachIndexed { season, element ->
|
||||
val seasonId = element.attr("data-id")
|
||||
if (seasonId.isNullOrBlank()) return@forEachIndexed
|
||||
|
||||
val seasonHtml = get("$mainUrl/ajax/v2/season/episodes/$seasonId").text
|
||||
val seasonDocument = Jsoup.parse(seasonHtml)
|
||||
seasonDocument.select("div.flw-item.film_single-item.episode-item.eps-item")
|
||||
.forEachIndexed { i, it ->
|
||||
.forEachIndexed { _, it ->
|
||||
val episodeImg = it.select("img")
|
||||
val episodeTitle = episodeImg.attr("title")
|
||||
val episodePosterUrl = episodeImg.attr("src")
|
||||
|
@ -321,7 +321,7 @@ class SflixProvider : MainAPI() {
|
|||
// For re-use in Zoro
|
||||
|
||||
fun Sources.toExtractorLink(caller: MainAPI, name: String): List<ExtractorLink>? {
|
||||
return this.file?.let {
|
||||
return this.file?.let { file ->
|
||||
val isM3u8 = URI(this.file).path.endsWith(".m3u8") || this.type.equals("hls", ignoreCase = true)
|
||||
if (isM3u8) {
|
||||
M3u8Helper().m3u8Generation(M3u8Helper.M3u8Stream(this.file, null), true).map { stream ->
|
||||
|
@ -339,7 +339,7 @@ class SflixProvider : MainAPI() {
|
|||
listOf(ExtractorLink(
|
||||
caller.name,
|
||||
this.label?.let { "${caller.name} - $it" } ?: caller.name,
|
||||
it,
|
||||
file,
|
||||
caller.mainUrl,
|
||||
getQualityFromName(this.type ?: ""),
|
||||
false,
|
||||
|
|
|
@ -77,11 +77,11 @@ class VfFilmProvider : MainAPI() {
|
|||
|
||||
private fun getDirect(original: String): String { // original data, https://vf-film.org/?trembed=1&trid=55313&trtype=1 for example
|
||||
val response = get(original).text
|
||||
val url = "iframe .*src=\"(.*?)\"".toRegex().find(response)?.groupValues?.get(1).toString() // https://vudeo.net/embed-uweno86lzx8f.html for example
|
||||
val url = "iframe .*src=\"(.*?)\"".toRegex().find(response)?.groupValues?.get(1)
|
||||
.toString() // https://vudeo.net/embed-uweno86lzx8f.html for example
|
||||
val vudoResponse = get(url).text
|
||||
val document = Jsoup.parse(vudoResponse)
|
||||
val vudoUrl = Regex("sources: \\[\"(.*?)\"]").find(document.html())?.groupValues?.get(1).toString() // direct mp4 link, https://m11.vudeo.net/2vp3ukyw2avjdohilpebtzuct42q5jwvpmpsez3xjs6d7fbs65dpuey2rbra/v.mp4 for exemple
|
||||
return vudoUrl
|
||||
return Regex("sources: \\[\"(.*?)\"]").find(document.html())?.groupValues?.get(1).toString()
|
||||
}
|
||||
|
||||
|
||||
|
@ -105,20 +105,20 @@ class VfFilmProvider : MainAPI() {
|
|||
|
||||
|
||||
val players = document.select("ul.TPlayerNv > li")
|
||||
var number_player = 0
|
||||
var numberPlayer = 0
|
||||
var found = false
|
||||
for (player in players) {
|
||||
if (player.selectFirst("> span").text() == "Vudeo") {
|
||||
found = true
|
||||
break
|
||||
} else {
|
||||
number_player += 1
|
||||
numberPlayer += 1
|
||||
}
|
||||
}
|
||||
if (found == false) {
|
||||
number_player = 0
|
||||
if (!found) {
|
||||
numberPlayer = 0
|
||||
}
|
||||
val i = number_player.toString()
|
||||
val i = numberPlayer.toString()
|
||||
val trid = Regex("iframe .*trid=(.*?)&").find(document.html())?.groupValues?.get(1)
|
||||
|
||||
val data = getDirect("https://vf-film.org/?trembed=$i&trid=$trid&trtype=1")
|
||||
|
|
|
@ -1,14 +1,6 @@
|
|||
package com.lagradost.cloudstream3.movieproviders
|
||||
|
||||
import org.jsoup.Jsoup
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.extractors.Vidstream
|
||||
import com.lagradost.cloudstream3.network.get
|
||||
import com.lagradost.cloudstream3.network.text
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import com.lagradost.cloudstream3.utils.getQualityFromName
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
import com.lagradost.cloudstream3.TvType
|
||||
|
||||
/** Needs to inherit from MainAPI() to
|
||||
* make the app know what functions to call
|
||||
|
|
|
@ -166,10 +166,10 @@ open class VidstreamProviderTemplate : MainAPI() {
|
|||
urls.pmap { url ->
|
||||
val response = get(url, timeout = 20).text
|
||||
val document = Jsoup.parse(response)
|
||||
document.select("div.main-inner")?.forEach {
|
||||
document.select("div.main-inner")?.forEach { inner ->
|
||||
// Always trim your text unless you want the risk of spaces at the start or end.
|
||||
val title = it.select(".widget-title").text().trim()
|
||||
val elements = it.select(".video-block").map {
|
||||
val title = inner.select(".widget-title").text().trim()
|
||||
val elements = inner.select(".video-block").map {
|
||||
val link = fixUrl(it.select("a").attr("href"))
|
||||
val image = it.select(".picture > img").attr("src")
|
||||
val name = it.select("div.name").text().trim().replace(Regex("""[Ee]pisode \d+"""), "")
|
||||
|
|
|
@ -6,10 +6,7 @@ import com.lagradost.cloudstream3.R
|
|||
import com.lagradost.cloudstream3.USER_AGENT
|
||||
import okhttp3.*
|
||||
import okhttp3.Headers.Companion.toHeaders
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.dnsoverhttps.DnsOverHttps
|
||||
import java.io.File
|
||||
import java.net.InetAddress
|
||||
import java.net.URI
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
|
|
@ -3,7 +3,6 @@ package com.lagradost.cloudstream3.network
|
|||
import android.annotation.SuppressLint
|
||||
import android.net.http.SslError
|
||||
import android.webkit.*
|
||||
import androidx.core.view.contains
|
||||
import com.lagradost.cloudstream3.AcraApplication
|
||||
import com.lagradost.cloudstream3.utils.Coroutines.main
|
||||
import kotlinx.coroutines.delay
|
||||
|
@ -11,7 +10,6 @@ import kotlinx.coroutines.runBlocking
|
|||
import okhttp3.Interceptor
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import java.net.URI
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class WebViewResolver(val interceptUrl: Regex) : Interceptor {
|
||||
|
@ -134,5 +132,4 @@ class WebViewResolver(val interceptUrl: Regex) : Interceptor {
|
|||
WebResourceResponse("application/octet-stream", null, this.body?.byteStream())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -7,7 +7,7 @@ import androidx.recyclerview.widget.GridLayoutManager
|
|||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlin.math.abs
|
||||
|
||||
class GrdLayoutManager(val context: Context, val spanCoun: Int) : GridLayoutManager(context, spanCoun) {
|
||||
class GrdLayoutManager(val context: Context, private val spanCoun: Int) : GridLayoutManager(context, spanCoun) {
|
||||
override fun onFocusSearchFailed(
|
||||
focused: View,
|
||||
focusDirection: Int,
|
||||
|
|
|
@ -100,7 +100,7 @@ class DownloadChildAdapter(
|
|||
private val progressBarDownload: ContentLoadingProgressBar = itemView.download_child_episode_progress_downloaded
|
||||
private val downloadImage: ImageView = itemView.download_child_episode_download
|
||||
|
||||
var localCard: VisualDownloadChildCached? = null
|
||||
private var localCard: VisualDownloadChildCached? = null
|
||||
|
||||
fun bind(card: VisualDownloadChildCached) {
|
||||
localCard = card
|
||||
|
|
|
@ -65,7 +65,7 @@ class DownloadChildFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
var downloadDeleteEventListener: ((Int) -> Unit)? = null
|
||||
private var downloadDeleteEventListener: ((Int) -> Unit)? = null
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
|
|
@ -89,7 +89,7 @@ class DownloadFragment : Fragment() {
|
|||
return inflater.inflate(R.layout.fragment_downloads, container, false)
|
||||
}
|
||||
|
||||
var downloadDeleteEventListener: ((Int) -> Unit)? = null
|
||||
private var downloadDeleteEventListener: ((Int) -> Unit)? = null
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
|
|
@ -98,7 +98,7 @@ class DownloadHeaderAdapter(
|
|||
private val downloadBar: ContentLoadingProgressBar = itemView.download_header_progress_downloaded
|
||||
private val downloadImage: ImageView = itemView.download_header_episode_download
|
||||
private val normalImage: ImageView = itemView.download_header_goto_child
|
||||
var localCard: VisualDownloadHeaderCached? = null
|
||||
private var localCard: VisualDownloadHeaderCached? = null
|
||||
|
||||
fun bind(card: VisualDownloadHeaderCached) {
|
||||
localCard = card
|
||||
|
|
|
@ -27,8 +27,8 @@ class EasyDownloadButton : IDisposable {
|
|||
}
|
||||
}
|
||||
|
||||
var downloadProgressEventListener: ((Triple<Int, Long, Long>) -> Unit)? = null
|
||||
var downloadStatusEventListener: ((Pair<Int, VideoDownloadManager.DownloadType>) -> Unit)? = null
|
||||
private var downloadProgressEventListener: ((Triple<Int, Long, Long>) -> Unit)? = null
|
||||
private var downloadStatusEventListener: ((Pair<Int, VideoDownloadManager.DownloadType>) -> Unit)? = null
|
||||
|
||||
fun setUpMaterialButton(
|
||||
setupCurrentBytes: Long?,
|
||||
|
|
|
@ -31,7 +31,7 @@ import kotlinx.coroutines.launch
|
|||
import kotlinx.coroutines.withContext
|
||||
|
||||
class HomeViewModel : ViewModel() {
|
||||
var repo: APIRepository? = null
|
||||
private var repo: APIRepository? = null
|
||||
|
||||
private val _apiName = MutableLiveData<String>()
|
||||
val apiName: LiveData<String> = _apiName
|
||||
|
@ -126,7 +126,7 @@ class HomeViewModel : ViewModel() {
|
|||
_bookmarks.postValue(list)
|
||||
}
|
||||
|
||||
var onGoingLoad: Job? = null
|
||||
private var onGoingLoad: Job? = null
|
||||
private fun loadAndCancel(api: MainAPI?) {
|
||||
onGoingLoad?.cancel()
|
||||
onGoingLoad = load(api)
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
package com.lagradost.cloudstream3.ui.result
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.*
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.APIHolder.getApiFromName
|
||||
import com.lagradost.cloudstream3.APIHolder.getId
|
||||
|
@ -28,13 +31,13 @@ import com.lagradost.cloudstream3.utils.VideoDownloadHelper
|
|||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.lang.Exception
|
||||
import kotlin.collections.set
|
||||
|
||||
const val EPISODE_RANGE_SIZE = 50
|
||||
const val EPISODE_RANGE_OVERLOAD = 60
|
||||
|
||||
class ResultViewModel : ViewModel() {
|
||||
var repo: APIRepository? = null
|
||||
private var repo: APIRepository? = null
|
||||
|
||||
private val _resultResponse: MutableLiveData<Resource<Any?>> = MutableLiveData()
|
||||
private val _episodes: MutableLiveData<List<ResultEpisode>> = MutableLiveData()
|
||||
|
|
|
@ -302,7 +302,7 @@ class SearchFragment : Fragment() {
|
|||
}
|
||||
|
||||
override fun onQueryTextChange(newText: String): Boolean {
|
||||
searchViewModel.quickSearch(newText)
|
||||
//searchViewModel.quickSearch(newText)
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
|
|
@ -8,16 +8,12 @@ import com.lagradost.cloudstream3.APIHolder.apis
|
|||
import com.lagradost.cloudstream3.SearchResponse
|
||||
import com.lagradost.cloudstream3.apmap
|
||||
import com.lagradost.cloudstream3.mvvm.Resource
|
||||
import com.lagradost.cloudstream3.pmap
|
||||
import com.lagradost.cloudstream3.ui.APIRepository
|
||||
import com.lagradost.cloudstream3.ui.APIRepository.Companion.providersActive
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.internal.notify
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
data class OnGoingSearch(
|
||||
val apiName: String,
|
||||
|
@ -87,8 +83,4 @@ class SearchViewModel : ViewModel() {
|
|||
|
||||
_searchResponse.postValue(Resource.Success(list))
|
||||
}
|
||||
|
||||
fun quickSearch(query: String) {
|
||||
return
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@ import kotlinx.coroutines.delay
|
|||
|
||||
const val DOWNLOAD_CHECK = "DownloadCheck"
|
||||
|
||||
class DownloadFileWorkManager(val context: Context, val workerParams: WorkerParameters) :
|
||||
class DownloadFileWorkManager(val context: Context, private val workerParams: WorkerParameters) :
|
||||
CoroutineWorker(context, workerParams) {
|
||||
|
||||
override suspend fun doWork(): Result {
|
||||
|
|
|
@ -39,7 +39,6 @@
|
|||
<string name="result_go_back">Gå tillbaka</string>
|
||||
<string name="episode_poster_img_des">@string/result_poster_img_des</string>
|
||||
<string name="play_episode">Spela Avsnitt</string>
|
||||
<string name="need_storage">Allow to download episodes</string>
|
||||
<string name="download">Ladda ner</string>
|
||||
<string name="download_storage_text">Intern lagring</string>
|
||||
|
||||
|
|
Loading…
Reference in a new issue