// アイテムのProfileNameからプロファイルを決定して、 // オプションでwaits!=nullのときはクライアントに通知 // 戻り値: プロファイルが変更された場合(結果、エラーになった場合も含む) private bool UpdateProfileItem(QueueItem item, List <Task> waits) { var getResult = server.GetProfile(item, item.ProfileName); var profile = (getResult != null) ? ServerSupport.DeepCopy(getResult.Profile) : server.PendingProfile; var priority = (getResult != null && getResult.Priority > 0) ? getResult.Priority : item.Priority; if (item.Profile == null || item.Profile.Name != profile.Name || item.Profile.LastUpdate != profile.LastUpdate || item.Priority != priority) { // 変更 item.Profile = profile; item.Priority = priority; // ハッシュリスト取得 if (profile != server.PendingProfile && // ペンディングの場合は決定したときに実行される item.IsSeparateHashRequired) { var hashpath = Path.GetDirectoryName(item.SrcPath) + ".hash"; if (hashCache.ContainsKey(hashpath) == false) { if (File.Exists(hashpath) == false) { item.State = QueueState.LogoPending; item.FailReason = "ハッシュファイルがありません: " + hashpath; return(true); } else { try { hashCache.Add(hashpath, new DirHash() { DirPath = hashpath, HashDict = HashUtil.ReadHashFile(hashpath) }); } catch (IOException e) { item.State = QueueState.LogoPending; item.FailReason = "ハッシュファイルの読み込みに失敗: " + e.Message; return(true); } } } var cacheItem = hashCache[hashpath]; var filename = item.FileName; if (cacheItem.HashDict.ContainsKey(filename) == false) { item.State = QueueState.LogoPending; item.FailReason = "ハッシュファイルにこのファイルのハッシュがありません"; return(true); } item.Hash = cacheItem.HashDict[filename]; } server.ReScheduleQueue(); UpdateQueueItem(item, waits); waits?.Add(ClientQueueUpdate(new QueueUpdate() { Type = UpdateType.Add, Item = item })); return(true); } return(false); }
private void ResetStateItem(QueueItem item, List <Task> waits) { item.Reset(); UpdateQueueItem(item, waits); waits.Add(NotifyQueueItemUpdate(item)); }
public async Task AddQueue(AddQueueRequest req) { List <Task> waits = new List <Task>(); addQueueCanceled = false; // ユーザ操作でない場合はログを記録する bool enableLog = (req.Mode == ProcMode.AutoBatch); if (req.Outputs.Count == 0) { await server.NotifyError("出力が1つもありません", enableLog); return; } // 既に追加されているファイルは除外する // バッチのときは全ファイルが対象だが、バッチじゃなければバッチのみが対象 var ignores = req.IsBatch ? Queue : Queue.Where(t => t.IsBatch); var ignoreSet = new HashSet <string>( ignores.Where(item => item.IsActive) .Select(item => item.SrcPath)); var items = ((req.Targets != null) ? req.Targets : Directory.GetFiles(req.DirPath) .Where(s => { string lower = s.ToLower(); return(lower.EndsWith(".ts") || lower.EndsWith(".m2t")); }) .Select(f => new AddQueueItem() { Path = f })) .Where(f => !ignoreSet.Contains(f.Path)).ToList(); waits.Add(WriteLine("" + items.Count + "件を追加処理します")); var map = server.ServiceMap; var numItems = 0; var progress = 0; // TSファイル情報を読む foreach (var additem in items) { waits.Add(WriteLine("(" + (++progress) + "/" + items.Count + ") " + Path.GetFileName(additem.Path) + " を処理中")); using (var info = new TsInfo(amtcontext)) { var failReason = ""; var addItems = new List <QueueItem>(); if (await Task.Run(() => info.ReadFile(additem.Path)) == false) { failReason = "TS情報取得に失敗: " + amtcontext.GetError(); } else { failReason = "TSファイルに映像が見つかりませんでした"; var list = info.GetProgramList(); var videopids = new List <int>(); int numFiles = 0; for (int i = 0; i < list.Length; ++i) { var prog = list[i]; if (prog.HasVideo && videopids.Contains(prog.VideoPid) == false) { videopids.Add(prog.VideoPid); var serviceName = "不明"; var tsTime = DateTime.MinValue; if (info.HasServiceInfo) { var service = info.GetServiceList().Where(s => s.ServiceId == prog.ServiceId).FirstOrDefault(); if (service.ServiceId != 0) { serviceName = service.ServiceName; } tsTime = info.GetTime(); } var outname = Path.GetFileNameWithoutExtension(additem.Path); if (numFiles > 0) { outname += "-マルチ" + numFiles; } Debug.Print("解析完了: " + additem.Path); foreach (var outitem in req.Outputs) { var genre = prog.Content.Select(s => ServerSupport.GetGenre(s)).ToList(); var item = new QueueItem() { Id = nextItemId++, Mode = req.Mode, SrcPath = additem.Path, Hash = additem.Hash, DstPath = outitem.DstPath + "\\" + outname, ServiceId = prog.ServiceId, ImageWidth = prog.Width, ImageHeight = prog.Height, TsTime = tsTime, ServiceName = serviceName, EventName = prog.EventName, State = QueueState.LogoPending, Priority = outitem.Priority, AddTime = DateTime.Now, ProfileName = outitem.Profile, Genre = genre, Tags = new List <string>() }; if (item.IsOneSeg) { item.State = QueueState.PreFailed; item.FailReason = "映像が小さすぎます(" + prog.Width + "," + prog.Height + ")"; } else { // ロゴファイルを探す if (req.Mode != ProcMode.DrcsCheck && map.ContainsKey(item.ServiceId) == false) { // 新しいサービスを登録 waits.Add(server.AddService(new ServiceSettingElement() { ServiceId = item.ServiceId, ServiceName = item.ServiceName, LogoSettings = new List <LogoSetting>() })); } // 追加時バッチ if (string.IsNullOrEmpty(req.AddQueueBat) == false) { waits.Add(WriteLine("追加時バッチ起動")); using (var scriptExecuter = new UserScriptExecuter() { Server = server, Phase = ScriptPhase.OnAdd, ScriptPath = server.GetBatDirectoryPath() + "\\" + req.AddQueueBat, Item = item, Prog = prog, OnOutput = WriteTextBytes }) { process = scriptExecuter; await scriptExecuter.Execute(); process = null; } if (addQueueCanceled) { break; } } ++numFiles; } addItems.Add(item); } } } } if (addQueueCanceled) { break; } if (addItems.Count == 0) { // アイテムが1つもないときはエラー項目として追加 foreach (var outitem in req.Outputs) { bool isAuto = false; var profileName = ServerSupport.ParseProfileName(outitem.Profile, out isAuto); var profile = isAuto ? null : ServerSupport.DeepCopy(server.GetProfile(profileName)); var item = new QueueItem() { Id = nextItemId++, Mode = req.Mode, Profile = profile, SrcPath = additem.Path, Hash = additem.Hash, DstPath = "", ServiceId = -1, ImageWidth = -1, ImageHeight = -1, TsTime = DateTime.MinValue, ServiceName = "不明", State = QueueState.PreFailed, FailReason = failReason, AddTime = DateTime.Now, ProfileName = outitem.Profile, Tags = new List <string>() }; addItems.Add(item); } } // 1ソースファイルに対するaddはatomicに実行したいので、 // このループではawaitしないこと foreach (var item in addItems) { if (item.State != QueueState.PreFailed) { // プロファイルを設定 UpdateProfileItem(item, null); } // 追加 item.Order = Queue.Count; Queue.Add(item); // まずは内部だけで状態を更新 UpdateQueueItem(item, null); // 状態が決まったらクライアント側に追加通知 waits.Add(ClientQueueUpdate(new QueueUpdate() { Type = UpdateType.Add, Item = item })); } numItems += addItems.Count; UpdateProgress(); waits.Add(server.RequestState()); } if (addQueueCanceled) { break; } } if (addQueueCanceled) { waits.Add(WriteLine("キャンセルされました")); } waits.Add(WriteLine("" + numItems + "件追加しました")); if (addQueueCanceled == false && numItems == 0) { waits.Add(server.NotifyError( "エンコード対象ファイルがありませんでした。パス:" + req.DirPath, enableLog)); await Task.WhenAll(waits); return; } else { waits.Add(server.NotifyMessage("" + numItems + "件追加しました", false)); } if (req.Mode != ProcMode.AutoBatch) { // 最後に使った設定を記憶しておく server.LastUsedProfile = req.Outputs[0].Profile; server.AddOutPathHistory(req.Outputs[0].DstPath); server.LastAddQueueBat = req.AddQueueBat; waits.Add(server.RequestUIState()); } waits.Add(server.RequestFreeSpace()); await Task.WhenAll(waits); }
private static bool IsInConsistent(QueueItem item, int priority, int key) { return(item.Priority != priority || key != item.Profile.ReqResources[EncodePhase].Canonical()); }
// アイテムを1つだけ強制的に開始する public void ForceStart(QueueItem item) { EnsureNumWorkers(running.Count + parking.Count + 1); ActivateOneWorker(item); }
private async Task <LogItem> ProcessItem(EncodeServer server, QueueItem src) { DateTime now = DateTime.Now; if (File.Exists(src.Path) == false) { return(FailLogItem(src.Path, "入力ファイルが見つかりません", now, now)); } bool isMp4 = src.Path.ToLower().EndsWith(".mp4"); string dstpath = Path.Combine(encoded, Path.GetFileName(src.Path)); string srcpath = src.Path; string localsrc = null; string localdst = dstpath; // ハッシュがある(ネットワーク経由)の場合はローカルにコピー if (hashList != null) { localsrc = tmpBase + "-in" + Path.GetExtension(srcpath); string name = Path.GetFileName(srcpath); if (hashList.ContainsKey(name) == false) { return(FailLogItem(src.Path, "入力ファイルのハッシュがありません", now, now)); } byte[] hash = await HashUtil.CopyWithHash(srcpath, localsrc); var refhash = hashList[name]; if (hash.SequenceEqual(refhash) == false) { File.Delete(localsrc); return(FailLogItem(src.Path, "コピーしたファイルのハッシュが一致しません", now, now)); } srcpath = localsrc; localdst = tmpBase + "-out.mp4"; } string json = Path.Combine( Path.GetDirectoryName(localdst), Path.GetFileNameWithoutExtension(localdst)) + "-enc.json"; string logpath = Path.Combine( Path.GetDirectoryName(dstpath), Path.GetFileNameWithoutExtension(dstpath)) + "-enc.log"; string args = server.MakeAmatsukazeArgs(isMp4, srcpath, localdst, json); string exename = server.appData.setting.AmatsukazePath; Util.AddLog(id, "エンコード開始: " + src.Path); Util.AddLog(id, "Args: " + exename + " " + args); DateTime start = DateTime.Now; var psi = new ProcessStartInfo(exename, args) { UseShellExecute = false, WorkingDirectory = Directory.GetCurrentDirectory(), RedirectStandardError = true, RedirectStandardOutput = true, RedirectStandardInput = false, CreateNoWindow = true }; IntPtr affinityMask = new IntPtr((long)server.affinityCreator.GetMask(id)); Util.AddLog(id, "AffinityMask: " + affinityMask.ToInt64()); int exitCode = -1; logText.Clear(); try { using (var p = Process.Start(psi)) { // アフィニティを設定 p.ProcessorAffinity = affinityMask; p.PriorityClass = ProcessPriorityClass.BelowNormal; process = p; using (logWriter = File.Create(logpath)) { await Task.WhenAll( RedirectOut(server, p.StandardOutput.BaseStream), RedirectOut(server, p.StandardError.BaseStream), Task.Run(() => p.WaitForExit())); } exitCode = p.ExitCode; } } catch (Win32Exception w32e) { Util.AddLog(id, "Amatsukazeプロセス起動に失敗"); throw w32e; } finally { logWriter = null; process = null; } DateTime finish = DateTime.Now; if (hashList != null) { File.Delete(localsrc); } if (exitCode == 0) { // 成功ならログを整形したテキストに置き換える using (var fs = new StreamWriter(File.Create(logpath), Encoding.Default)) { foreach (var str in logText.TextLines) { fs.WriteLine(str); } } } // ログファイルを専用フォルダにコピー if (File.Exists(logpath)) { string logbase = server.GetLogFileBase(start); Directory.CreateDirectory(Path.GetDirectoryName(logbase)); string dstlog = logbase + ".txt"; File.Copy(logpath, dstlog); if (File.Exists(json)) { string dstjson = logbase + ".json"; File.Move(json, dstjson); json = dstjson; } } if (exitCode == 0) { // 成功 var log = LogFromJson(isMp4, json, start, finish); // ハッシュがある(ネットワーク経由)の場合はリモートにコピー if (hashList != null) { log.SrcPath = src.Path; string outbase = Path.GetDirectoryName(dstpath) + "\\" + Path.GetFileNameWithoutExtension(dstpath); for (int i = 0; i < log.OutPath.Count; ++i) { string outext = Path.GetExtension(log.OutPath[i]); string outpath = outbase + ((i == 0) ? outext : ("-" + i + outext)); var hash = await HashUtil.CopyWithHash(log.OutPath[i], outpath); string name = Path.GetFileName(outpath); HashUtil.AppendHash(Path.Combine(encoded, "_mp4.hash"), name, hash); File.Delete(log.OutPath[i]); log.OutPath[i] = outpath; } } return(log); } else { // 失敗 return(FailLogItem(src.Path, "Amatsukaze.exeはコード" + exitCode + "で終了しました。", start, finish)); } }