public async Task <OperationResult <PlayActivityList> > CreatePlayActivity(User roadieUser, TrackStreamInfo streamInfo) { try { var sw = Stopwatch.StartNew(); var track = this.DbContext.Tracks .Include(x => x.ReleaseMedia) .Include(x => x.ReleaseMedia.Release) .Include(x => x.ReleaseMedia.Release.Artist) .Include(x => x.TrackArtist) .FirstOrDefault(x => x.RoadieId == SafeParser.ToGuid(streamInfo.Track.Value)); if (track == null) { return(new OperationResult <PlayActivityList>($"CreatePlayActivity: Unable To Find Track [{ streamInfo.Track.Value }]")); } if (!track.IsValid) { return(new OperationResult <PlayActivityList>($"CreatePlayActivity: Invalid Track. Track Id [{streamInfo.Track.Value}], FilePath [{track.FilePath}], Filename [{track.FileName}]")); } data.UserTrack userTrack = null; var now = DateTime.UtcNow; try { var user = roadieUser != null?this.DbContext.Users.FirstOrDefault(x => x.RoadieId == roadieUser.UserId) : null; if (user != null) { userTrack = this.DbContext.UserTracks.FirstOrDefault(x => x.UserId == user.Id && x.TrackId == track.Id); if (userTrack == null) { userTrack = new data.UserTrack(now) { UserId = user.Id, TrackId = track.Id }; this.DbContext.UserTracks.Add(userTrack); } userTrack.LastPlayed = now; userTrack.PlayedCount = (userTrack.PlayedCount ?? 0) + 1; this.CacheManager.ClearRegion(user.CacheRegion); await this.DbContext.SaveChangesAsync(); } } catch (Exception ex) { this.Logger.LogError(ex, $"Error in CreatePlayActivity, Creating UserTrack: User `{ roadieUser}` TrackId [{ track.Id }"); } track.PlayedCount = (track.PlayedCount ?? 0) + 1; track.LastPlayed = now; var release = this.DbContext.Releases.Include(x => x.Artist).FirstOrDefault(x => x.RoadieId == track.ReleaseMedia.Release.RoadieId); release.LastPlayed = now; release.PlayedCount = (release.PlayedCount ?? 0) + 1; var artist = this.DbContext.Artists.FirstOrDefault(x => x.RoadieId == release.Artist.RoadieId); artist.LastPlayed = now; artist.PlayedCount = (artist.PlayedCount ?? 0) + 1; data.Artist trackArtist = null; if (track.ArtistId.HasValue) { trackArtist = this.DbContext.Artists.FirstOrDefault(x => x.Id == track.ArtistId); trackArtist.LastPlayed = now; trackArtist.PlayedCount = (trackArtist.PlayedCount ?? 0) + 1; this.CacheManager.ClearRegion(trackArtist.CacheRegion); } this.CacheManager.ClearRegion(track.CacheRegion); this.CacheManager.ClearRegion(track.ReleaseMedia.Release.CacheRegion); this.CacheManager.ClearRegion(track.ReleaseMedia.Release.Artist.CacheRegion); var pl = new PlayActivityList { Artist = new DataToken { Text = track.ReleaseMedia.Release.Artist.Name, Value = track.ReleaseMedia.Release.Artist.RoadieId.ToString() }, TrackArtist = track.TrackArtist == null ? null : new DataToken { Text = track.TrackArtist.Name, Value = track.TrackArtist.RoadieId.ToString() }, Release = new DataToken { Text = track.ReleaseMedia.Release.Title, Value = track.ReleaseMedia.Release.RoadieId.ToString() }, Track = new DataToken { Text = track.Title, Value = track.RoadieId.ToString() }, User = new DataToken { Text = roadieUser.UserName, Value = roadieUser.UserId.ToString() }, PlayedDateDateTime = userTrack?.LastPlayed, ReleasePlayUrl = $"{ this.HttpContext.BaseUrl }/play/release/{ track.ReleaseMedia.Release.RoadieId}", Rating = track.Rating, UserRating = userTrack?.Rating, TrackPlayUrl = $"{ this.HttpContext.BaseUrl }/play/track/{ track.RoadieId}.mp3", ArtistThumbnail = this.MakeArtistThumbnailImage(track.TrackArtist != null ? track.TrackArtist.RoadieId : track.ReleaseMedia.Release.Artist.RoadieId), ReleaseThumbnail = this.MakeReleaseThumbnailImage(track.ReleaseMedia.Release.RoadieId), UserThumbnail = this.MakeUserThumbnailImage(roadieUser.UserId) }; if (!roadieUser.IsPrivate) { try { await this.PlayActivityHub.Clients.All.SendAsync("SendActivity", pl); } catch (Exception ex) { this.Logger.LogError(ex); } } await this.DbContext.SaveChangesAsync(); sw.Stop(); return(new OperationResult <PlayActivityList> { Data = pl, IsSuccess = userTrack != null, OperationTime = sw.ElapsedMilliseconds }); } catch (Exception ex) { this.Logger.LogError(ex, $"CreatePlayActivity RoadieUser `{ roadieUser }` StreamInfo `{ streamInfo }`"); } return(new OperationResult <PlayActivityList>()); }
public async Task <OperationResult <bool> > ScanCollection(ApplicationUser user, Guid collectionId, bool isReadOnly = false, bool doPurgeFirst = false, bool doUpdateRanks = true) { var sw = new Stopwatch(); sw.Start(); var releaseIdsInCollection = new List <int>(); var updatedReleaseIds = new List <int>(); var result = new List <data.PositionArtistRelease>(); var errors = new List <Exception>(); var collection = DbContext.Collections.FirstOrDefault(x => x.RoadieId == collectionId); if (collection == null) { await LogAndPublish($"ScanCollection Unknown Collection [{collectionId}]", LogLevel.Warning); return(new OperationResult <bool>(true, $"Collection Not Found [{collectionId}]")); } try { if (doPurgeFirst) { await LogAndPublish($"ScanCollection Purgeing Collection [{collectionId}]", LogLevel.Warning); var crs = DbContext.CollectionReleases.Where(x => x.CollectionId == collection.Id).ToArray(); DbContext.CollectionReleases.RemoveRange(crs); await DbContext.SaveChangesAsync(); } var collectionMissingRecords = DbContext.CollectionMissings.Where(x => x.CollectionId == collection.Id); DbContext.CollectionMissings.RemoveRange(collectionMissingRecords); await DbContext.SaveChangesAsync(); var par = collection.PositionArtistReleases(); if (par != null) { var now = DateTime.UtcNow; foreach (var csvRelease in par) { data.Artist artist = null; data.Release release = null; var searchName = csvRelease.Artist.NormalizeName(); var specialSearchName = csvRelease.Artist.ToAlphanumericName(); var artistResults = (from a in DbContext.Artists where a.Name.Contains(searchName) || a.SortName.Contains(searchName) || a.AlternateNames.Contains(searchName) || a.AlternateNames.Contains(specialSearchName) select a).ToArray(); if (!artistResults.Any()) { await LogAndPublish( $"Unable To Find Artist [{csvRelease.Artist}], SearchName [{searchName}]", LogLevel.Warning); csvRelease.Status = Statuses.Missing; DbContext.CollectionMissings.Add(new data.CollectionMissing { CollectionId = collection.Id, Position = csvRelease.Position, Artist = searchName, Release = csvRelease.Release.NormalizeName() }); continue; } foreach (var artistResult in artistResults) { artist = artistResult; searchName = csvRelease.Release.NormalizeName().ToLower(); specialSearchName = csvRelease.Release.ToAlphanumericName(); release = (from r in DbContext.Releases where r.ArtistId == artist.Id where r.Title.Contains(searchName) || r.AlternateNames.Contains(searchName) || r.AlternateNames.Contains(specialSearchName) select r ).FirstOrDefault(); if (release != null) { break; } } if (release == null) { await LogAndPublish( $"Unable To Find Release [{csvRelease.Release}] for Artist [{csvRelease.Artist}], SearchName [{searchName}]", LogLevel.Warning); csvRelease.Status = Statuses.Missing; DbContext.CollectionMissings.Add(new data.CollectionMissing { CollectionId = collection.Id, IsArtistFound = true, Position = csvRelease.Position, Artist = csvRelease.Artist, Release = searchName }); continue; } var isInCollection = DbContext.CollectionReleases.FirstOrDefault(x => x.CollectionId == collection.Id && x.ListNumber == csvRelease.Position && x.ReleaseId == release.Id); var updated = false; // Found in Database but not in collection add to Collection if (isInCollection == null) { DbContext.CollectionReleases.Add(new data.CollectionRelease { CollectionId = collection.Id, ReleaseId = release.Id, ListNumber = csvRelease.Position }); updated = true; } // If Item in Collection is at different List number update CollectionRelease else if (isInCollection.ListNumber != csvRelease.Position) { isInCollection.LastUpdated = now; isInCollection.ListNumber = csvRelease.Position; updated = true; } if (updated && !updatedReleaseIds.Any(x => x == release.Id)) { updatedReleaseIds.Add(release.Id); } releaseIdsInCollection.Add(release.Id); } collection.LastUpdated = now; await DbContext.SaveChangesAsync(); var dto = new CollectionList { CollectionCount = collection.CollectionCount, CollectionFoundCount = (from cr in DbContext.CollectionReleases where cr.CollectionId == collection.Id select cr.CollectionId).Count() }; if (dto.PercentComplete == 100) { // Lock so future implicit scans dont happen, with DB RI when releases are deleted they are removed from collection collection.IsLocked = true; collection.Status = Statuses.Complete; } else { collection.Status = Statuses.Incomplete; } var collectionReleasesToRemove = (from cr in DbContext.CollectionReleases where cr.CollectionId == collection.Id where !releaseIdsInCollection.Contains(cr.ReleaseId) select cr).ToArray(); if (collectionReleasesToRemove.Any()) { await LogAndPublish( $"Removing [{collectionReleasesToRemove.Count()}] Stale Release Records from Collection.", LogLevel.Information); DbContext.CollectionReleases.RemoveRange(collectionReleasesToRemove); } await DbContext.SaveChangesAsync(); if (doUpdateRanks) { foreach (var updatedReleaseId in updatedReleaseIds) { await UpdateReleaseRank(updatedReleaseId); } } CacheManager.ClearRegion(collection.CacheRegion); } } catch (Exception ex) { Logger.LogError(ex); errors.Add(ex); } sw.Stop(); Logger.LogInformation(string.Format("RescanCollection `{0}`, By User `{1}`, ElapsedTime [{2}]", collection, user, sw.ElapsedMilliseconds)); return(new OperationResult <bool> { AdditionalData = new Dictionary <string, object> { { "updatedReleaseIds", updatedReleaseIds.ToArray() } }, IsSuccess = !errors.Any(), Data = true, OperationTime = sw.ElapsedMilliseconds, Errors = errors }); }
/// <summary> /// The user has played a track. /// </summary> public override async Task <OperationResult <bool> > Scrobble(User roadieUser, ScrobbleInfo scrobble) { try { // If less than half of duration then do nothing if (scrobble.ElapsedTimeOfTrackPlayed.TotalSeconds < (scrobble.TrackDuration.TotalSeconds / 2)) { return(new OperationResult <bool> { Data = true, IsSuccess = true }); } var sw = Stopwatch.StartNew(); var track = this.DbContext.Tracks .Include(x => x.ReleaseMedia) .Include(x => x.ReleaseMedia.Release) .Include(x => x.ReleaseMedia.Release.Artist) .Include(x => x.TrackArtist) .FirstOrDefault(x => x.RoadieId == scrobble.TrackId); if (track == null) { return(new OperationResult <bool>($"Scrobble: Unable To Find Track [{ scrobble.TrackId }]")); } if (!track.IsValid) { return(new OperationResult <bool>($"Scrobble: Invalid Track. Track Id [{scrobble.TrackId}], FilePath [{track.FilePath}], Filename [{track.FileName}]")); } data.UserTrack userTrack = null; var now = DateTime.UtcNow; var success = false; try { var user = this.DbContext.Users.FirstOrDefault(x => x.RoadieId == roadieUser.UserId); userTrack = this.DbContext.UserTracks.FirstOrDefault(x => x.UserId == roadieUser.Id && x.TrackId == track.Id); if (userTrack == null) { userTrack = new data.UserTrack(now) { UserId = user.Id, TrackId = track.Id }; this.DbContext.UserTracks.Add(userTrack); } userTrack.LastPlayed = now; userTrack.PlayedCount = (userTrack.PlayedCount ?? 0) + 1; track.PlayedCount = (track.PlayedCount ?? 0) + 1; track.LastPlayed = now; var release = this.DbContext.Releases.Include(x => x.Artist).FirstOrDefault(x => x.RoadieId == track.ReleaseMedia.Release.RoadieId); release.LastPlayed = now; release.PlayedCount = (release.PlayedCount ?? 0) + 1; var artist = this.DbContext.Artists.FirstOrDefault(x => x.RoadieId == release.Artist.RoadieId); artist.LastPlayed = now; artist.PlayedCount = (artist.PlayedCount ?? 0) + 1; data.Artist trackArtist = null; if (track.ArtistId.HasValue) { trackArtist = this.DbContext.Artists.FirstOrDefault(x => x.Id == track.ArtistId); trackArtist.LastPlayed = now; trackArtist.PlayedCount = (trackArtist.PlayedCount ?? 0) + 1; this.CacheManager.ClearRegion(trackArtist.CacheRegion); } await this.DbContext.SaveChangesAsync(); this.CacheManager.ClearRegion(track.CacheRegion); this.CacheManager.ClearRegion(track.ReleaseMedia.Release.CacheRegion); this.CacheManager.ClearRegion(track.ReleaseMedia.Release.Artist.CacheRegion); this.CacheManager.ClearRegion(user.CacheRegion); success = true; } catch (Exception ex) { this.Logger.LogError(ex, $"Error in Scrobble, Creating UserTrack: User `{ roadieUser}` TrackId [{ track.Id }"); } sw.Stop(); this.Logger.LogInformation($"RoadieScrobbler: RoadieUser `{ roadieUser }` Scrobble `{ scrobble }`"); return(new OperationResult <bool> { Data = success, IsSuccess = userTrack != null, OperationTime = sw.ElapsedMilliseconds }); } catch (Exception ex) { this.Logger.LogError(ex, $"Scrobble RoadieUser `{ roadieUser }` Scrobble `{ scrobble }`"); } return(new OperationResult <bool>()); }