Skip to content

es/VerPelisTop: new source#37

Merged
cuong-tran merged 21 commits intomasterfrom
es/verpelistop
Mar 12, 2026
Merged

es/VerPelisTop: new source#37
cuong-tran merged 21 commits intomasterfrom
es/verpelistop

Conversation

@cuong-tran
Copy link
Contributor

@cuong-tran cuong-tran commented Feb 26, 2026

Summary by Sourcery

Add a new Spanish VerPelisTop anime extension based on the DooPlay theme, including host extractors and configuration.

New Features:

  • Introduce VerPelisTop Spanish source with popular, latest, search, and genre-filtered listings.
  • Add multi-host video extraction for VerPelisTop, supporting StreamTape, FileMoon, HexLoad, Uqload, StreamWish, VidHide, and universal extractor fallback.
  • Provide user preferences for preferred video quality, language, and streaming server within the VerPelisTop extension.
  • Implement HexloadExtractor to resolve HexLoad embed URLs into playable video streams with size-based quality labels.

Build:

  • Add Gradle module configuration and dependencies for the VerPelisTop extension, wiring in required extractor libraries.

Copilot AI review requested due to automatic review settings February 26, 2026 18:50
@sourcery-ai
Copy link

sourcery-ai bot commented Feb 26, 2026

Reviewer's Guide

Adds 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 HexloadExtractor

sequenceDiagram
  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
Loading

Class diagram for new VerPelisTop source and HexloadExtractor

classDiagram

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
Loading

File-Level Changes

Change Details Files
Introduce the VerPelisTop DooPlay-based Spanish anime source with listing, details parsing, search, filters, and video resolution logic.
  • Extend DooPlay to implement VerPelisTop source configuration (locale, name, baseUrl) and list endpoints for popular and latest content.
  • Customize animeDetailsParse to normalize and restrict genres based on a predefined GENRE_LIST and to parse description and thumbnails from the DooPlay layout.
  • Implement videoListParse and serverVideoResolver to POST to the DooPlay AJAX player endpoint, parse hosters from the iframe document, and fan out to host-specific and universal extractors using parallel utilities.
  • Define conventions mapping to detect known hosters (StreamTape, FileMoon, HexLoad, Uqload, StreamWish, VidHide) from URLs and route them to the appropriate extractor with labeled qualities.
  • Provide a searchAnimeRequest that combines text search with a genre URI filter via a GenreFilter implementation.
  • Override List
  • Set up extension preferences for preferred server and language via ListPreference, reusing the base quality preference from DooPlay.
  • Implement small utilities: hgCloud redirector for VidHide links, date override returning 0L, and string normalization to strip diacritics.
src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/VerPelisTop.kt
Add a dedicated HexloadExtractor that resolves HexLoad embed URLs via the provider’s download API and exposes them as Video objects with size-based quality labels.
  • Implement HexloadExtractor with an OkHttp client and headers, extracting the file id from embed URLs via regex.
  • POST to HexLoad’s /download endpoint with the required form fields and parse the JSON response into a ResultDto using kotlinx.serialization.
  • Generate a human-readable quality label from the file size (bytes to KB/MB/GB) and build a single Video instance with that label and the resolved URL.
  • Define serializable DTOs for the API response structure (Data and ResultDto) and a helper formatBytes function.
src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/HexloadExtractor.kt
Wire the VerPelisTop extension module into the build with the DooPlay theme and extractor library dependencies.
  • Declare VerPelisTop extension metadata (name, entry class, theme package, baseUrl, version code) in the module’s Gradle script.
  • Apply the shared common.gradle for extension configuration.
  • Add implementation dependencies on shared extractor libraries (StreamWish, Uqload, StreamTape, FileMoon, VidHide, Universal) and playlist utilities.
src/es/verpelistop/build.gradle

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @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 Source Added: A new Spanish-language anime/video source named 'VerPelisTop' has been introduced, expanding the available content.
  • Video Extractor Integration: The source integrates multiple video extractors including Uqload, Hexload, StreamWish, StreamTape, Filemoon, VidHide, and UniversalExtractor to support a wide array of video hosts.
  • Custom Hexload Extractor: A dedicated HexloadExtractor was developed to specifically handle video extraction from hexload.com, ensuring proper parsing and retrieval of video links.
  • Enhanced Filtering and Preferences: Genre filtering has been implemented, along with user preferences for selecting preferred video quality, language (Sub, Latino, Castellano), and streaming server.

🧠 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
  • src/es/verpelistop/build.gradle
    • Added build configuration for the new VerPelisTop extension, defining its name, class, base URL, and dependencies on various video extractors.
  • src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/HexloadExtractor.kt
    • Added a new Kotlin file implementing a custom extractor for hexload.com, including logic to parse video IDs, construct form bodies for download requests, and format video sizes.
  • src/es/verpelistop/src/eu/kanade/tachiyomi/animeextension/es/verpelistop/VerPelisTop.kt
    • Added the main Kotlin file for the VerPelisTop source, extending DooPlay, implementing popular and latest updates fetching, anime detail parsing, video link resolution using multiple extractors, genre filtering, and user preferences for quality, language, and server.
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 VerPelisTop source with popular/latest/search, filters, video extraction, and user preferences.
  • Add HexloadExtractor to resolve Hexload embed links into playable videos.
  • Add extension module resources (launcher icons) and build.gradle dependencies 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.

Comment on lines +260 to +263
return hgCloudLinkDomains.firstOrNull(::contains)?.let {
val redirectingDomain = hgCloudLinkRedirected.random()
return replace(it, redirectingDomain)
} ?: this
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Suggested change
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)

Copilot uses AI. Check for mistakes.
@baka-bon baka-bon closed this Mar 1, 2026
@cuong-tran cuong-tran reopened this Mar 1, 2026
@cuong-tran cuong-tran force-pushed the es/verpelistop branch 2 times, most recently from 123af26 to 5480874 Compare March 12, 2026 14:46
@cuong-tran cuong-tran requested a review from Copilot March 12, 2026 14:47
@cuong-tran
Copy link
Contributor Author

/gemini review

@cuong-tran
Copy link
Contributor Author

@sourcery-ai review

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@cuong-tran cuong-tran merged commit eafb1d2 into master Mar 12, 2026
4 checks passed
@github-actions github-actions bot deleted the es/verpelistop branch March 12, 2026 17:24
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 15, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants