mirror of
https://github.com/kodjodevf/mangayomi.git
synced 2026-04-20 23:22:07 +00:00
added Simkl tracker
- reverted dependency versions - force workflow to use older flutter version
This commit is contained in:
parent
6101d96c96
commit
61575f4795
19 changed files with 824 additions and 50 deletions
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
|
|
@ -25,6 +25,7 @@ jobs:
|
|||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
flutter-version: 3.32.8
|
||||
|
||||
- name: Setup Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
|
@ -100,6 +101,7 @@ jobs:
|
|||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
flutter-version: 3.32.8
|
||||
|
||||
- name: Setup Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
|
@ -156,6 +158,7 @@ jobs:
|
|||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
flutter-version: 3.32.8
|
||||
|
||||
- name: Setup Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
|
@ -199,6 +202,7 @@ jobs:
|
|||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
flutter-version: 3.32.8
|
||||
|
||||
- name: Setup Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
|
@ -266,6 +270,7 @@ jobs:
|
|||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
flutter-version: 3.32.8
|
||||
|
||||
- name: Setup Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
|
@ -406,6 +411,7 @@ jobs:
|
|||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
flutter-version: 3.32.8
|
||||
|
||||
- name: Setup Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
|
|
|||
BIN
assets/trackers_icons/tracker_simkl.png
Normal file
BIN
assets/trackers_icons/tracker_simkl.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
|
|
@ -6,8 +6,10 @@ import 'package:mangayomi/models/track_search.dart';
|
|||
import 'package:mangayomi/modules/more/settings/track/providers/track_providers.dart';
|
||||
import 'package:mangayomi/modules/tracker_library/tracker_library_screen.dart';
|
||||
import 'package:mangayomi/services/trackers/anilist.dart';
|
||||
import 'package:mangayomi/services/trackers/base_tracker.dart';
|
||||
import 'package:mangayomi/services/trackers/kitsu.dart';
|
||||
import 'package:mangayomi/services/trackers/myanimelist.dart';
|
||||
import 'package:mangayomi/services/trackers/simkl.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
part 'track_state_providers.g.dart';
|
||||
|
||||
|
|
@ -18,7 +20,7 @@ class TrackState extends _$TrackState {
|
|||
return track!;
|
||||
}
|
||||
|
||||
dynamic getNotifier(int syncId) {
|
||||
BaseTracker getNotifier(int syncId) {
|
||||
return switch (syncId) {
|
||||
1 => ref.read(
|
||||
myAnimeListProvider(syncId: syncId, itemType: itemType).notifier,
|
||||
|
|
@ -27,6 +29,7 @@ class TrackState extends _$TrackState {
|
|||
anilistProvider(syncId: syncId, itemType: itemType).notifier,
|
||||
),
|
||||
3 => ref.read(kitsuProvider(syncId: syncId, itemType: itemType).notifier),
|
||||
4 => ref.read(simklProvider(syncId: syncId, itemType: itemType).notifier),
|
||||
_ => throw Exception('Unsupported syncId: $syncId'),
|
||||
};
|
||||
}
|
||||
|
|
@ -106,13 +109,15 @@ class TrackState extends _$TrackState {
|
|||
);
|
||||
final tracker = getNotifier(syncId);
|
||||
|
||||
if (syncId == 1) {
|
||||
if (syncId == TrackerProviders.myAnimeList.syncId) {
|
||||
findManga = await tracker.findLibItem(newTrack, _isManga);
|
||||
} else if (syncId == 2) {
|
||||
} else if (syncId == TrackerProviders.anilist.syncId) {
|
||||
findManga = await tracker.findLibItem(newTrack, _isManga);
|
||||
findManga ??= await tracker.update(newTrack, _isManga);
|
||||
} else if (syncId == 3) {
|
||||
} else if (syncId == TrackerProviders.kitsu.syncId) {
|
||||
findManga = await tracker.update(newTrack, _isManga);
|
||||
} else if (syncId == TrackerProviders.simkl.syncId) {
|
||||
findManga = await tracker.findLibItem(newTrack, _isManga);
|
||||
}
|
||||
writeBack(findManga!);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'track_state_providers.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$trackStateHash() => r'b70770f8524a0d9059ffd3f52b42634c16672a0f';
|
||||
String _$trackStateHash() => r'a96b4e702c16304cb16604d6b2d7dca5d65ca8b0';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ class OAuth {
|
|||
|
||||
OAuth.fromJson(Map<String, dynamic> json) {
|
||||
tokenType = json['token_type'];
|
||||
expiresIn = json['expires_in'] as int;
|
||||
expiresIn = json['expires_in'] as int?;
|
||||
accessToken = json['access_token'];
|
||||
refreshToken = json['refresh_token'];
|
||||
clientId = json['client_id'];
|
||||
|
|
|
|||
|
|
@ -7,10 +7,12 @@ import 'package:mangayomi/models/track_preference.dart';
|
|||
import 'package:mangayomi/modules/more/settings/track/providers/track_providers.dart';
|
||||
import 'package:mangayomi/modules/more/settings/track/widgets/track_listile.dart';
|
||||
import 'package:mangayomi/modules/more/widgets/list_tile_widget.dart';
|
||||
import 'package:mangayomi/modules/tracker_library/tracker_library_screen.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:mangayomi/services/trackers/anilist.dart';
|
||||
import 'package:mangayomi/services/trackers/kitsu.dart';
|
||||
import 'package:mangayomi/services/trackers/myanimelist.dart';
|
||||
import 'package:mangayomi/services/trackers/simkl.dart';
|
||||
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
||||
|
||||
class TrackScreen extends ConsumerWidget {
|
||||
|
|
@ -65,16 +67,22 @@ class TrackScreen extends ConsumerWidget {
|
|||
),
|
||||
TrackListile(
|
||||
onTap: () async {
|
||||
await ref.read(anilistProvider(syncId: 2).notifier).login();
|
||||
await ref
|
||||
.read(
|
||||
anilistProvider(
|
||||
syncId: TrackerProviders.anilist.syncId,
|
||||
).notifier,
|
||||
)
|
||||
.login();
|
||||
},
|
||||
id: 2,
|
||||
id: TrackerProviders.anilist.syncId,
|
||||
entries: entries!,
|
||||
),
|
||||
TrackListile(
|
||||
onTap: () async {
|
||||
_showDialogLogin(context, ref);
|
||||
},
|
||||
id: 3,
|
||||
id: TrackerProviders.kitsu.syncId,
|
||||
entries: entries,
|
||||
),
|
||||
TrackListile(
|
||||
|
|
@ -82,13 +90,27 @@ class TrackScreen extends ConsumerWidget {
|
|||
await ref
|
||||
.read(
|
||||
myAnimeListProvider(
|
||||
syncId: 1,
|
||||
syncId: TrackerProviders.myAnimeList.syncId,
|
||||
itemType: null,
|
||||
).notifier,
|
||||
)
|
||||
.login();
|
||||
},
|
||||
id: 1,
|
||||
id: TrackerProviders.myAnimeList.syncId,
|
||||
entries: entries,
|
||||
),
|
||||
TrackListile(
|
||||
onTap: () async {
|
||||
await ref
|
||||
.read(
|
||||
simklProvider(
|
||||
syncId: TrackerProviders.simkl.syncId,
|
||||
itemType: null,
|
||||
).notifier,
|
||||
)
|
||||
.login();
|
||||
},
|
||||
id: TrackerProviders.simkl.syncId,
|
||||
entries: entries,
|
||||
),
|
||||
ListTile(
|
||||
|
|
@ -231,7 +253,11 @@ void _showDialogLogin(BuildContext context, WidgetRef ref) {
|
|||
isLoading = true;
|
||||
});
|
||||
final res = await ref
|
||||
.read(kitsuProvider(syncId: 3).notifier)
|
||||
.read(
|
||||
kitsuProvider(
|
||||
syncId: TrackerProviders.kitsu.syncId,
|
||||
).notifier,
|
||||
)
|
||||
.login(email, password);
|
||||
if (!res.$1) {
|
||||
setState(() {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ enum TrackerProviders {
|
|||
myAnimeList(syncId: 1, name: "MAL"),
|
||||
anilist(syncId: 2, name: "AL"),
|
||||
kitsu(syncId: 3, name: "Kitsu"),
|
||||
trakt(syncId: 4, name: "Trakt");
|
||||
simkl(syncId: 4, name: "Simkl");
|
||||
|
||||
const TrackerProviders({required this.syncId, required this.name});
|
||||
|
||||
|
|
@ -68,6 +68,7 @@ class _TrackerLibraryScreenState extends ConsumerState<TrackerLibraryScreen> {
|
|||
1 => _sectionsMAL(trackerProvider.syncId, itemType),
|
||||
2 => _sectionsAL(trackerProvider.syncId, itemType),
|
||||
3 => _sectionsKitsu(trackerProvider.syncId, itemType),
|
||||
4 => _sectionsSimkl(trackerProvider.syncId, itemType),
|
||||
_ => [],
|
||||
};
|
||||
if (_isSearch && _query.isNotEmpty) {
|
||||
|
|
@ -211,6 +212,47 @@ class _TrackerLibraryScreenState extends ConsumerState<TrackerLibraryScreen> {
|
|||
setState(() {});
|
||||
}
|
||||
|
||||
List<TrackLibrarySection> _sectionsSimkl(int syncId, ItemType itemType) {
|
||||
return [
|
||||
TrackLibrarySection(
|
||||
name: "Continue watching movies",
|
||||
syncId: syncId,
|
||||
func: _fetchUserData(syncId, ItemType.manga),
|
||||
itemType: ItemType.anime,
|
||||
),
|
||||
TrackLibrarySection(
|
||||
name: "Continue watching series",
|
||||
syncId: syncId,
|
||||
func: _fetchUserData(syncId, ItemType.anime),
|
||||
itemType: ItemType.anime,
|
||||
),
|
||||
TrackLibrarySection(
|
||||
name: "Trending Movies",
|
||||
syncId: syncId,
|
||||
func: _fetchGeneralData(syncId, ItemType.manga),
|
||||
itemType: ItemType.anime,
|
||||
),
|
||||
TrackLibrarySection(
|
||||
name: "Trending Series",
|
||||
syncId: syncId,
|
||||
func: _fetchGeneralData(syncId, ItemType.anime),
|
||||
itemType: ItemType.anime,
|
||||
),
|
||||
TrackLibrarySection(
|
||||
name: "Airing Series",
|
||||
syncId: syncId,
|
||||
func: _fetchGeneralData(syncId, ItemType.anime, rankingType: "airing"),
|
||||
itemType: ItemType.anime,
|
||||
),
|
||||
TrackLibrarySection(
|
||||
name: "Top Series (All Time)",
|
||||
syncId: syncId,
|
||||
func: _fetchGeneralData(syncId, ItemType.anime, rankingType: "best"),
|
||||
itemType: ItemType.anime,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
List<TrackLibrarySection> _sectionsMAL(int syncId, ItemType itemType) {
|
||||
return itemType == ItemType.anime
|
||||
? [
|
||||
|
|
@ -505,6 +547,7 @@ class _TrackerLibraryScreenState extends ConsumerState<TrackerLibraryScreen> {
|
|||
_getListile(l10n, TrackerProviders.myAnimeList.syncId),
|
||||
_getListile(l10n, TrackerProviders.anilist.syncId),
|
||||
_getListile(l10n, TrackerProviders.kitsu.syncId),
|
||||
_getListile(l10n, TrackerProviders.simkl.syncId),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
@ -561,13 +604,13 @@ class _TrackerLibraryScreenState extends ConsumerState<TrackerLibraryScreen> {
|
|||
),
|
||||
enabled: isLoggedIn,
|
||||
onTap: () {
|
||||
if (isManga == null) {
|
||||
if (isManga == null && syncId != TrackerProviders.simkl.syncId) {
|
||||
context.pop();
|
||||
_openSwitchTypeDialog(l10n, syncId);
|
||||
} else {
|
||||
ref.read(lastTrackerLibraryLocationStateProvider.notifier).set((
|
||||
syncId,
|
||||
isManga,
|
||||
isManga ?? true,
|
||||
));
|
||||
context.pop();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,11 +10,12 @@ import 'package:mangayomi/models/track_search.dart';
|
|||
import 'package:mangayomi/modules/more/settings/track/myanimelist/model.dart';
|
||||
import 'package:mangayomi/modules/more/settings/track/providers/track_providers.dart';
|
||||
import 'package:mangayomi/services/http/m_client.dart';
|
||||
import 'base_tracker.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
part 'anilist.g.dart';
|
||||
|
||||
@riverpod
|
||||
class Anilist extends _$Anilist {
|
||||
class Anilist extends _$Anilist implements BaseTracker {
|
||||
final http = MClient.init(reqcopyWith: {'useDartHttpClient': true});
|
||||
static final _isDesktop = Platform.isWindows || Platform.isLinux;
|
||||
final String _clientId = _isDesktop ? '13587' : '13588';
|
||||
|
|
@ -77,6 +78,7 @@ class Anilist extends _$Anilist {
|
|||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Track> update(Track track, bool isManga) async {
|
||||
final isNew = track.libraryId == null;
|
||||
final opName = isNew ? 'AddEntry' : 'UpdateEntry';
|
||||
|
|
@ -114,6 +116,7 @@ class Anilist extends _$Anilist {
|
|||
return track;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<TrackSearch>> search(String search, bool isManga) async {
|
||||
final type = isManga ? "MANGA" : "ANIME";
|
||||
final contentUnit = isManga ? "chapters" : "episodes";
|
||||
|
|
@ -169,6 +172,7 @@ class Anilist extends _$Anilist {
|
|||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Track?> findLibItem(Track track, bool isManga) async {
|
||||
final userId = int.parse(
|
||||
ref.read(tracksProvider(syncId: syncId))!.username!,
|
||||
|
|
@ -225,6 +229,7 @@ class Anilist extends _$Anilist {
|
|||
..totalChapter = jsonRes['media'][contentUnit] as int? ?? 0;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<TrackSearch>> fetchGeneralData({
|
||||
bool isManga = true,
|
||||
String rankingType =
|
||||
|
|
@ -284,6 +289,7 @@ class Anilist extends _$Anilist {
|
|||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<TrackSearch>> fetchUserData({bool isManga = true}) async {
|
||||
final userId = int.parse(
|
||||
ref.read(tracksProvider(syncId: syncId))!.username!,
|
||||
|
|
@ -454,6 +460,7 @@ class Anilist extends _$Anilist {
|
|||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<TrackStatus> statusList(bool isManga) => [
|
||||
isManga ? TrackStatus.reading : TrackStatus.watching,
|
||||
TrackStatus.completed,
|
||||
|
|
@ -497,6 +504,7 @@ class Anilist extends _$Anilist {
|
|||
return {"year": date.year, "month": date.month, "day": date.day};
|
||||
}
|
||||
|
||||
@override
|
||||
String displayScore(int score) {
|
||||
final prefs = isar.trackPreferences.getSync(syncId)!.prefs;
|
||||
final scoreFormat = jsonDecode(prefs!)['scoreFormat'];
|
||||
|
|
@ -515,6 +523,7 @@ class Anilist extends _$Anilist {
|
|||
};
|
||||
}
|
||||
|
||||
@override
|
||||
(int, int) getScoreValue() {
|
||||
final prefs = isar.trackPreferences.getSync(syncId)!.prefs;
|
||||
String scoreFormat = jsonDecode(prefs!)['scoreFormat'];
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'anilist.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$anilistHash() => r'fafb964252b3a5741e981cb8c2f0f2090b3b86ae';
|
||||
String _$anilistHash() => r'9294cd3486dd6dab50593cc367045b683a5be802';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
20
lib/services/trackers/base_tracker.dart
Normal file
20
lib/services/trackers/base_tracker.dart
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import 'package:mangayomi/models/track.dart';
|
||||
import 'package:mangayomi/models/track_search.dart';
|
||||
|
||||
abstract class BaseTracker {
|
||||
Future<Track?> findLibItem(Track track, bool isManga);
|
||||
Future<Track> update(Track track, bool isManga);
|
||||
List<TrackStatus> statusList(bool isManga);
|
||||
Future<List<TrackSearch>> search(String query, bool isManga);
|
||||
Future<List<TrackSearch>> fetchGeneralData({
|
||||
bool isManga,
|
||||
String rankingType,
|
||||
});
|
||||
Future<List<TrackSearch>> fetchUserData({bool isManga});
|
||||
|
||||
/// Anilist
|
||||
(int, int) getScoreValue();
|
||||
|
||||
/// Anilist
|
||||
String displayScore(int score);
|
||||
}
|
||||
|
|
@ -9,11 +9,12 @@ import 'package:mangayomi/models/track_search.dart';
|
|||
import 'package:mangayomi/modules/more/settings/track/myanimelist/model.dart';
|
||||
import 'package:mangayomi/modules/more/settings/track/providers/track_providers.dart';
|
||||
import 'package:mangayomi/services/http/m_client.dart';
|
||||
import 'base_tracker.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
part 'kitsu.g.dart';
|
||||
|
||||
@riverpod
|
||||
class Kitsu extends _$Kitsu {
|
||||
class Kitsu extends _$Kitsu implements BaseTracker {
|
||||
final http = MClient.init(reqcopyWith: {'useDartHttpClient': true});
|
||||
final String _clientId =
|
||||
'dd031b32d2f56c990b1425efe6c42ad847e7fe3ab46bf1299f05ecd856bdb7dd';
|
||||
|
|
@ -76,6 +77,7 @@ class Kitsu extends _$Kitsu {
|
|||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Track> update(Track track, bool isManga) async {
|
||||
final isNew = track.libraryId == null;
|
||||
final String? userId = isNew ? _getUserId() : null;
|
||||
|
|
@ -127,6 +129,7 @@ class Kitsu extends _$Kitsu {
|
|||
return track;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<TrackSearch>> search(String search, bool isManga) async {
|
||||
final accessToken = _getAccessToken();
|
||||
|
||||
|
|
@ -175,6 +178,7 @@ class Kitsu extends _$Kitsu {
|
|||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<TrackSearch>> fetchGeneralData({
|
||||
bool isManga = true,
|
||||
String rankingType = "popularityRank",
|
||||
|
|
@ -215,6 +219,7 @@ class Kitsu extends _$Kitsu {
|
|||
}).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<TrackSearch>> fetchUserData({bool isManga = true}) async {
|
||||
final type = isManga ? "manga" : "anime";
|
||||
final userId = _getUserId();
|
||||
|
|
@ -271,6 +276,7 @@ class Kitsu extends _$Kitsu {
|
|||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Track?> findLibItem(Track track, bool isManga) async {
|
||||
final type = isManga ? "manga" : "anime";
|
||||
final userId = _getUserId();
|
||||
|
|
@ -374,6 +380,7 @@ class Kitsu extends _$Kitsu {
|
|||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<TrackStatus> statusList(bool isManga) => [
|
||||
isManga ? TrackStatus.reading : TrackStatus.watching,
|
||||
TrackStatus.completed,
|
||||
|
|
@ -411,4 +418,14 @@ class Kitsu extends _$Kitsu {
|
|||
String? _toKitsuScore(int score) {
|
||||
return score > 0 ? (score * 2).toString() : null;
|
||||
}
|
||||
|
||||
@override
|
||||
String displayScore(int score) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
(int, int) getScoreValue() {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'kitsu.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$kitsuHash() => r'd46b955c92bc4d7382d32e17827da2e2b3a8434f';
|
||||
String _$kitsuHash() => r'3f6af42dd0bb3fe543197db692598384ca5cc95f';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -12,11 +12,12 @@ import 'package:mangayomi/models/track_search.dart';
|
|||
import 'package:mangayomi/modules/more/settings/track/myanimelist/model.dart';
|
||||
import 'package:mangayomi/modules/more/settings/track/providers/track_providers.dart';
|
||||
import 'package:mangayomi/services/http/m_client.dart';
|
||||
import 'base_tracker.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
part 'myanimelist.g.dart';
|
||||
|
||||
@riverpod
|
||||
class MyAnimeList extends _$MyAnimeList {
|
||||
class MyAnimeList extends _$MyAnimeList implements BaseTracker {
|
||||
final http = MClient.init(reqcopyWith: {'useDartHttpClient': true});
|
||||
static const _baseOAuthUrl = 'https://myanimelist.net/v1/oauth2';
|
||||
static const _baseApiUrl = 'https://api.myanimelist.net/v2';
|
||||
|
|
@ -118,6 +119,7 @@ class MyAnimeList extends _$MyAnimeList {
|
|||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<TrackSearch>> search(String query, isManga) async {
|
||||
final accessToken = await _getAccessToken();
|
||||
final url = Uri.parse(
|
||||
|
|
@ -170,6 +172,7 @@ class MyAnimeList extends _$MyAnimeList {
|
|||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<TrackSearch>> fetchGeneralData({
|
||||
bool isManga = true,
|
||||
String rankingType = "airing",
|
||||
|
|
@ -209,6 +212,7 @@ class MyAnimeList extends _$MyAnimeList {
|
|||
}).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<TrackSearch>> fetchUserData({bool isManga = true}) async {
|
||||
final accessToken = await _getAccessToken();
|
||||
final item = isManga ? "mangalist" : "animelist";
|
||||
|
|
@ -292,6 +296,7 @@ class MyAnimeList extends _$MyAnimeList {
|
|||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<TrackStatus> statusList(bool isManga) => [
|
||||
isManga ? TrackStatus.reading : TrackStatus.watching,
|
||||
TrackStatus.completed,
|
||||
|
|
@ -349,7 +354,8 @@ class MyAnimeList extends _$MyAnimeList {
|
|||
return jsonDecode(response.body)['name'];
|
||||
}
|
||||
|
||||
Future<Track> findLibItem(Track track, bool isManga) async {
|
||||
@override
|
||||
Future<Track?> findLibItem(Track track, bool isManga) async {
|
||||
final type = isManga ? "manga" : "anime";
|
||||
final contentUnit = isManga ? 'num_chapters' : 'num_episodes';
|
||||
final accessToken = await _getAccessToken();
|
||||
|
|
@ -391,6 +397,7 @@ class MyAnimeList extends _$MyAnimeList {
|
|||
return date.millisecondsSinceEpoch;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Track> update(Track track, bool isManga) async {
|
||||
final accessToken = await _getAccessToken();
|
||||
final formBody = {
|
||||
|
|
@ -431,4 +438,14 @@ class MyAnimeList extends _$MyAnimeList {
|
|||
headers: {'Authorization': 'Bearer $accessToken'},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String displayScore(int score) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
(int, int) getScoreValue() {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'myanimelist.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$myAnimeListHash() => r'739c836ddbfc7c2c2b7593304f481e8d35074391';
|
||||
String _$myAnimeListHash() => r'9846f76ce69f952d20cc1e8edbc5ca565cd4e7c9';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
419
lib/services/trackers/simkl.dart
Normal file
419
lib/services/trackers/simkl.dart
Normal file
|
|
@ -0,0 +1,419 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:flutter_qjs/quickjs/ffi.dart';
|
||||
import 'package:flutter_web_auth_2/flutter_web_auth_2.dart';
|
||||
import 'package:http_interceptor/http_interceptor.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/models/track.dart';
|
||||
import 'package:mangayomi/models/track_preference.dart';
|
||||
import 'package:mangayomi/models/track_search.dart';
|
||||
import 'package:mangayomi/modules/more/settings/track/myanimelist/model.dart';
|
||||
import 'package:mangayomi/modules/more/settings/track/providers/track_providers.dart';
|
||||
import 'package:mangayomi/services/http/m_client.dart';
|
||||
import 'base_tracker.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
part 'simkl.g.dart';
|
||||
|
||||
@riverpod
|
||||
class Simkl extends _$Simkl implements BaseTracker {
|
||||
final http = MClient.init(reqcopyWith: {'useDartHttpClient': true});
|
||||
static const _baseOAuthUrl = 'https://simkl.com/oauth';
|
||||
static const _baseApiUrl = 'https://api.simkl.com';
|
||||
static final _isDesktop = (Platform.isWindows || Platform.isLinux);
|
||||
static const _redirectUri = 'http://localhost:43824';
|
||||
static const _clientId =
|
||||
'1e0a52930b1bdface4e30c1a94a44641475f3c80b69a5ea939562153fccffb68';
|
||||
static const _clientSecret =
|
||||
'aed1dc0fa8b9906c493b87c513b430fde75ea5cdad0087e8d129fbc5d36f9be0';
|
||||
|
||||
String getFallbackClientId(String usedId) {
|
||||
return _clientId;
|
||||
}
|
||||
|
||||
@override
|
||||
void build({required int syncId, required ItemType? itemType}) {}
|
||||
|
||||
Future<bool?> login() async {
|
||||
final callbackUrlScheme = _isDesktop
|
||||
? 'http://localhost:43824'
|
||||
: 'mangayomi';
|
||||
final loginUrl = _authUrl();
|
||||
|
||||
try {
|
||||
final uri = await FlutterWebAuth2.authenticate(
|
||||
url: "$loginUrl&redirect_uri=$callbackUrlScheme",
|
||||
callbackUrlScheme: callbackUrlScheme,
|
||||
);
|
||||
final code = Uri.parse(uri).queryParameters['code'];
|
||||
if (code == null) return null;
|
||||
|
||||
final oAuthData = await _getOAuth(code);
|
||||
final oAuth = _buildOAuth(oAuthData, _clientId);
|
||||
final username = await _getUserName(oAuth.accessToken!);
|
||||
_saveOAuth(username, oAuth);
|
||||
|
||||
return true;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<TrackSearch>> fetchGeneralData({
|
||||
bool isManga = true,
|
||||
String rankingType = "trending",
|
||||
}) async {
|
||||
/// isManga <> isMovie
|
||||
final type = isManga ? "movies" : "tv";
|
||||
final accessToken = await _getAccessToken();
|
||||
final url = Uri.parse('$_baseApiUrl/$type/$rankingType').replace(
|
||||
queryParameters: {
|
||||
'extended': 'overview',
|
||||
if (rankingType == "airing") 'date': 'today',
|
||||
if (rankingType == "airing") 'sort': 'rank',
|
||||
if (rankingType == "best") 'filter': 'all',
|
||||
if (rankingType == "best") 'type': 'series',
|
||||
'clientId': _clientId,
|
||||
'limit': '15',
|
||||
},
|
||||
);
|
||||
final result = await _makeGetRequest(url, accessToken);
|
||||
final data = jsonDecode(result.body) as List?;
|
||||
return data?.map((e) {
|
||||
return TrackSearch(
|
||||
mediaId: e['ids']?['simkl_id'] ?? e['ids']?['simkl'],
|
||||
summary: e['overview'] ?? 'No summary available.',
|
||||
totalChapter: 0,
|
||||
coverUrl: e['fanart'] != null
|
||||
? 'https://wsrv.nl/?url=https://simkl.in/fanart/${e['fanart']}_medium.jpg'
|
||||
: e['poster'] != null
|
||||
? 'https://wsrv.nl/?url=https://simkl.in/posters/${e['poster']}_m.webp'
|
||||
: '',
|
||||
title: e['title'] ?? 'Unknown Title',
|
||||
score: (e["ratings"]?["simkl"]?["rating"] as num?)?.toDouble(),
|
||||
startDate: e["release_date"] ?? "",
|
||||
publishingType: isManga ? "movie" : "tv",
|
||||
publishingStatus: e["status"],
|
||||
trackingUrl:
|
||||
"https://simkl.com/$type/${e['ids']?['simkl_id'] ?? e['ids']?['simkl']}",
|
||||
syncId: syncId,
|
||||
);
|
||||
}).toList() ??
|
||||
[];
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<TrackSearch>> fetchUserData({bool isManga = true}) async {
|
||||
final type = isManga ? "movies" : "shows";
|
||||
final nodeType = isManga ? "movie" : "show";
|
||||
final accessToken = await _getAccessToken();
|
||||
final url = Uri.parse('$_baseApiUrl/sync/all-items/$type');
|
||||
final result = await _makeGetRequest(url, accessToken);
|
||||
final data = jsonDecode(result.body) as Map<String, dynamic>?;
|
||||
return (data?[type] as List?)?.map((e) {
|
||||
final node = e[nodeType];
|
||||
return TrackSearch(
|
||||
mediaId: node['ids']?['simkl'],
|
||||
summary: 'No summary available.',
|
||||
totalChapter: isManga ? 1 : e['total_episodes_count'],
|
||||
coverUrl: node['poster'] != null
|
||||
? "https://wsrv.nl/?url=https://simkl.in/posters/${node['poster']}_m.jpg"
|
||||
: "",
|
||||
title: node['title'] ?? 'Unknown Title',
|
||||
score: 0,
|
||||
startDate: "",
|
||||
publishingType: isManga ? "movie" : "tv",
|
||||
publishingStatus: e["status"],
|
||||
trackingUrl: "https://simkl.com/$type/${node['ids']?['simkl']}",
|
||||
syncId: syncId,
|
||||
);
|
||||
}).toList() ??
|
||||
[];
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Track?> findLibItem(Track track, bool isManga) async {
|
||||
final accessToken = await _getAccessToken();
|
||||
final url = Uri.parse(
|
||||
'$_baseApiUrl/sync/watched/',
|
||||
).replace(queryParameters: {"extended": "episodes,specials,counters"});
|
||||
final result = await _makePostRequest(url, accessToken, [
|
||||
{"simkl": track.mediaId},
|
||||
]);
|
||||
final data = jsonDecode(result.body) as List?;
|
||||
if ((data?.isNotEmpty ?? false) &&
|
||||
data!.firstOrNull?["result"] != "not_found") {
|
||||
final node = data.firstOrNull;
|
||||
if (node?["list"] is String) {
|
||||
track.status = _trackFromSimklStatus(node!["list"]);
|
||||
if (track.status == TrackStatus.completed &&
|
||||
node?["last_watched_at"] is String) {
|
||||
track.finishedReadingDate = DateTime.tryParse(
|
||||
node!["last_watched_at"],
|
||||
)?.millisecondsSinceEpoch;
|
||||
}
|
||||
}
|
||||
if (node?["episodes_watched"] is num) {
|
||||
track.lastChapterRead = (node!["episodes_watched"] as num).toInt();
|
||||
}
|
||||
if (node?["episodes_total"] is num) {
|
||||
track.totalChapter = (node!["episodes_total"] as num).toInt();
|
||||
}
|
||||
track.libraryId = 1;
|
||||
if (node?["result"] == false) {
|
||||
track.libraryId = 0;
|
||||
return await update(track, isManga);
|
||||
}
|
||||
}
|
||||
return track;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<TrackSearch>> search(String query, bool isManga) async {
|
||||
final accessToken = await _getAccessToken();
|
||||
final urlMovies = Uri.parse('$_baseApiUrl/search/movies').replace(
|
||||
queryParameters: {
|
||||
'q': query,
|
||||
'extended': 'full',
|
||||
'clientId': _clientId,
|
||||
'limit': '15',
|
||||
},
|
||||
);
|
||||
final resultMovies = await _makeGetRequest(urlMovies, accessToken);
|
||||
final dataMovies = jsonDecode(resultMovies.body) as List?;
|
||||
final movies =
|
||||
dataMovies?.map((e) {
|
||||
return TrackSearch(
|
||||
mediaId: e['ids']?['simkl_id'] ?? e['ids']?['simkl'],
|
||||
summary: e['overview'] ?? 'No summary available.',
|
||||
totalChapter: 0,
|
||||
coverUrl: e['poster'] != null
|
||||
? 'https://wsrv.nl/?url=https://simkl.in/posters/${e['poster']}_m.webp'
|
||||
: '',
|
||||
title: e['title'] ?? 'Unknown Title',
|
||||
score: (e["ratings"]?["simkl"]?["rating"] as num?)?.toDouble(),
|
||||
startDate: e["release_date"] ?? "",
|
||||
publishingType: "movie",
|
||||
publishingStatus: e["status"],
|
||||
trackingUrl:
|
||||
"https://simkl.com/movie/${e['ids']?['simkl_id'] ?? e['ids']?['simkl']}",
|
||||
syncId: syncId,
|
||||
);
|
||||
}).toList() ??
|
||||
[];
|
||||
final urlSeries = Uri.parse('$_baseApiUrl/search/tv').replace(
|
||||
queryParameters: {
|
||||
'q': query,
|
||||
'extended': 'full',
|
||||
'clientId': _clientId,
|
||||
'limit': '15',
|
||||
},
|
||||
);
|
||||
final resultSeries = await _makeGetRequest(urlSeries, accessToken);
|
||||
final dataSeries = jsonDecode(resultSeries.body) as List?;
|
||||
final series =
|
||||
dataSeries?.map((e) {
|
||||
return TrackSearch(
|
||||
mediaId: e['ids']?['simkl_id'] ?? e['ids']?['simkl'],
|
||||
summary: e['overview'] ?? 'No summary available.',
|
||||
totalChapter: 0,
|
||||
coverUrl: e['poster'] != null
|
||||
? 'https://wsrv.nl/?url=https://simkl.in/posters/${e['poster']}_m.webp'
|
||||
: '',
|
||||
title: e['title'] ?? 'Unknown Title',
|
||||
score: (e["ratings"]?["simkl"]?["rating"] as num?)?.toDouble(),
|
||||
startDate: e["release_date"] ?? "",
|
||||
publishingType: "tv",
|
||||
publishingStatus: e["status"],
|
||||
trackingUrl:
|
||||
"https://simkl.com/tv/${e['ids']?['simkl_id'] ?? e['ids']?['simkl']}",
|
||||
syncId: syncId,
|
||||
);
|
||||
}).toList() ??
|
||||
[];
|
||||
return movies + series;
|
||||
}
|
||||
|
||||
@override
|
||||
List<TrackStatus> statusList(bool isManga) => [
|
||||
TrackStatus.watching,
|
||||
TrackStatus.completed,
|
||||
TrackStatus.onHold,
|
||||
TrackStatus.dropped,
|
||||
TrackStatus.planToWatch,
|
||||
];
|
||||
|
||||
@override
|
||||
Future<Track> update(Track track, bool isManga) async {
|
||||
final accessToken = await _getAccessToken();
|
||||
final existRemote = track.libraryId == 1;
|
||||
final isMovie =
|
||||
track.trackingUrl?.replaceAll("https://simkl.com/", "").split("/")[0] ==
|
||||
"movie";
|
||||
final url =
|
||||
Uri.parse(
|
||||
existRemote
|
||||
? "$_baseApiUrl/sync/history"
|
||||
: "$_baseApiUrl/sync/add-to-list",
|
||||
).replace(
|
||||
queryParameters: {
|
||||
'extended': 'full',
|
||||
'clientId': _clientId,
|
||||
'limit': '15',
|
||||
},
|
||||
);
|
||||
final body = isMovie
|
||||
? {
|
||||
'movies': [
|
||||
{
|
||||
if (!existRemote) 'to': _trackToSimklStatus(track),
|
||||
if (existRemote) 'status': _trackToSimklStatus(track),
|
||||
'ids': {'simkl': track.mediaId},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {
|
||||
'shows': [
|
||||
{
|
||||
if (!existRemote) 'to': _trackToSimklStatus(track),
|
||||
if (existRemote) 'status': _trackToSimklStatus(track),
|
||||
'ids': {'simkl': track.mediaId},
|
||||
'episodes': [
|
||||
for (int i = 1; i <= (track.lastChapterRead ?? 1); i++)
|
||||
{'number': i},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
final result = await _makePostRequest(url, accessToken, body);
|
||||
if (result.statusCode >= 200 && result.statusCode < 300) {
|
||||
track.libraryId = 1;
|
||||
}
|
||||
if (result.statusCode == 201) {
|
||||
return track;
|
||||
}
|
||||
final temp = (jsonDecode(result.body) as Map<String, dynamic>?)?["added"];
|
||||
final data = _extractTrackData(
|
||||
temp?[isMovie ? "movies" : "shows"] as List?,
|
||||
track.mediaId,
|
||||
);
|
||||
return _parseTrack(track, data);
|
||||
}
|
||||
|
||||
Map<String, dynamic>? _extractTrackData(List? data, int? mediaId) {
|
||||
return data?.firstWhereOrNull((e) => e["ids"]?["simkl"] == mediaId);
|
||||
}
|
||||
|
||||
Track _parseTrack(Track track, Map<String, dynamic>? data) {
|
||||
if (data?["to"] is String) {
|
||||
track.status = _trackFromSimklStatus(data!["to"]);
|
||||
}
|
||||
return track;
|
||||
}
|
||||
|
||||
String _trackToSimklStatus(Track track) => switch (track.status) {
|
||||
TrackStatus.completed => "completed",
|
||||
TrackStatus.watching => "watching",
|
||||
TrackStatus.onHold => "hold",
|
||||
TrackStatus.dropped => "dropped",
|
||||
_ => "plantowatch",
|
||||
};
|
||||
|
||||
TrackStatus _trackFromSimklStatus(String status) => switch (status) {
|
||||
"completed" => TrackStatus.completed,
|
||||
"watching" => TrackStatus.watching,
|
||||
"hold" => TrackStatus.onHold,
|
||||
"dropped" => TrackStatus.dropped,
|
||||
_ => TrackStatus.planToWatch,
|
||||
};
|
||||
|
||||
Future<String> _getAccessToken() async {
|
||||
final track = ref.read(tracksProvider(syncId: syncId));
|
||||
final mALOAuth = OAuth.fromJson(
|
||||
jsonDecode(track!.oAuth!) as Map<String, dynamic>,
|
||||
);
|
||||
return mALOAuth.accessToken!;
|
||||
}
|
||||
|
||||
OAuth _buildOAuth(Map<String, dynamic> json, String clientId) {
|
||||
return OAuth.fromJson(json)..clientId = clientId;
|
||||
}
|
||||
|
||||
void _saveOAuth(String username, OAuth oAuth) {
|
||||
ref
|
||||
.read(tracksProvider(syncId: syncId).notifier)
|
||||
.login(
|
||||
TrackPreference(
|
||||
syncId: syncId,
|
||||
username: username,
|
||||
prefs: "",
|
||||
oAuth: jsonEncode(oAuth.toJson()),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<String> _getUserName(String accessToken) async {
|
||||
final response = await _makeGetRequest(
|
||||
Uri.parse('$_baseApiUrl/users/settings'),
|
||||
accessToken,
|
||||
);
|
||||
return "${jsonDecode(response.body)['account']['id']}";
|
||||
}
|
||||
|
||||
String _authUrl() {
|
||||
return '$_baseOAuthUrl/authorize?client_id=$_clientId&response_type=code';
|
||||
}
|
||||
|
||||
Future<dynamic> _getOAuth(String code) async {
|
||||
final params = {
|
||||
'code': code,
|
||||
'client_id': _clientId,
|
||||
'client_secret': _clientSecret,
|
||||
'redirect_uri': _redirectUri,
|
||||
'grant_type': 'authorization_code',
|
||||
};
|
||||
final response = await http.post(
|
||||
Uri.parse('$_baseApiUrl/oauth/token'),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: jsonEncode(params),
|
||||
);
|
||||
return jsonDecode(response.body);
|
||||
}
|
||||
|
||||
Future<Response> _makeGetRequest(Uri url, String accessToken) async {
|
||||
return await http.get(
|
||||
url,
|
||||
headers: {
|
||||
'Authorization': 'Bearer $accessToken',
|
||||
'simkl-api-key': _clientId,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<Response> _makePostRequest(
|
||||
Uri url,
|
||||
String accessToken,
|
||||
Object? body,
|
||||
) async {
|
||||
return await http.post(
|
||||
url,
|
||||
headers: {
|
||||
'Authorization': 'Bearer $accessToken',
|
||||
'simkl-api-key': _clientId,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: jsonEncode(body),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String displayScore(int score) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
(int, int) getScoreValue() {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
194
lib/services/trackers/simkl.g.dart
Normal file
194
lib/services/trackers/simkl.g.dart
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'simkl.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$simklHash() => r'7385cd7925dff352509f1b51d4ab2eb6af733b15';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
_SystemHash._();
|
||||
|
||||
static int combine(int hash, int value) {
|
||||
// ignore: parameter_assignments
|
||||
hash = 0x1fffffff & (hash + value);
|
||||
// ignore: parameter_assignments
|
||||
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
|
||||
return hash ^ (hash >> 6);
|
||||
}
|
||||
|
||||
static int finish(int hash) {
|
||||
// ignore: parameter_assignments
|
||||
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
|
||||
// ignore: parameter_assignments
|
||||
hash = hash ^ (hash >> 11);
|
||||
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _$Simkl extends BuildlessAutoDisposeNotifier<void> {
|
||||
late final int syncId;
|
||||
late final ItemType? itemType;
|
||||
|
||||
void build({
|
||||
required int syncId,
|
||||
required ItemType? itemType,
|
||||
});
|
||||
}
|
||||
|
||||
/// See also [Simkl].
|
||||
@ProviderFor(Simkl)
|
||||
const simklProvider = SimklFamily();
|
||||
|
||||
/// See also [Simkl].
|
||||
class SimklFamily extends Family<void> {
|
||||
/// See also [Simkl].
|
||||
const SimklFamily();
|
||||
|
||||
/// See also [Simkl].
|
||||
SimklProvider call({
|
||||
required int syncId,
|
||||
required ItemType? itemType,
|
||||
}) {
|
||||
return SimklProvider(
|
||||
syncId: syncId,
|
||||
itemType: itemType,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
SimklProvider getProviderOverride(
|
||||
covariant SimklProvider provider,
|
||||
) {
|
||||
return call(
|
||||
syncId: provider.syncId,
|
||||
itemType: provider.itemType,
|
||||
);
|
||||
}
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
|
||||
_allTransitiveDependencies;
|
||||
|
||||
@override
|
||||
String? get name => r'simklProvider';
|
||||
}
|
||||
|
||||
/// See also [Simkl].
|
||||
class SimklProvider extends AutoDisposeNotifierProviderImpl<Simkl, void> {
|
||||
/// See also [Simkl].
|
||||
SimklProvider({
|
||||
required int syncId,
|
||||
required ItemType? itemType,
|
||||
}) : this._internal(
|
||||
() => Simkl()
|
||||
..syncId = syncId
|
||||
..itemType = itemType,
|
||||
from: simklProvider,
|
||||
name: r'simklProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$simklHash,
|
||||
dependencies: SimklFamily._dependencies,
|
||||
allTransitiveDependencies: SimklFamily._allTransitiveDependencies,
|
||||
syncId: syncId,
|
||||
itemType: itemType,
|
||||
);
|
||||
|
||||
SimklProvider._internal(
|
||||
super._createNotifier, {
|
||||
required super.name,
|
||||
required super.dependencies,
|
||||
required super.allTransitiveDependencies,
|
||||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.syncId,
|
||||
required this.itemType,
|
||||
}) : super.internal();
|
||||
|
||||
final int syncId;
|
||||
final ItemType? itemType;
|
||||
|
||||
@override
|
||||
void runNotifierBuild(
|
||||
covariant Simkl notifier,
|
||||
) {
|
||||
return notifier.build(
|
||||
syncId: syncId,
|
||||
itemType: itemType,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Override overrideWith(Simkl Function() create) {
|
||||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: SimklProvider._internal(
|
||||
() => create()
|
||||
..syncId = syncId
|
||||
..itemType = itemType,
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
syncId: syncId,
|
||||
itemType: itemType,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
AutoDisposeNotifierProviderElement<Simkl, void> createElement() {
|
||||
return _SimklProviderElement(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is SimklProvider &&
|
||||
other.syncId == syncId &&
|
||||
other.itemType == itemType;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, syncId.hashCode);
|
||||
hash = _SystemHash.combine(hash, itemType.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
mixin SimklRef on AutoDisposeNotifierProviderRef<void> {
|
||||
/// The parameter `syncId` of this provider.
|
||||
int get syncId;
|
||||
|
||||
/// The parameter `itemType` of this provider.
|
||||
ItemType? get itemType;
|
||||
}
|
||||
|
||||
class _SimklProviderElement
|
||||
extends AutoDisposeNotifierProviderElement<Simkl, void> with SimklRef {
|
||||
_SimklProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
int get syncId => (origin as SimklProvider).syncId;
|
||||
@override
|
||||
ItemType? get itemType => (origin as SimklProvider).itemType;
|
||||
}
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||
|
|
@ -67,11 +67,16 @@ TrackStatus toTrackStatus(TrackStatus status, ItemType itemType, int syncId) {
|
|||
"Anilist",
|
||||
const Color.fromRGBO(51, 37, 50, 1),
|
||||
),
|
||||
_ => (
|
||||
3 => (
|
||||
"assets/trackers_icons/tracker_kitsu.webp",
|
||||
"Kitsu",
|
||||
const Color.fromRGBO(18, 25, 35, 1),
|
||||
),
|
||||
_ => (
|
||||
"assets/trackers_icons/tracker_simkl.png",
|
||||
"Simkl",
|
||||
const Color.fromARGB(255, 35, 15, 90),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
61
pubspec.lock
61
pubspec.lock
|
|
@ -5,26 +5,31 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: _fe_analyzer_shared
|
||||
sha256: da0d9209ca76bde579f2da330aeb9df62b6319c834fa7baae052021b0462401f
|
||||
sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "85.0.0"
|
||||
version: "76.0.0"
|
||||
_macros:
|
||||
dependency: transitive
|
||||
description: dart
|
||||
source: sdk
|
||||
version: "0.3.3"
|
||||
analyzer:
|
||||
dependency: "direct overridden"
|
||||
description:
|
||||
name: analyzer
|
||||
sha256: "974859dc0ff5f37bc4313244b3218c791810d03ab3470a579580279ba971a48d"
|
||||
sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.7.1"
|
||||
version: "6.11.0"
|
||||
analyzer_plugin:
|
||||
dependency: "direct overridden"
|
||||
description:
|
||||
name: analyzer_plugin
|
||||
sha256: dd574a0ab77de88b7d9c12bc4b626109a5ca9078216a79041a5c24c3a1bd103c
|
||||
sha256: "9661b30b13a685efaee9f02e5d01ed9f2b423bd889d28a304d02d704aee69161"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.13.7"
|
||||
version: "0.11.3"
|
||||
antlr4:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -519,10 +524,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: flex_color_scheme
|
||||
sha256: "034d5720747e6af39b2ad090d82dd92d33fde68e7964f1814b714c9d49ddbd64"
|
||||
sha256: "3344f8f6536c6ce0473b98e9f084ef80ca89024ad3b454f9c32cf840206f4387"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.3.0"
|
||||
version: "8.2.0"
|
||||
flex_seed_scheme:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1078,26 +1083,26 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0"
|
||||
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "11.0.1"
|
||||
version: "10.0.9"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
|
||||
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.10"
|
||||
version: "3.0.9"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
|
||||
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
version: "3.0.1"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1114,6 +1119,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
macros:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: macros
|
||||
sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.3-main.0"
|
||||
marquee:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -1159,7 +1172,7 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
path: "libs/android/media_kit_libs_android_video"
|
||||
ref: baeecb6aa673e49a173448909a9e15d17d1a9c23
|
||||
ref: HEAD
|
||||
resolved-ref: baeecb6aa673e49a173448909a9e15d17d1a9c23
|
||||
url: "https://github.com/Schnitzel5/media-kit.git"
|
||||
source: git
|
||||
|
|
@ -1168,7 +1181,7 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
path: "libs/ios/media_kit_libs_ios_video"
|
||||
ref: baeecb6aa673e49a173448909a9e15d17d1a9c23
|
||||
ref: HEAD
|
||||
resolved-ref: baeecb6aa673e49a173448909a9e15d17d1a9c23
|
||||
url: "https://github.com/Schnitzel5/media-kit.git"
|
||||
source: git
|
||||
|
|
@ -1177,7 +1190,7 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
path: "libs/linux/media_kit_libs_linux"
|
||||
ref: baeecb6aa673e49a173448909a9e15d17d1a9c23
|
||||
ref: HEAD
|
||||
resolved-ref: baeecb6aa673e49a173448909a9e15d17d1a9c23
|
||||
url: "https://github.com/Schnitzel5/media-kit.git"
|
||||
source: git
|
||||
|
|
@ -1186,7 +1199,7 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
path: "libs/macos/media_kit_libs_macos_video"
|
||||
ref: baeecb6aa673e49a173448909a9e15d17d1a9c23
|
||||
ref: HEAD
|
||||
resolved-ref: baeecb6aa673e49a173448909a9e15d17d1a9c23
|
||||
url: "https://github.com/Schnitzel5/media-kit.git"
|
||||
source: git
|
||||
|
|
@ -1204,7 +1217,7 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
path: "libs/windows/media_kit_libs_windows_video"
|
||||
ref: baeecb6aa673e49a173448909a9e15d17d1a9c23
|
||||
ref: HEAD
|
||||
resolved-ref: baeecb6aa673e49a173448909a9e15d17d1a9c23
|
||||
url: "https://github.com/Schnitzel5/media-kit.git"
|
||||
source: git
|
||||
|
|
@ -1891,10 +1904,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00"
|
||||
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.6"
|
||||
version: "0.7.4"
|
||||
time:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -2035,10 +2048,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
||||
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
version: "2.1.4"
|
||||
video_player:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -2265,4 +2278,4 @@ packages:
|
|||
version: "2.2.2"
|
||||
sdks:
|
||||
dart: ">=3.8.1 <4.0.0"
|
||||
flutter: ">=3.35.0"
|
||||
flutter: ">=3.29.0"
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ dependencies:
|
|||
html: ^0.15.5
|
||||
font_awesome_flutter: ^10.8.0
|
||||
expandable_text: ^2.3.0
|
||||
flex_color_scheme: ^8.3.0
|
||||
flex_color_scheme: ^8.1.0
|
||||
extended_image: ^10.0.0
|
||||
photo_view: ^0.15.0
|
||||
grouped_list: ^6.0.0
|
||||
|
|
@ -107,14 +107,14 @@ dependencies:
|
|||
|
||||
dependency_overrides:
|
||||
ffi: ^2.1.3
|
||||
analyzer: ^7.4.5
|
||||
analyzer: ">=5.2.0 <7.0.0"
|
||||
html: ^0.15.4
|
||||
flutter_web_auth_2:
|
||||
git:
|
||||
url: https://github.com/ThexXTURBOXx/flutter_web_auth_2.git
|
||||
ref: 3.x-without-v1
|
||||
path: flutter_web_auth_2
|
||||
analyzer_plugin: ^0.13.1
|
||||
analyzer_plugin: ^0.11.3
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
|
|||
Loading…
Reference in a new issue