diff --git a/RMuseum/Controllers/AudioNarrationController.cs b/RMuseum/Controllers/AudioNarrationController.cs index 364c628a..23e13da6 100644 --- a/RMuseum/Controllers/AudioNarrationController.cs +++ b/RMuseum/Controllers/AudioNarrationController.cs @@ -95,8 +95,10 @@ namespace RMuseum.Controllers return BadRequest(rsFileResult.ExceptionString); } files.Add(rsFileResult.Result); - } - return Ok(await _audioService.GetUploadSession(resSession.Result.Id)); + } + resSession = await _audioService.FinalizeNewUploadSession(resSession.Result, files.ToArray()); + + return Ok(resSession.Result); } catch (Exception exp) { diff --git a/RMuseum/Models/UploadSession/UploadSessionFile.cs b/RMuseum/Models/UploadSession/UploadSessionFile.cs index ec364429..86d0deb3 100644 --- a/RMuseum/Models/UploadSession/UploadSessionFile.cs +++ b/RMuseum/Models/UploadSession/UploadSessionFile.cs @@ -43,7 +43,11 @@ namespace RMuseum.Models.UploadSession /// public string FilePath { get; set; } - + /// + /// File check sum for mp3 files + /// + public string MP3FileCheckSum { get; set; } + /// /// Process Result /// diff --git a/RMuseum/RMuseum.csproj b/RMuseum/RMuseum.csproj index c91f7e29..afa80d93 100644 --- a/RMuseum/RMuseum.csproj +++ b/RMuseum/RMuseum.csproj @@ -25,6 +25,7 @@ + diff --git a/RMuseum/RMuseum.xml b/RMuseum/RMuseum.xml index 02274623..741a5f83 100644 --- a/RMuseum/RMuseum.xml +++ b/RMuseum/RMuseum.xml @@ -2809,6 +2809,11 @@ File Path + + + File check sum for mp3 files + + Process Result @@ -3404,6 +3409,14 @@ + + + finalize upload session (add files) + + + + + Get Upload Session (including files) @@ -4153,6 +4166,14 @@ + + + finalize upload session (add files) + + + + + Get Upload Session (including files) @@ -4170,7 +4191,12 @@ Database Contetxt - + + + Background Task Queue Instance + + + constructor @@ -4432,5 +4458,137 @@ + + + اطلاعات فایل صوتی شعر + + + + + سازنده + + + + + شناسۀ شعر + + + + + شناسۀ فایل صوتی با شروع از 1 برای هر شعر + + + + + مسیر فایل + + + + + شرح + + + + + آیا همگام شده + + + + + ساختار اطلاعات همگام سازی + + + + + ترتیب مصرع (-1 نشانگر آغاز فایل صوتی و -2 نشانگر پایان آن است) + + + + + زمان در فایل صوتی بر حسب میلی ثانیه + + + + + اطلاعات همگام سازی + + + + + نشانی دریافت فایل صوتی + + + + + آیا لینک دریافت فایل صوتی مستقیم است + + + + + شناسۀ یکتای اطلاعات همگام سازی + + + + + امضای یکتای فایل صوتی + + + + + محاسبه چک سام فایل + + + + + + آیا اطلاعات همگام سازی آپلود شده؟ + + + + + تبدیل به رشته + + شرح + + + + Warning: not always filled + + + + + Warning: Rarely filled + + + + + پردازشگر ذخیره و بارگذاری لیستهای اشعار + + + + + ذخیره در فایل + + + + + + + + + ذخیره در فایل + + + + + + + + + بارگذاری از فایل + + + + diff --git a/RMuseum/Services/IAudioNarrationService.cs b/RMuseum/Services/IAudioNarrationService.cs index 8f250c5e..34e0936f 100644 --- a/RMuseum/Services/IAudioNarrationService.cs +++ b/RMuseum/Services/IAudioNarrationService.cs @@ -43,11 +43,20 @@ namespace RMuseum.Services /// Task> SaveUploadedFile(IFormFile uploadedFile); + /// + /// finalize upload session (add files) + /// + /// + /// + /// + Task> FinalizeNewUploadSession(UploadSession session, UploadSessionFile[] files); + /// /// Get Upload Session (including files) /// /// /// Task> GetUploadSession(Guid id); + } } diff --git a/RMuseum/Services/Implementation/AudioNarrationService.cs b/RMuseum/Services/Implementation/AudioNarrationService.cs index 27958368..3b9c0454 100644 --- a/RMuseum/Services/Implementation/AudioNarrationService.cs +++ b/RMuseum/Services/Implementation/AudioNarrationService.cs @@ -1,4 +1,6 @@ -using Microsoft.AspNetCore.Http; +using Audit.WebApi; +using ganjoor; +using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using MySql.Data.MySqlClient; @@ -183,14 +185,13 @@ namespace RMuseum.Services.Implementation FileName = uploadedFile.FileName, Length = uploadedFile.Length, Name = uploadedFile.Name, - ProcessResult = true, - ProcessResultMsg = "" + ProcessResult = false, + ProcessResultMsg = "پردازش نشده (فایلهای mp3‌ که مشخصات آنها در فایلهای xml ارسالی یافت نشود پردازش نمی‌شوند)." }; string ext = Path.GetExtension(file.FileName).ToLower(); if(ext != ".mp3" && ext != ".xml" && ext != ".zip") { - file.ProcessResult = false; file.ProcessResultMsg = "تنها فایلهای با پسوند mp3، xml و zip قابل قبول هستند."; } else @@ -228,6 +229,91 @@ namespace RMuseum.Services.Implementation } } + /// + /// finalize upload session (add files) + /// + /// + /// + /// + public async Task> FinalizeNewUploadSession(UploadSession session, UploadSessionFile[] files) + { + try + { + session.UploadedFiles = files; + + _context.UploadSessions.Update(session); + await _context.SaveChangesAsync(); + + _backgroundTaskQueue.QueueBackgroundWorkItem + ( + async token => + { + using (RMuseumDbContext context = new RMuseumDbContext(Configuration)) //this is long running job, so _context might be already been freed/collected by GC + { + List mp3files = new List(); + foreach (UploadSessionFile file in session.UploadedFiles.Where(file => Path.GetExtension(file.FilePath) == ".mp3").ToList()) + { + try + { + file.MP3FileCheckSum = PoemAudio.ComputeCheckSum(file.FilePath); + mp3files.Add(file); + } + catch (Exception exp) + { + session.UploadedFiles.Where(f => f.Id == file.Id).SingleOrDefault().ProcessResultMsg = exp.ToString(); + context.UploadSessions.Update(session); + await context.SaveChangesAsync(); + } + } + + foreach (UploadSessionFile file in session.UploadedFiles.Where(file => Path.GetExtension(file.FilePath) == ".xml").ToList()) + { + try + { + foreach (PoemAudio audio in PoemAudioListProcessor.Load(file.FilePath)) + { + UploadSessionFile mp3file = mp3files.Where(mp3 => mp3.MP3FileCheckSum == audio.FileCheckSum).SingleOrDefault(); + if(mp3file == null) + { + session.UploadedFiles.Where(f => f.Id == file.Id).SingleOrDefault().ProcessResultMsg = "فایل mp3 متناظر یافت نشد (توجه فرمایید که همنامی اهمیت ندارد و فایل mp3 ارسالی باید دقیقاً همان فایلی باشد که همگامی با آن صورت گرفته است. اگر بعداً آن را جایگزین کرده‌اید مشخصات آن با مشخصات درج شده در فایل xml همسان نخواهد بود)."; + context.UploadSessions.Update(session); + } + else + { + //here we should produce and save ogg file + + session.UploadedFiles.Where(f => f.Id == file.Id).SingleOrDefault().ProcessResultMsg = ""; + session.UploadedFiles.Where(f => f.Id == file.Id).SingleOrDefault().ProcessResult = true; + session.UploadedFiles.Where(f => f.Id == mp3file.Id).SingleOrDefault().ProcessResultMsg = ""; + session.UploadedFiles.Where(f => f.Id == mp3file.Id).SingleOrDefault().ProcessResult = true; + } + await context.SaveChangesAsync(); + } + } + catch (Exception exp) + { + session.UploadedFiles.Where(f => f.Id == file.Id).SingleOrDefault().ProcessResultMsg = "فایل XML نامعتبر است. اطلاعات بیشتر: " + exp.ToString(); + context.UploadSessions.Update(session); + await context.SaveChangesAsync(); + } + } + } + + } + ); + + + + return new RServiceResult(session); + + } + catch (Exception exp) + { + return new RServiceResult(null, exp.ToString()); + } + } + + /// /// Get Upload Session (including files) /// @@ -260,15 +346,21 @@ namespace RMuseum.Services.Implementation /// protected readonly RMuseumDbContext _context; + /// + /// Background Task Queue Instance + /// + protected readonly IBackgroundTaskQueue _backgroundTaskQueue; + /// /// constructor /// /// /// - public AudioNarrationService(RMuseumDbContext context, IConfiguration configuration) + public AudioNarrationService(RMuseumDbContext context, IConfiguration configuration, IBackgroundTaskQueue backgroundTaskQueue) { _context = context; Configuration = configuration; + _backgroundTaskQueue = backgroundTaskQueue; } } } diff --git a/RMuseum/Services/Implementation/ImportedFromDesktopGanjoor/PoemAudio.cs b/RMuseum/Services/Implementation/ImportedFromDesktopGanjoor/PoemAudio.cs new file mode 100644 index 00000000..a5c045bb --- /dev/null +++ b/RMuseum/Services/Implementation/ImportedFromDesktopGanjoor/PoemAudio.cs @@ -0,0 +1,194 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Security.Cryptography; +using System.IO; + +namespace ganjoor +{ + /// + /// اطلاعات فایل صوتی شعر + /// + public class PoemAudio + { + /// + /// سازنده + /// + public PoemAudio() + { + } + + /// + /// شناسۀ شعر + /// + public int PoemId + { + get; + set; + } + + /// + /// شناسۀ فایل صوتی با شروع از 1 برای هر شعر + /// + public int Id + { + get; + set; + } + + /// + /// مسیر فایل + /// + public string FilePath + { + get; + set; + } + + /// + /// شرح + /// + public string Description + { + get; + set; + } + + /// + /// آیا همگام شده + /// + public bool IsSynced + { + get + { + return SyncArray != null; + } + } + + + /// + /// ساختار اطلاعات همگام سازی + /// + public struct SyncInfo + { + /// + /// ترتیب مصرع (-1 نشانگر آغاز فایل صوتی و -2 نشانگر پایان آن است) + /// + public int VerseOrder; + /// + /// زمان در فایل صوتی بر حسب میلی ثانیه + /// + public int AudioMiliseconds; + } + + /// + /// اطلاعات همگام سازی + /// + public SyncInfo[] SyncArray + { + get; + set; + } + + /// + /// نشانی دریافت فایل صوتی + /// + public string DownloadUrl + { + get; + set; + } + + /// + /// آیا لینک دریافت فایل صوتی مستقیم است + /// + public bool IsDirectlyDownloadable + { + get; + set; + } + + /// + /// شناسۀ یکتای اطلاعات همگام سازی + /// + public Guid SyncGuid + { + get; + set; + } + + /// + /// امضای یکتای فایل صوتی + /// + public string FileCheckSum + { + get; + set; + } + + /// + /// محاسبه چک سام فایل + /// + /// + public static string ComputeCheckSum(string filePath) + { + using (var md5 = MD5.Create()) + { + try + { + using (var stream = File.OpenRead(filePath)) + { + return BitConverter.ToString(md5.ComputeHash(stream)).Replace("-", "").ToLower(); + } + } + catch + { + return ""; //problem with file + } + } + + } + + /// + /// آیا اطلاعات همگام سازی آپلود شده؟ + /// + public bool IsUploaded + { + get; + set; + } + + /// + /// تبدیل به رشته + /// + /// شرح + public override string ToString() + { + return this.Description; + } + + + /// + ///Warning: not always filled + /// + public string PoetName + { + get; + set; + } + + /// + /// Warning: Rarely filled + /// + public string PoemTitle + { + get; + set; + } + + + + + } +} diff --git a/RMuseum/Services/Implementation/ImportedFromDesktopGanjoor/PoemAudioListProcessor.cs b/RMuseum/Services/Implementation/ImportedFromDesktopGanjoor/PoemAudioListProcessor.cs new file mode 100644 index 00000000..2fe7ab5c --- /dev/null +++ b/RMuseum/Services/Implementation/ImportedFromDesktopGanjoor/PoemAudioListProcessor.cs @@ -0,0 +1,237 @@ +using System; +using System.Collections.Generic; +using System.Xml; +using System.Reflection; +using System.IO; + +namespace ganjoor +{ + /// + /// پردازشگر ذخیره و بارگذاری لیستهای اشعار + /// + public class PoemAudioListProcessor + { + + /// + /// ذخیره در فایل + /// + /// + /// + /// + /// + public static bool Save(string FileName, PoemAudio poemAudio, bool oldVersion) + { + List lst = new List(); + lst.Add(poemAudio); + return Save(FileName, lst, oldVersion); + } + + /// + /// ذخیره در فایل + /// + /// + /// + /// + /// + public static bool Save(string FileName, List List, bool oldVersion) + { + XmlDocument doc = new XmlDocument(); + XmlNode poemAudioRootNode = doc.CreateNode(XmlNodeType.Element, "DesktopGanjoorPoemAudioList", ""); + doc.AppendChild(poemAudioRootNode); + foreach (PoemAudio audio in List) + { + XmlNode poemAudioNode = doc.CreateNode(XmlNodeType.Element, "PoemAudio", ""); + foreach (PropertyInfo prop in typeof(PoemAudio).GetProperties()) + { + if (!prop.CanWrite) + continue; + bool ignoreProp = false; + XmlNode propNode = doc.CreateNode(XmlNodeType.Element, prop.Name, ""); + if (prop.PropertyType == typeof(string)) + { + string value = prop.GetValue(audio, null) == null ? "" : prop.GetValue(audio, null).ToString(); + if (string.IsNullOrEmpty(value)) + { + ignoreProp = true; + } + else + propNode.InnerText = value; + } + else + if (prop.PropertyType == typeof(Int32)) + { + int value = Convert.ToInt32(prop.GetValue(audio, null)); + if (value == 0) + { + ignoreProp = true; + } + else + propNode.InnerText = value.ToString(); + } + else + if (prop.PropertyType == typeof(bool)) + { + bool value = Convert.ToBoolean(prop.GetValue(audio, null)); + if (!value) + { + ignoreProp = true; + } + else + propNode.InnerText = value.ToString(); + } + else + if (prop.PropertyType == typeof(Guid)) + { + string value = prop.GetValue(audio, null).ToString(); + if (string.IsNullOrEmpty(value)) + { + ignoreProp = true; + } + else + propNode.InnerText = value; + } + else + { + ignoreProp = true; + } + + if (!ignoreProp) + poemAudioNode.AppendChild(propNode); + } + //رفع اشکال نسخه قدیمی NAudio + XmlNode bugfixNode = doc.CreateNode(XmlNodeType.Element, "OneSecondBugFix", ""); + bugfixNode.InnerText = "1000"; + poemAudioNode.AppendChild(bugfixNode); + + XmlNode syncInfoArrayNode = doc.CreateNode(XmlNodeType.Element, "SyncArray", ""); + if (audio.SyncArray != null) + { + + foreach (PoemAudio.SyncInfo info in audio.SyncArray) + { + XmlNode syncInfoNode = doc.CreateNode(XmlNodeType.Element, "SyncInfo", ""); + + XmlNode vOrderNode = doc.CreateNode(XmlNodeType.Element, "VerseOrder", ""); + vOrderNode.InnerText = info.VerseOrder.ToString(); + syncInfoNode.AppendChild(vOrderNode); + + XmlNode vAudioMiliseconds = doc.CreateNode(XmlNodeType.Element, "AudioMiliseconds", ""); + if (oldVersion) + { + vAudioMiliseconds.InnerText = (info.AudioMiliseconds/2).ToString(); + } + else + vAudioMiliseconds.InnerText = info.AudioMiliseconds.ToString(); + syncInfoNode.AppendChild(vAudioMiliseconds); + + + syncInfoArrayNode.AppendChild(syncInfoNode); + } + } + + poemAudioNode.AppendChild(syncInfoArrayNode); + poemAudioRootNode.AppendChild(poemAudioNode); + } + try + { + doc.Save(FileName); + return true; + } + catch + { + + return false; + } + } + + + /// + /// بارگذاری از فایل + /// + /// + /// + public static List Load(string FileName) + { + List lstPoemAudio = new List(); + + XmlDocument doc = new XmlDocument(); + doc.LoadXml(File.ReadAllText(FileName)); + + //Collect List: + XmlNodeList poemAudioNodes = doc.GetElementsByTagName("PoemAudio"); + foreach (XmlNode poemAudioNode in poemAudioNodes) + { + PoemAudio poemAudioInfo = new PoemAudio(); + poemAudioInfo.IsDirectlyDownloadable = false; + poemAudioInfo.IsUploaded = false; + foreach (XmlNode Node in poemAudioNode.ChildNodes) + { + switch (Node.Name) + { + case "PoemId": + poemAudioInfo.PoemId = Convert.ToInt32(Node.InnerText); + break; + case "Id": + poemAudioInfo.Id = Convert.ToInt32(Node.InnerText); + break; + case "DownloadUrl": + poemAudioInfo.DownloadUrl = Node.InnerText; + break; + case "FilePath": + poemAudioInfo.FilePath = Node.InnerText; + break; + case "Description": + poemAudioInfo.Description = Node.InnerText; + break; + case "SyncGuid": + poemAudioInfo.SyncGuid = Guid.Parse(Node.InnerText); + break; + case "FileCheckSum": + poemAudioInfo.FileCheckSum = Node.InnerText; + break; + case "IsDirectlyDownloadable": + poemAudioInfo.IsDirectlyDownloadable = Convert.ToBoolean(Node.InnerText); + break; + case "IsUploaded": + poemAudioInfo.IsDirectlyDownloadable = Convert.ToBoolean(Node.InnerText); + break; + case "SyncArray": + { + XmlNodeList syncInfoNodeList = Node.SelectNodes("SyncInfo"); + if (syncInfoNodeList != null) + { + List lstSyncInfo = new List(); + + foreach (XmlNode nodeSyncIndo in syncInfoNodeList) + { + + lstSyncInfo.Add( + new PoemAudio.SyncInfo() + { + VerseOrder = Convert.ToInt32(nodeSyncIndo.SelectSingleNode("VerseOrder").InnerText), + AudioMiliseconds = Convert.ToInt32(nodeSyncIndo.SelectSingleNode("AudioMiliseconds").InnerText), + } + ); + } + + if (lstSyncInfo.Count > 0) + { + poemAudioInfo.SyncArray = lstSyncInfo.ToArray(); + } + } + } + break; + + } + + } + lstPoemAudio.Add(poemAudioInfo); + } + + + return lstPoemAudio; + } + + + } +}