mirror of
https://github.com/madari-media/madari-oss.git
synced 2026-04-21 15:11:57 +00:00
fix: trakt integration and multiple episode thing
This commit is contained in:
parent
481491d172
commit
2be343b276
4 changed files with 90 additions and 18 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue