-
-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
thepirat000
committed
Nov 20, 2019
1 parent
2b3ed6c
commit 639f7da
Showing
12 changed files
with
467 additions
and
86 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using System.IO; | ||
using System.IO.Compression; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using Microsoft.AspNetCore.Http; | ||
using Microsoft.AspNetCore.Mvc; | ||
using Microsoft.Extensions.FileProviders; | ||
using Microsoft.Extensions.Logging; | ||
using SpleeterAPI.Youtube; | ||
using SpleeterAPI.Split; | ||
using System.Collections.Concurrent; | ||
|
||
namespace SpleeterAPI.Controllers | ||
{ | ||
|
||
[Route("mp3")] | ||
[ApiController] | ||
public class Mp3Controller : ControllerBase | ||
{ | ||
private static long Max_Upload_Size = long.Parse(Startup.Configuration["Mp3:MaxUploadSize"]); | ||
private readonly static ConcurrentDictionary<string, DateTime> _processing = new ConcurrentDictionary<string, DateTime>(); | ||
|
||
private readonly ILogger<Mp3Controller> _logger; | ||
public Mp3Controller(ILogger<Mp3Controller> logger) | ||
{ | ||
_logger = logger; | ||
} | ||
|
||
[HttpPost("p")] | ||
[Produces("application/json")] | ||
public async Task<ActionResult<ProcessResponse>> Process([FromForm] string format, [FromForm] bool includeHf) | ||
{ | ||
if (format != "2stems" && format != "4stems" && format != "5stems" && format != "karaoke") | ||
{ | ||
return BadRequest("Format must be '2stems', '4stems' or '5stems'"); | ||
} | ||
if (Request.Form.Files?.Count == 0) | ||
{ | ||
return BadRequest("No files to process"); | ||
} | ||
var totalBytes = Request.Form.Files.Sum(f => f.Length); | ||
if (totalBytes > Max_Upload_Size) | ||
{ | ||
return BadRequest($"Can't process more than {Max_Upload_Size / 1024:N0} Mb of data"); | ||
} | ||
|
||
var archiveName = GetFileId(Request.Form.Files, format, includeHf, false); | ||
|
||
var now = DateTime.UtcNow; | ||
if (_processing.TryGetValue(archiveName, out DateTime startDate)) | ||
{ | ||
var startedSecondsAgo = (now - startDate).TotalSeconds; | ||
if (startedSecondsAgo < 1800) | ||
{ | ||
return new ProcessResponse() { Error = $"File {archiveName} is being processed, started {startedSecondsAgo:N0} seconds ago. Try again later in few more minutes..." }; | ||
} | ||
} | ||
|
||
if (format == "karaoke") | ||
{ | ||
if (Request.Form.Files.Count == 1) | ||
{ | ||
var mp3File = $"/output/{archiveName}.mp3"; | ||
if (System.IO.File.Exists(mp3File)) | ||
{ | ||
return new ProcessResponse() { FileId = $"{archiveName}.mp3" }; | ||
} | ||
} | ||
} | ||
|
||
var zipFile = $"/output/{archiveName}.zip"; | ||
if (System.IO.File.Exists(zipFile)) | ||
{ | ||
return new ProcessResponse() { FileId = $"{archiveName}.zip" }; | ||
} | ||
|
||
_processing[archiveName] = now; | ||
|
||
var inputFolder = $"/input/{archiveName}"; | ||
if (!Directory.Exists(inputFolder)) | ||
{ | ||
Directory.CreateDirectory(inputFolder); | ||
} | ||
var inputFilenames = new List<string>(); | ||
|
||
// 1. copy input files to a temp folder | ||
foreach (var file in Request.Form.Files) | ||
{ | ||
var fileName = SanitizeFilename(file.FileName); | ||
inputFilenames.Add($"{fileName}"); | ||
var filePath = $"{inputFolder}/{fileName}"; | ||
if (!System.IO.File.Exists(filePath)) | ||
{ | ||
using (var output = System.IO.File.Create(filePath)) | ||
{ | ||
await file.CopyToAsync(output); | ||
} | ||
} | ||
} | ||
if (inputFilenames.Count == 0) | ||
{ | ||
_processing.TryRemove(archiveName, out _); | ||
return Problem("Unknown problem when creating files"); | ||
} | ||
|
||
// 2. call spleeter with those multiple files | ||
var sw = Stopwatch.StartNew(); | ||
var inputFileParam = string.Join(' ', inputFilenames.Select(fn => $"\"{inputFolder}/{fn}\"")); | ||
var separateResult = SpliterHelper.Split(inputFileParam, archiveName, format, includeHf, _logger, isBatch: true); | ||
_logger.LogInformation($"Separation for {inputFilenames.Count} files:\n\tProcessing time: {sw.Elapsed:hh\\:mm\\:ss}"); | ||
|
||
if (separateResult.ExitCode != 0) | ||
{ | ||
_processing.TryRemove(archiveName, out _); | ||
return Problem($"spleeter separate command exited with code {separateResult.ExitCode}\nException: {separateResult.Exception}\nMessages: {separateResult.Output}."); | ||
} | ||
|
||
// 2.1 If karaoke | ||
if (format == "karaoke") | ||
{ | ||
// 2.1.1 If just 1 file -> copy to output renaming as karaoke and return mp3 file name | ||
if (inputFilenames.Count == 1) | ||
{ | ||
System.IO.File.Copy($"/output/{archiveName}/{Path.GetFileNameWithoutExtension(inputFilenames[0])}/accompaniment.mp3", $"/output/{archiveName}.mp3"); | ||
Directory.Delete($"/output/{archiveName}", true); | ||
Directory.Delete(inputFolder, true); | ||
_processing.TryRemove(archiveName, out _); | ||
return new ProcessResponse() { FileId = $"{archiveName}.mp3" }; | ||
} | ||
else | ||
{ | ||
// More than 1 karaoke -> remove all the vocals.mp3 | ||
foreach (var file in Directory.EnumerateFiles($"/output/{archiveName}", "vocals.mp3", SearchOption.AllDirectories)) | ||
{ | ||
System.IO.File.Delete(file); | ||
} | ||
} | ||
} | ||
|
||
// 3. Zip the output folder | ||
ZipFile.CreateFromDirectory($"/output/{archiveName}", zipFile, CompressionLevel.Fastest, false); | ||
|
||
// 4. Delete temp files | ||
Directory.Delete($"/output/{archiveName}", true); | ||
Directory.Delete(inputFolder, true); | ||
|
||
_processing.TryRemove(archiveName, out _); | ||
|
||
return new ProcessResponse() { FileId = $"{archiveName}.zip" }; | ||
} | ||
|
||
|
||
[HttpGet("d")] | ||
[Produces("application/json")] | ||
public ActionResult Download([FromQuery] string fn) | ||
{ | ||
if (string.IsNullOrWhiteSpace(fn)) | ||
{ | ||
return BadRequest(); | ||
} | ||
var file = $"/output/{fn}"; | ||
var cType = fn.ToLower().EndsWith("zip") ? "application/zip" : "application/mp3"; | ||
if (System.IO.File.Exists(file)) | ||
{ | ||
return PhysicalFile(file, cType, fn); | ||
} | ||
return Problem($"File {fn} not found"); | ||
} | ||
|
||
private string GetFileId(IFormFileCollection files, string format, bool includeHf, bool includeOri) | ||
{ | ||
int hash1 = string.Join('|', files.OrderBy(f => f.FileName).Select(f => f.FileName).ToArray()).GetStableHashCode(); | ||
int hash2 = (int)(files.Sum(f => f.Length) % int.MaxValue); | ||
var fileId = StringHelper.SeededString(hash1, 8) + StringHelper.SeededString(hash2, 8); | ||
fileId += $".{format}"; | ||
if (includeOri || includeHf) | ||
{ | ||
fileId += "."; | ||
if (includeHf) | ||
{ | ||
fileId += "hf"; | ||
} | ||
if (includeOri) | ||
{ | ||
fileId += "o"; | ||
} | ||
} | ||
return fileId; | ||
} | ||
|
||
private string SanitizeFilename(string filename) | ||
{ | ||
filename = filename.Replace("/", "").Replace("\\", "").Replace("\"", ""); | ||
return string.Concat(filename.Split(Path.GetInvalidFileNameChars())); | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
|
||
namespace SpleeterAPI.Split | ||
{ | ||
public static class StringHelper | ||
{ | ||
private static string Valid_Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; | ||
|
||
public static string SeededString(int seed, int size) | ||
{ | ||
var stringChars = new char[size]; | ||
var random = new Random(seed); | ||
for (int i = 0; i < stringChars.Length; i++) | ||
{ | ||
stringChars[i] = Valid_Chars[random.Next(Valid_Chars.Length)]; | ||
} | ||
var finalString = new String(stringChars); | ||
return finalString; | ||
} | ||
|
||
public static int GetStableHashCode(this string str) | ||
{ | ||
unchecked | ||
{ | ||
int hash1 = 5381; | ||
int hash2 = hash1; | ||
|
||
for (int i = 0; i < str.Length && str[i] != '\0'; i += 2) | ||
{ | ||
hash1 = ((hash1 << 5) + hash1) ^ str[i]; | ||
if (i == str.Length - 1 || str[i + 1] == '\0') | ||
break; | ||
hash2 = ((hash2 << 5) + hash2) ^ str[i + 1]; | ||
} | ||
|
||
return hash1 + (hash2 * 1566083941); | ||
} | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 changes: 1 addition & 2 deletions
3
Youtube/YoutubeProcessResponse.cs → Youtube/ProcessResponse.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.