mirror of
https://github.com/BililiveRecorder/BililiveRecorder.git
synced 2024-12-24 19:35:41 +08:00
Core: Re-write record trigger logic
This commit is contained in:
parent
fc0b955b3d
commit
5f4c9633bd
|
@ -21,6 +21,7 @@ namespace BililiveRecorder.Core
|
||||||
bool Recording { get; }
|
bool Recording { get; }
|
||||||
bool Streaming { get; }
|
bool Streaming { get; }
|
||||||
bool DanmakuConnected { get; }
|
bool DanmakuConnected { get; }
|
||||||
|
bool AutoRecordForThisSession { get; }
|
||||||
RecordingStats Stats { get; }
|
RecordingStats Stats { get; }
|
||||||
|
|
||||||
event EventHandler<RecordSessionStartedEventArgs>? RecordSessionStarted;
|
event EventHandler<RecordSessionStartedEventArgs>? RecordSessionStarted;
|
||||||
|
|
|
@ -21,7 +21,7 @@ namespace BililiveRecorder.Core
|
||||||
.Handle<Http412Exception>()
|
.Handle<Http412Exception>()
|
||||||
.CircuitBreakerAsync(
|
.CircuitBreakerAsync(
|
||||||
exceptionsAllowedBeforeBreaking: 1,
|
exceptionsAllowedBeforeBreaking: 1,
|
||||||
durationOfBreak: TimeSpan.FromMinutes(5),
|
durationOfBreak: TimeSpan.FromMinutes(2),
|
||||||
onBreak: (_, _) =>
|
onBreak: (_, _) =>
|
||||||
{
|
{
|
||||||
logger.Warning("检测到被屏蔽,暂停发送请求");
|
logger.Warning("检测到被屏蔽,暂停发送请求");
|
||||||
|
@ -59,7 +59,7 @@ namespace BililiveRecorder.Core
|
||||||
|
|
||||||
var retry = Policy.Handle<Exception>().RetryAsync();
|
var retry = Policy.Handle<Exception>().RetryAsync();
|
||||||
|
|
||||||
var bulkhead = Policy.BulkheadAsync(maxParallelization: 10, maxQueuingActions: 50);
|
var bulkhead = Policy.BulkheadAsync(maxParallelization: 5, maxQueuingActions: 200);
|
||||||
|
|
||||||
this.memoryCache = new MemoryCache(new MemoryCacheOptions());
|
this.memoryCache = new MemoryCache(new MemoryCacheOptions());
|
||||||
var memoryCacheProvider = new MemoryCacheProvider(this.memoryCache);
|
var memoryCacheProvider = new MemoryCacheProvider(this.memoryCache);
|
||||||
|
|
|
@ -41,8 +41,9 @@ namespace BililiveRecorder.Core
|
||||||
private string title = string.Empty;
|
private string title = string.Empty;
|
||||||
private string areaNameParent = string.Empty;
|
private string areaNameParent = string.Empty;
|
||||||
private string areaNameChild = string.Empty;
|
private string areaNameChild = string.Empty;
|
||||||
private bool streaming;
|
|
||||||
private bool danmakuConnected;
|
private bool danmakuConnected;
|
||||||
|
private bool streaming;
|
||||||
|
private bool autoRecordForThisSession = true;
|
||||||
|
|
||||||
private IRecordTask? recordTask;
|
private IRecordTask? recordTask;
|
||||||
private DateTimeOffset recordTaskStartTime;
|
private DateTimeOffset recordTaskStartTime;
|
||||||
|
@ -62,8 +63,11 @@ namespace BililiveRecorder.Core
|
||||||
this.cts = new CancellationTokenSource();
|
this.cts = new CancellationTokenSource();
|
||||||
this.ct = this.cts.Token;
|
this.ct = this.cts.Token;
|
||||||
|
|
||||||
|
this.PropertyChanged += this.Room_PropertyChanged;
|
||||||
this.RoomConfig.PropertyChanged += this.RoomConfig_PropertyChanged;
|
this.RoomConfig.PropertyChanged += this.RoomConfig_PropertyChanged;
|
||||||
|
|
||||||
this.timer.Elapsed += this.Timer_Elapsed;
|
this.timer.Elapsed += this.Timer_Elapsed;
|
||||||
|
|
||||||
this.danmakuClient.StatusChanged += this.DanmakuClient_StatusChanged;
|
this.danmakuClient.StatusChanged += this.DanmakuClient_StatusChanged;
|
||||||
this.danmakuClient.DanmakuReceived += this.DanmakuClient_DanmakuReceived;
|
this.danmakuClient.DanmakuReceived += this.DanmakuClient_DanmakuReceived;
|
||||||
|
|
||||||
|
@ -79,28 +83,14 @@ namespace BililiveRecorder.Core
|
||||||
public string Title { get => this.title; private set => this.SetField(ref this.title, value); }
|
public string Title { get => this.title; private set => this.SetField(ref this.title, value); }
|
||||||
public string AreaNameParent { get => this.areaNameParent; private set => this.SetField(ref this.areaNameParent, value); }
|
public string AreaNameParent { get => this.areaNameParent; private set => this.SetField(ref this.areaNameParent, value); }
|
||||||
public string AreaNameChild { get => this.areaNameChild; private set => this.SetField(ref this.areaNameChild, value); }
|
public string AreaNameChild { get => this.areaNameChild; private set => this.SetField(ref this.areaNameChild, value); }
|
||||||
public bool Streaming
|
|
||||||
{
|
|
||||||
get => this.streaming;
|
|
||||||
private set
|
|
||||||
{
|
|
||||||
if (value == this.streaming) return;
|
|
||||||
|
|
||||||
// 从未开播状态切换为开播状态时重置允许录制状态
|
public bool Streaming { get => this.streaming; private set => this.SetField(ref this.streaming, value); }
|
||||||
var triggerRecord = value && !this.streaming;
|
|
||||||
if (triggerRecord)
|
public bool AutoRecordForThisSession { get => this.autoRecordForThisSession; private set => this.SetField(ref this.autoRecordForThisSession, value); }
|
||||||
this.AutoRecordAllowedForThisSession = true;
|
|
||||||
|
|
||||||
this.streaming = value;
|
|
||||||
this.OnPropertyChanged(nameof(this.Streaming));
|
|
||||||
if (triggerRecord && this.RoomConfig.AutoRecord)
|
|
||||||
_ = Task.Run(() => this.CreateAndStartNewRecordTask());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public bool DanmakuConnected { get => this.danmakuConnected; private set => this.SetField(ref this.danmakuConnected, value); }
|
public bool DanmakuConnected { get => this.danmakuConnected; private set => this.SetField(ref this.danmakuConnected, value); }
|
||||||
public bool Recording => this.recordTask != null;
|
|
||||||
|
|
||||||
public bool AutoRecordAllowedForThisSession { get; private set; }
|
public bool Recording => this.recordTask != null;
|
||||||
|
|
||||||
public RoomConfig RoomConfig { get; }
|
public RoomConfig RoomConfig { get; }
|
||||||
public RecordingStats Stats { get; } = new RecordingStats();
|
public RecordingStats Stats { get; } = new RecordingStats();
|
||||||
|
@ -127,13 +117,13 @@ namespace BililiveRecorder.Core
|
||||||
{
|
{
|
||||||
lock (this.recordStartLock)
|
lock (this.recordStartLock)
|
||||||
{
|
{
|
||||||
this.AutoRecordAllowedForThisSession = true;
|
this.AutoRecordForThisSession = true;
|
||||||
|
|
||||||
_ = Task.Run(async () =>
|
_ = Task.Run(() =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await this.FetchRoomInfoThenCreateRecordTaskAsync().ConfigureAwait(false);
|
this.CreateAndStartNewRecordTask();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -147,7 +137,7 @@ namespace BililiveRecorder.Core
|
||||||
{
|
{
|
||||||
lock (this.recordStartLock)
|
lock (this.recordStartLock)
|
||||||
{
|
{
|
||||||
this.AutoRecordAllowedForThisSession = false;
|
this.AutoRecordForThisSession = false;
|
||||||
|
|
||||||
if (this.recordTask == null)
|
if (this.recordTask == null)
|
||||||
return;
|
return;
|
||||||
|
@ -163,6 +153,9 @@ namespace BililiveRecorder.Core
|
||||||
await this.FetchUserInfoAsync().ConfigureAwait(false);
|
await this.FetchUserInfoAsync().ConfigureAwait(false);
|
||||||
await this.FetchRoomInfoAsync().ConfigureAwait(false);
|
await this.FetchRoomInfoAsync().ConfigureAwait(false);
|
||||||
this.StartDamakuConnection(delay: false);
|
this.StartDamakuConnection(delay: false);
|
||||||
|
|
||||||
|
if (this.Streaming && this.AutoRecordForThisSession && this.RoomConfig.AutoRecord)
|
||||||
|
this.CreateAndStartNewRecordTask();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -194,13 +187,6 @@ namespace BililiveRecorder.Core
|
||||||
this.Name = user.Data?.Info?.Name ?? this.Name;
|
this.Name = user.Data?.Info?.Name ?? this.Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <exception cref="Exception"/>
|
|
||||||
private async Task FetchRoomInfoThenCreateRecordTaskAsync()
|
|
||||||
{
|
|
||||||
await this.FetchRoomInfoAsync().ConfigureAwait(false);
|
|
||||||
this.CreateAndStartNewRecordTask();
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
///
|
||||||
private void CreateAndStartNewRecordTask()
|
private void CreateAndStartNewRecordTask()
|
||||||
{
|
{
|
||||||
|
@ -237,8 +223,11 @@ namespace BililiveRecorder.Core
|
||||||
this.logger.Write(ex is ExecutionRejectedException ? LogEventLevel.Debug : LogEventLevel.Warning, ex, "启动录制出错");
|
this.logger.Write(ex is ExecutionRejectedException ? LogEventLevel.Debug : LogEventLevel.Warning, ex, "启动录制出错");
|
||||||
|
|
||||||
this.recordTask = null;
|
this.recordTask = null;
|
||||||
_ = Task.Run(async () => await this.TryRestartRecordingAsync().ConfigureAwait(false));
|
|
||||||
this.OnPropertyChanged(nameof(this.Recording));
|
this.OnPropertyChanged(nameof(this.Recording));
|
||||||
|
|
||||||
|
// 请求直播流出错时的重试逻辑
|
||||||
|
_ = Task.Run(async () => await this.RestartAfterRecordTaskFailedAsync().ConfigureAwait(false));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
RecordSessionStarted?.Invoke(this, new RecordSessionStartedEventArgs(this)
|
RecordSessionStarted?.Invoke(this, new RecordSessionStartedEventArgs(this)
|
||||||
|
@ -250,13 +239,12 @@ namespace BililiveRecorder.Core
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
private async Task TryRestartRecordingAsync(bool delay = true)
|
private async Task RestartAfterRecordTaskFailedAsync()
|
||||||
{
|
|
||||||
if (this.AutoRecordAllowedForThisSession)
|
|
||||||
{
|
{
|
||||||
|
if (!this.Streaming || !this.AutoRecordForThisSession)
|
||||||
|
return;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
|
||||||
if (delay)
|
|
||||||
{
|
{
|
||||||
if (!await this.recordRetryDelaySemaphoreSlim.WaitAsync(0).ConfigureAwait(false))
|
if (!await this.recordRetryDelaySemaphoreSlim.WaitAsync(0).ConfigureAwait(false))
|
||||||
return;
|
return;
|
||||||
|
@ -269,18 +257,21 @@ namespace BililiveRecorder.Core
|
||||||
{
|
{
|
||||||
this.recordRetryDelaySemaphoreSlim.Release();
|
this.recordRetryDelaySemaphoreSlim.Release();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.AutoRecordAllowedForThisSession)
|
if (!this.Streaming || !this.AutoRecordForThisSession)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await this.FetchRoomInfoThenCreateRecordTaskAsync().ConfigureAwait(false);
|
await this.FetchRoomInfoAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (this.Streaming && this.AutoRecordForThisSession)
|
||||||
|
this.CreateAndStartNewRecordTask();
|
||||||
}
|
}
|
||||||
catch (TaskCanceledException) { }
|
catch (TaskCanceledException) { }
|
||||||
|
catch (ExecutionRejectedException) { }
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
this.logger.Write(ex is ExecutionRejectedException ? LogEventLevel.Debug : LogEventLevel.Warning, ex, "重试开始录制时出错");
|
this.logger.Write(LogEventLevel.Warning, ex, "重试开始录制时出错");
|
||||||
}
|
_ = Task.Run(async () => await this.RestartAfterRecordTaskFailedAsync().ConfigureAwait(false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,7 +354,17 @@ namespace BililiveRecorder.Core
|
||||||
{
|
{
|
||||||
id = this.recordTask?.SessionId ?? default;
|
id = this.recordTask?.SessionId ?? default;
|
||||||
this.recordTask = null;
|
this.recordTask = null;
|
||||||
_ = Task.Run(async () => await this.TryRestartRecordingAsync(delay: false).ConfigureAwait(false));
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
// 录制结束退出后的重试逻辑
|
||||||
|
if (!this.Streaming || !this.AutoRecordForThisSession)
|
||||||
|
return;
|
||||||
|
|
||||||
|
await this.FetchRoomInfoAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (this.Streaming && this.AutoRecordForThisSession)
|
||||||
|
this.CreateAndStartNewRecordTask();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.basicDanmakuWriter.Disable();
|
this.basicDanmakuWriter.Disable();
|
||||||
|
@ -410,14 +411,44 @@ namespace BililiveRecorder.Core
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this.DanmakuConnected = false;
|
this.DanmakuConnected = false;
|
||||||
this.StartDamakuConnection();
|
this.StartDamakuConnection(delay: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
|
private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
|
||||||
{
|
{
|
||||||
|
this.StartDamakuConnection(delay: false);
|
||||||
|
|
||||||
if (this.RoomConfig.AutoRecord)
|
if (this.RoomConfig.AutoRecord)
|
||||||
_ = Task.Run(async () => await this.TryRestartRecordingAsync(delay: false).ConfigureAwait(false));
|
{
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await this.FetchRoomInfoAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (this.Streaming && this.AutoRecordForThisSession && this.RoomConfig.AutoRecord)
|
||||||
|
this.CreateAndStartNewRecordTask();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Room_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
switch (e.PropertyName)
|
||||||
|
{
|
||||||
|
case nameof(this.Streaming):
|
||||||
|
if (this.Streaming)
|
||||||
|
{
|
||||||
|
if (this.AutoRecordForThisSession && this.RoomConfig.AutoRecord)
|
||||||
|
this.CreateAndStartNewRecordTask();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.AutoRecordForThisSession = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RoomConfig_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
private void RoomConfig_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
|
@ -432,7 +463,11 @@ namespace BililiveRecorder.Core
|
||||||
break;
|
break;
|
||||||
case nameof(this.RoomConfig.AutoRecord):
|
case nameof(this.RoomConfig.AutoRecord):
|
||||||
if (this.RoomConfig.AutoRecord)
|
if (this.RoomConfig.AutoRecord)
|
||||||
this.AutoRecordAllowedForThisSession = true;
|
{
|
||||||
|
this.AutoRecordForThisSession = true;
|
||||||
|
if (this.Streaming && this.AutoRecordForThisSession)
|
||||||
|
this.CreateAndStartNewRecordTask();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -28,7 +28,6 @@
|
||||||
TextWrapping="NoWrap" TextTrimming="CharacterEllipsis" FontFamily="Microsoft Yahei"
|
TextWrapping="NoWrap" TextTrimming="CharacterEllipsis" FontFamily="Microsoft Yahei"
|
||||||
Text="{Binding Name,Mode=OneWay}" ToolTip="{Binding Name,Mode=OneWay}"
|
Text="{Binding Name,Mode=OneWay}" ToolTip="{Binding Name,Mode=OneWay}"
|
||||||
ContextMenu="{StaticResource CopyTextContextMenu}"/>
|
ContextMenu="{StaticResource CopyTextContextMenu}"/>
|
||||||
|
|
||||||
<Grid Grid.Row="1">
|
<Grid Grid.Row="1">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
|
@ -57,6 +56,9 @@
|
||||||
<ui:PathIcon Height="10" Margin="0,0,3,0" Style="{StaticResource PathIconDataAccessPoint}"
|
<ui:PathIcon Height="10" Margin="0,0,3,0" Style="{StaticResource PathIconDataAccessPoint}"
|
||||||
Foreground="{Binding Streaming,Converter={StaticResource BooleanToLiveStatusColorBrushConverter}}"
|
Foreground="{Binding Streaming,Converter={StaticResource BooleanToLiveStatusColorBrushConverter}}"
|
||||||
ToolTip="{Binding Streaming,Converter={StaticResource BooleanToLiveStatusTooltipConverter}}"/>
|
ToolTip="{Binding Streaming,Converter={StaticResource BooleanToLiveStatusTooltipConverter}}"/>
|
||||||
|
<ui:PathIcon Height="10" Margin="0,0,3,0" Style="{StaticResource PathIconDataVideoOffOutline}"
|
||||||
|
Visibility="{Binding AutoRecordForThisSession,Converter={StaticResource InvertBooleanToVisibilityCollapsedConverter}}"
|
||||||
|
Foreground="DarkOrange" ToolTip="本次直播不再自动录制,结束直播时恢复"/>
|
||||||
<ui:PathIcon Height="10" Style="{StaticResource PathIconDataUpperCaseIdentifier}" />
|
<ui:PathIcon Height="10" Style="{StaticResource PathIconDataUpperCaseIdentifier}" />
|
||||||
<TextBlock Text="{Binding RoomConfig.RoomId, StringFormat=\{0\},Mode=OneWay}" ContextMenu="{StaticResource CopyTextContextMenu}" Margin="4,0" FontSize="11"/>
|
<TextBlock Text="{Binding RoomConfig.RoomId, StringFormat=\{0\},Mode=OneWay}" ContextMenu="{StaticResource CopyTextContextMenu}" Margin="4,0" FontSize="11"/>
|
||||||
<ui:PathIcon Height="10" Style="{StaticResource PathIconDataLowerCaseIdentifier}" Margin="3,0"
|
<ui:PathIcon Height="10" Style="{StaticResource PathIconDataLowerCaseIdentifier}" Margin="3,0"
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
ui:TitleBar.IsBackEnabled="False"
|
ui:TitleBar.IsBackEnabled="False"
|
||||||
ui:TitleBar.IsBackButtonVisible="False"
|
ui:TitleBar.IsBackButtonVisible="False"
|
||||||
ui:TitleBar.IsIconVisible="True"
|
ui:TitleBar.IsIconVisible="True"
|
||||||
Width="960" Height="650" MinHeight="400" MinWidth="340"
|
Width="1000" Height="650" MinHeight="400" MinWidth="340"
|
||||||
WindowStartupLocation="CenterScreen"
|
WindowStartupLocation="CenterScreen"
|
||||||
FontFamily="Microsoft YaHei"
|
FontFamily="Microsoft YaHei"
|
||||||
Closing="Window_Closing" StateChanged="Window_StateChanged"
|
Closing="Window_Closing" StateChanged="Window_StateChanged"
|
||||||
|
|
|
@ -34,11 +34,11 @@
|
||||||
Null="{StaticResource AddRoomCardTemplate}"/>
|
Null="{StaticResource AddRoomCardTemplate}"/>
|
||||||
<converters:BoolToValueConverter x:Key="BooleanToUniformGridLayoutConverter">
|
<converters:BoolToValueConverter x:Key="BooleanToUniformGridLayoutConverter">
|
||||||
<converters:BoolToValueConverter.TrueValue>
|
<converters:BoolToValueConverter.TrueValue>
|
||||||
<ui:UniformGridLayout MinItemWidth="220" MinItemHeight="125"
|
<ui:UniformGridLayout MinItemWidth="230" MinItemHeight="125"
|
||||||
ItemsStretch="None" MinRowSpacing="7" MinColumnSpacing="5"/>
|
ItemsStretch="None" MinRowSpacing="7" MinColumnSpacing="5"/>
|
||||||
</converters:BoolToValueConverter.TrueValue>
|
</converters:BoolToValueConverter.TrueValue>
|
||||||
<converters:BoolToValueConverter.FalseValue>
|
<converters:BoolToValueConverter.FalseValue>
|
||||||
<ui:UniformGridLayout MinItemWidth="220" MinItemHeight="100"
|
<ui:UniformGridLayout MinItemWidth="230" MinItemHeight="100"
|
||||||
ItemsStretch="None" MinRowSpacing="7" MinColumnSpacing="5"/>
|
ItemsStretch="None" MinRowSpacing="7" MinColumnSpacing="5"/>
|
||||||
</converters:BoolToValueConverter.FalseValue>
|
</converters:BoolToValueConverter.FalseValue>
|
||||||
</converters:BoolToValueConverter>
|
</converters:BoolToValueConverter>
|
||||||
|
|
|
@ -85,6 +85,9 @@
|
||||||
<Style TargetType="ui:PathIcon" x:Key="PathIconDataMagnifyScan">
|
<Style TargetType="ui:PathIcon" x:Key="PathIconDataMagnifyScan">
|
||||||
<Setter Property="Data" Value="M17 22V20H20V17H22V20.5C22 20.89 21.84 21.24 21.54 21.54C21.24 21.84 20.89 22 20.5 22H17M7 22H3.5C3.11 22 2.76 21.84 2.46 21.54C2.16 21.24 2 20.89 2 20.5V17H4V20H7V22M17 2H20.5C20.89 2 21.24 2.16 21.54 2.46C21.84 2.76 22 3.11 22 3.5V7H20V4H17V2M7 2V4H4V7H2V3.5C2 3.11 2.16 2.76 2.46 2.46C2.76 2.16 3.11 2 3.5 2H7M10.5 6C13 6 15 8 15 10.5C15 11.38 14.75 12.2 14.31 12.9L17.57 16.16L16.16 17.57L12.9 14.31C12.2 14.75 11.38 15 10.5 15C8 15 6 13 6 10.5C6 8 8 6 10.5 6M10.5 8C9.12 8 8 9.12 8 10.5C8 11.88 9.12 13 10.5 13C11.88 13 13 11.88 13 10.5C13 9.12 11.88 8 10.5 8Z"/>
|
<Setter Property="Data" Value="M17 22V20H20V17H22V20.5C22 20.89 21.84 21.24 21.54 21.54C21.24 21.84 20.89 22 20.5 22H17M7 22H3.5C3.11 22 2.76 21.84 2.46 21.54C2.16 21.24 2 20.89 2 20.5V17H4V20H7V22M17 2H20.5C20.89 2 21.24 2.16 21.54 2.46C21.84 2.76 22 3.11 22 3.5V7H20V4H17V2M7 2V4H4V7H2V3.5C2 3.11 2.16 2.76 2.46 2.46C2.76 2.16 3.11 2 3.5 2H7M10.5 6C13 6 15 8 15 10.5C15 11.38 14.75 12.2 14.31 12.9L17.57 16.16L16.16 17.57L12.9 14.31C12.2 14.75 11.38 15 10.5 15C8 15 6 13 6 10.5C6 8 8 6 10.5 6M10.5 8C9.12 8 8 9.12 8 10.5C8 11.88 9.12 13 10.5 13C11.88 13 13 11.88 13 10.5C13 9.12 11.88 8 10.5 8Z"/>
|
||||||
</Style>
|
</Style>
|
||||||
|
<Style TargetType="ui:PathIcon" x:Key="PathIconDataVideoOffOutline">
|
||||||
|
<Setter Property="Data" Value="M3.41,1.86L2,3.27L4.73,6H4A1,1 0 0,0 3,7V17A1,1 0 0,0 4,18H16C16.21,18 16.39,17.92 16.55,17.82L19.73,21L21.14,19.59L12.28,10.73L3.41,1.86M5,16V8H6.73L14.73,16H5M15,8V10.61L21,16.61V6.5L17,10.5V7A1,1 0 0,0 16,6H10.39L12.39,8H15Z"/>
|
||||||
|
</Style>
|
||||||
<Style TargetType="ui:PathIcon" x:Key="PathIconData">
|
<Style TargetType="ui:PathIcon" x:Key="PathIconData">
|
||||||
<Setter Property="Data" Value=""/>
|
<Setter Property="Data" Value=""/>
|
||||||
</Style>
|
</Style>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user