public async Task Invoke(HttpContext context) { Logger.LogInformation($"{context.Request.Path}{context.Request.QueryString}\n{context.Request.Headers["Range"]}"); if (context.Request.Path.HasValue && context.Request.Path.Value.StartsWith("/API/")) { string[] pathElements = context.Request.Path.Value.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries); int i = 0; pathElements.ToList().ForEach(dr => { context.Response.Headers[$"X-API-Path-{i}"] = dr; }); context.Request.Query.ToList().ForEach(dr => { context.Response.Headers[$"X-API-QS-{dr.Key}"] = dr.Value; }); if (pathElements.Length > 1 && pathElements[1].Equals("Stream") /* && context.Request.Query.ContainsKey("id")*/) { if (context.Request.Query.ContainsKey("id")) { int fileID = -1; if (!int.TryParse(context.Request.Query["id"], out fileID)) { context.Response.StatusCode = 404; return; } Models.File musicFile = MusicContext.Files .Include(dr => dr.Library) .Where(dr => dr.ID == fileID).FirstOrDefault(); if (musicFile != null) { string fullPath = Path.Combine(musicFile.Library.Path, musicFile.Path); if (System.IO.File.Exists(fullPath)) { PlaybackData dat = null; bool newStream = false; lock (Buffer) { if (Buffer.ContainsKey(fileID.ToString())) { lock (dat = Buffer[fileID.ToString()]) { dat.RefreshAccessTime(); } } else { Buffer[fileID.ToString()] = dat = new PlaybackData(); newStream = true; } } if (newStream) { {// note how long the file is for output aproximation dat.Seconds = 600; dat.bitrate = 320; } dat.contentLengthCounter = 0; dat.writing = true; (new Thread(() => { string argsP = $"-i \"{fullPath}\" -map 0:0 -b:a {dat.bitrate}k -v 0 -f mp3 -"; Process ffmpeg; ffmpeg = new Process(); ffmpeg.StartInfo.WorkingDirectory = Options.ffmpeg; ffmpeg.StartInfo.FileName = "\"" + Options.ffmpeg + "ffmpeg.exe\""; ffmpeg.StartInfo.Arguments = argsP; ffmpeg.StartInfo.RedirectStandardOutput = true; //ffmpeg.StartInfo.CreateNoWindow = true; ffmpeg.StartInfo.UseShellExecute = false; ffmpeg.Start(); StreamReader reader = ffmpeg.StandardOutput; byte[] buffer = new byte[32768]; int read; while ((read = reader.BaseStream.Read(buffer, 0, buffer.Length)) > 0) { dat.dataStream.Write(buffer, 0, read); dat.contentLengthCounter += read; } Logger.LogInformation("Compleated Buffering Audio File"); Logger.LogInformation($"Length of data is {dat.contentLengthCounter}"); dat.writing = false; })).Start(); } int defaultStripeSize = 1024 * 256; int stillTranscodingStripeSize = 1024 * 16; int stripeSize = defaultStripeSize; string totalSizeString = "*"; //string totalSizeString = "999999999999"; //string totalSizeString = ""; int totalSize = -1; string startIndexString = "0"; string endIndexString = null; if (context.Request.Headers.ContainsKey("Range")) { string range = context.Request.Headers["Range"]; string[] range_eq_split = range.Split(new char[] { '=' }); string[] range_parts = range_eq_split[1].Split(new char[] { '-' }); startIndexString = range_parts[0]; if (range_parts.Length > 0 && range_parts[1].Length > 0) { endIndexString = range_parts[1]; } } int startIndex = int.Parse(startIndexString); if (endIndexString != null) { int requestedStripSize = int.Parse(endIndexString) - startIndex + 1; stripeSize = Math.Min(stripeSize, requestedStripSize); } int startingIndex = startIndex; // wait until at least enough data is read // this area is a little sketchy due to threading but consider that old values are OK here and we aren't writing while (dat.writing && dat.contentLengthCounter < (startingIndex + stillTranscodingStripeSize)) { Thread.Sleep(100); } lock (dat) { if (dat.writing) { stripeSize = Math.Min(stripeSize, stillTranscodingStripeSize); } stripeSize = Math.Min(stripeSize, dat.contentLengthCounter - startingIndex); if (!dat.writing) // we're done writing to the stream so clearly it contains the entire file { totalSize = dat.contentLengthCounter; totalSizeString = totalSize.ToString(); } //else // failed hack to get browsers to download partial //{ // totalSizeString = (startIndex + stripeSize * 2).ToString(); //} else if (dat.Seconds > 0) { totalSizeString = ((int)((dat.Seconds * (dat.bitrate * 1000)) / 8)).ToString(); } } //check stripeSize isn't negative (can only happen if fully transcoded else the system would avoid it) //check startIndexString isn't greater than totalSize //check startingIndex + stripeSize isn't greater than totalSize if (totalSize > 0 && startIndex >= totalSize) { context.Response.StatusCode = 416; context.Response.ContentType = "audio/mpeg"; context.Response.Headers["Accept-Ranges"] = "bytes"; context.Response.Headers["Content-Range"] = $"bytes {startIndex}-*/{totalSizeString}"; //context.Response.Headers["Content-Length"] = stripeSize.ToString(); } else { //if (totalSize >= 0 && (startIndex + stripeSize) >= totalSize) //{ // context.Response.StatusCode = 206; //} //else //{ // context.Response.StatusCode = 206; //} context.Response.StatusCode = 206; context.Response.ContentType = "audio/mpeg"; context.Response.Headers["Accept-Ranges"] = "bytes"; context.Response.Headers["Content-Range"] = $"bytes {startIndex}-{startIndex + stripeSize - 1}/{totalSizeString}"; context.Response.Headers["Content-Length"] = stripeSize.ToString(); //context.Response.Headers["Transfer-Encoding"] = @"chunked"; //context.Response.Headers["Cache-control"] = ""; //context.Response.Headers["Pragma"] = ""; //X-Content-Duration: 63.23 lock (dat) { byte[] outputBuffer = new byte[stripeSize]; dat.dataStream.readPosition = startIndex; dat.dataStream.Read(outputBuffer, 0, stripeSize); dat.RefreshAccessTime(); context.Response.Body.Write(outputBuffer, 0, stripeSize); } } //MemoryStream memStream = new MemoryStream(); //await reader.BaseStream.CopyToAsync(memStream); //memStream.Position = 0; //Response.ContentLength = memStream.Length; //await memStream.CopyToAsync(Response.Body); //await reader.BaseStream.CopyToAsync(Response.Body); //(new Thread(() => { // memStream //})).Start(); //memStream.Position = 0; //ffmpeg.WaitForExit(); //ffmpeg.Kill(); //ffmpeg.close(); //Response.Headers["Content-Range"] = "bytes 0-1/*"; //Response.ContentLength = contentLengthCounter; //Response.Headers["Content-Range"] = $"bytes 0-1/{contentLengthCounter}"; //{ // byte[] buffer = new byte[32768]; // int read; // while (((read = dat.dataStream.Read(buffer, 0, buffer.Length)) > 0) || dat.writing) // { // if (read == 0) // { // Thread.Sleep(100); // continue; // } // context.Response.Body.Write(buffer, 0, read); // lock(dat) // { // dat.RefreshAccessTime(); // } // } // Logger.LogInformation("Compleated Sending Buffering Audio File"); //} //return new FileStreamResult(memStream, HttpContext.Response.ContentType); //return new FileStreamResult(reader.BaseStream, HttpContext.Response.ContentType); //return new FileStreamResult(System.IO.File.OpenRead(fullPath), HttpContext.Response.ContentType); //return new PhysicalFileResult(fullPath, HttpContext.Response.ContentType); } else { context.Response.StatusCode = 404; return; } } else { context.Response.StatusCode = 404; return; } } else { context.Response.StatusCode = 404; return; } } else { await _next.Invoke(context); } //if (context.Response.Headers.Keys.Contains("X-API-Steam")) //{ // context.Response.Headers["X-API-SteamID"] = context.Response.Headers["X-API-Steam"]; // context.Response.Headers.Remove("X-API-Steam"); // return; //} //return; } else { await _next.Invoke(context); } }
void recursivePathDive(Library lib, string dirPath) { string[] Directories = Directory.GetDirectories(dirPath, @"*", SearchOption.TopDirectoryOnly); string[] Files = Directory.GetFiles(dirPath, @"*", SearchOption.TopDirectoryOnly); foreach (string FullPath in Files) { string SubPath = FullPath.Substring(lib.Path.Length).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); Alexandria.Models.File tmp = MusicContext.Files.Where(dr => dr.Library.Equals(lib) && dr.Path == SubPath).FirstOrDefault(); if (tmp == null) { // do we want to track this file? string FileExtension = ExtensionsToMonitor.Where(dr => Path.GetFileName(FullPath).EndsWith(dr)).FirstOrDefault(); if (FileExtension == null) { ConsoleColor tmpConCol = Console.ForegroundColor; Console.ForegroundColor = ConsoleColor.Gray; Console.WriteLine($"Skip\t{lib.Path} | {SubPath}"); Console.ForegroundColor = tmpConCol; continue; } { ConsoleColor tmpConCol = Console.ForegroundColor; Console.ForegroundColor = ConsoleColor.White; Console.WriteLine($"New\t{lib.Path} | {SubPath}"); Console.ForegroundColor = tmpConCol; } // gather file data // add to db Alexandria.Models.File newFile = new Alexandria.Models.File() { Library = lib, Archival = false, FileModified = System.IO.File.GetLastWriteTimeUtc(FullPath), RecordAdded = DateTime.UtcNow, Path = SubPath, Tags = new List <TagAssociation>() }; //TagAdder tagAdderRecord = new TagAdder() { Timestamp = DateTime.UtcNow, User = systemAccount }; //TagAssociation newTagAssoc = new TagAssociation() { Tag = unTaggedTag, Added = new List<TagAdder>() }; //newTagAssoc.Added.Add(tagAdderRecord); //newFile.Tags.Add(newTagAssoc); newFile.AddTag(unTaggedTag, systemAccount); MusicContext.Files.Add(newFile); MusicContext.SaveChanges(); } else { /* * // skip file unless modified date has changed * * // do we want to track this file? * string FileExtension = ExtensionsToMonitor.Where(dr => Path.GetFileName(FullPath).EndsWith(dr)).FirstOrDefault(); * if (FileExtension == null) continue; * * DateTime FileModDate = System.IO.File.GetLastWriteTimeUtc(FullPath); * if(FileModDate > tmp.Modified) * { * Console.WriteLine($"Changed\t{lib.Path} | {SubPath}"); * } */ { ConsoleColor tmpConCol = Console.ForegroundColor; Console.ForegroundColor = ConsoleColor.DarkGray; Console.WriteLine($"Known\t{lib.Path} | {SubPath}"); Console.ForegroundColor = tmpConCol; } } } foreach (string Directory in Directories) { recursivePathDive(lib, Directory); } }