@@ -19,20 +19,23 @@ import 'package:unyo/core/notification/extension_notifier.dart';
1919import 'package:unyo/core/notification/media_list_entry_notifier.dart' ;
2020import 'package:unyo/core/notification/user_notifier.dart' ;
2121import 'package:unyo/core/notification/video_info_notifier.dart' ;
22+ import 'package:unyo/core/services/api/dto/aniskip/aniskip_times_entity.dart' ;
23+ import 'package:unyo/core/services/api/http/api_response.dart' ;
24+ import 'package:unyo/core/services/api/http/http_service.dart' ;
2225import 'package:unyo/core/services/video/video_service.dart' ;
2326import 'package:unyo/data/repositories/extension_repository_aniyomi.dart' ;
2427import 'package:unyo/domain/entities/anime.dart' ;
2528import 'package:unyo/domain/entities/episode_info.dart' ;
2629import 'package:unyo/domain/entities/extension.dart' ;
2730import 'package:unyo/domain/entities/extension/video.dart' as ext;
31+ import 'package:unyo/config/config.dart' as config;
2832import 'package:unyo/domain/entities/media_list_entry.dart' ;
2933import 'package:unyo/domain/entities/user.dart' ;
3034import 'package:unyo/core/di/locator.dart' ;
3135import 'package:unyo/domain/entities/video_info.dart' ;
3236import 'package:unyo/presentation/dialogs/warning_dialog.dart' ;
3337
3438class VideoCubit extends Cubit <VideoState > with EffectMixin <VideoState > {
35- final Logger _logger = sl <Logger >();
3639
3740 // Repositories
3841 final ExtensionRepositoryAniyomi _extensionRepositoryAniyomi;
@@ -55,6 +58,8 @@ class VideoCubit extends Cubit<VideoState> with EffectMixin<VideoState> {
5558
5659 // Services
5760 late VideoService _videoService;
61+ final Logger _logger = sl <Logger >();
62+ final HttpService _httpService = sl <HttpService >();
5863
5964 // Others
6065 bool _videoServiceInitialized = false ;
@@ -78,6 +83,8 @@ class VideoCubit extends Cubit<VideoState> with EffectMixin<VideoState> {
7883 episodesInfo: [],
7984 mediaListEntry: MediaListEntryModel .empty (),
8085 availableCastDevices: [],
86+ openingSkipTimes: AniskipTimesResults (),
87+ endingSkipTimes: AniskipTimesResults (),
8188 isLoading: true ,
8289 ),
8390 ) {
@@ -117,6 +124,7 @@ class VideoCubit extends Cubit<VideoState> with EffectMixin<VideoState> {
117124 _videoInfoSubscription = _videoInfoNotifier.videoInfoStream.listen ((videoInfo) {
118125 _initializeVideoService (videoInfo);
119126 _videoInfoSubscription.cancel ();
127+ Future .delayed (const Duration (seconds: 1 ), () => _getAniskipSkipTimes (state.selectedAnime, videoInfo));
120128 });
121129 _selectedAnimeSubscription = _selectedAnimeNotifier.animeStream.listen ((selectedAnime) {
122130 emit (state.copyWith (selectedAnime: selectedAnime));
@@ -168,6 +176,52 @@ class VideoCubit extends Cubit<VideoState> with EffectMixin<VideoState> {
168176 _logger.i ("VideoService updated for new episode." );
169177 }
170178
179+ Future <void > _getAniskipSkipTimes (Anime selectedAnime, VideoInfo videoInfo) async {
180+ double formattedDuration = selectedAnime.duration > 0 ? selectedAnime.duration * 60 : 24 * 60 ;
181+ ApiResponse <AniskipTimesEntity > aniskipResponse =
182+ await _httpService.get (
183+ "${config .aniskiBaseEndpoint }/v2/skip-times/${selectedAnime .idMal }/${videoInfo .playlistIndex + 1 }?types=op&types=ed&episodeLength=$formattedDuration " ,
184+ fromJson: AniskipTimesEntity .fromJson
185+ );
186+ if (aniskipResponse.statusCode > 299 || aniskipResponse.data.statusCode > 299 ) {
187+ _logger.e ("Failed to fetch Aniskip times for malId: ${selectedAnime .idMal }. Status code: ${aniskipResponse .data .statusCode }. Message: ${aniskipResponse .data .message }" );
188+ return ;
189+ }
190+ final openingSkipTimes = aniskipResponse.data.results.where ((skip) => skip.skipType == "op" ).firstOrNull;
191+ final endingSkipTimes = aniskipResponse.data.results.where ((skip) => skip.skipType == "ed" ).firstOrNull;
192+ emit (state.copyWith (openingSkipTimes: openingSkipTimes ?? AniskipTimesResults (), endingSkipTimes: endingSkipTimes ?? AniskipTimesResults ()));
193+ }
194+
195+ String getSkipTimeText () {
196+ if (state.openingSkipTimes.interval.endTime > 0 &&
197+ _videoService.position.inSeconds > state.openingSkipTimes.interval.startTime &&
198+ state.openingSkipTimes.interval.endTime > _videoService.position.inSeconds) {
199+ return "Skip Opening" ;
200+ } else if (state.endingSkipTimes.interval.endTime > 0 &&
201+ _videoService.position.inSeconds > state.endingSkipTimes.interval.startTime &&
202+ state.endingSkipTimes.interval.endTime > _videoService.position.inSeconds) {
203+ return "Skip Ending" ;
204+ } else {
205+ return "+ ${state .loggedUser .settings .manualSkipTime .toString ()}s" ;
206+ }
207+ }
208+
209+
210+ void performSkipActin () {
211+ if (state.openingSkipTimes.interval.endTime > 0 &&
212+ _videoService.position.inSeconds > state.openingSkipTimes.interval.startTime &&
213+ state.openingSkipTimes.interval.endTime > _videoService.position.inSeconds) {
214+ _videoService.seekTo (Duration (seconds: state.openingSkipTimes.interval.endTime.toInt ()));
215+ } else if (state.endingSkipTimes.interval.endTime > 0 &&
216+ _videoService.position.inSeconds > state.endingSkipTimes.interval.startTime &&
217+ state.endingSkipTimes.interval.endTime > _videoService.position.inSeconds) {
218+ _videoService.seekTo (Duration (seconds: state.endingSkipTimes.interval.endTime.toInt ()));
219+ } else {
220+ _videoService.forward (Duration (seconds: state.loggedUser.settings.manualSkipTime));
221+ }
222+ }
223+
224+
171225 Future <void > _getAvailableCastDevices () async {
172226 List <CastDevice > availableCastDevices = await CastDiscoveryService ().search ();
173227 emit (state.copyWith (availableCastDevices: availableCastDevices));
0 commit comments