mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
fix voting api (#544)
This commit is contained in:
parent
bbbb7c4982
commit
44a2146c12
4 changed files with 54 additions and 112 deletions
|
@ -8,22 +8,14 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import com.lagradost.cloudstream3.app
|
import com.lagradost.cloudstream3.app
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.main
|
import com.lagradost.cloudstream3.utils.Coroutines.main
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
|
||||||
object VotingApi { // please do not cheat the votes lol
|
object VotingApi { // please do not cheat the votes lol
|
||||||
private const val LOGKEY = "VotingApi"
|
private const val LOGKEY = "VotingApi"
|
||||||
|
|
||||||
enum class VoteType(val value: Int) {
|
private const val apiDomain = "https://counterapi.com/api"
|
||||||
UPVOTE(1),
|
|
||||||
DOWNVOTE(-1),
|
|
||||||
NONE(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val apiDomain = "https://api.countapi.xyz"
|
|
||||||
|
|
||||||
private fun transformUrl(url: String): String = // dont touch or all votes get reset
|
private fun transformUrl(url: String): String = // dont touch or all votes get reset
|
||||||
MessageDigest
|
MessageDigest
|
||||||
|
@ -35,12 +27,12 @@ object VotingApi { // please do not cheat the votes lol
|
||||||
return getVotes(url)
|
return getVotes(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun SitePlugin.vote(requestType: VoteType): Int {
|
fun SitePlugin.hasVoted(): Boolean {
|
||||||
return vote(url, requestType)
|
return hasVoted(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun SitePlugin.getVoteType(): VoteType {
|
suspend fun SitePlugin.vote(): Int {
|
||||||
return getVoteType(url)
|
return vote(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun SitePlugin.canVote(): Boolean {
|
fun SitePlugin.canVote(): Boolean {
|
||||||
|
@ -50,28 +42,31 @@ object VotingApi { // please do not cheat the votes lol
|
||||||
// Plugin url to Int
|
// Plugin url to Int
|
||||||
private val votesCache = mutableMapOf<String, Int>()
|
private val votesCache = mutableMapOf<String, Int>()
|
||||||
|
|
||||||
suspend fun getVotes(pluginUrl: String): Int {
|
private fun getRepository(pluginUrl: String) = pluginUrl
|
||||||
val url = "${apiDomain}/get/cs3-votes/${transformUrl(pluginUrl)}"
|
.split("/")
|
||||||
|
.drop(2)
|
||||||
|
.take(3)
|
||||||
|
.joinToString("-")
|
||||||
|
|
||||||
|
private suspend fun readVote(pluginUrl: String): Int {
|
||||||
|
var url = "${apiDomain}/cs-${getRepository(pluginUrl)}/vote/${transformUrl(pluginUrl)}?readOnly=true"
|
||||||
Log.d(LOGKEY, "Requesting: $url")
|
Log.d(LOGKEY, "Requesting: $url")
|
||||||
return votesCache[pluginUrl] ?: app.get(url).parsedSafe<Result>()?.value?.also {
|
return app.get(url).parsedSafe<Result>()?.value ?: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun writeVote(pluginUrl: String): Boolean {
|
||||||
|
var url = "${apiDomain}/cs-${getRepository(pluginUrl)}/vote/${transformUrl(pluginUrl)}"
|
||||||
|
Log.d(LOGKEY, "Requesting: $url")
|
||||||
|
return app.get(url).parsedSafe<Result>()?.value != null
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getVotes(pluginUrl: String): Int =
|
||||||
|
votesCache[pluginUrl] ?: readVote(pluginUrl).also {
|
||||||
votesCache[pluginUrl] = it
|
votesCache[pluginUrl] = it
|
||||||
} ?: (0.also {
|
|
||||||
ioSafe {
|
|
||||||
createBucket(pluginUrl)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getVoteType(pluginUrl: String): VoteType {
|
fun hasVoted(pluginUrl: String) =
|
||||||
return getKey("cs3-votes/${transformUrl(pluginUrl)}") ?: VoteType.NONE
|
getKey("cs3-votes/${transformUrl(pluginUrl)}") ?: false
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun createBucket(pluginUrl: String) {
|
|
||||||
val url =
|
|
||||||
"${apiDomain}/create?namespace=cs3-votes&key=${transformUrl(pluginUrl)}&value=0&update_lowerbound=-2&update_upperbound=2&enable_reset=0"
|
|
||||||
Log.d(LOGKEY, "Requesting: $url")
|
|
||||||
app.get(url)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun canVote(pluginUrl: String): Boolean {
|
fun canVote(pluginUrl: String): Boolean {
|
||||||
if (!PluginManager.urlPlugins.contains(pluginUrl)) return false
|
if (!PluginManager.urlPlugins.contains(pluginUrl)) return false
|
||||||
|
@ -79,7 +74,7 @@ object VotingApi { // please do not cheat the votes lol
|
||||||
}
|
}
|
||||||
|
|
||||||
private val voteLock = Mutex()
|
private val voteLock = Mutex()
|
||||||
suspend fun vote(pluginUrl: String, requestType: VoteType): Int {
|
suspend fun vote(pluginUrl: String): Int {
|
||||||
// Prevent multiple requests at the same time.
|
// Prevent multiple requests at the same time.
|
||||||
voteLock.withLock {
|
voteLock.withLock {
|
||||||
if (!canVote(pluginUrl)) {
|
if (!canVote(pluginUrl)) {
|
||||||
|
@ -90,33 +85,21 @@ object VotingApi { // please do not cheat the votes lol
|
||||||
return getVotes(pluginUrl)
|
return getVotes(pluginUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
val savedType: VoteType =
|
if (hasVoted(pluginUrl)) {
|
||||||
getKey("cs3-votes/${transformUrl(pluginUrl)}") ?: VoteType.NONE
|
main {
|
||||||
|
Toast.makeText(context, R.string.already_voted, Toast.LENGTH_SHORT)
|
||||||
val newType = if (requestType == savedType) VoteType.NONE else requestType
|
.show()
|
||||||
val changeValue = if (requestType == savedType) {
|
|
||||||
-requestType.value
|
|
||||||
} else if (savedType == VoteType.NONE) {
|
|
||||||
requestType.value
|
|
||||||
} else if (savedType != requestType) {
|
|
||||||
-savedType.value + requestType.value
|
|
||||||
} else 0
|
|
||||||
|
|
||||||
// Pre-emptively set vote key
|
|
||||||
setKey("cs3-votes/${transformUrl(pluginUrl)}", newType)
|
|
||||||
|
|
||||||
val url =
|
|
||||||
"${apiDomain}/update/cs3-votes/${transformUrl(pluginUrl)}?amount=${changeValue}"
|
|
||||||
Log.d(LOGKEY, "Requesting: $url")
|
|
||||||
val res = app.get(url).parsedSafe<Result>()?.value
|
|
||||||
|
|
||||||
if (res == null) {
|
|
||||||
// "Refund" key if the response is invalid
|
|
||||||
setKey("cs3-votes/${transformUrl(pluginUrl)}", savedType)
|
|
||||||
} else {
|
|
||||||
votesCache[pluginUrl] = res
|
|
||||||
}
|
}
|
||||||
return res ?: 0
|
return getVotes(pluginUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (writeVote(pluginUrl)) {
|
||||||
|
setKey("cs3-votes/${transformUrl(pluginUrl)}", true)
|
||||||
|
votesCache[pluginUrl] = votesCache[pluginUrl]?.plus(1) ?: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return getVotes(pluginUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,10 +13,9 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.databinding.FragmentPluginDetailsBinding
|
import com.lagradost.cloudstream3.databinding.FragmentPluginDetailsBinding
|
||||||
import com.lagradost.cloudstream3.plugins.PluginManager
|
import com.lagradost.cloudstream3.plugins.PluginManager
|
||||||
import com.lagradost.cloudstream3.plugins.VotingApi
|
|
||||||
import com.lagradost.cloudstream3.plugins.VotingApi.canVote
|
import com.lagradost.cloudstream3.plugins.VotingApi.canVote
|
||||||
import com.lagradost.cloudstream3.plugins.VotingApi.getVoteType
|
|
||||||
import com.lagradost.cloudstream3.plugins.VotingApi.getVotes
|
import com.lagradost.cloudstream3.plugins.VotingApi.getVotes
|
||||||
|
import com.lagradost.cloudstream3.plugins.VotingApi.hasVoted
|
||||||
import com.lagradost.cloudstream3.plugins.VotingApi.vote
|
import com.lagradost.cloudstream3.plugins.VotingApi.vote
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.main
|
import com.lagradost.cloudstream3.utils.Coroutines.main
|
||||||
|
@ -106,7 +105,6 @@ class PluginDetailsFragment(val data: PluginViewData) : BottomSheetDialogFragmen
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!metadata.canVote()) {
|
if (!metadata.canVote()) {
|
||||||
downvote.alpha = .6f
|
|
||||||
upvote.alpha = .6f
|
upvote.alpha = .6f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,19 +135,11 @@ class PluginDetailsFragment(val data: PluginViewData) : BottomSheetDialogFragmen
|
||||||
|
|
||||||
upvote.setOnClickListener {
|
upvote.setOnClickListener {
|
||||||
ioSafe {
|
ioSafe {
|
||||||
metadata.vote(VotingApi.VoteType.UPVOTE).main {
|
metadata.vote().main {
|
||||||
updateVoting(it)
|
updateVoting(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
downvote.setOnClickListener {
|
|
||||||
ioSafe {
|
|
||||||
metadata.vote(VotingApi.VoteType.DOWNVOTE).main {
|
|
||||||
updateVoting(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ioSafe {
|
ioSafe {
|
||||||
metadata.getVotes().main {
|
metadata.getVotes().main {
|
||||||
|
@ -163,34 +153,15 @@ class PluginDetailsFragment(val data: PluginViewData) : BottomSheetDialogFragmen
|
||||||
val metadata = data.plugin.second
|
val metadata = data.plugin.second
|
||||||
binding?.apply {
|
binding?.apply {
|
||||||
pluginVotes.text = value.toString()
|
pluginVotes.text = value.toString()
|
||||||
when (metadata.getVoteType()) {
|
if (metadata.hasVoted()) {
|
||||||
VotingApi.VoteType.UPVOTE -> {
|
|
||||||
upvote.imageTintList = ColorStateList.valueOf(
|
upvote.imageTintList = ColorStateList.valueOf(
|
||||||
context?.colorFromAttribute(R.attr.colorPrimary) ?: R.color.colorPrimary
|
context?.colorFromAttribute(R.attr.colorPrimary) ?: R.color.colorPrimary
|
||||||
)
|
)
|
||||||
downvote.imageTintList = ColorStateList.valueOf(
|
} else {
|
||||||
context?.colorFromAttribute(R.attr.white) ?: R.color.white
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
VotingApi.VoteType.DOWNVOTE -> {
|
|
||||||
downvote.imageTintList = ColorStateList.valueOf(
|
|
||||||
context?.colorFromAttribute(R.attr.colorPrimary) ?: R.color.colorPrimary
|
|
||||||
)
|
|
||||||
upvote.imageTintList = ColorStateList.valueOf(
|
upvote.imageTintList = ColorStateList.valueOf(
|
||||||
context?.colorFromAttribute(R.attr.white) ?: R.color.white
|
context?.colorFromAttribute(R.attr.colorOnSurface) ?: R.color.white
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
VotingApi.VoteType.NONE -> {
|
|
||||||
upvote.imageTintList = ColorStateList.valueOf(
|
|
||||||
context?.colorFromAttribute(R.attr.white) ?: R.color.white
|
|
||||||
)
|
|
||||||
downvote.imageTintList = ColorStateList.valueOf(
|
|
||||||
context?.colorFromAttribute(R.attr.white) ?: R.color.white
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
|
@ -284,23 +284,10 @@
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:gravity="center_horizontal|center_vertical">
|
android:gravity="center_horizontal|center_vertical">
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/downvote"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="32dp"
|
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
|
||||||
android:src="@drawable/ic_baseline_thumb_down_24"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/plugin_votes"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/plugin_votes"
|
android:id="@+id/plugin_votes"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="32dp"
|
android:layout_marginTop="32dp"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:layout_marginBottom="32dp"
|
android:layout_marginBottom="32dp"
|
||||||
|
@ -308,7 +295,7 @@
|
||||||
android:text="0"
|
android:text="0"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/upvote"
|
app:layout_constraintEnd_toStartOf="@+id/upvote"
|
||||||
app:layout_constraintStart_toEndOf="@+id/downvote"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
|
|
|
@ -687,4 +687,5 @@
|
||||||
|
|
||||||
|
|
||||||
<string name="tv_no_focus_tag" translatable="false">tv_no_focus_tag</string>
|
<string name="tv_no_focus_tag" translatable="false">tv_no_focus_tag</string>
|
||||||
|
<string name="already_voted">You have already voted</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in a new issue