using System; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Threading.Tasks; using BililiveRecorder.Core.Config; using BililiveRecorder.Core.Config.V2; using BililiveRecorder.Core.Event; using BililiveRecorder.Core.SimpleWebhook; namespace BililiveRecorder.Core { public class Recorder : IRecorder { private readonly object lockObject = new object(); private readonly ObservableCollection roomCollection; private readonly IRoomFactory roomFactory; private readonly BasicWebhookV1 basicWebhookV1; private readonly BasicWebhookV2 basicWebhookV2; private bool disposedValue; public Recorder(IRoomFactory roomFactory, ConfigV2 config) { this.roomFactory = roomFactory ?? throw new ArgumentNullException(nameof(roomFactory)); this.Config = config ?? throw new ArgumentNullException(nameof(config)); this.roomCollection = new ObservableCollection(); this.Rooms = new ReadOnlyObservableCollection(this.roomCollection); this.basicWebhookV1 = new BasicWebhookV1(config); this.basicWebhookV2 = new BasicWebhookV2(config.Global); { foreach (var item in config.Rooms) this.AddRoom(item); this.SaveConfig(); } } public event EventHandler>? RecordSessionStarted; public event EventHandler>? RecordSessionEnded; public event EventHandler>? RecordFileOpening; public event EventHandler>? RecordFileClosed; public event EventHandler>? NetworkingStats; public event EventHandler>? RecordingStats; public event PropertyChangedEventHandler? PropertyChanged; public ConfigV2 Config { get; } public ReadOnlyObservableCollection Rooms { get; } public void AddRoom(int roomid) => this.AddRoom(roomid, true); public void AddRoom(int roomid, bool enabled) { lock (this.lockObject) { var roomConfig = new RoomConfig { RoomId = roomid, AutoRecord = enabled }; this.AddRoom(roomConfig); this.SaveConfig(); } } private void AddRoom(RoomConfig roomConfig) { roomConfig.SetParent(this.Config.Global); var room = this.roomFactory.CreateRoom(roomConfig); this.roomCollection.Add(room); this.AddEventSubscription(room); } public void RemoveRoom(IRoom room) { lock (this.lockObject) { if (this.roomCollection.Remove(room)) { this.RemoveEventSubscription(room); room.Dispose(); } this.SaveConfig(); } } public void SaveConfig() { this.Config.Rooms = this.Rooms.Select(x => x.RoomConfig).ToList(); ConfigParser.SaveTo(this.Config.Global.WorkDirectory!, this.Config); } #region Events private void AddEventSubscription(IRoom room) { room.RecordSessionStarted += this.Room_RecordSessionStarted; room.RecordSessionEnded += this.Room_RecordSessionEnded; room.RecordFileOpening += this.Room_RecordFileOpening; room.RecordFileClosed += this.Room_RecordFileClosed; room.NetworkingStats += this.Room_NetworkingStats; room.RecordingStats += this.Room_RecordingStats; room.PropertyChanged += this.Room_PropertyChanged; } private void Room_NetworkingStats(object sender, NetworkingStatsEventArgs e) { var room = (IRoom)sender; NetworkingStats?.Invoke(this, new AggregatedRoomEventArgs(room, e)); } private void Room_RecordingStats(object sender, RecordingStatsEventArgs e) { var room = (IRoom)sender; RecordingStats?.Invoke(this, new AggregatedRoomEventArgs(room, e)); } private void Room_RecordFileClosed(object sender, RecordFileClosedEventArgs e) { var room = (IRoom)sender; _ = 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(room, e)); } private void Room_RecordFileOpening(object sender, RecordFileOpeningEventArgs e) { var room = (IRoom)sender; _ = Task.Run(async () => await this.basicWebhookV2.SendFileOpeningAsync(e).ConfigureAwait(false)); RecordFileOpening?.Invoke(this, new AggregatedRoomEventArgs(room, e)); } private void Room_RecordSessionStarted(object sender, RecordSessionStartedEventArgs e) { var room = (IRoom)sender; _ = Task.Run(async () => await this.basicWebhookV2.SendSessionStartedAsync(e).ConfigureAwait(false)); RecordSessionStarted?.Invoke(this, new AggregatedRoomEventArgs(room, e)); } private void Room_RecordSessionEnded(object sender, RecordSessionEndedEventArgs e) { var room = (IRoom)sender; _ = Task.Run(async () => await this.basicWebhookV2.SendSessionEndedAsync(e).ConfigureAwait(false)); RecordSessionEnded?.Invoke(this, new AggregatedRoomEventArgs(room, e)); } private void Room_PropertyChanged(object sender, PropertyChangedEventArgs e) { // TODO // throw new NotImplementedException(); } private void RemoveEventSubscription(IRoom room) { 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; } #endregion #region Dispose protected virtual void Dispose(bool disposing) { if (!this.disposedValue) { if (disposing) { // dispose managed state (managed objects) this.SaveConfig(); foreach (var room in this.roomCollection) room.Dispose(); } // free unmanaged resources (unmanaged objects) and override finalizer // set large fields to null this.disposedValue = true; } } // 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 this.Dispose(disposing: true); GC.SuppressFinalize(this); } #endregion } }