Add Anime3rb source for anime streaming - #642
Conversation
Reviewer's GuideAdds a new Arabic Anime3rb streaming source implementing popular/latest/search, episode and video extraction, filtering, and user preferences (quality and custom domain) for the Tachiyomi/Aniyomi anime extension system. Sequence diagram for Anime3rb video extraction flowsequenceDiagram
actor User
participant Anime3rbSource as Anime3rb
participant Anime3rbWebsite as Anime3rb_website
participant PlaylistUtils
User->>Anime3rbSource: videoListParse(response)
Anime3rbSource->>Anime3rbWebsite: GET(onclick_url, headers)
Anime3rbWebsite-->>Anime3rbSource: Document
Anime3rbSource->>Anime3rbSource: Deobfuscator.deobfuscateScript(script)
Anime3rbSource->>Anime3rbSource: videoRegex.find(script)
alt [playlist found]
Anime3rbSource->>PlaylistUtils: extractFromHls(playlist)
PlaylistUtils-->>Anime3rbSource: List<Video>
Anime3rbSource->>Anime3rbSource: List<Video>.sort()
Anime3rbSource-->>User: sorted List<Video>
else [no playlist]
Anime3rbSource-->>User: empty List<Video>
end
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 2 issues, and left some high level feedback:
- The
GenreFilterclass currently contains the shared-preference properties andsetupPreferenceScreen/companion object, which should instead live on theAnime3rbclass implementingConfigurableAnimeSource; as written this won’t compile and the preferences UI won’t be wired correctly. searchAnimeRequestreferencesSectionFilter,CategoryFilter,SingleFilter, andgetFilterList()but onlyGenreFilteris defined in this file, so you’ll need to either add the missing filter classes andgetFilterList()implementation or import them from a shared library to avoid unresolved symbol errors.parseStatusis used inanimeDetailsParsebut isn’t defined anywhere in this file, so you should either implement it (mapping the Arabic/English status strings toSAnimestatuses) or import an existing helper to prevent compilation failures.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The `GenreFilter` class currently contains the shared-preference properties and `setupPreferenceScreen`/companion object, which should instead live on the `Anime3rb` class implementing `ConfigurableAnimeSource`; as written this won’t compile and the preferences UI won’t be wired correctly.
- `searchAnimeRequest` references `SectionFilter`, `CategoryFilter`, `SingleFilter`, and `getFilterList()` but only `GenreFilter` is defined in this file, so you’ll need to either add the missing filter classes and `getFilterList()` implementation or import them from a shared library to avoid unresolved symbol errors.
- `parseStatus` is used in `animeDetailsParse` but isn’t defined anywhere in this file, so you should either implement it (mapping the Arabic/English status strings to `SAnime` statuses) or import an existing helper to prevent compilation failures.
## Individual Comments
### Comment 1
<location path="src/ar/anime3rb/src/eu/kanade/tachiyomi/animeextension/ar/Anime3rb/Anime3rb.kt" line_range="139-140" />
<code_context>
+override fun videoListSelector(): String =
+ "li:contains(سيرفر)"
+
+private val videoRegex by lazy {
+ Regex("""(https?:)?//[^"]+\.m3u8""")
+}
+
</code_context>
<issue_to_address>
**issue (bug_risk):** Matched HLS URLs may be scheme-less (`//...`) and not normalized before being passed to the playlist extractor.
Because the regex can return values like `//host/path.m3u8`, `playlistUtils.extractFromHls` may receive URLs without a scheme and behave unexpectedly. Consider normalizing matches to fully qualified URLs (e.g., defaulting to `https:` or reusing the page’s scheme when the scheme group is empty) before calling `extractFromHls`.
</issue_to_address>
### Comment 2
<location path="src/ar/anime3rb/src/eu/kanade/tachiyomi/animeextension/ar/Anime3rb/Anime3rb.kt" line_range="126-128" />
<code_context>
+
+ episode.name = element.text()
+
+ episode.episode_number =
+ element.text()
+ .replace("الحلقة", "")
+ .trim()
+ .toFloatOrNull() ?: -1f
</code_context>
<issue_to_address>
**suggestion:** Episode number parsing relies on a simple replace, which may fail for non-standard titles.
Stripping the literal "الحلقة" then parsing the remainder as float will break for specials or titles that include extra text (ranges, parts, descriptors), causing `-1f` even when an episode number is present. Consider using a regex to extract the numeric portion (e.g. `\d+(?:\.\d+)?`) and falling back to `-1f` only when no number is found.
Suggested implementation:
```
val titleText = element.text()
episode.name = titleText
episode.episode_number =
episodeNumberRegex.find(titleText)
?.value
?.toFloatOrNull() ?: -1f
```
```
private val episodeNumberRegex by lazy {
Regex("""\d+(?:\.\d+)?""")
}
private val videoRegex by lazy {
Regex("""(https?:)?//[^"]+\.m3u8""")
}
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| private val videoRegex by lazy { | ||
| Regex("""(https?:)?//[^"]+\.m3u8""") |
There was a problem hiding this comment.
issue (bug_risk): Matched HLS URLs may be scheme-less (//...) and not normalized before being passed to the playlist extractor.
Because the regex can return values like //host/path.m3u8, playlistUtils.extractFromHls may receive URLs without a scheme and behave unexpectedly. Consider normalizing matches to fully qualified URLs (e.g., defaulting to https: or reusing the page’s scheme when the scheme group is empty) before calling extractFromHls.
| episode.episode_number = | ||
| element.text() | ||
| .replace("الحلقة", "") |
There was a problem hiding this comment.
suggestion: Episode number parsing relies on a simple replace, which may fail for non-standard titles.
Stripping the literal "الحلقة" then parsing the remainder as float will break for specials or titles that include extra text (ranges, parts, descriptors), causing -1f even when an episode number is present. Consider using a regex to extract the numeric portion (e.g. \d+(?:\.\d+)?) and falling back to -1f only when no number is found.
Suggested implementation:
val titleText = element.text()
episode.name = titleText
episode.episode_number =
episodeNumberRegex.find(titleText)
?.value
?.toFloatOrNull() ?: -1f
private val episodeNumberRegex by lazy {
Regex("""\d+(?:\.\d+)?""")
}
private val videoRegex by lazy {
Regex("""(https?:)?//[^"]+\.m3u8""")
}
Checklist:
extVersionCodevalue inbuild.gradlefor individual extensionsoverrideVersionCodeorbaseVersionCodeas needed for all multisrc extensionsisNsfw = trueflag inbuild.gradlewhen appropriateidif a source's name or language were changedweb_hi_res_512.pngwhen adding a new extensionAdd a 👍 reaction to pull requests you find important.
Summary by Sourcery
Add a new Arabic Anime3rb streaming source with search, browsing, and playback support.
New Features: