From 70ddc61257686a8576f6d16df593a3fd538e4e30 Mon Sep 17 00:00:00 2001 From: Athul Raj Date: Sun, 4 Sep 2022 13:51:02 +0530 Subject: [PATCH] add more life to discover page --- AnimDL.UI.Core/Constants.cs | 6 ++ .../Contracts/IFeaturedAnimeProvider.cs | 12 +++ .../Contracts/IRecentEpisodesProvider.cs | 6 ++ AnimDL.UI.Core/Models/AiredEpisode.cs | 10 +++ AnimDL.UI.Core/Models/FeaturedAnime.cs | 24 ++++++ .../AnimixPlayFeaturedAnimeProvider.cs | 29 +++++++ .../Services/RecentEpisodesProvider.cs | 80 +++++++++++++++++++ .../ViewModels/DiscoverViewModel.cs | 62 ++++++-------- AnimDL.WinUI/AnimDL.WinUI.csproj | 1 + AnimDL.WinUI/App.xaml.cs | 1 + .../Helpers/ServiceCollectionExtensions.cs | 3 + AnimDL.WinUI/Views/DiscoverPage.xaml | 78 +++++++++++++++++- AnimDL.WinUI/Views/ShellPage.xaml | 2 +- 13 files changed, 275 insertions(+), 39 deletions(-) create mode 100644 AnimDL.UI.Core/Constants.cs create mode 100644 AnimDL.UI.Core/Contracts/IFeaturedAnimeProvider.cs create mode 100644 AnimDL.UI.Core/Contracts/IRecentEpisodesProvider.cs create mode 100644 AnimDL.UI.Core/Models/AiredEpisode.cs create mode 100644 AnimDL.UI.Core/Models/FeaturedAnime.cs create mode 100644 AnimDL.UI.Core/Services/AnimixPlayFeaturedAnimeProvider.cs create mode 100644 AnimDL.UI.Core/Services/RecentEpisodesProvider.cs diff --git a/AnimDL.UI.Core/Constants.cs b/AnimDL.UI.Core/Constants.cs new file mode 100644 index 00000000..b43b640e --- /dev/null +++ b/AnimDL.UI.Core/Constants.cs @@ -0,0 +1,6 @@ +namespace AnimDL.UI.Core; + +public class Constants +{ + public const string UserAgent = @"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.81 Safari/537.36 Edg/104.0.1293.54"; +} diff --git a/AnimDL.UI.Core/Contracts/IFeaturedAnimeProvider.cs b/AnimDL.UI.Core/Contracts/IFeaturedAnimeProvider.cs new file mode 100644 index 00000000..2e46eb36 --- /dev/null +++ b/AnimDL.UI.Core/Contracts/IFeaturedAnimeProvider.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AnimDL.UI.Core.Contracts; + +public interface IFeaturedAnimeProvider +{ + IObservable> GetFeaturedAnime(); +} diff --git a/AnimDL.UI.Core/Contracts/IRecentEpisodesProvider.cs b/AnimDL.UI.Core/Contracts/IRecentEpisodesProvider.cs new file mode 100644 index 00000000..eab88478 --- /dev/null +++ b/AnimDL.UI.Core/Contracts/IRecentEpisodesProvider.cs @@ -0,0 +1,6 @@ +namespace AnimDL.UI.Core.Contracts; + +public interface IRecentEpisodesProvider +{ + IObservable> GetRecentlyAiredEpisodes(); +} diff --git a/AnimDL.UI.Core/Models/AiredEpisode.cs b/AnimDL.UI.Core/Models/AiredEpisode.cs new file mode 100644 index 00000000..95d852ed --- /dev/null +++ b/AnimDL.UI.Core/Models/AiredEpisode.cs @@ -0,0 +1,10 @@ +namespace AnimDL.UI.Core.Models; + +public class AiredEpisode +{ + public string Anime { get; set; } + public string InfoText { get; set; } + public string EpisodeUrl { get; set; } + public string Image { get; set; } + public DateTime TimeOfAiring { get; set; } +} diff --git a/AnimDL.UI.Core/Models/FeaturedAnime.cs b/AnimDL.UI.Core/Models/FeaturedAnime.cs new file mode 100644 index 00000000..404f3620 --- /dev/null +++ b/AnimDL.UI.Core/Models/FeaturedAnime.cs @@ -0,0 +1,24 @@ +using System.Text.Json.Serialization; + +namespace AnimDL.UI.Core.Models; + +public class FeaturedAnime +{ + public string Id => Url?.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries).Skip(1).Take(1).FirstOrDefault(); + public string[] GenresArray => Genres?.Split(","); + + [JsonPropertyName("title")] + public string Title { get; set; } + + [JsonPropertyName("url")] + public string Url { get; set; } + + [JsonPropertyName("img")] + public string Image { get; set; } + + [JsonPropertyName("genre")] + public string Genres { get; set; } + + [JsonPropertyName("desc")] + public string Description { get; set; } +} \ No newline at end of file diff --git a/AnimDL.UI.Core/Services/AnimixPlayFeaturedAnimeProvider.cs b/AnimDL.UI.Core/Services/AnimixPlayFeaturedAnimeProvider.cs new file mode 100644 index 00000000..09ae18df --- /dev/null +++ b/AnimDL.UI.Core/Services/AnimixPlayFeaturedAnimeProvider.cs @@ -0,0 +1,29 @@ +namespace AnimDL.UI.Core.Services; + +public class AnimixPlayFeaturedAnimeProvider : IFeaturedAnimeProvider +{ + private readonly HttpClient _httpClient; + + public AnimixPlayFeaturedAnimeProvider(HttpClient httpClient) + { + _httpClient = httpClient; + _httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(Constants.UserAgent); + } + + public IObservable> GetFeaturedAnime() + { + return Observable.Create>(async observer => + { + var response = await _httpClient.GetAsync("https://animixplay.to/assets/s/featured.json"); + + if(!response.IsSuccessStatusCode) + { + observer.OnError(new Exception("response does not contain success code")); + } + + var stream = await response.Content.ReadAsStreamAsync(); + observer.OnNext(await JsonSerializer.DeserializeAsync>(stream)); + observer.OnCompleted(); + }); + } +} diff --git a/AnimDL.UI.Core/Services/RecentEpisodesProvider.cs b/AnimDL.UI.Core/Services/RecentEpisodesProvider.cs new file mode 100644 index 00000000..9f27aebd --- /dev/null +++ b/AnimDL.UI.Core/Services/RecentEpisodesProvider.cs @@ -0,0 +1,80 @@ +using System.Globalization; +using System.Text.Json.Nodes; + +namespace AnimDL.UI.Core.Services; + +public class AnimixPlayEpisodesProvider : IRecentEpisodesProvider +{ + private readonly HttpClient _httpClient; + + public AnimixPlayEpisodesProvider(HttpClient httpClient) + { + _httpClient = httpClient; + _httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(Constants.UserAgent); + } + + public IObservable> GetRecentlyAiredEpisodes() + { + return Observable.Create>(async observer => + { + Dictionary postData = new() + { + ["seasonal"] = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + }; + + 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://animixplay.to/api/search"); + request.Content = content; + var response = await _httpClient.SendAsync(request); + + if (!response.IsSuccessStatusCode) + { + observer.OnError(new Exception("http resonse doesn't contain success code")); + } + + var result = await response.Content.ReadAsStringAsync(); + + if (string.IsNullOrEmpty(result)) + { + observer.OnError(new Exception("empty json response")); + } + + var jObject = JsonNode.Parse(result); + var episodes = jObject["result"].AsArray(); + + if (episodes is null) + { + observer.OnError(new Exception("no episodes")); + } + + var models = new List(); + + foreach (var item in episodes) + { + try + { + var model = new AiredEpisode + { + Anime = (string)item["title"].AsValue(), + EpisodeUrl = $"https://animixplay.to{(string)item["url"].AsValue()}", + InfoText = (string)item["infotext"].AsValue(), + TimeOfAiring = DateTime.ParseExact((string)item["timetop"].AsValue(), "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture).ToLocalTime(), + Image = (string)item["picture"].AsValue() + }; + + models.Add(model); + } + catch { } + } + + observer.OnNext(models); + + observer.OnCompleted(); + + return Disposable.Empty; + }); + } +} diff --git a/AnimDL.UI.Core/ViewModels/DiscoverViewModel.cs b/AnimDL.UI.Core/ViewModels/DiscoverViewModel.cs index bd8d8a05..12e2f50e 100644 --- a/AnimDL.UI.Core/ViewModels/DiscoverViewModel.cs +++ b/AnimDL.UI.Core/ViewModels/DiscoverViewModel.cs @@ -1,17 +1,17 @@ -using System.Text.Json.Serialization; -using MalApi; -using MalApi.Interfaces; - -namespace AnimDL.UI.Core.ViewModels; +namespace AnimDL.UI.Core.ViewModels; public class DiscoverViewModel : NavigatableViewModel, IHaveState { - public DiscoverViewModel() - { + private readonly IRecentEpisodesProvider _recentEpisodesProvider; + private readonly IFeaturedAnimeProvider _featuredAnimeProvider; + public DiscoverViewModel(IRecentEpisodesProvider recentEpisodesProvider, + IFeaturedAnimeProvider featuredAnimeProvider) + { Observable .Timer(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10)) .ObserveOn(RxApp.MainThreadScheduler) + .Where(_ => Featured is not null) .Subscribe(_ => { if (Featured.Count == 0) @@ -28,47 +28,37 @@ public DiscoverViewModel() SelectedIndex++; }); + + _recentEpisodesProvider = recentEpisodesProvider; + _featuredAnimeProvider = featuredAnimeProvider; } - [Reactive] public ObservableCollection Featured { get; set; } + [Reactive] public IList Featured { get; set; } = new List(); + [Reactive] public IList Episodes { get; set; } = new List(); [Reactive] public int SelectedIndex { get; set; } public void RestoreState(IState state) { - Featured = state.GetValue>(nameof(Featured)); + Featured = state.GetValue>(nameof(Featured)); + Episodes = state.GetValue>(nameof(Episodes)); } - public async Task SetInitialState() + public Task SetInitialState() { - using var client = new HttpClient(); - client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.81 Safari/537.36 Edg/104.0.1293.54"); - var stream = await client.GetStreamAsync("https://animixplay.to/assets/s/featured.json"); - Featured = await JsonSerializer.DeserializeAsync>(stream); + _featuredAnimeProvider.GetFeaturedAnime() + .ObserveOn(RxApp.MainThreadScheduler) + .Subscribe(featured => Featured = featured.ToList()); + + _recentEpisodesProvider.GetRecentlyAiredEpisodes() + .ObserveOn(RxApp.MainThreadScheduler) + .Subscribe(eps => Episodes = eps.ToList()); + + return Task.CompletedTask; } public void StoreState(IState state) { state.AddOrUpdate(Featured); + state.AddOrUpdate(Episodes); } -} - -public class FeaturedAnime -{ - public string Id => Url?.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries).Skip(1).Take(1).FirstOrDefault(); - public string[] GenresArray => Genres?.Split(","); - - [JsonPropertyName("title")] - public string Title { get; set; } - - [JsonPropertyName("url")] - public string Url { get; set; } - - [JsonPropertyName("img")] - public string Image { get; set; } - - [JsonPropertyName("genre")] - public string Genres { get; set; } - - [JsonPropertyName("desc")] - public string Description { get; set; } -} +} \ No newline at end of file diff --git a/AnimDL.WinUI/AnimDL.WinUI.csproj b/AnimDL.WinUI/AnimDL.WinUI.csproj index cd823619..090b9f49 100644 --- a/AnimDL.WinUI/AnimDL.WinUI.csproj +++ b/AnimDL.WinUI/AnimDL.WinUI.csproj @@ -23,6 +23,7 @@ + diff --git a/AnimDL.WinUI/App.xaml.cs b/AnimDL.WinUI/App.xaml.cs index f006c0c9..5768fb0a 100644 --- a/AnimDL.WinUI/App.xaml.cs +++ b/AnimDL.WinUI/App.xaml.cs @@ -1,4 +1,5 @@ using AnimDL.Core; +using AnimDL.UI.Core.Services; using Microsoft.Extensions.Hosting; using Microsoft.UI.Xaml; using Windows.ApplicationModel; diff --git a/AnimDL.WinUI/Helpers/ServiceCollectionExtensions.cs b/AnimDL.WinUI/Helpers/ServiceCollectionExtensions.cs index d3f00804..d1f5fc22 100644 --- a/AnimDL.WinUI/Helpers/ServiceCollectionExtensions.cs +++ b/AnimDL.WinUI/Helpers/ServiceCollectionExtensions.cs @@ -97,6 +97,9 @@ public static IServiceCollection AddApplicationServices(this IServiceCollection services.AddSingleton(); services.AddSingleton(); services.AddMemoryCache(); + services.AddHttpClient(); + services.AddTransient(); + services.AddTransient(); return services; } diff --git a/AnimDL.WinUI/Views/DiscoverPage.xaml b/AnimDL.WinUI/Views/DiscoverPage.xaml index 5a26b829..b1ada1b4 100644 --- a/AnimDL.WinUI/Views/DiscoverPage.xaml +++ b/AnimDL.WinUI/Views/DiscoverPage.xaml @@ -2,8 +2,10 @@ x:Class="AnimDL.WinUI.Views.DiscoverPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:animations="using:CommunityToolkit.WinUI.UI.Animations" + xmlns:cm="using:AnimDL.UI.Core.Models" + xmlns:ctk="using:CommunityToolkit.WinUI.UI.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:malapi="using:MalApi" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:viewmodels="using:AnimDL.UI.Core.ViewModels" xmlns:views="using:AnimDL.WinUI.Views" @@ -23,7 +25,7 @@ ItemsSource="{x:Bind ViewModel.Featured, Mode=OneWay}" SelectedIndex="{x:Bind ViewModel.SelectedIndex, Mode=TwoWay}"> - + @@ -71,5 +73,77 @@ SelectedPageIndex="{x:Bind Gallery.SelectedIndex, Mode=TwoWay}" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AnimDL.WinUI/Views/ShellPage.xaml b/AnimDL.WinUI/Views/ShellPage.xaml index e6bbd751..cd8a40ed 100644 --- a/AnimDL.WinUI/Views/ShellPage.xaml +++ b/AnimDL.WinUI/Views/ShellPage.xaml @@ -42,7 +42,7 @@ + Icon="World" />