Phase B (T1-5/T1-6/T2-2/T2-8/T2-12): download manager flyout w/ speed+pause/resume/cancel, bookmark bar, tab sleeping, tab search (open+closed), context menu w/ 'download video from this link' (79 tests GREEN)
This commit is contained in:
parent
af19dba8f4
commit
0caa1111e7
9 changed files with 763 additions and 3 deletions
108
VideoDownloader.App/Browser/Features/PageDownloadTracker.cs
Normal file
108
VideoDownloader.App/Browser/Features/PageDownloadTracker.cs
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace VideoDownloader.App.Browser.Features;
|
||||
|
||||
/// <summary>CoreWebView2DownloadOperation 의 최소 추상화(단위 테스트 fake 교체 가능).</summary>
|
||||
public interface IDownloadOperation
|
||||
{
|
||||
string Uri { get; }
|
||||
string FileName { get; }
|
||||
long BytesReceived { get; }
|
||||
long TotalBytesToReceive { get; }
|
||||
/// <summary>InProgress / Interrupted / Completed / Cancelled</summary>
|
||||
string State { get; }
|
||||
void Pause();
|
||||
void Resume();
|
||||
void Cancel();
|
||||
/// <summary>BytesReceived/State 변경 통지(구현은 호스트 스레드에서 발화).</summary>
|
||||
event Action? Changed;
|
||||
}
|
||||
|
||||
/// <summary>다운로드 매니저 UI 항목(T1-5) — 진행률/속도/상태를 INPC 로 노출.</summary>
|
||||
public sealed class PageDownloadItem : INotifyPropertyChanged
|
||||
{
|
||||
public IDownloadOperation Op { get; }
|
||||
public string FileName { get; }
|
||||
public string Uri { get; }
|
||||
public long TotalBytes { get; }
|
||||
private long _received;
|
||||
private long _bps;
|
||||
private string _state;
|
||||
|
||||
public PageDownloadItem(IDownloadOperation op, string fileName)
|
||||
{
|
||||
Op = op;
|
||||
FileName = fileName;
|
||||
Uri = op.Uri;
|
||||
TotalBytes = op.TotalBytesToReceive;
|
||||
_received = op.BytesReceived;
|
||||
_state = op.State;
|
||||
op.Changed += () =>
|
||||
{
|
||||
_received = op.BytesReceived;
|
||||
_state = op.State;
|
||||
OnPropertyChangedAll();
|
||||
};
|
||||
}
|
||||
|
||||
public double Percent => TotalBytes > 0 ? Math.Clamp(_received * 100.0 / TotalBytes, 0, 100) : 0;
|
||||
public long BytesPerSecond => _bps;
|
||||
public string State => _state;
|
||||
public bool IsFinished => _state is "Completed" or "Interrupted" or "Cancelled";
|
||||
public bool CanControl => _state == "InProgress";
|
||||
|
||||
public string SpeedText => _bps switch
|
||||
{
|
||||
<= 0 => "—",
|
||||
< 1024 => $"{(double)_bps:F1} B/s",
|
||||
< 1024 * 1024 => $"{_bps / 1024.0:F1} KB/s",
|
||||
_ => $"{_bps / 1024.0 / 1024.0:F1} MB/s",
|
||||
};
|
||||
|
||||
private long? _lastSpeedReceived;
|
||||
|
||||
/// <summary>속도 갱신: (현재까지 수신 바이트, 직전 호출로부터 경과).</summary>
|
||||
public void UpdateSpeed(long receivedNow, TimeSpan elapsed)
|
||||
{
|
||||
_received = receivedNow;
|
||||
if (elapsed.TotalSeconds > 0)
|
||||
{
|
||||
var delta = Math.Max(0, receivedNow - (_lastSpeedReceived ?? 0));
|
||||
_bps = (long)(delta / elapsed.TotalSeconds);
|
||||
}
|
||||
_lastSpeedReceived = receivedNow;
|
||||
OnPropertyChangedAll();
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
private void OnPropertyChangedAll()
|
||||
{
|
||||
foreach (var n in new[] { nameof(Percent), nameof(BytesPerSecond), nameof(SpeedText), nameof(State), nameof(IsFinished), nameof(CanControl) })
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(n));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>페이지 다운로드 목록(T1-5) — 최신순, 일시정지/재개/취소, 완료 정리.</summary>
|
||||
public sealed class PageDownloadTracker
|
||||
{
|
||||
public ObservableCollection<PageDownloadItem> Items { get; } = new();
|
||||
|
||||
public PageDownloadItem Add(IDownloadOperation op, string fileName)
|
||||
{
|
||||
var item = new PageDownloadItem(op, fileName);
|
||||
Items.Insert(0, item);
|
||||
return item;
|
||||
}
|
||||
|
||||
public void Pause(PageDownloadItem item) => item.Op.Pause();
|
||||
public void Resume(PageDownloadItem item) => item.Op.Resume();
|
||||
public void Cancel(PageDownloadItem item) => item.Op.Cancel();
|
||||
|
||||
public void ClearCompleted()
|
||||
{
|
||||
foreach (var done in Items.Where(i => i.IsFinished).ToList())
|
||||
Items.Remove(done);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue