// Plugin intialization function public void InitializeSource(int minimumScrobbleSeconds, TrackMonitoringStarted onTrackMonitoringStarted, TrackMonitoring onTrackMonitoring, TrackMonitoringEnded onTrackMonitoringEnded, ScrobbleTrack onScrobbleTrack) { _minimumScrobbleSeconds = minimumScrobbleSeconds; _onTrackMonitoringStarted = onTrackMonitoringStarted; _onTrackMonitoring = onTrackMonitoring; _onTrackMonitoringEnded = onTrackMonitoringEnded; _onScrobbleTrack = onScrobbleTrack; _isIntialized = true; try { // Create a new scrobbler timer, to fire every second _scrobbleTimer = new System.Timers.Timer(); _scrobbleTimer.Interval = 2000; // The ananoymous delegate event that occurs every time the timer fires (elapses) _scrobbleTimer.Elapsed += async(o, e) => { // Stop the timer to prevent multiple executions at the same time _scrobbleTimer.Stop(); // Check for the Windows Media Player process to ensure it's running. // If we don't check for it, the plugin would end up launching it, which we don't want Process[] wmpProcesses = Process.GetProcessesByName("wmplayer"); if (wmpProcesses.Length > 0 && _mediaPlayer == null) { _mediaPlayer = new WindowsMediaPlayer(); Console.WriteLine("Windows Media Player Plugin successfully connected to the WMP COM library."); } else if (wmpProcesses.Length == 0 && _mediaPlayer != null) { Console.WriteLine("Windows Media Player process not detected. Waiting for Windows Media Player process to start..."); } if (_mediaPlayer != null) { #if DebugWMPScrobbler Console.WriteLine("Windows Media Player Plugin checking media state..."); #endif if (_isEnabled) { // Get the current media from Windows Media Player itself (using our helper function) MediaItem mediaDetail = await GetMediaDetail().ConfigureAwait(false); // Get the media player state WMPPlayState playerState = _mediaPlayer?.Player?.playState ?? WMPPlayState.wmppsStopped; double playerPosition = _mediaPlayer?.Player?.Ctlcontrols?.currentPosition ?? 0; // Determine if there is any media loaded bool hasMedia = mediaDetail != null; // Determine if the current track is deemed to have 'ended' as it's been fully listened to bool hasReachedTrackEnd = hasMedia && (int)playerPosition + 1 >= (int)mediaDetail?.TrackLength && mediaDetail?.TrackLength > 0; // Determine if the current track playing isn't the last one we knew about bool hasTrackChanged = _currentMediaItem?.TrackName != mediaDetail?.TrackName; // Determine if the media player is in the 'Paused' state bool isPaused = playerState == WMPPlayState.wmppsPaused; // Determine if the media player is in the 'Playing' state bool isPlaying = playerState == WMPPlayState.wmppsPlaying; bool isTrackChanging = playerState == WMPPlayState.wmppsTransitioning; // Determine if the current media item is scrobbleable bool canScrobble = _currentMediaTrackingTime >= _minimumScrobbleSeconds && (_currentMediaTrackingTime >= Convert.ToInt32(Math.Min(Convert.ToInt32(_currentMediaItem?.TrackLength) / 2, 4 * 60)) && !_currentMediaWasScrobbled); #if DebugWMPScrobbler Console.WriteLine($"Windows Media Player Plugin: Position {playerPosition} of { mediaDetail?.TrackLength }, Tracker time: {_currentMediaTrackingTime}..."); #endif // If the media player is still playing and the track has changed, or the current media has reached it's end (and therefore the media player has stopped) if ((isPlaying && hasMedia && hasTrackChanged) || hasReachedTrackEnd) { // Reset the last paused state _lastStatePaused = false; // Reset the current tracking time to the default number of timer seconds _currentMediaTrackingTime = (_currentMediaTrackingTime == 2) ? 3 : 2; // If we knew about a media item before we got here if (_currentMediaItem != null) { // Fire the 'track monitoring has ended' event for the previous item _onTrackMonitoringEnded?.BeginInvoke(_currentMediaItem, null, null); // Fire the 'scrobble the item' event for the previous item _onScrobbleTrack?.BeginInvoke(_currentMediaItem, null, null); } Console.WriteLine("Windows Media Player: Raising Track Change Method."); // If the reason we are here is because there is a new track being monitored if (hasTrackChanged) { // Track when we started monitoring the new item (to pass to the Last.fm API) mediaDetail.StartedPlaying = DateTime.Now; // Set the current monitor item, to what the media player has told us is playing _currentMediaItem = mediaDetail; // Fire the 'track monitoring has started' even for the new item _onTrackMonitoringStarted?.BeginInvoke(mediaDetail, false, null, null); } // Otherwise if we got here because the current item has ended, and no new item is playing else if (hasReachedTrackEnd) { _currentMediaTrackingTime = 2; // Clear the currently tracked media item, so that if the user starts playing it again, it is treated // as an entirely new scrobble _currentMediaItem = null; } // Reset the flag determining if the current item has been added to the Scrobble queue _currentMediaWasScrobbled = false; } // If the media playing is playing and has media associated, we have reached the point where the item has been // tracked beyond the minimum number of tracking seconds and the item hasn't already been added to the scrobble queue else if (isPlaying && hasMedia && canScrobble && !_currentMediaWasScrobbled) { // Safely add the media item to the scrobble queue lock (_mediaLock) { _mediaToScrobble.Add(_currentMediaItem); } // Fire the 'we are still tracking this item' event _onTrackMonitoring?.BeginInvoke(_currentMediaItem, (int)playerPosition, null, null); // Update the current media tracking time _currentMediaTrackingTime += 2; // Mark the item as having been added to the scrobble queue //(potential improvement, move this property to _currentMediaItem and remove the local variable) _currentMediaWasScrobbled = true; Console.WriteLine($"Windows Media Player: Track {mediaDetail.TrackName} queued for Scrobbling."); } // The media player is playing, and is still playing the same track else if (isPlaying && !hasTrackChanged) { // If the media player wasn't last in the paused state if (_lastStatePaused) { // Fire the 'we started monitoring this item' event _onTrackMonitoringStarted?.BeginInvoke(_currentMediaItem, _lastStatePaused, null, null); // Reset the pause flag _lastStatePaused = false; } // Fire the 'we are still monitoring this item event' (possibly should be inside an else, although won't hurt // where it is) _onTrackMonitoring?.BeginInvoke(_currentMediaItem, (int)playerPosition, null, null); // Update the current media tracking time _currentMediaTrackingTime += 2; } // The media player is not playing else if (!isPlaying && !isTrackChanging) { // If we had been playing, invoke the Track Ended callback if (_currentMediaTrackingTime > 1 && !_lastStatePaused) { _onTrackMonitoringEnded?.BeginInvoke(mediaDetail, null, null); if (!isPaused) { // Fire the 'scrobble the item' event for the previous item _onScrobbleTrack?.BeginInvoke(_currentMediaItem, null, null); //_currentMediaItem = null; } } // Set the persisted pause state _lastStatePaused = isPaused; // If we're not paused (FF, Rewind) if (!isPaused) { // Reset the state tracking how long we played this track for _lastStatePaused = false; _currentMediaTrackingTime = 0; _currentMediaWasScrobbled = false; } } } #if DebugWMPScrobbler Console.WriteLine("Windows Media Plugin checking media state complete."); #endif } else if (_currentMediaItem != null) { _onTrackMonitoringEnded?.BeginInvoke(_currentMediaItem, null, null); _currentMediaItem = null; _currentMediaWasScrobbled = false; } _scrobbleTimer?.Start(); }; } catch (Exception ex) { try { _scrobbleTimer?.Start(); } catch (Exception exception) { // Can occur if you close the application as it's starting up } } }
// Plugin intialization function public void InitializeSource(int minimumScrobbleSeconds, TrackMonitoringStarted onTrackMonitoringStarted, TrackMonitoring onTrackMonitoring, TrackMonitoringEnded onTrackMonitoringEnded, ScrobbleTrack onScrobbleTrack) { _minimumScrobbleSeconds = minimumScrobbleSeconds; _onTrackMonitoringStarted = onTrackMonitoringStarted; _onTrackMonitoring = onTrackMonitoring; _onTrackMonitoringEnded = onTrackMonitoringEnded; _onScrobbleTrack = onScrobbleTrack; _isIntialized = true; try { // Create a new scrobbler timer, to fire at the specified number of seconds _scrobbleTimer = new Timer(); _scrobbleTimer.Interval = 1000 * _timerInterval; // The ananoymous delegate event that occurs every time the timer fires (elapses) _scrobbleTimer.Elapsed += async(o, e) => { // Stop the timer to prevent multiple executions at the same time _scrobbleTimer.Stop(); // Check for the iTunes process to ensure it's running. // If we don't check for it, the plugin would end up launching it when we connect, which we don't want Process[] iTunesProcesses = Process.GetProcessesByName("iTunes"); if (iTunesProcesses.Length > 0) { try { if (_isEnabled) { iTunesApp iTunesApp = new iTunesApp(); #if DebugiTunes Console.WriteLine("iTunes Plugin successfully connected to iTunes COM library."); Console.WriteLine("iTunes Plugin checking media state..."); #endif // Get the current media from iTunes itself (using our helper function) MediaItem mediaDetail = await GetMediaDetail(iTunesApp).ConfigureAwait(false); ITPlayerState playerState = ITPlayerState.ITPlayerStateStopped; double playerPosition = 0; try { // Get the iTunes media player state playerState = iTunesApp?.PlayerState ?? ITPlayerState.ITPlayerStateStopped; playerPosition = iTunesApp?.PlayerPosition ?? 0; } catch (COMException comEx) { // If the player is in an invalid state, this is going to happen! } // Determine if there is any media loaded bool hasMedia = mediaDetail != null; // Determine if the current track is deemed to have 'ended' as it's been fully listened to bool hasReachedTrackEnd = hasMedia && (int)playerPosition + _timerInterval >= (int)mediaDetail?.TrackLength && mediaDetail?.TrackLength > 0; // Determine if the current track playing isn't the last one we knew about bool hasTrackChanged = _currentMediaItem?.TrackName != mediaDetail?.TrackName; // Determine if the media player is in the 'Playing' state bool isPlaying = playerState == ITPlayerState.ITPlayerStatePlaying; // Determine if the media player is in the 'Stopped' (paused) state bool isPaused = playerState == ITPlayerState.ITPlayerStateStopped; // Determine if the current media item is scrobbleable bool canScrobble = _currentMediaTrackingTime >= _minimumScrobbleSeconds && (_currentMediaTrackingTime >= Convert.ToInt32(Math.Min(Convert.ToInt32(_currentMediaItem?.TrackLength) / 2, 4 * 60)) && !_currentMediaWasScrobbled); #if DebugiTunes Console.WriteLine($"iTunes Media Player Plugin: Position {playerPosition} of { mediaDetail?.TrackLength }, Tracker time: {_currentMediaTrackingTime}..."); #endif // If we have reached the point where the item has been tracked beyond the minimum number of tracking seconds // and the item hasn't already been added to the scrobble queue if (canScrobble && !_currentMediaWasScrobbled) { // Safely add the media item to the scrobble queue lock (_mediaLock) { _mediaToScrobble.Add(_currentMediaItem); } // Fire the 'we are still tracking this item' event _onTrackMonitoring?.BeginInvoke(_currentMediaItem, (int)playerPosition, null, null); // Mark the item as having been added to the scrobble queue //(potential improvement, move this property to _currentMediaItem and remove the local variable) _currentMediaWasScrobbled = true; Console.WriteLine($"Track {mediaDetail.TrackName} queued for Scrobbling."); } // If the media player is still playing and the track has changed, or the current media has reached it's end (and therefore the media player has stopped) if ((isPlaying && hasMedia && hasTrackChanged) || hasReachedTrackEnd) { // Reset the last paused state _lastStatePaused = false; // Reset the current tracking time to the default number of timer seconds _currentMediaTrackingTime = _timerInterval; // If we knew about a media item before we got here if (_currentMediaItem != null) { // Fire the 'track monitoring has ended' event for the previous item _onTrackMonitoringEnded?.BeginInvoke(_currentMediaItem, null, null); // Fire the 'scrobble the item' event for the previous item _onScrobbleTrack?.BeginInvoke(_currentMediaItem, null, null); } Console.WriteLine("iTunes: Raising Track Change Method."); // If the reason we are here is because there is a new track being monitored if (hasTrackChanged) { // Set the current monitor item, to what the media player has told us is playing _currentMediaItem = mediaDetail; // Fire the 'track monitoring has started' even for the new item _onTrackMonitoringStarted?.BeginInvoke(mediaDetail, false, null, null); // Track when we started monitoring the new item (to pass to the Last.fm API) mediaDetail.StartedPlaying = DateTime.Now; } // Otherwise if we got here because the current item has ended, and no new item is playing else if (hasReachedTrackEnd) { // Clear the currently tracked media item, so that if the user starts playing it again, it is treated // as an entirely new scrobble _currentMediaItem = null; } // Reset the flag determining if the current item has been added to the Scrobble queue _currentMediaWasScrobbled = false; } // The media player is playing, and is still playing the same track else if (isPlaying && !hasTrackChanged) { // If the media player wasn't last in the paused state if (_lastStatePaused) { // Fire the 'we started monitoring this item' event _onTrackMonitoringStarted?.BeginInvoke(_currentMediaItem, _lastStatePaused, null, null); // Reset the pause state flag _lastStatePaused = false; } // Fire the 'we are still monitoring this item event' (possibly should be inside an else, although won't hurt // where it is) _onTrackMonitoring?.BeginInvoke(_currentMediaItem, (int)playerPosition, null, null); // Update the current media tracking time _currentMediaTrackingTime += _timerInterval; } // The media player is not playing else if (!isPlaying) { // If we had been playing, invoke the Track Ended callback if (_currentMediaTrackingTime > _timerInterval && !_lastStatePaused) { // Fire the 'we've stopped tracking this item' event _onTrackMonitoringEnded?.BeginInvoke(mediaDetail, null, null); } // Set the persisted pause state _lastStatePaused = isPaused; // If we're not paused (FF, Rewind) if (!isPaused) { // Reset the state tracking how long we played this track for _lastStatePaused = false; _currentMediaTrackingTime = 0; _currentMediaWasScrobbled = false; } } // Release the iTunes COM library, so that the user _might_ be able to exit iTunes without getting a warning if (iTunesApp != null) { Marshal.ReleaseComObject(iTunesApp); iTunesApp = null; } System.GC.Collect(); } #if DebugiTunes Console.WriteLine("iTunes Plugin checking media state complete."); #endif } catch (COMException cEx) { // Ignore the COM exception, the library is either trying to communicate with a property // that isn't available. IE PlayerPosition is not available when the player is stopped. // It might also be tearing down } catch (Exception) { // Some other exception occured, at some point consider logging it...? } } else if (iTunesProcesses.Length == 0 && _currentMediaItem != null) { _onTrackMonitoringEnded?.BeginInvoke(_currentMediaItem, null, null); _currentMediaItem = null; _currentMediaWasScrobbled = false; Console.WriteLine("iTunes process not detected. Waiting for iTunes process to start..."); } _scrobbleTimer?.Start(); }; } catch (Exception ex) { try { // If an unexpected error occured, don't let that stop us from performing any tracking, // as the error might only be temporary _scrobbleTimer?.Start(); } catch (Exception exception) { // Can occur if you close the application as it's starting up } } }