diff --git a/Totoro.Core/Contracts/ITimestampsService.cs b/Totoro.Core/Contracts/ITimestampsService.cs index ed82547a..4cbc123c 100644 --- a/Totoro.Core/Contracts/ITimestampsService.cs +++ b/Totoro.Core/Contracts/ITimestampsService.cs @@ -4,5 +4,6 @@ public interface ITimestampsService { Task GetTimeStamps(long id, int ep, double duration); Task SubmitTimeStamp(long id, int ep, string skipType, Interval interval, double episodeLength); + Task Vote(string skipId, bool isThumpsUp); } } \ No newline at end of file diff --git a/Totoro.Core/Contracts/IViewService.cs b/Totoro.Core/Contracts/IViewService.cs index 9cfb44a2..43566834 100644 --- a/Totoro.Core/Contracts/IViewService.cs +++ b/Totoro.Core/Contracts/IViewService.cs @@ -7,7 +7,7 @@ public interface IViewService Task Authenticate(ListServiceType type); Task PlayVideo(string title, string url); Task SelectModel(IEnumerable models) where T : class; - Task SubmitTimeStamp(long malId, int ep, VideoStream stream, double duration, double introStart); + Task SubmitTimeStamp(long malId, int ep, VideoStream stream, AniSkipResult existingResult, double duration, double introStart); Task Question(string title, string message); Task Information(string title, string message); } diff --git a/Totoro.Core/Services/TimestampsService.cs b/Totoro.Core/Services/TimestampsService.cs index 3fa7c22c..c4bc5f27 100644 --- a/Totoro.Core/Services/TimestampsService.cs +++ b/Totoro.Core/Services/TimestampsService.cs @@ -60,6 +60,24 @@ public async Task SubmitTimeStamp(long id, int ep, string skipType, Interval int this.Log().Info("Submitted : {0}", response.IsSuccessStatusCode); } + public async Task Vote(string skipId, bool isThumpsUp) + { + var voteType = isThumpsUp ? "upvote" : "downvote"; + var postData = new Dictionary() + { + ["voteType"] = voteType + }; + + using var content = new FormUrlEncodedContent(postData); + content.Headers.Clear(); + content.Headers.Add("Content-Type", "application/x-www-form-urlencoded"); + + using var request = new HttpRequestMessage(HttpMethod.Post, $"https://api.aniskip.com/v2/skip-times/vote/{skipId}"); + request.Content = content; + var response = await _httpClient.SendAsync(request); + this.Log().Info($"Vote ({voteType}) submitted"); + } + private async ValueTask GetMalId(long id) { if (_settings.DefaultListService == ListServiceType.MyAnimeList) diff --git a/Totoro.Core/Services/WindowsUpdateService.cs b/Totoro.Core/Services/WindowsUpdateService.cs index c1a092d1..c5a4d909 100644 --- a/Totoro.Core/Services/WindowsUpdateService.cs +++ b/Totoro.Core/Services/WindowsUpdateService.cs @@ -15,12 +15,14 @@ public class WindowsUpdateService : ReactiveObject, IUpdateService, IEnableLogge public IObservable OnUpdateAvailable => _onUpdate; public WindowsUpdateService(HttpClient httpClient, - ISettings settings) + ILocalSettingsService localSettingsService) { + var autoUpdateEnabled = localSettingsService.ReadSetting(nameof(ISettings.AutoUpdate), true); + _httpClient = httpClient; _onUpdate = Observable .Timer(TimeSpan.Zero, TimeSpan.FromHours(1)) - .Where(_ => settings.AutoUpdate) + .Where(_ => autoUpdateEnabled) .ObserveOn(RxApp.TaskpoolScheduler) .SelectMany(_ => httpClient.GetStreamAsync("https://api.github.com/repos/athulrajts/AnimDL.GUI/releases/latest")) .Select(x => JsonNode.Parse(x)) diff --git a/Totoro.Core/ViewModels/WatchViewModel.cs b/Totoro.Core/ViewModels/WatchViewModel.cs index e2d9dcad..de251f3f 100644 --- a/Totoro.Core/ViewModels/WatchViewModel.cs +++ b/Totoro.Core/ViewModels/WatchViewModel.cs @@ -430,7 +430,7 @@ private void OnSubmitTimeStamps() RxApp.MainThreadScheduler.Schedule(async () => { MediaPlayer.Pause(); - await _viewService.SubmitTimeStamp(Anime.Id, CurrentEpisode.Value, SelectedStream, CurrentMediaDuration, _userSkipOpeningTime == 0 ? 0 : _userSkipOpeningTime - 5); + await _viewService.SubmitTimeStamp(Anime.Id, CurrentEpisode.Value, SelectedStream, AniSkipResult, CurrentMediaDuration, _userSkipOpeningTime == 0 ? 0 : _userSkipOpeningTime - 5); MediaPlayer.Play(); }); } diff --git a/Totoro.WinUI/Dialogs/ViewModels/SubmitTimeStampsViewModel.cs b/Totoro.WinUI/Dialogs/ViewModels/SubmitTimeStampsViewModel.cs index 93af7f91..43c5a599 100644 --- a/Totoro.WinUI/Dialogs/ViewModels/SubmitTimeStampsViewModel.cs +++ b/Totoro.WinUI/Dialogs/ViewModels/SubmitTimeStampsViewModel.cs @@ -1,5 +1,4 @@ - -using Totoro.WinUI.Media; +using Totoro.WinUI.Media; namespace Totoro.WinUI.Dialogs.ViewModels; @@ -18,6 +17,22 @@ public SubmitTimeStampsViewModel(ITimestampsService timestampsService) SkipNearEnd = ReactiveCommand.Create(() => MediaPlayer.Seek(TimeSpan.FromSeconds(EndPosition - 5))); Submit = ReactiveCommand.CreateFromTask(SubmitTimeStamp); + var canVote = this.WhenAnyValue(x => x.SelectedTimeStampType, x => x.ExistingResult) + .Where(x => x.Item2 is not null) + .Select(tuple => + { + (string type, AniSkipResult result) = tuple; + return type switch + { + "OP" => result.Opening is not null, + "ED" => result.Ending is not null, + _ => false + }; + }); + + VoteUp = ReactiveCommand.Create(() => Vote(true), canVote); + VoteDown = ReactiveCommand.Create(() => Vote(false), canVote); + MediaPlayer .PositionChanged .ObserveOn(RxApp.MainThreadScheduler) @@ -27,18 +42,21 @@ public SubmitTimeStampsViewModel(ITimestampsService timestampsService) .DistinctUntilChanged() .Subscribe(x => MediaPlayer.Seek(TimeSpan.FromSeconds(x))); - this.WhenAnyValue(x => x.SelectedTimeStampType) - .Subscribe(type => + this.WhenAnyValue(x => x.SelectedTimeStampType, x => x.ExistingResult) + .Subscribe(tuple => { + (string type, AniSkipResult result) = tuple; + if (type == "OP") { - StartPosition = SuggestedStartPosition; + StartPosition = result?.Opening?.Interval?.StartTime ?? SuggestedStartPosition; + EndPosition = result?.Opening?.Interval?.EndTime ?? StartPosition + 90; } else if (type == "ED") { - StartPosition = SuggestedEndPosition; + StartPosition = result?.Ending?.Interval?.StartTime ?? SuggestedEndPosition; + EndPosition = result?.Ending?.Interval?.EndTime ?? StartPosition + 90; } - EndPosition = StartPosition + 90; }); this.WhenAnyValue(x => x.Stream) @@ -51,6 +69,7 @@ public SubmitTimeStampsViewModel(ITimestampsService timestampsService) [Reactive] public string SelectedTimeStampType { get; set; } = "OP"; [Reactive] public double CurrentPlayerPosition { get; set; } [Reactive] public VideoStream Stream { get; set; } + [Reactive] public AniSkipResult ExistingResult { get; set; } public long MalId { get; set; } public int Episode { get; set; } public double Duration { get; set; } @@ -64,6 +83,8 @@ public SubmitTimeStampsViewModel(ITimestampsService timestampsService) public ICommand SetEndPosition { get; } public ICommand SkipNearEnd { get; } public ICommand Submit { get; } + public ICommand VoteUp { get; } + public ICommand VoteDown { get; } private void Play() { @@ -79,6 +100,18 @@ private void Play() }); } + private void Vote(bool vote) + { + if (SelectedTimeStampType == "OP" && ExistingResult.Opening is { } op) + { + _timestampsService.Vote(op.SkipId, vote); + } + else if (SelectedTimeStampType == "ED" && ExistingResult.Ending is { } ed) + { + _timestampsService.Vote(ed.SkipId, vote); + } + } + public async Task SubmitTimeStamp() { await _timestampsService.SubmitTimeStamp(MalId, Episode, SelectedTimeStampType.ToLower(), new Interval { StartTime = StartPosition, EndTime = EndPosition }, Duration); diff --git a/Totoro.WinUI/Dialogs/Views/SubmitTimeStampsView.xaml b/Totoro.WinUI/Dialogs/Views/SubmitTimeStampsView.xaml index 2338e59b..8dee937b 100644 --- a/Totoro.WinUI/Dialogs/Views/SubmitTimeStampsView.xaml +++ b/Totoro.WinUI/Dialogs/Views/SubmitTimeStampsView.xaml @@ -24,10 +24,22 @@ - + + + + + + return vm.SelectedModel as T; } - public async Task SubmitTimeStamp(long malId, int ep, VideoStream stream, double duration, double introStart) + public async Task SubmitTimeStamp(long malId, int ep, VideoStream stream, AniSkipResult existingResult, double duration, double introStart) { var vm = new SubmitTimeStampsViewModel(App.GetService()) // TODO fix later { @@ -122,7 +122,8 @@ public async Task SubmitTimeStamp(long malId, int ep, VideoStream stream, double StartPosition = introStart, SuggestedStartPosition = introStart, EndPosition = introStart + 85, - Duration = duration + Duration = duration, + ExistingResult = existingResult }; await _contentDialogService.ShowDialog(vm, d =>