BililiveRecorder/BililiveRecorder.Core/Recorder.cs

230 lines
9.5 KiB
C#
Raw Normal View History

2018-03-21 20:56:56 +08:00
using System;
2018-03-12 18:57:20 +08:00
using System.Collections.ObjectModel;
2018-11-28 22:29:35 +08:00
using System.ComponentModel;
2018-03-24 09:48:06 +08:00
using System.Linq;
2021-02-23 18:03:37 +08:00
using System.Threading.Tasks;
2020-11-27 18:51:02 +08:00
using BililiveRecorder.Core.Config;
2021-12-19 21:10:34 +08:00
using BililiveRecorder.Core.Config.V3;
2021-02-23 18:03:37 +08:00
using BililiveRecorder.Core.Event;
using BililiveRecorder.Core.SimpleWebhook;
2021-04-20 20:41:26 +08:00
using Serilog;
2018-03-12 18:57:20 +08:00
namespace BililiveRecorder.Core
{
internal class Recorder : IRecorder
2018-03-12 18:57:20 +08:00
{
2021-02-23 18:03:37 +08:00
private readonly object lockObject = new object();
private readonly ObservableCollection<IRoom> roomCollection;
private readonly IRoomFactory roomFactory;
2021-04-20 20:41:26 +08:00
private readonly ILogger logger;
2021-02-23 18:03:37 +08:00
private readonly BasicWebhookV1 basicWebhookV1;
private readonly BasicWebhookV2 basicWebhookV2;
2018-03-21 20:56:56 +08:00
2020-11-27 18:51:02 +08:00
private bool disposedValue;
2021-12-19 21:10:34 +08:00
public Recorder(IRoomFactory roomFactory, ConfigV3 config, ILogger logger)
2018-03-20 00:12:32 +08:00
{
2021-02-23 18:03:37 +08:00
this.roomFactory = roomFactory ?? throw new ArgumentNullException(nameof(roomFactory));
this.Config = config ?? throw new ArgumentNullException(nameof(config));
2021-04-20 20:41:26 +08:00
this.logger = logger?.ForContext<Recorder>() ?? throw new ArgumentNullException(nameof(logger));
2021-02-23 18:03:37 +08:00
this.roomCollection = new ObservableCollection<IRoom>();
this.Rooms = new ReadOnlyObservableCollection<IRoom>(this.roomCollection);
2018-11-10 12:17:41 +08:00
2021-02-23 18:03:37 +08:00
this.basicWebhookV1 = new BasicWebhookV1(config);
this.basicWebhookV2 = new BasicWebhookV2(config.Global);
2018-03-20 00:12:32 +08:00
{
2021-05-17 23:29:33 +08:00
logger.Debug("Recorder created with {RoomCount} rooms", config.Rooms.Count);
2021-04-30 19:35:15 +08:00
for (var i = 0; i < config.Rooms.Count; i++)
{
var item = config.Rooms[i];
if (item is not null)
this.AddRoom(roomConfig: item, initDelayFactor: i);
}
2021-02-23 18:03:37 +08:00
this.SaveConfig();
}
2018-03-20 00:12:32 +08:00
}
2018-03-12 18:57:20 +08:00
2021-02-23 18:03:37 +08:00
public event EventHandler<AggregatedRoomEventArgs<RecordSessionStartedEventArgs>>? RecordSessionStarted;
public event EventHandler<AggregatedRoomEventArgs<RecordSessionEndedEventArgs>>? RecordSessionEnded;
public event EventHandler<AggregatedRoomEventArgs<RecordFileOpeningEventArgs>>? RecordFileOpening;
public event EventHandler<AggregatedRoomEventArgs<RecordFileClosedEventArgs>>? RecordFileClosed;
2021-12-19 00:56:41 +08:00
public event EventHandler<AggregatedRoomEventArgs<IOStatsEventArgs>>? IOStats;
2021-02-23 18:03:37 +08:00
public event EventHandler<AggregatedRoomEventArgs<RecordingStatsEventArgs>>? RecordingStats;
public event EventHandler<IRoom>? StreamStarted;
2022-05-17 00:53:37 +08:00
#pragma warning disable CS0067 // The event 'Recorder.PropertyChanged' is never used
2021-02-23 18:03:37 +08:00
public event PropertyChangedEventHandler? PropertyChanged;
2022-05-17 00:53:37 +08:00
#pragma warning restore CS0067 // The event 'Recorder.PropertyChanged' is never used
2021-01-04 16:24:36 +08:00
2021-12-19 21:10:34 +08:00
public ConfigV3 Config { get; }
2021-01-04 16:24:36 +08:00
2021-02-23 18:03:37 +08:00
public ReadOnlyObservableCollection<IRoom> Rooms { get; }
2021-01-04 16:24:36 +08:00
2021-05-30 19:16:20 +08:00
public IRoom AddRoom(int roomid) => this.AddRoom(roomid, true);
2018-11-10 12:17:41 +08:00
2021-05-30 19:16:20 +08:00
public IRoom AddRoom(int roomid, bool enabled)
2018-03-21 20:56:56 +08:00
{
2021-02-23 18:03:37 +08:00
lock (this.lockObject)
2018-10-25 19:20:23 +08:00
{
2021-05-17 23:29:33 +08:00
this.logger.Debug("AddRoom {RoomId}, AutoRecord: {AutoRecord}", roomid, enabled);
2021-02-23 18:03:37 +08:00
var roomConfig = new RoomConfig { RoomId = roomid, AutoRecord = enabled };
2021-05-30 19:16:20 +08:00
var room = this.AddRoom(roomConfig, 0);
2021-02-23 18:03:37 +08:00
this.SaveConfig();
2021-05-30 19:16:20 +08:00
return room;
2021-01-01 14:46:27 +08:00
}
}
2021-05-30 19:16:20 +08:00
private IRoom AddRoom(RoomConfig roomConfig, int initDelayFactor)
2021-01-01 14:46:27 +08:00
{
2021-02-23 18:03:37 +08:00
roomConfig.SetParent(this.Config.Global);
2021-04-30 19:35:15 +08:00
var room = this.roomFactory.CreateRoom(roomConfig, initDelayFactor);
room.RecordSessionStarted += this.Room_RecordSessionStarted;
room.RecordSessionEnded += this.Room_RecordSessionEnded;
room.RecordFileOpening += this.Room_RecordFileOpening;
room.RecordFileClosed += this.Room_RecordFileClosed;
2021-12-19 00:56:41 +08:00
room.IOStats += this.Room_IOStats;
room.RecordingStats += this.Room_RecordingStats;
room.PropertyChanged += this.Room_PropertyChanged;
2021-02-23 18:03:37 +08:00
this.roomCollection.Add(room);
2021-05-30 19:16:20 +08:00
return room;
2021-02-23 18:03:37 +08:00
}
2021-02-23 18:03:37 +08:00
public void RemoveRoom(IRoom room)
{
lock (this.lockObject)
2018-10-25 19:20:23 +08:00
{
2021-02-23 18:03:37 +08:00
if (this.roomCollection.Remove(room))
{
// 如果提前 detach 会导致 FileClosed SessionEnded 收不到
// 目前没有在各种 event 里再使用 room object
// 如果以后要使用 IRecorder 上的 event 再重新捋一遍防止出现奇怪 bug
// 此处不会导致内存泄漏
// room.RecordSessionStarted -= this.Room_RecordSessionStarted;
// room.RecordSessionEnded -= this.Room_RecordSessionEnded;
// room.RecordFileOpening -= this.Room_RecordFileOpening;
// room.RecordFileClosed -= this.Room_RecordFileClosed;
// room.RecordingStats -= this.Room_RecordingStats;
// room.PropertyChanged -= this.Room_PropertyChanged;
2021-05-17 23:29:33 +08:00
this.logger.Debug("RemoveRoom {RoomId}", room.RoomConfig.RoomId);
2021-02-23 18:03:37 +08:00
room.Dispose();
2021-05-17 23:29:33 +08:00
this.SaveConfig();
2021-02-23 18:03:37 +08:00
}
2018-10-25 19:20:23 +08:00
}
2018-03-21 20:56:56 +08:00
}
2021-02-23 18:03:37 +08:00
public void SaveConfig()
2018-03-24 02:27:58 +08:00
{
2021-02-23 18:03:37 +08:00
this.Config.Rooms = this.Rooms.Select(x => x.RoomConfig).ToList();
ConfigParser.Save(this.Config);
2018-03-24 02:27:58 +08:00
}
2018-03-24 09:48:06 +08:00
2021-02-23 18:03:37 +08:00
#region Events
2019-08-14 21:41:41 +08:00
2023-08-24 23:08:56 +08:00
private void Room_IOStats(object? sender, IOStatsEventArgs e)
2021-02-23 18:03:37 +08:00
{
2023-08-24 23:08:56 +08:00
if (sender is not IRoom room) return;
2021-12-19 00:56:41 +08:00
IOStats?.Invoke(this, new AggregatedRoomEventArgs<IOStatsEventArgs>(room, e));
2019-08-14 21:41:41 +08:00
}
2023-08-24 23:08:56 +08:00
private void Room_RecordingStats(object? sender, RecordingStatsEventArgs e)
2021-02-23 18:03:37 +08:00
{
2023-08-24 23:08:56 +08:00
if (sender is not IRoom room) return;
2021-02-23 18:03:37 +08:00
RecordingStats?.Invoke(this, new AggregatedRoomEventArgs<RecordingStatsEventArgs>(room, e));
}
2020-12-20 20:56:40 +08:00
2023-08-24 23:08:56 +08:00
private void Room_RecordFileClosed(object? sender, RecordFileClosedEventArgs e)
2019-08-14 21:41:41 +08:00
{
2023-08-24 23:08:56 +08:00
if (sender is not IRoom room) return;
2021-02-23 18:03:37 +08:00
_ = Task.Run(async () => await this.basicWebhookV2.SendFileClosedAsync(e).ConfigureAwait(false));
_ = Task.Run(async () => await this.basicWebhookV1.SendAsync(new RecordEndData(e)).ConfigureAwait(false));
RecordFileClosed?.Invoke(this, new AggregatedRoomEventArgs<RecordFileClosedEventArgs>(room, e));
}
2023-08-24 23:08:56 +08:00
private void Room_RecordFileOpening(object? sender, RecordFileOpeningEventArgs e)
2021-02-23 18:03:37 +08:00
{
2023-08-24 23:08:56 +08:00
if (sender is not IRoom room) return;
2021-02-23 18:03:37 +08:00
_ = Task.Run(async () => await this.basicWebhookV2.SendFileOpeningAsync(e).ConfigureAwait(false));
RecordFileOpening?.Invoke(this, new AggregatedRoomEventArgs<RecordFileOpeningEventArgs>(room, e));
2018-03-24 09:48:06 +08:00
}
2018-11-01 23:40:50 +08:00
2023-08-24 23:08:56 +08:00
private void Room_RecordSessionStarted(object? sender, RecordSessionStartedEventArgs e)
2018-11-01 23:40:50 +08:00
{
2023-08-24 23:08:56 +08:00
if (sender is not IRoom room) return;
2021-02-23 18:03:37 +08:00
_ = Task.Run(async () => await this.basicWebhookV2.SendSessionStartedAsync(e).ConfigureAwait(false));
RecordSessionStarted?.Invoke(this, new AggregatedRoomEventArgs<RecordSessionStartedEventArgs>(room, e));
2018-11-01 23:40:50 +08:00
}
2023-08-24 23:08:56 +08:00
private void Room_RecordSessionEnded(object? sender, RecordSessionEndedEventArgs e)
2021-02-23 18:03:37 +08:00
{
2023-08-24 23:08:56 +08:00
if (sender is not IRoom room) return;
2021-02-23 18:03:37 +08:00
_ = Task.Run(async () => await this.basicWebhookV2.SendSessionEndedAsync(e).ConfigureAwait(false));
RecordSessionEnded?.Invoke(this, new AggregatedRoomEventArgs<RecordSessionEndedEventArgs>(room, e));
}
2018-11-28 22:29:35 +08:00
2023-08-24 23:08:56 +08:00
private void Room_PropertyChanged(object? sender, PropertyChangedEventArgs e)
2020-11-27 18:51:02 +08:00
{
if (sender is not IRoom room)
return;
if (e.PropertyName == nameof(IRoom.Streaming))
{
if (room.Streaming)
{
_ = Task.Run(async () => await this.basicWebhookV2.SendStreamStartedAsync(new StreamStartedEventArgs(room)).ConfigureAwait(false));
_ = Task.Run(() => StreamStarted?.Invoke(this, room));
}
else
{
_ = Task.Run(async () => await this.basicWebhookV2.SendStreamEndedAsync(new StreamEndedEventArgs(room)).ConfigureAwait(false));
}
}
2021-02-23 18:03:37 +08:00
// TODO
// throw new NotImplementedException();
2020-11-27 18:51:02 +08:00
}
2021-02-23 18:03:37 +08:00
#endregion
#region Dispose
2020-11-27 18:51:02 +08:00
protected virtual void Dispose(bool disposing)
{
2021-01-01 14:46:27 +08:00
if (!this.disposedValue)
2020-11-27 18:51:02 +08:00
{
if (disposing)
{
// dispose managed state (managed objects)
2021-04-20 20:41:26 +08:00
this.logger.Debug("Dispose called");
2021-02-23 18:03:37 +08:00
this.SaveConfig();
foreach (var room in this.roomCollection)
room.Dispose();
2020-11-27 18:51:02 +08:00
}
// free unmanaged resources (unmanaged objects) and override finalizer
// set large fields to null
2021-01-01 14:46:27 +08:00
this.disposedValue = true;
2020-11-27 18:51:02 +08:00
}
}
// override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
// ~Recorder()
// {
// // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
// Dispose(disposing: false);
// }
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
2021-01-01 14:46:27 +08:00
this.Dispose(disposing: true);
2020-11-27 18:51:02 +08:00
GC.SuppressFinalize(this);
}
2021-02-23 18:03:37 +08:00
#endregion
2018-03-12 18:57:20 +08:00
}
}