| 
														
															@@ -72,60 +72,6 @@ class Scrobble(TimeStampedModel): 
														 | 
													
												
											
												
													
														| 
														 | 
														
															     def __str__(self): 
														 | 
														
														 | 
														
															     def __str__(self): 
														 | 
													
												
											
												
													
														| 
														 | 
														
															         return f"Scrobble of {self.media_obj} {self.timestamp.year}-{self.timestamp.month}" 
														 | 
														
														 | 
														
															         return f"Scrobble of {self.media_obj} {self.timestamp.year}-{self.timestamp.month}" 
														 | 
													
												
											
												
													
														| 
														 | 
														
															  
														 | 
														
														 | 
														
															  
														 | 
													
												
											
												
													
														| 
														 | 
														
															-    def resumable(self, playback_ticks): 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        """Check if a scrobble is not finished or beyond the configured resume limit. 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															- 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        The idea here is to check whether a scrobble should be resumed, or a new 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        one created. If this method returns true, we should update an existing 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        scrobble, suggesting the user just paused their scrobble. This limit 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        should be different for different media. We are more likely to pause a video 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        or sports event for a while, and expect to resume it than an audio track or 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        a podcast. 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															- 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        """ 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        diff = None 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        # Default finish expectation 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        percent_for_completion = 100 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        # By default, assume we're not beyond resume limits 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        # This is to avoid spam scrobbles if webhooks go crazy 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        beyond_resume_limit = False 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        now = timezone.now() 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															- 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        if self.playback_position_ticks == playback_ticks: 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-            # shortcircut in the case where we've resumed a track at the same playback ticks 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-            return True 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															- 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        if self.video: 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-            diff = timedelta(seconds=Video.RESUME_LIMIT) 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-            percent_for_completion = Video.COMPLETION_PERCENT 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        if self.track: 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-            diff = timedelta(seconds=Track.RESUME_LIMIT) 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-            percent_for_completion = Track.COMPLETION_PERCENT 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        if self.podcast_episode: 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-            diff = timedelta(seconds=Episode.RESUME_LIMIT) 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-            percent_for_completion = Episode.COMPLETION_PERCENT 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        if self.sport_event: 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-            diff = timedelta(seconds=SportEvent.RESUME_LIMIT) 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-            percent_for_completion = SportEvent.COMPLETION_PERCENT 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															- 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        if diff and self.timestamp: 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-            beyond_resume_limit = self.timestamp + diff <= now 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															- 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        finished = self.percent_played >= percent_for_completion 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															- 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        resumable = not finished or not beyond_resume_limit 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															- 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        if not finished: 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-            logger.debug( 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-                f"{self} resumable, percent played {self.percent_played} is less than {percent_for_completion}" 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-            ) 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        if not beyond_resume_limit: 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-            logger.debug( 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-                f"{self} resumable, started less than {diff.seconds} seconds ago" 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-            ) 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															- 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        return not finished and not beyond_resume_limit 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															- 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															     @classmethod 
														 | 
														
														 | 
														
															     @classmethod 
														 | 
													
												
											
												
													
														| 
														 | 
														
															     def create_or_update_for_video( 
														 | 
														
														 | 
														
															     def create_or_update_for_video( 
														 | 
													
												
											
												
													
														| 
														 | 
														
															         cls, video: "Video", user_id: int, scrobble_data: dict 
														 | 
														
														 | 
														
															         cls, video: "Video", user_id: int, scrobble_data: dict 
														 | 
													
												
											
										
											
												
													
														 | 
														
															@@ -133,13 +79,15 @@ class Scrobble(TimeStampedModel): 
														 | 
													
												
											
												
													
														| 
														 | 
														
															         scrobble_data['video_id'] = video.id 
														 | 
														
														 | 
														
															         scrobble_data['video_id'] = video.id 
														 | 
													
												
											
												
													
														| 
														 | 
														
															  
														 | 
														
														 | 
														
															  
														 | 
													
												
											
												
													
														| 
														 | 
														
															         scrobble = ( 
														 | 
														
														 | 
														
															         scrobble = ( 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-            cls.objects.filter(video=video, user_id=user_id) 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															 
														 | 
														
														 | 
														
															+            cls.objects.filter( 
														 | 
													
												
											
												
													
														| 
														 | 
														
															 
														 | 
														
														 | 
														
															+                video=video, 
														 | 
													
												
											
												
													
														| 
														 | 
														
															 
														 | 
														
														 | 
														
															+                user_id=user_id, 
														 | 
													
												
											
												
													
														| 
														 | 
														
															 
														 | 
														
														 | 
														
															+                played_to_completion=False, 
														 | 
													
												
											
												
													
														| 
														 | 
														
															 
														 | 
														
														 | 
														
															+            ) 
														 | 
													
												
											
												
													
														| 
														 | 
														
															             .order_by('-modified') 
														 | 
														
														 | 
														
															             .order_by('-modified') 
														 | 
													
												
											
												
													
														| 
														 | 
														
															             .first() 
														 | 
														
														 | 
														
															             .first() 
														 | 
													
												
											
												
													
														| 
														 | 
														
															         ) 
														 | 
														
														 | 
														
															         ) 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        if scrobble and scrobble.resumable( 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-            scrobble_data['playback_position_ticks'] 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        ): 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															 
														 | 
														
														 | 
														
															+        if scrobble: 
														 | 
													
												
											
												
													
														| 
														 | 
														
															             logger.info( 
														 | 
														
														 | 
														
															             logger.info( 
														 | 
													
												
											
												
													
														| 
														 | 
														
															                 f"Found existing scrobble for video {video}, updating", 
														 | 
														
														 | 
														
															                 f"Found existing scrobble for video {video}, updating", 
														 | 
													
												
											
												
													
														| 
														 | 
														
															                 {"scrobble_data": scrobble_data}, 
														 | 
														
														 | 
														
															                 {"scrobble_data": scrobble_data}, 
														 | 
													
												
											
										
											
												
													
														 | 
														
															@@ -164,13 +112,15 @@ class Scrobble(TimeStampedModel): 
														 | 
													
												
											
												
													
														| 
														 | 
														
															         scrobble_data['track_id'] = track.id 
														 | 
														
														 | 
														
															         scrobble_data['track_id'] = track.id 
														 | 
													
												
											
												
													
														| 
														 | 
														
															  
														 | 
														
														 | 
														
															  
														 | 
													
												
											
												
													
														| 
														 | 
														
															         scrobble = ( 
														 | 
														
														 | 
														
															         scrobble = ( 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-            cls.objects.filter(track=track, user_id=user_id) 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															 
														 | 
														
														 | 
														
															+            cls.objects.filter( 
														 | 
													
												
											
												
													
														| 
														 | 
														
															 
														 | 
														
														 | 
														
															+                track=track, 
														 | 
													
												
											
												
													
														| 
														 | 
														
															 
														 | 
														
														 | 
														
															+                user_id=user_id, 
														 | 
													
												
											
												
													
														| 
														 | 
														
															 
														 | 
														
														 | 
														
															+                played_to_completion=False, 
														 | 
													
												
											
												
													
														| 
														 | 
														
															 
														 | 
														
														 | 
														
															+            ) 
														 | 
													
												
											
												
													
														| 
														 | 
														
															             .order_by('-modified') 
														 | 
														
														 | 
														
															             .order_by('-modified') 
														 | 
													
												
											
												
													
														| 
														 | 
														
															             .first() 
														 | 
														
														 | 
														
															             .first() 
														 | 
													
												
											
												
													
														| 
														 | 
														
															         ) 
														 | 
														
														 | 
														
															         ) 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        if scrobble and scrobble.resumable( 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-            scrobble_data['playback_position_ticks'] 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        ): 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															 
														 | 
														
														 | 
														
															+        if scrobble: 
														 | 
													
												
											
												
													
														| 
														 | 
														
															             logger.debug( 
														 | 
														
														 | 
														
															             logger.debug( 
														 | 
													
												
											
												
													
														| 
														 | 
														
															                 f"Found existing scrobble for track {track}, updating", 
														 | 
														
														 | 
														
															                 f"Found existing scrobble for track {track}, updating", 
														 | 
													
												
											
												
													
														| 
														 | 
														
															                 {"scrobble_data": scrobble_data}, 
														 | 
														
														 | 
														
															                 {"scrobble_data": scrobble_data}, 
														 | 
													
												
											
										
											
												
													
														 | 
														
															@@ -192,13 +142,15 @@ class Scrobble(TimeStampedModel): 
														 | 
													
												
											
												
													
														| 
														 | 
														
															         scrobble_data['podcast_episode_id'] = episode.id 
														 | 
														
														 | 
														
															         scrobble_data['podcast_episode_id'] = episode.id 
														 | 
													
												
											
												
													
														| 
														 | 
														
															  
														 | 
														
														 | 
														
															  
														 | 
													
												
											
												
													
														| 
														 | 
														
															         scrobble = ( 
														 | 
														
														 | 
														
															         scrobble = ( 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-            cls.objects.filter(podcast_episode=episode, user_id=user_id) 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															 
														 | 
														
														 | 
														
															+            cls.objects.filter( 
														 | 
													
												
											
												
													
														| 
														 | 
														
															 
														 | 
														
														 | 
														
															+                podcast_episode=episode, 
														 | 
													
												
											
												
													
														| 
														 | 
														
															 
														 | 
														
														 | 
														
															+                user_id=user_id, 
														 | 
													
												
											
												
													
														| 
														 | 
														
															 
														 | 
														
														 | 
														
															+                played_to_completion=False, 
														 | 
													
												
											
												
													
														| 
														 | 
														
															 
														 | 
														
														 | 
														
															+            ) 
														 | 
													
												
											
												
													
														| 
														 | 
														
															             .order_by('-modified') 
														 | 
														
														 | 
														
															             .order_by('-modified') 
														 | 
													
												
											
												
													
														| 
														 | 
														
															             .first() 
														 | 
														
														 | 
														
															             .first() 
														 | 
													
												
											
												
													
														| 
														 | 
														
															         ) 
														 | 
														
														 | 
														
															         ) 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        if scrobble and scrobble.resumable( 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-            scrobble_data['playback_position_ticks'] 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        ): 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															 
														 | 
														
														 | 
														
															+        if scrobble: 
														 | 
													
												
											
												
													
														| 
														 | 
														
															             logger.debug( 
														 | 
														
														 | 
														
															             logger.debug( 
														 | 
													
												
											
												
													
														| 
														 | 
														
															                 f"Found existing scrobble for podcast {episode}, updating", 
														 | 
														
														 | 
														
															                 f"Found existing scrobble for podcast {episode}, updating", 
														 | 
													
												
											
												
													
														| 
														 | 
														
															                 {"scrobble_data": scrobble_data}, 
														 | 
														
														 | 
														
															                 {"scrobble_data": scrobble_data}, 
														 | 
													
												
											
										
											
												
													
														 | 
														
															@@ -219,13 +171,15 @@ class Scrobble(TimeStampedModel): 
														 | 
													
												
											
												
													
														| 
														 | 
														
															     ) -> "Scrobble": 
														 | 
														
														 | 
														
															     ) -> "Scrobble": 
														 | 
													
												
											
												
													
														| 
														 | 
														
															         scrobble_data['sport_event_id'] = event.id 
														 | 
														
														 | 
														
															         scrobble_data['sport_event_id'] = event.id 
														 | 
													
												
											
												
													
														| 
														 | 
														
															         scrobble = ( 
														 | 
														
														 | 
														
															         scrobble = ( 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-            cls.objects.filter(sport_event=event, user_id=user_id) 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															 
														 | 
														
														 | 
														
															+            cls.objects.filter( 
														 | 
													
												
											
												
													
														| 
														 | 
														
															 
														 | 
														
														 | 
														
															+                sport_event=event, 
														 | 
													
												
											
												
													
														| 
														 | 
														
															 
														 | 
														
														 | 
														
															+                user_id=user_id, 
														 | 
													
												
											
												
													
														| 
														 | 
														
															 
														 | 
														
														 | 
														
															+                played_to_completion=False, 
														 | 
													
												
											
												
													
														| 
														 | 
														
															 
														 | 
														
														 | 
														
															+            ) 
														 | 
													
												
											
												
													
														| 
														 | 
														
															             .order_by('-modified') 
														 | 
														
														 | 
														
															             .order_by('-modified') 
														 | 
													
												
											
												
													
														| 
														 | 
														
															             .first() 
														 | 
														
														 | 
														
															             .first() 
														 | 
													
												
											
												
													
														| 
														 | 
														
															         ) 
														 | 
														
														 | 
														
															         ) 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        if scrobble and scrobble.resumable( 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-            scrobble_data['playback_position_ticks'] 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        ): 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															 
														 | 
														
														 | 
														
															+        if scrobble: 
														 | 
													
												
											
												
													
														| 
														 | 
														
															             logger.debug( 
														 | 
														
														 | 
														
															             logger.debug( 
														 | 
													
												
											
												
													
														| 
														 | 
														
															                 f"Found existing scrobble for sport event {event}, updating", 
														 | 
														
														 | 
														
															                 f"Found existing scrobble for sport event {event}, updating", 
														 | 
													
												
											
												
													
														| 
														 | 
														
															                 {"scrobble_data": scrobble_data}, 
														 | 
														
														 | 
														
															                 {"scrobble_data": scrobble_data}, 
														 | 
													
												
											
										
											
												
													
														 | 
														
															@@ -246,8 +200,6 @@ class Scrobble(TimeStampedModel): 
														 | 
													
												
											
												
													
														| 
														 | 
														
															         scrobble_status = scrobble_data.pop('mopidy_status', None) 
														 | 
														
														 | 
														
															         scrobble_status = scrobble_data.pop('mopidy_status', None) 
														 | 
													
												
											
												
													
														| 
														 | 
														
															         if not scrobble_status: 
														 | 
														
														 | 
														
															         if not scrobble_status: 
														 | 
													
												
											
												
													
														| 
														 | 
														
															             scrobble_status = scrobble_data.pop('jellyfin_status', None) 
														 | 
														
														 | 
														
															             scrobble_status = scrobble_data.pop('jellyfin_status', None) 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-        if not scrobble_status: 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															-            scrobble_status = "resumed" 
														 | 
														
														 | 
														
															 
														 | 
													
												
											
												
													
														| 
														 | 
														
															  
														 | 
														
														 | 
														
															  
														 | 
													
												
											
												
													
														| 
														 | 
														
															         logger.debug(f"Scrobbling to {scrobble} with status {scrobble_status}") 
														 | 
														
														 | 
														
															         logger.debug(f"Scrobbling to {scrobble} with status {scrobble_status}") 
														 | 
													
												
											
												
													
														| 
														 | 
														
															         scrobble.update_ticks(scrobble_data) 
														 | 
														
														 | 
														
															         scrobble.update_ticks(scrobble_data) 
														 |