Compare commits

..

3 Commits

5 changed files with 187 additions and 55 deletions

View File

@@ -6,6 +6,7 @@ using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -28,33 +29,40 @@ namespace EHDownloader
DownloadImageFailed, DownloadImageFailed,
} }
enum BookStates enum BookStates:int
{ {
[Description("等待")] [Description("等待")]
Wait, Wait = 0,
//[Description("擷取頁面失敗")] //[Description("擷取頁面失敗")]
//FetchError, //FetchError,
[Description("載入首頁失敗")] [Description("載入首頁失敗")]
LoadFailed, LoadFailed = -1,
[Description("擷取標題失敗")] [Description("擷取標題失敗")]
FetchTitleFailed, FetchTitleFailed = -2,
[Description("擷取頁集合失敗")] [Description("擷取頁集合失敗")]
FetchPageSetFailed, FetchPageSetFailed = -3,
[Description("擷取頁連結失敗")] [Description("擷取頁連結失敗")]
FetchPageLinkFailed, FetchPageLinkFailed = -4,
[Description("下載完成, 但是有些頁面出錯")]
DownloadFinishedButSomePageError = -5,
[Description("擷取頁面完成")] [Description("擷取頁面完成")]
FetchCompleted, FetchCompleted = 1,
[Description("下載完成")] [Description("下載完成")]
DownloadCompleted, DownloadCompleted = 2,
[Description("下載完成, 但是有些頁面出錯")] [Description("下載並封裝完成")]
DownloadFinishedButSomePageError, Compressed = 3,
Compressed,
} }
static class BookStateHelper
{
static public bool IsCompleted(this BookStates? state)
{
return state.HasValue && ((int)state.Value) >= (int)BookStates.Compressed;
}
}
class Book class Book
{ {
private const string PageSetXPath = @"//table[@class='ptb']//a"; private const string PageSetXPath = @"//table[@class='ptb']//a";
@@ -73,9 +81,9 @@ namespace EHDownloader
public List<Page> Pages { get; private set; } public List<Page> Pages { get; private set; }
public int PageCount => Pages.Count; public int PageCount => Pages?.Count ?? 0;
public BookStates BookState { get; set; } = BookStates.Wait; public BookStates State { get; set; } = BookStates.Wait;
public Book(string url, string parentFolder) public Book(string url, string parentFolder)
@@ -105,7 +113,7 @@ namespace EHDownloader
} }
catch (Exception ex) catch (Exception ex)
{ {
BookState = BookStates.LoadFailed; State = BookStates.LoadFailed;
return FetchBookResults.LoadPageFailed; return FetchBookResults.LoadPageFailed;
} }
@@ -115,7 +123,7 @@ namespace EHDownloader
} }
catch catch
{ {
BookState = BookStates.FetchTitleFailed; State = BookStates.FetchTitleFailed;
return FetchBookResults.FetchTitleFailed; return FetchBookResults.FetchTitleFailed;
} }
@@ -125,7 +133,7 @@ namespace EHDownloader
var htmlPageSets = doc.DocumentNode.SelectNodes(PageSetXPath); var htmlPageSets = doc.DocumentNode.SelectNodes(PageSetXPath);
if (htmlPageSets == null) if (htmlPageSets == null)
{ {
BookState = BookStates.FetchPageSetFailed; State = BookStates.FetchPageSetFailed;
return FetchBookResults.FetchPageSetFailed; return FetchBookResults.FetchPageSetFailed;
} }
foreach (var htmlPageSet in htmlPageSets) foreach (var htmlPageSet in htmlPageSets)
@@ -142,7 +150,7 @@ namespace EHDownloader
} }
catch catch
{ {
BookState = BookStates.FetchPageSetFailed; State = BookStates.FetchPageSetFailed;
return FetchBookResults.FetchTitleFailed; return FetchBookResults.FetchTitleFailed;
} }
@@ -170,7 +178,7 @@ namespace EHDownloader
} }
catch catch
{ {
BookState = BookStates.FetchPageLinkFailed; State = BookStates.FetchPageLinkFailed;
return FetchBookResults.FetchPageLinkFailed; return FetchBookResults.FetchPageLinkFailed;
} }
@@ -180,7 +188,7 @@ namespace EHDownloader
Pages[i].PageNumber = i + 1; Pages[i].PageNumber = i + 1;
} }
BookState = BookStates.FetchCompleted; State = BookStates.FetchCompleted;
return FetchBookResults.Completed; return FetchBookResults.Completed;
} }
@@ -218,15 +226,16 @@ namespace EHDownloader
if (Pages.All(p => p.Result == DownloadPageResults.Completed)) if (Pages.All(p => p.Result == DownloadPageResults.Completed))
{ {
BookState = BookStates.DownloadCompleted; State = BookStates.DownloadCompleted;
return; return;
} }
} }
BookState = BookStates.DownloadFinishedButSomePageError; State = BookStates.DownloadFinishedButSomePageError;
} }
private string GetFolderName() private string getFolderName()
{ {
if (Title == null) return null;
string folderName = Title; string folderName = Title;
foreach (var ch in System.IO.Path.GetInvalidFileNameChars()) foreach (var ch in System.IO.Path.GetInvalidFileNameChars())
folderName = folderName.Replace(ch, ' '); folderName = folderName.Replace(ch, ' ');
@@ -251,7 +260,7 @@ namespace EHDownloader
public async Task CompressAsync() public async Task CompressAsync()
{ {
string folderPath = getFolderPath(); string folderPath = getFolderPath();
if (BookState != BookStates.DownloadCompleted) if (State != BookStates.DownloadCompleted)
return; return;
if (!System.IO.Directory.Exists(ParentFolder)) if (!System.IO.Directory.Exists(ParentFolder))
return; return;
@@ -271,12 +280,19 @@ namespace EHDownloader
System.IO.Directory.Delete(folderPath, true); System.IO.Directory.Delete(folderPath, true);
} }
BookState = BookStates.Compressed; State = BookStates.Compressed;
} }
private string getFolderPath() private string getFolderPath()
{ {
return System.IO.Path.Combine(ParentFolder, GetFolderName()); string nane = getFolderName();
if (nane == null) return null;
return System.IO.Path.Combine(ParentFolder, nane);
}
public string Path
{
get => getFolderPath();
} }
} }

View File

@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Data.SQLite; using System.Data.SQLite;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Security.Policy;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -13,23 +14,82 @@ namespace EHDownloader
{ {
public DbContext() public DbContext()
{ {
if (File.Exists(Settings.GetDbPath())) return; if (File.Exists(Settings.GetDbPath()))
using (var cn = new SQLiteConnection(Settings.GetConnectString()))
{ {
cn.Execute(@" checkDB();
CREATE TABLE Book ( }
Url VARCHAR(256), else
Title VARNCHAR(256), {
PageCount smallint, createNewDB();
DownloadDateTime datetime,
CONSTRAINT Book_PK PRIMARY KEY (Url)
)");
} }
} }
public bool PageIsExists(string url) private void checkDB()
{
int DBVer = getDBSchemaVersion();
if(DBVer == 0)
{
updateDBSchema(DBVer);
}
}
private void updateDBSchema(int fromVer)
{
using (var cn = new SQLiteConnection(Settings.GetConnectString()))
{
switch (fromVer)
{
case 0:
{
// add App table and ver=1
cn.Execute(@"
CREATE TABLE App (DBSchemaVersion INTEGER NOT NULL);
INSERT INTO App (DBSchemaVersion) VALUES (1);
ALTER TABLE Book ADD COLUMN State INTEGER NOT NULL DEFAULT 3;
ALTER TABLE Book ADD COLUMN Path TEXT;
");
}
break;
}
}
}
private static void createNewDB()
{
using (var cn = new SQLiteConnection(Settings.GetConnectString()))
{
cn.Execute(@"
CREATE TABLE ""Book"" (
""Url"" VARCHAR(256),
""Title"" VARNCHAR(256),
""PageCount"" smallint,
""DownloadDateTime"" datetime,
""State"" INTEGER NOT NULL,
""Path"" TEXT,
CONSTRAINT ""Book_PK"" PRIMARY KEY(""Url"")
);
CREATE TABLE ""App"" (
""DBSchemaVersion"" INTEGER NOT NULL
);
INSERT INTO App (DBSchemaVersion) VALUES (1);
");
}
}
private int getDBSchemaVersion()
{
using (var cn = new SQLiteConnection(Settings.GetConnectString()))
{
// check App table is exists
var query_table_exists = "SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'App'";
bool app_table_is_exists = cn.Query(query_table_exists).Any();
if (!app_table_is_exists) return 0;
var query = "select DBSchemaVersion from App";
return cn.Query<int>(query).FirstOrDefault();
}
}
public bool BookIsExists(string url)
{ {
using (var cn = new SQLiteConnection(Settings.GetConnectString())) using (var cn = new SQLiteConnection(Settings.GetConnectString()))
{ {
@@ -39,14 +99,48 @@ namespace EHDownloader
} }
} }
public void InsertPage(Book book) public bool BookIsCompleted(string url)
{ {
using (var cn = new SQLiteConnection(Settings.GetConnectString())) using (var cn = new SQLiteConnection(Settings.GetConnectString()))
{ {
var query =
"select State from Book where Url=(@url)";
int? state = cn.Query<int?>(query, new { url }).FirstOrDefault();
return ((BookStates?)state).IsCompleted();
}
}
// null: book not exists
public BookStates? QueryBookState(string url)
{
using (var cn = new SQLiteConnection(Settings.GetConnectString()))
{
var query =
"select State from Book where Url=(@url)";
int? state = cn.Query<int?>(query, new { url }).FirstOrDefault();
return (BookStates)state;
}
}
public void InsertBook(Book book)
{
using (var cn = new SQLiteConnection(Settings.GetConnectString()))
{
var insertScript = var insertScript =
"INSERT INTO Book VALUES (@Url,@Title,@PageCount,@DownloadDateTime)"; "INSERT INTO Book VALUES (@Url,@Title,@PageCount,@DownloadDateTime,@State,@Path)";
cn.Execute(insertScript, book); cn.Execute(insertScript, book);
} }
} }
public void UpdateBook(Book book)
{
using (var cn = new SQLiteConnection(Settings.GetConnectString()))
{
var updateScript =
"UPDATE Book SET Title=@Title,PageCount=@PageCount,DownloadDateTime=@DownloadDateTime,State=@State,Path=@Path " +
"WHERE Url=@Url";
cn.Execute(updateScript, book);
}
}
} }
} }

View File

@@ -114,4 +114,10 @@
</PropertyGroup> </PropertyGroup>
<Error Condition="!Exists('..\packages\System.Data.SQLite.Core.1.0.111.0\build\net46\System.Data.SQLite.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\System.Data.SQLite.Core.1.0.111.0\build\net46\System.Data.SQLite.Core.targets'))" /> <Error Condition="!Exists('..\packages\System.Data.SQLite.Core.1.0.111.0\build\net46\System.Data.SQLite.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\System.Data.SQLite.Core.1.0.111.0\build\net46\System.Data.SQLite.Core.targets'))" />
</Target> </Target>
<PropertyGroup>
<ContentSQLiteInteropFiles>true</ContentSQLiteInteropFiles>
<CopySQLiteInteropFiles>false</CopySQLiteInteropFiles>
<CleanSQLiteInteropFiles>false</CleanSQLiteInteropFiles>
<CollectSQLiteInteropFiles>false</CollectSQLiteInteropFiles>
</PropertyGroup>
</Project> </Project>

View File

@@ -106,18 +106,32 @@ namespace EHDownloader
{ {
if (!isEhUrl(url)) return; if (!isEhUrl(url)) return;
if (lv_Tasks.Items.ContainsKey(url)) return; if (lv_Tasks.Items.ContainsKey(url)) return;
if (_dbContext.PageIsExists(url)) return; // if (_dbContext.BookIsExists(url)) return;
ListViewItem lvi = new ListViewItem(url) { Name = url }; await newTask(url);
lvi.SubItems.Add("Wait"); }
private async Task newTask(string url)
{
BookStates? newBookState = _dbContext.QueryBookState(url);
if (newBookState.IsCompleted())
{
// 已完成
return;
}
Book book = new Book(url, _folder); Book book = new Book(url, _folder);
if (!newBookState.HasValue)
_dbContext.InsertBook(book);
else
_dbContext.UpdateBook(book);
ListViewItem lvi = new ListViewItem(url) { Name = url };
lvi.SubItems.Add("Wait");
lvi.Text = url; lvi.Text = url;
lvi.Tag = book; lvi.Tag = book;
lv_Tasks.Items.Add(lvi); lv_Tasks.Items.Add(lvi);
await TryStartTask(lvi); await TryStartTask(lvi);
} }
private async Task TryStartTask() private async Task TryStartTask()
@@ -135,19 +149,21 @@ namespace EHDownloader
while (running) while (running)
{ {
lvi.SubItems[1].Text = book.BookState.GetDescription(); lvi.SubItems[1].Text = book.State.GetDescription();
switch (book.BookState) switch (book.State)
{ {
case BookStates.Wait: case BookStates.Wait:
await book.FetchAsync(); await book.FetchAsync();
_dbContext.UpdateBook(book);
break; break;
case BookStates.FetchCompleted: case BookStates.FetchCompleted:
await book.DownloadBookAsync(new Progress<DownloadProgressInfo>( await book.DownloadBookAsync(new Progress<DownloadProgressInfo>(
(info) => lvi.SubItems[1].Text = $"下載中 {info.PageNumber}/{info.PageCount}")); (info) => lvi.SubItems[1].Text = $"下載中 {info.PageNumber}/{info.PageCount}"));
_dbContext.UpdateBook(book);
break; break;
case BookStates.DownloadCompleted: case BookStates.DownloadCompleted:
await book.CompressAsync(); await book.CompressAsync();
_dbContext.InsertPage(book); _dbContext.UpdateBook(book);
goto default; goto default;
case BookStates.Compressed: case BookStates.Compressed:
case BookStates.LoadFailed: case BookStates.LoadFailed:
@@ -178,7 +194,7 @@ namespace EHDownloader
{ {
var lvi = lv_Tasks.Items[i]; var lvi = lv_Tasks.Items[i];
var book = (Book)lvi.Tag; var book = (Book)lvi.Tag;
switch (book.BookState) switch (book.State)
{ {
case BookStates.Wait: case BookStates.Wait:
case BookStates.FetchCompleted: case BookStates.FetchCompleted:
@@ -190,10 +206,10 @@ namespace EHDownloader
case BookStates.FetchTitleFailed: case BookStates.FetchTitleFailed:
case BookStates.FetchPageSetFailed: case BookStates.FetchPageSetFailed:
case BookStates.FetchPageLinkFailed: case BookStates.FetchPageLinkFailed:
book.BookState = BookStates.Wait; book.State = BookStates.Wait;
continue; continue;
case BookStates.DownloadFinishedButSomePageError: case BookStates.DownloadFinishedButSomePageError:
book.BookState = BookStates.FetchCompleted; book.State = BookStates.FetchCompleted;
continue; continue;
} }
} }

BIN
testdb/db_v1.sqlite Normal file

Binary file not shown.