├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── app ├── .gitignore ├── README.md ├── build.gradle ├── proguard-rules.pro ├── schemas │ └── com.astarivi.kaizoyu.core.storage.database.AppDatabase │ │ ├── 1.json │ │ ├── 2.json │ │ ├── 3.json │ │ └── 4.json └── src │ ├── beta │ ├── ic_launcher-playstore.png │ └── res │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_foreground.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_foreground.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_foreground.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_foreground.webp │ │ └── ic_launcher_round.webp │ │ └── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_foreground.webp │ │ └── ic_launcher_round.webp │ ├── debug │ ├── ic_launcher-playstore.png │ └── res │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_foreground.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_foreground.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_foreground.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_foreground.webp │ │ └── ic_launcher_round.webp │ │ └── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_foreground.webp │ │ └── ic_launcher_round.webp │ └── main │ ├── AndroidManifest.xml │ ├── ic_launcher-playstore.png │ ├── java │ └── com │ │ └── astarivi │ │ └── kaizoyu │ │ ├── KaizoyuApplication.java │ │ ├── MainActivity.java │ │ ├── core │ │ ├── adapters │ │ │ ├── AnimePagingAdapter.java │ │ │ ├── AnimeRecyclerAdapter.java │ │ │ ├── AnimeViewHolder.java │ │ │ ├── HttpFileDownloader.java │ │ │ ├── gui │ │ │ │ ├── ClickableMaterialToolbar.java │ │ │ │ ├── NestedCoordinatorLayout.java │ │ │ │ ├── TickSeekBar.java │ │ │ │ └── WindowCompatUtils.java │ │ │ ├── modal │ │ │ │ ├── GenericModalBottomSheet.java │ │ │ │ └── ModalOption.java │ │ │ └── tab │ │ │ │ └── TabFragment.java │ │ ├── common │ │ │ ├── AnalyticsClient.java │ │ │ ├── NotificationsHub.java │ │ │ └── ThreadedOnly.java │ │ ├── models │ │ │ ├── Result.java │ │ │ ├── anime │ │ │ │ ├── AnimeMapper.java │ │ │ │ ├── LocalAnime.java │ │ │ │ ├── RemoteAnime.java │ │ │ │ └── SeasonalAnime.java │ │ │ ├── base │ │ │ │ ├── AnimeBasicInfo.java │ │ │ │ └── EpisodeBasicInfo.java │ │ │ └── episode │ │ │ │ ├── EpisodeMapper.java │ │ │ │ ├── LocalEpisode.java │ │ │ │ └── RemoteEpisode.java │ │ ├── schedule │ │ │ └── AssistedScheduleFetcher.java │ │ ├── search │ │ │ ├── AssistedResultSearcher.java │ │ │ ├── IndependentResultSearcher.java │ │ │ ├── ManagedEpisodeSearch.java │ │ │ ├── Organizer.java │ │ │ ├── SearchEnhancer.java │ │ │ └── SearchUtils.java │ │ ├── storage │ │ │ ├── PersistenceRepository.java │ │ │ ├── database │ │ │ │ ├── AppDatabase.java │ │ │ │ ├── io │ │ │ │ │ ├── Export.java │ │ │ │ │ ├── Import.java │ │ │ │ │ └── Manager.java │ │ │ │ ├── migrations │ │ │ │ │ └── Migrations.java │ │ │ │ ├── repo │ │ │ │ │ ├── SavedShowRepo.java │ │ │ │ │ └── SearchHistoryRepo.java │ │ │ │ └── tables │ │ │ │ │ ├── id_overlays │ │ │ │ │ ├── IdOverlays.java │ │ │ │ │ └── IdOverlaysDao.java │ │ │ │ │ ├── saved_anime │ │ │ │ │ ├── SavedAnime.java │ │ │ │ │ ├── SavedAnimeDao.java │ │ │ │ │ └── SavedAnimeWithEpisodes.java │ │ │ │ │ ├── saved_episode │ │ │ │ │ ├── SavedEpisode.java │ │ │ │ │ └── SavedEpisodeDao.java │ │ │ │ │ └── search_history │ │ │ │ │ ├── SearchHistory.java │ │ │ │ │ └── SearchHistoryDao.java │ │ │ ├── ids │ │ │ │ ├── AnimeIds.java │ │ │ │ └── AnimeIdsDatabase.java │ │ │ └── properties │ │ │ │ └── ExtendedProperties.java │ │ ├── theme │ │ │ ├── AppCompatActivityTheme.java │ │ │ ├── Colors.java │ │ │ └── Theme.java │ │ ├── threading │ │ │ ├── ThreadingAssistant.java │ │ │ └── workers │ │ │ │ ├── EpisodePeriodicWorker.java │ │ │ │ ├── UpdatePeriodicWorker.java │ │ │ │ └── WorkerInitializers.java │ │ ├── updater │ │ │ └── UpdateManager.java │ │ └── video │ │ │ ├── VideoParser.java │ │ │ └── VideoQuality.java │ │ ├── details │ │ ├── AnimeDetailsActivity.java │ │ ├── DetailsUtils.java │ │ └── gui │ │ │ ├── AnimeEpisodesFragment.java │ │ │ ├── AnimeEpisodesModalBottomSheet.java │ │ │ ├── AnimeEpisodesViewModelV2.java │ │ │ ├── AnimeInfoFragment.java │ │ │ ├── AnimeInfoViewModel.java │ │ │ ├── adapters │ │ │ └── DetailsTabAdapter.java │ │ │ └── recycler │ │ │ ├── EpisodesRecyclerAdapter.java │ │ │ └── SharedEpisodeViewHolder.java │ │ ├── fullsearch │ │ ├── FullSearchActivity.java │ │ ├── FullSearchViewModel.java │ │ └── recycler │ │ │ └── AdvancedRecyclerAdapter.java │ │ ├── gui │ │ ├── NotificationModalBottomSheet.java │ │ ├── UpdaterModalBottomSheet.java │ │ ├── WelcomeModalBottomSheet.java │ │ ├── adapters │ │ │ ├── BackInterceptAdapter.java │ │ │ └── TabAdapter.java │ │ ├── home │ │ │ ├── HomeFragment.java │ │ │ ├── HomeViewModel.java │ │ │ ├── data │ │ │ │ └── Categories.java │ │ │ └── recycler │ │ │ │ ├── news │ │ │ │ └── NewsRecyclerAdapter.java │ │ │ │ └── recommendations │ │ │ │ ├── HomeFuturePagingSource.java │ │ │ │ └── HomePagingAdapter.java │ │ ├── library │ │ │ ├── LibraryFragment.java │ │ │ └── watching │ │ │ │ ├── SharedLibraryActivity.java │ │ │ │ ├── SharedLibraryViewModel.java │ │ │ │ └── adapter │ │ │ │ └── SharedLibraryPagingAdapter.java │ │ ├── more │ │ │ ├── MoreFragment.java │ │ │ ├── settings │ │ │ │ └── SettingsActivity.java │ │ │ └── storage │ │ │ │ └── StorageActivity.java │ │ └── schedule │ │ │ ├── ScheduleFragment.java │ │ │ ├── ScheduleViewModel.java │ │ │ └── recycler │ │ │ └── ScheduleRecyclerAdapter.java │ │ ├── search │ │ ├── SearchActivity.java │ │ ├── SearchViewModel.java │ │ └── recycler │ │ │ ├── SearchFuturePagingSource.java │ │ │ └── SearchPagingAdapter.java │ │ ├── updater │ │ └── UpdaterActivity.java │ │ ├── utils │ │ ├── Data.java │ │ ├── MathUtils.java │ │ ├── Threading.java │ │ ├── Translation.java │ │ └── Utils.java │ │ └── video │ │ ├── VideoPlayerActivity.java │ │ ├── VideoPlayerViewModel.java │ │ ├── gui │ │ ├── PlayerBarView.java │ │ ├── PlayerSkipView.java │ │ ├── PlayerTrackMenuView.java │ │ └── PlayerView.java │ │ └── utils │ │ ├── AnimeEpisodeManager.java │ │ └── BundleUtils.java │ ├── res │ ├── color │ │ ├── chip_background_state_list.xml │ │ ├── chip_text_state_list.xml │ │ └── main_tab_layout_selector.xml │ ├── drawable-hdpi │ │ └── ic_stat_name.png │ ├── drawable-mdpi │ │ └── ic_stat_name.png │ ├── drawable-xhdpi │ │ └── ic_stat_name.png │ ├── drawable-xxhdpi │ │ └── ic_stat_name.png │ ├── drawable-xxxhdpi │ │ └── ic_stat_name.png │ ├── drawable │ │ ├── avd_launch.xml │ │ ├── bg_details_lower_fade.xml │ │ ├── bg_details_upper_fade.xml │ │ ├── bg_item_lower_fade.xml │ │ ├── bg_item_upper_fade.xml │ │ ├── bg_player_left_fade.xml │ │ ├── bg_player_lower_fade.xml │ │ ├── bg_player_right_fade.xml │ │ ├── bg_rounded_all.xml │ │ ├── bg_rounded_upper.xml │ │ ├── empty_divider.xml │ │ ├── flag_jp.webp │ │ ├── flag_us.webp │ │ ├── flag_us_jp.webp │ │ ├── ic_advanced_bot.xml │ │ ├── ic_advanced_extension.xml │ │ ├── ic_advanced_quality.xml │ │ ├── ic_advanced_size.xml │ │ ├── ic_back_new.xml │ │ ├── ic_back_new_small.xml │ │ ├── ic_check.xml │ │ ├── ic_close.xml │ │ ├── ic_details_add.xml │ │ ├── ic_details_added.xml │ │ ├── ic_details_feedback.xml │ │ ├── ic_details_share.xml │ │ ├── ic_details_stars.xml │ │ ├── ic_favorite.xml │ │ ├── ic_favorite_active.xml │ │ ├── ic_general_placeholder.xml │ │ ├── ic_general_tips.xml │ │ ├── ic_home_settings.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_main_collection.xml │ │ ├── ic_main_emission_alt.xml │ │ ├── ic_main_home_alt.xml │ │ ├── ic_main_more_alt.xml │ │ ├── ic_pip_forward.xml │ │ ├── ic_pip_pause.xml │ │ ├── ic_pip_play.xml │ │ ├── ic_pip_rewind.xml │ │ ├── ic_player_hide.xml │ │ ├── ic_resume.xml │ │ ├── ic_schedule_time.xml │ │ ├── ic_search_advanced.xml │ │ ├── ic_search_date.xml │ │ ├── ic_search_history.xml │ │ ├── ic_search_subtype.xml │ │ ├── ic_settings_discord.xml │ │ ├── ic_settings_donate.xml │ │ ├── ic_settings_github.xml │ │ ├── ic_skip_intro.xml │ │ ├── ic_stop.xml │ │ ├── ic_subtitles.xml │ │ ├── ic_top.xml │ │ ├── player_timestamp.xml │ │ ├── progress_circle.xml │ │ ├── search_noresults.webp │ │ ├── seekbar_thumb.xml │ │ └── seekbar_track_fade.xml │ ├── font │ │ ├── lato_black.ttf │ │ ├── lato_bold.ttf │ │ ├── lato_light.ttf │ │ ├── lato_regular.ttf │ │ └── monomaniacone_regular.ttf │ ├── layout │ │ ├── activity_anime_details.xml │ │ ├── activity_fullsearch.xml │ │ ├── activity_main.xml │ │ ├── activity_search.xml │ │ ├── activity_settings.xml │ │ ├── activity_shared_library.xml │ │ ├── activity_storage.xml │ │ ├── activity_updater.xml │ │ ├── activity_video_player.xml │ │ ├── bottom_sheet_episodes.xml │ │ ├── bottom_sheet_generic.xml │ │ ├── bottom_sheet_notification.xml │ │ ├── bottom_sheet_updater.xml │ │ ├── bottom_sheet_welcome.xml │ │ ├── component_suggestion_chip.xml │ │ ├── fragment_anime_episodes.xml │ │ ├── fragment_anime_episodes_item.xml │ │ ├── fragment_anime_info.xml │ │ ├── fragment_home.xml │ │ ├── fragment_library.xml │ │ ├── fragment_more.xml │ │ ├── fragment_schedule.xml │ │ ├── fragment_search_suggestion.xml │ │ ├── item_advanced.xml │ │ ├── item_anime.xml │ │ ├── item_chip_category.xml │ │ ├── item_episode.xml │ │ ├── item_news.xml │ │ ├── item_sheet_generic.xml │ │ ├── player.xml │ │ ├── player_bar.xml │ │ ├── player_skip_manager.xml │ │ ├── player_track_item.xml │ │ └── player_track_menu.xml │ ├── menu │ │ └── search_bar_menu.xml │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ ├── raw │ │ ├── an_horizontal_loading.lottie │ │ ├── an_skip_back.json │ │ └── an_skip_forward.json │ ├── values-night-v31 │ │ └── themes.xml │ ├── values-night │ │ └── themes.xml │ ├── values-v27 │ │ └── themes.xml │ ├── values-v31 │ │ └── themes.xml │ ├── values │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── strings.xml │ │ ├── strings_anime.xml │ │ ├── strings_details.xml │ │ ├── strings_dow.xml │ │ ├── strings_errors.xml │ │ ├── strings_home.xml │ │ ├── strings_notifications.xml │ │ ├── strings_player.xml │ │ ├── strings_search.xml │ │ ├── strings_settings.xml │ │ ├── strings_storage.xml │ │ ├── styles.xml │ │ └── themes.xml │ └── xml │ │ └── shared_paths.xml │ └── resources │ └── tinylog.properties ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── jitpack.yml ├── media ├── 1.png ├── 2.png ├── 3.png ├── 4.png ├── comparison.png └── small-cover.png ├── service ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── settings.gradle └── src │ ├── main │ └── java │ │ └── com │ │ └── astarivi │ │ └── kaizolib │ │ ├── anilist │ │ ├── AniList.java │ │ ├── AniListCommon.java │ │ ├── AniListDeserializer.java │ │ ├── AniListQuery.java │ │ ├── AniListSchedule.java │ │ ├── exception │ │ │ ├── AniListException.java │ │ │ └── ParsingError.java │ │ └── model │ │ │ ├── AiringSchedule.java │ │ │ └── AniListAnime.java │ │ ├── ann │ │ ├── ANN.java │ │ ├── RssDeserializer.java │ │ └── model │ │ │ └── ANNItem.java │ │ ├── common │ │ ├── exception │ │ │ ├── NoResponseException.java │ │ │ └── UnexpectedStatusCodeException.java │ │ ├── network │ │ │ ├── CommonHeaders.java │ │ │ ├── HttpMethods.java │ │ │ ├── HttpMethodsV2.java │ │ │ └── UserHttpClient.java │ │ └── util │ │ │ ├── JsonMapper.java │ │ │ ├── ResponseToString.java │ │ │ └── StringPair.java │ │ ├── irc │ │ ├── IrcClient.java │ │ ├── client │ │ │ └── BaseIrcClient.java │ │ ├── exception │ │ │ ├── BlacklistedIpException.java │ │ │ ├── BotNotFoundException.java │ │ │ ├── GenericHandshakeException.java │ │ │ ├── IrcExceptionManager.java │ │ │ ├── NoQuickRetryException.java │ │ │ └── StrictModeException.java │ │ └── utils │ │ │ └── Utils.java │ │ ├── kitsu │ │ ├── Kitsu.java │ │ ├── KitsuRelations.java │ │ ├── KitsuSearchParams.java │ │ ├── KitsuUtils.java │ │ ├── exception │ │ │ ├── KitsuExceptionManager.java │ │ │ ├── NetworkConnectionException.java │ │ │ ├── NoResponseException.java │ │ │ ├── NoResultsException.java │ │ │ └── ParsingException.java │ │ ├── model │ │ │ ├── KitsuAnime.java │ │ │ ├── KitsuCategoriesResult.java │ │ │ ├── KitsuCategory.java │ │ │ ├── KitsuEpisode.java │ │ │ ├── KitsuEpisodeResults.java │ │ │ ├── KitsuResourceResult.java │ │ │ └── KitsuSearchResults.java │ │ └── parser │ │ │ └── ParseJson.java │ │ ├── kitsuv2 │ │ ├── common │ │ │ └── KitsuCommon.java │ │ ├── exception │ │ │ ├── CloudflareChallengeException.java │ │ │ ├── KitsuException.java │ │ │ ├── NotAuthenticatedException.java │ │ │ └── ParsingError.java │ │ ├── model │ │ │ ├── KitsuAnime.java │ │ │ ├── KitsuCredentials.java │ │ │ ├── KitsuEpisode.java │ │ │ ├── KitsuListEntry.java │ │ │ ├── KitsuUser.java │ │ │ └── RawResults.java │ │ ├── private_api │ │ │ ├── KitsuPrivate.java │ │ │ ├── LibraryParams.java │ │ │ └── Methods.java │ │ └── public_api │ │ │ ├── KitsuPublic.java │ │ │ ├── Methods.java │ │ │ └── SearchParams.java │ │ ├── nibl │ │ ├── Nibl.java │ │ ├── NiblUtils.java │ │ ├── model │ │ │ ├── NiblBot.java │ │ │ ├── NiblBotsResults.java │ │ │ ├── NiblResult.java │ │ │ └── NiblSearchResults.java │ │ └── parser │ │ │ └── ParseJson.java │ │ ├── subsplease │ │ ├── SubsPlease.java │ │ ├── SubsPleaseUtils.java │ │ ├── model │ │ │ ├── SubsPleaseAnime.java │ │ │ ├── SubsPleaseResult.java │ │ │ └── SubsPleaseTodayResult.java │ │ └── parser │ │ │ └── ParseJson.java │ │ └── xdcc │ │ ├── XDCCDownloader.java │ │ ├── base │ │ ├── TimeoutException.java │ │ ├── XDCCDownloadListener.java │ │ └── XDCCFailure.java │ │ └── model │ │ └── DCC.java │ └── test │ └── java │ └── com │ └── astarivi │ └── kaizolib │ ├── AniListTest.java │ ├── KitsuTest.java │ ├── Kitsuv2Test.java │ ├── NiblTest.java │ ├── RssTest.java │ ├── SubsPleaseTest.java │ └── XdccTest.java └── settings.gradle /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/ 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | .cxx 10 | local.properties 11 | app/src/main/res/values/strings_keys.xml -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | 23 | # ProGuard rules for tinylog 24 | 25 | -keepnames interface org.tinylog.** 26 | -keepnames class * implements org.tinylog.** 27 | -keepclassmembers class * implements org.tinylog.** { (...); } 28 | 29 | -dontwarn dalvik.system.VMStack 30 | -dontwarn java.lang.** 31 | -dontwarn javax.naming.** 32 | -dontwarn sun.reflect.Reflection 33 | 34 | -keep class com.startapp.** { 35 | *; 36 | } 37 | 38 | -keep class com.truenet.** { 39 | *; 40 | } 41 | 42 | -keepattributes Exceptions, InnerClasses, Signature, Deprecated, SourceFile, 43 | LineNumberTable, *Annotation*, EnclosingMethod 44 | -dontwarn android.webkit.JavascriptInterface 45 | -dontwarn com.startapp.** 46 | 47 | -dontwarn org.jetbrains.annotations.** -------------------------------------------------------------------------------- /app/src/beta/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/beta/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/beta/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/beta/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/beta/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/beta/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/beta/res/mipmap-hdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/beta/res/mipmap-hdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/beta/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/beta/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/beta/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/beta/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/beta/res/mipmap-mdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/beta/res/mipmap-mdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/beta/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/beta/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/beta/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/beta/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/beta/res/mipmap-xhdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/beta/res/mipmap-xhdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/beta/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/beta/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/beta/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/beta/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/beta/res/mipmap-xxhdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/beta/res/mipmap-xxhdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/beta/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/beta/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/beta/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/beta/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/beta/res/mipmap-xxxhdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/beta/res/mipmap-xxxhdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/beta/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/beta/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/debug/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/debug/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/debug/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-hdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/debug/res/mipmap-hdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/debug/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/debug/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-mdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/debug/res/mipmap-mdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/debug/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/debug/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-xhdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/debug/res/mipmap-xhdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/debug/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/debug/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-xxhdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/debug/res/mipmap-xxhdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/debug/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/core/adapters/gui/ClickableMaterialToolbar.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.core.adapters.gui; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.util.AttributeSet; 6 | import android.view.MotionEvent; 7 | 8 | import androidx.annotation.NonNull; 9 | import androidx.annotation.Nullable; 10 | 11 | import com.google.android.material.appbar.MaterialToolbar; 12 | 13 | 14 | public class ClickableMaterialToolbar extends MaterialToolbar { 15 | public ClickableMaterialToolbar(@NonNull Context context) { 16 | super(context); 17 | } 18 | 19 | public ClickableMaterialToolbar(@NonNull Context context, @Nullable AttributeSet attrs) { 20 | super(context, attrs); 21 | } 22 | 23 | public ClickableMaterialToolbar(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 24 | super(context, attrs, defStyleAttr); 25 | } 26 | 27 | @SuppressLint("ClickableViewAccessibility") 28 | @Override 29 | public boolean onTouchEvent(MotionEvent ev) { 30 | if (ev.getAction() == MotionEvent.ACTION_DOWN || ev.getAction() == MotionEvent.ACTION_POINTER_DOWN){ 31 | return performClick(); 32 | } 33 | 34 | return false; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/core/adapters/gui/WindowCompatUtils.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.core.adapters.gui; 2 | 3 | import android.graphics.Color; 4 | import android.os.Build; 5 | import android.view.View; 6 | import android.view.Window; 7 | import android.view.WindowManager; 8 | 9 | import androidx.core.view.OnApplyWindowInsetsListener; 10 | import androidx.core.view.ViewCompat; 11 | import androidx.core.view.WindowCompat; 12 | 13 | 14 | public class WindowCompatUtils { 15 | public static void setOnApplyWindowInsetsListener(View v, OnApplyWindowInsetsListener listener) { 16 | // For any caller before Android 11, add requestApplyInsets to mimic Android 11 behavior 17 | // There could be a better way, but this doesn't seem to impact performance that much 18 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { 19 | v.addOnLayoutChangeListener((v1, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> 20 | v1.requestApplyInsets() 21 | ); 22 | } 23 | 24 | ViewCompat.setOnApplyWindowInsetsListener( 25 | v, 26 | listener 27 | ); 28 | } 29 | 30 | public static void setWindowFullScreen(final Window window){ 31 | WindowCompat.setDecorFitsSystemWindows(window, false); 32 | // After Android 10, on Android 11, Insets behave in a much more predictable way, and 33 | // this flag makes more sense then. Using it before Android 11, will cause issues. 34 | if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) { 35 | window.setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); 36 | } 37 | 38 | window.setStatusBarColor( 39 | Color.TRANSPARENT 40 | ); 41 | window.setNavigationBarColor( 42 | Color.TRANSPARENT 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/core/adapters/modal/ModalOption.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.core.adapters.modal; 2 | 3 | import com.astarivi.kaizolib.common.util.StringPair; 4 | 5 | import java.util.Objects; 6 | 7 | 8 | public class ModalOption extends StringPair { 9 | private final boolean shouldHighlight; 10 | 11 | public ModalOption(final String title, final String description, final boolean shouldHighlight) { 12 | super(title, description); 13 | this.shouldHighlight = shouldHighlight; 14 | } 15 | 16 | public ModalOption(final String title, final String description) { 17 | super(title, description); 18 | this.shouldHighlight = false; 19 | } 20 | 21 | public boolean shouldHighlight() { 22 | return shouldHighlight; 23 | } 24 | 25 | @Override 26 | public boolean equals(Object obj) { 27 | if (this == obj) { 28 | return true; 29 | } 30 | 31 | if (obj instanceof ModalOption) { 32 | final ModalOption that = (ModalOption) obj; 33 | return getName().equalsIgnoreCase(that.getName()) 34 | && Objects.equals(getValue(), that.getValue()) 35 | && (shouldHighlight() == that.shouldHighlight()); 36 | } 37 | 38 | return false; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/core/adapters/tab/TabFragment.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.core.adapters.tab; 2 | 3 | import androidx.fragment.app.Fragment; 4 | 5 | 6 | public abstract class TabFragment extends Fragment { 7 | public abstract void onTabReselected(); 8 | public boolean shouldFragmentInterceptBack() { 9 | return false; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/core/common/AnalyticsClient.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.core.common; 2 | 3 | import org.acra.ACRA; 4 | import org.tinylog.Logger; 5 | 6 | 7 | public class AnalyticsClient { 8 | public static boolean isEnabled = false; 9 | 10 | public static void logBreadcrumb(String event) { 11 | if (!isEnabled) return; 12 | 13 | final String tag = "Event at " + System.currentTimeMillis(); 14 | 15 | ACRA.getErrorReporter().putCustomData(tag, event); 16 | Logger.info(String.format("%s %s", tag, event)); 17 | } 18 | 19 | public static void onError(String errorId, String message, Throwable exception) { 20 | if (!isEnabled) return; 21 | 22 | ACRA.getErrorReporter().putCustomData( 23 | "Event at " + System.currentTimeMillis(), 24 | String.format("Caught error: %s, with message: %s", errorId, message) 25 | ); 26 | 27 | ACRA.getErrorReporter().handleSilentException(exception); 28 | } 29 | 30 | public static void onError(String errorId, String message, String exception) { 31 | if (!isEnabled) return; 32 | 33 | onError(errorId, message, new Exception(exception)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/core/common/ThreadedOnly.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.core.common; 2 | 3 | import java.lang.annotation.*; 4 | 5 | 6 | /** 7 | * Methods annotated cannot be run in the main thread, and must be run 8 | * in a background thread. 9 | */ 10 | @Retention(RetentionPolicy.SOURCE) 11 | @Target({ElementType.TYPE, ElementType.METHOD}) 12 | public @interface ThreadedOnly { 13 | } -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/core/models/base/EpisodeBasicInfo.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.core.models.base; 2 | 3 | import android.os.Parcelable; 4 | 5 | import androidx.annotation.NonNull; 6 | 7 | 8 | public abstract class EpisodeBasicInfo implements Parcelable, Comparable { 9 | public abstract long getKitsuId(); 10 | public abstract long getAnimeKitsuId(); 11 | public abstract int getNumber(); 12 | public abstract int getLength(); 13 | public abstract void setLength(int length); 14 | public abstract EpisodeType getType(); 15 | 16 | @Override 17 | public int compareTo(@NonNull EpisodeBasicInfo o) { 18 | return getNumber() - o.getNumber(); 19 | } 20 | 21 | public enum EpisodeType { 22 | LOCAL, 23 | REMOTE 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/core/search/AssistedResultSearcher.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.core.search; 2 | 3 | import com.astarivi.kaizolib.common.network.CommonHeaders; 4 | import com.astarivi.kaizolib.common.network.HttpMethodsV2; 5 | import com.astarivi.kaizoyu.core.common.ThreadedOnly; 6 | 7 | import org.jetbrains.annotations.Nullable; 8 | import org.tinylog.Logger; 9 | 10 | import java.io.IOException; 11 | 12 | import okhttp3.HttpUrl; 13 | import okhttp3.Request; 14 | 15 | 16 | public class AssistedResultSearcher { 17 | @ThreadedOnly 18 | public static @Nullable SearchEnhancer getSearchEnhancer(long kitsuId) { 19 | if (kitsuId == -1) return null; 20 | 21 | Request.Builder getRequestBuilder = new Request.Builder(); 22 | 23 | getRequestBuilder.url( 24 | new HttpUrl.Builder() 25 | .scheme("https") 26 | .host("kaizoyu.ddns.net") 27 | .addPathSegments("enhancer/api/v1/kitsu_search") 28 | .addPathSegment(String.valueOf(kitsuId)) 29 | .build() 30 | ); 31 | 32 | CommonHeaders.addTo(getRequestBuilder, CommonHeaders.JSON_HEADERS); 33 | 34 | String body; 35 | 36 | try { 37 | body = HttpMethodsV2.executeRequest(getRequestBuilder.build()); 38 | } catch (IOException e) { 39 | Logger.error("UpdateManager.getLatestReleases error at executing request"); 40 | Logger.error(e); 41 | return null; 42 | } 43 | 44 | if (body == null) return null; 45 | 46 | return SearchEnhancer.fromJson(body); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/core/search/IndependentResultSearcher.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.core.search; 2 | 3 | import com.astarivi.kaizolib.nibl.Nibl; 4 | import com.astarivi.kaizolib.nibl.model.NiblResult; 5 | import com.astarivi.kaizoyu.core.common.ThreadedOnly; 6 | import com.astarivi.kaizoyu.core.models.Result; 7 | 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import java.util.Locale; 13 | 14 | 15 | public class IndependentResultSearcher { 16 | @ThreadedOnly 17 | public static @Nullable List searchEpisode(String title, int episode) { 18 | List results = fetchEpisode(title, episode); 19 | 20 | if (results == null) return null; 21 | 22 | return SearchUtils.parseResults(results); 23 | } 24 | 25 | @ThreadedOnly 26 | public static @Nullable ArrayList searchEpisode(String title) { 27 | List results = Nibl.searchAnime(40, title); 28 | 29 | if (results == null) return null; 30 | 31 | return SearchUtils.parseResults(results); 32 | } 33 | 34 | @ThreadedOnly 35 | public static @Nullable List fetchEpisode(String title, int episode) { 36 | if (episode > 999) { 37 | title = String.format(Locale.UK, "%s %d", title, episode); 38 | episode = -1; 39 | } 40 | 41 | return Nibl.searchAnimeEpisode(40, title, episode); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/core/search/ManagedEpisodeSearch.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.core.search; 2 | 3 | import com.astarivi.kaizolib.nibl.model.NiblResult; 4 | import com.astarivi.kaizoyu.core.common.ThreadedOnly; 5 | import com.astarivi.kaizoyu.core.models.Result; 6 | import com.astarivi.kaizoyu.core.models.base.AnimeBasicInfo; 7 | 8 | import org.jetbrains.annotations.Nullable; 9 | import org.tinylog.Logger; 10 | 11 | import java.util.List; 12 | 13 | 14 | /** 15 | * Exports managed episode searching methods to use everywhere. 16 | */ 17 | public class ManagedEpisodeSearch { 18 | @ThreadedOnly 19 | public static @Nullable List search(AnimeBasicInfo anime, int episode, @Nullable SearchEnhancer se) { 20 | if (se == null) { 21 | // Let's try to search by all titles ourselves. 22 | // Order will be Romaji -> English -> Japanese 23 | for (String title : anime.getAllTitles()) { 24 | List result = IndependentResultSearcher.searchEpisode(title, episode); 25 | 26 | if (result != null && !result.isEmpty()) { 27 | return result; 28 | } 29 | } 30 | 31 | return null; 32 | } 33 | 34 | List niblResults = IndependentResultSearcher.fetchEpisode( 35 | se.title, 36 | se.episode != null ? se.episode + episode : episode 37 | ); 38 | 39 | if (niblResults == null) { 40 | Logger.error("Got no results after using enhanced search."); 41 | return null; 42 | } 43 | 44 | se.filter(niblResults); 45 | 46 | return SearchUtils.parseResults(niblResults); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/core/search/Organizer.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.core.search; 2 | 3 | import com.astarivi.kaizoyu.core.models.Result; 4 | import com.astarivi.kaizoyu.core.video.VideoQuality; 5 | 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import java.util.TreeMap; 11 | 12 | 13 | public class Organizer { 14 | public static @NotNull TreeMap> organizeResultsByQuality(@NotNull List results) { 15 | TreeMap> resultingMap = new TreeMap<>(); 16 | 17 | for (Result result : results) { 18 | VideoQuality quality = result.getQuality(); 19 | 20 | if (!resultingMap.containsKey(quality)) resultingMap.put(quality, new ArrayList<>()); 21 | 22 | List currentList = resultingMap.get(quality); 23 | 24 | if (currentList == null) continue; 25 | 26 | currentList.add(result); 27 | } 28 | 29 | return resultingMap; 30 | } 31 | 32 | public static @NotNull TreeMap organizeResultsByClearTitle(@NotNull List results) { 33 | TreeMap resultingMap = new TreeMap<>(); 34 | 35 | for (Result result : results) { 36 | String clearTitle = String.format( 37 | "%s : %s | %s", 38 | result.getBotName(), 39 | result.getNiblResult().size, 40 | result.getCleanedFilename() 41 | ); 42 | 43 | resultingMap.put(clearTitle, result); 44 | } 45 | 46 | return resultingMap; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/core/storage/database/AppDatabase.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.core.storage.database; 2 | 3 | import androidx.room.AutoMigration; 4 | import androidx.room.Database; 5 | import androidx.room.RoomDatabase; 6 | 7 | import com.astarivi.kaizoyu.core.storage.database.migrations.Migrations; 8 | import com.astarivi.kaizoyu.core.storage.database.tables.id_overlays.IdOverlays; 9 | import com.astarivi.kaizoyu.core.storage.database.tables.id_overlays.IdOverlaysDao; 10 | import com.astarivi.kaizoyu.core.storage.database.tables.saved_anime.SavedAnime; 11 | import com.astarivi.kaizoyu.core.storage.database.tables.saved_anime.SavedAnimeDao; 12 | import com.astarivi.kaizoyu.core.storage.database.tables.saved_episode.SavedEpisode; 13 | import com.astarivi.kaizoyu.core.storage.database.tables.saved_episode.SavedEpisodeDao; 14 | import com.astarivi.kaizoyu.core.storage.database.tables.search_history.SearchHistory; 15 | import com.astarivi.kaizoyu.core.storage.database.tables.search_history.SearchHistoryDao; 16 | 17 | 18 | @Database( 19 | version = 4, 20 | entities = { 21 | IdOverlays.class, 22 | SavedAnime.class, 23 | SavedEpisode.class, 24 | SearchHistory.class 25 | }, 26 | autoMigrations = { 27 | @AutoMigration(from = 3, to = 4, spec = Migrations.AUTO_MIGRATION_3_4.class) 28 | } 29 | ) 30 | public abstract class AppDatabase extends RoomDatabase { 31 | public abstract IdOverlaysDao idOverlaysDao(); 32 | public abstract SearchHistoryDao searchHistoryDao(); 33 | public abstract SavedEpisodeDao savedEpisodeDao(); 34 | public abstract SavedAnimeDao savedAnimeDao(); 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/core/storage/database/migrations/Migrations.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.core.storage.database.migrations; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.room.DeleteTable; 5 | import androidx.room.migration.AutoMigrationSpec; 6 | import androidx.room.migration.Migration; 7 | import androidx.sqlite.db.SupportSQLiteDatabase; 8 | 9 | public class Migrations { 10 | public static final Migration MIGRATION_1_2 = new Migration(1, 2) { 11 | @Override 12 | public void migrate(@NonNull SupportSQLiteDatabase database) { 13 | database.execSQL("ALTER TABLE favorite_anime ADD COLUMN type INTEGER NOT NULL DEFAULT 1"); 14 | } 15 | }; 16 | 17 | public static final Migration MIGRATION_2_3 = new Migration(2, 3) { 18 | @Override 19 | public void migrate(@NonNull SupportSQLiteDatabase database) { 20 | database.execSQL("ALTER TABLE seen_episode ADD COLUMN notified INTEGER NOT NULL DEFAULT 0"); 21 | } 22 | }; 23 | 24 | @DeleteTable.Entries({ 25 | @DeleteTable(tableName = "favorite_anime"), 26 | @DeleteTable(tableName = "seen_episode"), 27 | @DeleteTable(tableName = "seen_anime") 28 | }) 29 | public static class AUTO_MIGRATION_3_4 implements AutoMigrationSpec { 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/core/storage/database/tables/id_overlays/IdOverlays.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.core.storage.database.tables.id_overlays; 2 | 3 | import androidx.room.Entity; 4 | import androidx.room.Index; 5 | import androidx.room.PrimaryKey; 6 | 7 | 8 | @Entity( 9 | tableName = "id_overlays", 10 | indices = { 11 | @Index( 12 | value = "kitsuId", 13 | unique = true 14 | ), 15 | @Index( 16 | value = "malId", 17 | unique = true 18 | ), 19 | @Index( 20 | value = "aniId", 21 | unique = true 22 | ) 23 | } 24 | ) 25 | public class IdOverlays { 26 | @PrimaryKey(autoGenerate = true) public int id = 0; 27 | public long kitsuId; 28 | public long malId = 0; 29 | public long aniId = 0; 30 | 31 | public IdOverlays() { 32 | } 33 | 34 | public IdOverlays(long kitsuId, long malId, long anilistId) { 35 | this.kitsuId = kitsuId; 36 | this.malId = malId; 37 | this.aniId = anilistId; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/core/storage/database/tables/id_overlays/IdOverlaysDao.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.core.storage.database.tables.id_overlays; 2 | 3 | import androidx.room.Dao; 4 | import androidx.room.Delete; 5 | import androidx.room.Insert; 6 | import androidx.room.OnConflictStrategy; 7 | import androidx.room.Query; 8 | import androidx.room.Update; 9 | 10 | import java.util.List; 11 | 12 | 13 | @Dao 14 | public interface IdOverlaysDao { 15 | @Insert(onConflict = OnConflictStrategy.REPLACE) 16 | long insert(IdOverlays IdOverlays); 17 | @Insert 18 | long[] insertAll(IdOverlays... anime); 19 | @Insert 20 | long[] insertAll(List anime); 21 | @Update 22 | void update(IdOverlays IdOverlays); 23 | @Update 24 | void updateAll(IdOverlays... IdOverlays); 25 | @Update 26 | void updateAll(List IdOverlays); 27 | @Delete 28 | void delete(IdOverlays IdOverlays); 29 | @Query("DELETE FROM id_overlays") 30 | void deleteAll(); 31 | @Query("DELETE FROM id_overlays WHERE id=:id") 32 | void delete(int id); 33 | @Query("SELECT * FROM id_overlays WHERE id=:id") 34 | IdOverlays get(long id); 35 | @Query("SELECT * FROM id_overlays WHERE kitsuId=:id") 36 | IdOverlays getFromKitsu(long id); 37 | @Query("SELECT * FROM id_overlays WHERE aniId=:id") 38 | IdOverlays getFromAni(long id); 39 | @Query("SELECT * FROM id_overlays WHERE malId=:id") 40 | IdOverlays getFromMAL(long id); 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/core/storage/database/tables/saved_anime/SavedAnime.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.core.storage.database.tables.saved_anime; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.room.Embedded; 5 | import androidx.room.Entity; 6 | import androidx.room.Ignore; 7 | import androidx.room.Index; 8 | import androidx.room.PrimaryKey; 9 | 10 | import com.astarivi.kaizoyu.core.models.anime.LocalAnime; 11 | 12 | import org.jetbrains.annotations.Contract; 13 | 14 | 15 | @Entity( 16 | tableName = "saved_anime", 17 | indices = { 18 | @Index( 19 | value = "kitsuId", 20 | unique = true 21 | ), 22 | @Index( 23 | value = "list" 24 | ) 25 | } 26 | ) 27 | public class SavedAnime { 28 | @PrimaryKey(autoGenerate = true) public int id = 0; 29 | @Embedded 30 | public LocalAnime anime; 31 | public long updateDate; 32 | public int list; 33 | 34 | public SavedAnime() { 35 | } 36 | 37 | @Ignore 38 | @Contract(pure = true) 39 | public SavedAnime(@NonNull LocalAnime anime, long updateDate) { 40 | this.id = anime.getDbId(); 41 | this.anime = anime; 42 | this.list = anime.getLocalList().getValue(); 43 | this.updateDate = updateDate; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/core/storage/database/tables/saved_anime/SavedAnimeWithEpisodes.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.core.storage.database.tables.saved_anime; 2 | 3 | import androidx.room.Embedded; 4 | import androidx.room.Relation; 5 | 6 | import com.astarivi.kaizoyu.core.storage.database.tables.saved_episode.SavedEpisode; 7 | 8 | import java.util.List; 9 | 10 | 11 | public class SavedAnimeWithEpisodes { 12 | @Embedded 13 | public SavedAnime anime; 14 | @Relation( 15 | parentColumn = "kitsuId", 16 | entityColumn = "animeKitsuId" 17 | ) 18 | public List episodes; 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/core/storage/database/tables/saved_episode/SavedEpisode.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.core.storage.database.tables.saved_episode; 2 | 3 | import androidx.room.Embedded; 4 | import androidx.room.Entity; 5 | import androidx.room.ForeignKey; 6 | import androidx.room.Ignore; 7 | import androidx.room.Index; 8 | import androidx.room.PrimaryKey; 9 | 10 | import com.astarivi.kaizoyu.core.models.episode.LocalEpisode; 11 | import com.astarivi.kaizoyu.core.storage.database.tables.saved_anime.SavedAnime; 12 | 13 | import org.jetbrains.annotations.Contract; 14 | 15 | 16 | @Entity( 17 | tableName = "saved_episode", 18 | foreignKeys = @ForeignKey( 19 | entity = SavedAnime.class, 20 | parentColumns = "kitsuId", 21 | childColumns = "animeKitsuId", 22 | onDelete = ForeignKey.CASCADE 23 | ), 24 | indices = { 25 | @Index( 26 | value = "animeKitsuId" 27 | ) 28 | } 29 | ) 30 | public class SavedEpisode { 31 | @PrimaryKey(autoGenerate = true) public int id = 0; 32 | @Embedded 33 | public LocalEpisode episode; 34 | public long updateDate; 35 | 36 | public SavedEpisode() { 37 | } 38 | 39 | @Ignore 40 | @Contract(pure = true) 41 | public SavedEpisode(LocalEpisode episode, long updateDate) { 42 | this.id = episode.dbId; 43 | this.episode = episode; 44 | this.updateDate = updateDate; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/core/storage/database/tables/saved_episode/SavedEpisodeDao.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.core.storage.database.tables.saved_episode; 2 | 3 | import androidx.room.Dao; 4 | import androidx.room.Delete; 5 | import androidx.room.Insert; 6 | import androidx.room.OnConflictStrategy; 7 | import androidx.room.Query; 8 | import androidx.room.Update; 9 | 10 | import java.util.List; 11 | 12 | @Dao 13 | public interface SavedEpisodeDao { 14 | @Insert(onConflict = OnConflictStrategy.IGNORE) 15 | long insert(SavedEpisode episode); 16 | @Insert 17 | long[] insertAll(SavedEpisode... episode); 18 | @Update 19 | void update(SavedEpisode episode); 20 | @Update 21 | void updateAll(SavedEpisode... episode); 22 | @Delete 23 | void delete(SavedEpisode episode); 24 | @Query("SELECT * FROM saved_episode") 25 | List getAll(); 26 | @Query("SELECT * FROM saved_episode WHERE id=:id") 27 | SavedEpisode get(int id); 28 | @Query("SELECT * FROM saved_episode WHERE kitsuId=:id") 29 | SavedEpisode getByOwnKitsuId(long id); 30 | @Query("SELECT * FROM saved_episode WHERE animeKitsuId=:animeKitsuId") 31 | SavedEpisode getEpisodeByAnimeKitsuId(long animeKitsuId); 32 | @Query("SELECT * FROM saved_episode WHERE kitsuId=:ownKitsuId") 33 | SavedEpisode getEpisodeByOwnKitsuId(long ownKitsuId); 34 | @Query("SELECT * FROM saved_episode WHERE animeKitsuId=:animeKitsuId AND number=:episodeNumber") 35 | SavedEpisode getEpisodeWith(int animeKitsuId, int episodeNumber); 36 | @Query("DELETE FROM saved_episode WHERE kitsuId=:id") 37 | void deleteByOwnKitsuId(long id); 38 | 39 | @Query("SELECT EXISTS(SELECT 1 FROM saved_episode WHERE kitsuId=:id)") 40 | Boolean existsByKitsuId(long id); 41 | } -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/core/storage/database/tables/search_history/SearchHistory.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.core.storage.database.tables.search_history; 2 | 3 | import androidx.room.Entity; 4 | import androidx.room.PrimaryKey; 5 | 6 | 7 | @Entity( 8 | tableName = "search_history" 9 | ) 10 | public class SearchHistory { 11 | @PrimaryKey(autoGenerate = true) public int id = 0; 12 | public String searchTerm; 13 | public long date; 14 | 15 | public SearchHistory() { 16 | } 17 | 18 | public SearchHistory(String searchTerm, long creationTimestamp) { 19 | this.searchTerm = searchTerm; 20 | this.date = creationTimestamp; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/core/storage/database/tables/search_history/SearchHistoryDao.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.core.storage.database.tables.search_history; 2 | 3 | import androidx.room.Dao; 4 | import androidx.room.Delete; 5 | import androidx.room.Insert; 6 | import androidx.room.OnConflictStrategy; 7 | import androidx.room.Query; 8 | import androidx.room.Update; 9 | 10 | import java.util.List; 11 | 12 | 13 | @Dao 14 | public interface SearchHistoryDao { 15 | @Insert(onConflict = OnConflictStrategy.REPLACE) 16 | long insert(SearchHistory searchHistory); 17 | @Insert 18 | long[] insertAll(SearchHistory... anime); 19 | @Insert 20 | long[] insertAll(List anime); 21 | @Update 22 | void update(SearchHistory searchHistory); 23 | @Update 24 | void updateAll(SearchHistory... searchHistory); 25 | @Update 26 | void updateAll(List searchHistory); 27 | @Delete 28 | void delete(SearchHistory searchHistory); 29 | @Query("DELETE FROM search_history") 30 | void deleteAll(); 31 | @Query("DELETE FROM search_history WHERE id=:id") 32 | void delete(int id); 33 | @Query("SELECT * FROM search_history ORDER BY date DESC") 34 | List getAll(); 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/core/storage/ids/AnimeIds.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.core.storage.ids; 2 | 3 | public record AnimeIds(long kitsuId, Long aniListId, Long malId) { 4 | } 5 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/core/theme/AppCompatActivityTheme.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.core.theme; 2 | 3 | import android.os.Bundle; 4 | 5 | import androidx.annotation.Nullable; 6 | import androidx.appcompat.app.AppCompatActivity; 7 | import androidx.appcompat.app.AppCompatDelegate; 8 | 9 | 10 | public abstract class AppCompatActivityTheme extends AppCompatActivity { 11 | @Override 12 | protected void onCreate(@Nullable Bundle savedInstanceState) { 13 | Theme theme = Theme.getCurrentTheme(); 14 | 15 | setTheme( 16 | theme.getTheme() 17 | ); 18 | 19 | if (theme == Theme.HIGH_CONTRAST) { 20 | AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); 21 | } else if (theme == Theme.KITSUNE_TAKEOVER) { 22 | AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); 23 | } 24 | 25 | super.onCreate(savedInstanceState); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/core/theme/Colors.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.core.theme; 2 | 3 | import android.graphics.Color; 4 | import android.graphics.drawable.GradientDrawable; 5 | import android.view.View; 6 | 7 | import androidx.annotation.AttrRes; 8 | import androidx.annotation.ColorInt; 9 | import androidx.core.graphics.ColorUtils; 10 | 11 | import com.google.android.material.color.MaterialColors; 12 | 13 | 14 | public class Colors { 15 | public static @ColorInt int getColorFromString(String string, float saturation, float value) { 16 | return Color.HSVToColor(new float[]{ 17 | (float) Math.abs(string.hashCode() % 360), 18 | saturation, 19 | value 20 | }); 21 | } 22 | 23 | public static @ColorInt int getSemiTransparentStatusBar(View source, @AttrRes int color) { 24 | return ColorUtils.setAlphaComponent( 25 | MaterialColors.getColor( 26 | source, 27 | color 28 | ), 29 | 153 30 | ); 31 | } 32 | 33 | public static @ColorInt int getColorScrim(View source, @AttrRes int color) { 34 | return ColorUtils.setAlphaComponent( 35 | MaterialColors.getColor( 36 | source, 37 | color 38 | ), 39 | 200 40 | ); 41 | } 42 | 43 | public static GradientDrawable fadeSurfaceFromStatusBar(View source, @AttrRes int color, GradientDrawable.Orientation orientation) { 44 | GradientDrawable gd = new GradientDrawable( 45 | orientation, 46 | new int[]{ 47 | getSemiTransparentStatusBar(source, color), 48 | ColorUtils.setAlphaComponent( 49 | MaterialColors.getColor( 50 | source, 51 | color 52 | ), 53 | 0 54 | ) 55 | } 56 | ); 57 | 58 | gd.setCornerRadius(0f); 59 | 60 | return gd; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/core/threading/workers/WorkerInitializers.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.core.threading.workers; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.work.Constraints; 6 | import androidx.work.ExistingPeriodicWorkPolicy; 7 | import androidx.work.NetworkType; 8 | import androidx.work.PeriodicWorkRequest; 9 | import androidx.work.WorkManager; 10 | 11 | import org.acra.ACRA; 12 | 13 | import java.util.concurrent.TimeUnit; 14 | 15 | 16 | public class WorkerInitializers { 17 | public static void queueWorkers(Context context) { 18 | if (ACRA.isACRASenderServiceProcess()) return; 19 | 20 | PeriodicWorkRequest updateWorkRequest = new PeriodicWorkRequest.Builder( 21 | UpdatePeriodicWorker.class, 22 | 24, 23 | TimeUnit.HOURS, 24 | 6, 25 | TimeUnit.HOURS 26 | ).setConstraints( 27 | new Constraints.Builder() 28 | .setRequiresDeviceIdle(false) 29 | .setRequiredNetworkType(NetworkType.CONNECTED) 30 | .build() 31 | ).build(); 32 | 33 | PeriodicWorkRequest episodeWorkRequest = new PeriodicWorkRequest.Builder( 34 | EpisodePeriodicWorker.class, 35 | 6, 36 | TimeUnit.HOURS, 37 | 2, 38 | TimeUnit.HOURS 39 | ).setConstraints( 40 | new Constraints.Builder() 41 | .setRequiresDeviceIdle(false) 42 | .setRequiredNetworkType(NetworkType.UNMETERED) 43 | .setRequiresBatteryNotLow(true) 44 | .build() 45 | ).build(); 46 | 47 | WorkManager workManager = WorkManager.getInstance(context); 48 | 49 | workManager.enqueueUniquePeriodicWork( 50 | "update_task", 51 | ExistingPeriodicWorkPolicy.UPDATE, 52 | updateWorkRequest 53 | ); 54 | 55 | workManager.enqueueUniquePeriodicWork( 56 | "episode_task", 57 | ExistingPeriodicWorkPolicy.UPDATE, 58 | episodeWorkRequest 59 | ); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/core/video/VideoQuality.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.core.video; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | 6 | public enum VideoQuality { 7 | FHD ("1080p"), 8 | UHD ("2160p"), 9 | HD ("720p"), 10 | ISD ("540p"), 11 | FSD ("480p"), 12 | SD ("360p"), 13 | DVD ("DVD"), 14 | UNKNOWN ("Unknown"); 15 | 16 | private final String quality; 17 | 18 | VideoQuality(String qt) { 19 | quality = qt; 20 | } 21 | 22 | @NonNull 23 | @Override 24 | public String toString() { 25 | return quality; 26 | } 27 | } -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/details/DetailsUtils.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.details; 2 | 3 | import android.content.Context; 4 | 5 | import com.astarivi.kaizolib.kitsu.exception.NetworkConnectionException; 6 | import com.astarivi.kaizoyu.core.adapters.HttpFileDownloader; 7 | import com.astarivi.kaizoyu.core.common.ThreadedOnly; 8 | import com.astarivi.kaizoyu.core.models.base.AnimeBasicInfo; 9 | 10 | import org.tinylog.Logger; 11 | 12 | import java.io.File; 13 | import java.io.IOException; 14 | 15 | 16 | public class DetailsUtils { 17 | @ThreadedOnly 18 | public static File downloadImage(Context context, AnimeBasicInfo anime, AnimeBasicInfo.ImageType type) throws NetworkConnectionException, IOException { 19 | String url = anime.getImageURLorFallback(type, AnimeBasicInfo.ImageSize.ORIGINAL); 20 | 21 | if (url == null) { 22 | throw new IllegalArgumentException("Argument 'anime' contains no URL for this type"); 23 | } 24 | 25 | final String[] filename = url.split("/"); 26 | 27 | HttpFileDownloader downloader = new HttpFileDownloader( 28 | url, 29 | new File(context.getCacheDir(), filename[filename.length-1]) 30 | ); 31 | 32 | try { 33 | return downloader.download(); 34 | } catch (IOException | NetworkConnectionException e) { 35 | Logger.error("Error downloading external thumbnail"); 36 | Logger.error(e); 37 | throw e; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/details/gui/AnimeInfoViewModel.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.details.gui; 2 | 3 | import androidx.lifecycle.ViewModel; 4 | 5 | import com.astarivi.kaizoyu.core.models.base.AnimeBasicInfo; 6 | 7 | 8 | // Unused, for now. 9 | public class AnimeInfoViewModel extends ViewModel { 10 | // @Getter 11 | // private final MutableLiveData> categories = new MutableLiveData<>(); 12 | // private Future fetchingFuture = null; 13 | 14 | // FIXME: Enable this 15 | public void initialize(AnimeBasicInfo anime) { 16 | // final int kitsuId = Integer.parseInt(anime.getAniListAnime().id); 17 | // 18 | // fetchingFuture = Threading.submitTask(Threading.TASK.INSTANT, () -> { 19 | // UserHttpClient userHttpClient = Data.getUserHttpClient(); 20 | // 21 | // KitsuRelations relations = new KitsuRelations(userHttpClient); 22 | // 23 | // try { 24 | // categories.postValue(relations.getKitsuCategories(kitsuId)); 25 | // // Relations aren't critical, they're optional pieces of data. 26 | // } catch (Exception ignored) { 27 | // } 28 | // }); 29 | } 30 | 31 | @Override 32 | protected void onCleared() { 33 | destroy(); 34 | 35 | super.onCleared(); 36 | } 37 | 38 | public void destroy() { 39 | // if (fetchingFuture != null && !fetchingFuture.isDone()) { 40 | // fetchingFuture.cancel(true); 41 | // } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/details/gui/adapters/DetailsTabAdapter.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.details.gui.adapters; 2 | 3 | import android.os.Bundle; 4 | 5 | import androidx.annotation.NonNull; 6 | import androidx.appcompat.app.AppCompatActivity; 7 | import androidx.fragment.app.Fragment; 8 | import androidx.viewpager2.adapter.FragmentStateAdapter; 9 | 10 | import com.astarivi.kaizoyu.details.gui.AnimeEpisodesFragment; 11 | import com.astarivi.kaizoyu.details.gui.AnimeInfoFragment; 12 | 13 | 14 | public class DetailsTabAdapter extends FragmentStateAdapter { 15 | private final Bundle bundle; 16 | 17 | public DetailsTabAdapter(AppCompatActivity activity, Bundle arguments) { 18 | super(activity); 19 | this.bundle = arguments; 20 | } 21 | 22 | @NonNull 23 | @Override 24 | public Fragment createFragment(int position) { 25 | Fragment fragment; 26 | 27 | if (position == 0) { 28 | fragment = new AnimeInfoFragment(); 29 | } else { 30 | fragment = new AnimeEpisodesFragment(); 31 | } 32 | 33 | fragment.setArguments(bundle); 34 | 35 | return fragment; 36 | } 37 | 38 | @Override 39 | public int getItemCount() { 40 | return 2; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/gui/NotificationModalBottomSheet.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.gui; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | 8 | import androidx.annotation.NonNull; 9 | import androidx.annotation.Nullable; 10 | 11 | import com.astarivi.kaizoyu.databinding.BottomSheetNotificationBinding; 12 | import com.google.android.material.bottomsheet.BottomSheetDialogFragment; 13 | 14 | 15 | public class NotificationModalBottomSheet extends BottomSheetDialogFragment { 16 | public static final String TAG = "NotificationBottomModalSheet"; 17 | private BottomSheetNotificationBinding binding; 18 | private Callback callback = null; 19 | 20 | public NotificationModalBottomSheet() { 21 | } 22 | public NotificationModalBottomSheet(Callback c) { 23 | callback = c; 24 | } 25 | 26 | @Nullable 27 | @Override 28 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 29 | binding = BottomSheetNotificationBinding.inflate(inflater, container, false); 30 | return binding.getRoot(); 31 | } 32 | 33 | @Override 34 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 35 | binding.denyButton.setOnClickListener(v -> dismiss()); 36 | binding.confirmButton.setOnClickListener(v -> { 37 | dismiss(); 38 | if (callback != null) callback.onSuccess(); 39 | }); 40 | } 41 | 42 | public interface Callback { 43 | void onSuccess(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/gui/adapters/BackInterceptAdapter.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.gui.adapters; 2 | 3 | public interface BackInterceptAdapter { 4 | boolean shouldFragmentInterceptBack(); 5 | } 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/gui/adapters/TabAdapter.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.gui.adapters; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.appcompat.app.AppCompatActivity; 5 | import androidx.fragment.app.Fragment; 6 | import androidx.viewpager2.adapter.FragmentStateAdapter; 7 | 8 | import com.astarivi.kaizoyu.gui.home.HomeFragment; 9 | import com.astarivi.kaizoyu.gui.library.LibraryFragment; 10 | import com.astarivi.kaizoyu.gui.more.MoreFragment; 11 | import com.astarivi.kaizoyu.gui.schedule.ScheduleFragment; 12 | 13 | 14 | public class TabAdapter extends FragmentStateAdapter { 15 | public TabAdapter(AppCompatActivity activity) { 16 | super(activity); 17 | } 18 | 19 | @NonNull 20 | @Override 21 | public Fragment createFragment(int position) { 22 | return switch (position) { 23 | case 0 -> new HomeFragment(); 24 | case 1 -> new ScheduleFragment(); 25 | case 2 -> new LibraryFragment(); 26 | default -> new MoreFragment(); 27 | }; 28 | } 29 | 30 | @Override 31 | public int getItemCount() { 32 | return 4; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/gui/home/recycler/recommendations/HomePagingAdapter.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.gui.home.recycler.recommendations; 2 | 3 | import android.view.View; 4 | import android.view.ViewGroup; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.core.util.Consumer; 8 | 9 | import com.astarivi.kaizoyu.core.adapters.AnimePagingAdapter; 10 | import com.astarivi.kaizoyu.core.adapters.AnimeViewHolder; 11 | import com.astarivi.kaizoyu.core.models.anime.RemoteAnime; 12 | import com.astarivi.kaizoyu.utils.Translation; 13 | import com.astarivi.kaizoyu.utils.Utils; 14 | 15 | 16 | public class HomePagingAdapter extends AnimePagingAdapter, RemoteAnime> { 17 | public HomePagingAdapter(Consumer itemClickListener) { 18 | super(itemClickListener); 19 | } 20 | 21 | @Override 22 | public AnimeViewHolder onCreateViewHolderStarted(@NonNull ViewGroup parent, int viewType) { 23 | return new AnimeViewHolder<>(inflateView(parent), true); 24 | } 25 | 26 | @Override 27 | public void onBindViewHolderStarted(@NonNull AnimeViewHolder holder, int position, RemoteAnime anime) { 28 | String animeStartDate = anime.getInternal().attributes.startDate; 29 | 30 | if (animeStartDate != null && !animeStartDate.isEmpty()) { 31 | holder.binding.launchDate.setText( 32 | Utils.getDateAsQuarters(animeStartDate) 33 | ); 34 | } else { 35 | holder.binding.launchDate.setVisibility(View.GONE); 36 | } 37 | 38 | holder.binding.subtype.setText( 39 | Translation.getSubTypeTranslation( 40 | anime.getInternal().attributes.subtype, 41 | holder.binding.getRoot().getContext() 42 | ) 43 | ); 44 | 45 | holder.binding.schedule.setVisibility(View.GONE); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/gui/library/watching/adapter/SharedLibraryPagingAdapter.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.gui.library.watching.adapter; 2 | 3 | import android.view.View; 4 | import android.view.ViewGroup; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.core.util.Consumer; 8 | 9 | import com.astarivi.kaizoyu.core.adapters.AnimePagingAdapter; 10 | import com.astarivi.kaizoyu.core.adapters.AnimeViewHolder; 11 | import com.astarivi.kaizoyu.core.models.anime.LocalAnime; 12 | import com.astarivi.kaizoyu.utils.Translation; 13 | 14 | 15 | public class SharedLibraryPagingAdapter extends AnimePagingAdapter, LocalAnime> { 16 | public SharedLibraryPagingAdapter(Consumer itemClickListener) { 17 | super(itemClickListener); 18 | } 19 | 20 | @Override 21 | public AnimeViewHolder onCreateViewHolderStarted(@NonNull ViewGroup parent, int viewType) { 22 | return new AnimeViewHolder<>(inflateView(parent), false); 23 | } 24 | 25 | @Override 26 | public void onBindViewHolderStarted(@NonNull AnimeViewHolder holder, int position, LocalAnime localAnime) { 27 | holder.binding.launchDate.setVisibility(View.GONE); 28 | 29 | assert localAnime.getSubtype() != null; 30 | holder.binding.subtype.setText( 31 | Translation.getSubTypeTranslation( 32 | localAnime.getSubtype(), 33 | holder.binding.getRoot().getContext() 34 | ) 35 | ); 36 | 37 | holder.binding.schedule.setVisibility(View.GONE); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/gui/schedule/recycler/ScheduleRecyclerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.gui.schedule.recycler; 2 | 3 | import android.view.View; 4 | import android.view.ViewGroup; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.core.util.Consumer; 8 | 9 | import com.astarivi.kaizoyu.core.adapters.AnimeRecyclerAdapter; 10 | import com.astarivi.kaizoyu.core.adapters.AnimeViewHolder; 11 | import com.astarivi.kaizoyu.core.models.anime.SeasonalAnime; 12 | import com.astarivi.kaizoyu.utils.Translation; 13 | 14 | 15 | public class ScheduleRecyclerAdapter extends AnimeRecyclerAdapter, SeasonalAnime> { 16 | 17 | public ScheduleRecyclerAdapter(Consumer itemClickListener) { 18 | super(itemClickListener); 19 | } 20 | 21 | @Override 22 | public AnimeViewHolder onCreateViewHolderStarted(@NonNull ViewGroup parent, int viewType) { 23 | return new AnimeViewHolder<>(inflateView(parent), true); 24 | } 25 | 26 | @Override 27 | public void onBindViewHolderStarted(@NonNull AnimeViewHolder holder, int position, SeasonalAnime seasonalAnime) { 28 | holder.binding.launchDate.setVisibility(View.GONE); 29 | 30 | holder.binding.subtype.setText( 31 | Translation.getSubTypeTranslation( 32 | seasonalAnime.getInternal().attributes.subtype, 33 | holder.binding.getRoot().getContext() 34 | ) 35 | ); 36 | 37 | holder.binding.schedule.setText( 38 | seasonalAnime.getEmissionTime() 39 | ); 40 | 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/search/SearchViewModel.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.search; 2 | 3 | import androidx.lifecycle.LiveData; 4 | import androidx.lifecycle.MutableLiveData; 5 | import androidx.lifecycle.Transformations; 6 | import androidx.lifecycle.ViewModel; 7 | import androidx.lifecycle.ViewModelKt; 8 | import androidx.paging.Pager; 9 | import androidx.paging.PagingConfig; 10 | import androidx.paging.PagingData; 11 | import androidx.paging.PagingLiveData; 12 | 13 | import com.astarivi.kaizoyu.core.models.anime.RemoteAnime; 14 | import com.astarivi.kaizoyu.search.recycler.SearchFuturePagingSource; 15 | 16 | import org.jetbrains.annotations.NotNull; 17 | 18 | import kotlinx.coroutines.CoroutineScope; 19 | import lombok.Getter; 20 | 21 | 22 | public class SearchViewModel extends ViewModel { 23 | @Getter 24 | private final LiveData> results; 25 | private final MutableLiveData queryLiveData = new MutableLiveData<>(); 26 | private boolean isSearchActive = false; 27 | 28 | public SearchViewModel() { 29 | results = Transformations.switchMap(queryLiveData, query -> { 30 | CoroutineScope viewModelScope = ViewModelKt.getViewModelScope(this); 31 | PagingConfig pagingConfig = new PagingConfig(20, 20, true, 20); 32 | 33 | Pager pager = new Pager<>( 34 | pagingConfig, 35 | () -> new SearchFuturePagingSource(query) 36 | ); 37 | 38 | return PagingLiveData.cachedIn(PagingLiveData.getLiveData(pager), viewModelScope); 39 | }); 40 | } 41 | 42 | public boolean hasSearch() { 43 | return results.getValue() != null; 44 | } 45 | 46 | public boolean hasOptedOutOfSearch() { 47 | return !isSearchActive; 48 | } 49 | 50 | public void optOutOfSearch() { 51 | isSearchActive = false; 52 | } 53 | 54 | public void searchAnime(@NotNull String search) { 55 | isSearchActive = true; 56 | queryLiveData.postValue(search); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/search/recycler/SearchPagingAdapter.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.search.recycler; 2 | 3 | import android.view.View; 4 | import android.view.ViewGroup; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.core.util.Consumer; 8 | 9 | import com.astarivi.kaizoyu.core.adapters.AnimePagingAdapter; 10 | import com.astarivi.kaizoyu.core.adapters.AnimeViewHolder; 11 | import com.astarivi.kaizoyu.core.models.anime.RemoteAnime; 12 | import com.astarivi.kaizoyu.utils.Translation; 13 | import com.astarivi.kaizoyu.utils.Utils; 14 | 15 | 16 | public class SearchPagingAdapter extends AnimePagingAdapter, RemoteAnime> { 17 | public SearchPagingAdapter(Consumer itemClickListener) { 18 | super(itemClickListener); 19 | } 20 | 21 | @Override 22 | public AnimeViewHolder onCreateViewHolderStarted(@NonNull ViewGroup parent, int viewType) { 23 | return new AnimeViewHolder<>(inflateView(parent), true); 24 | } 25 | 26 | @Override 27 | public void onBindViewHolderStarted(@NonNull AnimeViewHolder holder, int position, RemoteAnime anime) { 28 | String animeStartDate = anime.getInternal().attributes.startDate; 29 | 30 | if (animeStartDate != null && !animeStartDate.isEmpty()) { 31 | holder.binding.launchDate.setText( 32 | Utils.getDateAsQuarters(animeStartDate) 33 | ); 34 | } else { 35 | holder.binding.launchDate.setVisibility(View.GONE); 36 | } 37 | 38 | holder.binding.subtype.setText( 39 | Translation.getSubTypeTranslation( 40 | anime.getInternal().attributes.subtype, 41 | holder.binding.getRoot().getContext() 42 | ) 43 | ); 44 | 45 | holder.binding.schedule.setVisibility(View.GONE); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/utils/Data.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.utils; 2 | 3 | import com.astarivi.kaizolib.common.network.UserHttpClient; 4 | import com.astarivi.kaizoyu.core.storage.PersistenceRepository; 5 | import com.astarivi.kaizoyu.core.storage.database.AppDatabase; 6 | import com.astarivi.kaizoyu.core.storage.properties.ExtendedProperties; 7 | 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | import lombok.Getter; 11 | import lombok.Setter; 12 | 13 | 14 | public class Data { 15 | @Getter 16 | private static final TemporarySwitches temporarySwitches = new TemporarySwitches(); 17 | 18 | public static UserHttpClient getUserHttpClient() { 19 | return PersistenceRepository.getInstance().getHttpClient(); 20 | } 21 | 22 | public static ExtendedProperties getProperties(@NotNull CONFIGURATION type) { 23 | switch (type) { 24 | case BOTS: 25 | return PersistenceRepository.getInstance().getBotsConfiguration(); 26 | case APP: 27 | default: 28 | return PersistenceRepository.getInstance().getAppConfiguration(); 29 | } 30 | } 31 | 32 | public static void reloadProperties() { 33 | PersistenceRepository.getInstance().applyConfigurationChanges(); 34 | } 35 | 36 | public static AppDatabase getDatabase() { 37 | return PersistenceRepository.getInstance().getDatabase(); 38 | } 39 | 40 | public static boolean isDeviceLowSpec() { 41 | return PersistenceRepository.getInstance().isDeviceLowSpec(); 42 | } 43 | 44 | public enum CONFIGURATION { 45 | APP, BOTS 46 | } 47 | 48 | @Getter 49 | @Setter 50 | public static class TemporarySwitches { 51 | private boolean pendingFavoritesRefresh = false; 52 | private boolean pendingSeenEpisodeStateRefresh = false; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/utils/MathUtils.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.utils; 2 | 3 | public class MathUtils { 4 | public static float lerp(float startValue, float endValue, float fraction) { 5 | return startValue + (fraction * (endValue - startValue)); 6 | } 7 | 8 | public static float lerp( 9 | float outputMax, float outputMin, float inputMin, float inputMax, float value) { 10 | if (value <= inputMin) { 11 | return outputMax; 12 | } 13 | if (value >= inputMax) { 14 | return outputMin; 15 | } 16 | 17 | return lerp(outputMax, outputMin, (value - inputMin) / (inputMax - inputMin)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/utils/Translation.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.utils; 2 | 3 | import android.content.Context; 4 | 5 | import com.astarivi.kaizoyu.R; 6 | 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.time.DayOfWeek; 10 | 11 | 12 | public class Translation { 13 | public static @NotNull String getSubTypeTranslation(@NotNull String subtype, @NotNull Context context) { 14 | return switch (subtype) { 15 | case "ONA" -> context.getResources().getString(R.string.type_ona); 16 | case "OVA" -> context.getResources().getString(R.string.type_ova); 17 | case "TV" -> context.getResources().getString(R.string.type_tv); 18 | case "MOVIE" -> context.getResources().getString(R.string.type_movie); 19 | case "SPECIAL" -> context.getResources().getString(R.string.type_special); 20 | default -> context.getResources().getString(R.string.type_music); 21 | }; 22 | } 23 | 24 | public static String getNightThemeTranslation(int option, Context context) { 25 | return switch (option) { 26 | case 0 -> context.getResources().getString(R.string.night_theme_default); 27 | case 1 -> context.getResources().getString(R.string.night_theme_day); 28 | default -> context.getResources().getString(R.string.night_theme_night); 29 | }; 30 | } 31 | 32 | public static String getLocalizedDow(DayOfWeek dow, Context context) { 33 | return switch (dow) { 34 | case MONDAY -> context.getResources().getString(R.string.monday); 35 | case TUESDAY -> context.getResources().getString(R.string.tuesday); 36 | case WEDNESDAY -> context.getResources().getString(R.string.wednesday); 37 | case THURSDAY -> context.getResources().getString(R.string.thursday); 38 | case FRIDAY -> context.getResources().getString(R.string.friday); 39 | case SATURDAY -> context.getResources().getString(R.string.saturday); 40 | default -> context.getResources().getString(R.string.sunday); 41 | }; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/java/com/astarivi/kaizoyu/video/utils/BundleUtils.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizoyu.video.utils; 2 | 3 | import android.os.Build; 4 | import android.os.Bundle; 5 | 6 | import com.astarivi.kaizoyu.core.models.Result; 7 | import com.astarivi.kaizoyu.core.models.base.EpisodeBasicInfo; 8 | import com.astarivi.kaizoyu.core.models.episode.LocalEpisode; 9 | import com.astarivi.kaizoyu.core.models.episode.RemoteEpisode; 10 | 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | 15 | public class BundleUtils { 16 | @SuppressWarnings("deprecation") 17 | public static @Nullable Result getResultFromBundle(@NotNull Bundle bundle) { 18 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { 19 | return bundle.getParcelable("result", Result.class); 20 | } 21 | return bundle.getParcelable("result"); 22 | } 23 | 24 | @SuppressWarnings("deprecation") 25 | public static @Nullable EpisodeBasicInfo getEpisodeFromBundle(@NotNull Bundle bundle, @NotNull EpisodeBasicInfo.EpisodeType type) { 26 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { 27 | return bundle.getParcelable("episode"); 28 | } 29 | 30 | return switch (type) { 31 | case REMOTE -> bundle.getParcelable("episode", RemoteEpisode.class); 32 | default -> bundle.getParcelable("episode", LocalEpisode.class); 33 | }; 34 | } 35 | 36 | public enum PictureInPictureAction { 37 | PAUSE_OR_RESUME, 38 | REWIND_TEN, 39 | FORWARD_TEN 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/res/color/chip_background_state_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/color/chip_text_state_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/color/main_tab_layout_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_stat_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/main/res/drawable-hdpi/ic_stat_name.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_stat_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/main/res/drawable-mdpi/ic_stat_name.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_stat_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/main/res/drawable-xhdpi/ic_stat_name.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_stat_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/main/res/drawable-xxhdpi/ic_stat_name.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_stat_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/main/res/drawable-xxxhdpi/ic_stat_name.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_details_lower_fade.xml: -------------------------------------------------------------------------------- 1 | 5 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_details_upper_fade.xml: -------------------------------------------------------------------------------- 1 | 5 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_item_lower_fade.xml: -------------------------------------------------------------------------------- 1 | 5 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_item_upper_fade.xml: -------------------------------------------------------------------------------- 1 | 5 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_player_left_fade.xml: -------------------------------------------------------------------------------- 1 | 5 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_player_lower_fade.xml: -------------------------------------------------------------------------------- 1 | 5 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_player_right_fade.xml: -------------------------------------------------------------------------------- 1 | 5 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_rounded_all.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_rounded_upper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/empty_divider.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/flag_jp.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/main/res/drawable/flag_jp.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable/flag_us.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/main/res/drawable/flag_us.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable/flag_us_jp.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/main/res/drawable/flag_us_jp.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_advanced_bot.xml: -------------------------------------------------------------------------------- 1 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_advanced_extension.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_advanced_quality.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_advanced_size.xml: -------------------------------------------------------------------------------- 1 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_back_new.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_back_new_small.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_check.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_close.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_details_add.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_details_added.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_details_feedback.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_details_share.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_details_stars.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_favorite.xml: -------------------------------------------------------------------------------- 1 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_favorite_active.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_general_placeholder.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_general_tips.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_home_settings.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_main_collection.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_main_emission_alt.xml: -------------------------------------------------------------------------------- 1 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_main_home_alt.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_main_more_alt.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_pip_forward.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_pip_pause.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_pip_play.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_pip_rewind.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_player_hide.xml: -------------------------------------------------------------------------------- 1 | 8 | 12 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_resume.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_schedule_time.xml: -------------------------------------------------------------------------------- 1 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_search_advanced.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_search_date.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_search_history.xml: -------------------------------------------------------------------------------- 1 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_search_subtype.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_settings_discord.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_settings_github.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_skip_intro.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_stop.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_subtitles.xml: -------------------------------------------------------------------------------- 1 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_top.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/player_timestamp.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/progress_circle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | 17 | 18 | 19 | 20 | 21 | 26 | 27 | 32 | 33 | 34 | 40 | 41 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/search_noresults.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/main/res/drawable/search_noresults.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable/seekbar_thumb.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/seekbar_track_fade.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/font/lato_black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/main/res/font/lato_black.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/lato_bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/main/res/font/lato_bold.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/lato_light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/main/res/font/lato_light.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/lato_regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/main/res/font/lato_regular.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/monomaniacone_regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/main/res/font/monomaniacone_regular.ttf -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_updater.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 24 | 25 | 38 | -------------------------------------------------------------------------------- /app/src/main/res/layout/bottom_sheet_generic.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 15 | 16 | 28 | 29 | 33 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /app/src/main/res/layout/component_suggestion_chip.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_search_suggestion.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 22 | 23 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_chip_category.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_sheet_generic.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 19 | 30 | 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/res/layout/player_track_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 25 | 38 | -------------------------------------------------------------------------------- /app/src/main/res/menu/search_bar_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/raw/an_horizontal_loading.lottie: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/app/src/main/res/raw/an_horizontal_loading.lottie -------------------------------------------------------------------------------- /app/src/main/res/values-night-v31/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/values-v31/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings_anime.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | ONA 4 | OVA 5 | TV 6 | Movie 7 | Special 8 | Unsupported 9 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings_dow.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Monday 4 | Tuesday 5 | Wednesday 6 | Thursday 7 | Friday 8 | Saturday 9 | Sunday 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings_errors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | No results found for your request, please try again later 4 | There was a network error while processing your request, please try again later 5 | There was an error reading the response for your request, please try again later 6 | There is something wrong with your lists database. Back it up asap. 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings_notifications.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Thank you, and enjoy your stay! 4 | Platform related 5 | Content related 6 | App updates 7 | Checks for app version updates at a daily basis. 8 | New episodes 9 | Checks for new episodes for shows you\'re currently watching. 10 | Reports feedback 11 | Notifies about responses, and/or resolutions to submitted reports. 12 | New Kaizoyu! update available 13 | Update %s is out now and ready to be downloaded and installed! 14 | Ep %d of %s is out! 15 | Watch it now in Kaizoyu! 16 | New episode available for a show you\'re following 17 | Episode %d of %s\n 18 | Is out now, watch it now in Kaizoyu! 19 | Watch them now in Kaizoyu! 20 | New episodes available for several shows you\'re following 21 | There are %d new episodes available. 22 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings_player.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Starting download 4 | Subtitles 5 | Audio 6 | Episode 7 | Disable subtitles 8 | Preparing handshake 9 | Performing handshake 10 | Downloading initial buffer 11 | Playing in Advanced Mode. Progress won\'t be saved 12 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings_search.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Sorry, we found no matches for your\nsearch. Try checking your spelling\nor searching for other related works. 4 | Sorry, no results were found for this episode. 5 | We\'re already searching for an episode, please wait… 6 | We\'re already searching for an anime, please wait… 7 | Search online anime library… 8 | Search for XDCC files… 9 | Searches should be of at least %d characters. 10 | Entering Advanced Search 11 | You\'re about to enter Advanced Search mode. This mode allows you to search the full online library freely, but with a big trade-off. While using this mode, many cool features are disabled, and no watch history or favorites are saved. There\'s also little to no filtering of results. Be careful. This reminder will never be displayed again. 12 | I know what I\'m doing 13 | Every journey begins with a single step… 14 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 18 | 19 | 22 | 23 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/xml/shared_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 11 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/resources/tinylog.properties: -------------------------------------------------------------------------------- 1 | writer = logcat 2 | writer.level = trace 3 | writer.tagname = KaizoyuApp 4 | 5 | writer2 = file 6 | writer2.level = trace 7 | writer2.file = #{tinylog.directory}/log.txt -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | buildscript { 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:8.10.0' 9 | classpath 'com.google.android.gms:oss-licenses-plugin:0.10.6' 10 | 11 | // NOTE: Do not place your application dependencies here; they belong 12 | // in the individual module build.gradle files 13 | } 14 | } 15 | 16 | allprojects { 17 | repositories { 18 | google() 19 | mavenCentral() 20 | } 21 | 22 | gradle.projectsEvaluated { 23 | tasks.withType(JavaCompile) { 24 | options.compilerArgs << "-Xlint:deprecation" 25 | } 26 | } 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=false 20 | android.defaults.buildfeatures.buildconfig=true 21 | android.nonTransitiveRClass=false 22 | android.nonFinalResIds=false -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Jul 27 17:16:03 COT 2021 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /jitpack.yml: -------------------------------------------------------------------------------- 1 | jdk: 2 | - oraclejdk17 3 | before_install: 4 | - chmod +x gradlew 5 | install: 6 | - ./gradlew service:publishToMavenLocal -------------------------------------------------------------------------------- /media/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/media/1.png -------------------------------------------------------------------------------- /media/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/media/2.png -------------------------------------------------------------------------------- /media/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/media/3.png -------------------------------------------------------------------------------- /media/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/media/4.png -------------------------------------------------------------------------------- /media/comparison.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/media/comparison.png -------------------------------------------------------------------------------- /media/small-cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astarivi/Kaizoyu/053f9ae2770862e9c045347253dccf89ff853ec6/media/small-cover.png -------------------------------------------------------------------------------- /service/.gitignore: -------------------------------------------------------------------------------- 1 | /project 2 | /target 3 | *.class 4 | hs_err_* 5 | *.iml 6 | .idea/ 7 | build/ 8 | out/ 9 | *.log 10 | *.tar 11 | .gradle 12 | .classpath 13 | .project 14 | .settings 15 | .vscode -------------------------------------------------------------------------------- /service/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 astarivi 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /service/README.md: -------------------------------------------------------------------------------- 1 | # KaizoLib 2 | Java API for the Kaizoyu! app. 3 | 4 | ## Contents 5 | Everything that has to do with XDCC, Kitsu, or Nibl, is found here. 6 | 7 | ## Features 8 | - T-XDCC support 9 | - IRC DCC handshake support (Compliant to RFC1459) 10 | - IRC TLS/SSL (in Rizon network) 11 | - Nibl search API 12 | - Kitsu search API 13 | - Common model shared between modules 14 | - Android compatible 15 | - XDCC IPv6 Support -------------------------------------------------------------------------------- /service/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'KaizoLib' 2 | gpr.user = 'astarivi' 3 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/anilist/AniList.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.anilist; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | import java.util.TreeMap; 6 | 7 | 8 | @Deprecated 9 | public class AniList extends AniListCommon { 10 | public static @NotNull AniListQuery.Single get(long id){ 11 | TreeMap variables = new TreeMap<>(); 12 | variables.put("id", id); 13 | 14 | GraphQLRequest graphQLRequest = new GraphQLRequest( 15 | ANIME_QUERY_BY_ID, 16 | variables 17 | ); 18 | 19 | return new AniListQuery.Single(graphQLRequest); 20 | } 21 | 22 | public static @NotNull AniListQuery.Paged search(String title) { 23 | TreeMap variables = new TreeMap<>(); 24 | variables.put("name", title); 25 | 26 | PagedGraphQLRequest graphQlContent = new PagedGraphQLRequest( 27 | ANIME_QUERY_SEARCH_TITLE, 28 | variables 29 | ); 30 | 31 | return new AniListQuery.Paged(graphQlContent); 32 | } 33 | 34 | public static @NotNull AniListQuery.Paged sortedBy(TYPE queryType) { 35 | TreeMap variables = new TreeMap<>(); 36 | 37 | PagedGraphQLRequest graphQlContent = new PagedGraphQLRequest( 38 | String.format(GENERIC_ANIME_QUERY, queryType.query), 39 | variables 40 | ); 41 | 42 | return new AniListQuery.Paged(graphQlContent); 43 | } 44 | 45 | public enum TYPE { 46 | TRENDING("TRENDING_DESC"), 47 | SCORE("SCORE_DESC"), 48 | POPULARITY("POPULARITY_DESC"), 49 | FAVOURITES("FAVOURITES_DESC"); 50 | private final String query; 51 | TYPE(String query) { 52 | this.query = query; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/anilist/AniListDeserializer.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.anilist; 2 | 3 | import com.astarivi.kaizolib.anilist.exception.ParsingError; 4 | import com.astarivi.kaizolib.anilist.model.AniListAnime; 5 | import com.astarivi.kaizolib.common.util.JsonMapper; 6 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 7 | 8 | import java.io.IOException; 9 | import java.util.List; 10 | 11 | 12 | class AniListDeserializer { 13 | @JsonIgnoreProperties(ignoreUnknown = true) 14 | private static class ResponseMany { 15 | public DataMany data; 16 | 17 | private static class DataMany { 18 | public Page Page; 19 | } 20 | 21 | private static class Page { 22 | public PageInfo pageInfo; 23 | public List media; 24 | } 25 | 26 | private static class PageInfo { 27 | public boolean hasNextPage; 28 | } 29 | } 30 | 31 | protected static Deserialized deserializeMany(String serialized) throws ParsingError { 32 | ResponseMany response; 33 | try { 34 | response = JsonMapper.deserializeGeneric(serialized, ResponseMany.class); 35 | } catch (IOException | NullPointerException e) { 36 | throw new ParsingError(e); 37 | } 38 | 39 | return new Deserialized( 40 | response.data.Page.pageInfo.hasNextPage, 41 | response.data.Page.media 42 | ); 43 | } 44 | 45 | protected record Deserialized(boolean hasNext, List items) { 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/anilist/AniListQuery.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.anilist; 2 | 3 | import com.astarivi.kaizolib.anilist.exception.AniListException; 4 | import com.astarivi.kaizolib.anilist.model.AniListAnime; 5 | import com.astarivi.kaizolib.common.network.HttpMethodsV2; 6 | 7 | import java.io.IOException; 8 | import java.util.List; 9 | 10 | 11 | public class AniListQuery extends AniListCommon { 12 | public static class Single { 13 | private final AniListCommon.GraphQLRequest request; 14 | 15 | protected Single(AniListCommon.GraphQLRequest request) { 16 | this.request = request; 17 | } 18 | 19 | public AniListAnime get() throws AniListException, IOException { 20 | String response = HttpMethodsV2.executeRequest( 21 | getRequestFor(request) 22 | ); 23 | 24 | return AniListAnime.deserializeOne(response); 25 | } 26 | } 27 | 28 | public static class Paged { 29 | private final AniListCommon.PagedGraphQLRequest request; 30 | private boolean hasNext = true; 31 | private long page = 0; 32 | 33 | protected Paged(AniListCommon.PagedGraphQLRequest request) { 34 | this.request = request; 35 | } 36 | 37 | public boolean hasNext() { 38 | return hasNext; 39 | } 40 | 41 | public List current() throws AniListException, IOException { 42 | return execute(); 43 | } 44 | 45 | public List next() throws AniListException, IOException { 46 | if (!hasNext) throw new IndexOutOfBoundsException("No next page for this AniListQuery"); 47 | 48 | request.setPage(++page); 49 | return execute(); 50 | } 51 | 52 | private List execute() throws AniListException, IOException { 53 | String response = HttpMethodsV2.executeRequest( 54 | getRequestFor(request) 55 | ); 56 | 57 | AniListDeserializer.Deserialized res = AniListDeserializer.deserializeMany(response); 58 | hasNext = res.hasNext(); 59 | return res.items(); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/anilist/exception/AniListException.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.anilist.exception; 2 | 3 | public class AniListException extends Exception { 4 | public AniListException() { 5 | } 6 | 7 | public AniListException(String message) { 8 | super(message); 9 | } 10 | 11 | public AniListException(String message, Throwable cause) { 12 | super(message, cause); 13 | } 14 | 15 | public AniListException(Throwable cause) { 16 | super(cause); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/anilist/exception/ParsingError.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.anilist.exception; 2 | 3 | public class ParsingError extends AniListException{ 4 | public ParsingError() { 5 | } 6 | 7 | public ParsingError(String message) { 8 | super(message); 9 | } 10 | 11 | public ParsingError(String message, Throwable cause) { 12 | super(message, cause); 13 | } 14 | 15 | public ParsingError(Throwable cause) { 16 | super(cause); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/ann/ANN.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.ann; 2 | 3 | import com.astarivi.kaizolib.ann.model.ANNItem; 4 | import com.astarivi.kaizolib.common.network.CommonHeaders; 5 | import com.astarivi.kaizolib.common.network.HttpMethodsV2; 6 | 7 | import java.io.IOException; 8 | import java.util.List; 9 | import java.util.regex.Pattern; 10 | 11 | import okhttp3.HttpUrl; 12 | import okhttp3.Request; 13 | 14 | 15 | public class ANN { 16 | public static final HttpUrl ANN_URL = new HttpUrl.Builder() 17 | .scheme("https") 18 | .host("www.animenewsnetwork.com") 19 | .addPathSegments("all/rss.xml") 20 | .addQueryParameter("ann-edition", "w") 21 | .build(); 22 | 23 | public static final Pattern THUMBNAIL_PATTERN = Pattern.compile(""); 24 | 25 | public static List getANNFeed() throws IOException { 26 | Request.Builder getRequestBuilder = new Request.Builder(); 27 | getRequestBuilder.url( 28 | ANN_URL 29 | ); 30 | 31 | CommonHeaders.addTo(getRequestBuilder, CommonHeaders.XML_HEADERS); 32 | 33 | return RssDeserializer.deserialize( 34 | HttpMethodsV2.executeRequest(getRequestBuilder.build()) 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/ann/RssDeserializer.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.ann; 2 | 3 | import com.astarivi.kaizolib.ann.model.ANNItem; 4 | import com.astarivi.kaizolib.common.util.JsonMapper; 5 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 6 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; 7 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; 8 | 9 | import java.io.IOException; 10 | import java.util.List; 11 | 12 | 13 | class RssDeserializer { 14 | @JsonIgnoreProperties(ignoreUnknown = true) 15 | static class RssFeed { 16 | @JacksonXmlProperty(localName = "channel") 17 | public Channel channel; 18 | } 19 | 20 | @JsonIgnoreProperties(ignoreUnknown = true) 21 | static class Channel { 22 | @JacksonXmlProperty(localName = "item") 23 | @JacksonXmlElementWrapper(useWrapping = false) 24 | public List items; 25 | } 26 | 27 | protected static List deserialize(String serialized) throws IOException { 28 | RssFeed result = JsonMapper.getXmlReader().readValue(serialized, RssFeed.class); 29 | 30 | return result.channel.items; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/ann/model/ANNItem.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.ann.model; 2 | 3 | import com.astarivi.kaizolib.ann.ANN; 4 | import com.astarivi.kaizolib.common.network.CommonHeaders; 5 | import com.astarivi.kaizolib.common.network.HttpMethodsV2; 6 | import com.fasterxml.jackson.annotation.JsonIgnore; 7 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 8 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; 9 | 10 | import org.jetbrains.annotations.Nullable; 11 | import org.jsoup.Jsoup; 12 | import org.tinylog.Logger; 13 | 14 | import java.util.regex.Matcher; 15 | 16 | import okhttp3.HttpUrl; 17 | import okhttp3.Request; 18 | 19 | 20 | @JsonIgnoreProperties(ignoreUnknown = true) 21 | public class ANNItem { 22 | public String title; 23 | public String link; 24 | public String description; 25 | public String pubDate; 26 | public String category; 27 | 28 | @JacksonXmlProperty(localName = "description") 29 | public void setDescription(String value) { 30 | if (value == null) { 31 | description = null; 32 | return; 33 | } 34 | 35 | description = Jsoup.parse(value).text(); 36 | } 37 | 38 | @JsonIgnore 39 | public @Nullable String getThumbnailUrl() { 40 | if (link == null) return null; 41 | Request.Builder getRequestBuilder = new Request.Builder(); 42 | 43 | HttpUrl parsedUrl = HttpUrl.parse(link); 44 | 45 | if (parsedUrl == null) return null; 46 | 47 | getRequestBuilder.url( 48 | parsedUrl 49 | ); 50 | 51 | CommonHeaders.addTo(getRequestBuilder, CommonHeaders.TEXT_HEADERS); 52 | 53 | String feedBody; 54 | try { 55 | feedBody = HttpMethodsV2.executeRequest(getRequestBuilder.build()); 56 | } catch (Exception e) { 57 | Logger.error("Error fetching ANN thumbnail for {}", link); 58 | return null; 59 | } 60 | 61 | Matcher matcher = ANN.THUMBNAIL_PATTERN.matcher(feedBody); 62 | 63 | if (matcher.find()) { 64 | return matcher.group(1); 65 | } 66 | 67 | return null; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/common/exception/NoResponseException.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.common.exception; 2 | 3 | import java.io.IOException; 4 | 5 | public class NoResponseException extends IOException { 6 | public NoResponseException() { 7 | } 8 | 9 | public NoResponseException(String message) { 10 | super(message); 11 | } 12 | 13 | public NoResponseException(String message, Throwable cause) { 14 | super(message, cause); 15 | } 16 | 17 | public NoResponseException(Throwable cause) { 18 | super(cause); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/common/exception/UnexpectedStatusCodeException.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.common.exception; 2 | 3 | import java.io.IOException; 4 | 5 | public class UnexpectedStatusCodeException extends IOException { 6 | private final int code; 7 | 8 | public UnexpectedStatusCodeException(int code) { 9 | super(String.valueOf(code)); 10 | this.code = code; 11 | } 12 | 13 | public int getCode() { 14 | return code; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/common/network/CommonHeaders.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.common.network; 2 | 3 | import com.astarivi.kaizolib.common.util.StringPair; 4 | 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | import java.util.List; 8 | 9 | import okhttp3.Request; 10 | 11 | 12 | public class CommonHeaders { 13 | public static List JSON_HEADERS = List.of( 14 | new StringPair("Accept", "application/json"), 15 | new StringPair("Content-Type", "application/vnd.api+json") 16 | ); 17 | public static List KITSU_HEADERS = List.of( 18 | new StringPair("Accept", "application/vnd.api+json"), 19 | new StringPair("Content-Type", "application/vnd.api+json") 20 | ); 21 | public static List XML_HEADERS = List.of( 22 | new StringPair("Accept", "text/xml;charset=UTF-8"), 23 | new StringPair("Content-Type", "text/xml;charset=UTF-8") 24 | ); 25 | public static List TEXT_HEADERS = List.of( 26 | new StringPair("Accept", "text/html; charset=UTF-8"), 27 | new StringPair("Content-Type", "text/html; charset=UTF-8") 28 | ); 29 | 30 | public static void addTo(@NotNull Request.Builder requestBuilder, @NotNull List headers) { 31 | for (StringPair header : headers) { 32 | requestBuilder.addHeader(header.getName(), header.getValue()); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/common/network/HttpMethodsV2.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.common.network; 2 | 3 | import com.astarivi.kaizolib.common.exception.NoResponseException; 4 | import com.astarivi.kaizolib.common.exception.UnexpectedStatusCodeException; 5 | import com.astarivi.kaizolib.common.util.ResponseToString; 6 | 7 | import org.jetbrains.annotations.NotNull; 8 | import org.tinylog.Logger; 9 | 10 | import java.io.IOException; 11 | 12 | import okhttp3.OkHttpClient; 13 | import okhttp3.Request; 14 | import okhttp3.Response; 15 | 16 | public class HttpMethodsV2 { 17 | @NotNull 18 | public static String executeRequestWith(@NotNull Request request, @NotNull OkHttpClient client) throws IOException { 19 | Response response; 20 | try { 21 | response = client.newCall(request).execute(); 22 | } catch (IOException e) { 23 | Logger.debug("Couldn't reach {}", request.url()); 24 | throw e; 25 | } 26 | 27 | int responseCode = response.code(); 28 | final String responseContent = ResponseToString.read(response); 29 | if (responseContent == null) { 30 | Logger.debug("Remote had no response for {}", request.url()); 31 | throw new NoResponseException("Remote destination had no response to this request."); 32 | } 33 | 34 | return switch (responseCode) { 35 | case 304, 200 -> responseContent; 36 | default -> { 37 | Logger.error("Remote response code was incorrect, it was {}. (Only 200 and 304 allowed)", responseCode); 38 | throw new UnexpectedStatusCodeException(responseCode); 39 | } 40 | }; 41 | } 42 | 43 | @NotNull 44 | public static String executeRequest(@NotNull Request request) throws IOException { 45 | return executeRequestWith(request, UserHttpClient.getInstance().getHttpClient()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/common/network/UserHttpClient.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.common.network; 2 | 3 | import okhttp3.OkHttpClient; 4 | import okhttp3.Request; 5 | import okhttp3.Response; 6 | 7 | import org.jetbrains.annotations.NotNull; 8 | import org.tinylog.Logger; 9 | 10 | import java.io.IOException; 11 | 12 | 13 | public class UserHttpClient { 14 | private static volatile UserHttpClient _instance = null; 15 | private final OkHttpClient httpClient; 16 | 17 | private UserHttpClient(){ 18 | httpClient = new OkHttpClient(); 19 | } 20 | 21 | private UserHttpClient(OkHttpClient httpClient){ 22 | this.httpClient = httpClient; 23 | } 24 | 25 | public Response executeRequest(Request request) throws IOException { 26 | return httpClient.newCall(request).execute(); 27 | } 28 | 29 | public OkHttpClient getHttpClient(){ 30 | return httpClient; 31 | } 32 | 33 | public static @NotNull UserHttpClient getInstance() { 34 | if (_instance == null) { 35 | synchronized (UserHttpClient.class) { 36 | if (_instance == null) _instance = new UserHttpClient(); 37 | } 38 | } 39 | return _instance; 40 | } 41 | 42 | public void close() { 43 | try { 44 | httpClient.dispatcher().executorService().shutdownNow(); 45 | httpClient.connectionPool().evictAll(); 46 | if (httpClient.cache() != null) httpClient.cache().close(); 47 | } catch (IOException io) { 48 | Logger.warn("The HttpClient couldn't close. ", io); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/common/util/JsonMapper.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.common.util; 2 | 3 | import com.ctc.wstx.stax.WstxInputFactory; 4 | import com.ctc.wstx.stax.WstxOutputFactory; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import com.fasterxml.jackson.databind.ObjectReader; 7 | import com.fasterxml.jackson.databind.ObjectWriter; 8 | import com.fasterxml.jackson.dataformat.xml.XmlFactory; 9 | import com.fasterxml.jackson.dataformat.xml.XmlMapper; 10 | 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | import java.io.IOException; 14 | 15 | public class JsonMapper { 16 | private static final ObjectMapper objectMapper = new ObjectMapper(); 17 | private static final XmlMapper xmlMapper = XmlMapper.builder( 18 | XmlFactory.builder() 19 | .xmlInputFactory(new WstxInputFactory()) 20 | .xmlOutputFactory(new WstxOutputFactory()) 21 | .build() 22 | ).build(); 23 | 24 | public static ObjectReader getXmlReader() { 25 | return xmlMapper.reader(); 26 | } 27 | 28 | public static ObjectReader getObjectReader() { 29 | return objectMapper.reader(); 30 | } 31 | 32 | public static ObjectWriter getObjectWriter() { 33 | return objectMapper.writer(); 34 | } 35 | 36 | public static @NotNull T deserializeGeneric(@NotNull String jsonString, Class resultType) throws IOException { 37 | return getObjectReader().readValue(jsonString, resultType); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/common/util/ResponseToString.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.common.util; 2 | 3 | import okhttp3.Response; 4 | import okhttp3.ResponseBody; 5 | import org.jetbrains.annotations.NotNull; 6 | import org.jetbrains.annotations.Nullable; 7 | import org.tinylog.Logger; 8 | 9 | import java.io.IOException; 10 | 11 | 12 | public class ResponseToString { 13 | 14 | public static @Nullable String read(@NotNull Response response){ 15 | try { 16 | ResponseBody body = response.body(); 17 | 18 | if (body == null) return null; 19 | 20 | String bodyAsString = body.string(); 21 | body.close(); 22 | 23 | return bodyAsString; 24 | } catch (IOException e) { 25 | Logger.warn("The CloseableHttpResponse went through to the host and back," 26 | +"but it couldn't be read correctly, or didn't close. ", e); 27 | return null; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/common/util/StringPair.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.common.util; 2 | 3 | import java.util.Objects; 4 | 5 | 6 | public class StringPair { 7 | private final String name; 8 | private final String value; 9 | 10 | public StringPair(final String name, final String value) { 11 | this.name = name; 12 | this.value = value; 13 | } 14 | 15 | public String getName() { 16 | return this.name; 17 | } 18 | 19 | public String getValue() { 20 | return this.value; 21 | } 22 | 23 | @Override 24 | public boolean equals(final Object obj) { 25 | if (this == obj) { 26 | return true; 27 | } 28 | if (obj instanceof StringPair that) { 29 | return this.name.equalsIgnoreCase(that.name) && Objects.equals(this.value, that.value); 30 | } 31 | return false; 32 | } 33 | } -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/irc/exception/BlacklistedIpException.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.irc.exception; 2 | 3 | public class BlacklistedIpException extends Exception { 4 | public BlacklistedIpException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/irc/exception/BotNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.irc.exception; 2 | 3 | public class BotNotFoundException extends Exception{ 4 | public BotNotFoundException(String reason) { 5 | super(reason); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/irc/exception/GenericHandshakeException.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.irc.exception; 2 | 3 | public class GenericHandshakeException extends Exception { 4 | public GenericHandshakeException(Exception e) { 5 | super(e); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/irc/exception/IrcExceptionManager.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.irc.exception; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | import java.io.IOException; 6 | import java.util.concurrent.TimeoutException; 7 | 8 | 9 | public class IrcExceptionManager { 10 | public static @NotNull FailureCode getFailureCode(Exception e) { 11 | if (e instanceof StrictModeException) { 12 | return FailureCode.StrictModeFailure; 13 | } else if (e instanceof BotNotFoundException) { 14 | return FailureCode.BotNotFound; 15 | } else if (e instanceof BlacklistedIpException) { 16 | return FailureCode.BlacklistedIp; 17 | } else if (e instanceof NoQuickRetryException) { 18 | return FailureCode.NoQuickRetry; 19 | } else if (e instanceof TimeoutException) { 20 | return FailureCode.TimeOut; 21 | } else if (e instanceof IOException) { 22 | return FailureCode.IoException; 23 | } else if (e instanceof GenericHandshakeException) { 24 | return FailureCode.GenericHandshakeError; 25 | } 26 | 27 | return FailureCode.Generic; 28 | } 29 | 30 | public enum FailureCode{ 31 | TimeOut, 32 | Generic, 33 | NoQuickRetry, 34 | IoException, 35 | StrictModeFailure, 36 | GenericHandshakeError, 37 | BlacklistedIp, 38 | BotNotFound 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/irc/exception/NoQuickRetryException.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.irc.exception; 2 | 3 | public class NoQuickRetryException extends Exception{ 4 | public NoQuickRetryException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/irc/exception/StrictModeException.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.irc.exception; 2 | 3 | 4 | public class StrictModeException extends Exception{ 5 | public StrictModeException(Exception exception) { 6 | super(exception); 7 | } 8 | } -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/irc/utils/Utils.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.irc.utils; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | import java.util.Random; 6 | 7 | public class Utils { 8 | public static @NotNull String shuffle (String s) { 9 | StringBuilder result = new StringBuilder(s); 10 | int n = result.length(); 11 | Random rand = new Random(); 12 | 13 | while (n>1) { 14 | int randomPoint = rand.nextInt(n); 15 | char randomChar = result.charAt(randomPoint); 16 | result.setCharAt(n-1,randomChar); 17 | n--; 18 | } 19 | 20 | return result.toString(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/kitsu/KitsuRelations.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.kitsu; 2 | 3 | import com.astarivi.kaizolib.common.network.CommonHeaders; 4 | import com.astarivi.kaizolib.common.network.HttpMethods; 5 | import com.astarivi.kaizolib.common.network.UserHttpClient; 6 | import com.astarivi.kaizolib.kitsu.exception.NetworkConnectionException; 7 | import com.astarivi.kaizolib.kitsu.exception.NoResponseException; 8 | import com.astarivi.kaizolib.kitsu.exception.NoResultsException; 9 | import com.astarivi.kaizolib.kitsu.exception.ParsingException; 10 | import com.astarivi.kaizolib.kitsu.model.KitsuCategory; 11 | import com.astarivi.kaizolib.kitsu.model.KitsuCategoriesResult; 12 | import com.astarivi.kaizolib.kitsu.parser.ParseJson; 13 | 14 | import org.tinylog.Logger; 15 | 16 | import java.util.List; 17 | 18 | 19 | public class KitsuRelations { 20 | private final UserHttpClient client; 21 | 22 | public KitsuRelations(UserHttpClient client) { 23 | this.client = client; 24 | } 25 | 26 | public KitsuRelations() { 27 | client = UserHttpClient.getInstance(); 28 | } 29 | 30 | public List getKitsuCategories(int kitsuId) throws 31 | NetworkConnectionException, 32 | NoResponseException, 33 | ParsingException, 34 | NoResultsException 35 | { 36 | String responseContent = HttpMethods.get( 37 | client, 38 | KitsuUtils.buildCategoriesUri(kitsuId), 39 | CommonHeaders.KITSU_HEADERS 40 | ); 41 | 42 | KitsuCategoriesResult result = ParseJson.parseGeneric(responseContent, KitsuCategoriesResult.class); 43 | 44 | if (result.data == null || result.data.isEmpty()) { 45 | Logger.error("No results fetching Kitsu categories for anime id {}", kitsuId); 46 | throw new NoResultsException("No results found for this id"); 47 | } 48 | 49 | return result.data; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/kitsu/exception/KitsuExceptionManager.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.kitsu.exception; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | 6 | public class KitsuExceptionManager { 7 | public static @NotNull FailureCode getFailureCode(Exception e) { 8 | if (e instanceof NetworkConnectionException) { 9 | return FailureCode.NetworkConnectionException; 10 | } else if (e instanceof NoResponseException) { 11 | return FailureCode.NoResponseException; 12 | } else if (e instanceof NoResultsException) { 13 | return FailureCode.NoResultsException; 14 | } else if (e instanceof ParsingException) { 15 | return FailureCode.ParsingException; 16 | } 17 | 18 | return FailureCode.Generic; 19 | } 20 | 21 | public enum FailureCode { 22 | NetworkConnectionException, 23 | NoResponseException, 24 | NoResultsException, 25 | ParsingException, 26 | Generic 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/kitsu/exception/NetworkConnectionException.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.kitsu.exception; 2 | 3 | public class NetworkConnectionException extends Exception{ 4 | public NetworkConnectionException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/kitsu/exception/NoResponseException.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.kitsu.exception; 2 | 3 | public class NoResponseException extends Exception{ 4 | public NoResponseException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/kitsu/exception/NoResultsException.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.kitsu.exception; 2 | 3 | public class NoResultsException extends Exception { 4 | public NoResultsException(String cause) { 5 | super(cause); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/kitsu/exception/ParsingException.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.kitsu.exception; 2 | 3 | public class ParsingException extends Exception{ 4 | public ParsingException(Exception e) { 5 | super(e); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/kitsu/model/KitsuCategoriesResult.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.kitsu.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | 5 | import java.util.List; 6 | 7 | 8 | @JsonIgnoreProperties(ignoreUnknown = true) 9 | public class KitsuCategoriesResult { 10 | public List data; 11 | public KitsuCategoriesMeta meta; 12 | 13 | @JsonIgnoreProperties(ignoreUnknown = true) 14 | public static class KitsuCategoriesMeta { 15 | public int count; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/kitsu/model/KitsuCategory.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.kitsu.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | 5 | 6 | @JsonIgnoreProperties(ignoreUnknown = true) 7 | public class KitsuCategory { 8 | public String id; 9 | public String type; 10 | public KitsuCategoriesAttributes attributes; 11 | 12 | @JsonIgnoreProperties(ignoreUnknown = true) 13 | public static class KitsuCategoriesAttributes { 14 | public String createdAt; 15 | public String updatedAt; 16 | public String title; 17 | public String description; 18 | public Integer totalMediaCount; 19 | public String slug; 20 | public Boolean nsfw; 21 | public Integer childCount; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/kitsu/model/KitsuEpisodeResults.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.kitsu.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import java.util.List; 5 | 6 | @JsonIgnoreProperties(ignoreUnknown = true) 7 | public class KitsuEpisodeResults { 8 | public List data; 9 | public KitsuEpisodeMeta meta; 10 | 11 | @JsonIgnoreProperties(ignoreUnknown = true) 12 | public static class KitsuEpisodeMeta { 13 | public int count; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/kitsu/model/KitsuResourceResult.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.kitsu.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | 5 | 6 | @JsonIgnoreProperties(ignoreUnknown = true) 7 | public class KitsuResourceResult { 8 | public KitsuAnime data; 9 | } -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/kitsu/model/KitsuSearchResults.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.kitsu.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import java.util.List; 5 | 6 | 7 | @JsonIgnoreProperties(ignoreUnknown = true) 8 | public class KitsuSearchResults { 9 | public List data; 10 | public KitsuSearchMeta meta; 11 | 12 | @JsonIgnoreProperties(ignoreUnknown = true) 13 | public static class KitsuSearchMeta { 14 | public int count; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/kitsu/parser/ParseJson.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.kitsu.parser; 2 | 3 | import com.astarivi.kaizolib.common.util.JsonMapper; 4 | import com.astarivi.kaizolib.kitsu.exception.ParsingException; 5 | import com.astarivi.kaizolib.kitsu.model.KitsuEpisodeResults; 6 | 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.io.IOException; 10 | 11 | 12 | public class ParseJson { 13 | public static int parseEpisodesLength(@NotNull String jsonInString) throws ParsingException { 14 | KitsuEpisodeResults results = parseGeneric(jsonInString, KitsuEpisodeResults.class); 15 | 16 | if (results.meta == null) return 0; 17 | 18 | return results.meta.count; 19 | } 20 | 21 | public static @NotNull T parseGeneric(@NotNull String jsonString, Class resultType) throws ParsingException { 22 | try { 23 | return JsonMapper.getObjectReader().readValue(jsonString, resultType); 24 | } catch (IOException e) { 25 | throw new ParsingException(e); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/kitsuv2/common/KitsuCommon.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.kitsuv2.common; 2 | 3 | import com.astarivi.kaizolib.common.network.CommonHeaders; 4 | import com.astarivi.kaizolib.common.network.HttpMethodsV2; 5 | import com.astarivi.kaizolib.kitsuv2.exception.KitsuException; 6 | 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.io.IOException; 10 | 11 | import okhttp3.HttpUrl; 12 | import okhttp3.OkHttpClient; 13 | import okhttp3.Request; 14 | 15 | public abstract class KitsuCommon { 16 | protected static Request.@NotNull Builder getBaseBuilder(HttpUrl url) { 17 | Request.Builder builder = new Request.Builder().url(url); 18 | 19 | CommonHeaders.addTo(builder, CommonHeaders.KITSU_HEADERS); 20 | 21 | return builder; 22 | } 23 | 24 | protected static String executeGet(HttpUrl url) throws KitsuException { 25 | return execute(getBaseBuilder(url).get().build()); 26 | } 27 | 28 | protected static String execute(Request request) throws KitsuException { 29 | try { 30 | return HttpMethodsV2.executeRequest( 31 | request 32 | ); 33 | } catch (IOException e) { 34 | throw new KitsuException(e); 35 | } 36 | } 37 | 38 | protected static String executeWith(Request request, OkHttpClient client) throws KitsuException { 39 | try { 40 | return HttpMethodsV2.executeRequestWith( 41 | request, 42 | client 43 | ); 44 | } catch (IOException e) { 45 | throw new KitsuException(e); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/kitsuv2/exception/CloudflareChallengeException.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.kitsuv2.exception; 2 | 3 | public class CloudflareChallengeException extends Exception { 4 | private final String url; 5 | 6 | public CloudflareChallengeException(String url) { 7 | super("Cloudflare link returned a challenge"); 8 | this.url = url; 9 | } 10 | 11 | public String getUrl() { 12 | return url; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/kitsuv2/exception/KitsuException.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.kitsuv2.exception; 2 | 3 | 4 | public class KitsuException extends Exception { 5 | public KitsuException() { 6 | } 7 | 8 | public KitsuException(String message) { 9 | super(message); 10 | } 11 | 12 | public KitsuException(String message, Throwable cause) { 13 | super(message, cause); 14 | } 15 | 16 | public KitsuException(Throwable cause) { 17 | super(cause); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/kitsuv2/exception/NotAuthenticatedException.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.kitsuv2.exception; 2 | 3 | public class NotAuthenticatedException extends Exception { 4 | public NotAuthenticatedException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/kitsuv2/exception/ParsingError.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.kitsuv2.exception; 2 | 3 | import com.astarivi.kaizolib.anilist.exception.AniListException; 4 | 5 | 6 | public class ParsingError extends AniListException { 7 | public ParsingError() { 8 | } 9 | 10 | public ParsingError(String message) { 11 | super(message); 12 | } 13 | 14 | public ParsingError(String message, Throwable cause) { 15 | super(message, cause); 16 | } 17 | 18 | public ParsingError(Throwable cause) { 19 | super(cause); 20 | } 21 | } -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/kitsuv2/model/KitsuCredentials.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.kitsuv2.model; 2 | 3 | import com.astarivi.kaizolib.common.util.JsonMapper; 4 | import com.astarivi.kaizolib.kitsuv2.exception.ParsingError; 5 | import com.fasterxml.jackson.annotation.JsonIgnore; 6 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 7 | 8 | import org.jetbrains.annotations.Contract; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | import java.io.IOException; 12 | 13 | public record KitsuCredentials( 14 | String accessToken, 15 | String refreshToken, 16 | long createdAt, 17 | long expiresIn 18 | ) { 19 | @JsonIgnore 20 | public boolean accessTokenExpired() { 21 | long now = System.currentTimeMillis() / 1000; 22 | long leeway = 60; 23 | return now >= (createdAt + expiresIn - leeway); 24 | } 25 | 26 | @JsonIgnoreProperties(ignoreUnknown = true) 27 | private static class Deserializer { 28 | public String access_token; 29 | public String token_type; 30 | public long expires_in; 31 | public String refresh_token; 32 | public String scope; 33 | public long created_at; 34 | 35 | public Deserializer() { 36 | } 37 | } 38 | 39 | @NotNull 40 | @Contract("_ -> new") 41 | @JsonIgnore 42 | public static KitsuCredentials deserialize(String serialized) throws ParsingError { 43 | Deserializer deserialized; 44 | 45 | try { 46 | deserialized = JsonMapper.deserializeGeneric(serialized, Deserializer.class); 47 | } catch (IOException | NullPointerException e) { 48 | throw new ParsingError(e); 49 | } 50 | 51 | return new KitsuCredentials( 52 | deserialized.access_token, 53 | deserialized.refresh_token, 54 | deserialized.created_at, 55 | deserialized.expires_in 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/kitsuv2/model/KitsuUser.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.kitsuv2.model; 2 | 3 | import com.astarivi.kaizolib.common.util.JsonMapper; 4 | import com.astarivi.kaizolib.kitsuv2.exception.ParsingError; 5 | import com.fasterxml.jackson.annotation.JsonIgnore; 6 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 7 | import com.fasterxml.jackson.annotation.JsonProperty; 8 | 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | import java.io.IOException; 12 | import java.util.List; 13 | 14 | @JsonIgnoreProperties(ignoreUnknown = true) 15 | public class KitsuUser { 16 | public long id; 17 | public Attributes attributes; 18 | 19 | 20 | @JsonProperty("id") 21 | public void setId(String id) { 22 | this.id = Long.parseLong(id); 23 | } 24 | 25 | @JsonProperty("id") 26 | public void setId(long id) { 27 | this.id = id; 28 | } 29 | 30 | @JsonIgnoreProperties(ignoreUnknown = true) 31 | public static class Attributes { 32 | public String name; 33 | public String about; 34 | public KitsuAnime.KitsuAnimeImages avatar; 35 | public KitsuAnime.KitsuAnimeImages coverImage; 36 | public String email; 37 | } 38 | 39 | @JsonIgnoreProperties(ignoreUnknown = true) 40 | private static class Deserializer { 41 | public List data; 42 | 43 | public Deserializer() { 44 | } 45 | } 46 | 47 | @JsonIgnore 48 | public static @NotNull KitsuUser deserialize(String serialized) throws ParsingError { 49 | try { 50 | List data = JsonMapper.deserializeGeneric(serialized, KitsuUser.Deserializer.class).data; 51 | 52 | if (data == null || data.isEmpty()) { 53 | throw new ParsingError("Nothing to deserialize, no results"); 54 | } 55 | 56 | return data.get(0); 57 | } catch (IOException | NullPointerException e) { 58 | throw new ParsingError(e); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/kitsuv2/model/RawResults.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.kitsuv2.model; 2 | 3 | import java.util.List; 4 | 5 | public record RawResults(List anime, long count) { 6 | } 7 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/kitsuv2/private_api/KitsuPrivate.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.kitsuv2.private_api; 2 | 3 | import com.astarivi.kaizolib.kitsuv2.exception.KitsuException; 4 | import com.astarivi.kaizolib.kitsuv2.exception.ParsingError; 5 | import com.astarivi.kaizolib.kitsuv2.model.KitsuListEntry; 6 | import com.astarivi.kaizolib.kitsuv2.model.KitsuUser; 7 | import com.astarivi.kaizolib.kitsuv2.model.RawResults; 8 | 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | import java.util.List; 12 | 13 | public class KitsuPrivate extends Methods { 14 | @NotNull 15 | public static KitsuUser user() throws KitsuException, ParsingError { 16 | return KitsuUser.deserialize( 17 | executeGetWithCredentials(SELF_USER_ENDPOINT) 18 | ); 19 | } 20 | 21 | @NotNull 22 | public static List list(@NotNull LibraryParams libraryParams) throws KitsuException, ParsingError { 23 | return KitsuListEntry.deserialize( 24 | executeGetWithCredentials( 25 | libraryParams.buildURI() 26 | ) 27 | ); 28 | } 29 | 30 | @NotNull 31 | public static RawResults rawList(@NotNull LibraryParams libraryParams) throws KitsuException, ParsingError { 32 | return KitsuListEntry.deserializeRaw( 33 | executeGetWithCredentials( 34 | libraryParams.buildURI() 35 | ) 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/kitsuv2/public_api/Methods.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.kitsuv2.public_api; 2 | 3 | import com.astarivi.kaizolib.kitsuv2.common.KitsuCommon; 4 | import com.astarivi.kaizolib.kitsuv2.exception.KitsuException; 5 | 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import okhttp3.HttpUrl; 9 | 10 | public class Methods extends KitsuCommon { 11 | protected static @NotNull String idRequest(long id) throws KitsuException { 12 | return executeGet( 13 | new HttpUrl.Builder() 14 | .scheme("https") 15 | .host("kitsu.app") 16 | .addPathSegments("api/edge/anime") 17 | .addPathSegment(Long.toString(id)) 18 | .build() 19 | ); 20 | } 21 | 22 | protected static @NotNull String episodesRequest(long animeId, int limit, int offset) throws KitsuException{ 23 | return executeGet( 24 | new HttpUrl.Builder() 25 | .scheme("https") 26 | .host("kitsu.app") 27 | .addPathSegments("api/edge/episodes") 28 | .addQueryParameter("filter[media_id]", Long.toString(animeId)) 29 | .addQueryParameter("page[limit]", Integer.toString(limit)) 30 | .addQueryParameter("page[offset]", Integer.toString(offset)) 31 | .addQueryParameter("sort", "number") 32 | .build() 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/nibl/NiblUtils.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.nibl; 2 | 3 | import okhttp3.HttpUrl; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | 7 | public class NiblUtils { 8 | public static @NotNull HttpUrl buildLatestAnimeUri(int limit) { 9 | return new HttpUrl.Builder() 10 | .scheme("https") 11 | .host("api.nibl.co.uk") 12 | .addPathSegments("nibl/latest") 13 | .addQueryParameter("size", Integer.toString(limit)) 14 | .build(); 15 | } 16 | 17 | public static @NotNull HttpUrl buildSearchUri(int limit, String anime) { 18 | return new HttpUrl.Builder() 19 | .scheme("https") 20 | .host("api.nibl.co.uk") 21 | .addPathSegments("nibl/search") 22 | .addQueryParameter("query", anime) 23 | .addQueryParameter("size", Integer.toString(limit)) 24 | .build(); 25 | } 26 | 27 | public static @NotNull HttpUrl buildEpisodeSearchUri(int limit, String anime, int episode) { 28 | return new HttpUrl.Builder() 29 | .scheme("https") 30 | .host("api.nibl.co.uk") 31 | .addPathSegments("nibl/search") 32 | .addQueryParameter("query", anime) 33 | .addQueryParameter("episodeNumber", Integer.toString(episode)) 34 | .addQueryParameter("size", Integer.toString(limit)) 35 | .build(); 36 | } 37 | 38 | public static @NotNull HttpUrl buildBotsUri() { 39 | return new HttpUrl.Builder() 40 | .scheme("https") 41 | .host("api.nibl.co.uk") 42 | .addPathSegments("nibl/bots") 43 | .build(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/nibl/model/NiblBot.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.nibl.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | 5 | 6 | @JsonIgnoreProperties(ignoreUnknown = true) 7 | public class NiblBot { 8 | public int id; 9 | public String name; 10 | public String owner; 11 | public String lastProcessed; 12 | public int batchEnable; 13 | public int packSize; 14 | } 15 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/nibl/model/NiblBotsResults.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.nibl.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import java.util.List; 5 | 6 | 7 | @JsonIgnoreProperties(ignoreUnknown = true) 8 | public class NiblBotsResults { 9 | public String status; 10 | public String message; 11 | public List content; 12 | } 13 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/nibl/model/NiblResult.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.nibl.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | 5 | 6 | @JsonIgnoreProperties(ignoreUnknown = true) 7 | public class NiblResult { 8 | public int botId; 9 | public int number; 10 | public String name; 11 | public String size; 12 | public long sizekbits; 13 | public int episodeNumber; 14 | public String lastModified; 15 | } 16 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/nibl/model/NiblSearchResults.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.nibl.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import java.util.List; 5 | 6 | 7 | @JsonIgnoreProperties(ignoreUnknown = true) 8 | public class NiblSearchResults { 9 | public String status; 10 | public String message; 11 | public List content; 12 | } 13 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/nibl/parser/ParseJson.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.nibl.parser; 2 | 3 | import com.astarivi.kaizolib.common.util.JsonMapper; 4 | import com.astarivi.kaizolib.nibl.model.NiblBotsResults; 5 | import com.astarivi.kaizolib.nibl.model.NiblSearchResults; 6 | 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | import java.io.IOException; 11 | 12 | 13 | public class ParseJson { 14 | public static @Nullable NiblSearchResults parse(@NotNull String jsonInString) { 15 | try { 16 | return JsonMapper.getObjectReader().readValue(jsonInString, NiblSearchResults.class); 17 | } catch (IOException e) { 18 | return null; 19 | } 20 | } 21 | 22 | public static @Nullable NiblBotsResults parseBots(@NotNull String jsonInString) { 23 | try { 24 | return JsonMapper.getObjectReader().readValue(jsonInString, NiblBotsResults.class); 25 | } catch (IOException e) { 26 | return null; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/subsplease/SubsPleaseUtils.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.subsplease; 2 | 3 | import okhttp3.HttpUrl; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public class SubsPleaseUtils { 7 | public static @NotNull HttpUrl buildEmissionUrl(String timezone) { 8 | return new HttpUrl.Builder() 9 | .scheme("https") 10 | .host("subsplease.org") 11 | .addPathSegment("api/") 12 | .addQueryParameter("f", "schedule") 13 | .addQueryParameter("tz", timezone) 14 | .build(); 15 | } 16 | 17 | public static @NotNull HttpUrl buildTodayUrl(String timezone) { 18 | return new HttpUrl.Builder() 19 | .scheme("https") 20 | .host("subsplease.org") 21 | .addPathSegment("api/") 22 | .addQueryParameter("f", "schedule") 23 | .addQueryParameter("h", "true") 24 | .addQueryParameter("tz", timezone) 25 | .build(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/subsplease/model/SubsPleaseAnime.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.subsplease.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | 5 | 6 | @JsonIgnoreProperties(ignoreUnknown = true) 7 | public class SubsPleaseAnime { 8 | public String title; 9 | public String page; 10 | public String image_url; 11 | public String time; 12 | public Boolean aired; 13 | } -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/subsplease/model/SubsPleaseResult.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.subsplease.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 5 | import org.jetbrains.annotations.NotNull; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | import java.time.DayOfWeek; 9 | import java.util.List; 10 | 11 | 12 | @JsonIgnoreProperties(ignoreUnknown = true) 13 | public class SubsPleaseResult { 14 | public String tz; 15 | public SubsPleaseSchedule schedule; 16 | 17 | @JsonIgnoreProperties(ignoreUnknown = true) 18 | public static class SubsPleaseSchedule { 19 | public List Monday; 20 | public List Tuesday; 21 | public List Wednesday; 22 | public List Thursday; 23 | public List Friday; 24 | public List Saturday; 25 | public List Sunday; 26 | 27 | @JsonIgnore 28 | public @Nullable List getDay(@NotNull DayOfWeek dow) { 29 | switch(dow) { 30 | case MONDAY: 31 | return this.Monday; 32 | case TUESDAY: 33 | return this.Tuesday; 34 | case WEDNESDAY: 35 | return this.Wednesday; 36 | case THURSDAY: 37 | return this.Thursday; 38 | case FRIDAY: 39 | return this.Friday; 40 | case SATURDAY: 41 | return this.Saturday; 42 | case SUNDAY: 43 | default: 44 | return this.Sunday; 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/subsplease/model/SubsPleaseTodayResult.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.subsplease.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | 5 | import java.util.List; 6 | 7 | 8 | @JsonIgnoreProperties(ignoreUnknown = true) 9 | public class SubsPleaseTodayResult { 10 | public String tz; 11 | public List schedule; 12 | } 13 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/subsplease/parser/ParseJson.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.subsplease.parser; 2 | 3 | import com.astarivi.kaizolib.common.util.JsonMapper; 4 | import com.astarivi.kaizolib.subsplease.model.SubsPleaseResult; 5 | import com.astarivi.kaizolib.subsplease.model.SubsPleaseTodayResult; 6 | 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | import java.io.IOException; 10 | 11 | 12 | public class ParseJson { 13 | public static @Nullable SubsPleaseResult parse(String jsonInString) { 14 | try { 15 | return JsonMapper.getObjectReader().readValue(jsonInString, SubsPleaseResult.class); 16 | } catch (IOException e) { 17 | return null; 18 | } 19 | } 20 | 21 | public static @Nullable SubsPleaseTodayResult parseToday(String jsonInString) { 22 | try { 23 | return JsonMapper.getObjectReader().readValue(jsonInString, SubsPleaseTodayResult.class); 24 | } catch (IOException e) { 25 | return null; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/xdcc/base/TimeoutException.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.xdcc.base; 2 | 3 | public class TimeoutException extends Exception{ 4 | public TimeoutException() { 5 | 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/xdcc/base/XDCCDownloadListener.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.xdcc.base; 2 | 3 | import java.io.File; 4 | 5 | 6 | public interface XDCCDownloadListener { 7 | void onDownloadReadyToPlay(int progress, File downloadFile); 8 | 9 | void onProgress(int progress, String speed); 10 | 11 | void onFinished(File downloadFile); 12 | 13 | void onError(XDCCFailure failureCode); 14 | } 15 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/xdcc/base/XDCCFailure.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.xdcc.base; 2 | 3 | public enum XDCCFailure { 4 | ConnectionLost, 5 | TimedOut, 6 | UnknownHost, 7 | IOError 8 | } 9 | -------------------------------------------------------------------------------- /service/src/main/java/com/astarivi/kaizolib/xdcc/model/DCC.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib.xdcc.model; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | public record DCC(String filename, String ip, int port, long sizeBits, boolean ipv6) { 6 | @Override 7 | public @NotNull String toString() { 8 | return "DCC{" + 9 | "filename='" + filename + '\'' + 10 | ", ip='" + ip + '\'' + 11 | ", port=" + port + 12 | ", sizeBits=" + sizeBits + 13 | ", ipv6=" + ipv6 + 14 | '}'; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /service/src/test/java/com/astarivi/kaizolib/Kitsuv2Test.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertFalse; 5 | import static org.junit.jupiter.api.Assertions.assertNotNull; 6 | 7 | import com.astarivi.kaizolib.kitsuv2.model.KitsuAnime; 8 | import com.astarivi.kaizolib.kitsuv2.public_api.KitsuPublic; 9 | import com.astarivi.kaizolib.kitsuv2.public_api.SearchParams; 10 | 11 | import org.junit.jupiter.api.DisplayName; 12 | import org.junit.jupiter.api.Test; 13 | 14 | import java.util.List; 15 | 16 | 17 | public class Kitsuv2Test { 18 | @Test 19 | @DisplayName("Kitsuv2 get by Anime ID") 20 | void testKitsuById() throws Exception { 21 | KitsuAnime anime = KitsuPublic.get(43806); 22 | assertNotNull(anime); 23 | assertNotNull(anime.attributes); 24 | assertNotNull(anime.attributes.titles); 25 | assertNotNull(anime.attributes.posterImage); 26 | assertNotNull(anime.attributes.coverImage); 27 | assertEquals("Chainsaw Man", anime.attributes.canonicalTitle); 28 | assertEquals("Chainsaw Man", anime.attributes.titles.en); 29 | assertEquals("2022-10-11", anime.attributes.startDate); 30 | } 31 | 32 | @Test 33 | @DisplayName("Kitsuv2 search (20 results limit)") 34 | void testKitsuSearch() throws Exception { 35 | List anime = KitsuPublic.advancedSearch( 36 | new SearchParams() 37 | .setTitle("Attack on Titan") 38 | .setCustomParameter("sort", "popularityRank") 39 | ); 40 | 41 | assertNotNull(anime); 42 | assertFalse(anime.isEmpty()); 43 | assertEquals(20, anime.size()); 44 | 45 | for (KitsuAnime individualAnime : anime) { 46 | assertNotNull(individualAnime); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /service/src/test/java/com/astarivi/kaizolib/RssTest.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertNotNull; 4 | import static org.junit.jupiter.api.Assertions.assertTrue; 5 | 6 | import com.astarivi.kaizolib.ann.ANN; 7 | import com.astarivi.kaizolib.ann.model.ANNItem; 8 | 9 | import org.junit.jupiter.api.DisplayName; 10 | import org.junit.jupiter.api.Test; 11 | 12 | import java.util.List; 13 | 14 | 15 | public class RssTest { 16 | @Test 17 | @DisplayName("Rss ANN feed") 18 | void annFeed() throws Exception { 19 | List items = ANN.getANNFeed(); 20 | assertNotNull(items); 21 | assertTrue(items.size() > 1); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /service/src/test/java/com/astarivi/kaizolib/SubsPleaseTest.java: -------------------------------------------------------------------------------- 1 | package com.astarivi.kaizolib; 2 | 3 | import com.astarivi.kaizolib.common.network.UserHttpClient; 4 | import com.astarivi.kaizolib.subsplease.SubsPlease; 5 | import com.astarivi.kaizolib.subsplease.model.SubsPleaseAnime; 6 | import org.junit.jupiter.api.AfterAll; 7 | import org.junit.jupiter.api.BeforeAll; 8 | import org.junit.jupiter.api.DisplayName; 9 | import org.junit.jupiter.api.Test; 10 | import static org.junit.jupiter.api.Assertions.*; 11 | 12 | import java.time.DayOfWeek; 13 | import java.util.List; 14 | import java.util.TimeZone; 15 | import java.util.TreeMap; 16 | 17 | 18 | public class SubsPleaseTest { 19 | private static UserHttpClient userHttpClient; 20 | private static SubsPlease subsPlease; 21 | 22 | @BeforeAll 23 | static void setup() { 24 | userHttpClient = UserHttpClient.getInstance(); 25 | subsPlease = new SubsPlease(userHttpClient); 26 | } 27 | @Test 28 | @DisplayName("SubsPlease DOW Schedule") 29 | void testSchedule() { 30 | TreeMap> result = subsPlease.getAiringAnime(TimeZone.getDefault()); 31 | assertNotNull(result); 32 | } 33 | 34 | @Test 35 | @DisplayName("SubsPlease Today Schedule") 36 | void testTodaySchedule() { 37 | List result = subsPlease.getAnimeAiringToday(TimeZone.getDefault()); 38 | assertNotNull(result); 39 | } 40 | 41 | @AfterAll 42 | static void finish() { 43 | userHttpClient.close(); 44 | subsPlease = null; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':service' 2 | rootProject.name = "Kaizoyu!" --------------------------------------------------------------------------------