fix: trakt integration and multiple episode thing

This commit is contained in:
omkar 2025-01-14 18:42:59 +05:30
parent 481491d172
commit 2be343b276
4 changed files with 90 additions and 18 deletions

View file

@ -52,10 +52,15 @@ class _StremioItemSeasonSelectorState extends State<StremioItemSeasonSelector>
return; return;
} }
final index = getSelectedSeason();
_tabController = TabController( _tabController = TabController(
length: seasonMap.keys.length, length: seasonMap.keys.length,
vsync: this, vsync: this,
initialIndex: getSelectedSeason(), initialIndex: index.clamp(
0,
seasonMap.keys.isNotEmpty ? seasonMap.keys.length - 1 : 0,
),
); );
// This is for rendering the component again for the selection of another tab // This is for rendering the component again for the selection of another tab

View file

@ -59,6 +59,8 @@ class _VideoViewerState extends State<VideoViewer> {
Future<types.Meta>? traktProgress; Future<types.Meta>? traktProgress;
Future<void> saveWatchHistory() async { Future<void> saveWatchHistory() async {
_logger.info('Starting to save watch history...');
final duration = player.state.duration.inSeconds; final duration = player.state.duration.inSeconds;
if (duration <= 30) { if (duration <= 30) {
@ -68,7 +70,7 @@ class _VideoViewerState extends State<VideoViewer> {
if (gotFromTraktDuration == false) { if (gotFromTraktDuration == false) {
_logger.info( _logger.info(
"did not start the scrobbing because initially time is not retrieved from the api", "Did not start the scrobbling because initially time is not retrieved from the API.",
); );
return; return;
} }
@ -113,6 +115,8 @@ class _VideoViewerState extends State<VideoViewer> {
season: _source.season, season: _source.season,
), ),
); );
_logger.info('Watch history saved successfully.');
} }
late final controller = VideoController( late final controller = VideoController(
@ -128,26 +132,36 @@ class _VideoViewerState extends State<VideoViewer> {
int? traktId; int? traktId;
Future<void> setDurationFromTrakt() async { Future<void> setDurationFromTrakt({
Future<types.Meta>? traktProgress,
}) async {
_logger.info('Setting duration from Trakt...');
try { try {
if (player.state.duration.inSeconds < 2) { if (player.state.duration.inSeconds < 2) {
_logger.info('Duration is too short to set from Trakt.');
return; return;
} }
if (gotFromTraktDuration) { if (gotFromTraktDuration) {
_logger.info('Duration already set from Trakt.');
return; return;
} }
gotFromTraktDuration = true; gotFromTraktDuration = true;
if (!TraktService.isEnabled() || traktProgress == null) { if (!TraktService.isEnabled() ||
(traktProgress ?? this.traktProgress) == null) {
_logger.info(
'Trakt service is not enabled or progress is null. Playing video.');
player.play(); player.play();
return; return;
} }
final progress = await traktProgress; final progress = await (traktProgress ?? this.traktProgress);
if (this.meta is! types.Meta) { if (this.meta is! types.Meta) {
_logger.info('Meta is not of type types.Meta.');
return; return;
} }
@ -161,11 +175,14 @@ class _VideoViewerState extends State<VideoViewer> {
); );
if (duration.inSeconds > 10) { if (duration.inSeconds > 10) {
_logger.info('Seeking to duration: $duration');
await player.seek(duration); await player.seek(duration);
} }
await player.play(); await player.play();
_logger.info('Video started playing.');
} catch (e) { } catch (e) {
_logger.severe('Error setting duration from Trakt: $e');
await player.play(); await player.play();
} }
} }
@ -175,24 +192,43 @@ class _VideoViewerState extends State<VideoViewer> {
PlaybackConfig config = getPlaybackConfig(); PlaybackConfig config = getPlaybackConfig();
Future setupVideoThings() async { Future setupVideoThings() async {
_logger.info('Setting up video things...');
traktProgress = null;
traktProgress = TraktService.instance!.getProgress(
meta as types.Meta,
bypassCache: true,
);
_duration = player.stream.duration.listen((item) async { _duration = player.stream.duration.listen((item) async {
if (meta is types.Meta) {
setDurationFromTrakt(traktProgress: traktProgress);
}
if (item.inSeconds != 0) { if (item.inSeconds != 0) {
_logger.info('Duration updated: $item');
await saveWatchHistory(); await saveWatchHistory();
} }
}); });
_timer = Timer.periodic(const Duration(seconds: 30), (timer) { _timer = Timer.periodic(const Duration(seconds: 30), (timer) {
_logger.info('Periodic save watch history triggered.');
saveWatchHistory(); saveWatchHistory();
}); });
_streamListen = player.stream.playing.listen((playing) { _streamListen = player.stream.playing.listen((playing) {
_logger.info('Playing state changed: $playing');
saveWatchHistory(); saveWatchHistory();
}); });
_logger.info('Loading file...');
return loadFile(); return loadFile();
} }
destroyVideoThing() async { destroyVideoThing() async {
_logger.info('Destroying video things...');
timeLoaded = false; timeLoaded = false;
gotFromTraktDuration = false; gotFromTraktDuration = false;
traktProgress = null; traktProgress = null;
@ -200,11 +236,13 @@ class _VideoViewerState extends State<VideoViewer> {
for (final item in listener) { for (final item in listener) {
item.cancel(); item.cancel();
} }
listener = [];
_timer?.cancel(); _timer?.cancel();
_streamListen?.cancel(); _streamListen?.cancel();
_duration?.cancel(); _duration?.cancel();
if (meta is types.Meta && player.state.duration.inSeconds > 30) { if (meta is types.Meta && player.state.duration.inSeconds > 30) {
_logger.info('Stopping scrobbling and clearing cache...');
await TraktService.instance!.stopScrobbling( await TraktService.instance!.stopScrobbling(
meta: meta as types.Meta, meta: meta as types.Meta,
progress: currentProgressInPercentage, progress: currentProgressInPercentage,
@ -212,11 +250,14 @@ class _VideoViewerState extends State<VideoViewer> {
traktId: traktId, traktId: traktId,
); );
} }
_logger.info('Video things destroyed.');
} }
GlobalKey videoKey = GlobalKey(); GlobalKey videoKey = GlobalKey();
generateNewKey() { generateNewKey() {
_logger.info('Generating new key...');
videoKey = GlobalKey(); videoKey = GlobalKey();
setState(() {}); setState(() {});
@ -225,6 +266,8 @@ class _VideoViewerState extends State<VideoViewer> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_logger.info('Initializing VideoViewer...');
_source = widget.source; _source = widget.source;
SystemChrome.setEnabledSystemUIMode( SystemChrome.setEnabledSystemUIMode(
@ -234,6 +277,7 @@ class _VideoViewerState extends State<VideoViewer> {
if (player.platform is NativePlayer && !kIsWeb) { if (player.platform is NativePlayer && !kIsWeb) {
Future.microtask(() async { Future.microtask(() async {
_logger.info('Setting network timeout...');
await (player.platform as dynamic).setProperty('network-timeout', '60'); await (player.platform as dynamic).setProperty('network-timeout', '60');
}); });
} }
@ -242,17 +286,17 @@ class _VideoViewerState extends State<VideoViewer> {
_source, _source,
widget.meta!, widget.meta!,
); );
_logger.info('VideoViewer initialized.');
} }
Future<void> loadFile() async { Future<void> loadFile() async {
_logger.info('Loading file...');
Duration duration = const Duration(seconds: 0); Duration duration = const Duration(seconds: 0);
if (meta is types.Meta && TraktService.isEnabled()) { if (meta is types.Meta && TraktService.isEnabled()) {
_logger.info("Playing video ${(meta as types.Meta).selectedVideoIndex}"); _logger.info("Playing video ${(meta as types.Meta).selectedVideoIndex}");
traktProgress = TraktService.instance!.getProgress(
meta as types.Meta,
);
} else { } else {
final item = await zeeeWatchHistory!.getItemWatchHistory( final item = await zeeeWatchHistory!.getItemWatchHistory(
ids: [ ids: [
@ -279,6 +323,7 @@ class _VideoViewerState extends State<VideoViewer> {
switch (_source.runtimeType) { switch (_source.runtimeType) {
case const (FileSource): case const (FileSource):
if (kIsWeb) { if (kIsWeb) {
_logger.info('FileSource is not supported on web.');
return; return;
} }
player.open( player.open(
@ -300,10 +345,12 @@ class _VideoViewerState extends State<VideoViewer> {
play: false, play: false,
); );
} }
_logger.info('File loaded successfully.');
} }
late StreamSubscription<bool>? _streamListen; StreamSubscription<bool>? _streamListen;
late StreamSubscription<dynamic>? _duration; StreamSubscription<dynamic>? _duration;
@override @override
void dispose() { void dispose() {
@ -315,27 +362,34 @@ class _VideoViewerState extends State<VideoViewer> {
DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeLeft,
DeviceOrientation.landscapeRight, DeviceOrientation.landscapeRight,
]); ]);
SystemChrome.setEnabledSystemUIMode( SystemChrome.setEnabledSystemUIMode(
SystemUiMode.edgeToEdge, SystemUiMode.edgeToEdge,
overlays: [], overlays: [],
); );
destroyVideoThing(); destroyVideoThing();
player.dispose(); player.dispose();
super.dispose(); super.dispose();
_logger.info('VideoViewer disposed.');
} }
onVideoChange(DocSource source, LibraryItem item) async { onVideoChange(DocSource source, LibraryItem item) async {
_source = source;
meta = item;
setState(() {}); setState(() {});
await destroyVideoThing(); await destroyVideoThing();
_logger.info('Changing video source...');
_source = source;
meta = item;
setState(() {}); setState(() {});
traktProgress = null;
await setupVideoThings(); await setupVideoThings();
await setDurationFromTrakt();
setState(() {}); setState(() {});
generateNewKey(); generateNewKey();
_logger.info('Video source changed successfully.');
} }
@override @override

View file

@ -102,7 +102,7 @@ class _VideoViewerMobileState extends State<VideoViewerMobile> {
key: widget.videoKey, key: widget.videoKey,
onExitFullscreen: () async { onExitFullscreen: () async {
await defaultExitNativeFullscreen(); await defaultExitNativeFullscreen();
if (context.mounted) Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
controller: widget.controller, controller: widget.controller,
controls: MaterialVideoControls, controls: MaterialVideoControls,
@ -126,7 +126,14 @@ class _VideoViewerMobileState extends State<VideoViewerMobile> {
topButtonBar: [ topButtonBar: [
MaterialCustomButton( MaterialCustomButton(
onPressed: () { onPressed: () {
Navigator.of(context).pop(); Navigator.of(
context,
rootNavigator: true,
).pop();
Navigator.of(
context,
rootNavigator: true,
).pop();
}, },
icon: const Icon( icon: const Icon(
Icons.arrow_back, Icons.arrow_back,

View file

@ -992,6 +992,8 @@ class TraktService {
continue; continue;
} }
meta.videos = meta.videos ?? [];
final result = meta.videos?.firstWhereOrNull((video) { final result = meta.videos?.firstWhereOrNull((video) {
if (video.tvdbId != null && if (video.tvdbId != null &&
item['episode']['ids']['tvdb'] != null) { item['episode']['ids']['tvdb'] != null) {
@ -1009,6 +1011,10 @@ class TraktService {
final videoIndex = meta.videos!.indexOf(result); final videoIndex = meta.videos!.indexOf(result);
meta.videos![videoIndex].progress = item['progress']; meta.videos![videoIndex].progress = item['progress'];
_logger.info(
"Setting progress for ${meta.videos![videoIndex].name} to ${item['progress']}",
);
} }
return meta; return meta;
} else { } else {