From e6e54a09a4add18881bc40e8f6e52c09916795ff Mon Sep 17 00:00:00 2001 From: Lawrence Angrave <angrave@illinois.edu> Date: Sat, 6 Jan 2024 13:33:14 -0600 Subject: [PATCH] Fix NewPlayList and polish --- ClassTranscribeDatabase/Models/Models.cs | 10 +-- .../Controllers/AccountController.cs | 1 + .../Controllers/MediaController.cs | 8 +-- .../Controllers/PlaylistsController.cs | 67 +++++++++++++------ .../Controllers/TaskController.cs | 5 ++ ClassTranscribeServer/Program.cs | 2 +- TaskEngine/Tasks/DownloadMediaTask.cs | 4 +- .../PlaylistsControllerTest.cs | 30 +++++---- 8 files changed, 82 insertions(+), 45 deletions(-) diff --git a/ClassTranscribeDatabase/Models/Models.cs b/ClassTranscribeDatabase/Models/Models.cs index d75ab06..b71ea5b 100644 --- a/ClassTranscribeDatabase/Models/Models.cs +++ b/ClassTranscribeDatabase/Models/Models.cs @@ -245,12 +245,12 @@ public class Playlist : Entity public JObject JsonMetadata { get; set; } = new JObject(); // for serverside use public string Options { get; set; } = ""; - public virtual JObject getOptionsAsJson() { + public virtual JObject GetOptionsAsJson() { return String.IsNullOrEmpty(this.Options) ? new JObject() : JObject.Parse(this.Options); } - public virtual void setOptionsAsJson(JObject json) { - this.Options = json.ToString(); + public virtual void SetOptionsAsJson(JObject json) { + this.Options = json.ToString(Newtonsoft.Json.Formatting.None); } public int Index { get; set; } public Visibility Visibility { get; set; } @@ -283,11 +283,11 @@ public class Media : Entity public string Options { get; set; } = ""; - public virtual JObject getOptionsAsJson() { + public virtual JObject GetOptionsAsJson() { return String.IsNullOrEmpty(this.Options) ? new JObject() : JObject.Parse(this.Options); } - public virtual void setOptionsAsJson(JObject json) { + public virtual void SetOptionsAsJson(JObject json) { this.Options = json.ToString(Newtonsoft.Json.Formatting.None); } } diff --git a/ClassTranscribeServer/Controllers/AccountController.cs b/ClassTranscribeServer/Controllers/AccountController.cs index 273aeea..ce4112f 100644 --- a/ClassTranscribeServer/Controllers/AccountController.cs +++ b/ClassTranscribeServer/Controllers/AccountController.cs @@ -152,6 +152,7 @@ public class MediaWorkerLoginDTO [HttpPost] public async Task<ActionResult<LoggedInDTO>> MediaWorkerSignIn(MediaWorkerLoginDTO accessDTO) { + if (accessDTO == null) return BadRequest("Missing parameter"); try { string access = accessDTO.Access; diff --git a/ClassTranscribeServer/Controllers/MediaController.cs b/ClassTranscribeServer/Controllers/MediaController.cs index 6421560..e95ca06 100644 --- a/ClassTranscribeServer/Controllers/MediaController.cs +++ b/ClassTranscribeServer/Controllers/MediaController.cs @@ -60,7 +60,7 @@ public async Task<ActionResult<MediaDTO>> GetMedia(string id) var playlist = await _context.Playlists.FindAsync(media.PlaylistId); //unused var v = await _context.Videos.FindAsync(media.VideoId); var user = await _userUtils.GetUser(User); - var restrict = (bool?) playlist.getOptionsAsJson()[ "restrictRoomStream"] ?? false; + var restrict = (bool?) playlist.GetOptionsAsJson()[ "restrictRoomStream"] ?? false; var mediaDTO = new MediaDTO { Id = media.Id, @@ -71,7 +71,7 @@ public async Task<ActionResult<MediaDTO>> GetMedia(string id) SourceType = media.SourceType, Duration = media.Video.Duration, PublishStatus = media.PublishStatus, - Options = media.getOptionsAsJson(), + Options = media.GetOptionsAsJson(), Transcriptions = media.Video.Transcriptions .Select(t => new TranscriptionDTO { @@ -171,7 +171,7 @@ public async Task<IActionResult> PutMediaOption(string id, string option, string if(value == null) return BadRequest("value not specified"); Media media = await _context.Medias.FindAsync(id); - JObject theOptions = media.getOptionsAsJson(); + JObject theOptions = media.GetOptionsAsJson(); if (media == null) { return NotFound(); @@ -193,7 +193,7 @@ public async Task<IActionResult> PutMediaOption(string id, string option, string } else { return BadRequest("Invalid type");// should never happen } - media.setOptionsAsJson(theOptions); + media.SetOptionsAsJson(theOptions); await _context.SaveChangesAsync(); return NoContent(); } diff --git a/ClassTranscribeServer/Controllers/PlaylistsController.cs b/ClassTranscribeServer/Controllers/PlaylistsController.cs index 083ee78..d68e736 100644 --- a/ClassTranscribeServer/Controllers/PlaylistsController.cs +++ b/ClassTranscribeServer/Controllers/PlaylistsController.cs @@ -71,7 +71,7 @@ public async Task<ActionResult<PlaylistDTO>> GetPlaylistsByVideoId(string videoI PublishStatus = p.PublishStatus, ListCheckedAt = p.ListCheckedAt, ListUpdatedAt = p.ListUpdatedAt, - Options = p.getOptionsAsJson() + Options = p.GetOptionsAsJson() }).ToList().FirstOrDefault(); return playlist; } @@ -109,7 +109,7 @@ public async Task<ActionResult<IEnumerable<PlaylistDTO>>> GetPlaylists(string of PublishStatus = p.PublishStatus, ListCheckedAt = p.ListCheckedAt, ListUpdatedAt = p.ListUpdatedAt, - Options = p.getOptionsAsJson() + Options = p.GetOptionsAsJson() }).ToList(); return playlists; } @@ -139,7 +139,7 @@ public async Task<ActionResult<IEnumerable<PlaylistDTO>>> GetPlaylists2(string o var hideRoomVideos = new Dictionary<string,bool>(); foreach (var p in playLists) { - var restrict = (bool?) p.getOptionsAsJson()[ "restrictRoomStream"] ?? false; + var restrict = (bool?) p.GetOptionsAsJson()[ "restrictRoomStream"] ?? false; hideRoomVideos.Add(p.Id, restrict); }; var playlistDTOs = playLists.Select(p => new PlaylistDTO @@ -154,7 +154,7 @@ public async Task<ActionResult<IEnumerable<PlaylistDTO>>> GetPlaylists2(string o PublishStatus = p.PublishStatus, ListCheckedAt = p.ListCheckedAt, ListUpdatedAt = p.ListUpdatedAt, - Options = p.getOptionsAsJson(), + Options = p.GetOptionsAsJson(), Medias = p.Medias.Where(m => m.Video != null).Select(m => new MediaDTO { Id = m.Id, @@ -167,7 +167,7 @@ public async Task<ActionResult<IEnumerable<PlaylistDTO>>> GetPlaylists2(string o SourceType = m.SourceType, Duration = m.Video?.Duration, PublishStatus = m.PublishStatus, - Options = m.getOptionsAsJson(), + Options = m.GetOptionsAsJson(), Video = new VideoDTO { Id = m.Video.Id, @@ -241,7 +241,7 @@ public async Task<ActionResult<PlaylistDTO>> GetPlaylist(string id) // In memory transformation into DTO resut var hideRoomVideos = new Dictionary<string,bool>(); - var restrict = (bool?) p.getOptionsAsJson()[ "restrictRoomStream"] ?? false; + var restrict = (bool?) p.GetOptionsAsJson()[ "restrictRoomStream"] ?? false; List<MediaDTO> mediasDTO = mediaList.Select(m => new MediaDTO { @@ -254,7 +254,7 @@ public async Task<ActionResult<PlaylistDTO>> GetPlaylist(string id) SourceType = m.SourceType, Duration = m.Video?.Duration, PublishStatus = m.PublishStatus, - Options = m.getOptionsAsJson(), + Options = m.GetOptionsAsJson(), SceneDetectReady = m.Video != null && m.Video.HasSceneObjectData(), Ready = m.Video != null && "NoError" == m.Video.TranscriptionStatus , Video = m.Video == null ? null : new VideoDTO @@ -288,7 +288,7 @@ public async Task<ActionResult<PlaylistDTO>> GetPlaylist(string id) PublishStatus = p.PublishStatus, ListUpdatedAt = p.ListUpdatedAt, ListCheckedAt = p.ListCheckedAt, - Options = p.getOptionsAsJson() + Options = p.GetOptionsAsJson() }; } @@ -315,7 +315,7 @@ public async Task<IActionResult> PutPlaylistOptions(string id, JObject options) return new ChallengeResult(); } - p.setOptionsAsJson(options); + p.SetOptionsAsJson(options); try { @@ -364,7 +364,7 @@ public async Task<IActionResult> PutPlaylist(string id, PlaylistUpdateDTO playli var p = await _context.Playlists.FindAsync(playlist.Id); p.Name = playlist.Name; - p.setOptionsAsJson(playlist.Options); + p.SetOptionsAsJson(playlist.Options); p.PublishStatus = playlist.PublishStatus; try @@ -390,18 +390,19 @@ public async Task<IActionResult> PutPlaylist(string id, PlaylistUpdateDTO playli // POST: api/Playlists [HttpPost] [Authorize] - public async Task<ActionResult<Playlist>> PostPlaylist(Playlist playlist) + public async Task<ActionResult<PlaylistDTO>> PostPlaylist(NewPlaylistDTO playlist) { + if (playlist == null || playlist.OfferingId == null) { - return BadRequest(); + return BadRequest("Playlist missing or OfferingId missing"); } var offering = await _context.Offerings.FindAsync(playlist.OfferingId); if (offering == null) { - return BadRequest(); + return BadRequest("No such offering"); } var authorizationResult = await _authorizationService.AuthorizeAsync(this.User, offering, Globals.POLICY_UPDATE_OFFERING); @@ -410,28 +411,28 @@ public async Task<ActionResult<Playlist>> PostPlaylist(Playlist playlist) { if (User.Identity.IsAuthenticated) { - return new ForbidResult(); + return new ForbidResult("Not authenticated"); } - return new ChallengeResult(); + return new ChallengeResult("Not authorized for this offering"); } - + if (playlist.PlaylistIdentifier != null && playlist.PlaylistIdentifier.Length > 0) { playlist.PlaylistIdentifier = playlist.PlaylistIdentifier.Trim(); } - + var p = playlist.ToPlaylist(); // If playlists are deleted the Count != Max Index, so use the max index (still not perfect, what if 2 playlists are created at the same time) if (offering.Playlists != null && offering.Playlists.Count > 0) { - playlist.Index = 1 + offering.Playlists.Max(p => p.Index); + p.Index = 1 + offering.Playlists.Max(pl => pl.Index); } - _context.Playlists.Add(playlist); + _context.Playlists.Add(p); await _context.SaveChangesAsync(); - _wakeDownloader.UpdatePlaylist(playlist.Id); + _wakeDownloader.UpdatePlaylist(p.Id); - return CreatedAtAction("GetPlaylist", new { id = playlist.Id }, playlist); + return await GetPlaylist(p.Id); } // DELETE: api/Playlists/5 @@ -538,6 +539,30 @@ public class PlaylistUpdateDTO { public JObject Options { get; set; } public PublishStatus PublishStatus { get; set; } } + + public class NewPlaylistDTO + { + public string OfferingId { get; set; } + public string Name { get; set; } + public SourceType SourceType { get; set; } + public string PlaylistIdentifier { get; set; } + public JObject JsonMetadata { get; set; } + public JObject Options { get; set; } + public PublishStatus PublishStatus { get; set; } + + public Playlist ToPlaylist() + { + return new Playlist + { + OfferingId = OfferingId, + Name = Name, + PublishStatus = PublishStatus, + SourceType = SourceType, + JsonMetadata = JsonMetadata, + Options = Options.ToString(Newtonsoft.Json.Formatting.None) + }; + } + } public class PlaylistDTO { public string Id { get; set; } diff --git a/ClassTranscribeServer/Controllers/TaskController.cs b/ClassTranscribeServer/Controllers/TaskController.cs index 0ab36ac..0e9b811 100644 --- a/ClassTranscribeServer/Controllers/TaskController.cs +++ b/ClassTranscribeServer/Controllers/TaskController.cs @@ -157,6 +157,8 @@ public class PhraseHintsDTO //Future: [Authorize(Roles = Globals.ROLE_MEDIA_WORKER + "," + Globals.ROLE_ADMIN)] public async Task<ActionResult> UpdatePhraseHints(string videoId, PhraseHintsDTO phraseHintsDTO) { + if (videoId == null || phraseHintsDTO == null) return BadRequest("Missing parameters"); + Video video = await _context.Videos.FindAsync(videoId); string hints = phraseHintsDTO.PhraseHints ?? ""; @@ -181,6 +183,8 @@ public async Task<ActionResult> UpdatePhraseHints(string videoId, PhraseHintsDTO //Future: [Authorize(Roles = Globals.ROLE_MEDIA_WORKER + "," + Globals.ROLE_ADMIN)] public async Task<ActionResult> UpdateGlossary(string videoId, JObject glossary) { + if (videoId == null || glossary == null) return BadRequest("Missing parameters"); + string glossaryAsString = glossary.ToString(0); Video video = await _context.Videos.FindAsync(videoId); if(video.HasGlossaryData()) @@ -216,6 +220,7 @@ public async Task<ActionResult<Object>> GetGlossary(string videoId) { //Future: [Authorize(Roles = Globals.ROLE_MEDIA_WORKER + "," + Globals.ROLE_ADMIN)] public async Task<ActionResult> UpdateGlossaryTimestamp(string videoId, JObject glossaryTimestamp) { + if (videoId == null || glossaryTimestamp == null) return BadRequest("Missing parameters"); string glossaryTimestampAsString = glossaryTimestamp.ToString(0); Video video = await _context.Videos.FindAsync(videoId); if(video.HasGlossaryTimestamp()) diff --git a/ClassTranscribeServer/Program.cs b/ClassTranscribeServer/Program.cs index ea087d0..e653519 100644 --- a/ClassTranscribeServer/Program.cs +++ b/ClassTranscribeServer/Program.cs @@ -23,7 +23,7 @@ public static IWebHostBuilder CreateWebHostBuilder(string[] args) string viewSQL = Environment.GetEnvironmentVariable("LogEntityFrameworkSQL") ?? "false"; - if( viewSQL.Trim().ToLower() != "true") { + if( viewSQL.Trim().ToUpperInvariant() != "TRUE") { v.ConfigureLogging((context, logging) => { logging.AddFilter("Microsoft.EntityFrameworkCore.Database.Command", LogLevel.Warning); diff --git a/TaskEngine/Tasks/DownloadMediaTask.cs b/TaskEngine/Tasks/DownloadMediaTask.cs index a5b698e..aef6f7c 100644 --- a/TaskEngine/Tasks/DownloadMediaTask.cs +++ b/TaskEngine/Tasks/DownloadMediaTask.cs @@ -146,9 +146,9 @@ protected override async Task OnConsume(string mediaId, TaskParameters taskParam public async Task<Video> DownloadKalturaVideo(string subdir, Media media) { - string? swapInfo = media.getOptionsAsJson()?.GetValue("swapStreams")?.ToString(); + string? swapInfo = media.GetOptionsAsJson()?.GetValue("swapStreams")?.ToString(); GetLogger().LogInformation($"DownloadKalturaVideo ({media.Id}): swap streams: {swapInfo}"); - bool swapStreams = Boolean.Parse( media.getOptionsAsJson().GetValue("swapStreams").ToString()); + bool swapStreams = Boolean.Parse( media.GetOptionsAsJson().GetValue("swapStreams").ToString()); string? video2Url = null; string video1Url = media.JsonMetadata["downloadUrl"].ToString(); try { diff --git a/UnitTests/ClassTranscribeServer/ControllerTests/PlaylistsControllerTest.cs b/UnitTests/ClassTranscribeServer/ControllerTests/PlaylistsControllerTest.cs index 3d0b3b5..4f5d02b 100644 --- a/UnitTests/ClassTranscribeServer/ControllerTests/PlaylistsControllerTest.cs +++ b/UnitTests/ClassTranscribeServer/ControllerTests/PlaylistsControllerTest.cs @@ -292,7 +292,7 @@ public async Task Put_Playlist_Success() var updatedPlaylist = _context.Playlists.Find(playlist.Id); Assert.Equal(update.Name, updatedPlaylist.Name); Assert.Equal(update.PublishStatus, updatedPlaylist.PublishStatus); - Assert.Equal(update.Options, updatedPlaylist.getOptionsAsJson()); + Assert.Equal(update.Options, updatedPlaylist.GetOptionsAsJson()); Assert.Equal(playlist.SourceType, updatedPlaylist.SourceType); } @@ -322,36 +322,42 @@ public async Task Put_Playlist_Fail() [Fact] public async Task Post_Playlist_Success() { - var playlist = new Playlist + var playlist = new NewPlaylistDTO { OfferingId = "offering", SourceType = SourceType.Local, Name = "foo", - Index = 0 + JsonMetadata = JObject.Parse("{'JM':'XY'}"), + Options =JObject.Parse("{'OptionA':'A'}") }; _context.Offerings.Add(new Offering { Id = playlist.OfferingId }); var result = await _controller.PostPlaylist(playlist); - Assert.IsType<CreatedAtActionResult>(result.Result); + // Assert.IsType<ActionResult>(result); - var newPlaylist = _context.Playlists.Find(playlist.Id); - Assert.Equal(playlist, newPlaylist); + var newPlaylist = _context.Playlists.Find(result.Value.Id); + Assert.Equal(playlist.Name, newPlaylist.Name); Assert.Equal(PublishStatus.Published, playlist.PublishStatus); - Assert.Equal(Visibility.Visible, playlist.Visibility); + Assert.Equal(Visibility.Visible, newPlaylist.Visibility); + Assert.Equal(SourceType.Local, newPlaylist.SourceType); + Assert.Equal(playlist.OfferingId, newPlaylist.OfferingId); + Assert.Contains("OptionA", newPlaylist.Options); + Assert.Contains("XY", newPlaylist.JsonMetadata.ToString()); + } [Fact] public async Task Post_Playlist_Fail() { var result = await _controller.PostPlaylist(null); - Assert.IsType<BadRequestResult>(result.Result); + Assert.IsType<BadRequestObjectResult>(result.Result); - result = await _controller.PostPlaylist(new Playlist()); - Assert.IsType<BadRequestResult>(result.Result); + result = await _controller.PostPlaylist(new NewPlaylistDTO()); + Assert.IsType<BadRequestObjectResult>(result.Result); - result = await _controller.PostPlaylist(new Playlist() { OfferingId = "none" }); - Assert.IsType<BadRequestResult>(result.Result); + result = await _controller.PostPlaylist(new NewPlaylistDTO() { OfferingId = "none" }); + Assert.IsType<BadRequestObjectResult>(result.Result); } [Fact]