Skip to content
This repository was archived by the owner on Jan 4, 2025. It is now read-only.

Commit 41384bc

Browse files
authored
修复网络下载转换过程中失败的问题 (#25)
1 parent 0697f2b commit 41384bc

File tree

7 files changed

+115
-98
lines changed

7 files changed

+115
-98
lines changed

src/CleanReader.App/Controls/App/CoverImage.xaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
<Grid x:Name="CoverContainer" CornerRadius="{StaticResource ControlCornerRadius}">
2323
<controls:ImageEx
2424
x:Name="Image"
25+
EnableLazyLoading="True"
26+
IsCacheEnabled="True"
2527
PlaceholderSource="{StaticResource CoverPlaceholder}"
2628
PlaceholderStretch="UniformToFill"
2729
Stretch="UniformToFill" />

src/CleanReader.App/Pages/ExplorePage.xaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
<TabView.TabStripHeader>
4949
<ComboBox
5050
MinWidth="128"
51+
Margin="0,8,0,0"
5152
VerticalAlignment="Center"
5253
ItemsSource="{x:Bind _viewModel.BookSources}"
5354
SelectedItem="{x:Bind _viewModel.SelectedBookSource, Mode=TwoWay}">

src/CleanReader.App/Pages/ExplorePage.xaml.cs

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Richasy. All rights reserved.
22

33
using System;
4+
using System.Reactive.Linq;
45
using CleanReader.Models.Resources;
56
using CleanReader.ViewModels.Desktop;
67
using Microsoft.UI.Xaml;
@@ -27,20 +28,7 @@ private void OnShelfItemClick(object sender, RoutedEventArgs e)
2728
{
2829
var dispatcher = DispatcherQueue;
2930
var vm = (sender as FrameworkElement).DataContext as OnlineBookViewModel;
30-
LibraryViewModel.Instance.InsertOrUpdateBookEntryFromOnlineBookCommand.Execute(vm).Subscribe(book =>
31-
{
32-
DispatcherQueue.TryEnqueue(() =>
33-
{
34-
if (book != null)
35-
{
36-
AppViewModel.Instance.ShowTip(StringResources.ExploreBookAdded, Models.Constants.InfoType.Success);
37-
}
38-
else
39-
{
40-
AppViewModel.Instance.ShowTip(StringResources.FailedToDownload, Models.Constants.InfoType.Error);
41-
}
42-
});
43-
});
31+
LibraryViewModel.Instance.InsertOrUpdateBookEntryFromOnlineBookCommand.Execute(vm).Subscribe();
4432
}
4533

4634
#pragma warning disable CA1822 // 将成员标记为 static

src/CleanReader.App/Pages/ShelfPage.xaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,11 @@
156156
Padding="{StaticResource DefaultContainerPadding}"
157157
HorizontalScrollMode="Disabled"
158158
VerticalScrollBarVisibility="Auto">
159-
<ItemsRepeater Margin="0,12" ItemsSource="{x:Bind _viewModel.DisplayBooks}">
159+
<ItemsRepeater
160+
Margin="0,12"
161+
HorizontalCacheLength="100"
162+
ItemsSource="{x:Bind _viewModel.DisplayBooks}"
163+
VerticalCacheLength="100">
160164
<ItemsRepeater.ItemTemplate>
161165
<DataTemplate x:DataType="desktop:ShelfBookViewModel">
162166
<controls:ShelfBookCard Data="{x:Bind}" />

src/ViewModels/ViewModels.Desktop/LibraryViewModel/LibraryViewModel.Search.cs

Lines changed: 99 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Collections.Generic;
55
using System.IO;
66
using System.Linq;
7+
using System.Reactive.Linq;
78
using System.Threading;
89
using System.Threading.Tasks;
910
using System.Threading.Tasks.Dataflow;
@@ -132,112 +133,132 @@ private void SetSelectedSearchItem(OnlineBookViewModel vm)
132133
SelectedSearchBook = OnlineSearchBooks.SingleOrDefault(p => p.IsSelected)?.Book;
133134
}
134135

135-
private async Task<Book> GenerateBookEntryFromOnlineBookAsync(Services.Novel.Models.Book book = null)
136+
private IObservable<Book> GenerateBookEntryFromOnlineBook(Services.Novel.Models.Book book = null)
136137
{
137-
if (book == null)
138+
return Observable.StartAsync(async () =>
138139
{
139-
book = SelectedSearchBook;
140-
}
141-
142-
if (book != null)
143-
{
144-
Book entry = null;
145-
var dialog = ServiceLocator.Instance.GetService<ICustomDialog>(AppConstants.ProgressDialog);
146-
var cancelSource = new CancellationTokenSource();
147-
dialog.InjectTask(
148-
Task.Run(async () =>
140+
if (book == null)
149141
{
150-
EpubService.ClearCache();
151-
DispatcherQueue.TryEnqueue(() =>
152-
{
153-
dialog.InjectData(StringResources.GettingChapters);
154-
});
142+
book = SelectedSearchBook;
143+
}
155144

156-
var chapters = await _novelService.GetBookChaptersAsync(book.SourceId, book.Url, cancelSource);
157-
var chapterContents = new List<Tuple<int, string, string>>();
158-
var progress = 0;
159-
var total = chapters.Count;
160-
var options = new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 12, BoundedCapacity = total };
161-
options.CancellationToken = cancelSource.Token;
162-
var action = new ActionBlock<Services.Novel.Models.Chapter>(
163-
async chapter =>
145+
if (book != null)
146+
{
147+
Book entry = null;
148+
var dialog = ServiceLocator.Instance.GetService<ICustomDialog>(AppConstants.ProgressDialog);
149+
var cancelSource = new CancellationTokenSource();
150+
dialog.InjectTask(
151+
Task.Run(async () =>
164152
{
165-
if (cancelSource.Token.IsCancellationRequested)
166-
{
167-
return;
168-
}
169-
170-
var content = await _novelService.GetChapterContentAsync(book.SourceId, chapter, cancelSource);
171-
chapterContents.Add(new Tuple<int, string, string>(content.ChapterIndex, chapter.Title, content.Content));
172-
progress++;
153+
EpubService.ClearCache();
173154
DispatcherQueue.TryEnqueue(() =>
174155
{
175-
dialog.InjectData(new Tuple<int, int, string>(progress, total, string.Format(StringResources.ChapterDownloadingProgress, progress, total)));
156+
dialog.InjectData(StringResources.GettingChapters);
176157
});
177-
},
178-
options);
179158

180-
foreach (var chapter in chapters)
181-
{
182-
action.Post(chapter);
183-
}
159+
var chapters = await _novelService.GetBookChaptersAsync(book.SourceId, book.Url, cancelSource);
160+
var chapterContents = new List<Tuple<int, string, string>>();
161+
var progress = 0;
162+
var total = chapters.Count;
163+
var options = new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 12, BoundedCapacity = total };
164+
options.CancellationToken = cancelSource.Token;
165+
var action = new ActionBlock<Services.Novel.Models.Chapter>(
166+
async chapter =>
167+
{
168+
if (cancelSource.Token.IsCancellationRequested)
169+
{
170+
return;
171+
}
184172

185-
action.Complete();
186-
await action.Completion;
173+
var content = await _novelService.GetChapterContentAsync(book.SourceId, chapter, cancelSource);
174+
chapterContents.Add(new Tuple<int, string, string>(content.ChapterIndex, chapter.Title, content.Content));
175+
progress++;
176+
DispatcherQueue.TryEnqueue(() =>
177+
{
178+
dialog.InjectData(new Tuple<int, int, string>(progress, total, string.Format(StringResources.ChapterDownloadingProgress, progress, total)));
179+
});
180+
},
181+
options);
187182

188-
DispatcherQueue.TryEnqueue(() =>
189-
{
190-
dialog.InjectData(StringResources.ConvertingAndMovingFile);
191-
});
192-
var configure = new EpubServiceConfiguration()
193-
{
194-
Title = book.BookName,
195-
Author = book.Author,
196-
Language = "zh",
197-
OutputFileName = $"{book.BookName}.epub",
198-
OutputFolderPath = Path.Combine(_rootDirectory.FullName, VMConstants.Library.BooksFolder),
199-
};
183+
foreach (var chapter in chapters)
184+
{
185+
action.Post(chapter);
186+
}
200187

201-
configure = await EpubService.InitializeSplitedBookAsync(chapterContents, configure);
202-
var service = new EpubService(configure);
203-
await service.CreateAsync();
188+
action.Complete();
189+
await action.Completion;
204190

205-
entry = GetBookEntryFromOnlineBook(book, Path.Combine(configure.OutputFolderPath, configure.OutputFileName));
206-
}),
207-
cancelSource);
191+
DispatcherQueue.TryEnqueue(() =>
192+
{
193+
dialog.InjectData(StringResources.ConvertingAndMovingFile);
194+
});
195+
var configure = new EpubServiceConfiguration()
196+
{
197+
Title = book.BookName,
198+
Author = book.Author,
199+
Language = "zh",
200+
OutputFileName = $"{book.BookName}.epub",
201+
OutputFolderPath = Path.Combine(_rootDirectory.FullName, VMConstants.Library.BooksFolder),
202+
};
203+
204+
try
205+
{
206+
configure = await EpubService.InitializeSplitedBookAsync(chapterContents, configure);
207+
var service = new EpubService(configure);
208+
await service.CreateAsync();
209+
}
210+
catch (Exception ex)
211+
{
212+
AppViewModel.Instance.ShowTip(ex.Message, InfoType.Error);
213+
}
208214

209-
await dialog.ShowAsync();
210-
EpubService.ClearGenerated();
215+
entry = GetBookEntryFromOnlineBook(book, Path.Combine(configure.OutputFolderPath, configure.OutputFileName));
216+
}),
217+
cancelSource);
211218

212-
if (entry != null)
213-
{
214-
if (OriginalBook != null)
219+
await dialog.ShowAsync();
220+
EpubService.ClearGenerated();
221+
222+
if (entry != null)
215223
{
216-
var sourceEntry = await LibraryContext.Books.FirstOrDefaultAsync(p => p.Id == OriginalBook.Id);
217-
if (sourceEntry != null)
224+
if (OriginalBook != null)
218225
{
219-
await UpdateBookEntryAsync(sourceEntry, entry);
220-
entry = sourceEntry;
226+
var sourceEntry = await LibraryContext.Books.FirstOrDefaultAsync(p => p.Id == OriginalBook.Id);
227+
if (sourceEntry != null)
228+
{
229+
await UpdateBookEntryAsync(sourceEntry, entry);
230+
entry = sourceEntry;
231+
}
232+
else
233+
{
234+
await InsertBookEntryAsync(entry);
235+
}
236+
237+
OriginalBook = null;
221238
}
222239
else
223240
{
224241
await InsertBookEntryAsync(entry);
225242
}
243+
}
226244

227-
OriginalBook = null;
245+
if (entry != null)
246+
{
247+
AppViewModel.Instance.ShowTip(StringResources.ExploreBookAdded, InfoType.Success);
248+
AppViewModel.Instance.RequestNavigateTo(AppViewModel.Instance.NavigationList.First(p => p.Type == NavigationItemType.Item));
228249
}
229250
else
230251
{
231-
await InsertBookEntryAsync(entry);
252+
AppViewModel.Instance.ShowTip(StringResources.FailedToDownload, InfoType.Error);
232253
}
233-
}
234254

235-
return entry;
236-
}
237-
else
238-
{
239-
throw new Exception(StringResources.NoSelectedOnlineBook);
240-
}
255+
return entry;
256+
}
257+
else
258+
{
259+
throw new Exception(StringResources.NoSelectedOnlineBook);
260+
}
261+
});
241262
}
242263

243264
private async Task SyncBooksAsync()

src/ViewModels/ViewModels.Desktop/LibraryViewModel/LibraryViewModel.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ private LibraryViewModel()
5858
ShowReplaceSourceDialogCommand = ReactiveCommand.CreateFromTask<Book>(ShowReplaceSourceDialogAsync, outputScheduler: RxApp.MainThreadScheduler);
5959
OnlineSearchCommand = ReactiveCommand.CreateFromTask<string>(s => SearchOnlineBooksAsync(s), outputScheduler: RxApp.MainThreadScheduler);
6060
SelectOnlineSearchResultCommand = ReactiveCommand.Create<OnlineBookViewModel>(vm => SetSelectedSearchItem(vm), outputScheduler: RxApp.MainThreadScheduler);
61-
InsertOrUpdateBookEntryFromOnlineBookCommand = ReactiveCommand.CreateFromTask<OnlineBookViewModel, Book>(
62-
p => GenerateBookEntryFromOnlineBookAsync(p?.Book),
61+
InsertOrUpdateBookEntryFromOnlineBookCommand = ReactiveCommand.CreateFromObservable<OnlineBookViewModel, Book>(
62+
p => GenerateBookEntryFromOnlineBook(p?.Book),
6363
this.WhenAnyValue(x => x.SelectedSearchBook).Select(p => p != null),
6464
RxApp.MainThreadScheduler);
6565

src/ViewModels/ViewModels.Desktop/ShelfPageViewModel/ShelfPageViewModel.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -203,17 +203,17 @@ await Task.Run(async () =>
203203
books = books.Where(p => p.Type == BookType.Online).ToList();
204204
}
205205

206-
DispatcherQueue.TryEnqueue(() =>
206+
DispatcherQueue.TryEnqueue(async () =>
207207
{
208-
SortBooks(books);
208+
await SortBooksAsync(books);
209209
});
210210

211211
_settingsToolkit.WriteLocalSetting(SettingNames.SortType, CurrentSort);
212212
_settingsToolkit.WriteLocalSetting(SettingNames.BookType, CurrentBookType);
213213
_settingsToolkit.WriteLocalSetting(SettingNames.ShelfId, CurrentShelf?.Id ?? string.Empty);
214214
}
215215

216-
private void SortBooks(List<Book> source)
216+
private async Task SortBooksAsync(List<Book> source)
217217
{
218218
var list = source.Select(p => new ShelfBookViewModel(p, _dbContextRef)).ToList();
219219
if (CurrentSort == VMConstants.Shelf.SortType)
@@ -238,6 +238,7 @@ private void SortBooks(List<Book> source)
238238
}
239239

240240
DisplayBooks.Clear();
241+
await Task.CompletedTask;
241242
list.ForEach(p => DisplayBooks.Add(p));
242243
}
243244

0 commit comments

Comments
 (0)