BililiveRecorder/BililiveRecorder.WPF/Pages/RoomListPage.xaml.cs

402 lines
14 KiB
C#
Raw Normal View History

2020-11-27 18:51:02 +08:00
using System;
2020-12-05 18:30:04 +08:00
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
2020-12-10 17:02:56 +08:00
using System.Diagnostics;
2021-08-09 22:58:30 +08:00
using System.IO;
2020-11-27 18:51:02 +08:00
using System.Linq;
2020-12-05 18:30:04 +08:00
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
2020-11-27 18:51:02 +08:00
using BililiveRecorder.Core;
using BililiveRecorder.WPF.Controls;
2020-12-05 18:30:04 +08:00
using ModernWpf.Controls;
2021-02-23 18:03:37 +08:00
using Serilog;
2020-11-27 18:51:02 +08:00
2021-02-23 18:03:37 +08:00
#nullable enable
2020-11-27 18:51:02 +08:00
namespace BililiveRecorder.WPF.Pages
{
/// <summary>
/// Interaction logic for RoomList.xaml
/// </summary>
public partial class RoomListPage
{
2021-02-23 18:03:37 +08:00
private static readonly ILogger logger = Log.ForContext<RoomListPage>();
2020-11-27 18:51:02 +08:00
private static readonly Regex RoomIdRegex
= new Regex(@"^(?:https?:\/\/)?live\.bilibili\.com\/(?:blanc\/|h5\/)?(\d*)(?:\?.*)?$",
RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Compiled);
2021-02-23 18:03:37 +08:00
private readonly IRoom?[] NullRoom = new IRoom?[] { null };
2021-01-15 17:41:33 +08:00
private readonly KeyIndexMappingReadOnlyList NullRoomWithMapping;
2020-11-27 18:51:02 +08:00
public RoomListPage()
{
2021-01-15 17:41:33 +08:00
this.NullRoomWithMapping = new KeyIndexMappingReadOnlyList(this.NullRoom);
this.DataContextChanged += this.RoomListPage_DataContextChanged;
2021-01-01 14:46:27 +08:00
this.InitializeComponent();
2021-01-15 17:41:33 +08:00
}
2020-12-05 18:30:04 +08:00
2021-01-15 17:41:33 +08:00
private void RoomListPage_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
2021-02-23 18:03:37 +08:00
if (e.OldValue is IRecorder data_old) ((INotifyCollectionChanged)data_old.Rooms).CollectionChanged -= this.DataSource_CollectionChanged;
if (e.NewValue is IRecorder data_new) ((INotifyCollectionChanged)data_new.Rooms).CollectionChanged += this.DataSource_CollectionChanged;
2021-01-15 17:41:33 +08:00
this.ApplySort();
2020-12-05 18:30:04 +08:00
}
2021-01-15 17:41:33 +08:00
public static readonly DependencyProperty RoomListProperty =
DependencyProperty.Register(
nameof(RoomList),
typeof(object),
typeof(RoomListPage),
new PropertyMetadata(OnPropertyChanged));
public object RoomList
{
get => this.GetValue(RoomListProperty);
set => this.SetValue(RoomListProperty, value);
}
2020-12-05 18:30:04 +08:00
2021-01-15 17:41:33 +08:00
public static readonly DependencyProperty SortByProperty =
DependencyProperty.Register(
nameof(SortBy),
typeof(SortedBy),
typeof(RoomListPage),
new PropertyMetadata(OnPropertyChanged));
2020-12-05 18:30:04 +08:00
2021-01-15 17:41:33 +08:00
public SortedBy SortBy
2020-12-05 18:30:04 +08:00
{
2021-01-15 17:41:33 +08:00
get => (SortedBy)this.GetValue(SortByProperty);
2021-01-18 15:02:42 +08:00
set
2021-01-15 17:41:33 +08:00
{
2021-01-18 15:02:42 +08:00
this.SetValue(SortByProperty, value);
2021-01-15 17:41:33 +08:00
this.ApplySort();
}
}
2020-12-05 18:30:04 +08:00
2021-01-18 15:02:42 +08:00
private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) => ((RoomListPage)d).PrivateOnPropertyChanged(e);
private void PrivateOnPropertyChanged(DependencyPropertyChangedEventArgs e) { }
2021-01-15 17:41:33 +08:00
private void DataSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) => this.ApplySort();
private void ApplySort()
{
try
{
2021-02-23 18:03:37 +08:00
if (this.DataContext is not IRecorder recorder || recorder.Rooms.Count == 0)
2021-01-15 17:41:33 +08:00
{
this.RoomList = this.NullRoomWithMapping;
}
else
{
2021-02-23 18:03:37 +08:00
var data = recorder.Rooms;
IEnumerable<IRoom> orderedData = this.SortBy switch
2021-01-15 17:41:33 +08:00
{
2021-02-23 18:03:37 +08:00
SortedBy.RoomId => data.OrderBy(x => x.ShortId == 0 ? x.RoomConfig.RoomId : x.ShortId),
SortedBy.Status => from x in data orderby x.Recording descending, x.RoomConfig.AutoRecord descending, x.Streaming descending select x,
2021-01-15 17:41:33 +08:00
_ => data,
};
var result = new KeyIndexMappingReadOnlyList(orderedData.Concat(this.NullRoom).ToArray());
this.RoomList = result;
}
}
catch (Exception ex)
{
logger.Error(ex, "Error Sorting");
}
2020-11-27 18:51:02 +08:00
}
2021-02-23 18:03:37 +08:00
#pragma warning disable VSTHRD100 // Avoid async void methods
2020-11-27 18:51:02 +08:00
private async void RoomCard_DeleteRequested(object sender, EventArgs e)
2021-02-23 18:03:37 +08:00
#pragma warning restore VSTHRD100 // Avoid async void methods
2020-11-27 18:51:02 +08:00
{
2021-02-23 18:03:37 +08:00
if (this.DataContext is IRecorder rec && sender is IRoom room)
2020-11-27 18:51:02 +08:00
{
2021-01-29 20:31:25 +08:00
try
2020-11-27 18:51:02 +08:00
{
2021-01-29 20:31:25 +08:00
var dialog = new DeleteRoomConfirmDialog
{
DataContext = room,
Owner = Application.Current.MainWindow
2021-01-29 20:31:25 +08:00
};
2020-11-27 18:51:02 +08:00
var result = await dialog.ShowAndDisableMinimizeToTrayAsync();
2020-11-27 18:51:02 +08:00
2021-01-29 20:31:25 +08:00
if (result == ContentDialogResult.Primary)
{
rec.RemoveRoom(room);
}
2020-11-27 18:51:02 +08:00
}
2021-01-29 20:31:25 +08:00
catch (Exception) { }
2020-11-27 18:51:02 +08:00
}
}
2021-02-23 18:03:37 +08:00
#pragma warning disable VSTHRD100 // Avoid async void methods
2021-01-01 14:46:27 +08:00
private async void RoomCard_ShowSettingsRequested(object sender, EventArgs e)
2021-02-23 18:03:37 +08:00
#pragma warning restore VSTHRD100 // Avoid async void methods
2021-01-01 14:46:27 +08:00
{
try
{
await new PerRoomSettingsDialog
{
DataContext = sender,
Owner = Application.Current.MainWindow
}.ShowAndDisableMinimizeToTrayAsync();
2021-01-01 14:46:27 +08:00
}
catch (Exception) { }
}
2021-02-23 18:03:37 +08:00
#pragma warning disable VSTHRD100 // Avoid async void methods
2020-11-27 18:51:02 +08:00
private async void AddRoomCard_AddRoomRequested(object sender, string e)
2021-02-23 18:03:37 +08:00
#pragma warning restore VSTHRD100 // Avoid async void methods
2020-11-27 18:51:02 +08:00
{
var input = e.Trim();
2021-01-01 14:46:27 +08:00
if (string.IsNullOrWhiteSpace(input) || this.DataContext is not IRecorder rec) return;
2020-11-27 18:51:02 +08:00
if (!int.TryParse(input, out var roomid))
{
var m = RoomIdRegex.Match(input);
if (m.Success && m.Groups.Count > 1 && int.TryParse(m.Groups[1].Value, out var result2))
{
roomid = result2;
}
else
{
2021-01-29 20:31:25 +08:00
try
{
await new AddRoomFailedDialog
{
DataContext = AddRoomFailedDialog.AddRoomFailedErrorText.InvalidInput,
Owner = Application.Current.MainWindow
}.ShowAndDisableMinimizeToTrayAsync();
2021-01-29 20:31:25 +08:00
}
catch (Exception) { }
2020-11-27 18:51:02 +08:00
return;
}
}
if (roomid < 0)
{
2021-01-29 20:31:25 +08:00
try
{
await new AddRoomFailedDialog
{
DataContext = AddRoomFailedDialog.AddRoomFailedErrorText.RoomIdNegative,
Owner = Application.Current.MainWindow
}.ShowAndDisableMinimizeToTrayAsync();
2021-01-29 20:31:25 +08:00
}
catch (Exception) { }
2020-11-27 18:51:02 +08:00
return;
}
else if (roomid == 0)
{
2021-01-29 20:31:25 +08:00
try
{
await new AddRoomFailedDialog
{
DataContext = AddRoomFailedDialog.AddRoomFailedErrorText.RoomIdZero,
Owner = Application.Current.MainWindow
}.ShowAndDisableMinimizeToTrayAsync();
2021-01-29 20:31:25 +08:00
}
catch (Exception) { }
2020-11-27 18:51:02 +08:00
return;
}
2021-02-23 18:03:37 +08:00
if (rec.Rooms.Any(x => x.RoomConfig.RoomId == roomid || x.ShortId == roomid))
2020-11-27 18:51:02 +08:00
{
2021-01-29 20:31:25 +08:00
try
{
await new AddRoomFailedDialog
{
DataContext = AddRoomFailedDialog.AddRoomFailedErrorText.Duplicate,
Owner = Application.Current.MainWindow
}.ShowAndDisableMinimizeToTrayAsync();
2021-01-29 20:31:25 +08:00
}
catch (Exception) { }
2020-11-27 18:51:02 +08:00
return;
}
rec.AddRoom(roomid);
2020-12-05 18:30:04 +08:00
}
2021-02-23 18:03:37 +08:00
private void MenuItem_EnableAutoRecAll_Click(object sender, RoutedEventArgs e)
2020-12-05 18:30:04 +08:00
{
2021-01-08 12:17:31 +08:00
if (this.DataContext is not IRecorder rec) return;
2020-12-05 18:30:04 +08:00
2021-02-23 18:03:37 +08:00
foreach (var room in rec.Rooms)
room.RoomConfig.AutoRecord = true;
rec.SaveConfig();
2020-12-05 18:30:04 +08:00
}
2021-02-23 18:03:37 +08:00
private void MenuItem_DisableAutoRecAll_Click(object sender, RoutedEventArgs e)
2020-12-05 18:30:04 +08:00
{
2021-01-08 12:17:31 +08:00
if (this.DataContext is not IRecorder rec) return;
2020-12-05 18:30:04 +08:00
2021-02-23 18:03:37 +08:00
foreach (var room in rec.Rooms)
room.RoomConfig.AutoRecord = false;
rec.SaveConfig();
2020-12-05 18:30:04 +08:00
}
2021-01-15 17:41:33 +08:00
private void MenuItem_SortBy_Click(object sender, RoutedEventArgs e) => this.SortBy = (SortedBy)((MenuItem)sender).Tag;
2020-12-05 18:30:04 +08:00
private void MenuItem_ShowLog_Click(object sender, RoutedEventArgs e)
{
2021-01-01 14:46:27 +08:00
this.Splitter.Visibility = Visibility.Visible;
this.LogElement.Visibility = Visibility.Visible;
this.RoomListRowDefinition.Height = new GridLength(1, GridUnitType.Star);
this.LogRowDefinition.Height = new GridLength(1, GridUnitType.Star);
2020-12-05 18:30:04 +08:00
}
private void MenuItem_HideLog_Click(object sender, RoutedEventArgs e)
{
2021-01-01 14:46:27 +08:00
this.Splitter.Visibility = Visibility.Collapsed;
this.LogElement.Visibility = Visibility.Collapsed;
this.RoomListRowDefinition.Height = new GridLength(1, GridUnitType.Star);
this.LogRowDefinition.Height = new GridLength(0);
2020-12-05 18:30:04 +08:00
}
2021-01-08 12:17:31 +08:00
private void Log_ScrollViewer_Loaded(object sender, RoutedEventArgs e) => (sender as ScrollViewer)?.ScrollToEnd();
2020-12-05 18:30:04 +08:00
private void TextBlock_Copy_MouseRightButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
try
{
if (sender is TextBlock textBlock)
{
Clipboard.SetText(textBlock.Text);
}
}
catch (Exception)
{
}
2020-11-27 18:51:02 +08:00
}
2020-12-10 17:02:56 +08:00
private void MenuItem_OpenWorkDirectory_Click(object sender, RoutedEventArgs e)
{
try
{
2021-01-01 14:46:27 +08:00
if (this.DataContext is IRecorder rec)
Process.Start("explorer.exe", rec.Config.Global.WorkDirectory);
2020-12-10 17:02:56 +08:00
}
catch (Exception)
{
}
}
2021-01-08 18:54:50 +08:00
private void MenuItem_SaveConfig_Click(object sender, RoutedEventArgs e)
{
try
{
if (this.DataContext is IRecorder rec)
rec.SaveConfig();
}
catch (Exception)
{
}
}
private void MenuItem_ChangeWorkPath_Click(object sender, RoutedEventArgs e)
{
try
{
logger.Debug("ChangeWorkPath menu button invoked");
Process.Start(typeof(RoomListPage).Assembly.Location, "run --ask-path");
(Application.Current.MainWindow as NewMainWindow)?.CloseWithoutConfirmAction();
}
catch (Exception)
{ }
}
2021-01-08 18:54:50 +08:00
private void MenuItem_ShowHideTitleArea_Click(object sender, RoutedEventArgs e)
{
if (((MenuItem)sender).Tag is bool b && this.DataContext is IRecorder rec)
rec.Config.Global.WpfShowTitleAndArea = b;
}
2021-08-09 22:58:30 +08:00
private void MenuItem_ShowLogFilesInExplorer_Click(object sender, RoutedEventArgs e)
{
try
{
var logPath = Path.Combine(Path.GetDirectoryName(typeof(RoomListPage).Assembly.Location), "logs");
Process.Start("explorer.exe", logPath);
}
catch (Exception)
{ }
}
2020-11-27 18:51:02 +08:00
}
2020-12-05 18:30:04 +08:00
2021-01-15 17:41:33 +08:00
public enum SortedBy
2020-12-05 18:30:04 +08:00
{
None = 0,
RoomId,
Status,
}
2021-02-23 18:03:37 +08:00
internal class KeyIndexMappingReadOnlyList : IReadOnlyList<IRoom?>, IKeyIndexMapping
2020-12-05 18:30:04 +08:00
{
2021-02-23 18:03:37 +08:00
private readonly IReadOnlyList<IRoom?> data;
2020-12-05 18:30:04 +08:00
2021-02-23 18:03:37 +08:00
public KeyIndexMappingReadOnlyList(IReadOnlyList<IRoom?> data)
2020-12-05 18:30:04 +08:00
{
2021-01-15 17:41:33 +08:00
this.data = data;
2020-12-05 18:30:04 +08:00
}
2021-02-23 18:03:37 +08:00
public IRoom? this[int index] => this.data[index];
2020-12-05 18:30:04 +08:00
2021-01-15 17:41:33 +08:00
public int Count => this.data.Count;
2020-12-05 18:30:04 +08:00
2021-02-23 18:03:37 +08:00
public IEnumerator<IRoom?> GetEnumerator() => this.data.GetEnumerator();
2021-01-15 17:41:33 +08:00
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)this.data).GetEnumerator();
2020-12-05 18:30:04 +08:00
#region IKeyIndexMapping
private int lastRequestedIndex = IndexNotFound;
private const int IndexNotFound = -1;
// When UniqueIDs are supported, the ItemsRepeater caches the unique ID for each item
// with the matching UIElement that represents the item. When a reset occurs the
// ItemsRepeater pairs up the already generated UIElements with items in the data
// source.
// ItemsRepeater uses IndexForUniqueId after a reset to probe the data and identify
// the new index of an item to use as the anchor. If that item no
// longer exists in the data source it may try using another cached unique ID until
// either a match is found or it determines that all the previously visible items
// no longer exist.
public int IndexFromKey(string uniqueId)
{
// We'll try to increase our odds of finding a match sooner by starting from the
// position that we know was last requested and search forward.
2021-01-01 14:46:27 +08:00
var start = this.lastRequestedIndex;
for (var i = start; i < this.Count; i++)
2020-12-05 18:30:04 +08:00
{
2021-02-23 18:03:37 +08:00
if ((this[i]?.ObjectId ?? Guid.Empty).Equals(uniqueId))
2020-12-05 18:30:04 +08:00
return i;
}
// Then try searching backward.
2021-01-01 14:46:27 +08:00
start = Math.Min(this.Count - 1, this.lastRequestedIndex);
2020-12-05 18:30:04 +08:00
for (var i = start; i >= 0; i--)
{
2021-02-23 18:03:37 +08:00
if ((this[i]?.ObjectId ?? Guid.Empty).Equals(uniqueId))
2020-12-05 18:30:04 +08:00
return i;
}
return IndexNotFound;
}
public string KeyFromIndex(int index)
{
2021-02-23 18:03:37 +08:00
var key = this[index]?.ObjectId ?? Guid.Empty;
2021-01-01 14:46:27 +08:00
this.lastRequestedIndex = index;
2020-12-05 18:30:04 +08:00
return key.ToString();
}
#endregion
}
2020-11-27 18:51:02 +08:00
}