mirror of
https://github.com/kodjodevf/mangayomi.git
synced 2026-04-20 23:22:07 +00:00
Remove code duplication + BugFix
Fixed a bug, where newly added animes to AniList would register as "Rewatching" instead of "Plan to watch".
This commit is contained in:
parent
404e18c4a5
commit
4f1e2cb492
7 changed files with 298 additions and 919 deletions
|
|
@ -95,7 +95,7 @@ enum TrackStatus {
|
|||
onHold,
|
||||
dropped,
|
||||
planToRead,
|
||||
rereading,
|
||||
reReading,
|
||||
watching,
|
||||
planToWatch,
|
||||
reWatching,
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ const _TrackstatusEnumValueMap = {
|
|||
'onHold': 2,
|
||||
'dropped': 3,
|
||||
'planToRead': 4,
|
||||
'rereading': 5,
|
||||
'reReading': 5,
|
||||
'watching': 6,
|
||||
'planToWatch': 7,
|
||||
'reWatching': 8,
|
||||
|
|
@ -244,7 +244,7 @@ const _TrackstatusValueEnumMap = {
|
|||
2: TrackStatus.onHold,
|
||||
3: TrackStatus.dropped,
|
||||
4: TrackStatus.planToRead,
|
||||
5: TrackStatus.rereading,
|
||||
5: TrackStatus.reReading,
|
||||
6: TrackStatus.watching,
|
||||
7: TrackStatus.planToWatch,
|
||||
8: TrackStatus.reWatching,
|
||||
|
|
|
|||
|
|
@ -15,115 +15,69 @@ class TrackState extends _$TrackState {
|
|||
return track!;
|
||||
}
|
||||
|
||||
Future updateManga() async {
|
||||
Track? updateTrack;
|
||||
updateTrack = await switch (track!.syncId) {
|
||||
1 => switch (itemType) {
|
||||
ItemType.manga => ref
|
||||
.read(
|
||||
myAnimeListProvider(
|
||||
syncId: track!.syncId!,
|
||||
itemType: itemType,
|
||||
).notifier,
|
||||
)
|
||||
.updateManga(track!),
|
||||
_ => ref
|
||||
.read(
|
||||
myAnimeListProvider(
|
||||
syncId: track!.syncId!,
|
||||
itemType: itemType,
|
||||
).notifier,
|
||||
)
|
||||
.updateAnime(track!),
|
||||
},
|
||||
2 => switch (itemType) {
|
||||
ItemType.manga => ref
|
||||
.read(
|
||||
anilistProvider(
|
||||
syncId: track!.syncId!,
|
||||
itemType: itemType,
|
||||
).notifier,
|
||||
)
|
||||
.updateLibManga(track!),
|
||||
_ => ref
|
||||
.read(
|
||||
anilistProvider(
|
||||
syncId: track!.syncId!,
|
||||
itemType: itemType,
|
||||
).notifier,
|
||||
)
|
||||
.updateLibAnime(track!),
|
||||
},
|
||||
_ => ref
|
||||
.read(
|
||||
kitsuProvider(syncId: track!.syncId!, itemType: itemType).notifier,
|
||||
)
|
||||
.updateLib(track!, _isManga),
|
||||
dynamic getNotifier(int syncId) {
|
||||
return switch (syncId) {
|
||||
1 => ref.read(
|
||||
myAnimeListProvider(syncId: syncId, itemType: itemType).notifier,
|
||||
),
|
||||
2 => ref.read(
|
||||
anilistProvider(syncId: syncId, itemType: itemType).notifier,
|
||||
),
|
||||
3 => ref.read(kitsuProvider(syncId: syncId, itemType: itemType).notifier),
|
||||
_ => throw Exception('Unsupported syncId: $syncId'),
|
||||
};
|
||||
}
|
||||
|
||||
void writeBack(Track t) {
|
||||
ref
|
||||
.read(tracksProvider(syncId: track!.syncId!).notifier)
|
||||
.updateTrackManga(updateTrack, itemType!);
|
||||
.read(tracksProvider(syncId: t.syncId!).notifier)
|
||||
.updateTrackManga(t, itemType!);
|
||||
}
|
||||
|
||||
Future updateManga() async {
|
||||
final syncId = track!.syncId!;
|
||||
Track updateTrack = await getNotifier(syncId).update(track!, _isManga);
|
||||
writeBack(updateTrack);
|
||||
}
|
||||
|
||||
int getScoreMaxValue() {
|
||||
int? maxValue;
|
||||
if (track!.syncId == 1 || track!.syncId == 3) {
|
||||
maxValue = 10;
|
||||
} else if (track!.syncId == 2) {
|
||||
maxValue =
|
||||
ref
|
||||
.read(
|
||||
anilistProvider(
|
||||
syncId: track!.syncId!,
|
||||
itemType: itemType,
|
||||
).notifier,
|
||||
)
|
||||
.getScoreValue()
|
||||
.$1;
|
||||
final syncId = track!.syncId!;
|
||||
if (syncId == 2) {
|
||||
final tracker = getNotifier(syncId);
|
||||
return tracker.getScoreValue().$1;
|
||||
} else {
|
||||
return 10;
|
||||
}
|
||||
return maxValue!;
|
||||
}
|
||||
|
||||
String getTextMapper(String numberText) {
|
||||
if (track!.syncId == 1 || track!.syncId == 3) {
|
||||
} else if (track!.syncId == 2) {
|
||||
numberText = ref
|
||||
.read(anilistProvider(syncId: 2, itemType: itemType).notifier)
|
||||
.displayScore(int.parse(numberText));
|
||||
final syncId = track!.syncId!;
|
||||
if (syncId == 2) {
|
||||
final tracker = getNotifier(syncId);
|
||||
return tracker.displayScore(int.parse(numberText));
|
||||
} else {
|
||||
return numberText;
|
||||
}
|
||||
return numberText;
|
||||
}
|
||||
|
||||
int getScoreStep() {
|
||||
int? step;
|
||||
if (track!.syncId == 1 || track!.syncId == 3) {
|
||||
step = 1;
|
||||
} else if (track!.syncId == 2) {
|
||||
step =
|
||||
ref
|
||||
.read(
|
||||
anilistProvider(
|
||||
syncId: track!.syncId!,
|
||||
itemType: itemType,
|
||||
).notifier,
|
||||
)
|
||||
.getScoreValue()
|
||||
.$2;
|
||||
final syncId = track!.syncId!;
|
||||
if (syncId == 2) {
|
||||
final tracker = getNotifier(syncId);
|
||||
return tracker.getScoreValue().$2;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
return step!;
|
||||
}
|
||||
|
||||
String displayScore(int score) {
|
||||
String? result;
|
||||
if (track!.syncId == 1 || track!.syncId == 3) {
|
||||
result = score.toString();
|
||||
} else if (track!.syncId == 2) {
|
||||
result = ref
|
||||
.read(anilistProvider(syncId: 2, itemType: itemType).notifier)
|
||||
.displayScore(score);
|
||||
final syncId = track!.syncId!;
|
||||
if (syncId == 2) {
|
||||
final tracker = getNotifier(syncId);
|
||||
return tracker.displayScore(score);
|
||||
} else {
|
||||
return score.toString();
|
||||
}
|
||||
return result!;
|
||||
}
|
||||
|
||||
bool get _isManga => itemType == ItemType.manga;
|
||||
|
|
@ -134,7 +88,7 @@ class TrackState extends _$TrackState {
|
|||
int syncId,
|
||||
) async {
|
||||
Track? findManga;
|
||||
final track = Track(
|
||||
final newTrack = Track(
|
||||
mangaId: mangaId,
|
||||
score: 0,
|
||||
syncId: syncId,
|
||||
|
|
@ -143,113 +97,28 @@ class TrackState extends _$TrackState {
|
|||
title: trackSearch.title,
|
||||
lastChapterRead: 0,
|
||||
totalChapter: trackSearch.totalChapter,
|
||||
status: TrackStatus.planToRead,
|
||||
status: _isManga ? TrackStatus.planToRead : TrackStatus.planToWatch,
|
||||
startedReadingDate: 0,
|
||||
finishedReadingDate: 0,
|
||||
);
|
||||
final tracker = getNotifier(syncId);
|
||||
|
||||
if (syncId == 1) {
|
||||
findManga = await ref
|
||||
.read(
|
||||
myAnimeListProvider(syncId: syncId, itemType: itemType).notifier,
|
||||
)
|
||||
.findManga(track);
|
||||
findManga = await tracker.findLibItem(newTrack, _isManga);
|
||||
} else if (syncId == 2) {
|
||||
findManga =
|
||||
_isManga
|
||||
? await ref
|
||||
.read(
|
||||
anilistProvider(
|
||||
syncId: syncId,
|
||||
itemType: itemType,
|
||||
).notifier,
|
||||
)
|
||||
.findLibManga(track)
|
||||
: await ref
|
||||
.read(
|
||||
anilistProvider(
|
||||
syncId: syncId,
|
||||
itemType: itemType,
|
||||
).notifier,
|
||||
)
|
||||
.findLibAnime(track);
|
||||
findManga ??=
|
||||
_isManga
|
||||
? await ref
|
||||
.read(
|
||||
anilistProvider(
|
||||
syncId: syncId,
|
||||
itemType: itemType,
|
||||
).notifier,
|
||||
)
|
||||
.addLibManga(track)
|
||||
: await ref
|
||||
.read(
|
||||
anilistProvider(
|
||||
syncId: syncId,
|
||||
itemType: itemType,
|
||||
).notifier,
|
||||
)
|
||||
.addLibAnime(track);
|
||||
findManga = await tracker.findLibItem(newTrack, _isManga);
|
||||
findManga ??= await tracker.update(newTrack, _isManga);
|
||||
} else if (syncId == 3) {
|
||||
findManga = await ref
|
||||
.read(kitsuProvider(syncId: syncId, itemType: itemType).notifier)
|
||||
.addLib(track, _isManga);
|
||||
findManga = await tracker.update(newTrack, _isManga);
|
||||
}
|
||||
|
||||
ref
|
||||
.read(tracksProvider(syncId: syncId).notifier)
|
||||
.updateTrackManga(findManga!, itemType!);
|
||||
writeBack(findManga!);
|
||||
}
|
||||
|
||||
List<TrackStatus> getStatusList() {
|
||||
List<TrackStatus> statusList = [];
|
||||
List<TrackStatus> list = [];
|
||||
if (track!.syncId == 1) {
|
||||
statusList =
|
||||
_isManga
|
||||
? ref
|
||||
.read(
|
||||
myAnimeListProvider(
|
||||
syncId: track!.syncId!,
|
||||
itemType: itemType,
|
||||
).notifier,
|
||||
)
|
||||
.myAnimeListStatusListManga
|
||||
: ref
|
||||
.read(
|
||||
myAnimeListProvider(
|
||||
syncId: track!.syncId!,
|
||||
itemType: itemType,
|
||||
).notifier,
|
||||
)
|
||||
.myAnimeListStatusListAnime;
|
||||
} else if (track!.syncId == 2) {
|
||||
statusList =
|
||||
_isManga
|
||||
? ref
|
||||
.read(
|
||||
anilistProvider(
|
||||
syncId: track!.syncId!,
|
||||
itemType: itemType,
|
||||
).notifier,
|
||||
)
|
||||
.aniListStatusListManga
|
||||
: ref
|
||||
.read(
|
||||
anilistProvider(
|
||||
syncId: track!.syncId!,
|
||||
itemType: itemType,
|
||||
).notifier,
|
||||
)
|
||||
.aniListStatusListAnime;
|
||||
} else if (track!.syncId == 3) {
|
||||
statusList = ref
|
||||
.read(
|
||||
kitsuProvider(syncId: track!.syncId!, itemType: itemType).notifier,
|
||||
)
|
||||
.kitsuStatusList(_isManga);
|
||||
}
|
||||
final syncId = track!.syncId!;
|
||||
final tracker = getNotifier(syncId);
|
||||
List<TrackStatus> statusList = tracker.statusList(_isManga);
|
||||
for (var element in TrackStatus.values) {
|
||||
if (statusList.contains(element)) {
|
||||
list.add(element);
|
||||
|
|
@ -259,82 +128,14 @@ class TrackState extends _$TrackState {
|
|||
}
|
||||
|
||||
Future<Track?> findManga() async {
|
||||
Track? findManga;
|
||||
if (track!.syncId == 1) {
|
||||
findManga = await ref
|
||||
.read(
|
||||
myAnimeListProvider(
|
||||
syncId: track!.syncId!,
|
||||
itemType: itemType,
|
||||
).notifier,
|
||||
)
|
||||
.findManga(track!);
|
||||
} else if (track!.syncId == 2) {
|
||||
findManga =
|
||||
_isManga
|
||||
? await ref
|
||||
.read(
|
||||
anilistProvider(
|
||||
syncId: track!.syncId!,
|
||||
itemType: itemType,
|
||||
).notifier,
|
||||
)
|
||||
.findLibManga(track!)
|
||||
: await ref
|
||||
.read(
|
||||
anilistProvider(
|
||||
syncId: track!.syncId!,
|
||||
itemType: itemType,
|
||||
).notifier,
|
||||
)
|
||||
.findLibAnime(track!);
|
||||
} else if (track!.syncId == 3) {
|
||||
findManga = await ref
|
||||
.read(
|
||||
kitsuProvider(syncId: track!.syncId!, itemType: itemType).notifier,
|
||||
)
|
||||
.findLibItem(track!, _isManga);
|
||||
}
|
||||
return findManga;
|
||||
final syncId = track!.syncId!;
|
||||
final tracker = getNotifier(syncId);
|
||||
return await tracker.findLibItem(track!, _isManga);
|
||||
}
|
||||
|
||||
Future<List<TrackSearch>?> search(String query) async {
|
||||
List<TrackSearch>? tracks;
|
||||
if (track!.syncId == 1) {
|
||||
tracks = await ref
|
||||
.read(
|
||||
myAnimeListProvider(
|
||||
syncId: track!.syncId!,
|
||||
itemType: itemType,
|
||||
).notifier,
|
||||
)
|
||||
.search(query);
|
||||
} else if (track!.syncId == 2) {
|
||||
tracks =
|
||||
_isManga
|
||||
? await ref
|
||||
.read(
|
||||
anilistProvider(
|
||||
syncId: track!.syncId!,
|
||||
itemType: itemType,
|
||||
).notifier,
|
||||
)
|
||||
.search(query)
|
||||
: await ref
|
||||
.read(
|
||||
anilistProvider(
|
||||
syncId: track!.syncId!,
|
||||
itemType: itemType,
|
||||
).notifier,
|
||||
)
|
||||
.searchAnime(query);
|
||||
} else if (track!.syncId == 3) {
|
||||
tracks = await ref
|
||||
.read(
|
||||
kitsuProvider(syncId: track!.syncId!, itemType: itemType).notifier,
|
||||
)
|
||||
.search(query, _isManga);
|
||||
}
|
||||
return tracks;
|
||||
final syncId = track!.syncId!;
|
||||
final tracker = getNotifier(syncId);
|
||||
return await tracker.search(query, _isManga);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,15 +16,15 @@ part 'anilist.g.dart';
|
|||
@riverpod
|
||||
class Anilist extends _$Anilist {
|
||||
final http = MClient.init(reqcopyWith: {'useDartHttpClient': true});
|
||||
final String _clientId =
|
||||
(Platform.isWindows || Platform.isLinux) ? '13587' : '13588';
|
||||
static final isDesktop = Platform.isWindows || Platform.isLinux;
|
||||
final String _clientId = isDesktop ? '13587' : '13588';
|
||||
static const String _baseApiUrl = "https://graphql.anilist.co/";
|
||||
final String _redirectUri =
|
||||
(Platform.isWindows || Platform.isLinux)
|
||||
isDesktop
|
||||
? 'http://localhost:43824/success?code=1337'
|
||||
: 'mangayomi://success?code=1337';
|
||||
final String _clientSecret =
|
||||
(Platform.isWindows || Platform.isLinux)
|
||||
isDesktop
|
||||
? 'tJA13cAR2tCCXrJCwwvmwEDbWRoIaahFiJTXToHd'
|
||||
: 'G2fFUiGtgFd60D0lCkhgGKvMmrCfDmZXADQIzWXr';
|
||||
|
||||
|
|
@ -33,11 +33,10 @@ class Anilist extends _$Anilist {
|
|||
|
||||
Future<bool?> login() async {
|
||||
final callbackUrlScheme =
|
||||
(Platform.isWindows || Platform.isLinux)
|
||||
? 'http://localhost:43824'
|
||||
: 'mangayomi';
|
||||
isDesktop ? 'http://localhost:43824' : 'mangayomi';
|
||||
final loginUrl =
|
||||
'https://anilist.co/api/v2/oauth/authorize?client_id=$_clientId&redirect_uri=$_redirectUri&response_type=code';
|
||||
'https://anilist.co/api/v2/oauth/authorize?client_id=$_clientId'
|
||||
'&redirect_uri=$_redirectUri&response_type=code';
|
||||
|
||||
try {
|
||||
final uri = await FlutterWebAuth2.authenticate(
|
||||
|
|
@ -76,84 +75,19 @@ class Anilist extends _$Anilist {
|
|||
}
|
||||
}
|
||||
|
||||
Future<Track> addLibManga(Track track) async {
|
||||
final accessToken = await _getAccesToken();
|
||||
Future<Track> update(Track track, bool isManga) async {
|
||||
final isNew = track.libraryId == null;
|
||||
final opName = isNew ? 'AddEntry' : 'UpdateEntry';
|
||||
final idVarName = isNew ? 'mediaId' : 'id';
|
||||
final idVarValue = isNew ? track.mediaId : track.libraryId!;
|
||||
|
||||
const query = '''
|
||||
mutation AddManga(\$mangaId: Int, \$progress: Int, \$status: MediaListStatus) {
|
||||
SaveMediaListEntry(mediaId: \$mangaId, progress: \$progress, status: \$status) {
|
||||
id
|
||||
status
|
||||
}
|
||||
}
|
||||
''';
|
||||
|
||||
final body = {
|
||||
"query": query,
|
||||
"variables": {
|
||||
"mangaId": track.mediaId,
|
||||
"progress": track.lastChapterRead,
|
||||
"status": toAniListStatusManga(track.status),
|
||||
},
|
||||
};
|
||||
|
||||
final response = await http.post(
|
||||
Uri.parse(_baseApiUrl),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
'Authorization': 'Bearer $accessToken',
|
||||
},
|
||||
body: json.encode(body),
|
||||
);
|
||||
final data = json.decode(response.body);
|
||||
track.libraryId = data['data']['SaveMediaListEntry']['id'];
|
||||
return track;
|
||||
}
|
||||
|
||||
Future<Track> addLibAnime(Track track) async {
|
||||
final accessToken = await _getAccesToken();
|
||||
|
||||
const query = '''
|
||||
mutation AddAnime(\$animeId: Int, \$progress: Int, \$status: MediaListStatus) {
|
||||
SaveMediaListEntry(mediaId: \$animeId, progress: \$progress, status: \$status) {
|
||||
id
|
||||
status
|
||||
}
|
||||
}
|
||||
''';
|
||||
final body = {
|
||||
"query": query,
|
||||
"variables": {
|
||||
"animeId": track.mediaId,
|
||||
"progress": track.lastChapterRead,
|
||||
"status": toAniListStatusAnime(track.status),
|
||||
},
|
||||
};
|
||||
|
||||
final response = await http.post(
|
||||
Uri.parse(_baseApiUrl),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
'Authorization': 'Bearer $accessToken',
|
||||
},
|
||||
body: json.encode(body),
|
||||
);
|
||||
final data = json.decode(response.body);
|
||||
track.libraryId = data['data']['SaveMediaListEntry']['id'];
|
||||
return track;
|
||||
}
|
||||
|
||||
Future<Track> updateLibManga(Track track) async {
|
||||
final accessToken = await _getAccesToken();
|
||||
const query = '''
|
||||
mutation UpdateManga(\$listId: Int, \$progress: Int, \$status: MediaListStatus, \$score: Int, \$startedAt: FuzzyDateInput, \$completedAt: FuzzyDateInput) {
|
||||
final document = '''
|
||||
mutation $opName(\$$idVarName: Int!, \$progress: Int!, \$status: MediaListStatus${isNew ? '' : ', \$score: Int, \$startedAt: FuzzyDateInput, \$completedAt: FuzzyDateInput'} ) {
|
||||
SaveMediaListEntry(
|
||||
id: \$listId,
|
||||
${isNew ? 'mediaId' : 'id'}: \$$idVarName,
|
||||
progress: \$progress,
|
||||
status: \$status,
|
||||
scoreRaw: \$score,
|
||||
startedAt: \$startedAt,
|
||||
completedAt: \$completedAt,
|
||||
${!isNew ? 'scoreRaw: \$score, startedAt: \$startedAt, completedAt: \$completedAt,' : ''}
|
||||
) {
|
||||
id
|
||||
status
|
||||
|
|
@ -162,115 +96,47 @@ class Anilist extends _$Anilist {
|
|||
}
|
||||
''';
|
||||
|
||||
final body = {
|
||||
"query": query,
|
||||
"variables": {
|
||||
"listId": track.libraryId,
|
||||
"progress": track.lastChapterRead,
|
||||
"status": toAniListStatusManga(track.status),
|
||||
"score": track.score!,
|
||||
"startedAt": createDate(track.startedReadingDate!),
|
||||
"completedAt": createDate(track.finishedReadingDate!),
|
||||
},
|
||||
final vars = {
|
||||
idVarName: idVarValue,
|
||||
'progress': track.lastChapterRead,
|
||||
'status': toAniListStatus(track.status, isManga),
|
||||
if (!isNew) 'score': track.score!,
|
||||
if (!isNew) 'startedAt': createDate(track.startedReadingDate!),
|
||||
if (!isNew) 'completedAt': createDate(track.finishedReadingDate!),
|
||||
};
|
||||
|
||||
await http.post(
|
||||
Uri.parse(_baseApiUrl),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
'Authorization': 'Bearer $accessToken',
|
||||
},
|
||||
body: json.encode(body),
|
||||
);
|
||||
final data = await _executeGraphQL(document, vars);
|
||||
final entry = data['SaveMediaListEntry'] as Map<String, dynamic>;
|
||||
track.libraryId = entry['id'] as int;
|
||||
return track;
|
||||
}
|
||||
|
||||
Future<Track> updateLibAnime(Track track) async {
|
||||
final accessToken = await _getAccesToken();
|
||||
const query = '''
|
||||
mutation UpdateAnime(\$listId: Int, \$progress: Int, \$status: MediaListStatus, \$score: Int, \$startedAt: FuzzyDateInput, \$completedAt: FuzzyDateInput) {
|
||||
SaveMediaListEntry(
|
||||
id: \$listId,
|
||||
progress: \$progress,
|
||||
status: \$status,
|
||||
scoreRaw: \$score,
|
||||
startedAt: \$startedAt,
|
||||
completedAt: \$completedAt,
|
||||
) {
|
||||
id
|
||||
status
|
||||
progress
|
||||
}
|
||||
}
|
||||
''';
|
||||
|
||||
final body = {
|
||||
"query": query,
|
||||
"variables": {
|
||||
"listId": track.libraryId,
|
||||
"progress": track.lastChapterRead,
|
||||
"status": toAniListStatusAnime(track.status),
|
||||
"score": track.score!,
|
||||
"startedAt": createDate(track.startedReadingDate!),
|
||||
"completedAt": createDate(track.finishedReadingDate!),
|
||||
},
|
||||
};
|
||||
await http.post(
|
||||
Uri.parse(_baseApiUrl),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
'Authorization': 'Bearer $accessToken',
|
||||
},
|
||||
body: json.encode(body),
|
||||
);
|
||||
return track;
|
||||
}
|
||||
|
||||
Future<List<TrackSearch>> search(String search) async {
|
||||
final accessToken = await _getAccesToken();
|
||||
const query = '''
|
||||
Future<List<TrackSearch>> search(String search, bool isManga) async {
|
||||
final type = isManga ? "MANGA" : "ANIME";
|
||||
final contentUnit = isManga ? "chapters" : "episodes";
|
||||
final query = '''
|
||||
query Search(\$query: String) {
|
||||
Page(perPage: 50) {
|
||||
media(search: \$query, type: MANGA, format_not_in: [NOVEL]) {
|
||||
media(search: \$query, type: $type, format_not_in: [NOVEL]) {
|
||||
id
|
||||
title {
|
||||
userPreferred
|
||||
}
|
||||
coverImage {
|
||||
large
|
||||
}
|
||||
title { userPreferred }
|
||||
coverImage { large }
|
||||
format
|
||||
status
|
||||
chapters
|
||||
$contentUnit
|
||||
description
|
||||
startDate {
|
||||
year
|
||||
month
|
||||
day
|
||||
}
|
||||
startDate { year month day }
|
||||
}
|
||||
}
|
||||
}
|
||||
''';
|
||||
|
||||
final body = {
|
||||
"query": query,
|
||||
"variables": {"query": search},
|
||||
};
|
||||
final vars = {"query": search};
|
||||
|
||||
final response = await http.post(
|
||||
Uri.parse(_baseApiUrl),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
'Authorization': 'Bearer $accessToken',
|
||||
},
|
||||
body: json.encode(body),
|
||||
);
|
||||
|
||||
final data = json.decode(response.body);
|
||||
final data = await _executeGraphQL(query, vars);
|
||||
|
||||
final entries = List<Map<String, dynamic>>.from(
|
||||
data['data']['Page']['media'],
|
||||
data['Page']['media'] as List,
|
||||
);
|
||||
return entries
|
||||
.map(
|
||||
|
|
@ -280,7 +146,7 @@ class Anilist extends _$Anilist {
|
|||
trackingUrl: "",
|
||||
mediaId: jsonRes['id'],
|
||||
summary: jsonRes['description'] ?? "",
|
||||
totalChapter: jsonRes['chapters'] ?? 0,
|
||||
totalChapter: jsonRes[contentUnit] ?? 0,
|
||||
coverUrl: jsonRes['coverImage']['large'] ?? "",
|
||||
title: jsonRes['title']['userPreferred'],
|
||||
startDate:
|
||||
|
|
@ -295,229 +161,77 @@ class Anilist extends _$Anilist {
|
|||
.toList();
|
||||
}
|
||||
|
||||
Future<List<TrackSearch>> searchAnime(String search) async {
|
||||
final accessToken = await _getAccesToken();
|
||||
const query = '''
|
||||
query Search(\$query: String) {
|
||||
Page(perPage: 50) {
|
||||
media(search: \$query, type: ANIME) {
|
||||
id
|
||||
title {
|
||||
userPreferred
|
||||
}
|
||||
coverImage {
|
||||
large
|
||||
}
|
||||
format
|
||||
status
|
||||
episodes
|
||||
description
|
||||
startDate {
|
||||
year
|
||||
month
|
||||
day
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
''';
|
||||
|
||||
final body = {
|
||||
"query": query,
|
||||
"variables": {"query": search},
|
||||
};
|
||||
|
||||
final response = await http.post(
|
||||
Uri.parse(_baseApiUrl),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
'Authorization': 'Bearer $accessToken',
|
||||
},
|
||||
body: json.encode(body),
|
||||
Future<Track?> findLibItem(Track track, bool isManga) async {
|
||||
final userId = int.parse(
|
||||
ref.watch(tracksProvider(syncId: syncId))!.username!,
|
||||
);
|
||||
final type = isManga ? "MANGA" : "ANIME";
|
||||
final typeVar = isManga ? "manga_id" : "anime_id";
|
||||
final contentUnit = isManga ? "chapters" : "episodes";
|
||||
|
||||
final data = json.decode(response.body);
|
||||
|
||||
final entries = List<Map<String, dynamic>>.from(
|
||||
data['data']['Page']['media'],
|
||||
);
|
||||
return entries
|
||||
.map(
|
||||
(jsonRes) => TrackSearch(
|
||||
libraryId: jsonRes['id'],
|
||||
syncId: syncId,
|
||||
trackingUrl: "",
|
||||
mediaId: jsonRes['id'],
|
||||
summary: jsonRes['description'] ?? "",
|
||||
totalChapter: jsonRes['episodes'] ?? 0,
|
||||
coverUrl: jsonRes['coverImage']['large'] ?? "",
|
||||
title: jsonRes['title']['userPreferred'],
|
||||
startDate:
|
||||
jsonRes["start_date"] ??
|
||||
DateTime.fromMillisecondsSinceEpoch(
|
||||
parseDate(jsonRes, 'startDate'),
|
||||
).toString(),
|
||||
publishingType: "",
|
||||
publishingStatus: jsonRes['status'],
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
Future<Track?> findLibManga(Track track) async {
|
||||
final userId = ref.watch(tracksProvider(syncId: syncId))!.username;
|
||||
|
||||
final accessToken = await _getAccesToken();
|
||||
const query = '''
|
||||
query(\$id: Int!, \$manga_id: Int!) {
|
||||
final query = '''
|
||||
query(\$id: Int!, \$$typeVar: Int!) {
|
||||
Page {
|
||||
mediaList(userId: \$id, type: MANGA, mediaId: \$manga_id) {
|
||||
mediaList(userId: \$id, type: $type, mediaId: \$$typeVar) {
|
||||
id
|
||||
status
|
||||
scoreRaw: score(format: POINT_100)
|
||||
progress
|
||||
startedAt {
|
||||
year
|
||||
month
|
||||
day
|
||||
}
|
||||
completedAt {
|
||||
year
|
||||
month
|
||||
day
|
||||
}
|
||||
startedAt { year month day }
|
||||
completedAt { year month day }
|
||||
media {
|
||||
id
|
||||
title {
|
||||
userPreferred
|
||||
}
|
||||
coverImage {
|
||||
large
|
||||
}
|
||||
title { userPreferred }
|
||||
coverImage { large }
|
||||
format
|
||||
status
|
||||
chapters
|
||||
$contentUnit
|
||||
description
|
||||
startDate {
|
||||
year
|
||||
month
|
||||
day
|
||||
}
|
||||
startDate { year month day }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
''';
|
||||
|
||||
final body = {
|
||||
"query": query,
|
||||
"variables": {"id": int.parse(userId!), "manga_id": track.mediaId},
|
||||
};
|
||||
final vars = {"id": userId, typeVar: track.mediaId};
|
||||
|
||||
final response = await http.post(
|
||||
Uri.parse(_baseApiUrl),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
'Authorization': 'Bearer $accessToken',
|
||||
},
|
||||
body: json.encode(body),
|
||||
);
|
||||
final data = json.decode(response.body);
|
||||
final data = await _executeGraphQL(query, vars);
|
||||
final entries = List<Map<String, dynamic>>.from(
|
||||
data['data']['Page']['mediaList'],
|
||||
data['Page']['mediaList'] as List,
|
||||
);
|
||||
if (entries.isNotEmpty) {
|
||||
final jsonRes = entries.first;
|
||||
track.libraryId = jsonRes['id'];
|
||||
track.syncId = syncId;
|
||||
track.mediaId = jsonRes['media']['id'];
|
||||
track.status = _getALTrackStatusManga(jsonRes['status']);
|
||||
track.title = jsonRes['media']['title']['userPreferred'] ?? '';
|
||||
track.score = jsonRes['scoreRaw'] ?? 0;
|
||||
track.lastChapterRead = jsonRes['progress'] ?? 0;
|
||||
track.startedReadingDate = parseDate(jsonRes, 'startedAt');
|
||||
track.finishedReadingDate = parseDate(jsonRes, 'completedAt');
|
||||
track.totalChapter = jsonRes['media']["chapters"] ?? 0;
|
||||
}
|
||||
return entries.isNotEmpty ? track : null;
|
||||
if (entries.isEmpty) return null;
|
||||
|
||||
final jsonRes = entries.first;
|
||||
return track
|
||||
..libraryId = jsonRes['id'] as int
|
||||
..syncId = syncId
|
||||
..mediaId = jsonRes['media']['id'] as int
|
||||
..status = getALTrackStatus(jsonRes['status'], isManga)
|
||||
..title = jsonRes['media']['title']['userPreferred'] ?? ''
|
||||
..score = jsonRes['scoreRaw'] as int?
|
||||
..lastChapterRead = jsonRes['progress'] as int? ?? 0
|
||||
..startedReadingDate = parseDate(jsonRes, 'startedAt')
|
||||
..finishedReadingDate = parseDate(jsonRes, 'completedAt')
|
||||
..totalChapter = jsonRes['media'][contentUnit] as int? ?? 0;
|
||||
}
|
||||
|
||||
Future<Track?> findLibAnime(Track track) async {
|
||||
final userId = ref.watch(tracksProvider(syncId: syncId))!.username;
|
||||
|
||||
final accessToken = await _getAccesToken();
|
||||
const query = '''
|
||||
query(\$id: Int!, \$anime_id: Int!) {
|
||||
Page {
|
||||
mediaList(userId: \$id, type: ANIME, mediaId: \$anime_id) {
|
||||
id
|
||||
status
|
||||
scoreRaw: score(format: POINT_100)
|
||||
progress
|
||||
startedAt {
|
||||
year
|
||||
month
|
||||
day
|
||||
}
|
||||
completedAt {
|
||||
year
|
||||
month
|
||||
day
|
||||
}
|
||||
media {
|
||||
id
|
||||
title {
|
||||
userPreferred
|
||||
}
|
||||
coverImage {
|
||||
large
|
||||
}
|
||||
format
|
||||
status
|
||||
episodes
|
||||
description
|
||||
startDate {
|
||||
year
|
||||
month
|
||||
day
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
''';
|
||||
|
||||
final body = {
|
||||
"query": query,
|
||||
"variables": {"id": int.parse(userId!), "anime_id": track.mediaId},
|
||||
};
|
||||
|
||||
Future<Map<String, dynamic>> _executeGraphQL(
|
||||
String document,
|
||||
Map<String, dynamic> variables,
|
||||
) async {
|
||||
final response = await http.post(
|
||||
Uri.parse(_baseApiUrl),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
'Authorization': 'Bearer $accessToken',
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer ${await getAccessToken()}',
|
||||
},
|
||||
body: json.encode(body),
|
||||
body: jsonEncode({'query': document, 'variables': variables}),
|
||||
);
|
||||
final data = json.decode(response.body);
|
||||
final entries = List<Map<String, dynamic>>.from(
|
||||
data['data']['Page']['mediaList'],
|
||||
);
|
||||
if (entries.isNotEmpty) {
|
||||
final jsonRes = entries.first;
|
||||
track.libraryId = jsonRes['id'];
|
||||
track.syncId = syncId;
|
||||
track.mediaId = jsonRes['media']['id'];
|
||||
track.status = _getALTrackStatusAnime(jsonRes['status']);
|
||||
track.title = jsonRes['media']['title']['userPreferred'] ?? '';
|
||||
track.score = jsonRes['scoreRaw'] ?? 0;
|
||||
track.lastChapterRead = jsonRes['progress'] ?? 0;
|
||||
track.startedReadingDate = parseDate(jsonRes, 'startedAt');
|
||||
track.finishedReadingDate = parseDate(jsonRes, 'completedAt');
|
||||
track.totalChapter = jsonRes['media']["episodes"] ?? 0;
|
||||
}
|
||||
return entries.isNotEmpty ? track : null;
|
||||
|
||||
final decoded = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
|
||||
return decoded['data'] as Map<String, dynamic>;
|
||||
}
|
||||
|
||||
Future<(String, String)> _getCurrentUser(String accessToken) async {
|
||||
|
|
@ -551,7 +265,7 @@ class Anilist extends _$Anilist {
|
|||
);
|
||||
}
|
||||
|
||||
Future<String> _getAccesToken() async {
|
||||
Future<String> getAccessToken() async {
|
||||
final track = ref.watch(tracksProvider(syncId: syncId));
|
||||
final mALOAuth = OAuth.fromJson(
|
||||
jsonDecode(track!.oAuth!) as Map<String, dynamic>,
|
||||
|
|
@ -590,63 +304,35 @@ class Anilist extends _$Anilist {
|
|||
};
|
||||
}
|
||||
|
||||
TrackStatus _getALTrackStatusManga(String status) {
|
||||
TrackStatus getALTrackStatus(String status, bool isManga) {
|
||||
return switch (status) {
|
||||
"CURRENT" => TrackStatus.reading,
|
||||
"CURRENT" => isManga ? TrackStatus.reading : TrackStatus.watching,
|
||||
"COMPLETED" => TrackStatus.completed,
|
||||
"PAUSED" => TrackStatus.onHold,
|
||||
"DROPPED" => TrackStatus.dropped,
|
||||
"PLANNING" => TrackStatus.planToRead,
|
||||
_ => TrackStatus.rereading,
|
||||
"PLANNING" => isManga ? TrackStatus.planToRead : TrackStatus.planToWatch,
|
||||
_ => isManga ? TrackStatus.reReading : TrackStatus.reWatching,
|
||||
};
|
||||
}
|
||||
|
||||
TrackStatus _getALTrackStatusAnime(String status) {
|
||||
return switch (status) {
|
||||
"CURRENT" => TrackStatus.watching,
|
||||
"COMPLETED" => TrackStatus.completed,
|
||||
"PAUSED" => TrackStatus.onHold,
|
||||
"DROPPED" => TrackStatus.dropped,
|
||||
"PLANNING" => TrackStatus.planToWatch,
|
||||
_ => TrackStatus.reWatching,
|
||||
};
|
||||
}
|
||||
|
||||
List<TrackStatus> aniListStatusListManga = [
|
||||
TrackStatus.reading,
|
||||
List<TrackStatus> statusList(bool isManga) => [
|
||||
isManga ? TrackStatus.reading : TrackStatus.watching,
|
||||
TrackStatus.completed,
|
||||
TrackStatus.onHold,
|
||||
TrackStatus.dropped,
|
||||
TrackStatus.planToRead,
|
||||
TrackStatus.rereading,
|
||||
];
|
||||
List<TrackStatus> aniListStatusListAnime = [
|
||||
TrackStatus.watching,
|
||||
TrackStatus.completed,
|
||||
TrackStatus.onHold,
|
||||
TrackStatus.dropped,
|
||||
TrackStatus.planToWatch,
|
||||
TrackStatus.reWatching,
|
||||
isManga ? TrackStatus.planToRead : TrackStatus.planToWatch,
|
||||
isManga ? TrackStatus.reReading : TrackStatus.reWatching,
|
||||
];
|
||||
|
||||
String? toAniListStatusManga(TrackStatus status) {
|
||||
String? toAniListStatus(TrackStatus status, bool isManga) {
|
||||
return switch (status) {
|
||||
TrackStatus.reading => "CURRENT",
|
||||
TrackStatus.reading when isManga => "CURRENT",
|
||||
TrackStatus.watching when !isManga => "CURRENT",
|
||||
TrackStatus.completed => "COMPLETED",
|
||||
TrackStatus.onHold => "PAUSED",
|
||||
TrackStatus.dropped => "DROPPED",
|
||||
TrackStatus.planToRead => "PLANNING",
|
||||
_ => "REPEATING",
|
||||
};
|
||||
}
|
||||
|
||||
String? toAniListStatusAnime(TrackStatus status) {
|
||||
return switch (status) {
|
||||
TrackStatus.watching => "CURRENT",
|
||||
TrackStatus.completed => "COMPLETED",
|
||||
TrackStatus.onHold => "PAUSED",
|
||||
TrackStatus.dropped => "DROPPED",
|
||||
TrackStatus.planToWatch => "PLANNING",
|
||||
TrackStatus.planToRead when isManga => "PLANNING",
|
||||
TrackStatus.planToWatch when !isManga => "PLANNING",
|
||||
_ => "REPEATING",
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,73 +72,57 @@ class Kitsu extends _$Kitsu {
|
|||
}
|
||||
}
|
||||
|
||||
Future<Track?> addLib(Track track, bool isManga) async {
|
||||
final userId = _getUserId();
|
||||
final accessToken = _getAccessToken();
|
||||
var data = jsonEncode({
|
||||
'data': {
|
||||
'type': 'libraryEntries',
|
||||
'attributes': {
|
||||
'status': toKitsuStatus(track.status, isManga),
|
||||
'progress': track.lastChapterRead,
|
||||
},
|
||||
'relationships': {
|
||||
'user': {
|
||||
'data': {'id': userId, 'type': 'users'},
|
||||
},
|
||||
'media': {
|
||||
'data': {'id': track.mediaId, 'type': isManga ? 'manga' : 'anime'},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
var response = await http.post(
|
||||
Uri.parse('${_baseUrl}library-entries'),
|
||||
headers: {
|
||||
'Content-Type': 'application/vnd.api+json',
|
||||
'Authorization': 'Bearer $accessToken',
|
||||
},
|
||||
body: data,
|
||||
Future<Track> update(Track track, bool isManga) async {
|
||||
final isNew = track.libraryId == null;
|
||||
final String? userId = isNew ? _getUserId() : null;
|
||||
final type = isManga ? 'manga' : 'anime';
|
||||
final url = Uri.parse(
|
||||
'${_baseUrl}library-entries${isNew ? "" : "/$track.libraryId"}',
|
||||
);
|
||||
if (response.statusCode != 200) {
|
||||
return await findLibItem(track, true);
|
||||
}
|
||||
|
||||
var jsonData = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
track.libraryId = int.parse(jsonData['data']['id']);
|
||||
return track;
|
||||
}
|
||||
|
||||
Future<Track> updateLib(Track track, bool isManga) async {
|
||||
final accessToken = _getAccessToken();
|
||||
final data = jsonEncode({
|
||||
final headers = {
|
||||
"Content-Type": "application/vnd.api+json",
|
||||
'Authorization': 'Bearer ${getAccessToken()}',
|
||||
};
|
||||
final payload = jsonEncode({
|
||||
"data": {
|
||||
"type": "libraryEntries",
|
||||
"id": track.libraryId,
|
||||
if (!isNew) "id": track.libraryId,
|
||||
"attributes": {
|
||||
"status": toKitsuStatus(track.status, isManga),
|
||||
"progress": track.lastChapterRead,
|
||||
"ratingTwenty": _toKitsuScore(track.score!),
|
||||
"startedAt": _convertDate(track.startedReadingDate!),
|
||||
"finishedAt": _convertDate(track.finishedReadingDate!),
|
||||
if (!isNew) "ratingTwenty": _toKitsuScore(track.score!),
|
||||
if (!isNew) "startedAt": _convertDate(track.startedReadingDate!),
|
||||
if (!isNew) "finishedAt": _convertDate(track.finishedReadingDate!),
|
||||
},
|
||||
if (isNew)
|
||||
"relationships": {
|
||||
'user': {
|
||||
'data': {'id': userId, 'type': 'users'},
|
||||
},
|
||||
'media': {
|
||||
'data': {'id': track.mediaId, 'type': type},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await http.patch(
|
||||
Uri.parse('${_baseUrl}library-entries/${track.libraryId}'),
|
||||
headers: {
|
||||
"Content-Type": "application/vnd.api+json",
|
||||
'Authorization': 'Bearer $accessToken',
|
||||
},
|
||||
body: data,
|
||||
);
|
||||
if (isNew) {
|
||||
var response = await http.post(url, headers: headers, body: payload);
|
||||
if (response.statusCode != 200) {
|
||||
final found = await findLibItem(track, true);
|
||||
if (found == null) {
|
||||
throw Exception('Could not add $type entry for ${track.mediaId}');
|
||||
}
|
||||
}
|
||||
var jsonData = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
track.libraryId = int.parse(jsonData['data']['id']);
|
||||
} else {
|
||||
await http.patch(url, headers: headers, body: payload);
|
||||
}
|
||||
return track;
|
||||
}
|
||||
|
||||
Future<List<TrackSearch>> search(String search, bool isManga) async {
|
||||
final accessToken = _getAccessToken();
|
||||
final accessToken = getAccessToken();
|
||||
|
||||
final url = Uri.parse(_algoliaKeyUrl);
|
||||
final algoliaKeyResponse = await makeGetRequest(url, accessToken);
|
||||
|
|
@ -185,7 +169,7 @@ class Kitsu extends _$Kitsu {
|
|||
Future<Track?> findLibItem(Track track, bool isManga) async {
|
||||
final type = isManga ? "manga" : "anime";
|
||||
final userId = _getUserId();
|
||||
final accessToken = _getAccessToken();
|
||||
final accessToken = getAccessToken();
|
||||
|
||||
final url = Uri.parse(
|
||||
'${_baseUrl}library-entries?filter[${type}_id]=${track.libraryId}&filter[user_id]=$userId&include=$type',
|
||||
|
|
@ -199,18 +183,17 @@ class Kitsu extends _$Kitsu {
|
|||
}
|
||||
|
||||
Future<Response> makeGetRequest(Uri url, String accessToken) async {
|
||||
final response = await http.get(
|
||||
return await http.get(
|
||||
url,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
'Authorization': 'Bearer $accessToken',
|
||||
},
|
||||
);
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<Track?> getItem(Track track, String type) async {
|
||||
final accessToken = _getAccessToken();
|
||||
final accessToken = getAccessToken();
|
||||
final url = Uri.parse(
|
||||
'${_baseUrl}library-entries?filter[id]=${track.mediaId}&include=$type',
|
||||
);
|
||||
|
|
@ -233,18 +216,18 @@ class Kitsu extends _$Kitsu {
|
|||
final included = jsonResponse['included'][0]["attributes"];
|
||||
final id = int.parse(obj["id"]);
|
||||
final totalChapter = type == 'manga' ? "chapterCount" : "episodeCount";
|
||||
track.mediaId = id;
|
||||
track.libraryId = id;
|
||||
track.syncId = syncId;
|
||||
track.trackingUrl = _mediaUrl(type, id);
|
||||
track.totalChapter = included[totalChapter] ?? 0;
|
||||
track.status = getKitsuTrackStatus(attributes["status"], type);
|
||||
track.score = ((attributes["ratingTwenty"] ?? 0) / 2).toInt();
|
||||
track.title = included["canonicalTitle"];
|
||||
track.lastChapterRead = attributes["progress"];
|
||||
track.startedReadingDate = _parseDate(attributes["startedAt"]);
|
||||
track.finishedReadingDate = _parseDate(attributes["finishedAt"]);
|
||||
return track;
|
||||
return track
|
||||
..mediaId = id
|
||||
..libraryId = id
|
||||
..syncId = syncId
|
||||
..trackingUrl = _mediaUrl(type, id)
|
||||
..totalChapter = included[totalChapter] ?? 0
|
||||
..status = getKitsuTrackStatus(attributes["status"], type)
|
||||
..score = ((attributes["ratingTwenty"] ?? 0) / 2).toInt()
|
||||
..title = included["canonicalTitle"]
|
||||
..lastChapterRead = attributes["progress"]
|
||||
..startedReadingDate = _parseDate(attributes["startedAt"])
|
||||
..finishedReadingDate = _parseDate(attributes["finishedAt"]);
|
||||
}
|
||||
|
||||
Future<(String, String)> _getCurrentUser(String accessToken) async {
|
||||
|
|
@ -257,7 +240,7 @@ class Kitsu extends _$Kitsu {
|
|||
);
|
||||
}
|
||||
|
||||
String _getAccessToken() {
|
||||
String getAccessToken() {
|
||||
final track = ref.watch(tracksProvider(syncId: syncId));
|
||||
final mAKOAuth = OAuth.fromJson(
|
||||
jsonDecode(track!.oAuth!) as Map<String, dynamic>,
|
||||
|
|
@ -286,7 +269,7 @@ class Kitsu extends _$Kitsu {
|
|||
};
|
||||
}
|
||||
|
||||
List<TrackStatus> kitsuStatusList(bool isManga) => [
|
||||
List<TrackStatus> statusList(bool isManga) => [
|
||||
isManga ? TrackStatus.reading : TrackStatus.watching,
|
||||
TrackStatus.completed,
|
||||
TrackStatus.onHold,
|
||||
|
|
|
|||
|
|
@ -20,19 +20,18 @@ class MyAnimeList extends _$MyAnimeList {
|
|||
String baseOAuthUrl = 'https://myanimelist.net/v1/oauth2';
|
||||
String baseApiUrl = 'https://api.myanimelist.net/v2';
|
||||
String codeVerifier = "";
|
||||
static final isDesktop = (Platform.isWindows || Platform.isLinux);
|
||||
String clientId =
|
||||
(Platform.isWindows || Platform.isLinux)
|
||||
isDesktop
|
||||
? '39e9be346b4e7dbcc59a98357e2f8472'
|
||||
: '0c9100ccd443ddb441a319a881180f7f';
|
||||
|
||||
@override
|
||||
void build({required int syncId, required ItemType? itemType}) {}
|
||||
void build({required int syncId, ItemType? itemType}) {}
|
||||
|
||||
Future<bool?> login() async {
|
||||
final callbackUrlScheme =
|
||||
(Platform.isWindows || Platform.isLinux)
|
||||
? 'http://localhost:43824'
|
||||
: 'mangayomi';
|
||||
isDesktop ? 'http://localhost:43824' : 'mangayomi';
|
||||
final loginUrl = _authUrl();
|
||||
|
||||
try {
|
||||
|
|
@ -66,7 +65,7 @@ class MyAnimeList extends _$MyAnimeList {
|
|||
}
|
||||
}
|
||||
|
||||
Future<String> _getAccesToken() async {
|
||||
Future<String> getAccessToken() async {
|
||||
final track = ref.watch(tracksProvider(syncId: syncId));
|
||||
final mALOAuth = OAuth.fromJson(
|
||||
jsonDecode(track!.oAuth!) as Map<String, dynamic>,
|
||||
|
|
@ -101,86 +100,54 @@ class MyAnimeList extends _$MyAnimeList {
|
|||
return mALOAuth.accessToken!;
|
||||
}
|
||||
|
||||
Future<List<TrackSearch>> search(String query) async {
|
||||
final accessToken = await _getAccesToken();
|
||||
Future<List<TrackSearch>> search(String query, isManga) async {
|
||||
final accessToken = await getAccessToken();
|
||||
final url = Uri.parse(
|
||||
itemType == ItemType.manga ? '$baseApiUrl/manga' : '$baseApiUrl/anime',
|
||||
'$baseApiUrl/${isManga ? "manga" : "anime"}',
|
||||
).replace(queryParameters: {'q': query.trim(), 'nsfw': 'true'});
|
||||
final result = await http.get(
|
||||
url,
|
||||
headers: {'Authorization': 'Bearer $accessToken'},
|
||||
);
|
||||
final result = await makeGetRequest(url, accessToken);
|
||||
final res = jsonDecode(result.body) as Map<String, dynamic>;
|
||||
|
||||
List<int> mangaIds =
|
||||
res['data'] == null
|
||||
? []
|
||||
: (res['data'] as List).map((e) => e['node']["id"] as int).toList();
|
||||
List<TrackSearch> trackSearchResult = [];
|
||||
for (var mangaId in mangaIds) {
|
||||
final trackSearch =
|
||||
itemType == ItemType.manga
|
||||
? await getMangaDetails(mangaId, accessToken)
|
||||
: await getAnimeDetails(mangaId, accessToken);
|
||||
trackSearchResult.add(trackSearch);
|
||||
}
|
||||
final trackSearchResult = await Future.wait(
|
||||
mangaIds.map((id) => getDetails(id, accessToken, isManga)),
|
||||
);
|
||||
|
||||
return trackSearchResult
|
||||
.where((element) => !element.publishingType!.contains("novel"))
|
||||
.toList();
|
||||
}
|
||||
|
||||
Future<TrackSearch> getMangaDetails(int id, String accessToken) async {
|
||||
final url = Uri.parse('$baseApiUrl/manga/$id').replace(
|
||||
Future<TrackSearch> getDetails(
|
||||
int id,
|
||||
String accessToken,
|
||||
bool isManga,
|
||||
) async {
|
||||
final item = isManga ? "manga" : "anime";
|
||||
final contentUnit = isManga ? "num_chapters" : "num_episodes";
|
||||
final url = Uri.parse('$baseApiUrl/$item/$id').replace(
|
||||
queryParameters: {
|
||||
'fields':
|
||||
'id,title,synopsis,num_chapters,main_picture,status,media_type,start_date',
|
||||
'id,title,synopsis,$contentUnit,main_picture,status,media_type,start_date',
|
||||
},
|
||||
);
|
||||
|
||||
final result = await http.get(
|
||||
url,
|
||||
headers: {'Authorization': 'Bearer $accessToken'},
|
||||
);
|
||||
final result = await makeGetRequest(url, accessToken);
|
||||
final res = jsonDecode(result.body) as Map<String, dynamic>;
|
||||
|
||||
return TrackSearch(
|
||||
mediaId: res["id"],
|
||||
summary: res["synopsis"] ?? "",
|
||||
totalChapter: res["num_chapters"],
|
||||
totalChapter: res[contentUnit],
|
||||
coverUrl: res["main_picture"]["large"] ?? "",
|
||||
title: res["title"],
|
||||
startDate: res["start_date"] ?? "",
|
||||
publishingType: res["media_type"].toString().replaceAll("_", " "),
|
||||
publishingStatus: res["status"].toString().replaceAll("_", " "),
|
||||
trackingUrl: "https://myanimelist.net/manga/${res["id"]}",
|
||||
);
|
||||
}
|
||||
|
||||
Future<TrackSearch> getAnimeDetails(int id, String accessToken) async {
|
||||
final url = Uri.parse('$baseApiUrl/anime/$id').replace(
|
||||
queryParameters: {
|
||||
'fields':
|
||||
'id,title,synopsis,num_episodes,main_picture,status,media_type,start_date',
|
||||
},
|
||||
);
|
||||
|
||||
final result = await http.get(
|
||||
url,
|
||||
headers: {'Authorization': 'Bearer $accessToken'},
|
||||
);
|
||||
final res = jsonDecode(result.body) as Map<String, dynamic>;
|
||||
|
||||
return TrackSearch(
|
||||
mediaId: res["id"],
|
||||
summary: res["synopsis"] ?? "",
|
||||
totalChapter: res["num_episodes"],
|
||||
coverUrl: res["main_picture"]["large"] ?? "",
|
||||
title: res["title"],
|
||||
startDate: res["start_date"] ?? "",
|
||||
publishingType: res["media_type"].toString().replaceAll("_", " "),
|
||||
publishingStatus: res["status"].toString().replaceAll("_", " "),
|
||||
trackingUrl: "https://myanimelist.net/anime/${res["id"]}",
|
||||
trackingUrl: "https://myanimelist.net/$item/${res["id"]}",
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -207,61 +174,38 @@ class MyAnimeList extends _$MyAnimeList {
|
|||
return '$baseOAuthUrl/authorize?client_id=$clientId&code_challenge=$codeVerifier&response_type=code';
|
||||
}
|
||||
|
||||
TrackStatus _getMALTrackStatusManga(String status) {
|
||||
TrackStatus getMALTrackStatus(String status, bool isManga) {
|
||||
return switch (status) {
|
||||
"reading" => TrackStatus.reading,
|
||||
"reading" when isManga => TrackStatus.reading,
|
||||
"watching" when !isManga => TrackStatus.watching,
|
||||
"completed" => TrackStatus.completed,
|
||||
"on_hold" => TrackStatus.onHold,
|
||||
"dropped" => TrackStatus.dropped,
|
||||
"plan_to_read" => TrackStatus.planToRead,
|
||||
_ => TrackStatus.rereading,
|
||||
"plan_to_read" when isManga => TrackStatus.planToRead,
|
||||
"plan_to_watch" when !isManga => TrackStatus.planToWatch,
|
||||
_ => isManga ? TrackStatus.reReading : TrackStatus.planToWatch,
|
||||
};
|
||||
}
|
||||
|
||||
TrackStatus _getMALTrackStatusAnime(String status) {
|
||||
return switch (status) {
|
||||
"watching" => TrackStatus.watching,
|
||||
"completed" => TrackStatus.completed,
|
||||
"on_hold" => TrackStatus.onHold,
|
||||
"dropped" => TrackStatus.dropped,
|
||||
_ => TrackStatus.planToWatch,
|
||||
};
|
||||
}
|
||||
|
||||
List<TrackStatus> myAnimeListStatusListManga = [
|
||||
TrackStatus.reading,
|
||||
List<TrackStatus> statusList(bool isManga) => [
|
||||
isManga ? TrackStatus.reading : TrackStatus.watching,
|
||||
TrackStatus.completed,
|
||||
TrackStatus.onHold,
|
||||
TrackStatus.dropped,
|
||||
TrackStatus.planToRead,
|
||||
TrackStatus.rereading,
|
||||
];
|
||||
List<TrackStatus> myAnimeListStatusListAnime = [
|
||||
TrackStatus.watching,
|
||||
TrackStatus.completed,
|
||||
TrackStatus.onHold,
|
||||
TrackStatus.dropped,
|
||||
TrackStatus.planToWatch,
|
||||
isManga ? TrackStatus.planToRead : TrackStatus.planToWatch,
|
||||
if (isManga) TrackStatus.reReading,
|
||||
];
|
||||
|
||||
String? toMyAnimeListStatusManga(TrackStatus status) {
|
||||
String? toMyAnimeListStatus(TrackStatus status, bool isManga) {
|
||||
return switch (status) {
|
||||
TrackStatus.reading => "reading",
|
||||
TrackStatus.reading when isManga => "reading",
|
||||
TrackStatus.watching when !isManga => "watching",
|
||||
TrackStatus.completed => "completed",
|
||||
TrackStatus.onHold => "on_hold",
|
||||
TrackStatus.dropped => "dropped",
|
||||
TrackStatus.planToRead => "plan_to_read",
|
||||
_ => "reading",
|
||||
};
|
||||
}
|
||||
|
||||
String? toMyAnimeListStatusAnime(TrackStatus status) {
|
||||
return switch (status) {
|
||||
TrackStatus.watching => "watching",
|
||||
TrackStatus.completed => "completed",
|
||||
TrackStatus.onHold => "on_hold",
|
||||
TrackStatus.dropped => "dropped",
|
||||
_ => "plan_to_watch",
|
||||
TrackStatus.planToRead when isManga => "plan_to_read",
|
||||
TrackStatus.planToWatch when !isManga => "plan_to_watch",
|
||||
_ => isManga ? "reading" : "plan_to_watch",
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -280,70 +224,43 @@ class MyAnimeList extends _$MyAnimeList {
|
|||
}
|
||||
|
||||
Future<String> _getUserName(String accessToken) async {
|
||||
final response = await http.get(
|
||||
final response = await makeGetRequest(
|
||||
Uri.parse('$baseApiUrl/users/@me'),
|
||||
headers: {'Authorization': 'Bearer $accessToken'},
|
||||
accessToken,
|
||||
);
|
||||
return jsonDecode(response.body)['name'];
|
||||
}
|
||||
|
||||
Future<Track> findManga(Track track) async {
|
||||
final accessToken = await _getAccesToken();
|
||||
final uri = Uri.parse(
|
||||
itemType == ItemType.manga
|
||||
? '$baseApiUrl/manga/${track.mediaId}'
|
||||
: '$baseApiUrl/anime/${track.mediaId}',
|
||||
).replace(
|
||||
Future<Track> findLibItem(Track track, bool isManga) async {
|
||||
final type = isManga ? "manga" : "anime";
|
||||
final contentUnit = isManga ? 'num_chapters' : 'num_episodes';
|
||||
final accessToken = await getAccessToken();
|
||||
final uri = Uri.parse('$baseApiUrl/$type/${track.mediaId}').replace(
|
||||
queryParameters: {
|
||||
'fields':
|
||||
itemType == ItemType.manga
|
||||
? 'num_chapters,my_list_status{start_date,finish_date}'
|
||||
: 'num_episodes,my_list_status{start_date,finish_date}',
|
||||
'fields': '$contentUnit,my_list_status{start_date,finish_date}',
|
||||
},
|
||||
);
|
||||
final response = await http.get(
|
||||
uri,
|
||||
headers: {'Authorization': 'Bearer $accessToken'},
|
||||
);
|
||||
final response = await makeGetRequest(uri, accessToken);
|
||||
final mJson = jsonDecode(response.body);
|
||||
track.totalChapter =
|
||||
itemType == ItemType.manga
|
||||
? mJson['num_chapters'] ?? 0
|
||||
: mJson['num_episodes'] ?? 0;
|
||||
track.totalChapter = mJson[contentUnit] ?? 0;
|
||||
if (mJson['my_list_status'] != null) {
|
||||
track =
|
||||
itemType == ItemType.manga
|
||||
? _parseMangaItem(mJson["my_list_status"], track)
|
||||
: _parseAnimeItem(mJson["my_list_status"], track);
|
||||
track = parseItem(mJson["my_list_status"], track, isManga);
|
||||
} else {
|
||||
track =
|
||||
itemType == ItemType.manga
|
||||
? await updateManga(track)
|
||||
: await updateAnime(track);
|
||||
track = await update(track, isManga);
|
||||
}
|
||||
return track;
|
||||
}
|
||||
|
||||
Track _parseMangaItem(Map<String, dynamic> mJson, Track track) {
|
||||
bool isRereading = mJson["is_rereading"] ?? false;
|
||||
Track parseItem(Map<String, dynamic> mJson, Track track, bool isManga) {
|
||||
bool isRepeating =
|
||||
mJson[isManga ? "is_rereading" : "is_rewatching"] ?? false;
|
||||
track.status =
|
||||
isRereading
|
||||
? TrackStatus.rereading
|
||||
: _getMALTrackStatusManga(mJson["status"]);
|
||||
track.lastChapterRead = int.parse(mJson["num_chapters_read"].toString());
|
||||
track.score = int.parse(mJson["score"].toString());
|
||||
track.startedReadingDate = _parseDate(mJson["start_date"]);
|
||||
track.finishedReadingDate = _parseDate(mJson["finish_date"]);
|
||||
return track;
|
||||
}
|
||||
|
||||
Track _parseAnimeItem(Map<String, dynamic> mJson, Track track) {
|
||||
bool isReWatching = mJson["is_rewatching"] ?? false;
|
||||
track.status =
|
||||
isReWatching
|
||||
? TrackStatus.reWatching
|
||||
: _getMALTrackStatusAnime(mJson["status"]);
|
||||
track.lastChapterRead = int.parse(mJson["num_episodes_watched"].toString());
|
||||
isRepeating
|
||||
? (isManga ? TrackStatus.reReading : TrackStatus.reWatching)
|
||||
: getMALTrackStatus(mJson["status"], isManga);
|
||||
track.lastChapterRead = int.parse(
|
||||
mJson[isManga ? "num_chapters_read" : "num_episodes_watched"].toString(),
|
||||
);
|
||||
track.score = int.parse(mJson["score"].toString());
|
||||
track.startedReadingDate = _parseDate(mJson["start_date"]);
|
||||
track.finishedReadingDate = _parseDate(mJson["finish_date"]);
|
||||
|
|
@ -357,14 +274,20 @@ class MyAnimeList extends _$MyAnimeList {
|
|||
return date.millisecondsSinceEpoch;
|
||||
}
|
||||
|
||||
Future<Track> updateAnime(Track track) async {
|
||||
final accessToken = await _getAccesToken();
|
||||
Future<Track> update(Track track, bool isManga) async {
|
||||
final accessToken = await getAccessToken();
|
||||
final formBody = {
|
||||
'status':
|
||||
(toMyAnimeListStatusAnime(track.status) ?? 'watching').toString(),
|
||||
'is_rewatching': (track.status == TrackStatus.reWatching).toString(),
|
||||
(toMyAnimeListStatus(track.status, isManga) ??
|
||||
(isManga ? 'reading' : 'watching'))
|
||||
.toString(),
|
||||
isManga ? 'is_rereading' : 'is_rewatching':
|
||||
(track.status ==
|
||||
(isManga ? TrackStatus.reReading : TrackStatus.reWatching))
|
||||
.toString(),
|
||||
'score': track.score.toString(),
|
||||
'num_watched_episodes': track.lastChapterRead.toString(),
|
||||
isManga ? 'num_chapters_read' : 'num_watched_episodes':
|
||||
track.lastChapterRead.toString(),
|
||||
if (track.startedReadingDate != null)
|
||||
'start_date': _convertToIsoDate(track.startedReadingDate),
|
||||
if (track.finishedReadingDate != null)
|
||||
|
|
@ -372,36 +295,22 @@ class MyAnimeList extends _$MyAnimeList {
|
|||
};
|
||||
final request = Request(
|
||||
'PUT',
|
||||
Uri.parse('$baseApiUrl/anime/${track.mediaId}/my_list_status'),
|
||||
Uri.parse(
|
||||
'$baseApiUrl/${isManga ? "manga" : "anime"}'
|
||||
'/${track.mediaId}/my_list_status',
|
||||
),
|
||||
);
|
||||
request.bodyFields = formBody;
|
||||
request.headers.addAll({'Authorization': 'Bearer $accessToken'});
|
||||
final response = await Client().send(request);
|
||||
final mJson = jsonDecode(await response.stream.bytesToString());
|
||||
return _parseAnimeItem(mJson, track);
|
||||
return parseItem(mJson, track, isManga);
|
||||
}
|
||||
|
||||
Future<Track> updateManga(Track track) async {
|
||||
final accessToken = await _getAccesToken();
|
||||
final formBody = {
|
||||
'status':
|
||||
(toMyAnimeListStatusManga(track.status) ?? 'reading').toString(),
|
||||
'is_rereading': (track.status == TrackStatus.rereading).toString(),
|
||||
'score': track.score.toString(),
|
||||
'num_chapters_read': track.lastChapterRead.toString(),
|
||||
if (track.startedReadingDate != null)
|
||||
'start_date': _convertToIsoDate(track.startedReadingDate),
|
||||
if (track.finishedReadingDate != null)
|
||||
'finish_date': _convertToIsoDate(track.finishedReadingDate),
|
||||
};
|
||||
final request = Request(
|
||||
'PUT',
|
||||
Uri.parse('$baseApiUrl/manga/${track.mediaId}/my_list_status'),
|
||||
Future<Response> makeGetRequest(Uri url, String accessToken) async {
|
||||
return await http.get(
|
||||
url,
|
||||
headers: {'Authorization': 'Bearer $accessToken'},
|
||||
);
|
||||
request.bodyFields = formBody;
|
||||
request.headers.addAll({'Authorization': 'Bearer $accessToken'});
|
||||
final response = await Client().send(request);
|
||||
final mJson = jsonDecode(await response.stream.bytesToString());
|
||||
return _parseMangaItem(mJson, track);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ String getTrackStatus(TrackStatus status, BuildContext context) {
|
|||
TrackStatus.onHold => l10n.on_hold,
|
||||
TrackStatus.dropped => l10n.dropped,
|
||||
TrackStatus.planToRead => l10n.plan_to_read,
|
||||
TrackStatus.rereading => l10n.re_reading,
|
||||
TrackStatus.reReading => l10n.re_reading,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -49,7 +49,7 @@ TrackStatus toTrackStatus(TrackStatus status, ItemType itemType, int syncId) {
|
|||
? switch (status) {
|
||||
TrackStatus.reading => TrackStatus.watching,
|
||||
TrackStatus.planToRead => TrackStatus.planToWatch,
|
||||
TrackStatus.rereading => TrackStatus.reWatching,
|
||||
TrackStatus.reReading => TrackStatus.reWatching,
|
||||
_ => status,
|
||||
}
|
||||
: status;
|
||||
|
|
|
|||
Loading…
Reference in a new issue