mirror of
https://github.com/BililiveRecorder/BililiveRecorder.git
synced 2024-11-16 11:42:22 +08:00
Web: Add REST style API
This commit is contained in:
parent
958683a1d1
commit
abd62dc378
|
@ -85,7 +85,7 @@ namespace BililiveRecorder.Web.Schemas.Types
|
|||
}
|
||||
}
|
||||
|
||||
internal class SetRoomConfig
|
||||
public class SetRoomConfig // TODO MOVE THIS TYPE
|
||||
{
|
||||
public bool? AutoRecord { get; set; }
|
||||
public Optional<RecordMode>? OptionalRecordMode { get; set; }
|
||||
|
@ -130,7 +130,7 @@ namespace BililiveRecorder.Web.Schemas.Types
|
|||
}
|
||||
}
|
||||
|
||||
internal class SetGlobalConfig
|
||||
public class SetGlobalConfig // TODO MOVE THIS TYPE
|
||||
{
|
||||
public Optional<RecordMode>? OptionalRecordMode { get; set; }
|
||||
public Optional<CuttingMode>? OptionalCuttingMode { get; set; }
|
||||
|
|
|
@ -2,7 +2,7 @@ namespace BililiveRecorder.Web.Schemas.Types
|
|||
{
|
||||
public class RecorderVersion
|
||||
{
|
||||
internal static readonly RecorderVersion Instance = new();
|
||||
public static readonly RecorderVersion Instance = new();
|
||||
|
||||
public string Major { get; } = GitVersionInformation.Major;
|
||||
public string Minor { get; } = GitVersionInformation.Minor;
|
||||
|
|
49
BililiveRecorder.Web/Api/ConfigController.cs
Normal file
49
BililiveRecorder.Web/Api/ConfigController.cs
Normal file
|
@ -0,0 +1,49 @@
|
|||
using System;
|
||||
using AutoMapper;
|
||||
using BililiveRecorder.Core;
|
||||
using BililiveRecorder.Core.Config.V2;
|
||||
using BililiveRecorder.Web.Models;
|
||||
using BililiveRecorder.Web.Schemas.Types;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace BililiveRecorder.Web.Api
|
||||
{
|
||||
[ApiController, Route("api/[controller]", Name = "[controller] [action]")]
|
||||
public class ConfigController : ControllerBase
|
||||
{
|
||||
private readonly IMapper mapper;
|
||||
private readonly IRecorder recorder;
|
||||
|
||||
public ConfigController(IMapper mapper, IRecorder recorder)
|
||||
{
|
||||
this.mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
|
||||
this.recorder = recorder ?? throw new ArgumentNullException(nameof(recorder));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取软件默认设置
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet("default")]
|
||||
public ActionResult<DefaultConfig> GetDefaultConfig() => DefaultConfig.Instance;
|
||||
|
||||
/// <summary>
|
||||
/// 获取全局设置
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet("global")]
|
||||
public ActionResult<GlobalConfigDto> GetGlobalConfig() => this.mapper.Map<GlobalConfigDto>(this.recorder.Config.Global);
|
||||
|
||||
/// <summary>
|
||||
/// 设置全局设置
|
||||
/// </summary>
|
||||
/// <param name="config"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("global")]
|
||||
public ActionResult<GlobalConfigDto> SetGlobalConfig([FromBody] SetGlobalConfig config)
|
||||
{
|
||||
config.ApplyTo(this.recorder.Config.Global);
|
||||
return this.mapper.Map<GlobalConfigDto>(this.recorder.Config.Global);
|
||||
}
|
||||
}
|
||||
}
|
23
BililiveRecorder.Web/Api/DataMappingProfile.cs
Normal file
23
BililiveRecorder.Web/Api/DataMappingProfile.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
using AutoMapper;
|
||||
using BililiveRecorder.Core;
|
||||
using BililiveRecorder.Core.Config.V2;
|
||||
using BililiveRecorder.Web.Models;
|
||||
|
||||
namespace BililiveRecorder.Web.Api
|
||||
{
|
||||
public class DataMappingProfile : Profile
|
||||
{
|
||||
public DataMappingProfile()
|
||||
{
|
||||
this.CreateMap<IRoom, RoomDto>()
|
||||
.ForMember(x => x.RoomId, x => x.MapFrom(s => s.RoomConfig.RoomId))
|
||||
.ForMember(x => x.AutoRecord, x => x.MapFrom(s => s.RoomConfig.AutoRecord));
|
||||
|
||||
this.CreateMap<RecordingStats, RoomStatsDto>();
|
||||
|
||||
this.CreateMap<RoomConfig, RoomConfigDto>();
|
||||
|
||||
this.CreateMap<GlobalConfig, GlobalConfigDto>();
|
||||
}
|
||||
}
|
||||
}
|
403
BililiveRecorder.Web/Api/RoomController.cs
Normal file
403
BililiveRecorder.Web/Api/RoomController.cs
Normal file
|
@ -0,0 +1,403 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using AutoMapper;
|
||||
using BililiveRecorder.Core;
|
||||
using BililiveRecorder.Web.Models;
|
||||
using BililiveRecorder.Web.Schemas.Types;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace BililiveRecorder.Web.Api
|
||||
{
|
||||
[ApiController, Route("api/[controller]", Name = "[controller] [action]")]
|
||||
public 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));
|
||||
}
|
||||
|
||||
private IRoom? FetchRoom(int roomId) => this.recorder.Rooms.FirstOrDefault(x => x.ShortId == roomId || x.RoomConfig.RoomId == roomId);
|
||||
|
||||
private IRoom? FetchRoom(Guid objectId) => this.recorder.Rooms.FirstOrDefault(x => x.ObjectId == objectId);
|
||||
|
||||
/// <summary>
|
||||
/// 列出所有直播间
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public RoomDto[] GetRooms() => this.mapper.Map<RoomDto[]>(this.recorder.Rooms);
|
||||
|
||||
#region Create & Delete
|
||||
|
||||
/// <summary>
|
||||
/// 添加直播间
|
||||
/// </summary>
|
||||
/// <param name="createRoom"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(RestApiError), StatusCodes.Status400BadRequest)]
|
||||
public ActionResult<RoomDto> 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)
|
||||
return this.BadRequest(new RestApiError { Code = RestApiErrorCode.RoomExist, Message = "Can not add the same room multiple times." });
|
||||
|
||||
room = this.recorder.AddRoom(createRoom.RoomId, createRoom.AutoRecord);
|
||||
|
||||
return this.mapper.Map<RoomDto>(room);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除直播间
|
||||
/// </summary>
|
||||
/// <param name="roomId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpDelete("{roomId:int}")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)]
|
||||
public ActionResult<RoomDto> 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<RoomDto>(room);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除直播间
|
||||
/// </summary>
|
||||
/// <param name="objectId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpDelete("{objectId:guid}")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)]
|
||||
public ActionResult<RoomDto> 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<RoomDto>(room);
|
||||
}
|
||||
|
||||
#endregion
|
||||
#region Get Room
|
||||
|
||||
/// <summary>
|
||||
/// 读取一个直播间
|
||||
/// </summary>
|
||||
/// <param name="roomId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("{roomId:int}")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)]
|
||||
public ActionResult<RoomDto> 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<RoomDto>(room);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取一个直播间
|
||||
/// </summary>
|
||||
/// <param name="objectId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("{objectId:guid}")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)]
|
||||
public ActionResult<RoomDto> 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<RoomDto>(room);
|
||||
}
|
||||
|
||||
#endregion
|
||||
#region Get Room Stats
|
||||
|
||||
/// <summary>
|
||||
/// 读取直播间统计信息
|
||||
/// </summary>
|
||||
/// <param name="roomId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("{roomId:int}/stats")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)]
|
||||
public ActionResult<RoomStatsDto> GetRoomStats(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<RoomStatsDto>(room.Stats);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取直播间统计信息
|
||||
/// </summary>
|
||||
/// <param name="objectId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("{objectId:guid}/stats")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)]
|
||||
public ActionResult<RoomStatsDto> GetRoomStats(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<RoomStatsDto>(room.Stats);
|
||||
}
|
||||
|
||||
#endregion
|
||||
#region Room Config
|
||||
|
||||
/// <summary>
|
||||
/// 读取直播间设置
|
||||
/// </summary>
|
||||
/// <param name="roomId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("{roomId:int}/config")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)]
|
||||
public ActionResult<RoomConfigDto> 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<RoomConfigDto>(room.RoomConfig);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取直播间设置
|
||||
/// </summary>
|
||||
/// <param name="objectId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("{objectId:guid}/config")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)]
|
||||
public ActionResult<RoomConfigDto> 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<RoomConfigDto>(room.RoomConfig);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 修改直播间设置
|
||||
/// </summary>
|
||||
/// <param name="roomId"></param>
|
||||
/// <param name="config"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("{roomId:int}/config")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)]
|
||||
public ActionResult<RoomConfigDto> 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);
|
||||
|
||||
return this.mapper.Map<RoomConfigDto>(room.RoomConfig);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 修改直播间设置
|
||||
/// </summary>
|
||||
/// <param name="objectId"></param>
|
||||
/// <param name="config"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("{objectId:guid}/config")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)]
|
||||
public ActionResult<RoomConfigDto> 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);
|
||||
|
||||
return this.mapper.Map<RoomConfigDto>(room.RoomConfig);
|
||||
}
|
||||
|
||||
#endregion
|
||||
#region Room Action
|
||||
|
||||
/// <summary>
|
||||
/// 开始录制
|
||||
/// </summary>
|
||||
/// <param name="roomId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("{roomId:int}/start")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)]
|
||||
public ActionResult<RoomDto> 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<RoomDto>(room);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 开始录制
|
||||
/// </summary>
|
||||
/// <param name="objectId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("{objectId:guid}/start")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)]
|
||||
public ActionResult<RoomDto> 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<RoomDto>(room);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止录制
|
||||
/// </summary>
|
||||
/// <param name="roomId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("{roomId:int}/stop")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)]
|
||||
public ActionResult<RoomDto> 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<RoomDto>(room);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止录制
|
||||
/// </summary>
|
||||
/// <param name="objectId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("{objectId:guid}/stop")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)]
|
||||
public ActionResult<RoomDto> 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<RoomDto>(room);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 手动分段
|
||||
/// </summary>
|
||||
/// <param name="roomId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("{roomId:int}/split")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)]
|
||||
public ActionResult<RoomDto> 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<RoomDto>(room);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 手动分段
|
||||
/// </summary>
|
||||
/// <param name="objectId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("{objectId:guid}/split")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)]
|
||||
public ActionResult<RoomDto> 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<RoomDto>(room);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刷新直播间信息
|
||||
/// </summary>
|
||||
/// <param name="roomId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("{roomId:int}/refresh")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<RoomDto>> 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<RoomDto>(room);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刷新直播间信息
|
||||
/// </summary>
|
||||
/// <param name="objectId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("{objectId:guid}/refresh")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(RestApiError), StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<RoomDto>> 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<RoomDto>(room);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
15
BililiveRecorder.Web/Api/VersionController.cs
Normal file
15
BililiveRecorder.Web/Api/VersionController.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace BililiveRecorder.Web.Api
|
||||
{
|
||||
[ApiController, Route("api/[controller]", Name = "[controller] [action]")]
|
||||
public class VersionController : ControllerBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 读取软件版本信息
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public Schemas.Types.RecorderVersion GetVersion() => Schemas.Types.RecorderVersion.Instance;
|
||||
}
|
||||
}
|
|
@ -2,10 +2,15 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||
<NoWarn>1701;1702;1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoMapper" Version="11.0.1" />
|
||||
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="11.0.0" />
|
||||
<PackageReference Include="GraphQL.Server.Transports.AspNetCore.NewtonsoftJson" Version="5.0.2" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -15,6 +15,7 @@ namespace BililiveRecorder.Web
|
|||
|
||||
public ReadOnlyObservableCollection<IRoom> Rooms { get; } = new ReadOnlyObservableCollection<IRoom>(new ObservableCollection<IRoom>());
|
||||
|
||||
#pragma warning disable CS0067
|
||||
public event EventHandler<AggregatedRoomEventArgs<RecordSessionStartedEventArgs>>? RecordSessionStarted;
|
||||
public event EventHandler<AggregatedRoomEventArgs<RecordSessionEndedEventArgs>>? RecordSessionEnded;
|
||||
public event EventHandler<AggregatedRoomEventArgs<RecordFileOpeningEventArgs>>? RecordFileOpening;
|
||||
|
@ -22,6 +23,7 @@ namespace BililiveRecorder.Web
|
|||
public event EventHandler<AggregatedRoomEventArgs<NetworkingStatsEventArgs>>? NetworkingStats;
|
||||
public event EventHandler<AggregatedRoomEventArgs<RecordingStatsEventArgs>>? RecordingStats;
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
#pragma warning restore CS0067
|
||||
|
||||
public IRoom AddRoom(int roomid) => null!;
|
||||
|
||||
|
|
8
BililiveRecorder.Web/Models/CreateRoomDto.cs
Normal file
8
BililiveRecorder.Web/Models/CreateRoomDto.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace BililiveRecorder.Web.Models
|
||||
{
|
||||
public class CreateRoomDto
|
||||
{
|
||||
public int RoomId { get; set; }
|
||||
public bool AutoRecord { get; set; }
|
||||
}
|
||||
}
|
13
BililiveRecorder.Web/Models/GlobalConfigDto.cs
Normal file
13
BililiveRecorder.Web/Models/GlobalConfigDto.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BililiveRecorder.Web.Models
|
||||
{
|
||||
public class GlobalConfigDto
|
||||
{
|
||||
|
||||
}
|
||||
}
|
9
BililiveRecorder.Web/Models/RestApiError.cs
Normal file
9
BililiveRecorder.Web/Models/RestApiError.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace BililiveRecorder.Web.Models
|
||||
{
|
||||
public class RestApiError
|
||||
{
|
||||
public RestApiErrorCode Code { get; set; }
|
||||
|
||||
public string Message { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
25
BililiveRecorder.Web/Models/RestApiErrorCode.cs
Normal file
25
BililiveRecorder.Web/Models/RestApiErrorCode.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace BililiveRecorder.Web.Models
|
||||
{
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public enum RestApiErrorCode
|
||||
{
|
||||
/// <summary>
|
||||
/// 错误
|
||||
/// </summary>
|
||||
Unknown,
|
||||
/// <summary>
|
||||
/// 房间号不在允许的范围内
|
||||
/// </summary>
|
||||
RoomidOutOfRange,
|
||||
/// <summary>
|
||||
/// 房间已存在
|
||||
/// </summary>
|
||||
RoomExist,
|
||||
/// <summary>
|
||||
/// 房间不存在
|
||||
/// </summary>
|
||||
RoomNotFound,
|
||||
}
|
||||
}
|
7
BililiveRecorder.Web/Models/RoomConfigDto.cs
Normal file
7
BililiveRecorder.Web/Models/RoomConfigDto.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace BililiveRecorder.Web.Models
|
||||
{
|
||||
public class RoomConfigDto
|
||||
{
|
||||
// TODO auto generate this
|
||||
}
|
||||
}
|
20
BililiveRecorder.Web/Models/RoomDto.cs
Normal file
20
BililiveRecorder.Web/Models/RoomDto.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
using System;
|
||||
|
||||
namespace BililiveRecorder.Web.Models
|
||||
{
|
||||
public class RoomDto
|
||||
{
|
||||
public Guid ObjectId { get; set; }
|
||||
public int RoomId { get; set; }
|
||||
public bool AutoRecord { get; set; }
|
||||
public int ShortId { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Title { get; set; }
|
||||
public string? AreaNameParent { get; set; }
|
||||
public string? AreaNameChild { get; set; }
|
||||
public bool Recording { get; set; }
|
||||
public bool Streaming { get; set; }
|
||||
public bool DanmakuConnected { get; set; }
|
||||
public bool AutoRecordForThisSession { get; set; }
|
||||
}
|
||||
}
|
15
BililiveRecorder.Web/Models/RoomStatsDto.cs
Normal file
15
BililiveRecorder.Web/Models/RoomStatsDto.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
|
||||
namespace BililiveRecorder.Web.Models
|
||||
{
|
||||
public class RoomStatsDto
|
||||
{
|
||||
public TimeSpan SessionDuration { get; set; }
|
||||
public TimeSpan SessionMaxTimestamp { get; set; }
|
||||
public TimeSpan FileMaxTimestamp { get; set; }
|
||||
public double DurationRatio { get; set; }
|
||||
public long TotalInputBytes { get; set; }
|
||||
public long TotalOutputBytes { get; set; }
|
||||
public double NetworkMbps { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,5 +1,8 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using BililiveRecorder.Core;
|
||||
using BililiveRecorder.Web.Api;
|
||||
using BililiveRecorder.Web.Schemas;
|
||||
using GraphQL;
|
||||
using GraphQL.Server;
|
||||
|
@ -13,6 +16,7 @@ using Microsoft.Extensions.DependencyInjection;
|
|||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.OpenApi.Models;
|
||||
|
||||
namespace BililiveRecorder.Web
|
||||
{
|
||||
|
@ -38,6 +42,12 @@ namespace BililiveRecorder.Web
|
|||
|
||||
services.TryAddSingleton<IRecorder>(new FakeRecorderForWeb());
|
||||
|
||||
services.AddAutoMapper(c =>
|
||||
{
|
||||
c.AddProfile<DataMappingProfile>();
|
||||
});
|
||||
|
||||
// Graphql API
|
||||
services
|
||||
.AddCors(o => o.AddDefaultPolicy(p => p.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader()))
|
||||
.AddSingleton<RecorderSchema>()
|
||||
|
@ -49,34 +59,77 @@ namespace BililiveRecorder.Web
|
|||
var logger = provider.GetRequiredService<ILogger<Startup>>();
|
||||
options.UnhandledExceptionDelegate = ctx => logger.LogWarning(ctx.OriginalException, "Unhandled GraphQL Exception");
|
||||
})
|
||||
.AddDefaultEndpointSelectorPolicy()
|
||||
//.AddSystemTextJson()
|
||||
.AddNewtonsoftJson()
|
||||
.AddWebSockets()
|
||||
.AddGraphTypes(typeof(RecorderSchema))
|
||||
;
|
||||
|
||||
// REST API
|
||||
services
|
||||
.AddSwaggerGen(c =>
|
||||
{
|
||||
c.SwaggerDoc("brec", new OpenApiInfo
|
||||
{
|
||||
Title = "录播姬 REST API",
|
||||
Description = "录播姬网站 [rec.danmuji.org](https://rec.danmuji.org/) \n录播姬 GitHub [Bililive/BililiveRecorder](https://github.com/Bililive/BililiveRecorder) \n\n除了 REST API 以外,录播姬还有 Graphql API 可以使用。",
|
||||
Version = "v1"
|
||||
});
|
||||
|
||||
var filePath = Path.Combine(AppContext.BaseDirectory, "BililiveRecorder.Web.xml");
|
||||
c.IncludeXmlComments(filePath);
|
||||
})
|
||||
.AddRouting(c =>
|
||||
{
|
||||
c.LowercaseUrls = true;
|
||||
c.LowercaseQueryStrings = true;
|
||||
})
|
||||
.AddMvcCore(option =>
|
||||
{
|
||||
|
||||
})
|
||||
.AddApiExplorer();
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) => app
|
||||
.UseCors()
|
||||
.UseWebSockets()
|
||||
.UseGraphQLWebSockets<RecorderSchema>()
|
||||
.UseGraphQL<RecorderSchema>()
|
||||
.UseGraphQLPlayground()
|
||||
.UseGraphQLGraphiQL()
|
||||
.UseGraphQLAltair()
|
||||
.UseGraphQLVoyager()
|
||||
.Use(next => async context =>
|
||||
.UseRouting()
|
||||
.UseEndpoints(endpoints =>
|
||||
{
|
||||
if (context.Request.Path == "/")
|
||||
endpoints.MapControllers();
|
||||
|
||||
endpoints.MapGraphQL<RecorderSchema>();
|
||||
endpoints.MapGraphQLWebSockets<RecorderSchema>();
|
||||
|
||||
endpoints.MapSwagger();
|
||||
endpoints.MapGraphQLPlayground();
|
||||
endpoints.MapGraphQLGraphiQL();
|
||||
endpoints.MapGraphQLAltair();
|
||||
endpoints.MapGraphQLVoyager();
|
||||
|
||||
endpoints.MapGet("/", async context =>
|
||||
{
|
||||
context.Response.ContentType = "text/html";
|
||||
await context.Response.WriteAsync(ConstStrings.HOME_PAGE_HTML, encoding: System.Text.Encoding.UTF8).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
});
|
||||
|
||||
endpoints.MapGet("favicon.ico", async context =>
|
||||
{
|
||||
context.Response.Redirect("/");
|
||||
}
|
||||
context.Response.StatusCode = 404;
|
||||
await context.Response.WriteAsync(string.Empty);
|
||||
});
|
||||
})
|
||||
.UseSwaggerUI(c =>
|
||||
{
|
||||
c.SwaggerEndpoint("brec/swagger.json", "录播姬 REST API");
|
||||
})
|
||||
.Use(next => async context =>
|
||||
{
|
||||
context.Response.Redirect("/");
|
||||
await context.Response.WriteAsync(string.Empty);
|
||||
})
|
||||
;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user