// -------------------------------------------------------------------- // 検出ファイルリストテーブルにファイル情報を追加 // AddFileNames() で追加されない情報をすべて付与する // ファイルは再帰検索しない // -------------------------------------------------------------------- private static void AddInfosCore(TargetFolderInfo targetFolderInfo) { // フォルダー設定を読み込む FolderSettingsInDisk folderSettingsInDisk = YlCommon.LoadFolderSettings(targetFolderInfo.TargetPath); FolderSettingsInMemory folderSettingsInMemory = YlCommon.CreateFolderSettingsInMemory(folderSettingsInDisk); using ListContextInMemory listContextInMemory = new(); using TFoundSetter foundSetter = new(listContextInMemory); // 指定フォルダーの全レコード IQueryable <TFound> targetRecords = listContextInMemory.Founds.Where(x => x.Folder == targetFolderInfo.TargetPath); // 情報付与 foreach (TFound record in targetRecords) { FileInfo fileInfo = new(record.Path); record.LastWriteTime = JulianDay.DateTimeToModifiedJulianDate(fileInfo.LastWriteTime); record.FileSize = fileInfo.Length; foundSetter.SetTFoundValues(record, folderSettingsInMemory); YlModel.Instance.EnvModel.AppCancellationTokenSource.Token.ThrowIfCancellationRequested(); } AddFolderTagsInfo(targetFolderInfo, targetRecords, listContextInMemory.Tags, listContextInMemory.TagSequences); // コミット listContextInMemory.SaveChanges(); }
// ==================================================================== // private 関数 // ==================================================================== // -------------------------------------------------------------------- // request.db の 1 レコードを統計に追加 // -------------------------------------------------------------------- private async Task AddYukariRequest(DbSet <TYukariStatistics> yukariStatistics, TYukariRequest yukariRequest, DbSet <TFound> founds) { try { Debug.Assert(MainWindowViewModel != null, "AddYukariRequest() MainWindowViewModel is null"); await YlCommon.InputIdPrefixIfNeededWithInvoke(MainWindowViewModel); } catch (Exception) { // OperationCanceledException を通常の例外に変換 throw new Exception("ID 接頭辞が設定されていません。"); } Debug.Assert(YlModel.Instance.EnvModel.YlSettings.IdPrefix != null, "AddYukariRequest() IdPrefix is null"); TYukariStatistics yukariStatisticsRecord = new() { Id = YlModel.Instance.EnvModel.YlSettings.PrepareYukariStatisticsLastId(yukariStatistics), Dirty = true, RequestDatabasePath = YlModel.Instance.EnvModel.YlSettings.YukariRequestDatabasePath(), RequestTime = YukariRequestContext.LastWriteMjd(), RoomName = YlModel.Instance.EnvModel.YlSettings.YukariRoomName, //IdPrefix = YukaListerModel.Instance.EnvModel.YlSettings.IdPrefix, }; CopyYukariRequestToYukariStatistics(yukariRequest, yukariStatisticsRecord); CopyFoundToYukariStatisticsIfNeeded(founds, yukariStatisticsRecord); yukariStatistics.Add(yukariStatisticsRecord); Debug.WriteLine("AddYukariRequest() 追加: " + yukariStatisticsRecord.RequestMoviePath); }
// ==================================================================== // private 関数 // ==================================================================== // -------------------------------------------------------------------- // カテゴリーテーブルのレコードを作成 // -------------------------------------------------------------------- private static TCategory CreateCategoryRecord(Int32 idNumber, String name, String?ruby = null) { String?normalizedName = YlCommon.NormalizeDbString(name); (String? normalizedRubyForMusicInfo, _, _) = YlCommon.NormalizeDbRubyForMusicInfo(ruby); if (String.IsNullOrEmpty(normalizedRubyForMusicInfo)) { normalizedRubyForMusicInfo = normalizedName; } (String? normalizedRubyForSearch, _, _) = YlCommon.NormalizeDbRubyForSearch(normalizedRubyForMusicInfo); return(new TCategory() { // IRcBase Id = YlConstants.MUSIC_INFO_SYSTEM_ID_PREFIX + YlConstants.MUSIC_INFO_ID_SECOND_PREFIXES[(Int32)MusicInfoTables.TCategory] + idNumber.ToString("D3"), Import = false, Invalid = false, UpdateTime = YlConstants.INVALID_MJD, Dirty = true, // IRcMaster Name = normalizedName, Ruby = normalizedRubyForMusicInfo, RubyForSearch = normalizedRubyForSearch, Keyword = null, KeywordRubyForSearch = null, }); }
// ==================================================================== // コンストラクター // ==================================================================== // -------------------------------------------------------------------- // メインコンストラクター // -------------------------------------------------------------------- public SyncDataIo() { // 最初にログの設定 YlCommon.SetLogWriterSyncDetail(_logWriterSyncDetail); _musicInfoContext = new(); _yukariStatisticsContext = new(); }
// -------------------------------------------------------------------- // 過去の統計を更新 // -------------------------------------------------------------------- private void UpdatePast() { Debug.WriteLine("UpdatePast()"); using YukariStatisticsContext yukariStatisticsContext = new(); IQueryable <TYukariStatistics> targetYukariStatistics = yukariStatisticsContext.YukariStatistics.Where(x => (UpdatePastYukariStatisticsKind == UpdatePastYukariStatisticsKind.All || !x.AttributesDone) && !x.Invalid); foreach (TYukariStatistics oneStatistics in targetYukariStatistics) { #if DEBUGz if (oneStatistics.RequestMoviePath == @"D:\TempD\TestNkl\TST9_楽曲情報編集\テストタイアップ1_テスト楽曲101_ファイル歌手1,初音ミク.mp4") { } #endif if (!File.Exists(oneStatistics.RequestMoviePath)) { continue; } String requestMovieFolder = Path.GetDirectoryName(oneStatistics.RequestMoviePath) ?? String.Empty; if (YlCommon.FindSettingsFolder(requestMovieFolder) == null) { continue; } Debug.WriteLine("UpdatePast() 設定:" + oneStatistics.RequestMoviePath); // TFound 下準備 FileInfo fileInfo = new(oneStatistics.RequestMoviePath); TFound found = new() { Path = oneStatistics.RequestMoviePath, Folder = requestMovieFolder, ParentFolder = requestMovieFolder, LastWriteTime = JulianDay.DateTimeToModifiedJulianDate(fileInfo.LastWriteTime), FileSize = fileInfo.Length, }; // フォルダー設定を読み込む FolderSettingsInDisk folderSettingsInDisk = YlCommon.LoadFolderSettings(found.Folder); FolderSettingsInMemory folderSettingsInMemory = YlCommon.CreateFolderSettingsInMemory(folderSettingsInDisk); using ListContextInMemory listContextInMemory = new(); using TFoundSetter foundSetter = new(listContextInMemory); // TFound 設定 foundSetter.SetTFoundValues(found, folderSettingsInMemory); // 統計設定 DbCommon.CopyFoundToYukariStatisticsIfAttributesPrepared(found, oneStatistics); } yukariStatisticsContext.SaveChanges(); UpdatePastYukariStatisticsKind = UpdatePastYukariStatisticsKind.None; }
// ==================================================================== // private 関数 // ==================================================================== // -------------------------------------------------------------------- // インポート元を解凍 // -------------------------------------------------------------------- private String Extract() { String tempFolder = YlCommon.TempPath() + "\\"; Directory.CreateDirectory(tempFolder); ZipFile.ExtractToDirectory(_importSrcPath, tempFolder); String[] files = Directory.GetFiles(tempFolder, "*", SearchOption.AllDirectories); if (files.Length == 0) { throw new Exception("ゆかりすたー情報ファイルにインポートできるデータが存在しません。"); } return(files[0]); }
// ==================================================================== // protected 関数 // ==================================================================== // -------------------------------------------------------------------- // エクスポート処理 // ワーカースレッドで実行される前提 // -------------------------------------------------------------------- protected override Task ImportExportByWorker(Object?_) { String tempExportPath = YlCommon.TempPath(); MusicInfoContextDefault musicInfoContextDefault = new(); musicInfoContextDefault.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; MusicInfoContextExport musicInfoContextExport = new(tempExportPath); musicInfoContextExport.CreateDatabase(); // コピー // peopleInMemory.AddRange(peopleInMusicInfo) のように DbSet 全体を追加すると、アプリ終了時にタスクが終了しないため、Where を挟む Description = "エクスポートしています..."; musicInfoContextExport.Songs.AddRange(musicInfoContextDefault.Songs.Where(x => true)); musicInfoContextExport.People.AddRange(musicInfoContextDefault.People.Where(x => true)); musicInfoContextExport.TieUps.AddRange(musicInfoContextDefault.TieUps.Where(x => true)); musicInfoContextExport.TieUpGroups.AddRange(musicInfoContextDefault.TieUpGroups.Where(x => true)); musicInfoContextExport.Makers.AddRange(musicInfoContextDefault.Makers.Where(x => true)); musicInfoContextExport.SongAliases.AddRange(musicInfoContextDefault.SongAliases.Where(x => true)); musicInfoContextExport.TieUpAliases.AddRange(musicInfoContextDefault.TieUpAliases.Where(x => true)); musicInfoContextExport.ArtistSequences.AddRange(musicInfoContextDefault.ArtistSequences.Where(x => true)); musicInfoContextExport.LyristSequences.AddRange(musicInfoContextDefault.LyristSequences.Where(x => true)); musicInfoContextExport.ComposerSequences.AddRange(musicInfoContextDefault.ComposerSequences.Where(x => true)); musicInfoContextExport.ArrangerSequences.AddRange(musicInfoContextDefault.ArrangerSequences.Where(x => true)); musicInfoContextExport.TieUpGroupSequences.AddRange(musicInfoContextDefault.TieUpGroupSequences.Where(x => true)); musicInfoContextExport.Tags.AddRange(musicInfoContextDefault.Tags.Where(x => true)); musicInfoContextExport.TagSequences.AddRange(musicInfoContextDefault.TagSequences.Where(x => true)); musicInfoContextExport.SaveChanges(); // 古いファイルを削除 try { File.Delete(_exportYukaListerPath); } catch (Exception) { } _abortCancellationTokenSource.Token.ThrowIfCancellationRequested(); // 出力 // データベースファイルをそのまま圧縮しようとするとプロセスが使用中というエラーになることがある(2 回に 1 回くらい)ため、 // いったんデータベースファイルをコピーしてから圧縮する Description = "保存しています..."; String tempFolder = YlCommon.TempPath(); Directory.CreateDirectory(tempFolder); File.Copy(tempExportPath, tempFolder + "\\" + FILE_NAME_EXPORT_MUSIC_INFO); ZipFile.CreateFromDirectory(tempFolder, _exportYukaListerPath, CompressionLevel.Optimal, false); _abortCancellationTokenSource.Token.ThrowIfCancellationRequested(); return(Task.CompletedTask); }
// -------------------------------------------------------------------- // キャッシュ DB からディスク DB へコピー // -------------------------------------------------------------------- private static void CacheToDiskCore(TargetFolderInfo targetFolderInfo) { try { using CacheContext cacheContext = new(YlCommon.DriveLetter(targetFolderInfo.TargetPath)); cacheContext.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; // QueryTrackingBehavior.NoTracking(または AsNoTracking())時、結果の内容を変更して使いたい時は IQueryable<T> で受けてはならない(インスタンスの内容が変更できない) // List<T> 等に変換すれば結果のインスタンス内容を変更できる(変更しても SaveChanges() の対象にはならないと思う) List <TFound> cacheRecords = cacheContext.Founds.Where(x => x.ParentFolder == targetFolderInfo.TargetPath).ToList(); if (!cacheRecords.Any()) { // キャッシュが見つからない場合、ドライブレター以外の部分で合致するか再度検索 // 誤検知しないようコロンも含めて検索するので、YlCommon.WithoutDriveLetter() は使用しない String withoutDriveLetterOne = targetFolderInfo.TargetPath[1..];
// ==================================================================== // protected 関数 // ==================================================================== // -------------------------------------------------------------------- // 入力値を確認する // <例外> Exception, OperationCanceledException // -------------------------------------------------------------------- protected override void CheckProperties() { // タイアップグループの場合、名前の重複は無条件で NG のため、基底より先にチェック String?normalizedName = YlCommon.NormalizeDbString(Name); if (!String.IsNullOrEmpty(normalizedName)) { (_, Int32 numDups) = GetSameNameRecordsCaseInsensitive(normalizedName); if (numDups > 0) { throw new Exception(_caption + "「" + normalizedName + "」は既に登録されています。\n同じ名前の" + _caption + "は登録できません。"); } } // 基底 base.CheckProperties(); }
// -------------------------------------------------------------------- // キャッシュ更新(追加) // records の ParentFolder はすべて同じ前提 // -------------------------------------------------------------------- public void UpdateCache(List <TFound> records) { String parentFolder = records.First().ParentFolder; // 追加しようとしているキャッシュと同じ親フォルダーの旧キャッシュ削除 IQueryable <TFound> removes = Founds.Where(x => x.ParentFolder == parentFolder); Founds.RemoveRange(removes); // 追加しようとしているキャッシュとドライブレターが異なるキャッシュ削除 removes = Founds.Where(x => !x.ParentFolder.Contains(YlCommon.DriveLetter(parentFolder))); Founds.RemoveRange(removes); SaveChanges(); // 新キャッシュ追加 foreach (TFound record in records) { // Uid を初期化して自動的に Uid を振ってもらうようにする record.Uid = 0; } Founds.AddRange(records); // キャッシュ管理テーブル更新 TCacheHeader?cacheHeader = CacheHeaders.FirstOrDefault(x => x.ParentFolder == parentFolder); Boolean needAdd = false; if (cacheHeader == null) { cacheHeader = new() { ParentFolder = parentFolder, }; needAdd = true; } cacheHeader.UpdateTime = YlCommon.UtcNowMjd(); if (needAdd) { CacheHeaders.Add(cacheHeader); } SaveChanges(); }
// ==================================================================== // public 関数 // ==================================================================== // -------------------------------------------------------------------- // ゆかり検索対象フォルダー追加 // 指定された親フォルダーのみを追加し、サブフォルダーは追加しない // <例外> Exception // -------------------------------------------------------------------- public async Task AddTargetFolderAsync(String parentFolder) { await Task.Run(() => { #if DEBUGz Thread.Sleep(2000); #endif // フォルダーチェック if (String.IsNullOrEmpty(parentFolder)) { throw new Exception("追加するフォルダーの名前が空です。"); } if (!Directory.Exists(parentFolder)) { throw new Exception("指定されたフォルダーが存在しません:" + parentFolder); } // 親の重複チェック Boolean parentAdded = IndexOfTargetFolderInfoWithLock(parentFolder) >= 0; if (parentAdded) { throw new Exception(parentFolder + " は既に追加されています。"); } // 親の追加 TargetFolderInfo targetFolderInfo = new(parentFolder); lock (_targetFolderInfos) { _targetFolderInfos.Add(targetFolderInfo); } // 通知 YlModel.Instance.EnvModel.IsMainWindowDataGridCountChanged = true; YlModel.Instance.EnvModel.Sifolin.MainEvent.Set(); SetLomolinTargetDrives(); // スリープ状態のデバイスだとここで時間がかかる AdjustAutoTargetInfoIfNeeded(YlCommon.DriveLetter(parentFolder)); }); }
// -------------------------------------------------------------------- // サブフォルダーも含めて FolderTaskDetail を Remove にする // parentFolder は IsParent である必要がある // -------------------------------------------------------------------- public Boolean SetFolderTaskDetailOfFolderToRemove(String parentFolder) { lock (_targetFolderInfos) { Int32 parentIndex = IndexOfTargetFolderInfoWithoutLock(parentFolder); if (parentIndex < 0) { return(false); } Debug.Assert(_targetFolderInfos[parentIndex].IsParent, "SetFolderTaskDetailToRemove() not parent"); for (Int32 i = parentIndex; i < parentIndex + _targetFolderInfos[parentIndex].NumTotalFolders; i++) { _targetFolderInfos[i].SetFolderTaskKind(FolderTaskKind.Remove); _targetFolderInfos[i].SetFolderTaskDetail(FolderTaskDetail.Remove); _targetFolderInfos[i].FolderTaskStatus = FolderTaskStatus.Queued; } } // 通知 YlModel.Instance.EnvModel.IsMainWindowDataGridItemUpdated = true; YlModel.Instance.EnvModel.Sifolin.MainEvent.Set(); AdjustAutoTargetInfoIfNeeded(YlCommon.DriveLetter(parentFolder)); return(true); }
// ==================================================================== // protected 関数 // ==================================================================== // -------------------------------------------------------------------- // ネビュラコア(検索データ作成)のメインルーチン // -------------------------------------------------------------------- protected override Task CoreMainAsync() { while (true) { MainEvent.WaitOne(); Int32 startTick = Environment.TickCount; try { YlModel.Instance.EnvModel.AppCancellationTokenSource.Token.ThrowIfCancellationRequested(); if (YlModel.Instance.ProjModel.UndoneTargetFolderInfo() == null) { continue; } YlModel.Instance.EnvModel.YukaListerPartsStatus[(Int32)YukaListerPartsStatusIndex.Sifolin] = YukaListerStatus.Running; YlModel.Instance.EnvModel.LogWriter.LogMessage(Common.TRACE_EVENT_TYPE_STATUS, GetType().Name + " アクティブ化。"); if (YlModel.Instance.EnvModel.YlSettings.ApplyMusicInfoIntelligently) { YlModel.Instance.EnvModel.LogWriter.LogMessage(Common.TRACE_EVENT_TYPE_STATUS, "楽曲情報データベースが不十分な場合の誤適用を軽減する設定。"); } MusicInfoDatabaseToMemory(); _prevFolderTaskDetail = FolderTaskDetail.Done; while (true) { TargetFolderInfo?targetFolderInfo; // 削除 targetFolderInfo = YlModel.Instance.ProjModel.FindTargetFolderInfo(FolderTaskDetail.Remove); if (targetFolderInfo != null) { Remove(targetFolderInfo); continue; } // キャッシュ活用(全体の動作状況がエラーではない場合のみ) if (YlModel.Instance.EnvModel.YukaListerWholeStatus != YukaListerStatus.Error) { targetFolderInfo = YlModel.Instance.ProjModel.FindTargetFolderInfo(FolderTaskDetail.CacheToDisk); if (targetFolderInfo != null) { CacheToDisk(targetFolderInfo); continue; } } // すべてのフォルダーのキャッシュ活用が終わったら Yurelin をアクティブ化する // 起動直後に前回のゆかり予約を解析することを想定している if (_prevFolderTaskDetail == FolderTaskDetail.CacheToDisk) { Debug.WriteLine("Sifolin.CoreMain() キャッシュ活用後の Yurelin アクティブ化"); YlCommon.ActivateYurelinIfNeeded(); } // 更新削除 targetFolderInfo = YlModel.Instance.ProjModel.FindTargetFolderInfo(FolderTaskDetail.UpdateRemove); if (targetFolderInfo != null) { UpdateRemove(targetFolderInfo); continue; } // 更新サブフォルダー検索 targetFolderInfo = YlModel.Instance.ProjModel.FindTargetFolderInfo(FolderTaskDetail.UpdateFindSubFolders); if (targetFolderInfo != null) { UpdateFindSubFolders(targetFolderInfo); continue; } // サブフォルダー検索 targetFolderInfo = YlModel.Instance.ProjModel.FindTargetFolderInfo(FolderTaskDetail.FindSubFolders); if (targetFolderInfo != null) { FindSubFolders(targetFolderInfo); continue; } // ファイル名追加 targetFolderInfo = YlModel.Instance.ProjModel.FindTargetFolderInfo(FolderTaskDetail.AddFileNames); if (targetFolderInfo != null) { AddFileNames(targetFolderInfo); continue; } // ファイル情報追加 targetFolderInfo = YlModel.Instance.ProjModel.FindTargetFolderInfo(FolderTaskDetail.AddInfos); if (targetFolderInfo != null) { AddInfos(targetFolderInfo); continue; } // メモリー DB → ディスク DB(全体の動作状況がエラーではない場合のみ) if (_needsMemoryDbToDiskDb && YlModel.Instance.EnvModel.YukaListerWholeStatus != YukaListerStatus.Error) { MemoryToDisk(); _needsMemoryDbToDiskDb = false; continue; } // メモリー DB → キャッシュ DB if (_needsMemoryDbToCacheDb) { MemoryToCache(); _needsMemoryDbToCacheDb = false; continue; } // Kamlin アクティブ化 YlCommon.ActivateKamlinIfNeeded(); // Yurelin アクティブ化 YlCommon.ActivateYurelinIfNeeded(); // やることが無くなったのでループを抜けて待機へ向かう break; } } catch (OperationCanceledException) { return(Task.CompletedTask); } catch (Exception ex) { YlModel.Instance.EnvModel.NebulaCoreErrors.Enqueue(GetType().Name + " ループ稼働時エラー:\n" + ex.Message); YlModel.Instance.EnvModel.LogWriter.LogMessage(Common.TRACE_EVENT_TYPE_STATUS, " スタックトレース:\n" + ex.StackTrace); } // 念のため最後に表示を更新 YlModel.Instance.EnvModel.YukaListerPartsStatus[(Int32)YukaListerPartsStatusIndex.Sifolin] = YukaListerStatus.Ready; YlModel.Instance.EnvModel.IsMainWindowDataGridCountChanged = true; TimeSpan timeSpan = new(YlCommon.MiliToHNano(Environment.TickCount - startTick)); YlModel.Instance.EnvModel.LogWriter.LogMessage(Common.TRACE_EVENT_TYPE_STATUS, GetType().Name + " スリープ化:アクティブ時間:" + timeSpan.ToString(@"hh\:mm\:ss")); } }
// -------------------------------------------------------------------- // 稼働開始 // -------------------------------------------------------------------- public void Start() { // async を待機しない _ = YlCommon.LaunchTaskAsync <Object?>(_semaphoreSlim, WebServerByWorker, null, "Web サーバー"); }
// -------------------------------------------------------------------- // 指定フォルダ内のファイルを検索してゆかり用データベースに追加 // ユニーク ID、フルパス、フォルダーのみ記入する // ファイルは再帰検索しない // -------------------------------------------------------------------- private void AddFileNamesCore(TargetFolderInfo targetFolderInfo) { // フォルダー除外設定を読み込む if (YlCommon.DetectFolderExcludeSettingsStatus(targetFolderInfo.TargetPath) == FolderExcludeSettingsStatus.True) { return; } // Uid using ListContextInMemory listContextInMemory = new(); Int64 uid = listContextInMemory.Founds.Any() ? listContextInMemory.Founds.Max(x => x.Uid) + 1 : 1; // キャッシュが使われていない場合はディスク DB の Uid とも重複しないようにする(全体の動作状況がエラーではない場合のみ) if (YlModel.Instance.EnvModel.YukaListerWholeStatus != YukaListerStatus.Error && !targetFolderInfo.IsCacheUsed) { using ListContextInDisk listContextInDisk = new(); if (listContextInDisk.Founds.Any()) { uid = Math.Max(uid, listContextInDisk.Founds.Max(x => x.Uid) + 1); } } // 検索 String[] allPathes; try { allPathes = Directory.GetFiles(targetFolderInfo.TargetPath); } catch (Exception) { return; } // 追加準備 List <TFound> addRecords = new(); addRecords.Capacity = allPathes.Length; foreach (String path in allPathes) { if (!YlModel.Instance.EnvModel.YlSettings.TargetExts.Contains(Path.GetExtension(path).ToLower())) { continue; } TFound record = new(); record.Uid = uid; record.Path = path; record.Folder = Path.GetDirectoryName(path) ?? String.Empty; record.ParentFolder = targetFolderInfo.ParentPath; // 楽曲名とファイルサイズが両方とも初期値だと、ゆかりが検索結果をまとめてしまうため、ダミーのファイルサイズを入れる // (文字列である楽曲名を入れると処理が遅くなるので処理が遅くなりにくい数字のファイルサイズをユニークにする) record.FileSize = -uid; addRecords.Add(record); uid++; } // メモリー DB に追加 listContextInMemory.Founds.AddRange(addRecords); listContextInMemory.SaveChanges(); _needsMemoryDbToDiskDb = true; _needsMemoryDbToCacheDb = true; // キャッシュが使われていない場合はディスク DB にも追加(全体の動作状況がエラーではない場合のみ) if (YlModel.Instance.EnvModel.YukaListerWholeStatus != YukaListerStatus.Error && !targetFolderInfo.IsCacheUsed) { using ListContextInDisk listContextInDisk = new(); listContextInDisk.Founds.AddRange(addRecords); listContextInDisk.SaveChanges(); YlModel.Instance.EnvModel.LogWriter.LogMessage(Common.TRACE_EVENT_TYPE_STATUS, "ゆかり用リストデータベースにファイル名を追加しました。" + targetFolderInfo.TargetPath); } }
// ==================================================================== // protected 関数 // ==================================================================== // -------------------------------------------------------------------- // ネビュラコア(同期)のメインルーチン // -------------------------------------------------------------------- protected override async Task CoreMainAsync() { #if DEBUGz Debug.WriteLine("priority before: " + Thread.CurrentThread.Priority.ToString()); Thread.CurrentThread.Priority = ThreadPriority.Lowest; Debug.WriteLine("priority after: " + Thread.CurrentThread.Priority.ToString()); #endif YlCommon.SetLogWriterSyncDetail(_logWriterSyncDetail); while (true) { MainEvent.WaitOne(); Debug.Assert(MainWindowViewModel != null, "Syclin.CoreMain() MainWindowViewModel is null"); Int32 startTick = Environment.TickCount; try { YlModel.Instance.EnvModel.AppCancellationTokenSource.Token.ThrowIfCancellationRequested(); if (YlModel.Instance.EnvModel.YukaListerWholeStatus == YukaListerStatus.Error) { continue; } if (!YlModel.Instance.EnvModel.YlSettings.SyncMusicInfoDb) { continue; } IsActive = true; YlModel.Instance.EnvModel.LogWriter.LogMessage(Common.TRACE_EVENT_TYPE_STATUS, GetType().Name + " アクティブ化。"); // ログイン MainWindowViewModel.SetStatusBarMessageWithInvoke(Common.TRACE_EVENT_TYPE_STATUS, "同期準備中..."); await LoginToSyncServerAsync(); // データベースをバックアップ using MusicInfoContextDefault musicInfoContextDefault = new(); musicInfoContextDefault.BackupDatabase(); using YukariStatisticsContext yukariStatisticsContext = new(); yukariStatisticsContext.BackupDatabase(); // 再取得の場合は楽曲情報データベース初期化 CreateDatabaseIfNeeded(musicInfoContextDefault, yukariStatisticsContext); // ダウンロード (Int32 numTotalDownloads, Int32 numTotalImports) = await DownloadSyncDataAsync(); // アップロード Int32 numTotalUploads = await UploadSyncDataAsync(); if (numTotalUploads > 0) { // アップロードを行った場合は、自身がアップロードしたデータの更新日・Dirty を更新するために再ダウンロードが必要 (numTotalDownloads, numTotalImports) = await DownloadSyncDataAsync(); } // 完了表示 MainWindowViewModel.SetStatusBarMessageWithInvoke(Common.TRACE_EVENT_TYPE_STATUS, "データベース同期完了(ダウンロード" + (numTotalDownloads == 0 ? "無" : " " + numTotalDownloads.ToString("#,0") + " 件、うち " + numTotalImports.ToString("#,0") + " 件インポート") + "、アップロード" + (numTotalUploads == 0 ? "無" : " " + numTotalUploads.ToString("#,0") + " 件") + ")"); // 起動直後は Syclin と Yurelin が両方起動され、Syclin による属性未確認データのアップロード&ダウンロードと、Yurelin による属性確認が競合し、属性未確認の状態に戻ることがある // Syclin スリープ化直前に Yurelin を起動し、再度属性確認が行われるようにする YlCommon.ActivateYurelinIfNeeded(); } catch (OperationCanceledException) { return; } catch (Exception excep) { DbCommon.LogDatabaseExceptionIfCan(excep); // メッセージボックスではなくステータスバーにエラー表示 MainWindowViewModel.SetStatusBarMessageWithInvoke(TraceEventType.Error, excep.Message); YlModel.Instance.EnvModel.LogWriter.LogMessage(Common.TRACE_EVENT_TYPE_STATUS, " スタックトレース:\n" + excep.StackTrace); } IsActive = false; TimeSpan timeSpan = new(YlCommon.MiliToHNano(Environment.TickCount - startTick)); YlModel.Instance.EnvModel.LogWriter.LogMessage(Common.TRACE_EVENT_TYPE_STATUS, GetType().Name + " スリープ化:アクティブ時間:" + timeSpan.ToString(@"hh\:mm\:ss")); } }
// -------------------------------------------------------------------- // フォルダー設定で指定されているタグを TFound とゆかり用リストデータベースに付与する // -------------------------------------------------------------------- private static void AddFolderTagsInfo(TargetFolderInfo targetFolderInfo, IQueryable <TFound> records, DbSet <TTag> tags, DbSet <TTagSequence> tagSequences) { try { String tagKey = YlCommon.WithoutDriveLetter(targetFolderInfo.TargetPath); if (!YlModel.Instance.EnvModel.TagSettings.FolderTags.ContainsKey(tagKey)) { return; } // TTag にフォルダー設定のタグ情報と同名のタグがあるか? String tagValue = YlModel.Instance.EnvModel.TagSettings.FolderTags[tagKey]; TTag? tagRecord = DbCommon.SelectMasterByName(tags, tagValue); if (tagRecord == null) { // 同名のタグが無いので、tagKey を Id とするタグがまだ存在しなければ作成 String tagId = YlConstants.TEMP_ID_PREFIX + tagKey; tagRecord = DbCommon.SelectBaseById(tags, tagId); if (tagRecord == null) { tagRecord = new() { // IRcBase Id = tagId, Import = false, Invalid = false, UpdateTime = YlConstants.INVALID_MJD, Dirty = true, // IRcMaster Name = tagValue, Ruby = null, Keyword = null, }; tags.Add(tagRecord); } } Dictionary <String, Boolean> addedIds = new(); foreach (TFound record in records) { // TFound にタグ情報を追加 // 楽曲情報データベースで付与されたものと同じ場合は重複連結となるが、ゆかりが検索するためのものなので問題ない record.TagName += "," + tagRecord.Name; if (!String.IsNullOrEmpty(tagRecord.Ruby)) { record.TagRuby += "," + tagRecord.Ruby; } // ゆかり用リストデータベースの TTagSequence にタグ情報を追加 // 1 つのフォルダー内に同じ曲が複数個存在する場合があるので、既に作業済みの曲はスキップ if (String.IsNullOrEmpty(record.SongId) || addedIds.ContainsKey(record.SongId)) { continue; } // TTagSequence にフォルダー設定のタグ情報が無ければ保存 List <TTag> songTags = DbCommon.SelectSequencedTagsBySongId(tagSequences, tags, record.SongId); if (songTags.FirstOrDefault(x => x.Name == tagRecord.Name) == null) { IQueryable <Int32> sequenceResults = tagSequences.Where(x => x.Id == record.SongId).Select(x => x.Sequence); Int32 seqMax = sequenceResults.Any() ? sequenceResults.Max() : -1; TTagSequence tagSequenceRecord = new() { // IDbBase Id = record.SongId, Import = false, Invalid = false, UpdateTime = YlConstants.INVALID_MJD, Dirty = true, // IDbSequence Sequence = seqMax + 1, LinkId = tagRecord.Id, }; tagSequences.Add(tagSequenceRecord); addedIds[record.SongId] = true; } } } catch (Exception ex) { YlModel.Instance.EnvModel.NebulaCoreErrors.Enqueue("フォルダー設定タグ付与時エラー:\n" + ex.Message); YlModel.Instance.EnvModel.LogWriter.LogMessage(Common.TRACE_EVENT_TYPE_STATUS, " スタックトレース:\n" + ex.StackTrace); } }
// ==================================================================== // protected 関数 // ==================================================================== // -------------------------------------------------------------------- // ネビュラコア(動画リスト作成)のメインルーチン // -------------------------------------------------------------------- protected override async Task CoreMainAsync() { // リスト出力時の処理が重いことによるトラブルの可能性がゼロではないため、プライオリティーを下げる(チケット #97) Thread.CurrentThread.Priority = ThreadPriority.Lowest; while (true) { MainEvent.WaitOne(); Debug.Assert(MainWindowViewModel != null, "Kamlin.CoreMain() MainWindowViewModel is null"); Int32 startTick = Environment.TickCount; try { YlModel.Instance.EnvModel.AppCancellationTokenSource.Token.ThrowIfCancellationRequested(); // 全体の動作状況がエラーの場合はリスト作成しない if (YlModel.Instance.EnvModel.YukaListerWholeStatus == YukaListerStatus.Error) { continue; } // リストデータベースのレコード数が 0 ならリスト作成不要 using ListContextInDisk listContextInDisk = new(); if (!listContextInDisk.Founds.Any()) { continue; } YlModel.Instance.EnvModel.YukaListerPartsStatus[(Int32)YukaListerPartsStatusIndex.Kamlin] = YukaListerStatus.Running; YlModel.Instance.EnvModel.LogWriter.LogMessage(Common.TRACE_EVENT_TYPE_STATUS, GetType().Name + " アクティブ化。"); // 問題報告用に ID 接頭辞が必要 try { await YlCommon.InputIdPrefixIfNeededWithInvoke(MainWindowViewModel); } catch (Exception) { // OperationCanceledException を通常の例外に変換 throw new Exception("ID 接頭辞が設定されていません。"); } // リスト出力 YukariOutputWriter yukariOutputWriter = new(); yukariOutputWriter.Output(); YlModel.Instance.EnvModel.LogWriter.LogMessage(TraceEventType.Information, "リスト出力が完了しました。"); } catch (OperationCanceledException) { return; } catch (Exception ex) { YlModel.Instance.EnvModel.NebulaCoreErrors.Enqueue(GetType().Name + " ループ稼働時エラー:\n" + ex.Message); YlModel.Instance.EnvModel.LogWriter.LogMessage(Common.TRACE_EVENT_TYPE_STATUS, " スタックトレース:\n" + ex.StackTrace); } YlModel.Instance.EnvModel.YukaListerPartsStatus[(Int32)YukaListerPartsStatusIndex.Kamlin] = YukaListerStatus.Ready; TimeSpan timeSpan = new(YlCommon.MiliToHNano(Environment.TickCount - startTick)); YlModel.Instance.EnvModel.LogWriter.LogMessage(Common.TRACE_EVENT_TYPE_STATUS, GetType().Name + " スリープ化:アクティブ時間:" + timeSpan.ToString(@"hh\:mm\:ss")); } }
// ==================================================================== // protected 関数 // ==================================================================== // -------------------------------------------------------------------- // ネビュラコア(統計データ作成)のメインルーチン // -------------------------------------------------------------------- protected override async Task CoreMainAsync() { // 急ぎではないのでプライオリティーを下げる Thread.CurrentThread.Priority = ThreadPriority.Lowest; while (true) { MainEvent.WaitOne(); Int32 startTick = Environment.TickCount; try { YlModel.Instance.EnvModel.AppCancellationTokenSource.Token.ThrowIfCancellationRequested(); if (YlModel.Instance.EnvModel.YukaListerWholeStatus == YukaListerStatus.Error) { continue; } if (!YlModel.Instance.EnvModel.YlSettings.IsYukariRequestDatabaseValid()) { continue; } YlModel.Instance.EnvModel.LogWriter.LogMessage(Common.TRACE_EVENT_TYPE_STATUS, GetType().Name + " アクティブ化。"); // データベースアクセス準備 using YukariStatisticsContext yukariStatisticsContext = new(); using YukariRequestContext yukariRequestContext = new(); yukariRequestContext.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; using ListContextInDisk listContextInDisk = new(); listContextInDisk.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; UpdateLastYukariRequestClearTimeIfNeeded(yukariRequestContext.YukariRequests); // request.db にある予約の統計更新 await AnalyzeYukariRequests(yukariStatisticsContext.YukariStatistics, yukariRequestContext.YukariRequests, listContextInDisk.Founds); #if DEBUG Boolean hasChangesBak = yukariStatisticsContext.ChangeTracker.HasChanges(); Double lastWriteTimeBak = yukariStatisticsContext.LastWriteMjd(); #endif if (yukariStatisticsContext.ChangeTracker.HasChanges()) { yukariStatisticsContext.BackupDatabase(); } yukariStatisticsContext.SaveChanges(); #if DEBUG Debug.WriteLine("Yurelin.CoreMain() 更新フラグ " + hasChangesBak.ToString()); Debug.WriteLine("Yurelin.CoreMain() 実際のファイル更新 " + (lastWriteTimeBak == yukariStatisticsContext.LastWriteMjd() ? "なし" : "有り")); Debug.Assert(hasChangesBak == (lastWriteTimeBak != yukariStatisticsContext.LastWriteMjd()), "Yurelin.CoreMain() フラグが実際と異なった Bak: " + JulianDay.ModifiedJulianDateToDateTime(lastWriteTimeBak).ToString(YlConstants.TIME_FORMAT) + ", 現在: " + JulianDay.ModifiedJulianDateToDateTime(yukariStatisticsContext.LastWriteMjd()).ToString(YlConstants.TIME_FORMAT)); #endif if (UpdatePastYukariStatisticsKind != UpdatePastYukariStatisticsKind.None) { // 過去の統計(request.db にないものを含む)更新 UpdatePast(); } } catch (OperationCanceledException) { return; } catch (Exception ex) { YlModel.Instance.EnvModel.NebulaCoreErrors.Enqueue(GetType().Name + " ループ稼働時エラー:\n" + ex.Message); YlModel.Instance.EnvModel.LogWriter.LogMessage(Common.TRACE_EVENT_TYPE_STATUS, " スタックトレース:\n" + ex.StackTrace); } TimeSpan timeSpan = new(YlCommon.MiliToHNano(Environment.TickCount - startTick)); YlModel.Instance.EnvModel.LogWriter.LogMessage(Common.TRACE_EVENT_TYPE_STATUS, GetType().Name + " スリープ化:アクティブ時間:" + timeSpan.ToString(@"hh\:mm\:ss")); } }
// -------------------------------------------------------------------- // データベースのフルパス // -------------------------------------------------------------------- public override String DatabasePath() { return(YlCommon.YukaListerStatusFolderPath(_driveLetter, true) + FILE_NAME_CACHE_DATABASE); }
// ==================================================================== // コンストラクター // ==================================================================== // -------------------------------------------------------------------- // メインコンストラクター // -------------------------------------------------------------------- public FolderSettingsInMemory() { FolderNameRules = YlCommon.CreateRuleDictionary(); }