Generation of raid tiles with channel names and streaming status

This commit is contained in:
Enrico Ludwig 2024-08-19 16:28:02 +02:00
parent deda300eb2
commit 0aa931275b
7 changed files with 172 additions and 18 deletions

View File

@ -6,6 +6,7 @@ using Avalonia.Data.Core.Plugins;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using BetterRaid.ViewModels; using BetterRaid.ViewModels;
using BetterRaid.Views; using BetterRaid.Views;
using TwitchLib.Api;
using TwitchLib.Client; using TwitchLib.Client;
using TwitchLib.Client.Events; using TwitchLib.Client.Events;
using TwitchLib.Client.Models; using TwitchLib.Client.Models;
@ -20,6 +21,8 @@ public partial class App : Application
public static string TokenClientId = ""; public static string TokenClientId = "";
public static string TokenClientSecret = ""; public static string TokenClientSecret = "";
public static string TokenClientAccess = ""; public static string TokenClientAccess = "";
public static TwitchClient? TwitchClient = null;
public static TwitchAPI? TwitchAPI = null;
public override void Initialize() public override void Initialize()
{ {
@ -48,14 +51,18 @@ public partial class App : Application
}; };
var customClient = new WebSocketClient(clientOptions); var customClient = new WebSocketClient(clientOptions);
var client = new TwitchClient(customClient); TwitchClient = new TwitchClient(customClient);
client.Initialize(creds, TwitchChannelName); TwitchClient.Initialize(creds, TwitchChannelName);
client.OnMessageReceived += OnMessageReceived; TwitchClient.OnMessageReceived += OnMessageReceived;
client.OnConnected += OnConnected; TwitchClient.OnConnected += OnConnected;
client.OnConnectionError += OnConnectionError; TwitchClient.OnConnectionError += OnConnectionError;
client.Connect(); TwitchClient.Connect();
TwitchAPI = new TwitchAPI();
TwitchAPI.Settings.ClientId = TokenClientId;
TwitchAPI.Settings.AccessToken = TokenClientAccess;
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
} }

View File

@ -14,6 +14,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AsyncImageLoader.Avalonia" Version="3.3.0" />
<PackageReference Include="Avalonia" Version="11.1.0" /> <PackageReference Include="Avalonia" Version="11.1.0" />
<PackageReference Include="Avalonia.Desktop" Version="11.1.0" /> <PackageReference Include="Avalonia.Desktop" Version="11.1.0" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.1.0" /> <PackageReference Include="Avalonia.Themes.Fluent" Version="11.1.0" />

View File

@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17 # Visual Studio Version 17
VisualStudioVersion = 17.5.002.0 VisualStudioVersion = 17.5.002.0
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BetterRaid", "BetterRaid.csproj", "{25B567E7-5B84-4DF8-BAB4-1B802DC11876}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BetterRaid", "BetterRaid.csproj", "{050F930D-FE73-4FA8-A05B-E659A4A0CEEA}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -11,10 +11,10 @@ Global
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{25B567E7-5B84-4DF8-BAB4-1B802DC11876}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {050F930D-FE73-4FA8-A05B-E659A4A0CEEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{25B567E7-5B84-4DF8-BAB4-1B802DC11876}.Debug|Any CPU.Build.0 = Debug|Any CPU {050F930D-FE73-4FA8-A05B-E659A4A0CEEA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{25B567E7-5B84-4DF8-BAB4-1B802DC11876}.Release|Any CPU.ActiveCfg = Release|Any CPU {050F930D-FE73-4FA8-A05B-E659A4A0CEEA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{25B567E7-5B84-4DF8-BAB4-1B802DC11876}.Release|Any CPU.Build.0 = Release|Any CPU {050F930D-FE73-4FA8-A05B-E659A4A0CEEA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

14
Models/TwitchChannel.cs Normal file
View File

@ -0,0 +1,14 @@
namespace BetterRaid.Models;
public class TwitchChannel
{
public string? BroadcasterId { get; set; }
public string Name { get; set; }
public bool IsLive { get; set; }
public int ViewerCount { get; set; }
public TwitchChannel(string channelName)
{
Name = channelName;
}
}

View File

@ -2,7 +2,5 @@
public partial class MainWindowViewModel : ViewModelBase public partial class MainWindowViewModel : ViewModelBase
{ {
#pragma warning disable CA1822 // Mark members as static
public string Greeting => "Welcome to Avalonia!";
#pragma warning restore CA1822 // Mark members as static
} }

View File

@ -3,18 +3,29 @@
xmlns:vm="using:BetterRaid.ViewModels" xmlns:vm="using:BetterRaid.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="300" d:DesignHeight="450"
Width="600"
Height="800"
x:Class="BetterRaid.Views.MainWindow" x:Class="BetterRaid.Views.MainWindow"
x:DataType="vm:MainWindowViewModel" x:DataType="vm:MainWindowViewModel"
Icon="/Assets/avalonia-logo.ico" Icon="/Assets/avalonia-logo.ico"
Title="BetterRaid"> Title="BetterRaid">
<Design.DataContext> <Design.DataContext>
<!-- This only sets the DataContext for the previewer in an IDE,
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
<vm:MainWindowViewModel/> <vm:MainWindowViewModel/>
</Design.DataContext> </Design.DataContext>
<TextBlock Text="{Binding Greeting}" HorizontalAlignment="Center" VerticalAlignment="Center"/> <Grid
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
x:Name="raidGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
</Grid>
</Window> </Window>

View File

@ -1,11 +1,134 @@
using System;
using System.Collections.Generic;
using System.Linq;
using AsyncImageLoader;
using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Threading;
using BetterRaid.Models;
using BetterRaid.ViewModels;
using TwitchLib.Client.Events;
namespace BetterRaid.Views; namespace BetterRaid.Views;
public partial class MainWindow : Window public partial class MainWindow : Window
{ {
private string[] _channelNames = [
"Cedricun", // Ehrenbruder
"ZanTal", // Ehrenschwester
"PropzMaster",
"Artimus83",
"HyperonsLive",
"theshroomlife",
"Robocraft999",
"sllikson",
"Aron_dc",
"AIEsports"
];
public MainWindow() public MainWindow()
{ {
InitializeComponent(); InitializeComponent();
PrepareRaidGrid();
ConnectToTwitch();
}
private void PrepareRaidGrid()
{
var rows = (int)Math.Ceiling(_channelNames.Length / 3.0);
for (var i = 0; i < rows; i++)
{
raidGrid.RowDefinitions.Add(new RowDefinition(GridLength.Parse("200")));
}
var colIndex = 0;
var rowIndex = 0;
foreach (var channel in _channelNames)
{
var btn = new Button
{
Content = channel,
DataContext = new TwitchChannel(channel),
Margin = Thickness.Parse("5"),
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Stretch,
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Stretch,
HorizontalContentAlignment = Avalonia.Layout.HorizontalAlignment.Center,
VerticalContentAlignment = Avalonia.Layout.VerticalAlignment.Center
};
Grid.SetColumn(btn, colIndex);
Grid.SetRow(btn, rowIndex);
raidGrid.Children.Add(btn);
colIndex++;
if (colIndex % 3 == 0)
{
colIndex = 0;
rowIndex++;
}
}
}
private void ConnectToTwitch()
{
if (App.TwitchClient != null && App.TwitchAPI != null)
{
foreach (var c in raidGrid.Children)
{
if (c is Button btn)
{
var channel = (btn.DataContext as TwitchChannel)?.Name;
if (string.IsNullOrEmpty(channel) == false)
{
var channels = App.TwitchAPI.Helix.Search.SearchChannelsAsync(channel).Result;
var exactChannel = channels.Channels.FirstOrDefault(c => c.BroadcasterLogin.ToLower() == channel.ToLower());
Dispatcher.UIThread.Invoke(() =>
{
if (exactChannel != null)
{
if (btn.DataContext is TwitchChannel ctx)
{
ctx.BroadcasterId = exactChannel.Id;
var ib = new ImageBrush();
ImageBrushLoader.SetSource(ib, exactChannel.ThumbnailUrl);
btn.Background = ib;
var streamInfo = App.TwitchAPI.Helix.Streams.GetStreamsAsync(userLogins: new List<string>([channel])).Result;
var exactStreamInfo = streamInfo.Streams.FirstOrDefault(s => s.UserLogin.ToLower() == channel.ToLower());
if (exactStreamInfo != null)
{
if (exactChannel.IsLive)
{
btn.Foreground = new SolidColorBrush(new Color(byte.MaxValue, 0, byte.MaxValue, 0));
btn.Content = $"{exactChannel.DisplayName} ({exactStreamInfo.ViewerCount})";
}
else
{
btn.Foreground = new SolidColorBrush(new Color(byte.MaxValue, byte.MaxValue, 0, 0));
btn.Content = $"{exactChannel.DisplayName} (Offline)";
}
ctx.ViewerCount = exactStreamInfo.ViewerCount;
}
else
{
btn.Foreground = new SolidColorBrush(new Color(byte.MaxValue, byte.MaxValue, 0, 0));
btn.Content = $"{exactChannel.DisplayName} (Offline)";
}
}
}
});
}
}
}
}
} }
} }