Add - Added edit button for history overview and series
Chg - Detect special episodes in series to ignore them from new episodes Chg - History always tries to take the original version to not have multiple versions of the same season Fix - Updater failed because it couldn't update itself (for the fix you need to download the new updater)
This commit is contained in:
parent
76ddd9241a
commit
3e33843bc0
|
@ -137,7 +137,7 @@ public class CrSeries(Crunchyroll crunInstance){
|
|||
var s = result[season][key];
|
||||
if (data?.S != null && s.Id != data.Value.S) continue;
|
||||
int fallbackIndex = 0;
|
||||
var seasonData = await GetSeasonDataById(s);
|
||||
var seasonData = await GetSeasonDataById(s.Id);
|
||||
if (seasonData.Data != null){
|
||||
|
||||
if (crunInstance.CrunOptions.History){
|
||||
|
@ -268,7 +268,7 @@ public class CrSeries(Crunchyroll crunInstance){
|
|||
return crunchySeriesList;
|
||||
}
|
||||
|
||||
public async Task<CrunchyEpisodeList> GetSeasonDataById(SeriesSearchItem item, bool log = false){
|
||||
public async Task<CrunchyEpisodeList> GetSeasonDataById(string seasonID, bool log = false){
|
||||
CrunchyEpisodeList episodeList = new CrunchyEpisodeList(){ Data = new List<CrunchyEpisode>(), Total = 0, Meta = new Meta() };
|
||||
|
||||
if (crunInstance.CmsToken?.Cms == null){
|
||||
|
@ -277,7 +277,7 @@ public class CrSeries(Crunchyroll crunInstance){
|
|||
}
|
||||
|
||||
if (log){
|
||||
var showRequest = HttpClientReq.CreateRequestMessage($"{Api.Cms}/seasons/{item.Id}?preferred_audio_language=ja-JP", HttpMethod.Get, true, true, null);
|
||||
var showRequest = HttpClientReq.CreateRequestMessage($"{Api.Cms}/seasons/{seasonID}?preferred_audio_language=ja-JP", HttpMethod.Get, true, true, null);
|
||||
|
||||
var response = await HttpClientReq.Instance.SendHttpRequest(showRequest);
|
||||
|
||||
|
@ -290,7 +290,7 @@ public class CrSeries(Crunchyroll crunInstance){
|
|||
|
||||
//TODO
|
||||
|
||||
var episodeRequest = new HttpRequestMessage(HttpMethod.Get, $"{Api.Cms}/seasons/{item.Id}/episodes?preferred_audio_language=ja-JP");
|
||||
var episodeRequest = new HttpRequestMessage(HttpMethod.Get, $"{Api.Cms}/seasons/{seasonID}/episodes?preferred_audio_language=ja-JP");
|
||||
|
||||
episodeRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", crunInstance.Token?.access_token);
|
||||
|
||||
|
|
|
@ -35,7 +35,20 @@ public class History(Crunchyroll crunInstance){
|
|||
foreach (var key in result[season].Keys){
|
||||
var s = result[season][key];
|
||||
if (!string.IsNullOrEmpty(seasonId) && s.Id != seasonId) continue;
|
||||
var seasonData = await crunInstance.CrSeries.GetSeasonDataById(s);
|
||||
|
||||
var sId = s.Id;
|
||||
if (s.Versions is{ Count: > 0 }){
|
||||
foreach (var sVersion in s.Versions){
|
||||
if (sVersion.Original == true){
|
||||
if (sVersion.Guid != null){
|
||||
sId = sVersion.Guid;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var seasonData = await crunInstance.CrSeries.GetSeasonDataById(sId);
|
||||
UpdateWithSeasonData(seasonData);
|
||||
}
|
||||
}
|
||||
|
@ -107,7 +120,7 @@ public class History(Crunchyroll crunInstance){
|
|||
|
||||
historySeries.Seasons.Add(newSeason);
|
||||
|
||||
historySeries.Seasons = historySeries.Seasons.OrderBy(s => s.SeasonNum).ToList();
|
||||
historySeries.Seasons = historySeries.Seasons.OrderBy(s => s.SeasonNum != null ? int.Parse(s.SeasonNum) : 0).ToList();
|
||||
}
|
||||
historySeries.UpdateNewEpisodes();
|
||||
} else{
|
||||
|
@ -148,15 +161,24 @@ public class History(Crunchyroll crunInstance){
|
|||
|
||||
if (historySeason != null){
|
||||
foreach (var crunchyEpisode in seasonData.Data){
|
||||
if (historySeason.EpisodesList.All(e => e.EpisodeId != crunchyEpisode.Id)){
|
||||
|
||||
var historyEpisode = historySeason.EpisodesList.Find(e => e.EpisodeId == crunchyEpisode.Id);
|
||||
|
||||
if (historyEpisode == null){
|
||||
var newHistoryEpisode = new HistoryEpisode{
|
||||
EpisodeTitle = crunchyEpisode.Title,
|
||||
EpisodeId = crunchyEpisode.Id,
|
||||
Episode = crunchyEpisode.Episode,
|
||||
SpecialEpisode = !int.TryParse(crunchyEpisode.Episode, out _),
|
||||
};
|
||||
|
||||
historySeason.EpisodesList.Add(newHistoryEpisode);
|
||||
} else{
|
||||
//Update existing episode
|
||||
historyEpisode.EpisodeTitle = crunchyEpisode.Title;
|
||||
historyEpisode.SpecialEpisode = !int.TryParse(crunchyEpisode.Episode, out _);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
historySeason.EpisodesList.Sort(new NumericStringPropertyComparer());
|
||||
|
@ -167,7 +189,7 @@ public class History(Crunchyroll crunInstance){
|
|||
|
||||
historySeries.Seasons.Add(newSeason);
|
||||
|
||||
historySeries.Seasons = historySeries.Seasons.OrderBy(s => s.SeasonNum).ToList();
|
||||
historySeries.Seasons = historySeries.Seasons.OrderBy(s => s.SeasonNum != null ? int.Parse(s.SeasonNum) : 0).ToList();
|
||||
}
|
||||
historySeries.UpdateNewEpisodes();
|
||||
} else{
|
||||
|
@ -190,6 +212,7 @@ public class History(Crunchyroll crunInstance){
|
|||
|
||||
|
||||
newHistorySeries.Seasons.Add(newSeason);
|
||||
|
||||
newHistorySeries.UpdateNewEpisodes();
|
||||
}
|
||||
}
|
||||
|
@ -235,6 +258,7 @@ public class History(Crunchyroll crunInstance){
|
|||
EpisodeTitle = crunchyEpisode.Title,
|
||||
EpisodeId = crunchyEpisode.Id,
|
||||
Episode = crunchyEpisode.Episode,
|
||||
SpecialEpisode = !int.TryParse(crunchyEpisode.Episode, out _),
|
||||
};
|
||||
|
||||
newSeason.EpisodesList.Add(newHistoryEpisode);
|
||||
|
@ -255,6 +279,7 @@ public class History(Crunchyroll crunInstance){
|
|||
EpisodeTitle = episode.Title,
|
||||
EpisodeId = episode.Id,
|
||||
Episode = episode.Episode,
|
||||
SpecialEpisode = !int.TryParse(episode.Episode, out _),
|
||||
};
|
||||
|
||||
newSeason.EpisodesList.Add(newHistoryEpisode);
|
||||
|
@ -328,7 +353,7 @@ public class HistorySeries : INotifyPropertyChanged{
|
|||
|
||||
// Iterate over the Episodes from the end to the beginning
|
||||
for (int j = Seasons[i].EpisodesList.Count - 1; j >= 0 && !foundWatched; j--){
|
||||
if (!Seasons[i].EpisodesList[j].WasDownloaded){
|
||||
if (!Seasons[i].EpisodesList[j].WasDownloaded && !Seasons[i].EpisodesList[j].SpecialEpisode){
|
||||
count++;
|
||||
} else{
|
||||
foundWatched = true;
|
||||
|
@ -351,7 +376,7 @@ public class HistorySeries : INotifyPropertyChanged{
|
|||
|
||||
// Iterate over the Episodes from the end to the beginning
|
||||
for (int j = Seasons[i].EpisodesList.Count - 1; j >= 0 && !foundWatched; j--){
|
||||
if (!Seasons[i].EpisodesList[j].WasDownloaded){
|
||||
if (!Seasons[i].EpisodesList[j].WasDownloaded && !Seasons[i].EpisodesList[j].SpecialEpisode){
|
||||
//ADD to download queue
|
||||
await Seasons[i].EpisodesList[j].DownloadEpisode();
|
||||
} else{
|
||||
|
@ -419,6 +444,9 @@ public partial class HistoryEpisode : INotifyPropertyChanged{
|
|||
[JsonProperty("episode_was_downloaded")]
|
||||
public bool WasDownloaded{ get; set; }
|
||||
|
||||
[JsonProperty("episode_special_episode")]
|
||||
public bool SpecialEpisode{ get; set; }
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
public void ToggleWasDownloaded(){
|
||||
|
|
|
@ -32,7 +32,7 @@ public struct SeriesSearchItem{
|
|||
[JsonProperty("season_number")] public int SeasonNumber{ get; set; }
|
||||
public Dictionary<object, object> Images{ get; set; }
|
||||
[JsonProperty("mature_blocked")] public bool MatureBlocked{ get; set; }
|
||||
public List<Version> Versions{ get; set; }
|
||||
public List<Version>? Versions{ get; set; }
|
||||
public string Title{ get; set; }
|
||||
[JsonProperty("is_subbed")] public bool IsSubbed{ get; set; }
|
||||
public string Id{ get; set; }
|
||||
|
|
|
@ -4,18 +4,24 @@ using System.Linq;
|
|||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CRD.Downloader;
|
||||
using CRD.Utils;
|
||||
using CRD.Views;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace CRD.ViewModels;
|
||||
|
||||
public partial class HistoryPageViewModel : ViewModelBase{
|
||||
|
||||
public ObservableCollection<HistorySeries> Items{ get; }
|
||||
[ObservableProperty] private bool? _showLoading = false;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool? _showLoading = false;
|
||||
|
||||
[ObservableProperty]
|
||||
public HistorySeries _selectedSeries;
|
||||
|
||||
[ObservableProperty]
|
||||
public static bool _editMode;
|
||||
|
||||
public HistoryPageViewModel(){
|
||||
Items = Crunchyroll.Instance.HistoryList;
|
||||
|
||||
|
@ -23,9 +29,9 @@ public partial class HistoryPageViewModel : ViewModelBase{
|
|||
if (historySeries.ThumbnailImage == null){
|
||||
historySeries.LoadImage();
|
||||
}
|
||||
|
||||
historySeries.UpdateNewEpisodes();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -35,6 +41,18 @@ public partial class HistoryPageViewModel : ViewModelBase{
|
|||
_selectedSeries = null;
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
public void RemoveSeries(string? seriesId){
|
||||
|
||||
HistorySeries? objectToRemove = Crunchyroll.Instance.HistoryList.ToList().Find(se => se.SeriesId == seriesId) ?? null;
|
||||
if (objectToRemove != null) {
|
||||
Crunchyroll.Instance.HistoryList.Remove(objectToRemove);
|
||||
Items.Remove(objectToRemove);
|
||||
}
|
||||
CfgManager.WriteJsonToFile(CfgManager.PathCrHistory, Crunchyroll.Instance.HistoryList);
|
||||
}
|
||||
|
||||
|
||||
[RelayCommand]
|
||||
public void NavToSeries(){
|
||||
MessageBus.Current.SendMessage(new NavigationMessage(typeof(SeriesPageViewModel), false, false));
|
||||
|
@ -47,6 +65,7 @@ public partial class HistoryPageViewModel : ViewModelBase{
|
|||
await Items[i].FetchData("");
|
||||
Items[i].UpdateNewEpisodes();
|
||||
}
|
||||
|
||||
ShowLoading = false;
|
||||
}
|
||||
|
||||
|
@ -55,8 +74,7 @@ public partial class HistoryPageViewModel : ViewModelBase{
|
|||
for (int i = 0; i < Items.Count; i++){
|
||||
await Items[i].AddNewMissingToDownloads();
|
||||
}
|
||||
|
||||
ShowLoading = false;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
@ -23,9 +24,25 @@ public partial class MainWindowViewModel : ViewModelBase{
|
|||
_faTheme = App.Current.Styles[0] as FluentAvaloniaTheme;
|
||||
|
||||
Init();
|
||||
|
||||
CleanUpOldUpdater();
|
||||
|
||||
}
|
||||
|
||||
private void CleanUpOldUpdater() {
|
||||
string backupFilePath = Path.Combine(Directory.GetCurrentDirectory(), "Updater.exe.bak");
|
||||
|
||||
if (File.Exists(backupFilePath)) {
|
||||
try {
|
||||
File.Delete(backupFilePath);
|
||||
Console.WriteLine($"Deleted old updater file: {backupFilePath}");
|
||||
} catch (Exception ex) {
|
||||
Console.WriteLine($"Failed to delete old updater file: {ex.Message}");
|
||||
}
|
||||
} else {
|
||||
Console.WriteLine("No old updater file found to delete.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async void Init(){
|
||||
|
|
|
@ -3,6 +3,7 @@ using System.Threading.Tasks;
|
|||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CRD.Downloader;
|
||||
using CRD.Utils;
|
||||
using CRD.Views;
|
||||
using ReactiveUI;
|
||||
|
||||
|
@ -14,6 +15,9 @@ public partial class SeriesPageViewModel : ViewModelBase{
|
|||
[ObservableProperty]
|
||||
public HistorySeries _selectedSeries;
|
||||
|
||||
[ObservableProperty]
|
||||
public static bool _editMode;
|
||||
|
||||
public SeriesPageViewModel(){
|
||||
_selectedSeries = Crunchyroll.Instance.SelectedSeries;
|
||||
|
||||
|
@ -29,6 +33,19 @@ public partial class SeriesPageViewModel : ViewModelBase{
|
|||
MessageBus.Current.SendMessage(new NavigationMessage(typeof(SeriesPageViewModel),false,true));
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
public void RemoveSeason(string? season){
|
||||
|
||||
HistorySeason? objectToRemove = SelectedSeries.Seasons.Find(se => se.SeasonId == season) ?? null;
|
||||
if (objectToRemove != null) {
|
||||
SelectedSeries.Seasons.Remove(objectToRemove);
|
||||
}
|
||||
CfgManager.WriteJsonToFile(CfgManager.PathCrHistory, Crunchyroll.Instance.HistoryList);
|
||||
MessageBus.Current.SendMessage(new NavigationMessage(typeof(SeriesPageViewModel),false,true));
|
||||
}
|
||||
|
||||
|
||||
|
||||
[RelayCommand]
|
||||
public void NavBack(){
|
||||
SelectedSeries.UpdateNewEpisodes();
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
xmlns:vm="clr-namespace:CRD.ViewModels"
|
||||
xmlns:ui="clr-namespace:CRD.Utils.UI"
|
||||
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
x:DataType="vm:HistoryPageViewModel"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="CRD.Views.HistoryPageView">
|
||||
|
@ -23,6 +24,7 @@
|
|||
<StackPanel Grid.Row="0" Grid.Column="0" Orientation="Horizontal" Margin="20 0 0 0 ">
|
||||
<Button Command="{Binding RefreshAll}" Margin="10">Refresh All</Button>
|
||||
<Button Command="{Binding AddMissingToQueue}" Margin="10">Add To Queue</Button>
|
||||
<ToggleButton IsChecked="{Binding EditMode}" Margin="10">Edit</ToggleButton>
|
||||
</StackPanel>
|
||||
|
||||
<Grid Grid.Row="1" Grid.Column="0">
|
||||
|
@ -44,6 +46,9 @@
|
|||
</ListBox.ItemsPanel>
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
|
||||
<Grid>
|
||||
|
||||
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" MaxWidth="250" Width="250"
|
||||
MaxHeight="400" Height="400" Margin="5">
|
||||
<Grid>
|
||||
|
@ -58,6 +63,26 @@
|
|||
Margin="4,0,0,0">
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" MaxWidth="250" Width="250"
|
||||
MaxHeight="400" Height="400" Background="#90000000" IsVisible="{Binding $parent[UserControl].((vm:HistoryPageViewModel)DataContext).EditMode}">
|
||||
<Button MaxWidth="250" Width="250" MaxHeight="400" Height="400" FontStyle="Italic"
|
||||
VerticalAlignment="Center"
|
||||
Command="{Binding $parent[UserControl].((vm:HistoryPageViewModel)DataContext).RemoveSeries}"
|
||||
CommandParameter="{Binding SeriesId}"
|
||||
>
|
||||
<ToolTip.Tip>
|
||||
<TextBlock Text="Remove Series" FontSize="15" />
|
||||
</ToolTip.Tip>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<controls:SymbolIcon Symbol="Delete" FontSize="32" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
|
||||
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
</Image>
|
||||
|
||||
|
||||
|
||||
<Grid Grid.Row="1" Grid.Column="1">
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
|
@ -41,15 +40,14 @@
|
|||
|
||||
<TextBlock Grid.Row="0" FontSize="50" Text="{Binding SelectedSeries.SeriesTitle}"></TextBlock>
|
||||
<TextBlock Grid.Row="1" FontSize="20" TextWrapping="Wrap" Text="{Binding SelectedSeries.SeriesDescription}"></TextBlock>
|
||||
<Button Grid.Row="3" Command="{Binding UpdateData}" Margin="0 0 0 10">Fetch Series</Button>
|
||||
<StackPanel Grid.Row="3" Orientation="Horizontal">
|
||||
<Button Command="{Binding UpdateData}" Margin="0 0 5 10">Fetch Series</Button>
|
||||
<ToggleButton IsChecked="{Binding EditMode}" Margin="0 0 0 10">Edit</ToggleButton>
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<ScrollViewer Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2">
|
||||
<ItemsControl ItemsSource="{Binding SelectedSeries.Seasons}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
|
@ -63,7 +61,6 @@
|
|||
IsExpanded="False">
|
||||
|
||||
|
||||
|
||||
<controls:SettingsExpander.ItemTemplate>
|
||||
<DataTemplate>
|
||||
|
||||
|
@ -83,14 +80,22 @@
|
|||
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Center">
|
||||
|
||||
<Button Width="34" Height="34" Margin="0 0 10 0" Background="Transparent" BorderThickness="0" CornerRadius="50" IsVisible="{Binding !WasDownloaded}" Command="{Binding $parent[controls:SettingsExpander].((downloader:HistorySeason)DataContext).UpdateDownloaded}" CommandParameter="{Binding EpisodeId}">
|
||||
<Button Width="34" Height="34" Margin="0 0 10 0" Background="Transparent"
|
||||
BorderThickness="0" CornerRadius="50"
|
||||
IsVisible="{Binding !WasDownloaded}"
|
||||
Command="{Binding $parent[controls:SettingsExpander].((downloader:HistorySeason)DataContext).UpdateDownloaded}"
|
||||
CommandParameter="{Binding EpisodeId}">
|
||||
<Grid>
|
||||
<Ellipse Width="25" Height="25" Fill="Gray" />
|
||||
<controls:SymbolIcon Symbol="Checkmark" FontSize="18" />
|
||||
</Grid>
|
||||
</Button>
|
||||
|
||||
<Button Width="34" Height="34" Margin="0 0 10 0" Background="Transparent" BorderThickness="0" CornerRadius="50" IsVisible="{Binding WasDownloaded}" Command="{Binding $parent[controls:SettingsExpander].((downloader:HistorySeason)DataContext).UpdateDownloaded}" CommandParameter="{Binding EpisodeId}">
|
||||
<Button Width="34" Height="34" Margin="0 0 10 0" Background="Transparent"
|
||||
BorderThickness="0" CornerRadius="50"
|
||||
IsVisible="{Binding WasDownloaded}"
|
||||
Command="{Binding $parent[controls:SettingsExpander].((downloader:HistorySeason)DataContext).UpdateDownloaded}"
|
||||
CommandParameter="{Binding EpisodeId}">
|
||||
<Grid>
|
||||
<Ellipse Width="25" Height="25" Fill="#21a556" />
|
||||
<controls:SymbolIcon Symbol="Checkmark" FontSize="18" />
|
||||
|
@ -115,7 +120,9 @@
|
|||
<TextBlock Text="/" VerticalAlignment="Center"></TextBlock>
|
||||
<TextBlock Text="{Binding EpisodesList.Count}" VerticalAlignment="Center"></TextBlock>
|
||||
<Button Margin="10 0 0 0" FontStyle="Italic"
|
||||
VerticalAlignment="Center" Command="{Binding $parent[UserControl].((vm:SeriesPageViewModel)DataContext).UpdateData}" CommandParameter="{Binding SeasonId}" >
|
||||
VerticalAlignment="Center"
|
||||
Command="{Binding $parent[UserControl].((vm:SeriesPageViewModel)DataContext).UpdateData}"
|
||||
CommandParameter="{Binding SeasonId}">
|
||||
<ToolTip.Tip>
|
||||
<TextBlock Text="Fetch Season" FontSize="15" />
|
||||
</ToolTip.Tip>
|
||||
|
@ -123,6 +130,18 @@
|
|||
<controls:SymbolIcon Symbol="Refresh" FontSize="18" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Margin="10 0 0 0" FontStyle="Italic"
|
||||
VerticalAlignment="Center"
|
||||
Command="{Binding $parent[UserControl].((vm:SeriesPageViewModel)DataContext).RemoveSeason}"
|
||||
CommandParameter="{Binding SeasonId}"
|
||||
IsVisible="{Binding $parent[UserControl].((vm:SeriesPageViewModel)DataContext).EditMode}">
|
||||
<ToolTip.Tip>
|
||||
<TextBlock Text="Remove Season" FontSize="15" />
|
||||
</ToolTip.Tip>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<controls:SymbolIcon Symbol="Delete" FontSize="18" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</controls:SettingsExpander.Footer>
|
||||
|
||||
|
|
Loading…
Reference in New Issue