using System; using System.Linq; using System.Runtime.CompilerServices; using System.Threading.Tasks; using AutoMapper; using BililiveRecorder.Core; using BililiveRecorder.Web.Models; using BililiveRecorder.Web.Models.Rest; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; namespace BililiveRecorder.Web.Api { [ApiController, Route("api/[controller]", Name = "[controller] [action]")] public sealed class RoomController : ControllerBase { private readonly IMapper mapper; private readonly IRecorder recorder; public RoomController(IMapper mapper, IRecorder recorder) { this.mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); this.recorder = recorder ?? throw new ArgumentNullException(nameof(recorder)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private IRoom? FetchRoom(int roomId) => this.recorder.Rooms.FirstOrDefault(x => x.ShortId == roomId || x.RoomConfig.RoomId == roomId); [MethodImpl(MethodImplOptions.AggressiveInlining)] private IRoom? FetchRoom(Guid objectId) => this.recorder.Rooms.FirstOrDefault(x => x.ObjectId == objectId); /// /// 列出所有直播间 /// /// [HttpGet] public RoomDto[] GetRooms() => this.mapper.Map(this.recorder.Rooms); #region Create & Delete /// /// 添加直播间 /// /// /// [HttpPost] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(RestApiError), StatusCodes.Status400BadRequest)] public ActionResult CreateRoom([FromBody] CreateRoomDto createRoom) { if (createRoom.RoomId <= 0) return this.BadRequest(new RestApiError { Code = RestApiErrorCode.RoomidOutOfRange, Message = "Roomid must be greater than 0." }); var room = this.FetchRoom(createRoom.RoomId); if (room is not null) { if (room.RoomConfig.AutoRecord != createRoom.AutoRecord) room.RoomConfig.AutoRecord = createRoom.AutoRecord; } else { room = this.recorder.AddRoom(createRoom.RoomId, createRoom.AutoRecord); } return this.mapper.Map(room); } /// /// 删除直播间 /// /// /// [HttpDelete("{roomId:int}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)] public ActionResult DeleteRoom(int roomId) { var room = this.FetchRoom(roomId); if (room is null) return this.NotFound(new RestApiError { Code = RestApiErrorCode.RoomNotFound, Message = "Room not found" }); this.recorder.RemoveRoom(room); return this.mapper.Map(room); } /// /// 删除直播间 /// /// /// [HttpDelete("{objectId:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)] public ActionResult DeleteRoom(Guid objectId) { var room = this.FetchRoom(objectId); if (room is null) return this.NotFound(new RestApiError { Code = RestApiErrorCode.RoomNotFound, Message = "Room not found" }); this.recorder.RemoveRoom(room); return this.mapper.Map(room); } #endregion #region Get Room /// /// 读取一个直播间 /// /// /// [HttpGet("{roomId:int}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)] public ActionResult GetRoom(int roomId) { var room = this.FetchRoom(roomId); if (room is null) return this.NotFound(new RestApiError { Code = RestApiErrorCode.RoomNotFound, Message = "Room not found" }); return this.mapper.Map(room); } /// /// 读取一个直播间 /// /// /// [HttpGet("{objectId:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)] public ActionResult GetRoom(Guid objectId) { var room = this.FetchRoom(objectId); if (room is null) return this.NotFound(new RestApiError { Code = RestApiErrorCode.RoomNotFound, Message = "Room not found" }); return this.mapper.Map(room); } #endregion #region Get Room Stats /// /// 读取直播间录制统计信息 /// /// /// [HttpGet("{roomId:int}/stats")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)] public ActionResult GetRoomRecordingStats(int roomId) { var room = this.FetchRoom(roomId); if (room is null) return this.NotFound(new RestApiError { Code = RestApiErrorCode.RoomNotFound, Message = "Room not found" }); return this.mapper.Map(room.Stats); } /// /// 读取直播间录制统计信息 /// /// /// [HttpGet("{objectId:guid}/stats")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)] public ActionResult GetRoomRecordingStats(Guid objectId) { var room = this.FetchRoom(objectId); if (room is null) return this.NotFound(new RestApiError { Code = RestApiErrorCode.RoomNotFound, Message = "Room not found" }); return this.mapper.Map(room.Stats); } /// /// 读取直播间 IO 统计信息 /// /// /// [HttpGet("{roomId:int}/ioStats")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)] public ActionResult GetRoomIOStats(int roomId) { var room = this.FetchRoom(roomId); if (room is null) return this.NotFound(new RestApiError { Code = RestApiErrorCode.RoomNotFound, Message = "Room not found" }); return this.mapper.Map(room.Stats); } /// /// 读取直播间 IO 统计信息 /// /// /// [HttpGet("{objectId:guid}/ioStats")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)] public ActionResult GetRoomIOStats(Guid objectId) { var room = this.FetchRoom(objectId); if (room is null) return this.NotFound(new RestApiError { Code = RestApiErrorCode.RoomNotFound, Message = "Room not found" }); return this.mapper.Map(room.Stats); } #endregion #region Room Config /// /// 读取直播间设置 /// /// /// [HttpGet("{roomId:int}/config")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)] public ActionResult GetRoomConfig(int roomId) { var room = this.FetchRoom(roomId); if (room is null) return this.NotFound(new RestApiError { Code = RestApiErrorCode.RoomNotFound, Message = "Room not found" }); return this.mapper.Map(room.RoomConfig); } /// /// 读取直播间设置 /// /// /// [HttpGet("{objectId:guid}/config")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)] public ActionResult GetRoomConfig(Guid objectId) { var room = this.FetchRoom(objectId); if (room is null) return this.NotFound(new RestApiError { Code = RestApiErrorCode.RoomNotFound, Message = "Room not found" }); return this.mapper.Map(room.RoomConfig); } /// /// 修改直播间设置 /// /// /// /// [HttpPost("{roomId:int}/config")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)] public ActionResult SetRoomConfig(int roomId, [FromBody] SetRoomConfig config) { var room = this.FetchRoom(roomId); if (room is null) return this.NotFound(new RestApiError { Code = RestApiErrorCode.RoomNotFound, Message = "Room not found" }); config.ApplyTo(room.RoomConfig); this.recorder.SaveConfig(); return this.mapper.Map(room.RoomConfig); } /// /// 修改直播间设置 /// /// /// /// [HttpPost("{objectId:guid}/config")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)] public ActionResult SetRoomConfig(Guid objectId, [FromBody] SetRoomConfig config) { var room = this.FetchRoom(objectId); if (room is null) return this.NotFound(new RestApiError { Code = RestApiErrorCode.RoomNotFound, Message = "Room not found" }); config.ApplyTo(room.RoomConfig); this.recorder.SaveConfig(); return this.mapper.Map(room.RoomConfig); } #endregion #region Room Action /// /// 开始录制 /// /// /// [HttpPost("{roomId:int}/start")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)] public ActionResult StartRecording(int roomId) { var room = this.FetchRoom(roomId); if (room is null) return this.NotFound(new RestApiError { Code = RestApiErrorCode.RoomNotFound, Message = "Room not found" }); room.StartRecord(); return this.mapper.Map(room); } /// /// 开始录制 /// /// /// [HttpPost("{objectId:guid}/start")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)] public ActionResult StartRecording(Guid objectId) { var room = this.FetchRoom(objectId); if (room is null) return this.NotFound(new RestApiError { Code = RestApiErrorCode.RoomNotFound, Message = "Room not found" }); room.StartRecord(); return this.mapper.Map(room); } /// /// 停止录制 /// /// /// [HttpPost("{roomId:int}/stop")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)] public ActionResult StopRecording(int roomId) { var room = this.FetchRoom(roomId); if (room is null) return this.NotFound(new RestApiError { Code = RestApiErrorCode.RoomNotFound, Message = "Room not found" }); room.StopRecord(); return this.mapper.Map(room); } /// /// 停止录制 /// /// /// [HttpPost("{objectId:guid}/stop")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)] public ActionResult StopRecording(Guid objectId) { var room = this.FetchRoom(objectId); if (room is null) return this.NotFound(new RestApiError { Code = RestApiErrorCode.RoomNotFound, Message = "Room not found" }); room.StopRecord(); return this.mapper.Map(room); } /// /// 手动分段 /// /// /// [HttpPost("{roomId:int}/split")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)] public ActionResult SplitRecording(int roomId) { var room = this.FetchRoom(roomId); if (room is null) return this.NotFound(new RestApiError { Code = RestApiErrorCode.RoomNotFound, Message = "Room not found" }); room.SplitOutput(); return this.mapper.Map(room); } /// /// 手动分段 /// /// /// [HttpPost("{objectId:guid}/split")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)] public ActionResult SplitRecording(Guid objectId) { var room = this.FetchRoom(objectId); if (room is null) return this.NotFound(new RestApiError { Code = RestApiErrorCode.RoomNotFound, Message = "Room not found" }); room.SplitOutput(); return this.mapper.Map(room); } /// /// 刷新直播间信息 /// /// /// [HttpPost("{roomId:int}/refresh")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)] public async Task> RefreshRecordingAsync(int roomId) { var room = this.FetchRoom(roomId); if (room is null) return this.NotFound(new RestApiError { Code = RestApiErrorCode.RoomNotFound, Message = "Room not found" }); await room.RefreshRoomInfoAsync().ConfigureAwait(false); return this.mapper.Map(room); } /// /// 刷新直播间信息 /// /// /// [HttpPost("{objectId:guid}/refresh")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)] public async Task> RefreshRecordingAsync(Guid objectId) { var room = this.FetchRoom(objectId); if (room is null) return this.NotFound(new RestApiError { Code = RestApiErrorCode.RoomNotFound, Message = "Room not found" }); await room.RefreshRoomInfoAsync().ConfigureAwait(false); return this.mapper.Map(room); } #endregion } }