public IActionResult GetStocks()
        {
            try
            {
                using UserProfileContext userProfileContext = CreateUserProfileContext(out DbSet <RegisteredUser> registeredUsers, out DbSet <StockSong> stockSongs, out _);
                if (!IsTokenValid(registeredUsers, out RegisteredUser? loginUser))
                {
                    return(Unauthorized());
                }

                // キャッシュチェック
                DateTime lastModified = ServerCommon.LastModified(ServerConstants.FILE_NAME_USER_PROFILES);
                if (IsEntityTagValid(YbdCommon.DateTimeToModifiedJulianDate(lastModified)))
                {
                    Debug.WriteLine("GetStocks() キャッシュ有効: ");
                    return(NotModified());
                }

                StockSong[]          results = stockSongs.Where(x => x.UserId == loginUser.Id).OrderByDescending(x => x.RequestTime).ToArray();
                EntityTagHeaderValue eTag    = GenerateEntityTag(YbdCommon.DateTimeToModifiedJulianDate(lastModified));
                return(File(JsonSerializer.SerializeToUtf8Bytes(results), ServerConstants.MIME_TYPE_JSON, lastModified, eTag));
            }
            catch (Exception excep)
            {
                Debug.WriteLine("後で歌う予定リスト取得サーバーエラー:\n" + excep.Message);
                Debug.WriteLine(" スタックトレース:\n" + excep.StackTrace);
                return(InternalServerError());
            }
        }
        public IActionResult SetThumbnail([FromBody] TransferFile?transferFile)
        {
            try
            {
                using UserProfileContext userProfileContext = CreateUserProfileContext(out DbSet <RegisteredUser> registeredUsers, out _, out _);
                if (!IsTokenValid(registeredUsers, out RegisteredUser? loginUser))
                {
                    return(Unauthorized());
                }
                if (transferFile == null)
                {
                    return(BadRequest());
                }

                // 設定
                using MemoryStream memoryStream = new MemoryStream(transferFile.Content);
                loginUser.Bitmap       = ServerCommon.CreateThumbnail(memoryStream, transferFile.Mime, YbdConstants.USER_THUMBNAIL_WIDTH_MAX, YbdConstants.USER_THUMBNAIL_HEIGHT_MAX, true);
                loginUser.Mime         = transferFile.Mime;
                loginUser.LastModified = YbdCommon.UtcNowModifiedJulianDate();
                userProfileContext.SaveChanges();

                return(Ok());
            }
            catch (Exception excep)
            {
                Debug.WriteLine("プロフィール画像設定サーバーエラー:\n" + excep.Message);
                Debug.WriteLine(" スタックトレース:\n" + excep.StackTrace);
                return(InternalServerError());
            }
        }
Exemple #3
0
        public IActionResult GetUserNames()
        {
            try
            {
                // キャッシュチェック
                DateTime lastModified = ServerCommon.LastModified(ServerConstants.FILE_NAME_REQUEST_SONGS);
                if (IsEntityTagValid(YbdCommon.DateTimeToModifiedJulianDate(lastModified)))
                {
                    Debug.WriteLine("GetUserNames() キャッシュ有効: ");
                    return(NotModified());
                }

                using RequestSongContext requestSongContext = CreateRequestSongContext(out DbSet <RequestSong> requestSongs);
                String[] results = requestSongs.Where(x => x.UserId == String.Empty).Select(x => x.UserName).GroupBy(y => y).Select(z => z.Key).ToArray();

                // 追加ヘッダー
                AddTotalCountToHeader(results.Length);

                // 予約者名一覧
                EntityTagHeaderValue eTag = GenerateEntityTag(YbdCommon.DateTimeToModifiedJulianDate(lastModified));
                return(File(JsonSerializer.SerializeToUtf8Bytes(results), ServerConstants.MIME_TYPE_JSON, lastModified, eTag));
            }
            catch (Exception excep)
            {
                Debug.WriteLine("予約者名一覧取得サーバーエラー:\n" + excep.Message);
                Debug.WriteLine(" スタックトレース:\n" + excep.StackTrace);
                return(InternalServerError());
            }
        }
Exemple #4
0
        public IActionResult GetRequestSongs(String?query)
        {
            try
            {
                // キャッシュチェック
                DateTime lastModified = ServerCommon.LastModified(ServerConstants.FILE_NAME_REQUEST_SONGS);
                if (IsEntityTagValid(YbdCommon.DateTimeToModifiedJulianDate(lastModified)))
                {
                    Debug.WriteLine("GetRequestSongs() キャッシュ有効: " + query);
                    return(NotModified());
                }

                using RequestSongContext requestSongContext = CreateRequestSongContext(out DbSet <RequestSong> requestSongs);

                // 追加ヘッダー
                AddTotalCountToHeader(requestSongs.Count());

                // 予約一覧
                Dictionary <String, String> parameters = YbdCommon.AnalyzeQuery(query);
                Int32                page    = YbdCommon.GetPageFromQueryParameters(parameters);
                RequestSong[]        results = requestSongs.OrderByDescending(x => x.Sort).Skip(YbdConstants.PAGE_SIZE * page).Take(YbdConstants.PAGE_SIZE).ToArray();
                EntityTagHeaderValue eTag    = GenerateEntityTag(YbdCommon.DateTimeToModifiedJulianDate(lastModified));
                return(File(JsonSerializer.SerializeToUtf8Bytes(results), ServerConstants.MIME_TYPE_JSON, lastModified, eTag));
            }
            catch (Exception excep)
            {
                Debug.WriteLine("予約一覧取得サーバーエラー:\n" + excep.Message);
                Debug.WriteLine(" スタックトレース:\n" + excep.StackTrace);
                return(InternalServerError());
            }
        }
Exemple #5
0
        // --------------------------------------------------------------------
        // タイアップ名検索
        // --------------------------------------------------------------------
        private static IQueryable <AvailableSong> SearchByTieUpName(IQueryable <AvailableSong> records, String word)
        {
            Boolean isRuby = YbdCommon.IsRuby(word, out String ruby);

            return(records.Where(x => EF.Functions.Like(x.TieUpName, $"%{word}%") ||
                                 isRuby && EF.Functions.Like(x.TieUpRuby, $"%{ruby}%")));
        }
        public IActionResult AddUser([FromBody] LoginInfo registerInfo)
        {
            try
            {
                if (!registerInfo.IsValid())
                {
                    return(BadRequest());
                }

                using UserProfileContext userProfileContext = CreateUserProfileContext(out DbSet <RegisteredUser> registeredUsers, out _, out _);
                RegisteredUser newUser = new();
                newUser.Name         = registerInfo.Name;
                newUser.Password     = registerInfo.Password;
                newUser.LastModified = newUser.LastLogin = YbdCommon.UtcNowModifiedJulianDate();

                if (!IsAdminRegistered(registeredUsers))
                {
                    // 管理者未登録の場合は管理者登録でなければならない
                    if (newUser.Name != YbdConstants.ADMIN_NAME)
                    {
                        return(BadRequest());
                    }
                    newUser.IsAdmin = true;
                }

                // 同じ名前のユーザーが既に存在している場合は登録できない
                if (registeredUsers.FirstOrDefault(x => x.Name == newUser.Name) != null)
                {
                    return(Conflict());
                }

                // 登録
                HashPassword(newUser);
                registeredUsers.Add(newUser);
                userProfileContext.SaveChanges();

                String idAndToken = GenerateIdAndTokenString(newUser.Id);
                Debug.WriteLine("AddUser() " + idAndToken);

                // 登録と同時にログインできるように ID とログイン用トークンを返す
                return(Ok(idAndToken));
            }
            catch (Exception excep)
            {
                Debug.WriteLine("ユーザー登録サーバーエラー:\n" + excep.Message);
                Debug.WriteLine(" スタックトレース:\n" + excep.StackTrace);
                return(InternalServerError());
            }
        }
Exemple #7
0
        // ====================================================================
        // private static メンバー関数
        // ====================================================================

        // --------------------------------------------------------------------
        // AnyWord 検索
        // --------------------------------------------------------------------
        private static IQueryable <AvailableSong> SearchByAnyWord(IQueryable <AvailableSong> records, String word)
        {
            // String.Contains() が StringComparison.OrdinalIgnoreCase 付きで動作しないため、EF.Functions.Like() を使う
            Boolean isRuby = YbdCommon.IsRuby(word, out String ruby);

            return(records.Where(x => EF.Functions.Like(x.Path, $"%{word}%") ||
                                 EF.Functions.Like(x.SongName, $"%{word}%") ||
                                 isRuby && EF.Functions.Like(x.SongRuby, $"%{ruby}%") ||
                                 EF.Functions.Like(x.TieUpName, $"%{word}%") ||
                                 isRuby && EF.Functions.Like(x.TieUpRuby, $"%{ruby}%") ||
                                 EF.Functions.Like(x.ArtistName, $"%{word}%") ||
                                 isRuby && EF.Functions.Like(x.ArtistRuby, $"%{ruby}%") ||
                                 EF.Functions.Like(x.MakerName, $"%{word}%") ||
                                 isRuby && EF.Functions.Like(x.MakerRuby, $"%{ruby}%") ||
                                 EF.Functions.Like(x.Worker, $"%{word}%")));
        }
Exemple #8
0
        public IActionResult GetThumbnail(String?id)
        {
            try
            {
                using AvailableSongContext availableSongContext = CreateAvailableSongContext(out DbSet <AvailableSong> availableSongs);
                AvailableSong?availableSong = availableSongs.SingleOrDefault(x => x.Id == id);
                if (availableSong == null)
                {
                    // ID が見つからない
                    return(NotAcceptable());
                }

                using ThumbnailContext thumbnailContext = CreateThumbnailContext(out DbSet <Thumbnail> thumbnails);
                Thumbnail?thumbnail = thumbnails.FirstOrDefault(x => x.Path == availableSong.Path);
                if (thumbnail == null)
                {
                    // ID に対応するサムネイルが無い
                    thumbnail = DefaultThumbnail;
                    if (thumbnail == null)
                    {
                        throw new Exception();
                    }
                }

                // キャッシュチェック
                if (IsEntityTagValid(thumbnail.LastModified))
                {
                    Debug.WriteLine("GetThumbnail() キャッシュ有効: " + id);
                    return(NotModified());
                }

                // 実際の運用時はサムネイルの返却に時間がかかることを想定
                Random random = new();
                Thread.Sleep(random.Next(500, 1000));

                Debug.WriteLine("GetThumbnail() キャッシュ無し: " + id);
                DateTimeOffset       lastModified = new DateTimeOffset(YbdCommon.ModifiedJulianDateToDateTime(thumbnail.LastModified));
                EntityTagHeaderValue eTag         = GenerateEntityTag(thumbnail.LastModified);
                return(File(thumbnail.Bitmap, thumbnail.Mime, lastModified, eTag));
            }
            catch (Exception excep)
            {
                Debug.WriteLine("サムネイル取得サーバーエラー:\n" + excep.Message);
                Debug.WriteLine(" スタックトレース:\n" + excep.StackTrace);
                return(InternalServerError());
            }
        }
        public IActionResult Login([FromBody] LoginInfo loginInfo)
        {
            try
            {
                if (!loginInfo.IsValid())
                {
                    return(BadRequest());
                }

#if DEBUG
                Thread.Sleep(1000);
#endif

                // ユーザーを検索
                using UserProfileContext userProfileContext = CreateUserProfileContext(out DbSet <RegisteredUser> registeredUsers, out _, out _);
                RegisteredUser?loginUser = registeredUsers.SingleOrDefault(x => x.Name == loginInfo.Name);
                if (loginUser == null)
                {
                    return(NotAcceptable());
                }

                // パスワードハッシュの一致を確認
                if (loginUser.Password != HashPassword(loginInfo.Password, loginUser.Salt))
                {
                    return(NotAcceptable());
                }

                String idAndToken = GenerateIdAndTokenString(loginUser.Id);
                Debug.WriteLine("Login() " + idAndToken);

                loginUser.LastLogin = YbdCommon.UtcNowModifiedJulianDate();
                userProfileContext.SaveChanges();

                // ID とログイン用トークンを返す
                return(Ok(idAndToken));
            }
            catch (Exception excep)
            {
                Debug.WriteLine("ログインサーバーエラー:\n" + excep.Message);
                Debug.WriteLine(" スタックトレース:\n" + excep.StackTrace);
                return(InternalServerError());
            }
        }
 // --------------------------------------------------------------------
 // サムネイル生成
 // --------------------------------------------------------------------
 private static Thumbnail?CreateThumbnail(String moviePath, String imageFileName, Int32 maxWidth, Int32 maxHeight)
 {
     try
     {
         using FileStream sourceStream = new FileStream(imageFileName, FileMode.Open);
         return(new Thumbnail
         {
             Path = moviePath,
             Bitmap = ServerCommon.CreateThumbnail(sourceStream, ServerConstants.MIME_TYPE_PNG, maxWidth, maxHeight, false),
             Mime = ServerConstants.MIME_TYPE_PNG,
             LastModified = YbdCommon.DateTimeToModifiedJulianDate(ServerCommon.LastModified(imageFileName)),
         });
     }
     catch (Exception excep)
     {
         Debug.WriteLine("サムネイル作成エラー:\n" + excep.Message);
         Debug.WriteLine(" スタックトレース:\n" + excep.StackTrace);
         return(null);
     }
 }
        public IActionResult SetPassword([FromBody] String?[] passwords)
        {
            try
            {
                using UserProfileContext userProfileContext = CreateUserProfileContext(out DbSet <RegisteredUser> registeredUsers, out _, out _);
                if (!IsTokenValid(registeredUsers, out RegisteredUser? loginUser))
                {
                    return(Unauthorized());
                }
                if (passwords.Length < 2)
                {
                    return(BadRequest());
                }
                String?currentPassword = passwords[0];
                String?newPassword     = passwords[1];
                if (String.IsNullOrEmpty(currentPassword) || String.IsNullOrEmpty(newPassword))
                {
                    return(BadRequest());
                }

                // 現在のパスワードハッシュの一致を確認
                if (loginUser.Password != HashPassword(currentPassword, loginUser.Salt))
                {
                    return(NotAcceptable());
                }

                // 設定
                loginUser.Password     = newPassword;
                loginUser.LastModified = YbdCommon.UtcNowModifiedJulianDate();
                HashPassword(loginUser);
                userProfileContext.SaveChanges();

                return(Ok());
            }
            catch (Exception excep)
            {
                Debug.WriteLine("パスワード設定サーバーエラー:\n" + excep.Message);
                Debug.WriteLine(" スタックトレース:\n" + excep.StackTrace);
                return(InternalServerError());
            }
        }
        public IActionResult SetName([FromBody] String?newName)
        {
            try
            {
                using UserProfileContext userProfileContext = CreateUserProfileContext(out DbSet <RegisteredUser> registeredUsers, out _, out _);
                if (!IsTokenValid(registeredUsers, out RegisteredUser? loginUser))
                {
                    return(Unauthorized());
                }
                if (String.IsNullOrEmpty(newName))
                {
                    return(BadRequest());
                }

                // 管理者の名前は変更できない
                if (loginUser.IsAdmin)
                {
                    return(BadRequest());
                }

                // 同じ名前のユーザーが既に存在している場合は登録できない
                if (registeredUsers.FirstOrDefault(x => x.Name == newName) != null)
                {
                    return(Conflict());
                }

                // 設定
                loginUser.Name         = newName;
                loginUser.LastModified = YbdCommon.UtcNowModifiedJulianDate();
                userProfileContext.SaveChanges();

                return(Ok());
            }
            catch (Exception excep)
            {
                Debug.WriteLine("名前設定サーバーエラー:\n" + excep.Message);
                Debug.WriteLine(" スタックトレース:\n" + excep.StackTrace);
                return(InternalServerError());
            }
        }
        public IActionResult AddStock([FromBody] AvailableSong availableSong)
        {
            try
            {
                using UserProfileContext userProfileContext = CreateUserProfileContext(out DbSet <RegisteredUser> registeredUsers, out DbSet <StockSong> stockSongs, out _);
                if (!IsTokenValid(registeredUsers, out RegisteredUser? loginUser))
                {
                    return(Unauthorized());
                }

                StockSong?stockSong = stockSongs.SingleOrDefault(x => x.UserId == loginUser.Id && x.AvailableSongId == availableSong.Id);
                if (stockSong == null)
                {
                    // 新規追加
                    stockSong = new();
                    YbdCommon.CopySongProperty(availableSong, stockSong);
                    stockSong.AvailableSongId = availableSong.Id;
                    stockSong.UserId          = loginUser.Id;
                    stockSong.RequestTime     = YbdCommon.UtcNowModifiedJulianDate();
                    stockSongs.Add(stockSong);
                }
                else
                {
                    // 登録日時更新
                    stockSong.RequestTime = YbdCommon.UtcNowModifiedJulianDate();
                }
                userProfileContext.SaveChanges();

                return(Ok());
            }
            catch (Exception excep)
            {
                Debug.WriteLine("後で歌う予定追加サーバーエラー:\n" + excep.Message);
                Debug.WriteLine(" スタックトレース:\n" + excep.StackTrace);
                return(InternalServerError());
            }
        }
        public IActionResult GetPublicUserInfo(String?id)
        {
            try
            {
                // キャッシュチェック
                DateTime lastModified = ServerCommon.LastModified(ServerConstants.FILE_NAME_USER_PROFILES);
                if (IsEntityTagValid(YbdCommon.DateTimeToModifiedJulianDate(lastModified)))
                {
                    Debug.WriteLine("GetPublicUserInfo() キャッシュ有効: " + id);
                    return(NotModified());
                }

                if (String.IsNullOrEmpty(id))
                {
                    return(BadRequest());
                }

                using UserProfileContext userProfileContext = CreateUserProfileContext(out DbSet <RegisteredUser> registeredUsers, out _, out _);
                RegisteredUser?registeredUser = registeredUsers.SingleOrDefault(x => x.Id == id);
                if (registeredUser == null)
                {
                    return(NotAcceptable());
                }
                PublicUserInfo userInfo = new PublicUserInfo();
                registeredUser.CopyPublicInfo(userInfo, false);

                EntityTagHeaderValue eTag = GenerateEntityTag(YbdCommon.DateTimeToModifiedJulianDate(lastModified));
                return(File(JsonSerializer.SerializeToUtf8Bytes(userInfo), ServerConstants.MIME_TYPE_JSON, lastModified, eTag));
            }
            catch (Exception excep)
            {
                Debug.WriteLine("公開ユーザー情報取得サーバーエラー:\n" + excep.Message);
                Debug.WriteLine(" スタックトレース:\n" + excep.StackTrace);
                return(InternalServerError());
            }
        }
        public IActionResult GetUsers()
        {
            try
            {
                using UserProfileContext userProfileContext = CreateUserProfileContext(out DbSet <RegisteredUser> registeredUsers, out _, out _);
                if (!IsTokenValid(registeredUsers, out RegisteredUser? loginUser) || !loginUser.IsAdmin)
                {
                    return(Unauthorized());
                }

                // キャッシュチェック
                DateTime lastModified = ServerCommon.LastModified(ServerConstants.FILE_NAME_USER_PROFILES);
                if (IsEntityTagValid(YbdCommon.DateTimeToModifiedJulianDate(lastModified)))
                {
                    Debug.WriteLine("GetUsers() キャッシュ有効: ");
                    return(NotModified());
                }

                RegisteredUser[] registeredUsersArray = registeredUsers.Where(x => !x.IsAdmin).OrderBy(x => x.Name).ToArray();
                PublicUserInfo[] results = new PublicUserInfo[registeredUsersArray.Length];
                for (Int32 i = 0; i < registeredUsersArray.Length; i++)
                {
                    PublicUserInfo publicUserInfo = new();
                    registeredUsersArray[i].CopyPublicInfo(publicUserInfo, true);
                    results[i] = publicUserInfo;
                }
                EntityTagHeaderValue eTag = GenerateEntityTag(YbdCommon.DateTimeToModifiedJulianDate(lastModified));
                return(File(JsonSerializer.SerializeToUtf8Bytes(results), ServerConstants.MIME_TYPE_JSON, lastModified, eTag));
            }
            catch (Exception excep)
            {
                Debug.WriteLine("ユーザー一覧取得サーバーエラー:\n" + excep.Message);
                Debug.WriteLine(" スタックトレース:\n" + excep.StackTrace);
                return(InternalServerError());
            }
        }
Exemple #16
0
        public IActionResult AddRequestSong([FromBody] RequestSong requestSong)
        {
            try
            {
                if (!requestSong.IsValid())
                {
                    return(BadRequest());
                }

                // 予約者のユーザー ID が指定されている場合はその正当性を確認(なりすまし予約防止)
                using UserProfileContext userProfileContext = CreateUserProfileContext(out DbSet <RegisteredUser> registeredUsers, out DbSet <StockSong> stockSongs, out DbSet <HistorySong> historySongs);
                if (!String.IsNullOrEmpty(requestSong.UserId))
                {
                    if (!IsTokenValid(registeredUsers, out RegisteredUser? loginUser) || requestSong.UserId != loginUser.Id)
                    {
                        return(Unauthorized());
                    }
                }

                // 追加する曲の位置は最後
                using RequestSongContext requestSongContext = CreateRequestSongContext(out DbSet <RequestSong> requestSongs);
                Int32 sort;
                if (requestSongs.Any())
                {
                    sort = requestSongs.Max(x => x.Sort) + 1;
                }
                else
                {
                    sort = 1;
                }
                requestSong.Sort = sort;

                // 予約追加
                requestSongs.Add(requestSong);
                requestSongContext.SaveChanges();

                if (!String.IsNullOrEmpty(requestSong.UserId))
                {
                    // 予約者のユーザー ID が指定されている場合は履歴追加
                    HistorySong historySong = new();
                    YbdCommon.CopyHistorySongProperty(requestSong, historySong);
                    historySongs.Add(historySong);

                    // 後で歌う予定リストに追加されている場合はリストから削除
                    StockSong?stockSong = SearchStockSongByRequestSong(stockSongs, requestSong);
                    if (stockSong != null)
                    {
                        stockSongs.Remove(stockSong);
                    }

                    userProfileContext.SaveChanges();
                }

                SendSse(YbdConstants.SSE_DATA_REQUEST_CHANGED);
                return(Ok());
            }
            catch (Exception excep)
            {
                Debug.WriteLine("予約追加サーバーエラー:\n" + excep.Message);
                Debug.WriteLine(" スタックトレース:\n" + excep.StackTrace);
                return(InternalServerError());
            }
        }
Exemple #17
0
        public IActionResult SearchByWord(String?query)
        {
            try
            {
                // キャッシュチェック
                DateTime lastModified = ServerCommon.LastModified(ServerConstants.FILE_NAME_AVAILABLE_SONGS);
                if (IsEntityTagValid(YbdCommon.DateTimeToModifiedJulianDate(lastModified)))
                {
                    Debug.WriteLine("SearchByWord() キャッシュ有効: " + query);
                    return(NotModified());
                }

                SearchWord searchWord = new SearchWord(query);
                if (!searchWord.IsValid(out String? errorMessage))
                {
                    return(BadRequest());
                }

                using AvailableSongContext availableSongContext = CreateAvailableSongContext(out DbSet <AvailableSong> availableSongs);
                IQueryable <AvailableSong> searchResults = availableSongs;
                if (searchWord.Type == SearchWordType.AnyWord)
                {
                    // なんでも検索
                    String[] anyWords = SplitKeyword(searchWord.AnyWord);
                    for (Int32 i = 0; i < anyWords.Length; i++)
                    {
                        searchResults = SearchByAnyWord(searchResults, anyWords[i]);
                    }
                }
                else
                {
                    // 曲名
                    // ToDo: SearchBySongName() を関数化すると実行できるが、地の文にすると例外が発生する
                    String[] songNames = SplitKeyword(searchWord.SongName);
                    for (Int32 i = 0; i < songNames.Length; i++)
                    {
                        searchResults = SearchBySongName(searchResults, songNames[i]);
                    }

                    // タイアップ名
                    String[] tieUpNames = SplitKeyword(searchWord.TieUpName);
                    for (Int32 i = 0; i < tieUpNames.Length; i++)
                    {
                        searchResults = SearchByTieUpName(searchResults, tieUpNames[i]);
                    }

                    // 歌手名
                    String[] artistNames = SplitKeyword(searchWord.ArtistName);
                    for (Int32 i = 0; i < artistNames.Length; i++)
                    {
                        searchResults = SearchByArtistName(searchResults, artistNames[i]);
                    }

                    // 制作会社
                    String[] makers = SplitKeyword(searchWord.MakerName);
                    for (Int32 i = 0; i < makers.Length; i++)
                    {
                        searchResults = SearchByMaker(searchResults, makers[i]);
                    }

                    // カラオケ動画制作者
                    String[] workers = SplitKeyword(searchWord.Worker);
                    for (Int32 i = 0; i < workers.Length; i++)
                    {
                        searchResults = SearchByWorker(searchResults, workers[i]);
                    }

                    // ファイル名
                    String[] pathes = SplitKeyword(searchWord.Path);
                    for (Int32 i = 0; i < pathes.Length; i++)
                    {
                        searchResults = SearchByPath(searchResults, pathes[i]);
                    }
                }

                // 追加ヘッダー
                AddTotalCountToHeader(searchResults.Count());

                // 検索結果は AvailableSongContext の寿命と共に尽きるようなので、ToArray() で新しいコンテナに格納する
                AvailableSong[]      results = SortSearchResult(searchResults, searchWord.Sort).Skip(YbdConstants.PAGE_SIZE * searchWord.Page).Take(YbdConstants.PAGE_SIZE).ToArray();
                EntityTagHeaderValue eTag    = GenerateEntityTag(YbdCommon.DateTimeToModifiedJulianDate(lastModified));
                return(File(JsonSerializer.SerializeToUtf8Bytes(results), ServerConstants.MIME_TYPE_JSON, lastModified, eTag));
            }
            catch (Exception excep)
            {
                Debug.WriteLine("キーワード検索サーバーエラー:\n" + excep.Message);
                Debug.WriteLine(" スタックトレース:\n" + excep.StackTrace);
                return(InternalServerError());
            }
        }
        public IActionResult GetThumbnail(String?id)
        {
            try
            {
                RegisteredUser?registeredUser = null;
                if (String.IsNullOrEmpty(id))
                {
                    // 引数が空の場合は、ゲストのプロフィール画像を返す
                    if (DefaultGuestUserThumbnail == null)
                    {
                        throw new Exception();
                    }
                    registeredUser = new()
                    {
                        Bitmap       = DefaultGuestUserThumbnail.Bitmap,
                        Mime         = DefaultGuestUserThumbnail.Mime,
                        LastModified = YbdConstants.INVALID_MJD,
                    };
                }
                else
                {
                    using UserProfileContext userProfileContext = CreateUserProfileContext(out DbSet <RegisteredUser> registeredUsers, out _, out _);
                    registeredUser = registeredUsers.SingleOrDefault(x => x.Id == id);
                    if (registeredUser == null)
                    {
                        return(NotAcceptable());
                    }

                    // 指定されたユーザーにプロフィール画像が設定されていない場合
                    if (registeredUser.Bitmap.Length == 0)
                    {
                        Thumbnail?defaultThumbnail;
                        if (registeredUser.IsAdmin)
                        {
                            defaultThumbnail = DefaultAdminUserThumbnail;
                        }
                        else
                        {
                            defaultThumbnail = DefaultRegisteredUserThumbnail;
                        }
                        if (defaultThumbnail == null)
                        {
                            throw new Exception();
                        }
                        registeredUser = new()
                        {
                            Bitmap       = defaultThumbnail.Bitmap,
                            Mime         = defaultThumbnail.Mime,
                            LastModified = YbdConstants.INVALID_MJD,
                        };
                    }
                }

                // キャッシュチェック
                DateTime lastModified = YbdCommon.ModifiedJulianDateToDateTime(registeredUser.LastModified);
                if (IsEntityTagValid(YbdCommon.DateTimeToModifiedJulianDate(lastModified)))
                {
                    Debug.WriteLine("GetThumbnail() プロフィール画像キャッシュ有効: " + id);
                    return(NotModified());
                }

                // プロフィール画像を返す
                Debug.WriteLine("GetThumbnail() プロフィール画像キャッシュ無効: " + id);
                EntityTagHeaderValue eTag = GenerateEntityTag(registeredUser.LastModified);
                return(File(registeredUser.Bitmap, registeredUser.Mime, lastModified, eTag));
            }
            catch (Exception excep)
            {
                Debug.WriteLine("プロフィール画像取得サーバーエラー:\n" + excep.Message);
                Debug.WriteLine(" スタックトレース:\n" + excep.StackTrace);
                return(InternalServerError());
            }
        }