Conversation
Reviewer's GuideAdds a new Spanish VerPelisTop DooPlay-based anime extension with multi-host video extraction (including a custom Hexload extractor), genre filtering, and user-configurable preferences for language, server, and quality, plus the Gradle module wiring and extractor dependencies. Sequence diagram for VerPelisTop video resolution with HexloadExtractorsequenceDiagram
actor User
participant VerPelisTop
participant OkHttpClient as OkHttpClient
participant HexloadExtractor
User->>VerPelisTop: openEpisode()
VerPelisTop->>OkHttpClient: GET episodePage
OkHttpClient-->>VerPelisTop: episodeHtml
VerPelisTop->>VerPelisTop: videoListParse(response)
VerPelisTop->>VerPelisTop: select players
loop for each player
VerPelisTop->>OkHttpClient: POST adminAjax(dooplay_player_ajax)
OkHttpClient-->>VerPelisTop: iframeHtml
VerPelisTop->>OkHttpClient: GET iframeSource
OkHttpClient-->>VerPelisTop: iframeDoc
VerPelisTop->>VerPelisTop: parse hosters and classify by conventions
alt matched hexload hoster
VerPelisTop->>HexloadExtractor: videosFromUrl(hexloadUrl, prefix)
HexloadExtractor->>OkHttpClient: POST https://hexload.com/download
OkHttpClient-->>HexloadExtractor: jsonResult
HexloadExtractor-->>VerPelisTop: List Video
else other known hoster
VerPelisTop->>OkHttpClient: call specificExtractor(url)
OkHttpClient-->>VerPelisTop: List Video
end
alt unmatched hoster
VerPelisTop->>OkHttpClient: UniversalExtractor.videosFromUrl(url)
OkHttpClient-->>VerPelisTop: List Video
end
end
VerPelisTop->>VerPelisTop: sort(videos) using user preferences
VerPelisTop-->>User: playableVideoList
Class diagram for new VerPelisTop source and HexloadExtractorclassDiagram
class DooPlay
class VerPelisTop {
+popularAnimeRequest(page: Int): Request
+popularAnimeSelector(): String
+popularAnimeNextPageSelector(): String
+latestUpdatesRequest(page: Int): Request
+latestUpdatesSelector(): String
+latestUpdatesNextPageSelector(): String
+videoListSelector(): String
+animeDetailsParse(document: Document): SAnime
+videoListParse(response: Response): List~Video~
+getFilterList(): AnimeFilterList
+searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request
+setupPreferenceScreen(screen: PreferenceScreen): Unit
+sort(videos: List~Video~): List~Video~
+toDate(dateString: String): Long
+getDescription(document: Document): String
+episodeMovieText: String
+episodeSeasonPrefix: String
+prefQualityTitle: String
+prefQualityValues: Array~String~
+prefQualityEntries: Array~String~
-uqloadExtractor: UqloadExtractor
-hexloadExtractor: HexloadExtractor
-streamWishExtractor: StreamWishExtractor
-streamTapeExtractor: StreamTapeExtractor
-filemoonExtractor: FilemoonExtractor
-vidHideExtractor: VidHideExtractor
-universalExtractor: UniversalExtractor
-conventions: List~Pair~
}
class HexloadExtractor {
+HexloadExtractor(client: OkHttpClient, headers: Headers)
+videosFromUrl(url: String, prefix: String): List~Video~
-idRegex: Regex
-formatBytes(bytes: Long): String
}
class UqloadExtractor
class StreamWishExtractor
class StreamTapeExtractor
class FilemoonExtractor
class VidHideExtractor
class UniversalExtractor
class AnimeFilterList
class PreferenceScreen
class Video
class SAnime
class Document
class Request
class OkHttpClient
class Headers
VerPelisTop --|> DooPlay
VerPelisTop ..> HexloadExtractor
VerPelisTop ..> UqloadExtractor
VerPelisTop ..> StreamWishExtractor
VerPelisTop ..> StreamTapeExtractor
VerPelisTop ..> FilemoonExtractor
VerPelisTop ..> VidHideExtractor
VerPelisTop ..> UniversalExtractor
HexloadExtractor ..> OkHttpClient
HexloadExtractor ..> Headers
HexloadExtractor ..> Video
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Summary of ChangesHello @cuong-tran, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces a new Spanish anime/video source, VerPelisTop, built upon the existing DooPlay framework. It significantly enhances video playback capabilities by incorporating a comprehensive suite of video extractors, including a newly developed one for Hexload.com. The source also provides users with improved content discovery through genre filtering and personalized viewing options via configurable language and server preferences. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Changelog
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Hey - I've found 4 issues, and left some high level feedback:
- The preferred language preference uses
PREF_LANG_DEFAULT = "LATINO"while the entries/values are"Sub", "Latino", "Castellano", which means the default is not a valid value and the sorting checkit.quality.contains(lang)may never match; consider aligning the default with one of the entries (e.g.,"Latino"). - In
HexloadExtractor.videosFromUrl, you are passing the globalheadersdirectly to the POST request and also reusing them for theVideoobject; if Hexload requires specific referer/origin or lighter headers, consider building a host-specific header set instead of reusing the extension-wide headers.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The preferred language preference uses `PREF_LANG_DEFAULT = "LATINO"` while the entries/values are `"Sub", "Latino", "Castellano"`, which means the default is not a valid value and the sorting check `it.quality.contains(lang)` may never match; consider aligning the default with one of the entries (e.g., `"Latino"`).
- In `HexloadExtractor.videosFromUrl`, you are passing the global `headers` directly to the POST request and also reusing them for the `Video` object; if Hexload requires specific referer/origin or lighter headers, consider building a host-specific header set instead of reusing the extension-wide headers.
## Individual Comments
### Comment 1
<location path="src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/VerPelisTop.kt" line_range="115-116" />
<code_context>
+
+ val frameDoc = client.newCall(GET(iframeSource)).awaitSuccess().use { it.asJsoup() }
+
+ return frameDoc.select(".OD li[onclick]")
+ .map {
+ val server = it.select("span").text()
+ val lang = it.select("p").text().substringBefore("-").trim()
</code_context>
<issue_to_address>
**issue (bug_risk):** Iframe URL extraction via substring parsing is brittle and could break if attributes change format.
Using `substringAfter("src='")` / `substringBefore("'")` hardcodes single quotes and a specific attribute layout. A change to double quotes or attribute ordering will break URL extraction. Prefer parsing the HTML fragment with Jsoup and reading the iframe `src` directly, or at least support both quote styles in the parsing logic.
</issue_to_address>
### Comment 2
<location path="src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/VerPelisTop.kt" line_range="275-279" />
<code_context>
+
+ override fun List<Video>.sort(): List<Video> {
+ val quality = preferences.getString(prefQualityKey, prefQualityDefault)!!
+ val lang = preferences.getString(PREF_LANG_KEY, PREF_LANG_DEFAULT)!!
+ val server = preferences.getString(PREF_SERVER_KEY, PREF_SERVER_DEFAULT)!!
+ return sortedWith(
+ compareBy(
+ { it.quality.contains(lang) },
+ { it.quality.contains(server, true) },
+ { it.quality.contains(quality) },
</code_context>
<issue_to_address>
**issue (bug_risk):** Language and server preference matching should likely be case-insensitive and aligned with the labels used in qualities.
Here `it.quality.contains(lang)` is case-sensitive while `it.quality.contains(server, true)` is not, and `PREF_LANG_DEFAULT` is "LATINO" while `PREF_LANG_ENTRIES` uses "Latino". Because quality strings come from site text (e.g. `"$lang - StreamTape"`), user selections may not match the actual quality labels if the case differs. Consider using `contains(lang, ignoreCase = true)` and deriving `PREF_LANG_DEFAULT` from `PREF_LANG_ENTRIES` to keep them consistent.
</issue_to_address>
### Comment 3
<location path="src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/HexloadExtractor.kt" line_range="20-27" />
<code_context>
+ suspend fun videosFromUrl(url: String, prefix: String = "HexLoad"): List<Video> {
+ val id = idRegex.find(url)?.groupValues?.getOrNull(1) ?: return emptyList()
+
+ val formBody = FormBody.Builder()
+ .add("MIME Type", "application/x-www-form-urlencoded; charset=UTF-8")
+ .add("op", "download3")
+ .add("id", id)
+ .add("ajax", "1")
+ .add("method_free", "1")
+ .add("dataType", "json")
+ .build()
+
</code_context>
<issue_to_address>
**suggestion:** Placing the MIME type in the form body is unusual and likely unnecessary; headers are the appropriate place.
`.add("MIME Type", "application/x-www-form-urlencoded; charset=UTF-8")` appears to be trying to set `Content-Type` but instead places it in the POST body. Unless HexLoad specifically requires a `"MIME Type"` form field, this value is likely ignored and can be removed. If you need to control the request content type, set it via the request headers rather than as form data.
```suggestion
val formBody = FormBody.Builder()
.add("op", "download3")
.add("id", id)
.add("ajax", "1")
.add("method_free", "1")
.add("dataType", "json")
.build()
```
</issue_to_address>
### Comment 4
<location path="src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/HexloadExtractor.kt" line_range="29-31" />
<code_context>
+ .add("dataType", "json")
+ .build()
+
+ val video = client.newCall(
+ POST("https://hexload.com/download", headers, body = formBody),
+ ).awaitSuccess().parseAs<Data>().result
+
+ val quality = formatBytes(video.size).takeIf { it.isNotBlank() }
</code_context>
<issue_to_address>
**issue (bug_risk):** The `size` field type in `ResultDto` may not match the actual API response and can cause deserialization errors.
In `ResultDto`, `size` is a `Long`, but the example value is a JSON string (`"3065200640"`). If the API really returns a string here, kotlinx.serialization will throw when deserializing and you won’t get any videos. Consider changing `size` to `String`/`String?` and parsing it in `formatBytes` with error handling, or add a custom serializer that accepts both numeric and string values.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/VerPelisTop.kt
Outdated
Show resolved
Hide resolved
src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/VerPelisTop.kt
Outdated
Show resolved
Hide resolved
src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/HexloadExtractor.kt
Show resolved
Hide resolved
src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/HexloadExtractor.kt
Show resolved
Hide resolved
There was a problem hiding this comment.
Code Review
This pull request adds a new source, VerPelisTop, based on the DooPlay theme, supporting multiple video hosts and user preferences. A security audit found no significant vulnerabilities (Medium, High, or Critical) in build.gradle, HexloadExtractor.kt, and VerPelisTop.kt, indicating adherence to secure coding practices. However, there are a few suggestions to improve code clarity and performance, and to fix an incorrect API call in the Hexload extractor. Addressing these will ensure the extension is in great shape.
src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/HexloadExtractor.kt
Outdated
Show resolved
Hide resolved
src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/VerPelisTop.kt
Show resolved
Hide resolved
src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/VerPelisTop.kt
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Pull request overview
Adds a new Spanish DooPlay-based anime/video source extension for VerPelisTop, including its site implementation, a custom Hexload extractor, and required Android resources/build config.
Changes:
- Implement
VerPelisTopsource with popular/latest/search, filters, video extraction, and user preferences. - Add
HexloadExtractorto resolve Hexload embed links into playable videos. - Add extension module resources (launcher icons) and
build.gradledependencies for required extractors/utils.
Reviewed changes
Copilot reviewed 3 out of 8 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/VerPelisTop.kt |
New DooPlay source implementation, preferences, filters, and server extraction logic |
src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/HexloadExtractor.kt |
New extractor for Hexload download endpoint parsing into Video objects |
src/es/verpelistop/build.gradle |
New module gradle config and extractor/library dependencies |
src/es/verpelistop/res/mipmap-*/ic_launcher.png |
New launcher icons for the extension |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/VerPelisTop.kt
Show resolved
Hide resolved
src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/VerPelisTop.kt
Outdated
Show resolved
Hide resolved
| return hgCloudLinkDomains.firstOrNull(::contains)?.let { | ||
| val redirectingDomain = hgCloudLinkRedirected.random() | ||
| return replace(it, redirectingDomain) | ||
| } ?: this |
There was a problem hiding this comment.
redirectHgCloudHgLink() picks a redirect domain using hgCloudLinkRedirected.random(), which makes video extraction nondeterministic and can cause intermittent failures if a randomly chosen domain is down/blocked. Prefer a deterministic fallback strategy (e.g., try the original domain first, then try redirected domains in a fixed order only if the first request fails).
| return hgCloudLinkDomains.firstOrNull(::contains)?.let { | |
| val redirectingDomain = hgCloudLinkRedirected.random() | |
| return replace(it, redirectingDomain) | |
| } ?: this | |
| val originalDomain = hgCloudLinkDomains.firstOrNull(::contains) ?: return this | |
| // Map the original domain to a specific redirect domain deterministically. | |
| val domainIndex = hgCloudLinkDomains.indexOf(originalDomain).coerceAtLeast(0) | |
| val redirectIndex = domainIndex.coerceAtMost(hgCloudLinkRedirected.lastIndex) | |
| val redirectingDomain = hgCloudLinkRedirected[redirectIndex] | |
| return replace(originalDomain, redirectingDomain) |
src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/HexloadExtractor.kt
Show resolved
Hide resolved
123af26 to
5480874
Compare
|
/gemini review |
|
@sourcery-ai review |
There was a problem hiding this comment.
Hey - I've found 2 issues, and left some high level feedback:
- The language preference default (
PREF_LANG_DEFAULT = "LATINO") does not match any of thePREF_LANG_ENTRIESvalues ("Sub", "Latino", "Castellano"), so the ListPreference will have an invalid default; consider aligning the default with one of the entries (e.g. using the same casing and string). - The description extraction logic is duplicated in
Document.getDescription()andanimeDetailsParse(); consider reusinggetDescription()inanimeDetailsParse()to keep this in one place. - The
redirectHgCloudHgLink()helper usesrandom()to choose a redirecting domain, which introduces nondeterministic behavior; if not required for obfuscation, consider a deterministic mapping (e.g. first domain) to simplify debugging and reproducibility.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The language preference default (`PREF_LANG_DEFAULT = "LATINO"`) does not match any of the `PREF_LANG_ENTRIES` values (`"Sub", "Latino", "Castellano"`), so the ListPreference will have an invalid default; consider aligning the default with one of the entries (e.g. using the same casing and string).
- The description extraction logic is duplicated in `Document.getDescription()` and `animeDetailsParse()`; consider reusing `getDescription()` in `animeDetailsParse()` to keep this in one place.
- The `redirectHgCloudHgLink()` helper uses `random()` to choose a redirecting domain, which introduces nondeterministic behavior; if not required for obfuscation, consider a deterministic mapping (e.g. first domain) to simplify debugging and reproducibility.
## Individual Comments
### Comment 1
<location path="src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/VerPelisTop.kt" line_range="251" />
<code_context>
+ companion object {
+ private const val PREF_LANG_KEY = "preferred_lang"
+ private const val PREF_LANG_TITLE = "Preferred language"
+ private const val PREF_LANG_DEFAULT = "LATINO"
+ private const val PREF_SERVER_KEY = "preferred_server"
+ private val PREF_LANG_ENTRIES = arrayOf("Sub", "Latino", "Castellano")
</code_context>
<issue_to_address>
**issue (bug_risk):** Align the default language value with the actual ListPreference entry values
`PREF_LANG_ENTRIES` are used as both `entries` and `entryValues` ("Sub", "Latino", "Castellano"), but `PREF_LANG_DEFAULT` is set to "LATINO". Because ListPreference matches values case-sensitively, this default won’t match any `entryValues` and will not be applied as intended. Please change the default to an exact entry value (e.g. "Latino").
</issue_to_address>
### Comment 2
<location path="src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/VerPelisTop.kt" line_range="107-111" />
<code_context>
+ .add("type", player.attr("data-type"))
+ .build()
+
+ val iframeSource = client.newCall(POST("$baseUrl/wp-admin/admin-ajax.php", headers, body))
+ .awaitSuccess().bodyString()
+ .substringAfter("src='")
+ .substringBefore("'")
+ .replace("\\", "").ifEmpty { return emptyList() }
+
+ val frameDoc = client.newCall(GET(iframeSource)).awaitSuccess().useAsJsoup()
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Make iframe `src` extraction more robust against HTML/format changes
This logic assumes a very specific HTML format (`src='...'`) and will misbehave if the site switches to double quotes, reorders attributes, or adds extra attributes (you may end up with an invalid URL or most of the response body). Consider parsing the response as HTML with Jsoup and reading the iframe `src` attribute instead. If you keep string parsing, at least guard against missing delimiters (e.g., using `substringAfter("src='", missingDelimiterValue = "")` and returning early) before proceeding.
```suggestion
val iframeSource = client.newCall(POST("$baseUrl/wp-admin/admin-ajax.php", headers, body))
.awaitSuccess()
.useAsJsoup()
.selectFirst("iframe")
?.attr("src")
.orEmpty()
.replace("\\", "")
.ifEmpty { return emptyList() }
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/VerPelisTop.kt
Outdated
Show resolved
Hide resolved
src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/VerPelisTop.kt
Show resolved
Hide resolved
(cherry picked from commit 178e85b)
There was a problem hiding this comment.
Code Review
This pull request introduces a new Spanish source, VerPelisTop, based on the DooPlay theme. It includes a custom extractor for HexLoad and support for multiple other video hosts. The implementation is solid, but I have a few suggestions to improve code robustness and maintainability. My feedback focuses on replacing unsafe non-null assertions with safer alternatives, improving the handling of hardcoded strings, using more idiomatic Kotlin for filtering, and ensuring user-facing strings are translated to Spanish for consistency.
src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/VerPelisTop.kt
Show resolved
Hide resolved
src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/VerPelisTop.kt
Show resolved
Hide resolved
src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/VerPelisTop.kt
Show resolved
Hide resolved
src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/VerPelisTop.kt
Outdated
Show resolved
Hide resolved
src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/VerPelisTop.kt
Outdated
Show resolved
Hide resolved
src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/VerPelisTop.kt
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 237 out of 252 changed files in this pull request and generated 6 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/HexloadExtractor.kt
Show resolved
Hide resolved
src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/VerPelisTop.kt
Show resolved
Hide resolved
src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/VerPelisTop.kt
Show resolved
Hide resolved
src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/VerPelisTop.kt
Outdated
Show resolved
Hide resolved
src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/HexloadExtractor.kt
Show resolved
Hide resolved
src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/HexloadExtractor.kt
Show resolved
Hide resolved
5480874 to
ea799fb
Compare
Summary by Sourcery
Add a new Spanish VerPelisTop anime extension based on the DooPlay theme, including host extractors and configuration.
New Features:
Build: