├── app
├── src
│ ├── main
│ │ ├── java
│ │ │ └── ani
│ │ │ │ └── saikou
│ │ │ │ ├── others
│ │ │ │ ├── .gitignore
│ │ │ │ ├── DisabledReports.kt
│ │ │ │ ├── Idiosyncrasy.kt
│ │ │ │ ├── ResettableTimer.kt
│ │ │ │ ├── webview
│ │ │ │ │ └── CloudFlare.kt
│ │ │ │ ├── GlideApp.kt
│ │ │ │ ├── MalSyncBackup.kt
│ │ │ │ ├── AniSkip.kt
│ │ │ │ └── Jikan.kt
│ │ │ │ ├── settings
│ │ │ │ ├── Developer.kt
│ │ │ │ ├── ReaderSettings.kt
│ │ │ │ ├── UserInterfaceSettings.kt
│ │ │ │ ├── PlayerSettings.kt
│ │ │ │ ├── DevelopersAdapter.kt
│ │ │ │ └── ForksDialogFragment.kt
│ │ │ │ ├── anilist
│ │ │ │ ├── Genre.kt
│ │ │ │ ├── BannerImage.kt
│ │ │ │ ├── UrlMedia.kt
│ │ │ │ ├── Login.kt
│ │ │ │ └── api
│ │ │ │ │ ├── Recommendations.kt
│ │ │ │ │ ├── Studio.kt
│ │ │ │ │ └── FuzzyDate.kt
│ │ │ │ ├── media
│ │ │ │ ├── Studio.kt
│ │ │ │ ├── Source.kt
│ │ │ │ ├── Selected.kt
│ │ │ │ ├── Character.kt
│ │ │ │ ├── TitleAdapter.kt
│ │ │ │ ├── CharacterDetailsAdapter.kt
│ │ │ │ └── OtherDetailsViewModel.kt
│ │ │ │ ├── parsers
│ │ │ │ ├── anime
│ │ │ │ │ ├── HentaiStream.kt
│ │ │ │ │ └── extractors
│ │ │ │ │ │ ├── StreamTape.kt
│ │ │ │ │ │ ├── FPlayer.kt
│ │ │ │ │ │ └── VidStreaming.kt
│ │ │ │ ├── AnimeSources.kt
│ │ │ │ ├── MangaSources.kt
│ │ │ │ └── manga
│ │ │ │ │ ├── MangaPill.kt
│ │ │ │ │ ├── MangaKatana.kt
│ │ │ │ │ ├── MangaKomi.kt
│ │ │ │ │ └── MangaRead.kt
│ │ │ │ ├── manga
│ │ │ │ ├── Manga.kt
│ │ │ │ ├── MangaSourceAdapter.kt
│ │ │ │ └── MangaChapter.kt
│ │ │ │ ├── user
│ │ │ │ ├── ListViewPagerAdapter.kt
│ │ │ │ └── ListViewModel.kt
│ │ │ │ ├── anime
│ │ │ │ ├── AnimeSourceAdapter.kt
│ │ │ │ ├── Episode.kt
│ │ │ │ ├── Anime.kt
│ │ │ │ └── VideoCache.kt
│ │ │ │ ├── NoInternet.kt
│ │ │ │ ├── LoginFragment.kt
│ │ │ │ ├── download
│ │ │ │ └── video
│ │ │ │ │ └── MyDownloadService.kt
│ │ │ │ └── subcriptions
│ │ │ │ └── SubscriptionWorker.kt
│ │ ├── res
│ │ │ ├── font
│ │ │ │ ├── poppins.ttf
│ │ │ │ ├── poppins_bold.ttf
│ │ │ │ ├── poppins_thin.ttf
│ │ │ │ ├── poppins_semi_bold.ttf
│ │ │ │ └── poppins_family.xml
│ │ │ ├── drawable
│ │ │ │ ├── un_checked_home.png
│ │ │ │ ├── item_score.xml
│ │ │ │ ├── item_type.xml
│ │ │ │ ├── item_user_score.xml
│ │ │ │ ├── control_background_40dp.xml
│ │ │ │ ├── round_corner.xml
│ │ │ │ ├── bottom_nav.xml
│ │ │ │ ├── rounded_top_nav.xml
│ │ │ │ ├── shape_corner_16dp.xml
│ │ │ │ ├── item_ongoing.xml
│ │ │ │ ├── linear_gradient_black_horizontal.xml
│ │ │ │ ├── linear_gradient_black.xml
│ │ │ │ ├── linear_gradient_bg.xml
│ │ │ │ ├── linear_gradient_nav.xml
│ │ │ │ ├── ic_round_alpha_t_box_24.xml
│ │ │ │ ├── ic_round_space_bar_24.xml
│ │ │ │ ├── ic_round_play_arrow_24.xml
│ │ │ │ ├── ic_page_numbering.xml
│ │ │ │ ├── ic_round_font_size_24.xml
│ │ │ │ ├── ic_round_pause_24.xml
│ │ │ │ ├── ic_round_source_24.xml
│ │ │ │ ├── ic_round_arrow_drop_down_24.xml
│ │ │ │ ├── ic_round_skip_next_24.xml
│ │ │ │ ├── ic_round_person_24.xml
│ │ │ │ ├── ic_round_filter_alt_24.xml
│ │ │ │ ├── ic_round_info_24.xml
│ │ │ │ ├── ic_round_arrow_back_ios_new_24.xml
│ │ │ │ ├── ic_round_skip_previous_24.xml
│ │ │ │ ├── ic_round_subtitles_24.xml
│ │ │ │ ├── ic_round_fast_forward_24.xml
│ │ │ │ ├── ic_round_lock_24.xml
│ │ │ │ ├── ic_round_play_circle_24.xml
│ │ │ │ ├── ic_round_audiotrack_24.xml
│ │ │ │ ├── ic_round_home_24.xml
│ │ │ │ ├── ic_round_view_array_24.xml
│ │ │ │ ├── ic_round_fast_rewind_24.xml
│ │ │ │ ├── ic_round_swipe_up_alt_24.xml
│ │ │ │ ├── ic_skip.xml
│ │ │ │ ├── ic_round_swipe_down_alt_24.xml
│ │ │ │ ├── ic_round_add_circle_24.xml
│ │ │ │ ├── ic_round_favorite_24.xml
│ │ │ │ ├── ic_round_format_text_24.xml
│ │ │ │ ├── ic_round_remove_red_eye_24.xml
│ │ │ │ ├── ic_round_calendar_today_24.xml
│ │ │ │ ├── ic_round_star_24.xml
│ │ │ │ ├── ic_round_download_24.xml
│ │ │ │ ├── ic_round_collections_bookmark_24.xml
│ │ │ │ ├── ic_round_photo_size_select_actual_24.xml
│ │ │ │ ├── ic_round_restaurant_24.xml
│ │ │ │ ├── ic_round_view_column_24.xml
│ │ │ │ ├── ic_round_picture_in_picture_alt_24.xml
│ │ │ │ ├── ic_round_straighten_24.xml
│ │ │ │ ├── ic_round_view_comfy_24.xml
│ │ │ │ ├── ic_round_video_library_24.xml
│ │ │ │ ├── ic_round_dns_24.xml
│ │ │ │ ├── ic_round_close_24.xml
│ │ │ │ ├── ic_round_date_range_24.xml
│ │ │ │ ├── ic_round_notifications_none_24.xml
│ │ │ │ ├── ic_round_add_circle_outline_24.xml
│ │ │ │ ├── ic_round_play_disabled_24.xml
│ │ │ │ ├── ic_launcher_background.xml
│ │ │ │ ├── ic_round_amp_stories_24.xml
│ │ │ │ ├── ic_round_search_24.xml
│ │ │ │ ├── ic_baseline_screen_lock_portrait_24.xml
│ │ │ │ ├── ic_round_heart_broken_24.xml
│ │ │ │ ├── ic_round_fullscreen_24.xml
│ │ │ │ ├── ic_round_brightness_medium_24.xml
│ │ │ │ ├── ic_round_playlist_play_24.xml
│ │ │ │ ├── ic_round_sd_card_24.xml
│ │ │ │ ├── ic_anilist.xml
│ │ │ │ ├── ic_round_share_24.xml
│ │ │ │ ├── ic_round_lock_open_24.xml
│ │ │ │ ├── ic_round_refresh_24.xml
│ │ │ │ ├── ic_round_volume_up_24.xml
│ │ │ │ ├── ic_round_favorite_border_24.xml
│ │ │ │ ├── ic_round_translate_variant_24.xml
│ │ │ │ ├── ic_round_playlist_add_24.xml
│ │ │ │ ├── ic_round_brightness_high_24.xml
│ │ │ │ ├── ic_round_color_24.xml
│ │ │ │ ├── ic_round_touch_app_24.xml
│ │ │ │ ├── baseline_paid_24.xml
│ │ │ │ ├── ic_round_high_quality_24.xml
│ │ │ │ ├── ic_round_art_track_24.xml
│ │ │ │ ├── ic_round_screen_rotation_alt_24.xml
│ │ │ │ ├── spinner_icon.xml
│ │ │ │ ├── ic_round_movie_filter_24.xml
│ │ │ │ ├── spinner_icon_manga.xml
│ │ │ │ ├── ic_round_smart_button_24.xml
│ │ │ │ ├── ic_round_accessible_forward_24.xml
│ │ │ │ ├── ic_round_cast_24.xml
│ │ │ │ ├── ic_round_import_contacts_24.xml
│ │ │ │ ├── ic_round_sync_24.xml
│ │ │ │ ├── ic_round_brightness_auto_24.xml
│ │ │ │ ├── ic_round_auto_awesome_24.xml
│ │ │ │ ├── ic_round_view_list_24.xml
│ │ │ │ ├── ic_round_brightness_4_24.xml
│ │ │ │ ├── ic_round_new_releases_24.xml
│ │ │ │ ├── ic_round_notifications_active_24.xml
│ │ │ │ ├── ic_round_grid_view_24.xml
│ │ │ │ ├── ic_round_animation_24.xml
│ │ │ │ ├── ic_round_swipe_vertical_24.xml
│ │ │ │ ├── ic_myanimelist.xml
│ │ │ │ ├── ui_bg.xml
│ │ │ │ ├── ic_launcher_foreground.xml
│ │ │ │ ├── ic_round_settings_24.xml
│ │ │ │ ├── ic_github.xml
│ │ │ │ ├── monochrome.xml
│ │ │ │ ├── ic_round_help_24.xml
│ │ │ │ ├── ic_round_sort_24.xml
│ │ │ │ ├── ic_telegram.xml
│ │ │ │ ├── ic_round_slow_motion_video_24.xml
│ │ │ │ ├── ic_round_reader_settings.xml
│ │ │ │ ├── ic_round_video_settings_24.xml
│ │ │ │ └── ic_round_menu_book_24.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── anim
│ │ │ │ └── over_shoot.xml
│ │ │ ├── values
│ │ │ │ ├── preloaded_fonts.xml
│ │ │ │ ├── attr.xml
│ │ │ │ └── colors.xml
│ │ │ ├── color
│ │ │ │ ├── tab_layout_text.xml
│ │ │ │ ├── tab_layout_icon.xml
│ │ │ │ └── button_switch_track.xml
│ │ │ ├── layout
│ │ │ │ ├── item_progressbar.xml
│ │ │ │ ├── item_title.xml
│ │ │ │ ├── item_dropdown.xml
│ │ │ │ ├── item_recyclerview.xml
│ │ │ │ ├── item_chip.xml
│ │ │ │ ├── splash_screen.xml
│ │ │ │ ├── activity_exoplayer.xml
│ │ │ │ ├── item_episodes_recyclerview.xml
│ │ │ │ ├── activity_search.xml
│ │ │ │ ├── item_question.xml
│ │ │ │ ├── fragment_list.xml
│ │ │ │ ├── item_subtitle_text.xml
│ │ │ │ ├── bottom_sheet_webview.xml
│ │ │ │ ├── item_title_text.xml
│ │ │ │ ├── item_count_down.xml
│ │ │ │ ├── bottom_sheet_subtitles.xml
│ │ │ │ ├── item_title_chipgroup.xml
│ │ │ │ ├── item_title_recycler.xml
│ │ │ │ ├── item_character_details.xml
│ │ │ │ ├── item_genre.xml
│ │ │ │ ├── item_stream.xml
│ │ │ │ ├── bottom_sheet_developers.xml
│ │ │ │ ├── activity_faq.xml
│ │ │ │ ├── fragment_anime_watch.xml
│ │ │ │ ├── activity_genre.xml
│ │ │ │ ├── item_image.xml
│ │ │ │ ├── item_title_trailer.xml
│ │ │ │ ├── fragment_anime.xml
│ │ │ │ └── fragment_manga.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── menu
│ │ │ │ ├── menu_media.xml
│ │ │ │ ├── anime_menu_detail.xml
│ │ │ │ ├── manga_menu_detail.xml
│ │ │ │ ├── list_sort_menu.xml
│ │ │ │ └── bottom_navbar_menu.xml
│ │ │ ├── values-night
│ │ │ │ ├── themes.xml
│ │ │ │ └── colors.xml
│ │ │ └── xml
│ │ │ │ └── provider_paths.xml
│ │ └── ic_launcher-playstore.png
│ └── debug
│ │ └── res
│ │ └── values
│ │ └── strings.xml
└── .gitignore
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── beta.md
├── .idea
├── codeStyles
│ └── codeStyleConfig.xml
├── compiler.xml
├── vcs.xml
├── kotlinScripting.xml
├── discord.xml
└── gradle.xml
├── settings.gradle.kts
├── .github
├── ISSUE_TEMPLATE
│ ├── config.yml
│ └── feature_request.yml
├── workflows
│ ├── pull-req.yml
│ └── latest.yml
└── FUNDING.yml
└── gradle.properties
/app/src/main/java/ani/saikou/others/.gitignore:
--------------------------------------------------------------------------------
1 | DisabledReports.kt
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /debug
3 | /debug/output-metadata.json
4 | /release
--------------------------------------------------------------------------------
/app/src/main/res/font/poppins.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/professorDeveloper/saikou/HEAD/app/src/main/res/font/poppins.ttf
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/professorDeveloper/saikou/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | .idea
4 | build
5 | local.properties
6 | .DS_Store
7 | /captures
8 | .externalNativeBuild
9 | .cxx
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-playstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/professorDeveloper/saikou/HEAD/app/src/main/ic_launcher-playstore.png
--------------------------------------------------------------------------------
/app/src/main/res/font/poppins_bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/professorDeveloper/saikou/HEAD/app/src/main/res/font/poppins_bold.ttf
--------------------------------------------------------------------------------
/app/src/main/res/font/poppins_thin.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/professorDeveloper/saikou/HEAD/app/src/main/res/font/poppins_thin.ttf
--------------------------------------------------------------------------------
/beta.md:
--------------------------------------------------------------------------------
1 | # 1.2.3.1
2 |
3 | - Fix Mangareader Crashes
4 | - Fix Mangareader getting stuck on loading
5 | - Fix Allanime
6 | - Fix ConsumeBili
--------------------------------------------------------------------------------
/app/src/main/res/font/poppins_semi_bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/professorDeveloper/saikou/HEAD/app/src/main/res/font/poppins_semi_bold.ttf
--------------------------------------------------------------------------------
/app/src/main/res/drawable/un_checked_home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/professorDeveloper/saikou/HEAD/app/src/main/res/drawable/un_checked_home.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/professorDeveloper/saikou/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/professorDeveloper/saikou/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/professorDeveloper/saikou/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/professorDeveloper/saikou/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/debug/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Saikou β
4 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/professorDeveloper/saikou/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/professorDeveloper/saikou/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/professorDeveloper/saikou/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/professorDeveloper/saikou/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/professorDeveloper/saikou/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/professorDeveloper/saikou/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/anim/over_shoot.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/others/DisabledReports.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.others
2 |
3 | const val DisabledReports=true
4 | //Setting this to false, will allow sending crash reports to Saikou's Firebase Crashlytics
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/settings/Developer.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.settings
2 |
3 | data class Developer(
4 | val name: String,
5 | val pfp: String,
6 | val role: String,
7 | val url: String
8 | )
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/item_score.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/item_type.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/item_user_score.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/kotlinScripting.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/control_background_40dp.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/round_corner.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bottom_nav.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/rounded_top_nav.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | dependencyResolutionManagement {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | maven("https://jitpack.io")
6 | }
7 | }
8 |
9 | rootProject.name = "Saikou"
10 | include(":app")
11 |
--------------------------------------------------------------------------------
/.idea/discord.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shape_corner_16dp.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/anilist/Genre.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.anilist
2 |
3 | import java.io.Serializable
4 |
5 | data class Genre(
6 | val name: String,
7 | var id: Int,
8 | var thumbnail: String,
9 | var time: Long,
10 | ) : Serializable
--------------------------------------------------------------------------------
/app/src/main/res/drawable/item_ongoing.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/media/Studio.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.media
2 |
3 | import java.io.Serializable
4 |
5 | data class Studio(
6 | val id: String,
7 | val name: String,
8 | var yearMedia: MutableMap>? = null
9 | ) : Serializable
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/preloaded_fonts.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - @font/poppins_bold
5 | - @font/poppins_thin
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/color/tab_layout_text.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/color/tab_layout_icon.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Jan 12 15:21:18 IST 2023
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/media/Source.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.media
2 |
3 | import java.io.Serializable
4 |
5 | data class Source(
6 | val link: String,
7 | val name: String,
8 | val cover: String,
9 | val headers: MutableMap? = null
10 | ) : Serializable
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/parsers/anime/HentaiStream.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.parsers.anime
2 |
3 | class HentaiStream: HentaiFF() {
4 | override val name: String = "Hentai Stream"
5 | override val saveName: String = "hentai_stream"
6 | override val hostUrl = "https://hentaistream.moe"
7 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/linear_gradient_black_horizontal.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/color/button_switch_track.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_progressbar.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/linear_gradient_black.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/linear_gradient_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/linear_gradient_nav.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/anilist/BannerImage.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.anilist
2 |
3 | import java.io.Serializable
4 |
5 | data class BannerImage(
6 | val url: String?,
7 | var time: Long,
8 | ) : Serializable {
9 | fun checkTime(): Boolean {
10 | return (System.currentTimeMillis() - time) >= (1000 * 60 * 60 * 6)
11 | }
12 | }
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: Discord Server
4 | url: https://discord.gg/2T7TunuwFZ
5 | about: Join our Discord server for support and updates.
6 | - name: Telegram group
7 | url: https://t.me/saikou_discussion
8 | about: Join our Telegram discussion group for support and updates.
9 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/manga/Manga.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.manga
2 |
3 | import java.io.Serializable
4 |
5 | data class Manga(
6 | var totalChapters: Int? = null,
7 | var selectedChapter: String? = null,
8 | var chapters: MutableMap? = null,
9 | var slug: String? = null,
10 | var author: String?=null,
11 | ) : Serializable
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/font/poppins_family.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/values/attr.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_media.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_alpha_t_box_24.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_space_bar_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_title.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/media/Selected.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.media
2 |
3 | import java.io.Serializable
4 |
5 | data class Selected(
6 | var window: Int = 0,
7 | var recyclerStyle: Int? = null,
8 | var recyclerReversed: Boolean = false,
9 | var chip: Int = 0,
10 | var source: Int = 0,
11 | var preferDub: Boolean = false,
12 | var server: String? = null,
13 | var video: Int = 0,
14 | var latest: Float = 0f,
15 | ) : Serializable
16 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/settings/ReaderSettings.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.settings
2 |
3 | import java.io.Serializable
4 |
5 | data class ReaderSettings(
6 | var showSource: Boolean = true,
7 | var showSystemBars: Boolean = false,
8 |
9 | var autoDetectWebtoon: Boolean = true,
10 | var default: CurrentReaderSettings = CurrentReaderSettings(),
11 |
12 | var askIndividual: Boolean = true,
13 | var updateForH: Boolean = false
14 | ) : Serializable
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_play_arrow_24.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_page_numbering.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/provider_paths.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
9 |
12 |
15 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_font_size_24.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_pause_24.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_source_24.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/media/Character.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.media
2 |
3 | import ani.saikou.anilist.api.FuzzyDate
4 | import java.io.Serializable
5 |
6 | data class Character(
7 | val id: Int,
8 | val name: String?,
9 | val image: String?,
10 | val banner: String?,
11 | val role: String,
12 |
13 | var description: String? = null,
14 | var age: String? = null,
15 | var gender: String? = null,
16 | var dateOfBirth: FuzzyDate? = null,
17 | var roles: ArrayList? = null
18 | ) : Serializable
--------------------------------------------------------------------------------
/app/src/main/res/menu/anime_menu_detail.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/manga_menu_detail.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_arrow_drop_down_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_skip_next_24.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/user/ListViewPagerAdapter.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.user
2 |
3 | import androidx.fragment.app.Fragment
4 | import androidx.fragment.app.FragmentActivity
5 | import androidx.viewpager2.adapter.FragmentStateAdapter
6 |
7 | class ListViewPagerAdapter(private val size: Int, private val calendar: Boolean, fragment: FragmentActivity) :
8 | FragmentStateAdapter(fragment) {
9 | override fun getItemCount(): Int = size
10 | override fun createFragment(position: Int): Fragment = ListFragment.newInstance(position, calendar)
11 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_person_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_filter_alt_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_info_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_dropdown.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_recyclerview.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_arrow_back_ios_new_24.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_skip_previous_24.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_subtitles_24.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/list_sort_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/values-night/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #000000
4 | #A8EEEEEE
5 | @color/bg_white
6 | #1C1C1C
7 | #001C1C1C
8 | #40ffffff
9 | #40ffffff
10 | #54000000
11 | #80000000
12 | #29FF6B08
13 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_chip.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_fast_forward_24.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_lock_24.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_play_circle_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_audiotrack_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_home_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_view_array_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_fast_rewind_24.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_swipe_up_alt_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_skip.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_swipe_down_alt_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_add_circle_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/bottom_navbar_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_favorite_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_format_text_24.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_remove_red_eye_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_calendar_today_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_star_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/splash_screen.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_download_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_collections_bookmark_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_photo_size_select_actual_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_restaurant_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_view_column_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_picture_in_picture_alt_24.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_straighten_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_view_comfy_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_video_library_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_dns_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_close_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_date_range_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_notifications_none_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_add_circle_outline_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_play_disabled_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/anime/AnimeSourceAdapter.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.anime
2 |
3 | import ani.saikou.media.MediaDetailsViewModel
4 | import ani.saikou.media.SourceAdapter
5 | import ani.saikou.media.SourceSearchDialogFragment
6 | import ani.saikou.parsers.ShowResponse
7 | import kotlinx.coroutines.CoroutineScope
8 |
9 | class AnimeSourceAdapter(
10 | sources: List,
11 | val model: MediaDetailsViewModel,
12 | val i: Int,
13 | val id: Int,
14 | fragment: SourceSearchDialogFragment,
15 | scope: CoroutineScope
16 | ) : SourceAdapter(sources, fragment, scope) {
17 |
18 | override suspend fun onItemClick(source: ShowResponse) {
19 | model.overrideEpisodes(i, source, id)
20 | }
21 | }
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/manga/MangaSourceAdapter.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.manga
2 |
3 | import ani.saikou.media.MediaDetailsViewModel
4 | import ani.saikou.media.SourceAdapter
5 | import ani.saikou.media.SourceSearchDialogFragment
6 | import ani.saikou.parsers.ShowResponse
7 | import kotlinx.coroutines.CoroutineScope
8 |
9 | class MangaSourceAdapter(
10 | sources: List,
11 | val model: MediaDetailsViewModel,
12 | val i: Int,
13 | val id: Int,
14 | fragment: SourceSearchDialogFragment,
15 | scope: CoroutineScope
16 | ) : SourceAdapter(sources, fragment, scope) {
17 | override suspend fun onItemClick(source: ShowResponse) {
18 | model.overrideMangaChapters(i, source, id)
19 | }
20 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
6 |
8 |
11 |
14 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_amp_stories_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_search_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_screen_lock_portrait_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_exoplayer.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_episodes_recyclerview.xml:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/settings/UserInterfaceSettings.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.settings
2 |
3 | import java.io.Serializable
4 |
5 | data class UserInterfaceSettings(
6 | var darkMode: Boolean? = null,
7 | var showYtButton: Boolean = true,
8 | var animeDefaultView: Int = 0,
9 | var mangaDefaultView: Int = 0,
10 |
11 | //App
12 | var immersiveMode: Boolean = false,
13 | var smallView: Boolean = true,
14 | var defaultStartUpTab: Int = 1,
15 | var homeLayoutShow: MutableList = mutableListOf(true, false, false, true, false, false, true),
16 |
17 | //Animations
18 | var bannerAnimations: Boolean = true,
19 | var layoutAnimations: Boolean = true,
20 | var animationSpeed: Float = 1f
21 |
22 | ) : Serializable
--------------------------------------------------------------------------------
/.github/workflows/pull-req.yml:
--------------------------------------------------------------------------------
1 | name: PR Test Builds
2 |
3 | on:
4 | pull_request:
5 | branches: [ main ]
6 | jobs:
7 | build:
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 | - uses: actions/checkout@v3
12 |
13 | - name: set up JDK 17
14 | uses: actions/setup-java@v3
15 | with:
16 | java-version: '17'
17 | distribution: 'temurin'
18 | cache: gradle
19 |
20 | - name: Grant execute permission for gradlew
21 | run: chmod +x gradlew
22 | - name: Build with Gradle
23 | run: ./gradlew assembleDebug
24 | shell: bash
25 |
26 | - name: Upload a Build Artifact
27 | uses: actions/upload-artifact@v3.0.0
28 | with:
29 | path: "app/build/outputs/apk/debug/app-debug.apk"
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_heart_broken_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_fullscreen_24.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_brightness_medium_24.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_playlist_play_24.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_sd_card_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_anilist.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_share_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/others/Idiosyncrasy.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("UNCHECKED_CAST", "DEPRECATION")
2 |
3 | package ani.saikou.others
4 |
5 | import android.content.Intent
6 | import android.os.Build
7 | import android.os.Bundle
8 | import java.io.Serializable
9 |
10 | inline fun Bundle.getSerialized(key: String): T? {
11 | return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
12 | this.getSerializable(key, T::class.java)
13 | else
14 | this.getSerializable(key) as? T
15 | }
16 |
17 | inline fun Intent.getSerialized(key: String): T? {
18 | return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
19 | this.getSerializableExtra(key, T::class.java)
20 | else
21 | this.getSerializableExtra(key) as? T
22 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_lock_open_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_refresh_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_search.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_volume_up_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_favorite_border_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_translate_variant_24.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
13 | custom: ['https://www.buymeacoffee.com/brahmkshatriya']
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_playlist_add_24.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/parsers/anime/extractors/StreamTape.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.parsers.anime.extractors
2 |
3 | import ani.saikou.FileUrl
4 | import ani.saikou.client
5 | import ani.saikou.getSize
6 | import ani.saikou.parsers.*
7 |
8 | class StreamTape(override val server: VideoServer) : VideoExtractor() {
9 | private val linkRegex = Regex("""'robotlink'\)\.innerHTML = '(.+?)'\+ \('(.+?)'\)""")
10 |
11 | override suspend fun extract(): VideoContainer {
12 | val reg = linkRegex.find(client.get(server.embed.url.replace("tape.com","adblocker.xyz")).text)?:return VideoContainer(listOf())
13 | val extractedUrl = FileUrl("https:${reg.groups[1]!!.value + reg.groups[2]!!.value.substring(3)}")
14 | return VideoContainer(listOf(Video(null, VideoType.CONTAINER, extractedUrl, getSize(extractedUrl))))
15 | }
16 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_brightness_high_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/user/ListViewModel.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.user
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.MutableLiveData
5 | import androidx.lifecycle.ViewModel
6 | import ani.saikou.anilist.Anilist
7 | import ani.saikou.loadData
8 | import ani.saikou.media.Media
9 | import ani.saikou.tryWithSuspend
10 |
11 | class ListViewModel : ViewModel() {
12 | var grid = MutableLiveData(loadData("listGrid") ?: true)
13 |
14 | private val lists = MutableLiveData>>()
15 | fun getLists(): LiveData>> = lists
16 | suspend fun loadLists(anime: Boolean, userId: Int, sortOrder: String? = null) {
17 | tryWithSuspend {
18 | lists.postValue(Anilist.query.getMediaLists(anime, userId, sortOrder))
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_color_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_question.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/anime/Episode.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.anime
2 |
3 | import ani.saikou.FileUrl
4 | import ani.saikou.parsers.VideoExtractor
5 | import java.io.Serializable
6 |
7 | data class Episode(
8 | val number: String,
9 | var link: String? = null,
10 | var title: String? = null,
11 | var desc: String? = null,
12 | var thumb: FileUrl? = null,
13 | var filler: Boolean = false,
14 | var selectedExtractor: String? = null,
15 | var selectedVideo: Int = 0,
16 | var selectedSubtitle: Int? = -1,
17 | var extractors: MutableList?=null,
18 | @Transient var extractorCallback: ((VideoExtractor) -> Unit)?=null,
19 | var allStreams: Boolean = false,
20 | var watched: Long? = null,
21 | var maxLength: Long? = null,
22 | val extra: Map?=null,
23 |
24 | ) : Serializable
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_touch_app_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/baseline_paid_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/anime/Anime.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.anime
2 |
3 | import ani.saikou.media.Studio
4 | import java.io.Serializable
5 |
6 | data class Anime(
7 | var totalEpisodes: Int? = null,
8 |
9 | var episodeDuration: Int? = null,
10 | var season: String? = null,
11 | var seasonYear: Int? = null,
12 |
13 | var op: ArrayList = arrayListOf(),
14 | var ed: ArrayList = arrayListOf(),
15 |
16 | var mainStudio: Studio? = null,
17 |
18 | var youtube: String? = null,
19 | var nextAiringEpisode: Int? = null,
20 | var nextAiringEpisodeTime: Long? = null,
21 |
22 | var selectedEpisode: String? = null,
23 | var episodes: MutableMap? = null,
24 | var slug: String? = null,
25 | var kitsuEpisodes: Map? = null,
26 | var fillerEpisodes: Map? = null,
27 | ) : Serializable
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_high_quality_24.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_art_track_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_screen_rotation_alt_24.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/others/ResettableTimer.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.others
2 |
3 | import java.util.*
4 | import java.util.concurrent.atomic.*
5 |
6 | class ResettableTimer {
7 | var resetLock = AtomicBoolean(false)
8 | var timer = Timer()
9 | fun reset(timerTask: TimerTask, delay: Long) {
10 | if (!resetLock.getAndSet(true)) {
11 | timer.cancel()
12 | timer.purge()
13 | timer = Timer()
14 | timer.schedule(object : TimerTask() {
15 | override fun run() {
16 | if (!resetLock.getAndSet(true)) {
17 | timerTask.run()
18 | timer.cancel()
19 | timer.purge()
20 | resetLock.set(false)
21 | }
22 | }
23 | }, delay)
24 | resetLock.set(false)
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/media/TitleAdapter.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.media
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.recyclerview.widget.RecyclerView
6 | import ani.saikou.databinding.ItemTitleBinding
7 |
8 | class TitleAdapter(private val text: String) : RecyclerView.Adapter() {
9 | inner class TitleViewHolder(val binding: ItemTitleBinding) : RecyclerView.ViewHolder(binding.root)
10 |
11 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TitleViewHolder {
12 | val binding = ItemTitleBinding.inflate(LayoutInflater.from(parent.context), parent, false)
13 | return TitleViewHolder(binding)
14 | }
15 |
16 | override fun onBindViewHolder(holder: TitleViewHolder, position: Int) {
17 | holder.binding.itemTitle.text = text
18 | }
19 |
20 | override fun getItemCount(): Int = 1
21 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/spinner_icon.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
15 |
16 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_movie_filter_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/spinner_icon_manga.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
15 |
16 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_smart_button_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/anilist/UrlMedia.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.anilist
2 |
3 | import android.app.Activity
4 | import android.net.Uri
5 | import android.os.Bundle
6 | import androidx.core.os.bundleOf
7 | import ani.saikou.loadMedia
8 | import ani.saikou.startMainActivity
9 |
10 | class UrlMedia : Activity() {
11 | override fun onCreate(savedInstanceState: Bundle?) {
12 | super.onCreate(savedInstanceState)
13 | var id: Int? = intent?.extras?.getInt("media", 0) ?: 0
14 | var isMAL = false
15 | var continueMedia = true
16 | if (id == 0) {
17 | continueMedia = false
18 | val data: Uri? = intent?.data
19 | isMAL = data?.host != "anilist.co"
20 | id = data?.pathSegments?.getOrNull(1)?.toIntOrNull()
21 | } else loadMedia = id
22 | startMainActivity(this, bundleOf("mediaId" to id, "mal" to isMAL, "continue" to continueMedia))
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/NoInternet.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou
2 |
3 | import android.os.Bundle
4 | import android.view.ViewGroup
5 | import androidx.appcompat.app.AppCompatActivity
6 | import androidx.core.view.updateLayoutParams
7 | import ani.saikou.databinding.ActivityNoInternetBinding
8 |
9 | class NoInternet : AppCompatActivity() {
10 | override fun onCreate(savedInstanceState: Bundle?) {
11 | super.onCreate(savedInstanceState)
12 |
13 | val binding = ActivityNoInternetBinding.inflate(layoutInflater)
14 | setContentView(binding.root)
15 |
16 | binding.refreshContainer.updateLayoutParams {
17 | topMargin = statusBarHeight
18 | bottomMargin = navBarHeight
19 | }
20 | binding.refreshButton.setOnClickListener {
21 | if (isOnline(this)) {
22 | startMainActivity(this)
23 | }
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/parsers/AnimeSources.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.parsers
2 |
3 | import ani.saikou.Lazier
4 | import ani.saikou.lazyList
5 | import ani.saikou.parsers.anime.*
6 |
7 | object AnimeSources : WatchSources() {
8 | override val list: List> = lazyList(
9 | "AllAnime" to ::AllAnime,
10 | "Gogo" to ::Gogo,
11 | "Zoro" to ::Zoro,
12 | "Marin" to ::Marin,
13 | "AnimePahe" to ::AnimePahe,
14 | "Consume Bili" to ::ConsumeBili,
15 | // "Enime" to ::Enime,
16 | "NineAnime" to ::NineAnime,
17 | )
18 | }
19 |
20 | object HAnimeSources : WatchSources() {
21 | val aList: List> = lazyList(
22 | "HentaiMama" to ::HentaiMama,
23 | "Haho" to ::Haho,
24 | "HentaiStream" to ::HentaiStream,
25 | "HentaiFF" to ::HentaiFF,
26 | )
27 |
28 | override val list = listOf(aList,AnimeSources.list).flatten()
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_accessible_forward_24.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_cast_24.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_import_contacts_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_sync_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_subtitle_text.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_brightness_auto_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_auto_awesome_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/anilist/Login.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.anilist
2 |
3 | import android.content.Context
4 | import android.net.Uri
5 | import android.os.Bundle
6 | import androidx.appcompat.app.AppCompatActivity
7 | import ani.saikou.logError
8 | import ani.saikou.logger
9 | import ani.saikou.startMainActivity
10 |
11 | class Login : AppCompatActivity() {
12 | override fun onCreate(savedInstanceState: Bundle?) {
13 | super.onCreate(savedInstanceState)
14 | val data: Uri? = intent?.data
15 | logger(data.toString())
16 | try {
17 | Anilist.token = Regex("""(?<=access_token=).+(?=&token_type)""").find(data.toString())!!.value
18 | val filename = "anilistToken"
19 | this.openFileOutput(filename, Context.MODE_PRIVATE).use {
20 | it.write(Anilist.token!!.toByteArray())
21 | }
22 | } catch (e: Exception) {
23 | logError(e)
24 | }
25 | startMainActivity(this)
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_view_list_24.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_brightness_4_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_new_releases_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_notifications_active_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_grid_view_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
16 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_animation_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/anime/VideoCache.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.anime
2 |
3 | import android.content.Context
4 | import androidx.media3.common.util.UnstableApi
5 | import androidx.media3.database.StandaloneDatabaseProvider
6 | import androidx.media3.datasource.cache.LeastRecentlyUsedCacheEvictor
7 | import androidx.media3.datasource.cache.SimpleCache
8 | import java.io.File
9 |
10 | @UnstableApi
11 | object VideoCache {
12 | private var simpleCache: SimpleCache? = null
13 | fun getInstance(context: Context): SimpleCache {
14 | val databaseProvider = StandaloneDatabaseProvider(context)
15 | if (simpleCache == null)
16 | simpleCache = SimpleCache(
17 | File(context.cacheDir, "exoplayer").also { it.deleteOnExit() }, // Ensures always fresh file
18 | LeastRecentlyUsedCacheEvictor(300L * 1024L * 1024L),
19 | databaseProvider
20 | )
21 | return simpleCache as SimpleCache
22 | }
23 |
24 | fun release() {
25 | simpleCache?.release()
26 | simpleCache = null
27 | }
28 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_swipe_vertical_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/others/webview/CloudFlare.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.others.webview
2 |
3 | import android.graphics.Bitmap
4 | import android.webkit.WebView
5 | import android.webkit.WebViewClient
6 | import ani.saikou.FileUrl
7 |
8 | class CloudFlare(override val location: FileUrl) : WebViewBottomDialog() {
9 | val cfTag = "cf_clearance"
10 |
11 | override var title = "Cloudflare Bypass"
12 | override val webViewClient = object : WebViewClient() {
13 | override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
14 | val cookie = cookies.getCookie(url.toString())
15 | if (cookie?.contains(cfTag) == true) {
16 | val clearance = cookie.substringAfter("$cfTag=").substringBefore(";")
17 | privateCallback.invoke(mapOf(cfTag to clearance))
18 | }
19 | super.onPageStarted(view, url, favicon)
20 | }
21 | }
22 |
23 | companion object {
24 | fun newInstance(url: FileUrl) = CloudFlare(url)
25 | fun newInstance(url: String) = CloudFlare(FileUrl(url))
26 | }
27 | }
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FF5DAE
4 | #FF007F
5 | #40FF007F
6 | #C50053
7 | #91A6FF
8 | #3358FF
9 | #8091A6FF
10 | #000000
11 | #EEEEEE
12 | @color/bg_white
13 | @color/bg_black
14 | #A8000000
15 | #fff
16 | #00FFFFFF
17 | #40000000
18 | #19000000
19 | #54EEEEEE
20 | #A9FFFFFF
21 | #80FFFFFF
22 | #E63956
23 | #54FF8400
24 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_myanimelist.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/manga/MangaChapter.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.manga
2 |
3 | import ani.saikou.parsers.MangaChapter
4 | import ani.saikou.parsers.MangaImage
5 | import java.io.Serializable
6 | import kotlin.math.floor
7 |
8 | data class MangaChapter(
9 | val number: String,
10 | var link: String,
11 | var title: String? = null,
12 | var description: String? = null,
13 | ) : Serializable {
14 | constructor(chapter: MangaChapter) : this(chapter.number, chapter.link, chapter.title, chapter.description)
15 |
16 | private val images = mutableListOf()
17 | fun images(): List = images
18 | fun addImages(image: List) {
19 | if (images.isNotEmpty()) return
20 | image.forEach { images.add(it) }
21 | (0..floor((images.size.toFloat() - 1f) / 2).toInt()).forEach {
22 | val i = it * 2
23 | dualPages.add(images[i] to images.getOrNull(i + 1))
24 | }
25 | }
26 |
27 | private val dualPages = mutableListOf>()
28 | fun dualPages(): List> = dualPages
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/bottom_sheet_webview.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
21 |
22 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ui_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
17 |
18 | -
25 |
31 |
32 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/anilist/api/Recommendations.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.anilist.api
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 | @Serializable
6 | data class Recommendation(
7 | // The id of the recommendation
8 | @SerialName("id") var id: Int?,
9 |
10 | // Users rating of the recommendation
11 | @SerialName("rating") var rating: Int?,
12 |
13 | // The rating of the recommendation by currently authenticated user
14 | // @SerialName("userRating") var userRating: RecommendationRating?,
15 |
16 | // The media the recommendation is from
17 | @SerialName("media") var media: Media?,
18 |
19 | // The recommended media
20 | @SerialName("mediaRecommendation") var mediaRecommendation: Media?,
21 |
22 | // The user that first created the recommendation
23 | @SerialName("user") var user: User?,
24 | )
25 | @Serializable
26 | data class RecommendationConnection(
27 | //@SerialName("edges") var edges: List?,
28 |
29 | @SerialName("nodes") var nodes: List?,
30 |
31 | // The pagination information
32 | //@SerialName("pageInfo") var pageInfo: PageInfo?,
33 |
34 | )
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/parsers/MangaSources.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.parsers
2 |
3 | import ani.saikou.Lazier
4 | import ani.saikou.lazyList
5 | import ani.saikou.parsers.manga.*
6 |
7 | object MangaSources : MangaReadSources() {
8 | override val list: List> = lazyList(
9 | "MangaKakalot" to ::MangaKakalot,
10 | "MangaBuddy" to ::MangaBuddy,
11 | "MangaPill" to ::MangaPill,
12 | "MangaDex" to ::MangaDex,
13 | "MangaReaderTo" to ::MangaReaderTo,
14 | "AllAnime" to ::AllAnime,
15 | "Toonily" to ::Toonily,
16 | // "MangaHub" to ::MangaHub,
17 | "MangaKatana" to ::MangaKatana,
18 | "MangaKomi" to ::MangaKomi,
19 | "Manga4Life" to ::Manga4Life,
20 | "MangaRead" to ::MangaRead,
21 | "ComickFun" to ::ComickFun,
22 | "ColoredManga" to ::ColoredManga,
23 | )
24 | }
25 |
26 | object HMangaSources : MangaReadSources() {
27 | val aList: List> = lazyList(
28 | "NineHentai" to ::NineHentai,
29 | "Manhwa18" to ::Manhwa18,
30 | "NHentai" to ::NHentai,
31 | )
32 | override val list = listOf(aList,MangaSources.list).flatten()
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_title_text.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
18 |
19 |
30 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/LoginFragment.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import androidx.fragment.app.Fragment
8 | import ani.saikou.anilist.Anilist
9 | import ani.saikou.databinding.FragmentLoginBinding
10 |
11 | class LoginFragment : Fragment() {
12 |
13 | private var _binding: FragmentLoginBinding? = null
14 | private val binding get() = _binding!!
15 |
16 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
17 | _binding = FragmentLoginBinding.inflate(layoutInflater, container, false)
18 | return binding.root
19 | }
20 |
21 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
22 | binding.loginButton.setOnClickListener { Anilist.loginIntent(requireActivity()) }
23 | binding.loginDiscord.setOnClickListener { openLinkInBrowser(getString(R.string.discord)) }
24 | binding.loginTelegram.setOnClickListener { openLinkInBrowser(getString(R.string.telegram)) }
25 | binding.loginGithub.setOnClickListener { openLinkInBrowser(getString(R.string.github)) }
26 | }
27 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | android.defaults.buildfeatures.buildconfig=true
19 | android.nonTransitiveRClass=true
20 | android.nonFinalResIds=true
21 | org.gradle.unsafe.configuration-cache=true
22 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
6 |
8 |
11 |
14 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_settings_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_github.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_count_down.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
21 |
22 |
31 |
32 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/monochrome.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/bottom_sheet_subtitles.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
21 |
22 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_title_chipgroup.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
18 |
19 |
27 |
28 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/parsers/anime/extractors/FPlayer.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.parsers.anime.extractors
2 |
3 | import ani.saikou.asyncMap
4 | import ani.saikou.client
5 | import ani.saikou.getSize
6 | import ani.saikou.parsers.*
7 | import kotlinx.serialization.Serializable
8 |
9 | class FPlayer(override val server: VideoServer) : VideoExtractor() {
10 |
11 | override suspend fun extract(): VideoContainer {
12 | val url = server.embed.url
13 | val apiLink = url.replace("/v/", "/api/source/")
14 | try {
15 | val json = client.post(apiLink, referer = url).parsed()
16 | if (json.success) {
17 | return VideoContainer(json.data?.asyncMap {
18 | Video(
19 | it.label.replace("p", "").toIntOrNull(),
20 | VideoType.CONTAINER,
21 | it.file,
22 | getSize(it.file)
23 | )
24 | }?: listOf())
25 | }
26 |
27 | } catch (e: Exception) {}
28 | return VideoContainer(listOf())
29 | }
30 |
31 | @Serializable
32 | private data class Data(
33 | val file: String,
34 | val label: String
35 | )
36 |
37 | @Serializable
38 | private data class Json(
39 | val success: Boolean,
40 | val data: List?
41 | )
42 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_title_recycler.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
18 |
19 |
32 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/anilist/api/Studio.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.anilist.api
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class Studio(
8 | // The id of the studio
9 | @SerialName("id") var id: Int,
10 |
11 | // The name of the studio
12 | // Originally non-nullable, needs to be nullable due to it not being always queried
13 | @SerialName("name") var name: String?,
14 |
15 | // If the studio is an animation studio or a different kind of company
16 | @SerialName("isAnimationStudio") var isAnimationStudio: Boolean?,
17 |
18 | // The media the studio has worked on
19 | @SerialName("media") var media: MediaConnection?,
20 |
21 | // The url for the studio page on the AniList website
22 | @SerialName("siteUrl") var siteUrl: String?,
23 |
24 | // If the studio is marked as favourite by the currently authenticated user
25 | @SerialName("isFavourite") var isFavourite: Boolean?,
26 |
27 | // The amount of user's who have favourited the studio
28 | @SerialName("favourites") var favourites: Int?,
29 | )
30 |
31 | @Serializable
32 | data class StudioConnection(
33 | //@SerialName("edges") var edges: List?,
34 |
35 | @SerialName("nodes") var nodes: List?,
36 |
37 | // The pagination information
38 | //@SerialName("pageInfo") var pageInfo: PageInfo?,
39 | )
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/download/video/MyDownloadService.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.download.video
2 |
3 | import android.app.Notification
4 | import androidx.media3.common.util.UnstableApi
5 | import androidx.media3.exoplayer.offline.Download
6 | import androidx.media3.exoplayer.offline.DownloadManager
7 | import androidx.media3.exoplayer.offline.DownloadNotificationHelper
8 | import androidx.media3.exoplayer.offline.DownloadService
9 | import androidx.media3.exoplayer.scheduler.PlatformScheduler
10 | import androidx.media3.exoplayer.scheduler.Scheduler
11 | import ani.saikou.R
12 |
13 | @UnstableApi
14 | class MyDownloadService : DownloadService(1, 1, "download_service", R.string.downloads, 0) {
15 | companion object {
16 | private const val JOB_ID = 1
17 | private const val FOREGROUND_NOTIFICATION_ID = 1
18 | }
19 |
20 | override fun getDownloadManager(): DownloadManager = Helper.downloadManager(this)
21 |
22 | override fun getScheduler(): Scheduler = PlatformScheduler(this, JOB_ID)
23 |
24 | override fun getForegroundNotification(downloads: MutableList, notMetRequirements: Int): Notification =
25 | DownloadNotificationHelper(this, "download_service").buildProgressNotification(
26 | this,
27 | R.drawable.monochrome,
28 | null,
29 | null,
30 | downloads,
31 | notMetRequirements
32 | )
33 | }
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/others/GlideApp.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.others
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.Context
5 | import ani.saikou.okHttpClient
6 | import com.bumptech.glide.Glide
7 | import com.bumptech.glide.GlideBuilder
8 | import com.bumptech.glide.Registry
9 | import com.bumptech.glide.annotation.GlideModule
10 | import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader
11 | import com.bumptech.glide.load.engine.cache.InternalCacheDiskCacheFactory
12 | import com.bumptech.glide.load.model.GlideUrl
13 | import com.bumptech.glide.module.AppGlideModule
14 | import java.io.InputStream
15 |
16 |
17 | @GlideModule
18 | class SaikouGlideApp : AppGlideModule(){
19 | @SuppressLint("CheckResult")
20 | override fun applyOptions(context: Context, builder: GlideBuilder) {
21 | super.applyOptions(context, builder)
22 | val diskCacheSizeBytes = 1024 * 1024 * 100 // 100 MiB
23 | builder.apply {
24 | setDiskCache(InternalCacheDiskCacheFactory(context, "img", diskCacheSizeBytes.toLong()))
25 | }
26 | }
27 |
28 | override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
29 | registry.replace(
30 | GlideUrl::class.java,
31 | InputStream::class.java,
32 | OkHttpUrlLoader.Factory(okHttpClient)
33 | )
34 | super.registerComponents(context, glide, registry)
35 | }
36 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_character_details.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
15 |
16 |
27 |
28 |
36 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/settings/PlayerSettings.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.settings
2 |
3 | import java.io.Serializable
4 |
5 | data class PlayerSettings(
6 | //Video
7 | var videoInfo: Boolean = true,
8 | var defaultSpeed: Int = 5,
9 | var cursedSpeeds: Boolean = false,
10 | var resize: Int = 0,
11 |
12 | //Subtitles
13 | var subtitles: Boolean = true,
14 | var primaryColor: Int = 4,
15 | var secondaryColor: Int = 0,
16 | var outline: Int = 0,
17 | var subBackground: Int = 0,
18 | var subWindow: Int = 0,
19 | var font: Int = 0,
20 | var fontSize: Int = 20,
21 | var locale: Int = 2,
22 |
23 | //TimeStamps
24 | var timeStampsEnabled: Boolean = true,
25 | var useProxyForTimeStamps: Boolean = true,
26 | var showTimeStampButton: Boolean = true,
27 |
28 | //Auto
29 | var autoSkipOPED: Boolean = false,
30 | var autoPlay: Boolean = true,
31 | var autoSkipFiller: Boolean = false,
32 |
33 | //Update Progress
34 | var askIndividual: Boolean = true,
35 | var updateForH: Boolean = false,
36 | var watchPercentage: Float = 0.8f,
37 |
38 | //Behaviour
39 | var alwaysContinue: Boolean = true,
40 | var focusPause: Boolean = true,
41 | var gestures: Boolean = true,
42 | var doubleTap: Boolean = true,
43 | var seekTime: Int = 10,
44 | var skipTime: Int = 85,
45 |
46 | //Other
47 | var cast: Boolean = false,
48 | var pip: Boolean = true
49 | ) : Serializable
50 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_help_24.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_sort_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_telegram.xml:
--------------------------------------------------------------------------------
1 |
6 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/parsers/manga/MangaPill.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.parsers.manga
2 |
3 | import ani.saikou.client
4 | import ani.saikou.parsers.MangaChapter
5 | import ani.saikou.parsers.MangaImage
6 | import ani.saikou.parsers.MangaParser
7 | import ani.saikou.parsers.ShowResponse
8 |
9 | class MangaPill : MangaParser() {
10 |
11 | override val name = "MangaPill"
12 | override val saveName = "manga_pill"
13 | override val hostUrl = "https://mangapill.com"
14 |
15 | override suspend fun loadChapters(mangaLink: String, extra: Map?): List {
16 | return client.get(mangaLink).document.select("#chapters > div > a").reversed().map {
17 | val chap = it.text().replace("Chapter ", "")
18 | MangaChapter(chap, hostUrl + it.attr("href"))
19 | }
20 | }
21 |
22 | override suspend fun loadImages(chapterLink: String): List {
23 | return client.get(chapterLink).document.select("img.js-page").map {
24 | MangaImage(it.attr("data-src"))
25 | }
26 | }
27 |
28 | override suspend fun search(query: String): List {
29 | val link = "$hostUrl/quick-search?q=${encode(query)}"
30 | return client.get(link).document.select(".bg-card").map {
31 | ShowResponse(
32 | it.select(".font-black").text(),
33 | hostUrl + it.attr("href"),
34 | it.select("img").attr("src")
35 | )
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_slow_motion_video_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_genre.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
17 |
18 |
23 |
24 |
37 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.yml:
--------------------------------------------------------------------------------
1 | name: Feature Request
2 | description: Suggest a feature for Saikou
3 | labels: enhancement
4 | body:
5 |
6 | - type: markdown
7 | attributes:
8 | value: |
9 | Please make sure that you have read the entire README.
10 | Especially the [Planned Stuff](https://github.com/saikou-app/saikou#planned-stuff) and [Rejected Stuff](https://github.com/saikou-app/saikou#rejected-stuff).
11 |
12 | - type: input
13 | attributes:
14 | label: Summary
15 | description: A short summary of what your feature request is.
16 | validations:
17 | required: true
18 |
19 | - type: textarea
20 | attributes:
21 | label: The Problem
22 | description: >
23 | What problem is your feature trying to solve?
24 | What becomes easier or possible when this feature is implemented?
25 | validations:
26 | required: true
27 |
28 | - type: textarea
29 | attributes:
30 | label: The Ideal Solution
31 | description: >
32 | What is your ideal solution to the problem?
33 | What would you like this feature to do?
34 | validations:
35 | required: true
36 |
37 | - type: textarea
38 | attributes:
39 | label: The Current Solution
40 | description: What is the current solution to the problem, if any?
41 | validations:
42 | required: false
43 |
44 | - type: textarea
45 | attributes:
46 | label: Additional Context
47 | description: If there is anything else to say, please do so here.
48 |
49 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_reader_settings.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_round_video_settings_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/settings/DevelopersAdapter.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.settings
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.recyclerview.widget.RecyclerView
6 | import ani.saikou.databinding.ItemDeveloperBinding
7 | import ani.saikou.loadData
8 | import ani.saikou.loadImage
9 | import ani.saikou.openLinkInBrowser
10 | import ani.saikou.setAnimation
11 |
12 | class DevelopersAdapter(private val developers: Array) :
13 | RecyclerView.Adapter() {
14 | private val uiSettings = loadData("ui_settings") ?: UserInterfaceSettings()
15 |
16 | inner class DeveloperViewHolder(val binding: ItemDeveloperBinding) : RecyclerView.ViewHolder(binding.root) {
17 | init {
18 | itemView.setOnClickListener {
19 | openLinkInBrowser(developers[bindingAdapterPosition].url)
20 | }
21 | }
22 | }
23 |
24 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DeveloperViewHolder {
25 | return DeveloperViewHolder(ItemDeveloperBinding.inflate(LayoutInflater.from(parent.context), parent, false))
26 | }
27 |
28 | override fun onBindViewHolder(holder: DeveloperViewHolder, position: Int) {
29 | val b = holder.binding
30 | setAnimation(b.root.context, b.root, uiSettings)
31 | val dev = developers[position]
32 | b.devName.text = dev.name
33 | b.devProfile.loadImage(dev.pfp)
34 | b.devRole.text = dev.role
35 | }
36 |
37 | override fun getItemCount(): Int = developers.size
38 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_stream.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
23 |
24 |
28 |
29 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/bottom_sheet_developers.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
27 |
28 |
39 |
40 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_faq.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
24 |
25 |
39 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/subcriptions/SubscriptionWorker.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.subcriptions
2 |
3 | import android.content.Context
4 | import androidx.work.*
5 | import ani.saikou.loadData
6 | import ani.saikou.subcriptions.Subscription.Companion.defaultTime
7 | import ani.saikou.subcriptions.Subscription.Companion.timeMinutes
8 | import kotlinx.coroutines.Dispatchers
9 | import kotlinx.coroutines.withContext
10 | import java.util.concurrent.*
11 |
12 | class SubscriptionWorker(val context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {
13 |
14 | override suspend fun doWork(): Result {
15 | withContext(Dispatchers.IO){
16 | Subscription.perform(context)
17 | }
18 | return Result.success()
19 | }
20 |
21 | companion object {
22 |
23 | private const val SUBSCRIPTION_WORK_NAME = "work_subscription"
24 | fun enqueue(context: Context) {
25 | val curTime = loadData("subscriptions_time") ?: defaultTime
26 | if(timeMinutes[curTime]>0L) {
27 | val constraints = Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()
28 | val periodicSyncDataWork = PeriodicWorkRequest.Builder(
29 | SubscriptionWorker::class.java, 6, TimeUnit.HOURS
30 | ).apply {
31 | addTag(SUBSCRIPTION_WORK_NAME)
32 | setConstraints(constraints)
33 | }.build()
34 | WorkManager.getInstance(context).enqueueUniquePeriodicWork(
35 | SUBSCRIPTION_WORK_NAME, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE, periodicSyncDataWork
36 | )
37 | }
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/parsers/anime/extractors/VidStreaming.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.parsers.anime.extractors
2 |
3 | import ani.saikou.asyncMapNotNull
4 | import ani.saikou.client
5 | import ani.saikou.parsers.VideoContainer
6 | import ani.saikou.parsers.VideoExtractor
7 | import ani.saikou.parsers.VideoServer
8 |
9 | class VidStreaming(override val server: VideoServer) : VideoExtractor() {
10 | override suspend fun extract(): VideoContainer {
11 | // `streaming.php` is the most likely one to be replaced
12 | // however sometimes the source will be using `embed` or `load`
13 | val url =
14 | server.embed.url
15 | .replace("streaming.php?", "loadserver.php?")
16 | .replace("embed.php", "loadserver.php")
17 | .replace("load.php", "loadserver.php")
18 |
19 | val res = client
20 | .get(url, mapOf("Referer" to "https://goload.one"))
21 | .document.select("ul.list-server-items > li.linkserver")
22 |
23 | return VideoContainer(
24 | res.asyncMapNotNull { server ->
25 | val src = server.attr("data-video") // link to the source
26 | val type = server.text().lowercase() // e.g streamsb, vidstreaming, multi quality
27 |
28 | // Using a when makes it easier to expand upon later
29 | return@asyncMapNotNull when (type) {
30 | "streamsb" -> StreamSB(VideoServer(name = "StreamSB", embedUrl = src)).extract().videos
31 | "xstreamcdn" -> FPlayer(VideoServer(name = "XStreamCDN", embedUrl = src)).extract().videos
32 | else -> null
33 | }
34 | }.flatten()
35 | )
36 | }
37 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_anime_watch.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
18 |
19 |
31 |
32 |
42 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_genre.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
21 |
22 |
35 |
36 |
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_image.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
16 |
17 |
27 |
28 |
35 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/.github/workflows/latest.yml:
--------------------------------------------------------------------------------
1 | name: Android CI Latest
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 |
7 | jobs:
8 | build:
9 | runs-on: ubuntu-latest
10 |
11 | steps:
12 | - uses: actions/checkout@v3
13 |
14 | - name: Set variables
15 | run: |
16 | VER=$(cat beta.md | head -n1 | sed -e "s/# //")
17 | echo "VERSION=$VER" >> $GITHUB_ENV
18 | - name: set up JDK 17
19 | uses: actions/setup-java@v3
20 | with:
21 | java-version: '17'
22 | distribution: 'temurin'
23 | cache: gradle
24 |
25 | - name: Cook KeyStore
26 | run: echo "${{ secrets.KEYSTORE_B64 }}" | base64 -d > $GITHUB_WORKSPACE/signing-key.jks
27 |
28 | - name: Enable Reports
29 | run: sed "s/DisabledReports=true/DisabledReports=false/g" -i app/src/main/java/ani/saikou/others/DisabledReports.kt
30 |
31 | - name: Grant execute permission for gradlew
32 | run: chmod +x gradlew
33 | - name: Build with Gradle
34 | run: ./gradlew assembleDebug
35 | -Pandroid.injected.signing.store.file=$GITHUB_WORKSPACE/signing-key.jks
36 | -Pandroid.injected.signing.store.password=${{ secrets.PASSWORD }}
37 | -Pandroid.injected.signing.key.alias=key0
38 | -Pandroid.injected.signing.key.password=${{ secrets.PASSWORD }}
39 | shell: bash
40 |
41 | - name: Upload a Build Artifact
42 | uses: actions/upload-artifact@v3.0.0
43 | with:
44 | path: "app/build/outputs/apk/debug/app-debug.apk"
45 |
46 | - name: Upload APK to Discord
47 | shell: bash
48 | run: |
49 | curl -F 'payload_json={"username": "Saikou Debug Builds", "avatar_url": "https://media.discordapp.net/attachments/927944825016311858/1046676593252253726/saikou.png"}' -F "saikou_debug=@app/build/outputs/apk/debug/app-debug.apk" ${{ secrets.DISCORD_WEBHOOK_URL }}
50 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/others/MalSyncBackup.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.others
2 |
3 | import ani.saikou.client
4 | import ani.saikou.parsers.ShowResponse
5 | import ani.saikou.tryWithSuspend
6 | import kotlinx.serialization.SerialName
7 | import kotlinx.serialization.Serializable
8 |
9 | object MalSyncBackup {
10 | @Serializable
11 | data class MalBackUpSync(
12 | @SerialName("Pages") val pages: Map>? = null
13 | )
14 |
15 | @Serializable
16 | data class Page(
17 | val identifier: String,
18 | val title: String,
19 | val url: String? = null,
20 | val image: String? = null,
21 | val active: Boolean? = null,
22 | )
23 |
24 | suspend fun get(id: Int, name: String, dub: Boolean = false): ShowResponse? {
25 | return tryWithSuspend {
26 | val json =
27 | client.get("https://raw.githubusercontent.com/MALSync/MAL-Sync-Backup/master/data/anilist/anime/$id.json")
28 | if (json.text != "404: Not Found")
29 | json.parsed().pages?.get(name)?.forEach {
30 | val page = it.value
31 | val isDub = page.title.lowercase().replace(" ", "").endsWith("(dub)")
32 | val slug = if (dub == isDub) page.identifier else null
33 | if (slug != null && page.active == true && page.url != null) {
34 | val url = when(name){
35 | "Gogoanime" -> slug
36 | "Tenshi" -> slug
37 | else -> page.url
38 | }
39 | return@tryWithSuspend ShowResponse(page.title, url, page.image ?: "")
40 | }
41 | }
42 | return@tryWithSuspend null
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/media/CharacterDetailsAdapter.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.media
2 |
3 | import android.annotation.SuppressLint
4 | import android.app.Activity
5 | import android.view.LayoutInflater
6 | import android.view.ViewGroup
7 | import androidx.recyclerview.widget.RecyclerView
8 | import ani.saikou.databinding.ItemCharacterDetailsBinding
9 | import ani.saikou.others.SpoilerPlugin
10 | import io.noties.markwon.Markwon
11 | import io.noties.markwon.SoftBreakAddsNewLinePlugin
12 |
13 | class CharacterDetailsAdapter(private val character: Character, private val activity: Activity) :
14 | RecyclerView.Adapter() {
15 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GenreViewHolder {
16 | val binding = ItemCharacterDetailsBinding.inflate(LayoutInflater.from(parent.context), parent, false)
17 | return GenreViewHolder(binding)
18 | }
19 |
20 | @SuppressLint("SetTextI18n")
21 | override fun onBindViewHolder(holder: GenreViewHolder, position: Int) {
22 | val binding = holder.binding
23 | val desc =
24 | (if (character.age != "null") "__Age:__ " + character.age else "") +
25 | (if (character.dateOfBirth.toString() != "") "\n__Birthday:__ " + character.dateOfBirth.toString() else "") +
26 | (if (character.gender != "null") "\n__Gender:__ " + character.gender else "") + "\n" + character.description
27 |
28 | binding.characterDesc.isTextSelectable
29 | val markWon = Markwon.builder(activity).usePlugin(SoftBreakAddsNewLinePlugin.create()).usePlugin(SpoilerPlugin()).build()
30 | markWon.setMarkdown(binding.characterDesc, desc)
31 |
32 | }
33 |
34 | override fun getItemCount(): Int = 1
35 | inner class GenreViewHolder(val binding: ItemCharacterDetailsBinding) : RecyclerView.ViewHolder(binding.root)
36 | }
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/others/AniSkip.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.others
2 |
3 | import ani.saikou.client
4 | import ani.saikou.tryWithSuspend
5 | import kotlinx.serialization.Serializable
6 | import java.net.URLEncoder
7 |
8 | object AniSkip {
9 |
10 | @Suppress("BlockingMethodInNonBlockingContext")
11 | suspend fun getResult(malId: Int, episodeNumber: Int, episodeLength: Long, useProxyForTimeStamps: Boolean): List? {
12 | val url =
13 | "https://api.aniskip.com/v2/skip-times/$malId/$episodeNumber?types[]=ed&types[]=mixed-ed&types[]=mixed-op&types[]=op&types[]=recap&episodeLength=$episodeLength"
14 | return tryWithSuspend {
15 | val a = if(useProxyForTimeStamps)
16 | client.get("https://corsproxy.io/?${URLEncoder.encode(url, "utf-8").replace("+", "%20")}")
17 | else
18 | client.get(url)
19 | val res = a.parsed()
20 | if (res.found) res.results else null
21 | }
22 | }
23 |
24 | @Serializable
25 | data class AniSkipResponse(
26 | val found: Boolean,
27 | val results: List?,
28 | val message: String?,
29 | val statusCode: Int
30 | )
31 |
32 | @Serializable
33 | data class Stamp(
34 | val interval: AniSkipInterval,
35 | val skipType: String,
36 | val skipId: String,
37 | val episodeLength: Double
38 | )
39 |
40 |
41 | fun String.getType(): String {
42 | return when (this) {
43 | "op" -> "Opening"
44 | "ed" -> "Ending"
45 | "recap" -> "Recap"
46 | "mixed-ed" -> "Mixed Ending"
47 | "mixed-op" -> "Mixed Opening"
48 | else -> this
49 | }
50 | }
51 |
52 | @Serializable
53 | data class AniSkipInterval(
54 | val startTime: Double,
55 | val endTime: Double
56 | )
57 | }
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/others/Jikan.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.others
2 |
3 | import ani.saikou.anime.Episode
4 | import ani.saikou.client
5 | import ani.saikou.tryWithSuspend
6 | import kotlinx.serialization.SerialName
7 | import kotlinx.serialization.Serializable
8 |
9 | object Jikan {
10 |
11 | const val apiUrl = "https://api.jikan.moe/v4/"
12 |
13 | suspend inline fun query(endpoint: String): T? {
14 | return tryWithSuspend { client.get("$apiUrl$endpoint").parsed() }
15 | }
16 |
17 | suspend fun getEpisodes(malId: Int): Map {
18 | var hasNextPage = true
19 | var page = 0
20 | val eps = mutableMapOf()
21 | while (hasNextPage) {
22 | page++
23 | val res = query("anime/$malId/episodes?page=$page")
24 | res?.data?.forEach {
25 | val ep = it.malID.toString()
26 | eps[ep] = Episode(ep, title = it.title,
27 | //Personal revenge with 34566 :prayge:
28 | filler = if(malId!=34566) it.filler else true
29 | )
30 | }
31 | hasNextPage = res?.pagination?.hasNextPage == true
32 | }
33 | return eps
34 | }
35 |
36 | @Serializable
37 | data class EpisodeResponse(
38 | val pagination: Pagination? = null,
39 | val data: List? = null
40 | ) {
41 | @Serializable
42 | data class Datum(
43 | @SerialName("mal_id")
44 | val malID: Int,
45 | val title: String? = null,
46 | val filler: Boolean,
47 | // val recap: Boolean,
48 | )
49 |
50 | @Serializable
51 | data class Pagination(
52 | @SerialName("has_next_page")
53 | val hasNextPage: Boolean? = null
54 | )
55 | }
56 |
57 | }
58 |
59 |
60 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/parsers/manga/MangaKatana.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.parsers.manga
2 |
3 | import ani.saikou.client
4 | import ani.saikou.findBetween
5 | import ani.saikou.parsers.MangaChapter
6 | import ani.saikou.parsers.MangaImage
7 | import ani.saikou.parsers.MangaParser
8 | import ani.saikou.parsers.ShowResponse
9 |
10 | class MangaKatana : MangaParser() {
11 |
12 | override val name = "MangaKatana"
13 | override val saveName = "manga_katana"
14 | override val hostUrl = "https://mangakatana.com"
15 |
16 | override suspend fun search(query: String): List {
17 | val doc = client.get("$hostUrl/?search=${encode(query)}").document
18 | val mediaData = doc.select("#book_list div.media div.wrap_img a")
19 | val titleData = doc.select("#book_list div.text h3 a")
20 | val data = titleData.zip(mediaData)
21 | return data.map {
22 | val name = it.first.text()
23 | val link = it.second.attr("href")
24 | val cover = it.second.select("img").attr("src")
25 | ShowResponse(name= name, link = link, coverUrl = cover)
26 | }
27 | }
28 |
29 | override suspend fun loadChapters(mangaLink: String, extra: Map?): List {
30 | val doc = client.get(mangaLink).document
31 | val data = doc.select("tbody tr a")
32 | val chapNumRe = Regex("([0-9]+(?:\\.[0-9]+)?)")
33 | return data.reversed().map {
34 | MangaChapter(number = chapNumRe.find(it.text())?.groupValues?.get(1).toString(), link = it.attr("href"))
35 | }
36 | }
37 |
38 | override suspend fun loadImages(chapterLink: String): List {
39 | val match = client.get(chapterLink).text.findBetween("=[",",];f")!!
40 | return match.split(",").map {
41 | MangaImage(url = it.replace("\"", "").replace("'", ""))
42 | }
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/parsers/manga/MangaKomi.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.parsers.manga
2 |
3 | import ani.saikou.client
4 | import ani.saikou.parsers.MangaChapter
5 | import ani.saikou.parsers.MangaImage
6 | import ani.saikou.parsers.MangaParser
7 | import ani.saikou.parsers.ShowResponse
8 |
9 |
10 |
11 | class MangaKomi : MangaParser() {
12 |
13 | override val name = "MangaKomi"
14 | override val saveName = "manga_komi"
15 | override val hostUrl = "https://mangakomi.io"
16 |
17 | override suspend fun search(query: String): List {
18 | val doc = client.get("$hostUrl/?s=${encode(query)}&post_type=wp-manga").document
19 | val mediaData = doc.select("h3.h4 a")
20 | val titleData = doc.select("div.tab-thumb.c-image-hover a img")
21 | val data = titleData.zip(mediaData)
22 | return data.map {
23 | val name = it.second.text()
24 | val link = it.second.attr("href")
25 | val cover = it.first.select("img").attr("data-src")
26 | ShowResponse(name= name, link = link, coverUrl = cover)
27 | }
28 | }
29 |
30 | override suspend fun loadChapters(mangaLink: String, extra: Map?): List {
31 | val doc = client.get(mangaLink).document
32 | val data = doc.select("li.wp-manga-chapter a")
33 | val chapNumRe = Regex("([0-9]+(?:\\.[0-9]+)?)")
34 | return data.reversed().mapNotNull {
35 | val number = chapNumRe.find(it.text())?.groups?.get(1)?.value ?: return@mapNotNull null
36 | MangaChapter(number, it.attr("href"))
37 | }
38 | }
39 |
40 | override suspend fun loadImages(chapterLink: String): List {
41 | val doc = client.get(chapterLink).document
42 | val imgs = doc.select("div.reading-content > div > img")
43 | return imgs.map {
44 | MangaImage(url = it.attr("data-src").trim())
45 | }
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_title_trailer.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
19 |
20 |
23 |
24 |
37 |
38 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/parsers/manga/MangaRead.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.parsers.manga
2 |
3 | import ani.saikou.client
4 | import ani.saikou.parsers.MangaChapter
5 | import ani.saikou.parsers.MangaImage
6 | import ani.saikou.parsers.MangaParser
7 | import ani.saikou.parsers.ShowResponse
8 |
9 | class MangaRead: MangaParser() {
10 |
11 | override val name = "MangaRead"
12 | override val saveName = "manga_read"
13 | override val hostUrl = "https://mangaread.org"
14 |
15 | override suspend fun search(query: String): List {
16 | val doc = client.get("$hostUrl/?s=${encode(query)}&post_type=wp-manga").document
17 | val data = doc.select("div.c-tabs-item > div > div > div > a")
18 | val imgs = data.select("img")
19 | return data.zip(imgs).map {
20 | ShowResponse(
21 | name = it.first.attr("title"),
22 | link = it.first.attr("href"),
23 | coverUrl = it.second.attr("data-src")
24 | )
25 | }
26 | }
27 |
28 | override suspend fun loadChapters(mangaLink: String, extra: Map?): List {
29 | val doc = client.get(mangaLink).document
30 | val chapters = doc.select("div.page-content-listing.single-page > div > ul > li > a").reversed()
31 | val chapRegex = Regex("Chapter (\\d+)")
32 | return chapters.mapIndexed { _, chapter ->
33 | MangaChapter(
34 | number = chapRegex.find(chapter.text())?.groupValues?.get(1).toString(),
35 | link = chapter.attr("href"),
36 | title = chapter.text()
37 | )
38 | }
39 | }
40 |
41 | override suspend fun loadImages(chapterLink: String): List {
42 | val doc = client.get(chapterLink).document
43 | val imgs = doc.select("div.reading-content > div > img")
44 | return imgs.map {
45 | MangaImage(url = it.attr("data-src").trim())
46 | }
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/settings/ForksDialogFragment.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.settings
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import androidx.recyclerview.widget.LinearLayoutManager
8 | import ani.saikou.BottomSheetDialogFragment
9 | import ani.saikou.R
10 | import ani.saikou.databinding.BottomSheetDevelopersBinding
11 |
12 | class ForksDialogFragment : BottomSheetDialogFragment() {
13 | private var _binding: BottomSheetDevelopersBinding? = null
14 | private val binding get() = _binding!!
15 |
16 | private val developers = arrayOf(
17 | Developer("Saikou TV","https://avatars.githubusercontent.com/u/32782575?s=120&v=4","Nanoc6","https://github.com/Nanoc6/SaikouTV"),
18 | Developer("Saikou SP (Spanish)","https://avatars.githubusercontent.com/u/80992641?s=120&v=4",".DiegoPYL","https://github.com/Diegopyl1209/saikouSP"),
19 | Developer("Saikou IT (Italiano)","https://avatars.githubusercontent.com/u/38143733?s=120&v=4","AntonyDP","https://github.com/antonydp/saikou-italiano"),
20 | Developer("Saikou VN (Vietnamese)","https://avatars.githubusercontent.com/u/68330291?s=120&v=4","Vu Nguyen","https://github.com/hoangvu12/saikou")
21 | )
22 |
23 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
24 | _binding = BottomSheetDevelopersBinding.inflate(inflater, container, false)
25 | return binding.root
26 | }
27 |
28 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
29 | super.onViewCreated(view, savedInstanceState)
30 | binding.devsTitle.setText(R.string.forks)
31 | binding.devsRecyclerView.adapter = DevelopersAdapter(developers)
32 | binding.devsRecyclerView.layoutManager = LinearLayoutManager(requireContext())
33 | }
34 |
35 | override fun onDestroy() {
36 | _binding = null
37 | super.onDestroy()
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/anilist/api/FuzzyDate.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.anilist.api
2 |
3 | import kotlinx.serialization.SerialName
4 | import java.io.Serializable
5 | import java.text.DateFormatSymbols
6 | import java.util.*
7 |
8 | @kotlinx.serialization.Serializable
9 | data class FuzzyDate(
10 | @SerialName("year") val year: Int? = null,
11 | @SerialName("month") val month: Int? = null,
12 | @SerialName("day") val day: Int? = null,
13 | ) : Serializable, Comparable {
14 | override fun toString(): String {
15 | if (day == null && year == null && month == null)
16 | return "??"
17 | val a = if (month != null) DateFormatSymbols().months[month - 1] else ""
18 | return (if (day != null) "$day " else "") + a + (if (year != null) ", $year" else "")
19 | }
20 |
21 | fun getToday(): FuzzyDate {
22 | val cal = Calendar.getInstance()
23 | return FuzzyDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DAY_OF_MONTH))
24 | }
25 |
26 | fun toVariableString(): String {
27 | return ("{"
28 | + (if (year != null) "year:$year" else "")
29 | + (if (month != null) ",month:$month" else "")
30 | + (if (day != null) ",day:$day" else "")
31 | + "}")
32 | }
33 |
34 | fun toISOString(): String {
35 | return "${
36 | year.toString().padStart(4, '0')
37 | }-${
38 | month.toString().padStart(2, '0')
39 | }-${
40 | day.toString().padStart(2, '0')
41 | }"
42 | }
43 |
44 | // fun toInt(): Int {
45 | // return 10000 * (this.year ?: 0) + 100 * (this.month ?: 0) + (this.day ?: 0)
46 | // }
47 |
48 | override fun compareTo(other: FuzzyDate): Int = when {
49 | year != other.year -> (year ?: 0) - (other.year ?: 0)
50 | month != other.month -> (month ?: 0) - (other.month ?: 0)
51 | else -> (day ?: 0) - (other.day ?: 0)
52 | }
53 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_anime.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
24 |
25 |
26 |
35 |
36 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_manga.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
24 |
25 |
26 |
35 |
36 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/app/src/main/java/ani/saikou/media/OtherDetailsViewModel.kt:
--------------------------------------------------------------------------------
1 | package ani.saikou.media
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.MutableLiveData
5 | import androidx.lifecycle.ViewModel
6 | import ani.saikou.anilist.Anilist
7 | import java.text.DateFormat
8 | import java.util.*
9 |
10 | class OtherDetailsViewModel : ViewModel() {
11 | private val character: MutableLiveData = MutableLiveData(null)
12 | fun getCharacter(): LiveData = character
13 | suspend fun loadCharacter(m: Character) {
14 | if (character.value == null) character.postValue(Anilist.query.getCharacterDetails(m))
15 | }
16 |
17 | private val studio: MutableLiveData = MutableLiveData(null)
18 | fun getStudio(): LiveData = studio
19 | suspend fun loadStudio(m: Studio) {
20 | if (studio.value == null) studio.postValue(Anilist.query.getStudioDetails(m))
21 | }
22 |
23 | private val calendar: MutableLiveData