Skip to content

Commit a46fe80

Browse files
authored
Adding HubCloud and PixelDrain Improvement (#2161)
1 parent ef114f9 commit a46fe80

File tree

3 files changed

+249
-0
lines changed

3 files changed

+249
-0
lines changed
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
package com.lagradost.cloudstream3.extractors
2+
3+
import com.lagradost.api.Log
4+
import com.lagradost.cloudstream3.SubtitleFile
5+
import com.lagradost.cloudstream3.amap
6+
import com.lagradost.cloudstream3.app
7+
import com.lagradost.cloudstream3.utils.ExtractorApi
8+
import com.lagradost.cloudstream3.utils.ExtractorLink
9+
import com.lagradost.cloudstream3.utils.Qualities
10+
import com.lagradost.cloudstream3.utils.loadExtractor
11+
import com.lagradost.cloudstream3.utils.newExtractorLink
12+
import java.net.URI
13+
import java.net.URL
14+
15+
class HubCloud : ExtractorApi() {
16+
override val name = "Hub-Cloud"
17+
override val mainUrl = "https://hubcloud.ink"
18+
override val requiresReferer = false
19+
20+
override suspend fun getUrl(
21+
url: String,
22+
referer: String?,
23+
subtitleCallback: (SubtitleFile) -> Unit,
24+
callback: (ExtractorLink) -> Unit
25+
) {
26+
val tag = "HubCloud"
27+
val realUrl = url.takeIf {
28+
try { URL(it); true } catch (e: Exception) { Log.e(tag, "Invalid URL: ${e.message}"); false }
29+
} ?: return
30+
31+
val baseUrl=getBaseUrl(realUrl)
32+
33+
val href = try {
34+
if ("hubcloud.php" in realUrl) {
35+
realUrl
36+
} else {
37+
val rawHref = app.get(realUrl).document.select("#download").attr("href")
38+
if (rawHref.startsWith("http", ignoreCase = true)) {
39+
rawHref
40+
} else {
41+
baseUrl.trimEnd('/') + "/" + rawHref.trimStart('/')
42+
}
43+
}
44+
} catch (e: Exception) {
45+
Log.e(tag, "Failed to extract href: ${e.message}")
46+
""
47+
}
48+
49+
if (href.isBlank()) {
50+
Log.w(tag, "No valid href found")
51+
return
52+
}
53+
54+
val document = app.get(href).document
55+
val size = document.selectFirst("i#size")?.text().orEmpty()
56+
val header = document.selectFirst("div.card-header")?.text().orEmpty()
57+
58+
val headerDetails = cleanTitle(header)
59+
60+
val labelExtras = buildString {
61+
if (headerDetails.isNotEmpty()) append("[$headerDetails]")
62+
if (size.isNotEmpty()) append("[$size]")
63+
}
64+
val quality = getIndexQuality(header)
65+
66+
document.select("div.card-body h2 a.btn").amap { element ->
67+
val link = element.attr("href")
68+
val text = element.text()
69+
70+
when {
71+
text.contains("FSL Server", ignoreCase = true) -> {
72+
callback.invoke(
73+
newExtractorLink(
74+
"$referer [FSL Server]",
75+
"$referer [FSL Server] $labelExtras",
76+
link,
77+
) { this.quality = quality }
78+
)
79+
}
80+
81+
text.contains("Download File", ignoreCase = true) -> {
82+
callback.invoke(
83+
newExtractorLink(
84+
"$referer",
85+
"$referer $labelExtras",
86+
link,
87+
) { this.quality = quality }
88+
)
89+
}
90+
91+
text.contains("BuzzServer", ignoreCase = true) -> {
92+
val buzzResp = app.get("$link/download", referer = link, allowRedirects = false)
93+
val dlink = buzzResp.headers["hx-redirect"].orEmpty()
94+
if (dlink.isNotBlank()) {
95+
callback.invoke(
96+
newExtractorLink(
97+
"$referer [BuzzServer]",
98+
"$referer [BuzzServer] $labelExtras",
99+
dlink,
100+
) { this.quality = quality }
101+
)
102+
} else {
103+
Log.w(tag, "BuzzServer: No redirect")
104+
}
105+
}
106+
107+
text.contains("pixeldra", ignoreCase = true) || text.contains("pixel", ignoreCase = true) -> {
108+
val baseUrlLink = getBaseUrl(link)
109+
val finalURL = if (link.contains("download", true)) link
110+
else "$baseUrlLink/api/file/${link.substringAfterLast("/")}?download"
111+
112+
callback(
113+
newExtractorLink(
114+
"Pixeldrain",
115+
"Pixeldrain $labelExtras",
116+
finalURL
117+
) { this.quality = quality }
118+
)
119+
}
120+
121+
text.contains("S3 Server", ignoreCase = true) -> {
122+
callback.invoke(
123+
newExtractorLink(
124+
"$referer S3 Server",
125+
"$referer S3 Server $labelExtras",
126+
link,
127+
) { this.quality = quality }
128+
)
129+
}
130+
131+
text.contains("FSLv2", ignoreCase = true) -> {
132+
callback.invoke(
133+
newExtractorLink(
134+
"$referer FSLv2",
135+
"$referer FSLv2 $labelExtras",
136+
link,
137+
) { this.quality = quality }
138+
)
139+
}
140+
141+
text.contains("10Gbps", ignoreCase = true) -> {
142+
var currentLink = link
143+
var redirectUrl: String?
144+
var redirectCount = 0
145+
val maxRedirects = 3
146+
147+
while (redirectCount < maxRedirects) {
148+
val response = app.get(currentLink, allowRedirects = false)
149+
redirectUrl = response.headers["location"]
150+
151+
if (redirectUrl == null) {
152+
Log.e(tag, "10Gbps: No redirect")
153+
return@amap
154+
}
155+
156+
if ("link=" in redirectUrl) {
157+
val finalLink = redirectUrl.substringAfter("link=")
158+
callback.invoke(
159+
newExtractorLink(
160+
"10Gbps [Download]",
161+
"10Gbps [Download] $labelExtras",
162+
finalLink
163+
) { this.quality = quality }
164+
)
165+
return@amap
166+
}
167+
168+
currentLink = redirectUrl
169+
redirectCount++
170+
}
171+
172+
Log.e(tag, "10Gbps: Redirect limit reached ($maxRedirects)")
173+
return@amap
174+
}
175+
176+
else -> {
177+
loadExtractor(link, "", subtitleCallback, callback)
178+
}
179+
}
180+
}
181+
}
182+
183+
private fun getIndexQuality(str: String?): Int {
184+
return Regex("(\\d{3,4})[pP]").find(str.orEmpty())?.groupValues?.getOrNull(1)?.toIntOrNull()
185+
?: Qualities.P2160.value
186+
}
187+
188+
private fun getBaseUrl(url: String): String {
189+
return try {
190+
URI(url).let { "${it.scheme}://${it.host}" }
191+
} catch (e: Exception) {
192+
""
193+
}
194+
}
195+
196+
fun cleanTitle(title: String): String {
197+
val parts = title.split(".", "-", "_")
198+
199+
val qualityTags = listOf(
200+
"WEBRip", "WEB-DL", "WEB", "BluRay", "HDRip", "DVDRip", "HDTV",
201+
"CAM", "TS", "R5", "DVDScr", "BRRip", "BDRip", "DVD", "PDTV",
202+
"HD"
203+
)
204+
205+
val audioTags = listOf(
206+
"AAC", "AC3", "DTS", "MP3", "FLAC", "DD5", "EAC3", "Atmos"
207+
)
208+
209+
val subTags = listOf(
210+
"ESub", "ESubs", "Subs", "MultiSub", "NoSub", "EnglishSub", "HindiSub"
211+
)
212+
213+
val codecTags = listOf(
214+
"x264", "x265", "H264", "HEVC", "AVC"
215+
)
216+
217+
val startIndex = parts.indexOfFirst { part ->
218+
qualityTags.any { tag -> part.contains(tag, ignoreCase = true) }
219+
}
220+
221+
val endIndex = parts.indexOfLast { part ->
222+
subTags.any { tag -> part.contains(tag, ignoreCase = true) } ||
223+
audioTags.any { tag -> part.contains(tag, ignoreCase = true) } ||
224+
codecTags.any { tag -> part.contains(tag, ignoreCase = true) }
225+
}
226+
227+
return if (startIndex != -1 && endIndex != -1 && endIndex >= startIndex) {
228+
parts.subList(startIndex, endIndex + 1).joinToString(".")
229+
} else if (startIndex != -1) {
230+
parts.subList(startIndex, parts.size).joinToString(".")
231+
} else {
232+
parts.takeLast(3).joinToString(".")
233+
}
234+
}
235+
}

library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/PixelDrainExtractor.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ package com.lagradost.cloudstream3.extractors
55
import com.lagradost.cloudstream3.*
66
import com.lagradost.cloudstream3.utils.*
77

8+
class PixelDrainDev : PixelDrain(){
9+
override var mainUrl = "https://pixeldrain.dev"
10+
}
811
open class PixelDrain : ExtractorApi() {
912
override val name = "PixelDrain"
1013
override val mainUrl = "https://pixeldrain.com"
@@ -37,3 +40,5 @@ open class PixelDrain : ExtractorApi() {
3740
}
3841
}
3942
}
43+
44+

library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,11 +308,17 @@ import com.lagradost.cloudstream3.extractors.Ztreamhub
308308
import com.lagradost.cloudstream3.extractors.FileMoon
309309
import com.lagradost.cloudstream3.extractors.FileMoonSx
310310
import com.lagradost.cloudstream3.extractors.FilemoonV2
311+
import com.lagradost.cloudstream3.extractors.HubCloud
312+
import com.lagradost.cloudstream3.extractors.PixelDrainDev
311313
import com.lagradost.cloudstream3.extractors.Techinmind
312314
import com.lagradost.cloudstream3.mvvm.logError
315+
import kotlinx.coroutines.CoroutineScope
316+
import kotlinx.coroutines.Dispatchers
313317
import kotlinx.coroutines.coroutineScope
314318
import kotlinx.coroutines.delay
315319
import kotlinx.coroutines.ensureActive
320+
import kotlinx.coroutines.launch
321+
import kotlinx.coroutines.runBlocking
316322
import me.xdrop.fuzzywuzzy.FuzzySearch
317323
import org.jsoup.Jsoup
318324
import java.net.URI
@@ -842,6 +848,7 @@ suspend fun loadExtractor(
842848
)
843849
}
844850

851+
845852
/**
846853
* Tries to load the appropriate extractor based on link, returns true if any extractor is loaded.
847854
* */
@@ -1001,6 +1008,7 @@ val extractorApis: MutableList<ExtractorApi> = arrayListOf(
10011008
VidMoxy(),
10021009
Sobreatsesuyp(),
10031010
PixelDrain(),
1011+
PixelDrainDev(),
10041012
MailRu(),
10051013

10061014
Tomatomatela(),
@@ -1238,6 +1246,7 @@ val extractorApis: MutableList<ExtractorApi> = arrayListOf(
12381246
VinovoSi(),
12391247
VinovoTo(),
12401248
CloudMailRu(),
1249+
HubCloud(),
12411250
)
12421251

12431252

0 commit comments

Comments
 (0)