Skip to content

Commit

Permalink
add more life to discover page
Browse files Browse the repository at this point in the history
  • Loading branch information
insomniachi committed Sep 4, 2022
1 parent b216d0d commit 70ddc61
Show file tree
Hide file tree
Showing 13 changed files with 275 additions and 39 deletions.
6 changes: 6 additions & 0 deletions AnimDL.UI.Core/Constants.cs
Original file line number Diff line number Diff line change
@@ -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";
}
12 changes: 12 additions & 0 deletions AnimDL.UI.Core/Contracts/IFeaturedAnimeProvider.cs
Original file line number Diff line number Diff line change
@@ -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<IEnumerable<FeaturedAnime>> GetFeaturedAnime();
}
6 changes: 6 additions & 0 deletions AnimDL.UI.Core/Contracts/IRecentEpisodesProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace AnimDL.UI.Core.Contracts;

public interface IRecentEpisodesProvider
{
IObservable<IEnumerable<AiredEpisode>> GetRecentlyAiredEpisodes();
}
10 changes: 10 additions & 0 deletions AnimDL.UI.Core/Models/AiredEpisode.cs
Original file line number Diff line number Diff line change
@@ -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; }
}
24 changes: 24 additions & 0 deletions AnimDL.UI.Core/Models/FeaturedAnime.cs
Original file line number Diff line number Diff line change
@@ -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; }
}
29 changes: 29 additions & 0 deletions AnimDL.UI.Core/Services/AnimixPlayFeaturedAnimeProvider.cs
Original file line number Diff line number Diff line change
@@ -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<IEnumerable<FeaturedAnime>> GetFeaturedAnime()
{
return Observable.Create<IEnumerable<FeaturedAnime>>(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<List<FeaturedAnime>>(stream));
observer.OnCompleted();
});
}
}
80 changes: 80 additions & 0 deletions AnimDL.UI.Core/Services/RecentEpisodesProvider.cs
Original file line number Diff line number Diff line change
@@ -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<IEnumerable<AiredEpisode>> GetRecentlyAiredEpisodes()
{
return Observable.Create<IEnumerable<AiredEpisode>>(async observer =>
{
Dictionary<string, string> 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<AiredEpisode>();

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;
});
}
}
62 changes: 26 additions & 36 deletions AnimDL.UI.Core/ViewModels/DiscoverViewModel.cs
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -28,47 +28,37 @@ public DiscoverViewModel()

SelectedIndex++;
});

_recentEpisodesProvider = recentEpisodesProvider;
_featuredAnimeProvider = featuredAnimeProvider;
}

[Reactive] public ObservableCollection<FeaturedAnime> Featured { get; set; }
[Reactive] public IList<FeaturedAnime> Featured { get; set; } = new List<FeaturedAnime>();
[Reactive] public IList<AiredEpisode> Episodes { get; set; } = new List<AiredEpisode>();
[Reactive] public int SelectedIndex { get; set; }

public void RestoreState(IState state)
{
Featured = state.GetValue<ObservableCollection<FeaturedAnime>>(nameof(Featured));
Featured = state.GetValue<IList<FeaturedAnime>>(nameof(Featured));
Episodes = state.GetValue<IList<AiredEpisode>>(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<ObservableCollection<FeaturedAnime>>(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; }
}
}
1 change: 1 addition & 0 deletions AnimDL.WinUI/AnimDL.WinUI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<PackageReference Include="CommunityToolkit.WinUI.UI.Controls" Version="7.1.2" />
<PackageReference Include="DiscordRichPresence" Version="1.0.175" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.1.4" />
<PackageReference Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="2.0.8" />
<PackageReference Include="ReactiveMarbles.ObservableEvents.SourceGenerator" Version="1.1.4">
Expand Down
1 change: 1 addition & 0 deletions AnimDL.WinUI/App.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using AnimDL.Core;
using AnimDL.UI.Core.Services;
using Microsoft.Extensions.Hosting;
using Microsoft.UI.Xaml;
using Windows.ApplicationModel;
Expand Down
3 changes: 3 additions & 0 deletions AnimDL.WinUI/Helpers/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ public static IServiceCollection AddApplicationServices(this IServiceCollection
services.AddSingleton<IVolatileStateStorage, VolatileStateStorage>();
services.AddSingleton<ISchedule, Schedule>();
services.AddMemoryCache();
services.AddHttpClient();
services.AddTransient<IRecentEpisodesProvider, AnimixPlayEpisodesProvider>();
services.AddTransient<IFeaturedAnimeProvider, AnimixPlayFeaturedAnimeProvider>();

return services;
}
Expand Down
78 changes: 76 additions & 2 deletions AnimDL.WinUI/Views/DiscoverPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -23,7 +25,7 @@
ItemsSource="{x:Bind ViewModel.Featured, Mode=OneWay}"
SelectedIndex="{x:Bind ViewModel.SelectedIndex, Mode=TwoWay}">
<FlipView.ItemTemplate>
<DataTemplate x:DataType="viewmodels:FeaturedAnime">
<DataTemplate x:DataType="cm:FeaturedAnime">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
Expand Down Expand Up @@ -71,5 +73,77 @@
SelectedPageIndex="{x:Bind Gallery.SelectedIndex, Mode=TwoWay}" />
</Grid>

<Grid Grid.Row="1">
<GridView
x:Name="AnimeListView"
Grid.Row="1"
animations:ItemsReorderAnimation.Duration="00:00:00.4000000"
IsItemClickEnabled="True"
ItemsSource="{x:Bind ViewModel.Episodes, Mode=OneWay}">
<GridView.ItemTemplate>
<DataTemplate x:DataType="cm:AiredEpisode">
<Grid
x:Name="MainGrid"
Width="190"
Height="320">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ctk:ImageEx
Grid.Row="0"
CacheMode="BitmapCache"
IsCacheEnabled="True"
Source="{x:Bind Image, Mode=OneWay}"
Stretch="UniformToFill" />

<Grid
x:Name="NextEpisodeInContainer"
Grid.Row="0"
Height="30"
HorizontalAlignment="Right"
VerticalAlignment="Top">
<Grid.Background>
<SolidColorBrush Opacity="0.5" Color="Black" />
</Grid.Background>
<TextBlock
x:Name="NextEpisodeInText"
FontSize="20"
Foreground="White" />
</Grid>

<Border
x:Name="Border"
Grid.Row="1"
Height="60"
Padding="3"
Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}"
BorderThickness="0,4,0,0">
<StackPanel>
<TextBlock
Margin="0,3,0,0"
Padding="3"
VerticalAlignment="Center"
FontSize="15"
Text="{x:Bind Anime, Mode=TwoWay}"
TextAlignment="Center"
TextTrimming="WordEllipsis"
TextWrapping="NoWrap"
ToolTipService.ToolTip="{x:Bind Anime, Mode=OneWay}" />
<TextBlock HorizontalAlignment="Center" Text="{x:Bind InfoText}" />
</StackPanel>
</Border>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemContainerStyle>
<Style TargetType="GridViewItem">
<Setter Property="Margin" Value="10" />
</Style>
</GridView.ItemContainerStyle>
</GridView>

</Grid>

</Grid>
</views:DiscoverPageBase>
2 changes: 1 addition & 1 deletion AnimDL.WinUI/Views/ShellPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
<NavigationViewItem
helpers:NavigationHelper.NavigateTo="AnimDL.UI.Core.ViewModels.DiscoverViewModel"
Content="Discover"
Icon="Map" />
Icon="World" />
<NavigationViewItem
x:Uid="Shell_UserList"
helpers:NavigationHelper.NavigateTo="AnimDL.UI.Core.ViewModels.UserListViewModel"
Expand Down

0 comments on commit 70ddc61

Please sign in to comment.