public async Task <AffectedViewersDto> Handle(DeletePlaylistCommand request, CancellationToken cancellationToken)
        {
            try
            {
                using var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);

                var room = await _roomRepository.GetAsync(request.RoomId, cancellationToken)
                           ?? throw new ArgumentException("Room could not be found");

                var playlist = await _playlistRepository.GetAsync(room.ActivePlaylistId.Value, cancellationToken)
                               ?? throw new ArgumentException("Playlist could not be found");

                var viewer = await _viewerRepository.GetAsync(request.MemberId, cancellationToken)
                             ?? throw new ArgumentException("Viewer could not be found");

                if (!playlist.Owner.Equals(viewer))
                {
                    throw new InformativeException("Playlist does not belong to you");
                }

                await _playlistRepository.DeleteAsync(playlist.Id, cancellationToken);

                var remainingPlaylists = await _playlistRepository.GetAllByViewerAsync(request.MemberId, cancellationToken);

                if (remainingPlaylists == null || remainingPlaylists.Count == 0)
                {
                    var newPlaylistId = _identifierProvider.GenerateGuid();
                    var newPlaylist   = new Playlist(newPlaylistId, "Temporary", viewer);

                    await _playlistRepository.AddAsync(newPlaylist, cancellationToken);

                    room.UpdateSelectedPlaylist(newPlaylist);
                }
                else
                {
                    room.UpdateSelectedPlaylist(remainingPlaylists.First());
                }

                await _roomRepository.UpdateAsync(room, cancellationToken);

                transaction.Complete();

                var affectedViewers = room.Viewers.ToList();
                affectedViewers.Add(room.Host);

                return(_mapper.Map <AffectedViewersDto>(affectedViewers));
            }
            catch (InformativeException exception)
            {
                _logger.LogError(exception, $"Could not delete playlist {request.RoomId.ToString()} by member " +
                                 $"{request.MemberId.ToString()}");
                throw;
            }
            catch (Exception exception)
            {
                _logger.LogError(exception, $"Could not delete playlist {request.RoomId.ToString()} by member " +
                                 $"{request.MemberId.ToString()}");
                throw new InformativeException("Could not delete playlist. Please retry");
            }
        }
Exemple #2
0
        public async Task <PlaylistDto> Handle(GetPlaylistQuery request, CancellationToken cancellationToken)
        {
            try
            {
                var room = await _roomRepository.GetAsync(request.RoomId, cancellationToken)
                           ?? throw new InformativeException("Room could not be found");

                if (!room.IsPlaylistSelected)
                {
                    throw new InformativeException("There is no playlist in the room");
                }

                var playlist = await _playlistRepository.GetAsync(room.ActivePlaylistId.Value, cancellationToken);

                var availablePlaylists = await _playlistRepository.GetAllByViewerAsync(
                    room.Host.Profile.Id, cancellationToken);

                var dto = _mapper.Map <PlaylistDto>(playlist);
                dto.AvailablePlaylists = _mapper.Map <IList <SelectablePlaylistDto> >(availablePlaylists);
                return(dto);
            }
            catch (Exception exception)
            {
                _logger.LogError(exception, $"Could not get playlist {request.RoomId}");
                throw new InformativeException("Could not get playlist. Please retry");
            }
        }
Exemple #3
0
        public async Task <AffectedViewersDto> Handle(ChangeActivePlaylistCommand request, CancellationToken cancellationToken)
        {
            try
            {
                using var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);

                var room = await _roomRepository.GetAsync(request.RoomId, cancellationToken)
                           ?? throw new ArgumentException("Room could not be found");

                var newPlaylist = await _playlistRepository.GetAsync(request.PlaylistId, cancellationToken)
                                  ?? throw new ArgumentException("New playlist could not be found");

                room.UpdateSelectedPlaylist(newPlaylist);

                if (room.IsPlaylistSelected)
                {
                    var previousPlaylist = await _playlistRepository.GetAsync(room.ActivePlaylistId.Value, cancellationToken)
                                           ?? throw new InvalidOperationException(
                                                     "Previous active playlist could not be found");

                    if (previousPlaylist.IsTemporary)
                    {
                        await _playlistRepository.DeleteAsync(previousPlaylist.Id, cancellationToken);
                    }
                }

                await _roomRepository.UpdateAsync(room, cancellationToken);

                transaction.Complete();

                var affectedViewers = room.Viewers.ToList();
                affectedViewers.Add(room.Host);

                return(_mapper.Map <AffectedViewersDto>(affectedViewers));
            }
            catch (InformativeException exception)
            {
                _logger.LogError(exception, $"Could not change active playlist in room {request.RoomId.ToString()}");
                throw;
            }
            catch (Exception exception)
            {
                _logger.LogError(exception, $"Could not change active playlist in room {request.RoomId.ToString()}");
                throw new InformativeException("Could not change active playlist in room. Please retry");
            }
        }
        public async Task <Unit> Handle(SavePlaylistCommand request, CancellationToken cancellationToken)
        {
            try
            {
                using var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);

                var room = await _roomRepository.GetAsync(request.RoomId, cancellationToken)
                           ?? throw new ArgumentException("Room could not be found");

                var viewer = await _viewerRepository.GetAsync(request.ViewerId, cancellationToken)
                             ?? throw new ArgumentException("Viewer could not be found");

                if (!room.Viewers.Contains(viewer))
                {
                    throw new InvalidOperationException("Viewer does not belong to this room");
                }

                var activePlaylist = await _playlistRepository.GetAsync(room.ActivePlaylistId.Value, cancellationToken)
                                     ?? throw new ArgumentException("Playlist could not be found");

                if (activePlaylist.Owner == viewer)
                {
                    if (!activePlaylist.IsTemporary)
                    {
                        throw new InvalidOperationException("Playlist is added already");
                    }

                    activePlaylist.IsTemporary = false;
                }
                else
                {
                    Guid newPlaylistId = _identifierProvider.GenerateGuid();
                    activePlaylist = CreatePlaylistCopy(newPlaylistId, viewer, activePlaylist);
                }

                await _playlistRepository.AddAsync(activePlaylist, cancellationToken);

                transaction.Complete();

                return(Unit.Value);
            }
            catch (InformativeException exception)
            {
                _logger.LogError(exception, $"Could not add playlist {request.RoomId.ToString()} " +
                                 $"to host of room {request.RoomId.ToString()}");
                throw;
            }
            catch (Exception exception)
            {
                _logger.LogError(exception, $"Could not add playlist {request.RoomId.ToString()} " +
                                 $"to host of room {request.RoomId.ToString()}");
                throw new InformativeException("Could not add playlist. Please retry");
            }
        }
Exemple #5
0
        public async Task <IList <Playlist> > GetPlaylistsAsync()
        {
            using (_busyStack.GetToken())
            {
                var items = await _playlistRepository.GetAsync()
                            .ConfigureAwait(true);

                return(items.Select(p => _playlistMapper.Get(p))
                       .ToList());
            }
        }
        public async Task <AffectedViewersDto> Handle(SwitchContentCommand request, CancellationToken cancellationToken)
        {
            try
            {
                using var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);

                var room = await _roomRepository.GetAsync(request.RoomId, cancellationToken)
                           ?? throw new InvalidOperationException($"Room {request.RoomId.ToString()} does not exist");

                var playlist = await _playlistRepository.GetAsync(room.ActivePlaylistId.Value, cancellationToken)
                               ?? throw new InvalidOperationException($"Playlist {room.ActivePlaylistId.ToString()} " +
                                                                      $"does not exist");

                ContentId contentId;
                if (playlist.Contains(room.CurrentContent?.ContentId))
                {
                    contentId = request.Direction == SwitchContentDirection.Next
                        ? playlist.GetNextContent(room.CurrentContent?.ContentId)
                        : playlist.GetPreviousContent(room.CurrentContent?.ContentId);
                }
                else
                {
                    contentId = playlist.FirstOrDefault()?.ContentId;
                }

                room.CurrentContent = new CurrentContent(
                    contentId,
                    ContentPlayerState.Paused,
                    playingTime: 0,
                    lastUpdatedPlayingTime: DateTime.Now);

                await _roomRepository.UpdateAsync(room, cancellationToken);

                transaction.Complete();

                var affectedViewers = room.Viewers.ToList();
                affectedViewers.Add(room.Host);

                return(_mapper.Map <AffectedViewersDto>(affectedViewers));
            }
            catch (InformativeException exception)
            {
                _logger.LogError(exception, $"Could not play next content from active playlist of " +
                                 $"room {request.RoomId.ToString()}");
                throw;
            }
            catch (Exception exception)
            {
                _logger.LogError(exception, $"Could not play next content from active playlist of " +
                                 $"room {request.RoomId.ToString()}");
                throw new InformativeException("Could not play next content from the playlist. Please retry");
            }
        }
Exemple #7
0
        public async Task AddAsync(string url, string author, string title, Guid playlistId)
        {
            var playlist = await _playlistRepository.GetAsync(playlistId);

            if (playlist == null)
            {
                throw new Exception($"Playlist with id: '{playlistId}' does not exists");
            }

            var song = new Song(url, author, title, playlistId);
            await _songRepository.AddAsync(song);
        }
        public async Task <AffectedViewersDto> Handle(RemoveContentFromPlaylistCommand request, CancellationToken cancellationToken)
        {
            try
            {
                using var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);

                var room = await _roomRepository.GetAsync(request.CurrentRoomId, cancellationToken)
                           ?? throw new ArgumentException(
                                     $"Room {request.CurrentRoomId.ToString()} could not be found");

                var activePlaylist = await _playlistRepository.GetAsync(room.ActivePlaylistId.Value, cancellationToken)
                                     ?? throw new InvalidOperationException(
                                               $"Playlist {room.ActivePlaylistId.ToString()} " +
                                               $"could not be found");

                var contentId = _mapper.Map <ContentId>(request.ContentId);

                if (contentId.Equals(room.CurrentContent?.ContentId))
                {
                    throw new InformativeException("Could not remove currently playing content.");
                }

                activePlaylist.Remove(contentId);
                await _playlistRepository.UpdateAsync(activePlaylist, cancellationToken);

                transaction.Complete();

                var affectedUsers = room.Viewers.ToList();
                affectedUsers.Add(room.Host);

                return(_mapper.Map <AffectedViewersDto>(affectedUsers));
            }
            catch (InformativeException exception)
            {
                _logger.LogError(exception, $"Could not remove content {request.ContentId} from active playlist of " +
                                 $"room {request.CurrentRoomId.ToString()}");
                throw;
            }
            catch (Exception exception)
            {
                _logger.LogError(exception, $"Could not remove content {request.ContentId} from active playlist of " +
                                 $"room {request.CurrentRoomId.ToString()}");
                throw new InformativeException("Could not remove content from the playlist. Please retry");
            }
        }
        public async Task <Unit> Handle(CloseRoomCommand request, CancellationToken cancellationToken)
        {
            try
            {
                using var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);

                var room = await _roomRepository.GetAsync(request.RoomId, cancellationToken)
                           ?? throw new ArgumentException("Room could not be found");

                if (room.Host.Profile.Id != request.MemberId)
                {
                    throw new InformativeException("Member does not have permission");
                }

                if (room.IsPlaylistSelected)
                {
                    var activePlaylist = await _playlistRepository.GetAsync(room.ActivePlaylistId.Value, cancellationToken)
                                         ?? throw new ArgumentException("Active playlist could not be found");

                    if (activePlaylist.IsTemporary)
                    {
                        await _playlistRepository.DeleteAsync(activePlaylist.Id, cancellationToken);
                    }
                }

                await _roomRepository.DeleteAsync(room.Id, cancellationToken);

                transaction.Complete();

                return(Unit.Value);
            }
            catch (InformativeException exception)
            {
                _logger.LogError(exception, $"Could not close room {request.RoomId.ToString()}");
                throw;
            }
            catch (Exception exception)
            {
                _logger.LogError(exception, $"Could not close room {request.RoomId.ToString()}");
                throw new InformativeException("Could not close room. Please retry");
            }
        }
Exemple #10
0
        // [MaximumConcurrentExecutions(1)]
        // [DisableConcurrentExecution(timeoutInSeconds: 60 * 60 * 2)]
        public async Task <bool> Execute(ParsedItemResult item, Guid playlistId, PerformContext context)
        {
            _setContext(context);
            if (item is null || string.IsNullOrEmpty(item.VideoType))
            {
                return(false);
            }

            Log($"Starting process item:\n\t{item.Id}\n\t{item.Title}\n\thttps://www.youtube.com/watch?v={item.Id}");

            var playlist = await _playlistRepository.GetAsync(playlistId);

            var url = item.VideoType.ToLower().Equals("youtube") ? $"https://www.youtube.com/watch?v={item.Id}" :
                      item.VideoType.Equals("mixcloud") ? $"https://mixcloud.com/{item.Id}" :
                      string.Empty;

            if (string.IsNullOrEmpty(url))
            {
                LogError($"Unknown video type for ParsedItem: {item.Id} - {playlist.Id}");
            }
            else
            {
                Log($"Getting info");
                var info = await _audioDownloader.GetInfo(url, playlist.Podcast.AppUserId);

                if (info != RemoteUrlType.Invalid)
                {
                    Log($"URL is valid");

                    var podcast = await _podcastRepository.GetAsync(playlist.PodcastId);

                    var uid = Guid.NewGuid();
                    Log($"Downloading audio");
                    var localFile = Path.Combine(Path.GetTempPath(), $"{System.Guid.NewGuid()}.mp3");
                    try {
                        var entry = new PodcastEntry {
                            SourceUrl        = url,
                            ProcessingStatus = ProcessingStatus.Uploading,
                            Playlist         = playlist,
                            Podcast          = podcast
                        };
                        await _processor.GetInformation(entry, podcast.AppUserId);

                        podcast.PodcastEntries.Add(entry);
                        await _unitOfWork.CompleteAsync();

                        var result = await _preProcessor.PreProcessEntry(podcast.AppUser, entry);

                        return(result == EntryProcessResult.Succeeded);
                    } catch (AudioDownloadException e) {
                        //TODO: we should mark this as failed
                        //so we don't continuously process it
                        LogError(e.Message);
                    }
                }
                else
                {
                    LogError($"Processing playlist item {item.Id} failed");
                    return(false);
                }
            }

            return(true);
        }
        public async Task <bool> Execute(Guid playlistId, PerformContext context)
        {
            Log($"Starting playlist processing for {playlistId}");
            context.WriteLine($"Starting playlist processing for {playlistId}");
            try {
                var playlist = await _playlistRepository.GetAsync(playlistId);

                var cutoffDate = await _playlistRepository.GetCutoffDate(playlistId);

                var user = playlist.Podcast.AppUser;

                //first check user has a valid subscription
                var subs  = user.GetCurrentSubscription();
                var isGod = await _userManager.IsInRoleAsync(user, "god-mode");

                if (subs is null && !isGod)
                {
                    LogWarning($"User: {user.Id} does not have a valid subscription");
                    return(false);
                }

                //next check quotas
                Log("Checking quotas");
                var quota     = user.DiskQuota ?? _storageSettings.DefaultUserQuota;
                var totalUsed = (await _entryRepository.GetAllForUserAsync(user.Id))
                                .Select(x => x.AudioFileSize)
                                .Sum();

                if (totalUsed >= quota)
                {
                    LogError($"Storage quota exceeded for {user.GetBestGuessName()}");
                    BackgroundJob.Enqueue <INotifyJobCompleteService>(
                        service => service.NotifyUser(
                            user.Id.ToString(),
                            $"Failure processing playlist\n{playlist.Podcast.Title}\n",
                            $"Your have exceeded your storage quota of {quota.Bytes().ToString()}",
                            playlist.Podcast.GetAuthenticatedUrl(_appSettings.SiteUrl),
                            playlist.Podcast.GetThumbnailUrl(_storageSettings.CdnUrl,
                                                             _imageFileStorageSettings.ContainerName),
                            NotificationOptions.StorageExceeded
                            ));
                    return(false);
                }

                Log("Quotas passed");
                //check for active subscription
                var resultList = new List <ParsedItemResult>();
                var count      = user.PlaylistAllowedEntryCount ?? _storageSettings.DefaultEntryCount;

                if (_youTubeParser.ValidateUrl(playlist.SourceUrl))
                {
                    Log("Parsing YouTube");
                    var url = playlist.SourceUrl;
                    resultList = await _youTubeParser
                                 .GetEntries(
                        url,
                        user.Id,
                        cutoffDate,
                        count);
                }
                else if (MixcloudParser.ValidateUrl(playlist.SourceUrl))
                {
                    Log("Parsing MixCloud");
                    var entries = await _mixcloudParser
                                  .GetEntries(playlist.SourceUrl, count);

                    resultList = entries
                                 .OrderBy(r => r.UploadDate)
                                 .Take(_storageSettings.DefaultEntryCount)
                                 .ToList();
                }

                Log($"Found {resultList.Count} candidates");

                //order in reverse so the newest item is added first
                foreach (var item in resultList.Where(item =>
                                                      playlist.PodcastEntries.All(e => e.SourceItemId != item.Id)))
                {
                    await _trimPlaylist(playlist, count);

                    Log($"Found candidate\n\tParsedId:{item.Id}\n\tPodcastId:{playlist.Podcast.Id}\n\t{playlist.Id}");
                    BackgroundJob
                    .Enqueue <ProcessPlaylistItemJob>(
                        service => service.Execute(item, playlist.Id, null)
                        );
                }

                Log($"Finished playlists");
                return(true);
            } catch (PlaylistExpiredException) {
                //TODO: Remove playlist and notify user
                LogError($"Playlist: {playlistId} cannot be found");
            } catch (Exception ex) {
                LogError(ex.Message);
                context.WriteLine($"ERROR(ProcessPlayListJob): {ex.Message}");
            }

            return(false);
        }
        public async Task <PlaylistDto> GetAsync(Guid id)
        {
            var playlist = await _playlistRepository.GetAsync(id);

            return(_mapper.Map <Playlist, PlaylistDto>(playlist));
        }
Exemple #13
0
 public Task <Page <Playlist> > GetAsync(PlaylistQuery query,
                                         CancellationToken cancel = default)
 {
     return(_playlistRepository.GetAsync(query, cancel));
 }