fix voting api (#544)

This commit is contained in:
Cloudburst 2023-08-09 23:44:17 +02:00 committed by GitHub
parent bbbb7c4982
commit 44a2146c12
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 54 additions and 112 deletions

View file

@ -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)
} }
} }

View file

@ -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
) )
} }
} }
} }
} }
}

View file

@ -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

View file

@ -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>