├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ └── feature_request.yml └── workflows │ ├── beta.yml │ ├── pull-req.yml │ ├── signing-key.jks │ └── stable.yml ├── .gitignore ├── LICENSE.md ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── debug │ └── res │ │ └── values │ │ └── strings.xml │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── ani │ │ └── saikou │ │ ├── App.kt │ │ ├── Functions.kt │ │ ├── MainActivity.kt │ │ ├── Network.kt │ │ ├── connections │ │ ├── UpdateProgress.kt │ │ ├── anilist │ │ │ ├── Anilist.kt │ │ │ ├── AnilistMutations.kt │ │ │ ├── AnilistQueries.kt │ │ │ ├── AnilistViewModel.kt │ │ │ ├── BannerImage.kt │ │ │ ├── Genre.kt │ │ │ ├── Login.kt │ │ │ ├── SearchResults.kt │ │ │ ├── UrlMedia.kt │ │ │ └── api │ │ │ │ ├── Character.kt │ │ │ │ ├── Data.kt │ │ │ │ ├── FuzzyDate.kt │ │ │ │ ├── Media.kt │ │ │ │ ├── Page.kt │ │ │ │ ├── Recommendations.kt │ │ │ │ ├── Staff.kt │ │ │ │ ├── Studio.kt │ │ │ │ └── User.kt │ │ ├── discord │ │ │ ├── Discord.kt │ │ │ ├── Login.kt │ │ │ ├── RPC.kt │ │ │ └── serializers │ │ │ │ ├── Activity.kt │ │ │ │ ├── Identity.kt │ │ │ │ ├── Presence.kt │ │ │ │ ├── Res.kt │ │ │ │ └── User.kt │ │ └── mal │ │ │ ├── Login.kt │ │ │ ├── MAL.kt │ │ │ └── MALQueries.kt │ │ ├── download │ │ └── video │ │ │ ├── Helper.kt │ │ │ └── MyDownloadService.kt │ │ ├── home │ │ ├── AnimeFragment.kt │ │ ├── AnimePageAdapter.kt │ │ ├── HomeFragment.kt │ │ ├── LoginFragment.kt │ │ ├── MangaFragment.kt │ │ ├── MangaPageAdapter.kt │ │ └── NoInternet.kt │ │ ├── media │ │ ├── Author.kt │ │ ├── AuthorActivity.kt │ │ ├── CalendarActivity.kt │ │ ├── Character.kt │ │ ├── CharacterAdapter.kt │ │ ├── CharacterDetailsActivity.kt │ │ ├── CharacterDetailsAdapter.kt │ │ ├── GenreActivity.kt │ │ ├── GenreAdapter.kt │ │ ├── Media.kt │ │ ├── MediaAdaptor.kt │ │ ├── MediaDetailsActivity.kt │ │ ├── MediaDetailsViewModel.kt │ │ ├── MediaInfoFragment.kt │ │ ├── MediaListDialogFragment.kt │ │ ├── MediaListDialogSmallFragment.kt │ │ ├── OtherDetailsViewModel.kt │ │ ├── ProgressAdapter.kt │ │ ├── SearchActivity.kt │ │ ├── SearchAdapter.kt │ │ ├── SearchFilterBottomDialog.kt │ │ ├── Selected.kt │ │ ├── Source.kt │ │ ├── SourceAdapter.kt │ │ ├── SourceSearchDialogFragment.kt │ │ ├── Studio.kt │ │ ├── StudioActivity.kt │ │ ├── TitleAdapter.kt │ │ ├── anime │ │ │ ├── Anime.kt │ │ │ ├── AnimeSourceAdapter.kt │ │ │ ├── AnimeWatchAdapter.kt │ │ │ ├── AnimeWatchFragment.kt │ │ │ ├── Episode.kt │ │ │ ├── EpisodeAdapters.kt │ │ │ ├── ExoplayerView.kt │ │ │ ├── SelectorDialogFragment.kt │ │ │ ├── SubtitleDialogFragment.kt │ │ │ └── VideoCache.kt │ │ ├── manga │ │ │ ├── Manga.kt │ │ │ ├── MangaChapter.kt │ │ │ ├── MangaChapterAdapter.kt │ │ │ ├── MangaReadAdapter.kt │ │ │ ├── MangaReadFragment.kt │ │ │ ├── MangaSourceAdapter.kt │ │ │ └── mangareader │ │ │ │ ├── BaseImageAdapter.kt │ │ │ │ ├── ChapterLoaderDialog.kt │ │ │ │ ├── DualPageAdapter.kt │ │ │ │ ├── ImageAdapter.kt │ │ │ │ ├── MangaReaderActivity.kt │ │ │ │ ├── PreloadLinearLayoutManager.kt │ │ │ │ ├── ReaderSettingsDialogFragment.kt │ │ │ │ ├── RemoveBordersTransformation.kt │ │ │ │ └── Swipy.kt │ │ ├── novel │ │ │ ├── BookDialog.kt │ │ │ ├── NovelReadAdapter.kt │ │ │ ├── NovelReadFragment.kt │ │ │ ├── NovelResponseAdapter.kt │ │ │ ├── UrlAdapter.kt │ │ │ └── novelreader │ │ │ │ ├── NovelReaderActivity.kt │ │ │ │ └── NovelReaderSettingsDialogFragment.kt │ │ └── user │ │ │ ├── ListActivity.kt │ │ │ ├── ListFragment.kt │ │ │ ├── ListViewModel.kt │ │ │ └── ListViewPagerAdapter.kt │ │ ├── others │ │ ├── AniSkip.kt │ │ ├── AppUpdater.kt │ │ ├── CustomBottomDialog.kt │ │ ├── Download.kt │ │ ├── GlideApp.kt │ │ ├── Idiosyncrasy.kt │ │ ├── ImageViewDialog.kt │ │ ├── Jikan.kt │ │ ├── JsUnpacker.kt │ │ ├── Kitsu.kt │ │ ├── MalScraper.kt │ │ ├── MalSyncBackup.kt │ │ ├── OutlineTextView.kt │ │ ├── ResettableTimer.kt │ │ ├── SpoilerPlugin.kt │ │ ├── Xpandable.kt │ │ ├── imagesearch │ │ │ ├── ImageSearchActivity.kt │ │ │ ├── ImageSearchResultAdapter.kt │ │ │ └── ImageSearchViewModel.kt │ │ └── webview │ │ │ ├── CloudFlare.kt │ │ │ └── WebViewBottomDialog.kt │ │ ├── parsers │ │ ├── BaseParser.kt │ │ ├── BaseSources.kt │ │ ├── anime │ │ │ ├── AnimeExtractors.kt │ │ │ ├── AnimeParser.kt │ │ │ ├── AnimeSources.kt │ │ │ ├── VideoExtractor.kt │ │ │ ├── extractors │ │ │ │ ├── ALions.kt │ │ │ │ ├── AWish.kt │ │ │ │ ├── Animeflix.kt │ │ │ │ ├── BetaPlayer.kt │ │ │ │ ├── DoodStream.kt │ │ │ │ ├── FPlayer.kt │ │ │ │ ├── FileMoon.kt │ │ │ │ ├── GogoCDN.kt │ │ │ │ ├── KickAssAnimeV2.kt │ │ │ │ ├── Maverickki.kt │ │ │ │ ├── MixDrop.kt │ │ │ │ ├── Mp4Upload.kt │ │ │ │ ├── OkRu.kt │ │ │ │ ├── PinkBird.kt │ │ │ │ ├── RapidCloud.kt │ │ │ │ ├── StreamSB.kt │ │ │ │ ├── StreamTape.kt │ │ │ │ └── VidStreaming.kt │ │ │ └── sources │ │ │ │ ├── AllAnime.kt │ │ │ │ ├── AniWave.kt │ │ │ │ ├── AnimeDao.kt │ │ │ │ ├── AnimeDummy.kt │ │ │ │ ├── AnimePahe.kt │ │ │ │ ├── Animeflix.kt │ │ │ │ ├── Animension.kt │ │ │ │ ├── Gogo.kt │ │ │ │ ├── Haho.kt │ │ │ │ ├── HentaiFF.kt │ │ │ │ ├── HentaiMama.kt │ │ │ │ ├── HentaiStream.kt │ │ │ │ ├── Kaido.kt │ │ │ │ ├── KickAssAnime.kt │ │ │ │ └── SFlix.kt │ │ ├── manga │ │ │ ├── MangaParser.kt │ │ │ ├── MangaSources.kt │ │ │ └── sources │ │ │ │ ├── AllAnime.kt │ │ │ │ ├── AsurScans.kt │ │ │ │ ├── ComickFun.kt │ │ │ │ ├── Manga4Life.kt │ │ │ │ ├── MangaBuddy.kt │ │ │ │ ├── MangaDex.kt │ │ │ │ ├── MangaDummy.kt │ │ │ │ ├── MangaHub.kt │ │ │ │ ├── MangaKakalot.kt │ │ │ │ ├── MangaKatana.kt │ │ │ │ ├── MangaPill.kt │ │ │ │ ├── MangaRead.kt │ │ │ │ ├── Manhwa18.kt │ │ │ │ ├── MmScans.kt │ │ │ │ ├── NineHentai.kt │ │ │ │ └── Toonily.kt │ │ └── novel │ │ │ ├── NovelParser.kt │ │ │ ├── NovelSources.kt │ │ │ └── sources │ │ │ └── NovelDummy.kt │ │ ├── settings │ │ ├── CurrentNovelReaderSettings.kt │ │ ├── CurrentReaderSettings.kt │ │ ├── Developer.kt │ │ ├── DevelopersAdapter.kt │ │ ├── DevelopersDialogFragment.kt │ │ ├── FAQActivity.kt │ │ ├── FAQAdapter.kt │ │ ├── ForksDialogFragment.kt │ │ ├── NovelReaderSettings.kt │ │ ├── PlayerSettings.kt │ │ ├── PlayerSettingsActivity.kt │ │ ├── ReaderSettings.kt │ │ ├── ReaderSettingsActivity.kt │ │ ├── SettingsActivity.kt │ │ ├── SettingsDialogFragment.kt │ │ ├── UserInterfaceSettings.kt │ │ └── UserInterfaceSettingsActivity.kt │ │ └── subcriptions │ │ ├── AlarmReceiver.kt │ │ ├── Notifications.kt │ │ ├── Subscription.kt │ │ ├── SubscriptionHelper.kt │ │ └── SubscriptionWorker.kt │ └── res │ ├── anim │ └── over_shoot.xml │ ├── color │ ├── button_switch_track.xml │ ├── tab_layout_icon.xml │ └── tab_layout_text.xml │ ├── drawable │ ├── anim_pause_to_play.xml │ ├── anim_play_to_pause.xml │ ├── anim_rewind.xml │ ├── anim_skip.xml │ ├── anim_splash.xml │ ├── baseline_invert_colors_24.xml │ ├── bottom_nav.xml │ ├── buymecofee.png │ ├── control_background_40dp.xml │ ├── ic_add.xml │ ├── ic_anilist.xml │ ├── ic_baseline_screen_lock_portrait_24.xml │ ├── ic_discord.xml │ ├── ic_github.xml │ ├── ic_launcher_background.xml │ ├── ic_launcher_foreground.xml │ ├── ic_minus.xml │ ├── ic_myanimelist.xml │ ├── ic_page_numbering.xml │ ├── ic_round_accessible_forward_24.xml │ ├── ic_round_add_circle_24.xml │ ├── ic_round_add_circle_outline_24.xml │ ├── ic_round_alpha_t_box_24.xml │ ├── ic_round_amp_stories_24.xml │ ├── ic_round_animation_24.xml │ ├── ic_round_arrow_back_ios_new_24.xml │ ├── ic_round_arrow_drop_down_24.xml │ ├── ic_round_art_track_24.xml │ ├── ic_round_audiotrack_24.xml │ ├── ic_round_auto_awesome_24.xml │ ├── ic_round_brightness_4_24.xml │ ├── ic_round_brightness_auto_24.xml │ ├── ic_round_brightness_high_24.xml │ ├── ic_round_brightness_medium_24.xml │ ├── ic_round_calendar_today_24.xml │ ├── ic_round_cast_24.xml │ ├── ic_round_close_24.xml │ ├── ic_round_collections_bookmark_24.xml │ ├── ic_round_color_24.xml │ ├── ic_round_date_range_24.xml │ ├── ic_round_dns_24.xml │ ├── ic_round_download_24.xml │ ├── ic_round_edit_note_24.xml │ ├── ic_round_fast_forward_24.xml │ ├── ic_round_fast_rewind_24.xml │ ├── ic_round_favorite_24.xml │ ├── ic_round_favorite_border_24.xml │ ├── ic_round_filter_alt_24.xml │ ├── ic_round_font_size_24.xml │ ├── ic_round_format_text_24.xml │ ├── ic_round_fullscreen_24.xml │ ├── ic_round_grid_view_24.xml │ ├── ic_round_heart_broken_24.xml │ ├── ic_round_help_24.xml │ ├── ic_round_high_quality_24.xml │ ├── ic_round_home_24.xml │ ├── ic_round_image_search_24.xml │ ├── ic_round_import_contacts_24.xml │ ├── ic_round_info_24.xml │ ├── ic_round_lock_24.xml │ ├── ic_round_lock_open_24.xml │ ├── ic_round_menu_book_24.xml │ ├── ic_round_movie_filter_24.xml │ ├── ic_round_new_releases_24.xml │ ├── ic_round_notifications_active_24.xml │ ├── ic_round_notifications_none_24.xml │ ├── ic_round_pause_24.xml │ ├── ic_round_person_24.xml │ ├── ic_round_photo_size_select_actual_24.xml │ ├── ic_round_picture_in_picture_alt_24.xml │ ├── ic_round_play_arrow_24.xml │ ├── ic_round_play_circle_24.xml │ ├── ic_round_play_disabled_24.xml │ ├── ic_round_playlist_add_24.xml │ ├── ic_round_playlist_play_24.xml │ ├── ic_round_reader_settings.xml │ ├── ic_round_refresh_24.xml │ ├── ic_round_remove_red_eye_24.xml │ ├── ic_round_restaurant_24.xml │ ├── ic_round_screen_rotation_24.xml │ ├── ic_round_screen_rotation_alt_24.xml │ ├── ic_round_sd_card_24.xml │ ├── ic_round_search_24.xml │ ├── ic_round_settings_24.xml │ ├── ic_round_share_24.xml │ ├── ic_round_skip_next_24.xml │ ├── ic_round_skip_previous_24.xml │ ├── ic_round_slow_motion_video_24.xml │ ├── ic_round_smart_button_24.xml │ ├── ic_round_sort_24.xml │ ├── ic_round_source_24.xml │ ├── ic_round_space_bar_24.xml │ ├── ic_round_star_24.xml │ ├── ic_round_straighten_24.xml │ ├── ic_round_subtitles_24.xml │ ├── ic_round_swipe_down_alt_24.xml │ ├── ic_round_swipe_up_alt_24.xml │ ├── ic_round_swipe_vertical_24.xml │ ├── ic_round_sync_24.xml │ ├── ic_round_touch_app_24.xml │ ├── ic_round_translate_variant_24.xml │ ├── ic_round_video_library_24.xml │ ├── ic_round_video_settings_24.xml │ ├── ic_round_view_array_24.xml │ ├── ic_round_view_column_24.xml │ ├── ic_round_view_comfy_24.xml │ ├── ic_round_view_list_24.xml │ ├── ic_round_volume_up_24.xml │ ├── ic_skip.xml │ ├── ic_telegram.xml │ ├── ic_upi_icon.xml │ ├── item_ongoing.xml │ ├── item_score.xml │ ├── item_type.xml │ ├── item_user_score.xml │ ├── kofi_s_tag_dark.png │ ├── linear_gradient_bg.xml │ ├── linear_gradient_black.xml │ ├── linear_gradient_black_horizontal.xml │ ├── linear_gradient_nav.xml │ ├── mono.xml │ ├── monochrome.xml │ ├── round_corner.xml │ ├── rounded_top_nav.xml │ ├── shape_corner_16dp.xml │ ├── spinner_icon.xml │ ├── spinner_icon_manga.xml │ └── ui_bg.xml │ ├── font │ ├── poppins.ttf │ ├── poppins_bold.ttf │ ├── poppins_family.xml │ ├── poppins_semi_bold.ttf │ └── poppins_thin.ttf │ ├── layout-land │ └── activity_media.xml │ ├── layout │ ├── activity_author.xml │ ├── activity_character.xml │ ├── activity_discord.xml │ ├── activity_exoplayer.xml │ ├── activity_faq.xml │ ├── activity_genre.xml │ ├── activity_image_search.xml │ ├── activity_list.xml │ ├── activity_main.xml │ ├── activity_manga_reader.xml │ ├── activity_media.xml │ ├── activity_no_internet.xml │ ├── activity_novel_reader.xml │ ├── activity_player_settings.xml │ ├── activity_reader_settings.xml │ ├── activity_search.xml │ ├── activity_settings.xml │ ├── activity_studio.xml │ ├── activity_user_interface_settings.xml │ ├── bottom_sheet_book.xml │ ├── bottom_sheet_current_novel_reader_settings.xml │ ├── bottom_sheet_current_reader_settings.xml │ ├── bottom_sheet_custom.xml │ ├── bottom_sheet_developers.xml │ ├── bottom_sheet_image.xml │ ├── bottom_sheet_media_list.xml │ ├── bottom_sheet_media_list_small.xml │ ├── bottom_sheet_search_filter.xml │ ├── bottom_sheet_selector.xml │ ├── bottom_sheet_settings.xml │ ├── bottom_sheet_source_search.xml │ ├── bottom_sheet_subtitles.xml │ ├── bottom_sheet_webview.xml │ ├── exo_player_control_view.xml │ ├── exo_player_view.xml │ ├── fragment_anime.xml │ ├── fragment_anime_watch.xml │ ├── fragment_home.xml │ ├── fragment_list.xml │ ├── fragment_login.xml │ ├── fragment_manga.xml │ ├── fragment_media_info.xml │ ├── item_anime_page.xml │ ├── item_anime_watch.xml │ ├── item_chapter_list.xml │ ├── item_character.xml │ ├── item_character_details.xml │ ├── item_chip.xml │ ├── item_count_down.xml │ ├── item_developer.xml │ ├── item_dropdown.xml │ ├── item_dual_page.xml │ ├── item_episode_compact.xml │ ├── item_episode_grid.xml │ ├── item_episode_list.xml │ ├── item_episodes_recyclerview.xml │ ├── item_genre.xml │ ├── item_image.xml │ ├── item_manga_page.xml │ ├── item_media_compact.xml │ ├── item_media_large.xml │ ├── item_media_page.xml │ ├── item_media_page_small.xml │ ├── item_novel_header.xml │ ├── item_novel_response.xml │ ├── item_progressbar.xml │ ├── item_quels.xml │ ├── item_question.xml │ ├── item_recyclerview.xml │ ├── item_search_by_image.xml │ ├── item_search_header.xml │ ├── item_seekbar_dialog.xml │ ├── item_stream.xml │ ├── item_subtitle_text.xml │ ├── item_title.xml │ ├── item_title_chipgroup.xml │ ├── item_title_recycler.xml │ ├── item_title_text.xml │ ├── item_title_trailer.xml │ ├── item_url.xml │ └── splash_screen.xml │ ├── menu │ ├── anime_menu_detail.xml │ ├── bottom_navbar_menu.xml │ ├── list_sort_menu.xml │ ├── manga_menu_detail.xml │ └── menu_media.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.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 │ ├── values-night │ ├── colors.xml │ └── themes.xml │ ├── values │ ├── attr.xml │ ├── colors.xml │ ├── font_certs.xml │ ├── preloaded_fonts.xml │ ├── strings.xml │ ├── style.xml │ └── themes.xml │ └── xml │ └── provider_paths.xml ├── beta.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts └── stable.md /.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: brahmkshatriya 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 -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.github/workflows/signing-key.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tas33n/saikou/7df974ab4556a0ac1b2a4b90399582b5b711249e/.github/workflows/signing-key.jks -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | .idea 4 | build 5 | local.properties 6 | .DS_Store 7 | /captures 8 | .externalNativeBuild 9 | .cxx 10 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /debug 3 | /debug/output-metadata.json 4 | /release -------------------------------------------------------------------------------- /app/src/debug/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Saikou β 4 | -------------------------------------------------------------------------------- /app/src/main/java/ani/saikou/connections/UpdateProgress.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.connections 2 | 3 | import ani.saikou.R 4 | import ani.saikou.Refresh 5 | import ani.saikou.connections.anilist.Anilist 6 | import ani.saikou.connections.mal.MAL 7 | import ani.saikou.currContext 8 | import ani.saikou.media.Media 9 | import ani.saikou.toast 10 | import kotlinx.coroutines.CoroutineScope 11 | import kotlinx.coroutines.Dispatchers 12 | import kotlinx.coroutines.launch 13 | import kotlin.math.roundToInt 14 | 15 | fun updateProgress(media: Media, number: String) { 16 | if (Anilist.userid != null) { 17 | CoroutineScope(Dispatchers.IO).launch { 18 | val a = number.toFloatOrNull()?.roundToInt() 19 | if (a != media.userProgress) { 20 | Anilist.mutation.editList( 21 | media.id, 22 | a, 23 | status = if (media.userStatus == "REPEATING") media.userStatus else "CURRENT" 24 | ) 25 | MAL.query.editList( 26 | media.idMAL, 27 | media.anime != null, 28 | a, null, 29 | if (media.userStatus == "REPEATING") media.userStatus!! else "CURRENT" 30 | ) 31 | toast(currContext()?.getString(R.string.setting_progress, a)) 32 | } 33 | media.userProgress = a 34 | Refresh.all() 35 | } 36 | } else { 37 | toast(currContext()?.getString(R.string.login_anilist_account)) 38 | } 39 | } -------------------------------------------------------------------------------- /app/src/main/java/ani/saikou/connections/anilist/BannerImage.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.connections.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 | } -------------------------------------------------------------------------------- /app/src/main/java/ani/saikou/connections/anilist/Genre.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.connections.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/java/ani/saikou/connections/anilist/Login.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.connections.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/java/ani/saikou/connections/anilist/UrlMedia.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.connections.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/connections/anilist/api/Recommendations.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.connections.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/connections/anilist/api/Studio.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.connections.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/connections/discord/serializers/Activity.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.connections.discord.serializers 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | @Serializable 6 | data class Activity ( 7 | @SerialName("application_id") 8 | val applicationId: String? = null, 9 | val name: String? = null, 10 | val details: String? = null, 11 | val state: String? = null, 12 | val type: Int? = null, 13 | val timestamps: Timestamps? = null, 14 | val assets: Assets? = null, 15 | val buttons: List? = null, 16 | val metadata: Metadata? = null 17 | ) { 18 | @Serializable 19 | data class Assets( 20 | @SerialName("large_image") 21 | val largeImage: String? = null, 22 | 23 | @SerialName("large_text") 24 | val largeText: String? = null, 25 | 26 | @SerialName("small_image") 27 | val smallImage: String? = null, 28 | 29 | @SerialName("small_text") 30 | val smallText: String? = null 31 | ) 32 | 33 | @Serializable 34 | data class Metadata( 35 | @SerialName("button_urls") 36 | val buttonUrls: List 37 | ) 38 | 39 | @Serializable 40 | data class Timestamps( 41 | val start: Long? = null, 42 | val stop: Long? = null 43 | ) 44 | } -------------------------------------------------------------------------------- /app/src/main/java/ani/saikou/connections/discord/serializers/Identity.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.connections.discord.serializers 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | 6 | @Serializable 7 | data class Identity( 8 | val token: String, 9 | val properties: Properties, 10 | val compress: Boolean, 11 | val intents: Long 12 | ) { 13 | 14 | @Serializable 15 | data class Response ( 16 | val op: Long, 17 | val d: Identity 18 | ) 19 | 20 | @Serializable 21 | data class Properties ( 22 | @SerialName("\$os") 23 | val os: String, 24 | 25 | @SerialName("\$browser") 26 | val browser: String, 27 | 28 | @SerialName("\$device") 29 | val device: String 30 | ) 31 | } -------------------------------------------------------------------------------- /app/src/main/java/ani/saikou/connections/discord/serializers/Presence.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.connections.discord.serializers 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | data class Presence ( 7 | val activities: List = listOf(), 8 | val afk: Boolean = true, 9 | val since: Long? = null, 10 | val status: String? = null 11 | ){ 12 | @Serializable 13 | data class Response ( 14 | val op: Long, 15 | val d: Presence 16 | ) 17 | } 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/java/ani/saikou/connections/discord/serializers/Res.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.connections.discord.serializers 2 | 3 | import kotlinx.serialization.Serializable 4 | import kotlinx.serialization.json.JsonElement 5 | 6 | @Serializable 7 | data class Res( 8 | val t: String?, 9 | val s: Int?, 10 | val op: Int, 11 | val d: JsonElement 12 | ) -------------------------------------------------------------------------------- /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/home/LoginFragment.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.home 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.R 9 | import ani.saikou.connections.anilist.Anilist 10 | import ani.saikou.databinding.FragmentLoginBinding 11 | import ani.saikou.openLinkInBrowser 12 | 13 | class LoginFragment : Fragment() { 14 | 15 | private var _binding: FragmentLoginBinding? = null 16 | private val binding get() = _binding!! 17 | 18 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { 19 | _binding = FragmentLoginBinding.inflate(layoutInflater, container, false) 20 | return binding.root 21 | } 22 | 23 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 24 | binding.loginButton.setOnClickListener { Anilist.loginIntent(requireActivity()) } 25 | binding.loginDiscord.setOnClickListener { openLinkInBrowser(getString(R.string.discord)) } 26 | binding.loginTelegram.setOnClickListener { openLinkInBrowser(getString(R.string.telegram)) } 27 | binding.loginGithub.setOnClickListener { openLinkInBrowser(getString(R.string.github)) } 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/main/java/ani/saikou/home/NoInternet.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.home 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 | import ani.saikou.isOnline 9 | import ani.saikou.navBarHeight 10 | import ani.saikou.startMainActivity 11 | import ani.saikou.statusBarHeight 12 | 13 | class NoInternet : AppCompatActivity() { 14 | override fun onCreate(savedInstanceState: Bundle?) { 15 | super.onCreate(savedInstanceState) 16 | 17 | val binding = ActivityNoInternetBinding.inflate(layoutInflater) 18 | setContentView(binding.root) 19 | 20 | binding.refreshContainer.updateLayoutParams { 21 | topMargin = statusBarHeight 22 | bottomMargin = navBarHeight 23 | } 24 | binding.refreshButton.setOnClickListener { 25 | if (isOnline(this)) { 26 | startMainActivity(this) 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/main/java/ani/saikou/media/Author.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.media 2 | 3 | import java.io.Serializable 4 | 5 | data class Author( 6 | val id: String, 7 | val name: String, 8 | var yearMedia: MutableMap>? = null 9 | ) : Serializable 10 | -------------------------------------------------------------------------------- /app/src/main/java/ani/saikou/media/Character.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.media 2 | 3 | import ani.saikou.connections.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/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/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/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/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/java/ani/saikou/media/anime/Anime.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.media.anime 2 | 3 | import ani.saikou.media.Author 4 | import ani.saikou.media.Studio 5 | import java.io.Serializable 6 | 7 | data class Anime( 8 | var totalEpisodes: Int? = null, 9 | 10 | var episodeDuration: Int? = null, 11 | var season: String? = null, 12 | var seasonYear: Int? = null, 13 | 14 | var op: ArrayList = arrayListOf(), 15 | var ed: ArrayList = arrayListOf(), 16 | 17 | var mainStudio: Studio? = null, 18 | var author: Author?=null, 19 | 20 | var youtube: String? = null, 21 | var nextAiringEpisode: Int? = null, 22 | var nextAiringEpisodeTime: Long? = null, 23 | 24 | var selectedEpisode: String? = null, 25 | var episodes: MutableMap? = null, 26 | var slug: String? = null, 27 | var kitsuEpisodes: Map? = null, 28 | var fillerEpisodes: Map? = null, 29 | ) : Serializable -------------------------------------------------------------------------------- /app/src/main/java/ani/saikou/media/anime/AnimeSourceAdapter.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.media.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/media/anime/Episode.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.media.anime 2 | 3 | import ani.saikou.FileUrl 4 | import ani.saikou.parsers.anime.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 | ) : Serializable 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/java/ani/saikou/media/anime/VideoCache.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.media.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/java/ani/saikou/media/manga/Manga.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.media.manga 2 | 3 | import ani.saikou.media.Author 4 | import java.io.Serializable 5 | 6 | data class Manga( 7 | var totalChapters: Int? = null, 8 | var selectedChapter: String? = null, 9 | var chapters: MutableMap? = null, 10 | var slug: String? = null, 11 | var author: Author?=null, 12 | ) : Serializable -------------------------------------------------------------------------------- /app/src/main/java/ani/saikou/media/manga/MangaChapter.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.media.manga 2 | 3 | import ani.saikou.parsers.manga.MangaChapter 4 | import ani.saikou.parsers.manga.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/java/ani/saikou/media/manga/MangaSourceAdapter.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.media.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/java/ani/saikou/media/user/ListViewModel.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.media.user 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.ViewModel 6 | import ani.saikou.connections.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/java/ani/saikou/media/user/ListViewPagerAdapter.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.media.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/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/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/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/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/java/ani/saikou/parsers/anime/AnimeExtractors.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.parsers.anime 2 | 3 | import android.net.Uri 4 | import ani.saikou.parsers.anime.extractors.* 5 | 6 | object AnimeExtractors { 7 | 8 | fun getExtractor(name: String, server: VideoServer): VideoExtractor? { 9 | var domain = Uri.parse(server.embed.url).host ?: return null 10 | if (domain.startsWith("www.")) {domain = domain.substring(4)} 11 | 12 | val extractor: VideoExtractor? = when (domain) { 13 | "filemoon.to", "filemoon.sx" -> FileMoon(server) 14 | "rapid-cloud.co" -> RapidCloud(server) 15 | "streamtape.com" -> StreamTape(server) 16 | "vidstream.pro" -> VidStreaming(server) 17 | "mp4upload.com" -> Mp4Upload(server) 18 | "playtaku.net","goone.pro" -> GogoCDN(server) 19 | "alions.pro" -> ALions(server) 20 | "awish.pro" -> AWish(server) 21 | "dood.wf" -> DoodStream(server) 22 | "ok.ru" -> OkRu(server) 23 | else -> { 24 | println("$name : No extractor found for: $domain | ${server.embed.url}") 25 | null 26 | } 27 | } 28 | return extractor 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/main/java/ani/saikou/parsers/anime/AnimeSources.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.parsers.anime 2 | 3 | import ani.saikou.Lazier 4 | import ani.saikou.lazyList 5 | import ani.saikou.parsers.BaseParser 6 | import ani.saikou.parsers.WatchSources 7 | import ani.saikou.parsers.anime.sources.AllAnime 8 | import ani.saikou.parsers.anime.sources.AniWave 9 | import ani.saikou.parsers.anime.sources.AnimeDao 10 | import ani.saikou.parsers.anime.sources.AnimePahe 11 | import ani.saikou.parsers.anime.sources.Animension 12 | import ani.saikou.parsers.anime.sources.Gogo 13 | import ani.saikou.parsers.anime.sources.Haho 14 | import ani.saikou.parsers.anime.sources.HentaiFF 15 | import ani.saikou.parsers.anime.sources.HentaiMama 16 | import ani.saikou.parsers.anime.sources.HentaiStream 17 | import ani.saikou.parsers.anime.sources.Kaido 18 | 19 | object AnimeSources : WatchSources() { 20 | override val list: List> = lazyList( 21 | // "Dummy" to ::AnimeDummy, 22 | "Gogo" to ::Gogo, 23 | "AllAnime" to ::AllAnime, 24 | "AnimeDao" to ::AnimeDao, 25 | "AnimePahe" to ::AnimePahe, 26 | "Animension" to ::Animension, 27 | "Kaido" to ::Kaido, 28 | "AniWave" to ::AniWave, 29 | // "Sflix" to ::SFlix, 30 | 31 | 32 | ) 33 | } 34 | 35 | object HAnimeSources : WatchSources() { 36 | private val aList: List> = lazyList( 37 | "Haho" to ::Haho, 38 | "HentaiMama" to ::HentaiMama, 39 | "HentaiStream" to ::HentaiStream, 40 | "HentaiFF" to ::HentaiFF, 41 | 42 | ) 43 | 44 | override val list = listOf(aList, AnimeSources.list).flatten() 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/ani/saikou/parsers/anime/extractors/ALions.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.parsers.anime.extractors 2 | 3 | import ani.saikou.client 4 | import ani.saikou.others.JsUnpacker 5 | import ani.saikou.parsers.anime.Video 6 | import ani.saikou.parsers.anime.VideoContainer 7 | import ani.saikou.parsers.anime.VideoExtractor 8 | import ani.saikou.parsers.anime.VideoServer 9 | import ani.saikou.parsers.anime.VideoType 10 | 11 | class ALions(override val server: VideoServer) : VideoExtractor() { 12 | override suspend fun extract(): VideoContainer { 13 | val player = client.get(server.embed.url).document.html() 14 | val script = Regex("").find(player)!!.groups[1]!!.value // I could do this with nice-http html parser, but that's too much effort and regex only needs 3ms 15 | 16 | val url = Regex("file:\"([^\"]+)\"\\}").find(JsUnpacker(script).unpack()!!)!!.groups[1]!!.value 17 | return VideoContainer(listOf(Video(null, VideoType.M3U8, url))) 18 | } 19 | } -------------------------------------------------------------------------------- /app/src/main/java/ani/saikou/parsers/anime/extractors/AWish.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.parsers.anime.extractors 2 | 3 | import ani.saikou.client 4 | import ani.saikou.parsers.anime.Video 5 | import ani.saikou.parsers.anime.VideoContainer 6 | import ani.saikou.parsers.anime.VideoExtractor 7 | import ani.saikou.parsers.anime.VideoServer 8 | import ani.saikou.parsers.anime.VideoType 9 | 10 | class AWish(override val server: VideoServer) : VideoExtractor() { 11 | override suspend fun extract(): VideoContainer { 12 | val player = client.get(server.embed.url).document.html() 13 | 14 | val url = Regex("file:\"([^\"]+)\"\\}").find(player)!!.groups[1]!!.value 15 | return VideoContainer(listOf(Video(null, VideoType.M3U8, url))) 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/java/ani/saikou/parsers/anime/extractors/Animeflix.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.parsers.anime.extractors 2 | 3 | import ani.saikou.client 4 | import ani.saikou.parsers.anime.Video 5 | import ani.saikou.parsers.anime.VideoContainer 6 | import ani.saikou.parsers.anime.VideoExtractor 7 | import ani.saikou.parsers.anime.VideoServer 8 | import ani.saikou.parsers.anime.VideoType 9 | import org.json.JSONObject 10 | 11 | class Animeflix(override val server: VideoServer) : VideoExtractor() { 12 | override suspend fun extract(): VideoContainer { 13 | val response = client.get(server.embed.url, 14 | headers = mapOf("accept-language" to "en-US,en;q=0.9") 15 | ) 16 | val responseBody = response.body?.string() 17 | val json = JSONObject(responseBody) 18 | val source = json.getString("source") 19 | 20 | println("vajha vahj as : $source") 21 | 22 | val player = client.get(source, headers = mapOf("accept-language" to "en-US,en;q=0.9")).document.html() 23 | val url = Regex("const source = `([^`]+)`").find(player)!!.groups[1]!!.value 24 | return VideoContainer(listOf(Video(null, VideoType.M3U8, url))) 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /app/src/main/java/ani/saikou/parsers/anime/extractors/BetaPlayer.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.parsers.anime 2 | 3 | import ani.saikou.FileUrl 4 | import ani.saikou.Mapper 5 | import ani.saikou.client 6 | import ani.saikou.parsers.* 7 | import kotlinx.serialization.SerialName 8 | import kotlinx.serialization.Serializable 9 | 10 | class BetaPlayer(override val server: VideoServer) : VideoExtractor() { 11 | override suspend fun extract(): VideoContainer { 12 | // This needs to be implemented differently 13 | // but I need to find an anime which uses https://kaast1.com/betaplayer/ instead of 14 | // betaplayer.life first 15 | if (server.embed.url.contains("kaast1")) return VideoContainer(listOf()) 16 | 17 | val res = 18 | client 19 | .get(server.embed.url, server.embed.headers) 20 | .text 21 | .substringAfter("JSON.parse('") 22 | .substringBefore("')") 23 | 24 | val json = Mapper.parse("{\"files\":$res}") 25 | return VideoContainer( 26 | json.files.map() { video -> 27 | return@map Video( 28 | video.quality.removeSuffix("p").toInt(), 29 | VideoType.CONTAINER, 30 | FileUrl(video.link, server.embed.headers), 31 | ) 32 | }, 33 | ) 34 | } 35 | 36 | @Serializable 37 | data class BetaPlayerJSON(val files: List) { 38 | @Serializable 39 | data class BetaPlayerFiles( 40 | @SerialName("label") val quality: String, 41 | @SerialName("file") val link: String, 42 | val type: String, 43 | ) 44 | } 45 | } -------------------------------------------------------------------------------- /app/src/main/java/ani/saikou/parsers/anime/extractors/DoodStream.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.parsers.anime.extractors 2 | 3 | import android.net.Uri 4 | import ani.saikou.FileUrl 5 | import ani.saikou.client 6 | import ani.saikou.findBetween 7 | import ani.saikou.getSize 8 | import ani.saikou.parsers.anime.Video 9 | import ani.saikou.parsers.anime.VideoContainer 10 | import ani.saikou.parsers.anime.VideoExtractor 11 | import ani.saikou.parsers.anime.VideoServer 12 | import ani.saikou.parsers.anime.VideoType 13 | import java.util.* 14 | 15 | class DoodStream(override val server: VideoServer) : VideoExtractor() { 16 | override suspend fun extract(): VideoContainer { 17 | val domain = "https://" + Uri.parse(server.embed.url).host!! 18 | val res = client.get(server.embed.url).text 19 | val hash = res.findBetween("/pass_md5/", "'")!! 20 | val token = res.findBetween("token=", "&")!! 21 | val url = client.get("$domain/pass_md5/$hash", referer = domain).text 22 | val link = FileUrl("$url?token=$token&expiry=${Date().time}}", mapOf("referer" to domain)) 23 | println(link) 24 | return VideoContainer( 25 | listOf(Video(null, VideoType.CONTAINER, link, getSize(link))) 26 | ) 27 | } 28 | } -------------------------------------------------------------------------------- /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.anime.Video 7 | import ani.saikou.parsers.anime.VideoContainer 8 | import ani.saikou.parsers.anime.VideoExtractor 9 | import ani.saikou.parsers.anime.VideoServer 10 | import ani.saikou.parsers.anime.VideoType 11 | import ani.saikou.tryWithSuspend 12 | import kotlinx.serialization.Serializable 13 | 14 | class FPlayer(override val server: VideoServer) : VideoExtractor() { 15 | 16 | override suspend fun extract(): VideoContainer { 17 | val url = server.embed.url 18 | val apiLink = url.replace("/v/", "/api/source/") 19 | return tryWithSuspend { 20 | val json = client.post(apiLink, referer = url).parsed() 21 | if (json.success) { 22 | VideoContainer(json.data?.asyncMap { 23 | Video( 24 | it.label.replace("p", "").toIntOrNull(), 25 | VideoType.CONTAINER, 26 | it.file, 27 | getSize(it.file) 28 | ) 29 | }?: listOf()) 30 | } else null 31 | } ?: VideoContainer(listOf()) 32 | } 33 | 34 | @Serializable 35 | private data class Data( 36 | val file: String, 37 | val label: String 38 | ) 39 | 40 | @Serializable 41 | private data class Json( 42 | val success: Boolean, 43 | val data: List? 44 | ) 45 | } -------------------------------------------------------------------------------- /app/src/main/java/ani/saikou/parsers/anime/extractors/FileMoon.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.others.JsUnpacker 7 | import ani.saikou.parsers.anime.Video 8 | import ani.saikou.parsers.anime.VideoContainer 9 | import ani.saikou.parsers.anime.VideoExtractor 10 | import ani.saikou.parsers.anime.VideoServer 11 | import ani.saikou.parsers.anime.VideoType 12 | 13 | private val packedRegex = Regex("""eval\(function\(p,a,c,k,e,.*\)\)""") 14 | private fun getPacked(string: String): String? { 15 | return packedRegex.find(string)?.value 16 | } 17 | 18 | private fun getAndUnpack(string: String): String { 19 | val packedText = getPacked(string) 20 | return JsUnpacker(packedText).unpack() ?: string 21 | } 22 | class FileMoon(override val server: VideoServer) : VideoExtractor() { 23 | override suspend fun extract(): VideoContainer { 24 | val page = client.get(server.embed.url) 25 | val unpacked = getAndUnpack(page.text) 26 | val link = Regex("file:\"(.+?)\"").find(unpacked)?.groupValues?.last() 27 | 28 | link?.let { 29 | return VideoContainer( 30 | listOf( 31 | Video( 32 | null, 33 | VideoType.M3U8, 34 | FileUrl(link, mapOf("Referer" to server.embed.url)), 35 | getSize(link) 36 | ) 37 | ) 38 | ) 39 | } 40 | return VideoContainer(listOf()) 41 | 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /app/src/main/java/ani/saikou/parsers/anime/extractors/Maverickki.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.parsers.anime 2 | 3 | import android.net.Uri 4 | import ani.saikou.Mapper 5 | import ani.saikou.client 6 | import ani.saikou.parsers.* 7 | import kotlinx.serialization.Serializable 8 | 9 | class Maverickki(override val server: VideoServer) : VideoExtractor() { 10 | override suspend fun extract(): VideoContainer { 11 | val host = "https://" + Uri.parse(server.embed.url).host 12 | val res = 13 | client 14 | .get(server.embed.url.replace("/embed/", "/api/source/"), server.embed.headers) 15 | .body 16 | .string() 17 | val json = Mapper.parse(res) 18 | 19 | val videos = listOf(Video(null, VideoType.M3U8, host + json.hls)) 20 | 21 | val subtitles = 22 | json.subtitles?.map { sub -> 23 | Subtitle( 24 | sub.name, 25 | host + sub.src, 26 | // SubtitleType.VTT // Let this be auto-filled 27 | ) 28 | } 29 | ?: listOf() 30 | 31 | return VideoContainer(videos, subtitles) 32 | } 33 | 34 | @Serializable 35 | data class MaverickkiResponse(val hls: String, val subtitles: List?) { 36 | @Serializable 37 | data class MaverickkiSubtitles( 38 | val name: String, 39 | val src: String, 40 | ) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/ani/saikou/parsers/anime/extractors/Mp4Upload.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.anime.Video 7 | import ani.saikou.parsers.anime.VideoContainer 8 | import ani.saikou.parsers.anime.VideoExtractor 9 | import ani.saikou.parsers.anime.VideoServer 10 | import ani.saikou.parsers.anime.VideoType 11 | 12 | class Mp4Upload(override val server: VideoServer) : VideoExtractor() { 13 | override suspend fun extract(): VideoContainer { 14 | val link = client.get(server.embed.url).document 15 | .select("script").html() 16 | .substringAfter("src: \"").substringBefore("\"") 17 | val host = link.substringAfter("https://").substringBefore("/") 18 | val file = FileUrl(link, mapOf("host" to host)) 19 | return VideoContainer( 20 | listOf(Video(null, VideoType.CONTAINER, file, getSize(file))) 21 | ) 22 | } 23 | } -------------------------------------------------------------------------------- /app/src/main/java/ani/saikou/parsers/anime/extractors/OkRu.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.parsers.anime.extractors 2 | 3 | import ani.saikou.client 4 | import ani.saikou.parsers.anime.Video 5 | import ani.saikou.parsers.anime.VideoContainer 6 | import ani.saikou.parsers.anime.VideoExtractor 7 | import ani.saikou.parsers.anime.VideoServer 8 | import ani.saikou.parsers.anime.VideoType 9 | 10 | class OkRu(override val server: VideoServer) : VideoExtractor() { 11 | override suspend fun extract(): VideoContainer { 12 | val player = client.get(server.embed.url).document.html() 13 | val mediaUrl = Regex("https://vd\\d+\\.mycdn\\.me/e[^\\\\]+").find(player) 14 | 15 | return VideoContainer( 16 | listOf( 17 | Video(null, VideoType.M3U8, mediaUrl!!.value), 18 | Video(null, VideoType.DASH, mediaUrl.next()!!.value) 19 | ) 20 | ) 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/java/ani/saikou/parsers/anime/extractors/PinkBird.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.parsers.anime 2 | 3 | import android.util.Base64 4 | import ani.saikou.FileUrl 5 | import ani.saikou.Mapper 6 | import ani.saikou.client 7 | import ani.saikou.parsers.* 8 | import kotlinx.serialization.SerialName 9 | import kotlinx.serialization.Serializable 10 | 11 | class PinkBird(override val server: VideoServer) : VideoExtractor() { 12 | override suspend fun extract(): VideoContainer { 13 | val slice = client.get(server.embed.url, server.embed.headers).body.string() 14 | val json = Mapper.parse(slice) 15 | 16 | return VideoContainer( 17 | videos = 18 | json.sources.map { source -> 19 | return@map Video( 20 | null, 21 | VideoType.M3U8, 22 | FileUrl( 23 | "https://pb.kaast1.com/manifest/${Base64.decode(source.encodedBase64, Base64.NO_WRAP).decodeToString()}/master.m3u8", 24 | server.embed.headers 25 | ), 26 | ) 27 | } 28 | ) 29 | } 30 | 31 | @Serializable 32 | data class PinkDuckJSON( 33 | @SerialName("data") val sources: List, 34 | ) { 35 | @Serializable 36 | data class PinkDuckSource( 37 | @SerialName("eid") val encodedBase64: String, 38 | ) 39 | } 40 | } -------------------------------------------------------------------------------- /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.anime.Video 7 | import ani.saikou.parsers.anime.VideoContainer 8 | import ani.saikou.parsers.anime.VideoExtractor 9 | import ani.saikou.parsers.anime.VideoServer 10 | import ani.saikou.parsers.anime.VideoType 11 | 12 | class StreamTape(override val server: VideoServer) : VideoExtractor() { 13 | private val linkRegex = Regex("""'robotlink'\)\.innerHTML = '(.+?)'\+ \('(.+?)'\)""") 14 | 15 | override suspend fun extract(): VideoContainer { 16 | val reg = linkRegex.find(client.get(server.embed.url.replace("tape.com","adblocker.xyz")).text)?:return VideoContainer(listOf()) 17 | val extractedUrl = FileUrl("https:${reg.groups[1]!!.value + reg.groups[2]!!.value.substring(3)}") 18 | return VideoContainer(listOf(Video(null, VideoType.CONTAINER, extractedUrl, getSize(extractedUrl)))) 19 | } 20 | } -------------------------------------------------------------------------------- /app/src/main/java/ani/saikou/parsers/anime/extractors/VidStreaming.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.parsers.anime.extractors 2 | 3 | import ani.saikou.client 4 | import ani.saikou.findBetween 5 | import ani.saikou.parsers.anime.Video 6 | import ani.saikou.parsers.anime.VideoContainer 7 | import ani.saikou.parsers.anime.VideoExtractor 8 | import ani.saikou.parsers.anime.VideoServer 9 | import ani.saikou.parsers.anime.VideoType 10 | 11 | class VidStreaming(override val server: VideoServer) : VideoExtractor() { 12 | override suspend fun extract(): VideoContainer { 13 | if(server.embed.url.contains("srcd")) { 14 | val link = client.get(server.embed.url).text.findBetween("\"file\": '", "',")!! 15 | return VideoContainer(listOf(Video(null, VideoType.M3U8, link, null))) 16 | } 17 | val url = client.get(server.embed.url).document.select("iframe").attr("src") 18 | if(url.contains("filemoon")) { 19 | return FileMoon(VideoServer("FileMoon", url)).extract() 20 | } 21 | return GogoCDN(VideoServer("GogoCDN", url)).extract() 22 | } 23 | } -------------------------------------------------------------------------------- /app/src/main/java/ani/saikou/parsers/anime/sources/AnimeDummy.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.parsers.anime.sources 2 | 3 | import ani.saikou.parsers.ShowResponse 4 | import ani.saikou.parsers.anime.AnimeParser 5 | import ani.saikou.parsers.anime.Episode 6 | import ani.saikou.parsers.anime.VideoServer 7 | 8 | class AnimeDummy : AnimeParser() { 9 | 10 | override val name = "Dummy" 11 | override val saveName = "anime_dummy" 12 | override val hostUrl = "https://example.com" 13 | override val isDubAvailableSeparately = false 14 | 15 | override suspend fun loadEpisodes( 16 | animeLink: String, 17 | extra: Map? 18 | ): List { 19 | TODO("Not yet implemented") 20 | } 21 | 22 | override suspend fun loadVideoServers( 23 | episodeLink: String, 24 | extra: Map? 25 | ): List { 26 | TODO("Not yet implemented") 27 | } 28 | 29 | override suspend fun search(query: String): List { 30 | TODO("Not yet implemented") 31 | } 32 | } -------------------------------------------------------------------------------- /app/src/main/java/ani/saikou/parsers/manga/MangaSources.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.parsers.manga 2 | 3 | import ani.saikou.Lazier 4 | import ani.saikou.lazyList 5 | import ani.saikou.parsers.BaseParser 6 | import ani.saikou.parsers.MangaReadSources 7 | import ani.saikou.parsers.manga.sources.AllAnime 8 | import ani.saikou.parsers.manga.sources.AsuraScans 9 | import ani.saikou.parsers.manga.sources.Manga4Life 10 | import ani.saikou.parsers.manga.sources.MangaBuddy 11 | import ani.saikou.parsers.manga.sources.MangaDex 12 | import ani.saikou.parsers.manga.sources.MangaKakalot 13 | import ani.saikou.parsers.manga.sources.MangaKatana 14 | import ani.saikou.parsers.manga.sources.MangaPill 15 | import ani.saikou.parsers.manga.sources.MangaRead 16 | import ani.saikou.parsers.manga.sources.Manhwa18 17 | import ani.saikou.parsers.manga.sources.MmScans 18 | import ani.saikou.parsers.manga.sources.NineHentai 19 | import ani.saikou.parsers.manga.sources.Toonily 20 | 21 | object MangaSources : MangaReadSources() { 22 | override val list: List> = lazyList( 23 | "AsuraScans" to ::AsuraScans, 24 | "MangaKakalot" to ::MangaKakalot, 25 | "MangaBuddy" to ::MangaBuddy, 26 | "MangaPill" to ::MangaPill, 27 | "MangaDex" to ::MangaDex, 28 | "AllAnime" to ::AllAnime, 29 | "Toonily" to ::Toonily, 30 | "MmScans" to ::MmScans, 31 | "MangaKatana" to ::MangaKatana, 32 | "Manga4Life" to ::Manga4Life, 33 | "MangaRead" to ::MangaRead 34 | 35 | ) 36 | } 37 | 38 | object HMangaSources : MangaReadSources() { 39 | private val aList: List> = lazyList( 40 | "NineHentai" to ::NineHentai, 41 | "Manhwa18" to ::Manhwa18, 42 | ) 43 | override val list = listOf(aList, MangaSources.list).flatten() 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/ani/saikou/parsers/manga/sources/MangaDummy.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.parsers.manga.sources 2 | 3 | import ani.saikou.parsers.ShowResponse 4 | import ani.saikou.parsers.manga.MangaChapter 5 | import ani.saikou.parsers.manga.MangaImage 6 | import ani.saikou.parsers.manga.MangaParser 7 | 8 | class MangaDummy : MangaParser() { 9 | 10 | override val name = "Dummy" 11 | override val saveName = "manga_dummy" 12 | override val hostUrl = "https://example.com" 13 | 14 | override suspend fun loadChapters( 15 | mangaLink: String, 16 | extra: Map? 17 | ): List { 18 | TODO("Not yet implemented") 19 | } 20 | 21 | override suspend fun loadImages(chapterLink: String): List { 22 | TODO("Not yet implemented") 23 | } 24 | 25 | override suspend fun search(query: String): List { 26 | TODO("Not yet implemented") 27 | } 28 | } -------------------------------------------------------------------------------- /app/src/main/java/ani/saikou/parsers/manga/sources/MangaPill.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.parsers.manga.sources 2 | 3 | import ani.saikou.FileUrl 4 | import ani.saikou.client 5 | import ani.saikou.parsers.manga.MangaChapter 6 | import ani.saikou.parsers.manga.MangaImage 7 | import ani.saikou.parsers.manga.MangaParser 8 | import ani.saikou.parsers.ShowResponse 9 | 10 | class MangaPill : MangaParser() { 11 | 12 | override val name = "MangaPill" 13 | override val saveName = "manga_pill" 14 | override val hostUrl = "https://mangapill.com" 15 | 16 | val headers = mapOf("referer" to hostUrl) 17 | 18 | override suspend fun loadChapters( 19 | mangaLink: String, extra: Map? 20 | ): List { 21 | return client.get(mangaLink).document.select("#chapters > div > a").map { 22 | val chap = it.text().replace("Chapter ", "") 23 | MangaChapter(chap, hostUrl + it.attr("href")) 24 | } 25 | } 26 | 27 | override suspend fun loadImages(chapterLink: String): List { 28 | return client.get(chapterLink).document.select("img.js-page").map { 29 | MangaImage(FileUrl(it.attr("data-src"), headers)) 30 | } 31 | 32 | } 33 | 34 | override suspend fun search(query: String): List { 35 | val link = "$hostUrl/quick-search?q=${encode(query)}" 36 | return client.get(link).document.select(".bg-card").map { 37 | ShowResponse( 38 | it.select(".font-black").text(), 39 | hostUrl + it.attr("href"), 40 | FileUrl(it.select("img").attr("src"), headers) 41 | ) 42 | } 43 | 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/ani/saikou/parsers/novel/NovelSources.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.parsers.novel 2 | 3 | import ani.saikou.Lazier 4 | import ani.saikou.lazyList 5 | import ani.saikou.parsers.BaseParser 6 | import ani.saikou.parsers.NovelReadSources 7 | import ani.saikou.parsers.novel.sources.NovelDummy 8 | 9 | object NovelSources : NovelReadSources() { 10 | override val list: List> = lazyList( 11 | "Dummy" to ::NovelDummy, 12 | ) 13 | } -------------------------------------------------------------------------------- /app/src/main/java/ani/saikou/parsers/novel/sources/NovelDummy.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.parsers.novel.sources 2 | 3 | import ani.saikou.parsers.ShowResponse 4 | import ani.saikou.parsers.novel.Book 5 | import ani.saikou.parsers.novel.NovelParser 6 | 7 | class NovelDummy : NovelParser(){ 8 | 9 | override val name = "Dummy" 10 | override val saveName = "novel_dummy" 11 | override val hostUrl = "https://example.com" 12 | override val volumeRegex: Regex = Regex("") 13 | 14 | override suspend fun loadBook(link: String, extra: Map?): Book { 15 | TODO("Not yet implemented") 16 | } 17 | 18 | override suspend fun search(query: String): List { 19 | TODO("Not yet implemented") 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /app/src/main/java/ani/saikou/settings/CurrentNovelReaderSettings.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.settings 2 | 3 | import java.io.Serializable 4 | 5 | data class CurrentNovelReaderSettings( 6 | var currentThemeName: String = "Default", 7 | var layout: Layouts = Layouts.PAGED, 8 | var dualPageMode: CurrentReaderSettings.DualPageModes = CurrentReaderSettings.DualPageModes.Automatic, 9 | var lineHeight: Float = 1.4f, 10 | var margin: Float = 0.06f, 11 | var justify: Boolean = true, 12 | var hyphenation: Boolean = true, 13 | var useDarkTheme: Boolean = false, 14 | var invert: Boolean = false, 15 | var maxInlineSize: Int = 720, 16 | var maxBlockSize: Int = 1440, 17 | var horizontalScrollBar: Boolean = true, 18 | var keepScreenOn: Boolean = false, 19 | var volumeButtons: Boolean = false, 20 | ) : Serializable { 21 | 22 | enum class Layouts(val string: String) { 23 | PAGED("Paged"), 24 | SCROLLED("Scrolled"); 25 | 26 | companion object { 27 | operator fun get(value: Int) = values().firstOrNull { it.ordinal == value } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /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/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/java/ani/saikou/settings/NovelReaderSettings.kt: -------------------------------------------------------------------------------- 1 | package ani.saikou.settings 2 | 3 | import java.io.Serializable 4 | 5 | data class NovelReaderSettings( 6 | var showSource: Boolean = true, 7 | var showSystemBars: Boolean = false, 8 | var default: CurrentNovelReaderSettings = CurrentNovelReaderSettings(), 9 | var askIndividual: Boolean = true, 10 | ) : Serializable -------------------------------------------------------------------------------- /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/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/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 -------------------------------------------------------------------------------- /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/res/anim/over_shoot.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/main/res/color/button_switch_track.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/color/tab_layout_icon.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/color/tab_layout_text.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_invert_colors_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bottom_nav.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/buymecofee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tas33n/saikou/7df974ab4556a0ac1b2a4b90399582b5b711249e/app/src/main/res/drawable/buymecofee.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/control_background_40dp.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_add.xml: -------------------------------------------------------------------------------- 1 | 7 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_anilist.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/drawable/ic_github.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 6 | 8 | 11 | 14 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 6 | 8 | 11 | 14 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_minus.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_myanimelist.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_page_numbering.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_accessible_forward_24.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_add_circle_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_alpha_t_box_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_amp_stories_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_animation_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 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_arrow_drop_down_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_art_track_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_auto_awesome_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_brightness_4_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_brightness_auto_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_brightness_high_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_brightness_medium_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_calendar_today_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_cast_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_close_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_color_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_date_range_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_download_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_edit_note_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_fast_forward_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_fast_rewind_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_favorite_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_filter_alt_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_font_size_24.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_format_text_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_fullscreen_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /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_heart_broken_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_help_24.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_high_quality_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_home_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_image_search_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_import_contacts_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_info_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_lock_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_lock_open_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_movie_filter_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_notifications_none_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_pause_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_person_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_picture_in_picture_alt_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_play_arrow_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_play_disabled_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_playlist_add_24.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_playlist_play_24.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_reader_settings.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_refresh_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_remove_red_eye_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_screen_rotation_alt_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_sd_card_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_search_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_settings_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_share_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_skip_next_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_slow_motion_video_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_smart_button_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_sort_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_source_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/drawable/ic_round_star_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_straighten_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_subtitles_24.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_swipe_down_alt_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_swipe_up_alt_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_swipe_vertical_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_sync_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_touch_app_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_translate_variant_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_video_library_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_video_settings_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 13 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_view_array_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_view_comfy_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_view_list_24.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_volume_up_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_skip.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_telegram.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/item_ongoing.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/kofi_s_tag_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tas33n/saikou/7df974ab4556a0ac1b2a4b90399582b5b711249e/app/src/main/res/drawable/kofi_s_tag_dark.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/linear_gradient_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/linear_gradient_black.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/linear_gradient_black_horizontal.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/linear_gradient_nav.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/monochrome.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 13 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/round_corner.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/rounded_top_nav.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_corner_16dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/spinner_icon.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/spinner_icon_manga.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ui_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 17 | 18 | 25 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/font/poppins.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tas33n/saikou/7df974ab4556a0ac1b2a4b90399582b5b711249e/app/src/main/res/font/poppins.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/poppins_bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tas33n/saikou/7df974ab4556a0ac1b2a4b90399582b5b711249e/app/src/main/res/font/poppins_bold.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/poppins_family.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/font/poppins_semi_bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tas33n/saikou/7df974ab4556a0ac1b2a4b90399582b5b711249e/app/src/main/res/font/poppins_semi_bold.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/poppins_thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tas33n/saikou/7df974ab4556a0ac1b2a4b90399582b5b711249e/app/src/main/res/font/poppins_thin.ttf -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_discord.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_exoplayer.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_faq.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 24 | 25 | 39 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_genre.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 21 | 22 | 35 | 36 | 42 | 43 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_search.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/bottom_sheet_developers.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 27 | 28 | 39 | 40 | -------------------------------------------------------------------------------- /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/bottom_sheet_webview.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 21 | 22 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_anime_watch.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 18 | 19 | 31 | 32 | 42 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_character_details.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 15 | 16 | 27 | 28 | 36 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_chip.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_count_down.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 21 | 22 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_dropdown.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_episodes_recyclerview.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_genre.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 17 | 18 | 23 | 24 | 37 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_image.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 16 | 17 | 27 | 28 | 35 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_progressbar.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_question.xml: -------------------------------------------------------------------------------- 1 | 2 |