Compare commits
3 Commits
6e145daf52
...
e9cc212684
| Author | SHA1 | Date | |
|---|---|---|---|
| e9cc212684 | |||
| c899f5591e | |||
| e79f9c140d |
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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()))
|
||||||
|
{
|
||||||
|
checkDB();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
createNewDB();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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()))
|
using (var cn = new SQLiteConnection(Settings.GetConnectString()))
|
||||||
{
|
{
|
||||||
cn.Execute(@"
|
cn.Execute(@"
|
||||||
CREATE TABLE Book (
|
CREATE TABLE ""Book"" (
|
||||||
Url VARCHAR(256),
|
""Url"" VARCHAR(256),
|
||||||
Title VARNCHAR(256),
|
""Title"" VARNCHAR(256),
|
||||||
PageCount smallint,
|
""PageCount"" smallint,
|
||||||
DownloadDateTime datetime,
|
""DownloadDateTime"" datetime,
|
||||||
CONSTRAINT Book_PK PRIMARY KEY (Url)
|
""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);
|
||||||
|
");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool PageIsExists(string url)
|
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()))
|
||||||
|
{
|
||||||
|
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()))
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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;
|
||||||
|
await newTask(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task newTask(string url)
|
||||||
|
{
|
||||||
|
BookStates? newBookState = _dbContext.QueryBookState(url);
|
||||||
|
if (newBookState.IsCompleted())
|
||||||
|
{
|
||||||
|
// 已完成
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Book book = new Book(url, _folder);
|
||||||
|
if (!newBookState.HasValue)
|
||||||
|
_dbContext.InsertBook(book);
|
||||||
|
else
|
||||||
|
_dbContext.UpdateBook(book);
|
||||||
|
|
||||||
|
|
||||||
ListViewItem lvi = new ListViewItem(url) { Name = url };
|
ListViewItem lvi = new ListViewItem(url) { Name = url };
|
||||||
lvi.SubItems.Add("Wait");
|
lvi.SubItems.Add("Wait");
|
||||||
|
|
||||||
Book book = new Book(url, _folder);
|
|
||||||
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
BIN
testdb/db_v1.sqlite
Normal file
Binary file not shown.
Reference in New Issue
Block a user