feat: Integrate the AniList API since Jikan is shutting down

This commit is contained in:
2026-07-25 16:34:14 +02:00
parent 0dba2a9909
commit 6c2af9fc57
23 changed files with 40865 additions and 218 deletions

View File

@@ -317,9 +317,9 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
);
// Update the cache
final cacheIndex = _mangas.indexWhere((m) => m.id == event.id);
final cacheIndex = _animes.indexWhere((m) => m.id == event.id);
assert(cacheIndex != -1, 'The anime must exist');
_mangas.removeAt(cacheIndex);
_animes.removeAt(cacheIndex);
// Update the database
await GetIt.I.get<DatabaseService>().deleteAnime(event.id);
@@ -338,9 +338,9 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
);
// Update the cache
final cacheIndex = _animes.indexWhere((a) => a.id == event.id);
final cacheIndex = _mangas.indexWhere((a) => a.id == event.id);
assert(cacheIndex != -1, 'The manga must exist');
_animes.removeAt(cacheIndex);
_mangas.removeAt(cacheIndex);
// Update the database
await GetIt.I.get<DatabaseService>().deleteManga(event.id);

View File

@@ -1,7 +1,10 @@
import 'package:anitrack/src/data/anime.dart';
import 'package:anitrack/src/data/manga.dart';
import 'package:anitrack/src/data/search_result.dart';
import 'package:anitrack/src/data/source.dart';
import 'package:anitrack/src/data/type.dart';
import 'package:anitrack/src/service/anilist/anilist_client.dart';
import 'package:anitrack/src/service/anilist/model.dart';
import 'package:anitrack/src/ui/bloc/anime_list_bloc.dart' as list;
import 'package:anitrack/src/ui/bloc/navigation_bloc.dart';
import 'package:anitrack/src/ui/constants.dart';
@@ -22,6 +25,9 @@ class AnimeSearchBloc extends Bloc<AnimeSearchEvent, AnimeSearchState> {
on<ResultTappedEvent>(_onResultTapped);
}
// The client to use for AniList GraphQL queries.
final _anilistClient = AniListClient();
Future<void> _onRequested(
AnimeSearchRequestedEvent event,
Emitter<AnimeSearchState> emit,
@@ -67,23 +73,21 @@ class AnimeSearchBloc extends Bloc<AnimeSearchEvent, AnimeSearchState> {
if (state.trackingType == TrackingMediumType.anime) {
// Anime
final result = await Jikan().searchAnime(
query: state.searchQuery,
);
final result = await _anilistClient.searchAnime(state.searchQuery);
emit(
state.copyWith(
working: false,
searchResults: result
.map(
(Anime anime) => SearchResult(
(anime) => SearchResult(
anime.title,
anime.malId.toString(),
anime.id,
anime.episodes,
anime.imageUrl,
anime.synopsis ?? '',
anime.airing,
anime.broadcast?.split(' ').first,
anime.description ?? '',
anime.isAiring,
anime.broadcastDay,
),
)
.toList(),
@@ -91,21 +95,19 @@ class AnimeSearchBloc extends Bloc<AnimeSearchEvent, AnimeSearchState> {
);
} else {
// Manga
final result = await Jikan().searchManga(
query: state.searchQuery,
);
final result = await _anilistClient.searchManga(state.searchQuery);
emit(
state.copyWith(
working: false,
searchResults: result
.map(
(Manga manga) => SearchResult(
(manga) => SearchResult(
manga.title,
manga.malId.toString(),
manga.id,
manga.chapters,
manga.imageUrl,
manga.synopsis ?? '',
manga.description ?? '',
// TODO(Unknown): Implement for Manga
false,
null,
@@ -133,6 +135,7 @@ class AnimeSearchBloc extends Bloc<AnimeSearchEvent, AnimeSearchState> {
event.result.thumbnailUrl,
event.result.isAiring,
event.result.broadcastDay,
TrackingDataSource.anilist,
),
)
: list.MangaAddedEvent(
@@ -144,6 +147,7 @@ class AnimeSearchBloc extends Bloc<AnimeSearchEvent, AnimeSearchState> {
0,
event.result.total,
event.result.thumbnailUrl,
TrackingDataSource.anilist,
),
),
);

View File

@@ -1,5 +1,7 @@
import 'dart:async';
import 'package:anitrack/src/data/source.dart';
import 'package:anitrack/src/service/anilist/anilist_client.dart';
import 'package:anitrack/src/ui/bloc/anime_list_bloc.dart';
import 'package:bloc/bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@@ -43,9 +45,18 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
String? broadcastDay;
bool airing;
try {
final apiData = await Jikan().getAnime(int.parse(anime.id));
airing = apiData.airing;
broadcastDay = apiData.broadcast?.split(' ').first;
switch (anime.source) {
case TrackingDataSource.mal:
final apiData = await Jikan().getAnime(int.parse(anime.id));
airing = apiData.airing;
broadcastDay = apiData.broadcast?.split(' ').first;
case TrackingDataSource.anilist:
final apiData = await GetIt.I.get<AniListClient>().getAnimeById(
anime.id,
);
airing = apiData.isAiring;
broadcastDay = apiData.broadcastDay;
}
} catch (ex) {
print('API request for anime ${anime.id} failed: $ex');
airing = false;

View File

@@ -4,6 +4,7 @@ import 'dart:io';
import 'package:anitrack/i18n/strings.g.dart';
import 'package:anitrack/src/data/anime.dart';
import 'package:anitrack/src/data/manga.dart';
import 'package:anitrack/src/data/source.dart';
import 'package:anitrack/src/data/type.dart';
import 'package:anitrack/src/service/database.dart';
import 'package:anitrack/src/ui/bloc/anime_list_bloc.dart';
@@ -131,6 +132,7 @@ class SettingsBloc extends Bloc<SettingsEvent, SettingsState> {
// NOTE: When the calendar gets refreshed, this should also get cleared
true,
null,
TrackingDataSource.mal,
),
);
}
@@ -207,6 +209,7 @@ class SettingsBloc extends Bloc<SettingsEvent, SettingsState> {
// 0 means that MAL does not know
totalChapters == 0 ? null : totalChapters,
data.imageUrl,
TrackingDataSource.mal,
),
);
}