@@ -76,6 +76,142 @@ public async Task<PagedList<Album>> GetAll(int skip, int take, string search)
7676 } ;
7777 }
7878
79+ /// <summary>
80+ /// Gets the list of album submissions.
81+ /// </summary>
82+ /// <returns></returns>
83+ [ HttpGet ]
84+ [ Authorize ( Roles = AppUser . AdminRole ) ]
85+ public async Task < List < AlbumSubmissionByArtist > > GetSubmissions ( )
86+ {
87+ return await DbSession . Query < AlbumSubmissionByArtist > ( )
88+ . Where ( s => s . Status == ApprovalStatus . Pending )
89+ . Take ( 100 )
90+ . ToListAsync ( ) ;
91+ }
92+
93+ /// <summary>
94+ /// Approves an album submission, converting it to a permanent album with songs live on Chavah.
95+ /// </summary>
96+ /// <param name="submission"></param>
97+ /// <returns>The ID of the new album.</returns>
98+ [ HttpPost ]
99+ [ Authorize ( Roles = AppUser . AdminRole ) ]
100+ public async Task < string > ApproveSubmission ( [ FromBody ] AlbumSubmissionByArtist submission )
101+ {
102+ // Put the album art on the CDN with the correct file name containing the album name, artist name, etc.
103+ // Then Delete the temporary album art file.
104+ var albumArtUriCdn = await cdnManagerService . UploadAlbumArtAsync ( submission . AlbumArt . Url , submission . Artist , submission . Name , System . IO . Path . GetExtension ( submission . AlbumArt . CdnId ) ) ;
105+ await cdnManagerService . DeleteTempFileAsync ( submission . AlbumArt . CdnId ) ;
106+
107+ // Store the new album
108+ var newAlbum = new Album
109+ {
110+ AlbumArtUri = albumArtUriCdn ,
111+ Artist = submission . Artist ,
112+ BackgroundColor = submission . BackColor ,
113+ ForegroundColor = submission . ForeColor ,
114+ IsVariousArtists = submission . Artist == "Various Artists" ,
115+ MutedColor = submission . MutedColor ,
116+ Name = submission . Name ,
117+ SongCount = submission . Songs . Count ,
118+ TextShadowColor = submission . TextShadowColor ,
119+ } ;
120+ await DbSession . StoreAsync ( newAlbum ) ;
121+
122+ // Store the Artist as well if we don't already have an artist for them.
123+ Artist ? existingArtist = null ;
124+ if ( submission . Artist != "Various Artists" )
125+ {
126+ // Grab the existing artist only if we're not the special case "Various Artists"
127+ existingArtist = await DbSession . Query < Artist > ( )
128+ . FirstOrDefaultAsync ( a => a . Name == submission . Artist ) ;
129+ }
130+
131+ // Create the artist if necessary.
132+ if ( existingArtist == null )
133+ {
134+ existingArtist = new Artist
135+ {
136+ Bio = "" ,
137+ Name = submission . Artist ,
138+ Contact = submission . ArtistEmail ,
139+ Disambiguation = submission . Artist == "Various Artists" ? submission . Name : null ,
140+ DonationUrl = string . IsNullOrWhiteSpace ( submission . ArtistPayPalEmail ) ? null : new Uri ( $ "paypal:?email={ Uri . EscapeDataString ( submission . ArtistPayPalEmail ) } ")
141+ } ;
142+ await DbSession . StoreAsync ( existingArtist ) ;
143+ }
144+
145+ // Store the songs in the DB.
146+ var songNumber = 1 ;
147+ var songsWithTempFiles = new Dictionary < Song , TempFile > ( submission . Songs . Count ) ;
148+ foreach ( var tempFile in submission . Songs )
149+ {
150+ var ( english , hebrew ) = tempFile . Name . GetEnglishAndHebrew ( ) ;
151+ var song = new Song
152+ {
153+ Album = submission . Name ,
154+ AlbumHebrewName = null ,
155+ Artist = submission . Artist ,
156+ AlbumArtUri = albumArtUriCdn ,
157+ CommunityRankStanding = CommunityRankStanding . Normal ,
158+ ContributingArtists = [ .. english . GetFeaturedArtistsFromSongName ( ) ] ,
159+ Genres = [ ] ,
160+ Name = english ,
161+ HebrewName = hebrew ,
162+ Number = songNumber ,
163+ PurchaseUri = submission . PurchaseUrl ,
164+ UploadDate = DateTime . UtcNow ,
165+ AlbumId = newAlbum . Id ,
166+ ArtistId = existingArtist . Id ,
167+ AlbumColors = new AlbumColors
168+ {
169+ Background = newAlbum . BackgroundColor ,
170+ Foreground = newAlbum . ForegroundColor ,
171+ Muted = newAlbum . MutedColor ,
172+ TextShadow = newAlbum . TextShadowColor
173+ } ,
174+ Uri = tempFile . Url
175+ } ;
176+ await DbSession . StoreAsync ( song ) ;
177+
178+ songsWithTempFiles . Add ( song , tempFile ) ;
179+ songNumber ++ ;
180+ }
181+
182+ // Finally, we can mark the submission as approved.
183+ submission . Status = ApprovalStatus . Approved ;
184+ await DbSession . SaveChangesAsync ( ) ;
185+
186+ // Now that the songs are saved in the database, migrate their URIs from temp file to
187+ // a final file name that includes song, artist, album, etc.ccting
188+ foreach ( var ( song , tempFile ) in songsWithTempFiles )
189+ {
190+ songUploadService . MoveSongUriFromTemporaryToFinal ( tempFile , submission , song . Number , song . Id ! ) ;
191+ }
192+
193+ return newAlbum . Id ! ;
194+ }
195+
196+ /// <summary>
197+ /// Rejects an album submission. The album won't be converted to a permanent album on the station. The song uploads and album art upload will be deleted.
198+ /// </summary>
199+ /// <param name="submission">The album submission to reject.</param>
200+ /// <returns></returns>
201+ [ HttpPost ]
202+ [ Authorize ( Roles = AppUser . AdminRole ) ]
203+ public async Task RejectSubmission ( [ FromBody ] AlbumSubmissionByArtist submission )
204+ {
205+ var existingSubmission = await DbSession . LoadAsync < AlbumSubmissionByArtist > ( submission . Id ) ;
206+ if ( existingSubmission == null )
207+ {
208+ throw new ArgumentException ( $ "Could not find album submission with ID { submission . Id } ") ;
209+ }
210+
211+ // Mark it as rejected. The submission and its temp files will be deleted at a later date during a background task; see AlbumSubmissionCleanup.cs
212+ existingSubmission . Status = ApprovalStatus . Rejected ;
213+ }
214+
79215 [ HttpGet ]
80216 public async Task < IActionResult > GetAlbumArtBySongId ( string songId )
81217 {
@@ -194,32 +330,22 @@ public async Task<TempFile> UploadTempFile([FromForm] IFormFile file)
194330 throw new InvalidOperationException ( $ "Cannot upload temp file because there have been too many temp files uploaded recently.") ;
195331 }
196332
197- // Clean up any old temp files.
198- var tempFilesReadyForDeletion = await DbSession . Query < TempFile > ( )
199- . Where ( t => t . CreatedAt < DateTimeOffset . UtcNow . AddDays ( 30 ) )
200- . ToListAsync ( ) ;
201- if ( tempFilesReadyForDeletion . Count > 0 )
202- {
203- foreach ( var tempFileToDelete in tempFilesReadyForDeletion )
204- {
205- await cdnManagerService . DeleteTempFileAsync ( tempFileToDelete . CdnId ) ;
206- DbSession . Delete ( tempFileToDelete ) ;
207- }
208- await DbSession . SaveChangesAsync ( ) ;
209- }
210-
211333 // OK, we're cool to upload it to our CDN.
212334 using var mediaFileStream = file . OpenReadStream ( ) ;
213335 var fileExtension = isMp3 ? ".mp3" : isPng ? ".png" : isWebp ? ".webp" : ".jpg" ;
214336 var fileName = Guid . NewGuid ( ) . ToString ( ) + fileExtension ;
215337 var tempFileUri = await cdnManagerService . UploadTempFileAsync ( mediaFileStream , fileName ) ;
216- return new TempFile
338+ var tempFile = new TempFile
217339 {
218340 Url = tempFileUri ,
219341 Name = fileName ,
220342 CdnId = fileName ,
221- Id = $ "TempFiles/{ fileName } /{ DateTime . UtcNow : O} "
343+ Id = $ "TempFiles/{ fileName } /{ DateTime . UtcNow : O} ",
344+ CreatedAt = DateTimeOffset . UtcNow
222345 } ;
346+
347+ await DbSession . StoreAsync ( tempFile ) ;
348+ return tempFile ;
223349 }
224350
225351 [ HttpPost ]
@@ -343,6 +469,8 @@ public async Task<string> UploadAlbumSubmissionByArtist([FromBody] AlbumSubmissi
343469
344470 // Save it in the database.
345471 album . Id = $ "AlbumSubmissionsByArtist/{ album . Artist } /{ album . Name } /{ DateTime . UtcNow : O} ";
472+ album . Status = ApprovalStatus . Pending ;
473+ album . CreatedAt = DateTimeOffset . UtcNow ;
346474 await DbSession . StoreAsync ( album ) ;
347475
348476 // Notify admins.
0 commit comments