Files
app/windows/StreamPlayer.Desktop/Views/PlayerWindow.axaml.cs
2025-12-17 19:20:55 +00:00

230 lines
6.8 KiB
C#

using System;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Markup.Xaml;
using Avalonia.Threading;
using LibVLCSharp.Avalonia;
using LibVLCSharp.Shared;
using StreamPlayer.Desktop.Models;
using StreamPlayer.Desktop.Services;
namespace StreamPlayer.Desktop.Views;
public partial class PlayerWindow : Window
{
private readonly StreamChannel _channel;
private readonly StreamUrlResolver _resolver = new();
private readonly CancellationTokenSource _lifetimeCts = new();
private LibVLC? _libVlc;
private MediaPlayer? _mediaPlayer;
private bool _overlayVisible = true;
private TextBlock? _channelNameText;
private Border? _loadingOverlay;
private Border? _errorOverlay;
private TextBlock? _statusText;
private VideoView? _videoView;
private TextBlock? _errorText;
private Border? _overlay;
public PlayerWindow() : this(new StreamChannel("Canal", string.Empty))
{
}
public PlayerWindow(StreamChannel channel)
{
_channel = channel;
InitializeComponent();
if (_channelNameText != null)
{
_channelNameText.Text = channel.Name;
}
Title = $"Reproduciendo {channel.Name}";
}
protected override void OnOpened(EventArgs e)
{
base.OnOpened(e);
_ = LoadAndPlayAsync();
}
private async Task LoadAndPlayAsync()
{
try
{
await Dispatcher.UIThread.InvokeAsync(() =>
{
if (_loadingOverlay != null)
{
_loadingOverlay.IsVisible = true;
}
if (_errorOverlay != null)
{
_errorOverlay.IsVisible = false;
}
if (_statusText != null)
{
_statusText.Text = "Resolviendo stream…";
}
});
string resolved = await _resolver.ResolveAsync(_channel.PageUrl, _lifetimeCts.Token);
await Dispatcher.UIThread.InvokeAsync(() => StartPlayback(resolved));
}
catch (OperationCanceledException)
{
// ignored
}
catch (Exception ex)
{
await Dispatcher.UIThread.InvokeAsync(() => ShowError($"No se pudo iniciar el stream: {ex.Message}"));
}
}
private void StartPlayback(string streamUrl)
{
ReleasePlayer();
_libVlc = new LibVLC();
_mediaPlayer = new MediaPlayer(_libVlc);
_mediaPlayer.Playing += MediaPlayerOnPlaying;
_mediaPlayer.Buffering += MediaPlayerOnBuffering;
_mediaPlayer.EncounteredError += MediaPlayerOnEncounteredError;
_mediaPlayer.EndReached += MediaPlayerOnEndReached;
if (_videoView != null)
{
_videoView.MediaPlayer = _mediaPlayer;
}
using var media = new Media(_libVlc, new Uri(streamUrl));
media.AddOption($":http-referrer={_channel.PageUrl}");
media.AddOption(":http-header=Origin: https://streamtpmedia.com");
media.AddOption(":http-header=Accept: */*");
media.AddOption(":http-header=Connection: keep-alive");
media.AddOption(":network-caching=1500");
media.AddOption(":clock-jitter=0");
media.AddOption(":clock-synchro=0");
media.AddOption(":adaptive-use-access=true");
media.AddOption(":http-user-agent=Mozilla/5.0 (Linux; Android 13; Pixel 7 Build/TQ3A.230805.001) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0 Mobile Safari/537.36 ExoPlayer/2.18.7");
media.AddOption(":input-repeat=-1");
_mediaPlayer.Play(media);
}
private void MediaPlayerOnEndReached(object? sender, EventArgs e)
{
Dispatcher.UIThread.Post(() =>
{
if (_loadingOverlay != null)
{
_loadingOverlay.IsVisible = false;
}
});
}
private void MediaPlayerOnEncounteredError(object? sender, EventArgs e)
{
Dispatcher.UIThread.Post(() => ShowError("Error en la reproducción. Intenta nuevamente más tarde."));
}
private void MediaPlayerOnBuffering(object? sender, MediaPlayerBufferingEventArgs e)
{
Dispatcher.UIThread.Post(() =>
{
if (_loadingOverlay != null)
{
_loadingOverlay.IsVisible = true;
}
if (_statusText != null)
{
_statusText.Text = "Conectando…";
}
});
}
private void MediaPlayerOnPlaying(object? sender, EventArgs e)
{
Dispatcher.UIThread.Post(() =>
{
if (_loadingOverlay != null)
{
_loadingOverlay.IsVisible = false;
}
SetOverlayVisible(false);
});
}
private void ShowError(string message)
{
if (_loadingOverlay != null)
{
_loadingOverlay.IsVisible = false;
}
if (_errorOverlay != null)
{
_errorOverlay.IsVisible = true;
}
if (_errorText != null)
{
_errorText.Text = message;
}
}
private void ReleasePlayer()
{
if (_mediaPlayer != null)
{
_mediaPlayer.Playing -= MediaPlayerOnPlaying;
_mediaPlayer.Buffering -= MediaPlayerOnBuffering;
_mediaPlayer.EncounteredError -= MediaPlayerOnEncounteredError;
_mediaPlayer.EndReached -= MediaPlayerOnEndReached;
_mediaPlayer.Dispose();
_mediaPlayer = null;
}
if (_libVlc != null)
{
_libVlc.Dispose();
_libVlc = null;
}
}
private void OnVideoPointerPressed(object? sender, PointerPressedEventArgs e)
{
SetOverlayVisible(!_overlayVisible);
}
private void SetOverlayVisible(bool visible)
{
_overlayVisible = visible;
if (_overlay != null)
{
_overlay.IsVisible = visible;
}
}
private void OnCloseClicked(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
{
Close();
}
protected override void OnClosing(WindowClosingEventArgs e)
{
base.OnClosing(e);
_lifetimeCts.Cancel();
ReleasePlayer();
_lifetimeCts.Dispose();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
_channelNameText = this.FindControl<TextBlock>("ChannelNameText");
_loadingOverlay = this.FindControl<Border>("LoadingOverlay");
_errorOverlay = this.FindControl<Border>("ErrorOverlay");
_statusText = this.FindControl<TextBlock>("StatusText");
_videoView = this.FindControl<VideoView>("VideoView");
_errorText = this.FindControl<TextBlock>("ErrorText");
_overlay = this.FindControl<Border>("Overlay");
}
}