Drop torrent download in favor of magnet links
This commit is contained in:
1 parent
49edbb8185
commit
e485d79bb1
10 files changed
+85
-35
No files matched your search
@@ -33,6 +33,8 @@ object EhCookieStore : CookiesStorage {
|
||||
)
|
||||
}
|
||||
|
||||
fun getUserId() = getCookies(EhUrl.HOST_E)?.get(KEY_IPB_MEMBER_ID)
|
||||
|
||||
fun getIdentityCookies(): List<Pair<String, String?>> {
|
||||
val eCookies = getCookies(EhUrl.HOST_E)
|
||||
val exCookies = getCookies(EhUrl.HOST_EX)
|
||||
@@ -48,8 +50,6 @@ object EhCookieStore : CookiesStorage {
|
||||
|
||||
fun flush() = manager.flush()
|
||||
|
||||
fun getCookieHeader(url: String): String? = manager.getCookie(url)
|
||||
|
||||
// See https://github.com/Ehviewer-Overhauled/Ehviewer/issues/873
|
||||
override suspend fun addCookie(requestUrl: Url, cookie: Cookie) {
|
||||
if (cookie.name != KEY_UTMP_NAME) {
|
||||
|
||||
@@ -180,6 +180,11 @@ object EhEngine {
|
||||
return ehRequest(url, referer).fetchUsingAsByteBuffer(TorrentParser::parse)
|
||||
}
|
||||
|
||||
suspend fun getTorrentKey() = EhCookieStore.getUserId()?.let { id ->
|
||||
val key = ehRequest(EhUrl.URL_HOME).fetchUsingAsText(HomeParser::parseTorrentKey)
|
||||
"${id}x$key"
|
||||
}
|
||||
|
||||
suspend fun getArchiveList(url: String, gid: Long, token: String): ArchiveParser.Result {
|
||||
val funds = if (EhUtils.isExHentai) getFunds() else null
|
||||
return ehRequest(url, EhUrl.getGalleryDetailUrl(gid, token))
|
||||
|
||||
@@ -133,6 +133,11 @@ object EhUrl {
|
||||
}.buildString()
|
||||
|
||||
fun getTagDefinitionUrl(tag: String) = "https://ehwiki.org/wiki/" + tag.replace(' ', '_')
|
||||
|
||||
fun getTrackerUrl(gid: Long, key: String?) = ehUrl(
|
||||
path = listOfNotNull("$gid", key, "announce"),
|
||||
host = "ehtracker.org",
|
||||
).buildString()
|
||||
}
|
||||
|
||||
inline fun ehUrl(path: String? = null, host: String = EhUrl.domain, builder: ParametersBuilder.() -> Unit) = ehUrl(
|
||||
|
||||
@@ -8,7 +8,8 @@ import kotlinx.parcelize.Parcelize
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
object HomeParser {
|
||||
private val PATTERN_FUNDS = Regex("Available: ([\\d,]+) Credits.*Available: ([\\d,]+) kGP", RegexOption.DOT_MATCHES_ALL)
|
||||
private val TorrentKeyRegex = Regex("Your current key is: <[^>]*>([^<]*)<")
|
||||
private val FundsRegex = Regex("Available: ([\\d,]+) Credits.*Available: ([\\d,]+) kGP", RegexOption.DOT_MATCHES_ALL)
|
||||
private const val INSUFFICIENT_FUNDS = "Insufficient funds."
|
||||
|
||||
fun parse(body: ByteBuffer) = runCatching {
|
||||
@@ -21,8 +22,11 @@ object HomeParser {
|
||||
}
|
||||
}
|
||||
|
||||
fun parseTorrentKey(body: String) = TorrentKeyRegex.find(body)?.run { groupValues[1] }
|
||||
?: throw ParseException("Parse torrent key error")
|
||||
|
||||
fun parseFunds(body: String): Funds {
|
||||
PATTERN_FUNDS.find(body)?.groupValues?.run {
|
||||
FundsRegex.find(body)?.groupValues?.run {
|
||||
val fundsC = ParserUtils.parseInt(get(1), 0)
|
||||
val fundsGP = ParserUtils.parseInt(get(2), 0) * 1000
|
||||
return Funds("%,d+".format(fundsGP), "%,d".format(fundsC))
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.hippo.ehviewer.icons.filled
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.material.icons.materialIcon
|
||||
import androidx.compose.material.icons.materialPath
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.hippo.ehviewer.icons.EhIcons
|
||||
import com.hippo.ehviewer.util.unsafeLazy
|
||||
|
||||
val EhIcons.Filled.Magnet by unsafeLazy {
|
||||
materialIcon(name = "Magnet") {
|
||||
materialPath {
|
||||
moveTo(3.0F, 7.0F)
|
||||
verticalLineTo(13.0F)
|
||||
arcTo(9.0F, 9.0F, 0.0F, false, false, 12.0F, 22.0F)
|
||||
arcTo(9.0F, 9.0F, 0.0F, false, false, 21.0F, 13.0F)
|
||||
verticalLineTo(7.0F)
|
||||
horizontalLineTo(17.0F)
|
||||
verticalLineTo(13.0F)
|
||||
arcTo(5.0F, 5.0F, 0.0F, false, true, 12.0F, 18.0F)
|
||||
arcTo(5.0F, 5.0F, 0.0F, false, true, 7.0F, 13.0F)
|
||||
verticalLineTo(7.0F)
|
||||
moveTo(17.0F, 5.0F)
|
||||
horizontalLineTo(21.0F)
|
||||
verticalLineTo(2.0F)
|
||||
horizontalLineTo(17.0F)
|
||||
moveTo(3.0F, 5.0F)
|
||||
horizontalLineTo(7.0F)
|
||||
verticalLineTo(2.0F)
|
||||
horizontalLineTo(3.0F)
|
||||
close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
@Suppress("UnusedPrivateMember")
|
||||
private fun IconMagnetPreview() {
|
||||
Image(imageVector = EhIcons.Default.Magnet, contentDescription = null)
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
package com.hippo.ehviewer.ui.screen
|
||||
|
||||
import android.Manifest
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Context
|
||||
import android.os.Environment
|
||||
import android.content.Intent
|
||||
import android.text.TextUtils.TruncateAt.END
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.animation.SharedTransitionScope
|
||||
@@ -29,7 +30,6 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ImageSearch
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material.icons.filled.SwapVerticalCircle
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.FilledTonalButton
|
||||
@@ -68,10 +68,10 @@ import androidx.paging.compose.itemKey
|
||||
import androidx.window.core.layout.WindowWidthSizeClass
|
||||
import arrow.core.partially1
|
||||
import arrow.fx.coroutines.parMap
|
||||
import arrow.fx.coroutines.parZip
|
||||
import com.hippo.ehviewer.EhDB
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.EhCookieStore
|
||||
import com.hippo.ehviewer.client.EhEngine
|
||||
import com.hippo.ehviewer.client.EhFilter.remember
|
||||
import com.hippo.ehviewer.client.EhUrl
|
||||
@@ -92,6 +92,8 @@ import com.hippo.ehviewer.dao.DownloadInfo
|
||||
import com.hippo.ehviewer.dao.Filter
|
||||
import com.hippo.ehviewer.dao.FilterMode
|
||||
import com.hippo.ehviewer.download.DownloadManager
|
||||
import com.hippo.ehviewer.icons.EhIcons
|
||||
import com.hippo.ehviewer.icons.filled.Magnet
|
||||
import com.hippo.ehviewer.ktbuilder.imageRequest
|
||||
import com.hippo.ehviewer.ktbuilder.launchIn
|
||||
import com.hippo.ehviewer.ui.GalleryInfoBottomSheet
|
||||
@@ -126,11 +128,10 @@ import com.hippo.ehviewer.ui.tools.getClippedRefreshKey
|
||||
import com.hippo.ehviewer.ui.tools.getLimit
|
||||
import com.hippo.ehviewer.ui.tools.getOffset
|
||||
import com.hippo.ehviewer.ui.tools.rememberInVM
|
||||
import com.hippo.ehviewer.util.AppConfig
|
||||
import com.hippo.ehviewer.util.FavouriteStatusRouter
|
||||
import com.hippo.ehviewer.util.FileUtils
|
||||
import com.hippo.ehviewer.util.addTextToClipboard
|
||||
import com.hippo.ehviewer.util.bgWork
|
||||
import com.hippo.ehviewer.util.displayString
|
||||
import com.hippo.ehviewer.util.flattenForEach
|
||||
import com.hippo.ehviewer.util.isAtLeastQ
|
||||
import com.hippo.ehviewer.util.requestPermission
|
||||
@@ -140,6 +141,7 @@ import eu.kanade.tachiyomi.util.lang.launchUI
|
||||
import eu.kanade.tachiyomi.util.lang.withIOContext
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import io.ktor.http.encodeURLParameter
|
||||
import kotlin.math.roundToInt
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.CoroutineStart
|
||||
@@ -148,7 +150,6 @@ import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.launch
|
||||
import moe.tarsin.coroutines.runSuspendCatching
|
||||
import splitties.systemservices.downloadManager
|
||||
|
||||
context(CoroutineScope, DestinationsNavigator, DialogState, MainActivity, SnackbarHostState, SharedTransitionScope, TransitionsVisibilityScope)
|
||||
@Composable
|
||||
@@ -542,16 +543,18 @@ fun BelowHeader(galleryDetail: GalleryDetail) {
|
||||
)
|
||||
val torrentText = stringResource(R.string.torrent_count, galleryDetail.torrentCount)
|
||||
val permissionDenied = stringResource(R.string.permission_denied)
|
||||
val downloadTorrentFailed = stringResource(R.string.download_torrent_failure)
|
||||
val downloadTorrentStarted = stringResource(R.string.download_torrent_started)
|
||||
val noTorrents = stringResource(R.string.no_torrents)
|
||||
val torrentResult = remember(galleryDetail) {
|
||||
async(Dispatchers.IO + Job(), CoroutineStart.LAZY) {
|
||||
EhEngine.getTorrentList(galleryDetail.torrentUrl!!, galleryDetail.gid, galleryDetail.token)
|
||||
parZip(
|
||||
{ EhEngine.getTorrentList(galleryDetail.torrentUrl!!, galleryDetail.gid, galleryDetail.token) },
|
||||
{ EhEngine.getTorrentKey() },
|
||||
{ list, key -> list to key },
|
||||
)
|
||||
}
|
||||
}
|
||||
suspend fun showTorrentDialog() {
|
||||
val torrentList = bgWork { torrentResult.await() }
|
||||
val (torrentList, key) = bgWork { torrentResult.await() }
|
||||
if (torrentList.isEmpty()) {
|
||||
showSnackbar(noTorrents)
|
||||
} else {
|
||||
@@ -561,21 +564,20 @@ fun BelowHeader(galleryDetail: GalleryDetail) {
|
||||
onItemClick = { dismissWith(it) },
|
||||
)
|
||||
}
|
||||
val url = selected.url
|
||||
val name = "${selected.name}.torrent"
|
||||
val r = android.app.DownloadManager.Request(url.toUri())
|
||||
r.setDestinationInExternalPublicDir(
|
||||
Environment.DIRECTORY_DOWNLOADS,
|
||||
AppConfig.APP_DIRNAME + "/" + FileUtils.sanitizeFilename(name),
|
||||
)
|
||||
r.setNotificationVisibility(android.app.DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
|
||||
EhCookieStore.getCookieHeader(url)?.let { r.addRequestHeader("Cookie", it) }
|
||||
downloadManager.enqueue(r)
|
||||
showSnackbar(downloadTorrentStarted)
|
||||
val hash = selected.url.dropLast(8).takeLast(40)
|
||||
val name = selected.name.encodeURLParameter()
|
||||
val tracker = EhUrl.getTrackerUrl(galleryDetail.gid, key).encodeURLParameter()
|
||||
val link = "magnet:?xt=urn:btih:$hash&dn=$name&tr=$tracker"
|
||||
val intent = Intent(Intent.ACTION_VIEW, link.toUri())
|
||||
try {
|
||||
startActivity(intent)
|
||||
} catch (_: ActivityNotFoundException) {
|
||||
withUIContext { addTextToClipboard(link, true) }
|
||||
}
|
||||
}
|
||||
}
|
||||
EhIconButton(
|
||||
icon = Icons.Default.SwapVerticalCircle,
|
||||
icon = EhIcons.Default.Magnet,
|
||||
text = torrentText,
|
||||
onClick = {
|
||||
launchIO {
|
||||
@@ -585,8 +587,7 @@ fun BelowHeader(galleryDetail: GalleryDetail) {
|
||||
runSuspendCatching {
|
||||
showTorrentDialog()
|
||||
}.onFailure {
|
||||
logcat(it)
|
||||
showSnackbar(downloadTorrentFailed)
|
||||
showSnackbar(it.displayString())
|
||||
}
|
||||
} else {
|
||||
showSnackbar(permissionDenied)
|
||||
|
||||
@@ -60,7 +60,6 @@
|
||||
<string name="tag_vote_up">Stem opp</string>
|
||||
<string name="pref_grayscale">Gråskala</string>
|
||||
<string name="action_share">Del</string>
|
||||
<string name="download_torrent_started">Nedlasting av torrent startet</string>
|
||||
<string name="filter_the_tag">Blokker etiketten «%s»\?</string>
|
||||
<string name="rating9">Underbar</string>
|
||||
<string name="error_invalid_url">Ugyldig nettadresse</string>
|
||||
|
||||
@@ -126,8 +126,6 @@
|
||||
<string name="rate_successfully">评分成功</string>
|
||||
<string name="rate_failed">评分失败</string>
|
||||
<string name="no_torrents">没有种子</string>
|
||||
<string name="download_torrent_started">开始下载种子</string>
|
||||
<string name="download_torrent_failure">无法下载种子</string>
|
||||
<string name="torrents">种子</string>
|
||||
<string name="not_favorited">未收藏</string>
|
||||
<string name="add_favorites_dialog_title">添加收藏</string>
|
||||
|
||||
@@ -126,8 +126,6 @@
|
||||
<string name="rate_successfully">評分成功</string>
|
||||
<string name="rate_failed">評分失敗</string>
|
||||
<string name="no_torrents">沒有種子</string>
|
||||
<string name="download_torrent_started">開始下載種子</string>
|
||||
<string name="download_torrent_failure">無法下載種子</string>
|
||||
<string name="torrents">種子</string>
|
||||
<string name="not_favorited">未收藏</string>
|
||||
<string name="add_favorites_dialog_title">新增到收藏裡</string>
|
||||
|
||||
@@ -142,8 +142,6 @@
|
||||
<string name="rate_successfully">Rate successfully</string>
|
||||
<string name="rate_failed">Rate failed</string>
|
||||
<string name="no_torrents">No torrents</string>
|
||||
<string name="download_torrent_started">Torrent download started</string>
|
||||
<string name="download_torrent_failure">Failed to download torrent</string>
|
||||
<string name="torrents">Torrents</string>
|
||||
<string name="not_favorited">Not favorited</string>
|
||||
<string name="add_favorites_dialog_title">Add to favorites</string>
|
||||
|
||||
Reference in new issue
Block a user